@@ -8,99 +8,6 @@ interface ExtendedError extends Error {
8
8
[ key : string ] : any ;
9
9
}
10
10
11
- /**
12
- * Transforms Error object into an object literal with all it's attributes
13
- * attached to it.
14
- *
15
- * Based on: https://github.com/ftlabs/js-abbreviate/blob/fa709e5f139e7770a71827b1893f22418097fbda/index.js#L95-L106
16
- *
17
- * @param error An Error containing all relevant information
18
- * @returns An object with all error properties
19
- */
20
- function objectifyError ( error : ExtendedError ) : object {
21
- // These properties are implemented as magical getters and don't show up in `for-in` loop
22
- const err : {
23
- stack : string | undefined ;
24
- message : string ;
25
- name : string ;
26
- [ key : string ] : any ;
27
- } = {
28
- message : error . message ,
29
- name : error . name ,
30
- stack : error . stack ,
31
- } ;
32
-
33
- for ( const i in error ) {
34
- if ( Object . prototype . hasOwnProperty . call ( error , i ) ) {
35
- err [ i ] = error [ i ] ;
36
- }
37
- }
38
-
39
- return err ;
40
- }
41
-
42
- const NAN_VALUE = '[NaN]' ;
43
- const UNDEFINED_VALUE = '[undefined]' ;
44
-
45
- /**
46
- * Serializer function used as 2nd argument to JSON.serialize in `serialize()` util function.
47
- */
48
- function serializer ( ) : ( key : string , value : any ) => any {
49
- const stack : any [ ] = [ ] ;
50
- const keys : string [ ] = [ ] ;
51
- const cycleReplacer = ( _ : string , value : any ) => {
52
- if ( stack [ 0 ] === value ) {
53
- return '[Circular ~]' ;
54
- }
55
- return `[Circular ~.${ keys . slice ( 0 , stack . indexOf ( value ) ) . join ( '.' ) } ]` ;
56
- } ;
57
-
58
- return function ( this : any , key : string , value : any ) : any {
59
- let currentValue : any = value ;
60
-
61
- // NaN and undefined are not JSON.parseable, but we want to preserve this information
62
- if ( isNaN ( value ) ) {
63
- currentValue = NAN_VALUE ;
64
- } else if ( isUndefined ( value ) ) {
65
- currentValue = UNDEFINED_VALUE ;
66
- }
67
-
68
- if ( stack . length > 0 ) {
69
- const thisPos = stack . indexOf ( this ) ;
70
-
71
- if ( thisPos !== - 1 ) {
72
- stack . splice ( thisPos + 1 ) ;
73
- keys . splice ( thisPos , Infinity , key ) ;
74
- } else {
75
- stack . push ( this ) ;
76
- keys . push ( key ) ;
77
- }
78
-
79
- if ( stack . indexOf ( currentValue ) !== - 1 ) {
80
- currentValue = cycleReplacer . call ( this , key , currentValue ) ;
81
- }
82
- } else {
83
- stack . push ( currentValue ) ;
84
- }
85
-
86
- return currentValue instanceof Error ? objectifyError ( currentValue ) : currentValue ;
87
- } ;
88
- }
89
-
90
- /**
91
- * Reviver function used as 2nd argument to JSON.parse in `deserialize()` util function.
92
- */
93
- function reviver ( _key : string , value : any ) : any {
94
- // NaN and undefined are not JSON.parseable, but we want to preserve this information
95
- if ( value === NAN_VALUE ) {
96
- return NaN ;
97
- }
98
- if ( value === UNDEFINED_VALUE ) {
99
- return undefined ;
100
- }
101
- return value ;
102
- }
103
-
104
11
/**
105
12
* Serializes the given object into a string.
106
13
* Like JSON.stringify, but doesn't throw on circular references.
@@ -114,7 +21,7 @@ function reviver(_key: string, value: any): any {
114
21
* @returns A string containing the serialized object.
115
22
*/
116
23
export function serialize < T > ( object : T ) : string {
117
- return JSON . stringify ( object , serializer ( ) ) ;
24
+ return JSON . stringify ( object ) ;
118
25
}
119
26
120
27
/**
@@ -125,7 +32,7 @@ export function serialize<T>(object: T): string {
125
32
* @returns The deserialized object.
126
33
*/
127
34
export function deserialize < T > ( str : string ) : T {
128
- return JSON . parse ( str , reviver ) as T ;
35
+ return JSON . parse ( str ) as T ;
129
36
}
130
37
131
38
/**
@@ -321,3 +228,141 @@ export function assign(target: any, ...args: any[]): object {
321
228
322
229
return to ;
323
230
}
231
+
232
+ /**
233
+ * Transforms Error object into an object literal with all it's attributes
234
+ * attached to it.
235
+ *
236
+ * Based on: https://github.com/ftlabs/js-abbreviate/blob/fa709e5f139e7770a71827b1893f22418097fbda/index.js#L95-L106
237
+ *
238
+ * @param error An Error containing all relevant information
239
+ * @returns An object with all error properties
240
+ */
241
+ function objectifyError ( error : ExtendedError ) : object {
242
+ // These properties are implemented as magical getters and don't show up in `for-in` loop
243
+ const err : {
244
+ stack : string | undefined ;
245
+ message : string ;
246
+ name : string ;
247
+ [ key : string ] : any ;
248
+ } = {
249
+ message : error . message ,
250
+ name : error . name ,
251
+ stack : error . stack ,
252
+ } ;
253
+
254
+ for ( const i in error ) {
255
+ if ( Object . prototype . hasOwnProperty . call ( error , i ) ) {
256
+ err [ i ] = error [ i ] ;
257
+ }
258
+ }
259
+
260
+ return err ;
261
+ }
262
+
263
+ /**
264
+ * standardizeValue()
265
+ *
266
+ * translates undefined/NaN values to "[undefined]"/"[NaN]" respectively,
267
+ * serializes Error objects
268
+ * filter global objects
269
+ */
270
+ function standardizeValue ( value : any , key : any ) : any {
271
+ if ( key === 'domain' && typeof value === 'object' && ( value as { _events : any } ) . _events ) {
272
+ return '[Domain]' ;
273
+ }
274
+
275
+ if ( key === 'domainEmitter' ) {
276
+ return '[DomainEmitter]' ;
277
+ }
278
+
279
+ if ( typeof ( global as any ) !== 'undefined' && value === global ) {
280
+ return '[Global]' ;
281
+ }
282
+
283
+ if ( typeof ( window as any ) !== 'undefined' && value === window ) {
284
+ return '[Window]' ;
285
+ }
286
+
287
+ if ( typeof ( document as any ) !== 'undefined' && value === document ) {
288
+ return '[Document]' ;
289
+ }
290
+
291
+ if ( value instanceof Date ) {
292
+ return `[Date] ${ value } ` ;
293
+ }
294
+
295
+ if ( value instanceof Error ) {
296
+ return objectifyError ( value ) ;
297
+ }
298
+
299
+ if ( isNaN ( value ) ) {
300
+ return '[NaN]' ;
301
+ }
302
+
303
+ if ( isUndefined ( value ) ) {
304
+ return '[undefined]' ;
305
+ }
306
+
307
+ if ( typeof value === 'function' ) {
308
+ return `[Function] ${ ( value as ( ) => void ) . name || '<unknown-function-name>' } ` ;
309
+ }
310
+
311
+ return value ;
312
+ }
313
+
314
+ /**
315
+ * standardizer()
316
+ *
317
+ * Remove circular references,
318
+ * translates undefined/NaN values to "[undefined]"/"[NaN]" respectively,
319
+ * and takes care of Error objects serialization
320
+ */
321
+ function standardizer ( ) : ( key : string , value : any ) => any {
322
+ const stack : any [ ] = [ ] ;
323
+ const keys : string [ ] = [ ] ;
324
+
325
+ /** recursive */
326
+ function cycleStandardizer ( _key : string , value : any ) : any {
327
+ if ( stack [ 0 ] === value ) {
328
+ return '[Circular ~]' ;
329
+ }
330
+ return `[Circular ~.${ keys . slice ( 0 , stack . indexOf ( value ) ) . join ( '.' ) } ]` ;
331
+ }
332
+
333
+ return function ( this : any , key : string , value : any ) : any {
334
+ if ( stack . length > 0 ) {
335
+ const thisPos = stack . indexOf ( this ) ;
336
+
337
+ if ( thisPos === - 1 ) {
338
+ stack . push ( this ) ;
339
+ keys . push ( key ) ;
340
+ } else {
341
+ stack . splice ( thisPos + 1 ) ;
342
+ keys . splice ( thisPos , Infinity , key ) ;
343
+ }
344
+
345
+ if ( stack . indexOf ( value ) !== - 1 ) {
346
+ // tslint:disable-next-line:no-parameter-reassignment
347
+ value = cycleStandardizer . call ( this , key , value ) ;
348
+ }
349
+ } else {
350
+ stack . push ( value ) ;
351
+ }
352
+
353
+ return standardizeValue ( value , key ) ;
354
+ } ;
355
+ }
356
+
357
+ /**
358
+ * safeNormalize()
359
+ *
360
+ * Creates a copy of the input by applying standardizer function on it and parsing it back to unify the data
361
+ */
362
+ export function safeNormalize ( input : any ) : any {
363
+ try {
364
+ return JSON . parse ( JSON . stringify ( input , standardizer ( ) ) ) ;
365
+ } catch ( _oO ) {
366
+ return '**non-serializable**' ;
367
+ }
368
+ }
0 commit comments