@grest-ts/config 0.0.6 → 0.0.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -21
- package/README.md +57 -57
- package/dist/tsconfig.publish.tsbuildinfo +1 -1
- package/package.json +7 -7
- package/src/GGConfig.ts +69 -69
- package/src/GGConfigKey.ts +106 -106
- package/src/GGConfigLocator.ts +112 -112
- package/src/GGConfigStore.ts +100 -100
- package/src/GG_CONFIG.ts +4 -4
- package/src/assureValidConfigPath.spec.ts +140 -140
- package/src/assureValidConfigPath.ts +33 -33
- package/src/index-node.ts +10 -10
- package/src/keys/GGResource.ts +20 -20
- package/src/keys/GGSecret.ts +20 -20
- package/src/keys/GGSetting.ts +28 -28
- package/src/stores/GGConfigStoreFile.ts +69 -69
- package/src/stores/GGConfigStoreLocal.ts +109 -109
package/src/keys/GGSecret.ts
CHANGED
|
@@ -1,20 +1,20 @@
|
|
|
1
|
-
import {GGConfigKey} from "../GGConfigKey";
|
|
2
|
-
import {GGValidator} from "@grest-ts/schema";
|
|
3
|
-
|
|
4
|
-
export class GGSecret<T> extends GGConfigKey<T> {
|
|
5
|
-
|
|
6
|
-
public static readonly NAME = "[GGSecret]";
|
|
7
|
-
|
|
8
|
-
constructor(name: string, schema: GGValidator<T>, description: string) {
|
|
9
|
-
super(name, schema, description);
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
public getStoreKey(): string {
|
|
13
|
-
return GGSecret.NAME
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
public reveal(): T {
|
|
17
|
-
return this.getValue();
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
}
|
|
1
|
+
import {GGConfigKey} from "../GGConfigKey";
|
|
2
|
+
import {GGValidator} from "@grest-ts/schema";
|
|
3
|
+
|
|
4
|
+
export class GGSecret<T> extends GGConfigKey<T> {
|
|
5
|
+
|
|
6
|
+
public static readonly NAME = "[GGSecret]";
|
|
7
|
+
|
|
8
|
+
constructor(name: string, schema: GGValidator<T>, description: string) {
|
|
9
|
+
super(name, schema, description);
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
public getStoreKey(): string {
|
|
13
|
+
return GGSecret.NAME
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
public reveal(): T {
|
|
17
|
+
return this.getValue();
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
}
|
package/src/keys/GGSetting.ts
CHANGED
|
@@ -1,28 +1,28 @@
|
|
|
1
|
-
import {GGConfigKey, Widen} from "../GGConfigKey";
|
|
2
|
-
import {deepFreeze} from "@grest-ts/common";
|
|
3
|
-
import {GGValidator} from "@grest-ts/schema";
|
|
4
|
-
|
|
5
|
-
export class GGSetting<T> extends GGConfigKey<T> {
|
|
6
|
-
|
|
7
|
-
public static readonly NAME = "[GGSetting]";
|
|
8
|
-
|
|
9
|
-
readonly #default: T;
|
|
10
|
-
|
|
11
|
-
constructor(name: string, schema: GGValidator<T>, defaultValue: Widen<T>, description: string) {
|
|
12
|
-
super(name, schema, description);
|
|
13
|
-
this.#default = deepFreeze(defaultValue as T);
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
public override getDefault(): T {
|
|
17
|
-
return this.#default;
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
public getStoreKey(): string {
|
|
21
|
-
return GGSetting.NAME
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
public get(): T {
|
|
25
|
-
return this.getValue();
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
}
|
|
1
|
+
import {GGConfigKey, Widen} from "../GGConfigKey";
|
|
2
|
+
import {deepFreeze} from "@grest-ts/common";
|
|
3
|
+
import {GGValidator} from "@grest-ts/schema";
|
|
4
|
+
|
|
5
|
+
export class GGSetting<T> extends GGConfigKey<T> {
|
|
6
|
+
|
|
7
|
+
public static readonly NAME = "[GGSetting]";
|
|
8
|
+
|
|
9
|
+
readonly #default: T;
|
|
10
|
+
|
|
11
|
+
constructor(name: string, schema: GGValidator<T>, defaultValue: Widen<T>, description: string) {
|
|
12
|
+
super(name, schema, description);
|
|
13
|
+
this.#default = deepFreeze(defaultValue as T);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
public override getDefault(): T {
|
|
17
|
+
return this.#default;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
public getStoreKey(): string {
|
|
21
|
+
return GGSetting.NAME
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
public get(): T {
|
|
25
|
+
return this.getValue();
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
}
|
|
@@ -1,69 +1,69 @@
|
|
|
1
|
-
import * as fs from 'fs';
|
|
2
|
-
import {GGConfigStore} from "../GGConfigStore";
|
|
3
|
-
import {GGConfigKey} from "../GGConfigKey";
|
|
4
|
-
import {dirname, join} from "node:path";
|
|
5
|
-
import {fileURLToPath} from "node:url";
|
|
6
|
-
|
|
7
|
-
/**
|
|
8
|
-
* Local file-based settings strategy.
|
|
9
|
-
* Reads settings from settings.json in the config directory.
|
|
10
|
-
* Watches file for changes and automatically reloads.
|
|
11
|
-
*/
|
|
12
|
-
export class GGConfigStoreFile<Key extends GGConfigKey> extends GGConfigStore<Key> {
|
|
13
|
-
|
|
14
|
-
private readonly file: string;
|
|
15
|
-
#watcher: fs.FSWatcher | null = null;
|
|
16
|
-
#debounceTimer: NodeJS.Timeout | null = null;
|
|
17
|
-
#valuesCache: Map<GGConfigKey, unknown> = new Map();
|
|
18
|
-
|
|
19
|
-
constructor(file: string, moduleUrl?: string) {
|
|
20
|
-
super();
|
|
21
|
-
const prefix = moduleUrl ? dirname(fileURLToPath(moduleUrl)) : "";
|
|
22
|
-
this.file = join(prefix, file);
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
public override async start(): Promise<void> {
|
|
26
|
-
await super.start();
|
|
27
|
-
await this.refresh(true);
|
|
28
|
-
this.#watcher = fs.watch(this.file, (eventType) => {
|
|
29
|
-
if (eventType === 'change') {
|
|
30
|
-
if (this.#debounceTimer) clearTimeout(this.#debounceTimer);
|
|
31
|
-
// Adding debounce so file writes would have time to correctly complete.
|
|
32
|
-
this.#debounceTimer = setTimeout(() => this.refresh(false), 100);
|
|
33
|
-
}
|
|
34
|
-
});
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
public override async teardown(): Promise<void> {
|
|
38
|
-
if (this.#watcher) {
|
|
39
|
-
this.#watcher.close();
|
|
40
|
-
this.#watcher = null;
|
|
41
|
-
}
|
|
42
|
-
await super.teardown();
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
public async refresh(isInitialLoad: boolean): Promise<void> {
|
|
46
|
-
const fileContent = fs.readFileSync(this.file, 'utf-8');
|
|
47
|
-
if (!fileContent) {
|
|
48
|
-
throw new Error("Settings file is empty! Why is that?")
|
|
49
|
-
}
|
|
50
|
-
const fileJson = JSON.parse(fileContent);
|
|
51
|
-
this.#valuesCache.clear();
|
|
52
|
-
this.keys.forEach(key => {
|
|
53
|
-
const path = key.name.split("/");
|
|
54
|
-
let val: any = undefined;
|
|
55
|
-
if (path.length > 1) {
|
|
56
|
-
val = fileJson;
|
|
57
|
-
for (let i = 1; i < path.length; i++) {
|
|
58
|
-
val = val?.[path[i]]
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
this.#valuesCache.set(key, this.resolveValue(key, val, isInitialLoad));
|
|
62
|
-
});
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
public getValue<T>(key: GGConfigKey<T>): T {
|
|
66
|
-
return this.#valuesCache.get(key) as T;
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
}
|
|
1
|
+
import * as fs from 'fs';
|
|
2
|
+
import {GGConfigStore} from "../GGConfigStore";
|
|
3
|
+
import {GGConfigKey} from "../GGConfigKey";
|
|
4
|
+
import {dirname, join} from "node:path";
|
|
5
|
+
import {fileURLToPath} from "node:url";
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Local file-based settings strategy.
|
|
9
|
+
* Reads settings from settings.json in the config directory.
|
|
10
|
+
* Watches file for changes and automatically reloads.
|
|
11
|
+
*/
|
|
12
|
+
export class GGConfigStoreFile<Key extends GGConfigKey> extends GGConfigStore<Key> {
|
|
13
|
+
|
|
14
|
+
private readonly file: string;
|
|
15
|
+
#watcher: fs.FSWatcher | null = null;
|
|
16
|
+
#debounceTimer: NodeJS.Timeout | null = null;
|
|
17
|
+
#valuesCache: Map<GGConfigKey, unknown> = new Map();
|
|
18
|
+
|
|
19
|
+
constructor(file: string, moduleUrl?: string) {
|
|
20
|
+
super();
|
|
21
|
+
const prefix = moduleUrl ? dirname(fileURLToPath(moduleUrl)) : "";
|
|
22
|
+
this.file = join(prefix, file);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
public override async start(): Promise<void> {
|
|
26
|
+
await super.start();
|
|
27
|
+
await this.refresh(true);
|
|
28
|
+
this.#watcher = fs.watch(this.file, (eventType) => {
|
|
29
|
+
if (eventType === 'change') {
|
|
30
|
+
if (this.#debounceTimer) clearTimeout(this.#debounceTimer);
|
|
31
|
+
// Adding debounce so file writes would have time to correctly complete.
|
|
32
|
+
this.#debounceTimer = setTimeout(() => this.refresh(false), 100);
|
|
33
|
+
}
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
public override async teardown(): Promise<void> {
|
|
38
|
+
if (this.#watcher) {
|
|
39
|
+
this.#watcher.close();
|
|
40
|
+
this.#watcher = null;
|
|
41
|
+
}
|
|
42
|
+
await super.teardown();
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
public async refresh(isInitialLoad: boolean): Promise<void> {
|
|
46
|
+
const fileContent = fs.readFileSync(this.file, 'utf-8');
|
|
47
|
+
if (!fileContent) {
|
|
48
|
+
throw new Error("Settings file is empty! Why is that?")
|
|
49
|
+
}
|
|
50
|
+
const fileJson = JSON.parse(fileContent);
|
|
51
|
+
this.#valuesCache.clear();
|
|
52
|
+
this.keys.forEach(key => {
|
|
53
|
+
const path = key.name.split("/");
|
|
54
|
+
let val: any = undefined;
|
|
55
|
+
if (path.length > 1) {
|
|
56
|
+
val = fileJson;
|
|
57
|
+
for (let i = 1; i < path.length; i++) {
|
|
58
|
+
val = val?.[path[i]]
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
this.#valuesCache.set(key, this.resolveValue(key, val, isInitialLoad));
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
public getValue<T>(key: GGConfigKey<T>): T {
|
|
66
|
+
return this.#valuesCache.get(key) as T;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
}
|
|
@@ -1,110 +1,110 @@
|
|
|
1
|
-
import {GGConfigStore} from "../GGConfigStore";
|
|
2
|
-
import {GGConfigKey, Widen} from "../GGConfigKey";
|
|
3
|
-
import {deepFreeze} from "@grest-ts/common";
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* Extracts the keys from T that are GGConfigKey instances or objects containing them.
|
|
7
|
-
* Uses a depth limit (D) to prevent infinite recursion on circular types (e.g. GGSchema).
|
|
8
|
-
*/
|
|
9
|
-
type ConfigKeyOf<T, D extends 1[] = []> = D['length'] extends 5 ? never : {
|
|
10
|
-
[K in keyof T]: [T[K]] extends [never] ? never :
|
|
11
|
-
T[K] extends GGConfigKey<any> ? K :
|
|
12
|
-
T[K] extends Function ? never :
|
|
13
|
-
T[K] extends object ? (ConfigKeyOf<T[K], [...D, 1]> extends never ? never : K) : never
|
|
14
|
-
}[keyof T];
|
|
15
|
-
|
|
16
|
-
/**
|
|
17
|
-
* Maps a config definition to the shape of values it expects.
|
|
18
|
-
* GGConfigKey<V> → V, objects with config keys → recurse, everything else → excluded.
|
|
19
|
-
*/
|
|
20
|
-
export type ConfigValues<T, D extends 1[] = []> =
|
|
21
|
-
D['length'] extends 5 ? never :
|
|
22
|
-
T extends GGConfigKey<infer V> ? Widen<V> :
|
|
23
|
-
T extends object ? (
|
|
24
|
-
ConfigKeyOf<T, D> extends never ? never : { [K in ConfigKeyOf<T, D>]: ConfigValues<T[K], [...D, 1]> }
|
|
25
|
-
) : never;
|
|
26
|
-
|
|
27
|
-
/**
|
|
28
|
-
* Type-checks a local config values object against a config definition.
|
|
29
|
-
* Returns the values object as-is — this is a compile-time helper, not a store.
|
|
30
|
-
*
|
|
31
|
-
* Use with GGConfigStoreLocal in your runtime's compose():
|
|
32
|
-
* @example
|
|
33
|
-
* ```typescript
|
|
34
|
-
* // config/local.ts — just data:
|
|
35
|
-
* export default createLocalConfig(MyConfig, {
|
|
36
|
-
* mysql: {
|
|
37
|
-
* host: { host: "localhost", port: 3306, database: "mydb" },
|
|
38
|
-
* user: { username: "root", password: "root" },
|
|
39
|
-
* },
|
|
40
|
-
* jwtSecret: "dev-secret",
|
|
41
|
-
* })
|
|
42
|
-
*
|
|
43
|
-
* // runtime.ts — fresh store per compose():
|
|
44
|
-
* import localConfig from "./config/local.js";
|
|
45
|
-
* new GGConfigLocator(MyConfig)
|
|
46
|
-
* .add([GGResource, GGSecret], new GGConfigStoreLocal(MyConfig, localConfig))
|
|
47
|
-
* ```
|
|
48
|
-
*/
|
|
49
|
-
export function createLocalConfig<T extends object>(_config: T, values: ConfigValues<T>): ConfigValues<T> {
|
|
50
|
-
deepFreeze(values)
|
|
51
|
-
return values;
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
/**
|
|
55
|
-
* Type-safe local development config store.
|
|
56
|
-
* Refuses to start in production — crashes immediately if NODE_ENV is "production".
|
|
57
|
-
* Use createLocalConfig() for the best DX, or .set() for manual control.
|
|
58
|
-
*/
|
|
59
|
-
export class GGConfigStoreLocal<Struct extends object, Key extends GGConfigKey = GGConfigKey> extends GGConfigStore<Key> {
|
|
60
|
-
|
|
61
|
-
readonly #values = new Map<GGConfigKey, unknown>();
|
|
62
|
-
readonly #valuesCache = new Map<GGConfigKey, unknown>();
|
|
63
|
-
|
|
64
|
-
constructor(config: Struct, values: ConfigValues<Struct>) {
|
|
65
|
-
super();
|
|
66
|
-
walkAndSet(this, config, values);
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
public set<T>(key: GGConfigKey<T>, value: T): this {
|
|
70
|
-
this.#values.set(key, value);
|
|
71
|
-
return this;
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
public override async start(): Promise<void> {
|
|
75
|
-
if (process.env.NODE_ENV === "production") {
|
|
76
|
-
throw new Error(
|
|
77
|
-
"GGConfigStoreLocal cannot be used in production. " +
|
|
78
|
-
"Use GGConfigStoreAwsSecretsManager or another production-safe store."
|
|
79
|
-
);
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
this.keys.forEach(key => {
|
|
83
|
-
this.#valuesCache.set(key, this.resolveValue(key, this.#values.get(key), true));
|
|
84
|
-
});
|
|
85
|
-
|
|
86
|
-
await super.start();
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
public override async teardown(): Promise<void> {
|
|
90
|
-
this.#valuesCache.clear();
|
|
91
|
-
await super.teardown();
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
public getValue<T>(key: GGConfigKey<T>): T {
|
|
95
|
-
return this.#valuesCache.get(key) as T;
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
function walkAndSet(store: GGConfigStoreLocal<any>, config: any, values: any) {
|
|
101
|
-
for (const prop of Object.keys(values)) {
|
|
102
|
-
const configEntry = config[prop];
|
|
103
|
-
const value = values[prop];
|
|
104
|
-
if (configEntry instanceof GGConfigKey) {
|
|
105
|
-
store.set(configEntry, value);
|
|
106
|
-
} else if (configEntry != null && typeof configEntry === 'object' && value != null && typeof value === 'object') {
|
|
107
|
-
walkAndSet(store, configEntry, value);
|
|
108
|
-
}
|
|
109
|
-
}
|
|
1
|
+
import {GGConfigStore} from "../GGConfigStore";
|
|
2
|
+
import {GGConfigKey, Widen} from "../GGConfigKey";
|
|
3
|
+
import {deepFreeze} from "@grest-ts/common";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Extracts the keys from T that are GGConfigKey instances or objects containing them.
|
|
7
|
+
* Uses a depth limit (D) to prevent infinite recursion on circular types (e.g. GGSchema).
|
|
8
|
+
*/
|
|
9
|
+
type ConfigKeyOf<T, D extends 1[] = []> = D['length'] extends 5 ? never : {
|
|
10
|
+
[K in keyof T]: [T[K]] extends [never] ? never :
|
|
11
|
+
T[K] extends GGConfigKey<any> ? K :
|
|
12
|
+
T[K] extends Function ? never :
|
|
13
|
+
T[K] extends object ? (ConfigKeyOf<T[K], [...D, 1]> extends never ? never : K) : never
|
|
14
|
+
}[keyof T];
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Maps a config definition to the shape of values it expects.
|
|
18
|
+
* GGConfigKey<V> → V, objects with config keys → recurse, everything else → excluded.
|
|
19
|
+
*/
|
|
20
|
+
export type ConfigValues<T, D extends 1[] = []> =
|
|
21
|
+
D['length'] extends 5 ? never :
|
|
22
|
+
T extends GGConfigKey<infer V> ? Widen<V> :
|
|
23
|
+
T extends object ? (
|
|
24
|
+
ConfigKeyOf<T, D> extends never ? never : { [K in ConfigKeyOf<T, D>]: ConfigValues<T[K], [...D, 1]> }
|
|
25
|
+
) : never;
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Type-checks a local config values object against a config definition.
|
|
29
|
+
* Returns the values object as-is — this is a compile-time helper, not a store.
|
|
30
|
+
*
|
|
31
|
+
* Use with GGConfigStoreLocal in your runtime's compose():
|
|
32
|
+
* @example
|
|
33
|
+
* ```typescript
|
|
34
|
+
* // config/local.ts — just data:
|
|
35
|
+
* export default createLocalConfig(MyConfig, {
|
|
36
|
+
* mysql: {
|
|
37
|
+
* host: { host: "localhost", port: 3306, database: "mydb" },
|
|
38
|
+
* user: { username: "root", password: "root" },
|
|
39
|
+
* },
|
|
40
|
+
* jwtSecret: "dev-secret",
|
|
41
|
+
* })
|
|
42
|
+
*
|
|
43
|
+
* // runtime.ts — fresh store per compose():
|
|
44
|
+
* import localConfig from "./config/local.js";
|
|
45
|
+
* new GGConfigLocator(MyConfig)
|
|
46
|
+
* .add([GGResource, GGSecret], new GGConfigStoreLocal(MyConfig, localConfig))
|
|
47
|
+
* ```
|
|
48
|
+
*/
|
|
49
|
+
export function createLocalConfig<T extends object>(_config: T, values: ConfigValues<T>): ConfigValues<T> {
|
|
50
|
+
deepFreeze(values)
|
|
51
|
+
return values;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Type-safe local development config store.
|
|
56
|
+
* Refuses to start in production — crashes immediately if NODE_ENV is "production".
|
|
57
|
+
* Use createLocalConfig() for the best DX, or .set() for manual control.
|
|
58
|
+
*/
|
|
59
|
+
export class GGConfigStoreLocal<Struct extends object, Key extends GGConfigKey = GGConfigKey> extends GGConfigStore<Key> {
|
|
60
|
+
|
|
61
|
+
readonly #values = new Map<GGConfigKey, unknown>();
|
|
62
|
+
readonly #valuesCache = new Map<GGConfigKey, unknown>();
|
|
63
|
+
|
|
64
|
+
constructor(config: Struct, values: ConfigValues<Struct>) {
|
|
65
|
+
super();
|
|
66
|
+
walkAndSet(this, config, values);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
public set<T>(key: GGConfigKey<T>, value: T): this {
|
|
70
|
+
this.#values.set(key, value);
|
|
71
|
+
return this;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
public override async start(): Promise<void> {
|
|
75
|
+
if (process.env.NODE_ENV === "production") {
|
|
76
|
+
throw new Error(
|
|
77
|
+
"GGConfigStoreLocal cannot be used in production. " +
|
|
78
|
+
"Use GGConfigStoreAwsSecretsManager or another production-safe store."
|
|
79
|
+
);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
this.keys.forEach(key => {
|
|
83
|
+
this.#valuesCache.set(key, this.resolveValue(key, this.#values.get(key), true));
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
await super.start();
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
public override async teardown(): Promise<void> {
|
|
90
|
+
this.#valuesCache.clear();
|
|
91
|
+
await super.teardown();
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
public getValue<T>(key: GGConfigKey<T>): T {
|
|
95
|
+
return this.#valuesCache.get(key) as T;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
function walkAndSet(store: GGConfigStoreLocal<any>, config: any, values: any) {
|
|
101
|
+
for (const prop of Object.keys(values)) {
|
|
102
|
+
const configEntry = config[prop];
|
|
103
|
+
const value = values[prop];
|
|
104
|
+
if (configEntry instanceof GGConfigKey) {
|
|
105
|
+
store.set(configEntry, value);
|
|
106
|
+
} else if (configEntry != null && typeof configEntry === 'object' && value != null && typeof value === 'object') {
|
|
107
|
+
walkAndSet(store, configEntry, value);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
110
|
}
|