Skip to content

Commit ca1b596

Browse files
committed
feat: CfgMgr data compression methods
1 parent c980ff3 commit ca1b596

File tree

2 files changed

+49
-5
lines changed

2 files changed

+49
-5
lines changed

.changeset/happy-humans-tell.md

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@sv443-network/userutils": minor
3+
---
4+
5+
Added `encodeData()` and `decodeData()` to the ConfigManager options to allow for easy data compression

lib/ConfigManager.ts

+44-5
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,20 @@ export interface ConfigManagerOptions<TData> {
2323
* ⚠️ Never decrement this number and optimally don't skip any numbers either!
2424
*/
2525
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>,
2640
/**
2741
* A dictionary of functions that can be used to migrate data from older versions of the configuration to newer ones.
2842
* 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> {
4862
public readonly defaultConfig: TData;
4963
private cachedConfig: TData;
5064
private migrations?: ConfigMigrationsDict;
65+
private encodeData: ConfigManagerOptions<TData>["encodeData"];
66+
private decodeData: ConfigManagerOptions<TData>["decodeData"];
5167

5268
/**
5369
* 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> {
6581
this.defaultConfig = options.defaultConfig;
6682
this.cachedConfig = options.defaultConfig;
6783
this.migrations = options.migrations;
84+
this.encodeData = options.encodeData;
85+
this.decodeData = options.decodeData;
6886
}
6987

7088
/**
@@ -85,12 +103,12 @@ export class ConfigManager<TData = any> {
85103
if(isNaN(gmFmtVer))
86104
await GM.setValue(`_uucfgver-${this.id}`, gmFmtVer = this.formatVersion);
87105

88-
let parsed = JSON.parse(gmData);
106+
let parsed = await this.deserializeData(gmData);
89107

90108
if(gmFmtVer < this.formatVersion && this.migrations)
91109
parsed = await this.runMigrations(parsed, gmFmtVer);
92110

93-
return this.cachedConfig = typeof parsed === "object" ? parsed : undefined;
111+
return this.cachedConfig = parsed;
94112
}
95113
catch(err) {
96114
await this.saveDefaultData();
@@ -111,7 +129,7 @@ export class ConfigManager<TData = any> {
111129
this.cachedConfig = data;
112130
return new Promise<void>(async (resolve) => {
113131
await Promise.all([
114-
GM.setValue(`_uucfg-${this.id}`, JSON.stringify(data)),
132+
GM.setValue(`_uucfg-${this.id}`, await this.serializeData(data)),
115133
GM.setValue(`_uucfgver-${this.id}`, this.formatVersion),
116134
]);
117135
resolve();
@@ -123,7 +141,7 @@ export class ConfigManager<TData = any> {
123141
this.cachedConfig = this.defaultConfig;
124142
return new Promise<void>(async (resolve) => {
125143
await Promise.all([
126-
GM.setValue(`_uucfg-${this.id}`, JSON.stringify(this.defaultConfig)),
144+
GM.setValue(`_uucfg-${this.id}`, await this.serializeData(this.defaultConfig)),
127145
GM.setValue(`_uucfgver-${this.id}`, this.formatVersion),
128146
]);
129147
resolve();
@@ -170,13 +188,34 @@ export class ConfigManager<TData = any> {
170188
}
171189

172190
await Promise.all([
173-
GM.setValue(`_uucfg-${this.id}`, JSON.stringify(newData)),
191+
GM.setValue(`_uucfg-${this.id}`, await this.serializeData(newData)),
174192
GM.setValue(`_uucfgver-${this.id}`, lastFmtVer),
175193
]);
176194

177195
return newData as TData;
178196
}
179197

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+
180219
/** Copies a JSON-compatible object and loses its internal references */
181220
private deepCopy<T>(obj: T): T {
182221
return JSON.parse(JSON.stringify(obj));

0 commit comments

Comments
 (0)