@@ -23,6 +23,20 @@ export interface ConfigManagerOptions<TData> {
23
23
* ⚠️ Never decrement this number and optimally don't skip any numbers either!
24
24
*/
25
25
formatVersion : number ;
26
+ /**
27
+ * Function to use to encode the data prior to saving it in persistent storage.
28
+ * The input data is a serialized JSON object.
29
+ *
30
+ * You can make use of UserUtils' [`compress()`](https://github.com/Sv443-Network/UserUtils?tab=readme-ov-file#compress) function here to make the data use up less space at the cost of a little bit of performance.
31
+ */
32
+ encodeData ?: ( data : string ) => string | Promise < string > ,
33
+ /**
34
+ * Function to use to decode the data after reading it from persistent storage.
35
+ * The result should be a valid JSON object.
36
+ *
37
+ * You can make use of UserUtils' [`decompress()`](https://github.com/Sv443-Network/UserUtils?tab=readme-ov-file#decompress) function here to make the data use up less space at the cost of a little bit of performance.
38
+ */
39
+ decodeData ?: ( data : string ) => string | Promise < string > ,
26
40
/**
27
41
* A dictionary of functions that can be used to migrate data from older versions of the configuration to newer ones.
28
42
* The keys of the dictionary should be the format version that the functions can migrate to, from the previous whole integer value.
@@ -48,6 +62,8 @@ export class ConfigManager<TData = any> {
48
62
public readonly defaultConfig : TData ;
49
63
private cachedConfig : TData ;
50
64
private migrations ?: ConfigMigrationsDict ;
65
+ private encodeData : ConfigManagerOptions < TData > [ "encodeData" ] ;
66
+ private decodeData : ConfigManagerOptions < TData > [ "decodeData" ] ;
51
67
52
68
/**
53
69
* Creates an instance of ConfigManager to manage a user configuration that is cached in memory and persistently saved across sessions.
@@ -65,6 +81,8 @@ export class ConfigManager<TData = any> {
65
81
this . defaultConfig = options . defaultConfig ;
66
82
this . cachedConfig = options . defaultConfig ;
67
83
this . migrations = options . migrations ;
84
+ this . encodeData = options . encodeData ;
85
+ this . decodeData = options . decodeData ;
68
86
}
69
87
70
88
/**
@@ -85,12 +103,12 @@ export class ConfigManager<TData = any> {
85
103
if ( isNaN ( gmFmtVer ) )
86
104
await GM . setValue ( `_uucfgver-${ this . id } ` , gmFmtVer = this . formatVersion ) ;
87
105
88
- let parsed = JSON . parse ( gmData ) ;
106
+ let parsed = await this . deserializeData ( gmData ) ;
89
107
90
108
if ( gmFmtVer < this . formatVersion && this . migrations )
91
109
parsed = await this . runMigrations ( parsed , gmFmtVer ) ;
92
110
93
- return this . cachedConfig = typeof parsed === "object" ? parsed : undefined ;
111
+ return this . cachedConfig = parsed ;
94
112
}
95
113
catch ( err ) {
96
114
await this . saveDefaultData ( ) ;
@@ -111,7 +129,7 @@ export class ConfigManager<TData = any> {
111
129
this . cachedConfig = data ;
112
130
return new Promise < void > ( async ( resolve ) => {
113
131
await Promise . all ( [
114
- GM . setValue ( `_uucfg-${ this . id } ` , JSON . stringify ( data ) ) ,
132
+ GM . setValue ( `_uucfg-${ this . id } ` , await this . serializeData ( data ) ) ,
115
133
GM . setValue ( `_uucfgver-${ this . id } ` , this . formatVersion ) ,
116
134
] ) ;
117
135
resolve ( ) ;
@@ -123,7 +141,7 @@ export class ConfigManager<TData = any> {
123
141
this . cachedConfig = this . defaultConfig ;
124
142
return new Promise < void > ( async ( resolve ) => {
125
143
await Promise . all ( [
126
- GM . setValue ( `_uucfg-${ this . id } ` , JSON . stringify ( this . defaultConfig ) ) ,
144
+ GM . setValue ( `_uucfg-${ this . id } ` , await this . serializeData ( this . defaultConfig ) ) ,
127
145
GM . setValue ( `_uucfgver-${ this . id } ` , this . formatVersion ) ,
128
146
] ) ;
129
147
resolve ( ) ;
@@ -170,13 +188,34 @@ export class ConfigManager<TData = any> {
170
188
}
171
189
172
190
await Promise . all ( [
173
- GM . setValue ( `_uucfg-${ this . id } ` , JSON . stringify ( newData ) ) ,
191
+ GM . setValue ( `_uucfg-${ this . id } ` , await this . serializeData ( newData ) ) ,
174
192
GM . setValue ( `_uucfgver-${ this . id } ` , lastFmtVer ) ,
175
193
] ) ;
176
194
177
195
return newData as TData ;
178
196
}
179
197
198
+ /** Serializes the data using the optional this.encodeData() and returns it as a string */
199
+ private async serializeData ( data : TData ) {
200
+ const stringData = JSON . stringify ( data ) ;
201
+ if ( ! this . encodeData )
202
+ return stringData ;
203
+
204
+ const encRes = this . encodeData ( stringData ) ;
205
+ if ( encRes instanceof Promise )
206
+ return await encRes ;
207
+ return encRes ;
208
+ }
209
+
210
+ /** Deserializes the data using the optional this.decodeData() and returns it as a JSON object */
211
+ private async deserializeData ( data : string ) {
212
+ let decRes = this . decodeData ? this . decodeData ( data ) : undefined ;
213
+ if ( decRes instanceof Promise )
214
+ decRes = await decRes ;
215
+
216
+ return JSON . parse ( decRes ?? data ) as TData ;
217
+ }
218
+
180
219
/** Copies a JSON-compatible object and loses its internal references */
181
220
private deepCopy < T > ( obj : T ) : T {
182
221
return JSON . parse ( JSON . stringify ( obj ) ) ;
0 commit comments