@awsless/json 0.0.9 → 0.0.11

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
@@ -1,18 +1,19 @@
1
-
2
1
  # @awsless/json
3
2
 
4
3
  The `@awsless/json` package adds support for more JavaScript native types to JSON.
5
4
 
6
5
  Features:
7
- - Lightweight / Using the JS native JSON parser.
8
- - JSON backwards compatible.
9
- - No precision loss.
10
- - Includes support for basic JS types.
11
- - Extendable.
6
+
7
+ - Lightweight / Using the JS native JSON parser.
8
+ - JSON backwards compatible.
9
+ - No precision loss.
10
+ - Includes support for basic JS types.
11
+ - Extendable.
12
12
 
13
13
  ## The Problem
14
14
 
15
15
  JSON doesn't have support for types like:
16
+
16
17
  - `undefined`
17
18
  - `NaN`
18
19
  - `Infinity`
@@ -33,7 +34,7 @@ Additionally, the native `JSON.parse/stringify` functions do not address the pot
33
34
  ## Basic Usage
34
35
 
35
36
  ```ts
36
- import { parse, stringify } from '@awsless/json';
37
+ import { parse, stringify } from '@awsless/json'
37
38
 
38
39
  // The output will be {"$bigint":"1"}
39
40
  const json = stringify(1n)
@@ -47,7 +48,7 @@ const value = parse(json)
47
48
  In some situations, you may not have control over the JSON parser being used. In these instances, your JSON can still be parsed, but the output may be incorrect. We can correct the inaccurate output by using the `patch` function.
48
49
 
49
50
  ```ts
50
- import { stringify, patch } from '@awsless/json';
51
+ import { stringify, patch } from '@awsless/json'
51
52
 
52
53
  const json = stringify(1n)
53
54
 
@@ -63,7 +64,7 @@ const fixed = patch(broken)
63
64
  We let you extend JSON to support your own custom types.
64
65
 
65
66
  ```ts
66
- import { parse, stringify, Serializable } from '@awsless/json';
67
+ import { parse, stringify, Serializable, setGlobalTypes } from '@awsless/json'
67
68
 
68
69
  class Custom {
69
70
  readonly value
@@ -80,10 +81,33 @@ const $custom: Serializable<Custom, string> = {
80
81
  }
81
82
 
82
83
  // Stringify your custom type.
83
- const json = stringify(new Custom('example'), { $custom })
84
+ const json = stringify(new Custom('example'), {
85
+ types: { $custom },
86
+ })
84
87
 
85
88
  // Parse the json with your custom type.
86
- const value = parse(json, { $custom })
89
+ const value = parse(json, {
90
+ types: { $custom },
91
+ })
92
+
93
+ // Additionally, you can globally add your own extensions.
94
+ setGlobalTypes({ $custom })
95
+ ```
96
+
97
+ ## Preserve undefined object properties
98
+
99
+ The native `JSON.stringify` will strip undefined object properties to generate smaller JSON output. We do the same thing but we have a special option to opt out of this behavior if correctness is important to you.
100
+
101
+ ```ts
102
+ const input = { key: undefined }
103
+
104
+ const smallerJson = stringify(input, {
105
+ preserveUndefinedValues: true,
106
+ }) // {}
107
+
108
+ const moreCorrectJson = stringify(input, {
109
+ preserveUndefinedValues: true,
110
+ }) // {"key":{"$undefined":0}}
87
111
  ```
88
112
 
89
113
  ## Precision Loss
@@ -91,15 +115,15 @@ const value = parse(json, { $custom })
91
115
  When using the native `JSON.parse/stringify` functions you could lose precision when parsing native numbers. And you don't always have the ability to extend JSON with your own custom types. For example when you’re communicating with a third-party API. For this reason, we have 2 utility functions that will parse the native JSON number type to your own precision-safe alternative.
92
116
 
93
117
  > [!NOTE]
94
- > These utility functions will only work in environments that support `JSON.rawJSON`
95
- > https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/rawJSON#browser_compatibility
118
+ > These utility functions will only work in environments that support `JSON.rawJSON` > https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/rawJSON#browser_compatibility
96
119
 
97
120
  ```ts
98
- import { safeNumberParse, safeNumberStringify } from '@awsless/json';
99
- import { BigFloat, eq } from '@awsless/big-float';
121
+ import { safeNumberParse, safeNumberStringify } from '@awsless/json'
122
+ import { BigFloat, eq } from '@awsless/big-float'
123
+
124
+ const one = new BigFloat(1)
100
125
 
101
- const value = new BigFloat(1)
102
- const json = safeNumberStringify(ONE, {
126
+ const json = safeNumberStringify(one, {
103
127
  is: v => v instanceof BigFloat,
104
128
  stringify: v => v.toString(),
105
129
  })
@@ -110,7 +134,7 @@ const result = safeNumberParse('1', {
110
134
  parse: v => new BigFloat(v),
111
135
  })
112
136
 
113
- console.log(eq(value, result)) // true
137
+ console.log(eq(one, result)) // true
114
138
  ```
115
139
 
116
140
  ## Known Issue's
package/dist/index.cjs CHANGED
@@ -20,14 +20,28 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
20
20
  // src/index.ts
21
21
  var index_exports = {};
22
22
  __export(index_exports, {
23
+ $bigfloat: () => $bigfloat,
24
+ $bigint: () => $bigint,
25
+ $binary: () => $binary,
26
+ $date: () => $date,
27
+ $duration: () => $duration,
28
+ $infinity: () => $infinity,
29
+ $map: () => $map,
30
+ $mockdate: () => $mockdate,
31
+ $nan: () => $nan,
32
+ $regexp: () => $regexp,
33
+ $set: () => $set,
34
+ $undefined: () => $undefined,
35
+ $url: () => $url,
23
36
  createReplacer: () => createReplacer,
24
37
  createReviver: () => createReviver,
25
38
  createSafeNumberReplacer: () => createSafeNumberReplacer,
26
39
  createSafeNumberReviver: () => createSafeNumberReviver,
27
- parse: () => parse,
40
+ parse: () => parse2,
28
41
  patch: () => patch,
29
42
  safeNumberParse: () => safeNumberParse,
30
43
  safeNumberStringify: () => safeNumberStringify,
44
+ setGlobalTypes: () => setGlobalTypes,
31
45
  stringify: () => stringify,
32
46
  unpatch: () => unpatch
33
47
  });
@@ -37,7 +51,7 @@ module.exports = __toCommonJS(index_exports);
37
51
  var import_big_float = require("@awsless/big-float");
38
52
  var $bigfloat = {
39
53
  is: (v) => v instanceof import_big_float.BigFloat,
40
- parse: (v) => new import_big_float.BigFloat(v),
54
+ parse: (v) => (0, import_big_float.parse)(v),
41
55
  stringify: (v) => v.toString()
42
56
  };
43
57
 
@@ -145,11 +159,11 @@ var baseTypes = {
145
159
  };
146
160
 
147
161
  // src/parse.ts
148
- var parse = (json, types = {}) => {
162
+ var parse2 = (json, options) => {
149
163
  const replacements = [];
150
164
  const result = JSON.parse(
151
165
  json,
152
- createReviver(types, (target, key, value) => {
166
+ createReviver(options?.types, (target, key, value) => {
153
167
  replacements.push([target, key, value]);
154
168
  })
155
169
  );
@@ -174,12 +188,10 @@ var createReviver = (types = {}, registerReplacement) => {
174
188
  const stringified = original[typeName];
175
189
  if ("parse" in type) {
176
190
  return type.parse(stringified);
177
- } else if (registerReplacement) {
191
+ } else {
178
192
  const result = type.replace(stringified);
179
- registerReplacement(this, key, result);
193
+ registerReplacement?.(this, key, result);
180
194
  return result;
181
- } else {
182
- return type.replace(stringified);
183
195
  }
184
196
  }
185
197
  }
@@ -189,16 +201,19 @@ var createReviver = (types = {}, registerReplacement) => {
189
201
  };
190
202
 
191
203
  // src/stringify.ts
192
- var stringify = (value, types = {}) => {
193
- return JSON.stringify(value, createReplacer(types));
204
+ var stringify = (value, options) => {
205
+ return JSON.stringify(value, createReplacer(options));
194
206
  };
195
- var createReplacer = (types = {}) => {
196
- types = {
207
+ var createReplacer = (options) => {
208
+ const types = {
197
209
  ...baseTypes,
198
- ...types
210
+ ...options?.types
199
211
  };
200
212
  return function(key, value) {
201
213
  const original = this[key];
214
+ if (!options?.preserveUndefinedValues && key && typeof original === "undefined" && typeof this === "object" && !Array.isArray(this)) {
215
+ return value;
216
+ }
202
217
  for (const [typeName, type] of Object.entries(types)) {
203
218
  if (type.is(original)) {
204
219
  return {
@@ -212,12 +227,17 @@ var createReplacer = (types = {}) => {
212
227
 
213
228
  // src/patch.ts
214
229
  var patch = (value, types = {}) => {
215
- return parse(JSON.stringify(value), types);
230
+ return parse2(JSON.stringify(value), types);
216
231
  };
217
232
  var unpatch = (value, types = {}) => {
218
233
  return JSON.parse(stringify(value, types));
219
234
  };
220
235
 
236
+ // src/global.ts
237
+ var setGlobalTypes = (types) => {
238
+ Object.assign(baseTypes, types);
239
+ };
240
+
221
241
  // src/safe-number/parse.ts
222
242
  var safeNumberParse = (json, props) => {
223
243
  return JSON.parse(
@@ -248,8 +268,28 @@ var createSafeNumberReplacer = (props) => {
248
268
  return value;
249
269
  };
250
270
  };
271
+
272
+ // src/type/mockdate.ts
273
+ var $mockdate = {
274
+ is: (v) => typeof v === "object" && v !== null && "toISOString" in v && typeof v.toISOString === "function" && "getTime" in v && typeof v.getTime === "function" && "toUTCString" in v && typeof v.toUTCString === "function",
275
+ parse: (v) => new Date(v),
276
+ stringify: (v) => v.toISOString()
277
+ };
251
278
  // Annotate the CommonJS export names for ESM import in node:
252
279
  0 && (module.exports = {
280
+ $bigfloat,
281
+ $bigint,
282
+ $binary,
283
+ $date,
284
+ $duration,
285
+ $infinity,
286
+ $map,
287
+ $mockdate,
288
+ $nan,
289
+ $regexp,
290
+ $set,
291
+ $undefined,
292
+ $url,
253
293
  createReplacer,
254
294
  createReviver,
255
295
  createSafeNumberReplacer,
@@ -258,6 +298,7 @@ var createSafeNumberReplacer = (props) => {
258
298
  patch,
259
299
  safeNumberParse,
260
300
  safeNumberStringify,
301
+ setGlobalTypes,
261
302
  stringify,
262
303
  unpatch
263
304
  });
package/dist/index.d.cts CHANGED
@@ -1,3 +1,6 @@
1
+ import { BigFloat } from '@awsless/big-float';
2
+ import { Duration } from '@awsless/duration';
3
+
1
4
  type Serializable<I, O> = {
2
5
  is: (value: unknown) => boolean;
3
6
  stringify: (value: I) => O;
@@ -11,13 +14,22 @@ type SerializableTypes = Record<string, Serializable<any, any>>;
11
14
  declare const patch: (value: unknown, types?: SerializableTypes) => any;
12
15
  declare const unpatch: (value: unknown, types?: SerializableTypes) => any;
13
16
 
14
- declare const parse: (json: string, types?: SerializableTypes) => any;
17
+ type Options$1 = {
18
+ types?: SerializableTypes;
19
+ };
20
+ declare const parse: (json: string, options?: Options$1) => any;
15
21
  type Reviver$1 = (this: any, key: string, value: any) => any;
16
22
  declare const createReviver: (types?: SerializableTypes, registerReplacement?: (target: any, key: string, value: unknown) => void) => Reviver$1;
17
23
 
18
- declare const stringify: (value: unknown, types?: SerializableTypes) => string;
24
+ type Options = {
25
+ types?: SerializableTypes;
26
+ preserveUndefinedValues?: boolean;
27
+ };
28
+ declare const stringify: (value: unknown, options?: Options) => string;
19
29
  type Replacer$1 = (this: any, key: string, value: any) => any;
20
- declare const createReplacer: (types?: SerializableTypes) => Replacer$1;
30
+ declare const createReplacer: (options?: Options) => Replacer$1;
31
+
32
+ declare const setGlobalTypes: (types: SerializableTypes) => void;
21
33
 
22
34
  type Props$1 = {
23
35
  parse: (value: string) => unknown;
@@ -36,4 +48,30 @@ declare const safeNumberStringify: <T>(value: unknown, props: Props<T>) => strin
36
48
  type Replacer = (this: any, key: string, value: any) => any;
37
49
  declare const createSafeNumberReplacer: <T>(props: Props<T>) => Replacer;
38
50
 
39
- export { type Serializable, createReplacer, createReviver, createSafeNumberReplacer, createSafeNumberReviver, parse, patch, safeNumberParse, safeNumberStringify, stringify, unpatch };
51
+ declare const $bigfloat: Serializable<BigFloat, string>;
52
+
53
+ declare const $bigint: Serializable<bigint, string>;
54
+
55
+ declare const $binary: Serializable<Uint8Array, string>;
56
+
57
+ declare const $date: Serializable<Date, string>;
58
+
59
+ declare const $duration: Serializable<Duration, string>;
60
+
61
+ declare const $infinity: Serializable<typeof Infinity, 1 | 0>;
62
+
63
+ declare const $map: Serializable<Map<unknown, unknown>, [unknown, unknown][]>;
64
+
65
+ declare const $mockdate: Serializable<Date, string>;
66
+
67
+ declare const $nan: Serializable<typeof NaN, 0>;
68
+
69
+ declare const $regexp: Serializable<RegExp, [string, string]>;
70
+
71
+ declare const $set: Serializable<Set<unknown>, unknown[]>;
72
+
73
+ declare const $undefined: Serializable<undefined, 0>;
74
+
75
+ declare const $url: Serializable<URL, string>;
76
+
77
+ export { $bigfloat, $bigint, $binary, $date, $duration, $infinity, $map, $mockdate, $nan, $regexp, $set, $undefined, $url, type Serializable, createReplacer, createReviver, createSafeNumberReplacer, createSafeNumberReviver, parse, patch, safeNumberParse, safeNumberStringify, setGlobalTypes, stringify, unpatch };
package/dist/index.d.ts CHANGED
@@ -1,3 +1,6 @@
1
+ import { BigFloat } from '@awsless/big-float';
2
+ import { Duration } from '@awsless/duration';
3
+
1
4
  type Serializable<I, O> = {
2
5
  is: (value: unknown) => boolean;
3
6
  stringify: (value: I) => O;
@@ -11,13 +14,22 @@ type SerializableTypes = Record<string, Serializable<any, any>>;
11
14
  declare const patch: (value: unknown, types?: SerializableTypes) => any;
12
15
  declare const unpatch: (value: unknown, types?: SerializableTypes) => any;
13
16
 
14
- declare const parse: (json: string, types?: SerializableTypes) => any;
17
+ type Options$1 = {
18
+ types?: SerializableTypes;
19
+ };
20
+ declare const parse: (json: string, options?: Options$1) => any;
15
21
  type Reviver$1 = (this: any, key: string, value: any) => any;
16
22
  declare const createReviver: (types?: SerializableTypes, registerReplacement?: (target: any, key: string, value: unknown) => void) => Reviver$1;
17
23
 
18
- declare const stringify: (value: unknown, types?: SerializableTypes) => string;
24
+ type Options = {
25
+ types?: SerializableTypes;
26
+ preserveUndefinedValues?: boolean;
27
+ };
28
+ declare const stringify: (value: unknown, options?: Options) => string;
19
29
  type Replacer$1 = (this: any, key: string, value: any) => any;
20
- declare const createReplacer: (types?: SerializableTypes) => Replacer$1;
30
+ declare const createReplacer: (options?: Options) => Replacer$1;
31
+
32
+ declare const setGlobalTypes: (types: SerializableTypes) => void;
21
33
 
22
34
  type Props$1 = {
23
35
  parse: (value: string) => unknown;
@@ -36,4 +48,30 @@ declare const safeNumberStringify: <T>(value: unknown, props: Props<T>) => strin
36
48
  type Replacer = (this: any, key: string, value: any) => any;
37
49
  declare const createSafeNumberReplacer: <T>(props: Props<T>) => Replacer;
38
50
 
39
- export { type Serializable, createReplacer, createReviver, createSafeNumberReplacer, createSafeNumberReviver, parse, patch, safeNumberParse, safeNumberStringify, stringify, unpatch };
51
+ declare const $bigfloat: Serializable<BigFloat, string>;
52
+
53
+ declare const $bigint: Serializable<bigint, string>;
54
+
55
+ declare const $binary: Serializable<Uint8Array, string>;
56
+
57
+ declare const $date: Serializable<Date, string>;
58
+
59
+ declare const $duration: Serializable<Duration, string>;
60
+
61
+ declare const $infinity: Serializable<typeof Infinity, 1 | 0>;
62
+
63
+ declare const $map: Serializable<Map<unknown, unknown>, [unknown, unknown][]>;
64
+
65
+ declare const $mockdate: Serializable<Date, string>;
66
+
67
+ declare const $nan: Serializable<typeof NaN, 0>;
68
+
69
+ declare const $regexp: Serializable<RegExp, [string, string]>;
70
+
71
+ declare const $set: Serializable<Set<unknown>, unknown[]>;
72
+
73
+ declare const $undefined: Serializable<undefined, 0>;
74
+
75
+ declare const $url: Serializable<URL, string>;
76
+
77
+ export { $bigfloat, $bigint, $binary, $date, $duration, $infinity, $map, $mockdate, $nan, $regexp, $set, $undefined, $url, type Serializable, createReplacer, createReviver, createSafeNumberReplacer, createSafeNumberReviver, parse, patch, safeNumberParse, safeNumberStringify, setGlobalTypes, stringify, unpatch };
package/dist/index.js CHANGED
@@ -1,8 +1,8 @@
1
1
  // src/type/bigfloat.ts
2
- import { BigFloat } from "@awsless/big-float";
2
+ import { BigFloat, parse } from "@awsless/big-float";
3
3
  var $bigfloat = {
4
4
  is: (v) => v instanceof BigFloat,
5
- parse: (v) => new BigFloat(v),
5
+ parse: (v) => parse(v),
6
6
  stringify: (v) => v.toString()
7
7
  };
8
8
 
@@ -110,11 +110,11 @@ var baseTypes = {
110
110
  };
111
111
 
112
112
  // src/parse.ts
113
- var parse = (json, types = {}) => {
113
+ var parse2 = (json, options) => {
114
114
  const replacements = [];
115
115
  const result = JSON.parse(
116
116
  json,
117
- createReviver(types, (target, key, value) => {
117
+ createReviver(options?.types, (target, key, value) => {
118
118
  replacements.push([target, key, value]);
119
119
  })
120
120
  );
@@ -139,12 +139,10 @@ var createReviver = (types = {}, registerReplacement) => {
139
139
  const stringified = original[typeName];
140
140
  if ("parse" in type) {
141
141
  return type.parse(stringified);
142
- } else if (registerReplacement) {
142
+ } else {
143
143
  const result = type.replace(stringified);
144
- registerReplacement(this, key, result);
144
+ registerReplacement?.(this, key, result);
145
145
  return result;
146
- } else {
147
- return type.replace(stringified);
148
146
  }
149
147
  }
150
148
  }
@@ -154,16 +152,19 @@ var createReviver = (types = {}, registerReplacement) => {
154
152
  };
155
153
 
156
154
  // src/stringify.ts
157
- var stringify = (value, types = {}) => {
158
- return JSON.stringify(value, createReplacer(types));
155
+ var stringify = (value, options) => {
156
+ return JSON.stringify(value, createReplacer(options));
159
157
  };
160
- var createReplacer = (types = {}) => {
161
- types = {
158
+ var createReplacer = (options) => {
159
+ const types = {
162
160
  ...baseTypes,
163
- ...types
161
+ ...options?.types
164
162
  };
165
163
  return function(key, value) {
166
164
  const original = this[key];
165
+ if (!options?.preserveUndefinedValues && key && typeof original === "undefined" && typeof this === "object" && !Array.isArray(this)) {
166
+ return value;
167
+ }
167
168
  for (const [typeName, type] of Object.entries(types)) {
168
169
  if (type.is(original)) {
169
170
  return {
@@ -177,12 +178,17 @@ var createReplacer = (types = {}) => {
177
178
 
178
179
  // src/patch.ts
179
180
  var patch = (value, types = {}) => {
180
- return parse(JSON.stringify(value), types);
181
+ return parse2(JSON.stringify(value), types);
181
182
  };
182
183
  var unpatch = (value, types = {}) => {
183
184
  return JSON.parse(stringify(value, types));
184
185
  };
185
186
 
187
+ // src/global.ts
188
+ var setGlobalTypes = (types) => {
189
+ Object.assign(baseTypes, types);
190
+ };
191
+
186
192
  // src/safe-number/parse.ts
187
193
  var safeNumberParse = (json, props) => {
188
194
  return JSON.parse(
@@ -213,15 +219,36 @@ var createSafeNumberReplacer = (props) => {
213
219
  return value;
214
220
  };
215
221
  };
222
+
223
+ // src/type/mockdate.ts
224
+ var $mockdate = {
225
+ is: (v) => typeof v === "object" && v !== null && "toISOString" in v && typeof v.toISOString === "function" && "getTime" in v && typeof v.getTime === "function" && "toUTCString" in v && typeof v.toUTCString === "function",
226
+ parse: (v) => new Date(v),
227
+ stringify: (v) => v.toISOString()
228
+ };
216
229
  export {
230
+ $bigfloat,
231
+ $bigint,
232
+ $binary,
233
+ $date,
234
+ $duration,
235
+ $infinity,
236
+ $map,
237
+ $mockdate,
238
+ $nan,
239
+ $regexp,
240
+ $set,
241
+ $undefined,
242
+ $url,
217
243
  createReplacer,
218
244
  createReviver,
219
245
  createSafeNumberReplacer,
220
246
  createSafeNumberReviver,
221
- parse,
247
+ parse2 as parse,
222
248
  patch,
223
249
  safeNumberParse,
224
250
  safeNumberStringify,
251
+ setGlobalTypes,
225
252
  stringify,
226
253
  unpatch
227
254
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@awsless/json",
3
- "version": "0.0.9",
3
+ "version": "0.0.11",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "keywords": [
@@ -41,12 +41,12 @@
41
41
  }
42
42
  },
43
43
  "peerDependencies": {
44
- "@awsless/big-float": "^0.0.6",
45
- "@awsless/duration": "^0.0.3"
44
+ "@awsless/big-float": "^0.1.5",
45
+ "@awsless/duration": "^0.0.4"
46
46
  },
47
47
  "devDependencies": {
48
- "@awsless/big-float": "^0.0.6",
49
- "@awsless/duration": "^0.0.3"
48
+ "@awsless/big-float": "^0.1.5",
49
+ "@awsless/duration": "^0.0.4"
50
50
  },
51
51
  "scripts": {
52
52
  "test": "pnpm code test",