@@ -8,99 +8,6 @@ interface ExtendedError extends Error {
88 [ key : string ] : any ;
99}
1010
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-
10411/**
10512 * Serializes the given object into a string.
10613 * Like JSON.stringify, but doesn't throw on circular references.
@@ -114,7 +21,7 @@ function reviver(_key: string, value: any): any {
11421 * @returns A string containing the serialized object.
11522 */
11623export function serialize < T > ( object : T ) : string {
117- return JSON . stringify ( object , serializer ( ) ) ;
24+ return JSON . stringify ( object ) ;
11825}
11926
12027/**
@@ -125,7 +32,7 @@ export function serialize<T>(object: T): string {
12532 * @returns The deserialized object.
12633 */
12734export function deserialize < T > ( str : string ) : T {
128- return JSON . parse ( str , reviver ) as T ;
35+ return JSON . parse ( str ) as T ;
12936}
13037
13138/**
@@ -321,3 +228,141 @@ export function assign(target: any, ...args: any[]): object {
321228
322229 return to ;
323230}
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