1
- import { flattie } from 'flattie'
2
- import { nestie } from 'nestie'
3
-
4
1
export const SkipSerialization = Symbol (
5
2
'ba1894562a5b3e00de68679b5e01fed0de0a0aac6da459729c2f4d665d928ccc' ,
6
3
)
@@ -9,47 +6,65 @@ type SerializationRules = <T>(
9
6
unserializedValue : T ,
10
7
) => string | number | boolean | null | undefined | typeof SkipSerialization
11
8
12
- export function serialize < R > ( data : Record < string , unknown > , rules : SerializationRules ) {
13
- if ( hasCircularReferences ( data ) ) {
14
- throw new Error ( 'Neither Next.js nor obj-serialize supports circular references.' )
15
- }
9
+ function detectCircularReferences (
10
+ object : unknown ,
11
+ seenObjects = new Set < unknown > ( ) ,
12
+ ) : boolean {
13
+ if ( object !== null && typeof object === 'object' ) {
14
+ if ( seenObjects . has ( object ) ) return true
16
15
17
- return nestie (
18
- Object . entries ( flattie ( data ) as Record < string , unknown > ) . reduce (
19
- ( newData , [ key , value ] ) => {
20
- let oldValue = value
21
- const serialized = rules ( value )
22
-
23
- if ( typeof serialized === typeof SkipSerialization ) {
24
- newData [ key ] = oldValue
25
- } else if ( serialized !== oldValue ) {
26
- newData [ key ] = serialized
27
- }
28
-
29
- return newData
30
- } ,
31
- { } as Record < string , unknown > ,
32
- ) ,
33
- ) as Record < string , R >
16
+ seenObjects . add ( object )
17
+ for ( const key of Object . keys ( object as object ) ) {
18
+ if (
19
+ detectCircularReferences ( ( object as { [ key : string ] : unknown } ) [ key ] , seenObjects )
20
+ )
21
+ return true
22
+ }
23
+
24
+ seenObjects . delete ( object )
25
+ }
26
+ return false
34
27
}
35
28
36
29
function hasCircularReferences ( object : unknown ) : boolean {
37
- const seenObjects = new Set < unknown > ( )
30
+ return detectCircularReferences ( object )
31
+ }
38
32
39
- const detect = ( object : unknown ) : boolean = > {
40
- if ( object !== null && typeof object === ' object' ) {
41
- if ( seenObjects . has ( object ) ) return true
33
+ function isPlainObject ( obj : unknown ) : obj is Record < string , unknown > {
34
+ return Object . prototype . toString . call ( obj ) === '[ object Object]'
35
+ }
42
36
43
- seenObjects . add ( object )
37
+ function processValue ( value : unknown , rules : SerializationRules ) : unknown {
38
+ if ( Array . isArray ( value ) ) {
39
+ return value
40
+ . map ( ( item ) => processValue ( item , rules ) )
41
+ . filter ( ( item ) => item !== SkipSerialization )
42
+ } else if ( typeof value === 'object' && value !== null && isPlainObject ( value ) ) {
43
+ const processedObject = Object . fromEntries (
44
+ Object . entries ( value )
45
+ . map ( ( [ key , val ] ) => [ key , processValue ( val , rules ) ] )
46
+ . filter ( ( [ , val ] ) => val !== SkipSerialization ) ,
47
+ )
44
48
45
- for ( const key of Object . keys ( object as object ) )
46
- if ( detect ( ( object as { [ key : string ] : unknown } ) [ key ] ) ) return true
49
+ if ( Object . keys ( processedObject ) . length === 0 && ! rules ( value ) )
50
+ return SkipSerialization
47
51
48
- seenObjects . delete ( object )
49
- }
52
+ return processedObject
53
+ } else {
54
+ const serialized = rules ( value )
55
+ if ( serialized === SkipSerialization ) return value
50
56
51
- return false
57
+ return serialized
58
+ }
59
+ }
60
+
61
+ export function serialize < V > ( data : unknown , rules : SerializationRules ) {
62
+ if ( hasCircularReferences ( data ) ) {
63
+ throw new Error ( 'Neither Next.js nor obj-serialize supports circular references.' )
52
64
}
53
65
54
- return detect ( object )
66
+ const result = processValue ( data , rules )
67
+ return result === SkipSerialization
68
+ ? ( { } as Record < string , V > )
69
+ : ( result as Record < string , V > )
55
70
}
0 commit comments