1
+ import { Principal } from "@dfinity/principal" ;
2
+ import { nonNullish } from "@dfinity/utils" ;
3
+
4
+ const JSON_KEY_BIGINT = "__bigint__" ;
5
+ const JSON_KEY_PRINCIPAL = "__principal__" ;
6
+ const JSON_KEY_UINT8ARRAY = "__uint8array__" ;
7
+ const JSON_KEY_ARRAYBUFFER = "__arraybuffer__" ;
8
+
9
+ // Helper function to convert ArrayBuffer to Base64
10
+ const arrayBufferToBase64 = ( buffer : ArrayBuffer ) : string => {
11
+ let binary = "" ;
12
+ const bytes = new Uint8Array ( buffer ) ;
13
+ const len = bytes . byteLength ;
14
+ for ( let i = 0 ; i < len ; i ++ ) {
15
+ binary += String . fromCharCode ( bytes [ i ] ) ;
16
+ }
17
+ return btoa ( binary ) ;
18
+ } ;
19
+
20
+ // Helper function to convert Base64 to ArrayBuffer
21
+ const base64ToArrayBuffer = ( base64 : string ) : ArrayBuffer => {
22
+ const binary_string = atob ( base64 ) ;
23
+ const len = binary_string . length ;
24
+ const bytes = new Uint8Array ( len ) ;
25
+ for ( let i = 0 ; i < len ; i ++ ) {
26
+ bytes [ i ] = binary_string . charCodeAt ( i ) ;
27
+ }
28
+ return bytes . buffer ;
29
+ } ;
30
+
31
+ /**
32
+ * A custom replacer for `JSON.stringify` that converts specific types not natively supported
33
+ * by the API into JSON-compatible formats.
34
+ *
35
+ * Supported conversions:
36
+ * - `BigInt` → `{ "__bigint__": string }`
37
+ * - `Principal` → `{ "__principal__": string }`
38
+ * - `Uint8Array` → `{ "__uint8array__": number[] }`
39
+ * - `ArrayBuffer` → `{ "__arraybuffer__": string }` (base64 encoded)
40
+ *
41
+ * @param {string } _key - Ignored. Only provided for API compatibility.
42
+ * @param {unknown } value - The value to transform before stringification.
43
+ * @returns {unknown } The transformed value if it matches a known type, otherwise the original value.
44
+ */
45
+ export const jsonReplacer = ( _key : string , value : unknown ) : unknown => {
46
+ if ( typeof value === "bigint" ) {
47
+ return { [ JSON_KEY_BIGINT ] : `${ value } ` } ;
48
+ }
49
+
50
+ if ( nonNullish ( value ) && value instanceof Principal ) {
51
+ return { [ JSON_KEY_PRINCIPAL ] : value . toText ( ) } ;
52
+ }
53
+
54
+ if ( nonNullish ( value ) && value instanceof Uint8Array ) {
55
+ return { [ JSON_KEY_UINT8ARRAY ] : Array . from ( value ) } ;
56
+ }
57
+
58
+ if ( nonNullish ( value ) && value instanceof ArrayBuffer ) {
59
+ return { [ JSON_KEY_ARRAYBUFFER ] : arrayBufferToBase64 ( value ) } ;
60
+ }
61
+
62
+ return value ;
63
+ } ;
64
+
65
+ /**
66
+ * A custom reviver for `JSON.parse` that reconstructs specific types from their JSON-encoded representations.
67
+ *
68
+ * This reverses the transformations applied by `jsonReplacer`, restoring the original types.
69
+ *
70
+ * Supported conversions:
71
+ * - `{ "__bigint__": string }` → `BigInt`
72
+ * - `{ "__principal__": string }` → `Principal`
73
+ * - `{ "__uint8array__": number[] }` → `Uint8Array`
74
+ * - `{ "__arraybuffer__": string }` → `ArrayBuffer` (from base64)
75
+ *
76
+ * @param {string } _key - Ignored but provided for API compatibility.
77
+ * @param {unknown } value - The parsed value to transform.
78
+ * @returns {unknown } The reconstructed value if it matches a known type, otherwise the original value.
79
+ */
80
+ export const jsonReviver = ( _key : string , value : unknown ) : unknown => {
81
+ const mapValue = < T > ( key : string ) : T => ( value as Record < string , T > ) [ key ] ;
82
+
83
+ if (
84
+ nonNullish ( value ) &&
85
+ typeof value === "object" &&
86
+ JSON_KEY_BIGINT in value
87
+ ) {
88
+ return BigInt ( mapValue ( JSON_KEY_BIGINT ) ) ;
89
+ }
90
+
91
+ if (
92
+ nonNullish ( value ) &&
93
+ typeof value === "object" &&
94
+ JSON_KEY_PRINCIPAL in value
95
+ ) {
96
+ return Principal . fromText ( mapValue ( JSON_KEY_PRINCIPAL ) ) ;
97
+ }
98
+
99
+ if (
100
+ nonNullish ( value ) &&
101
+ typeof value === "object" &&
102
+ JSON_KEY_UINT8ARRAY in value
103
+ ) {
104
+ return Uint8Array . from ( mapValue ( JSON_KEY_UINT8ARRAY ) ) ;
105
+ }
106
+
107
+ if (
108
+ nonNullish ( value ) &&
109
+ typeof value === "object" &&
110
+ JSON_KEY_ARRAYBUFFER in value
111
+ ) {
112
+ return base64ToArrayBuffer ( mapValue ( JSON_KEY_ARRAYBUFFER ) ) ;
113
+ }
114
+
115
+ return value ;
116
+ } ;
0 commit comments