@james-pre/config 1.0.1 → 1.1.0
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/dist/manager.d.ts +30 -21
- package/dist/manager.js +37 -18
- package/package.json +3 -3
package/dist/manager.d.ts
CHANGED
|
@@ -3,12 +3,13 @@ import type { FlattenKeys, GetByString, PartialRecursive } from 'utilium';
|
|
|
3
3
|
import * as z from 'zod';
|
|
4
4
|
export type FileType = 'system' | 'user' | 'local';
|
|
5
5
|
/** Information about a loaded config file */
|
|
6
|
-
interface File<Value> {
|
|
6
|
+
interface File<Value, Options extends LoadOptions = LoadOptions> {
|
|
7
7
|
path: string;
|
|
8
8
|
wasIncluded: boolean;
|
|
9
9
|
data: Value;
|
|
10
10
|
type: FileType;
|
|
11
11
|
auto: boolean;
|
|
12
|
+
options: Partial<Options>;
|
|
12
13
|
}
|
|
13
14
|
/**
|
|
14
15
|
* How a file is being loaded
|
|
@@ -23,21 +24,21 @@ export interface LoadOptions {
|
|
|
23
24
|
/**
|
|
24
25
|
* If enabled, the config file will still be loaded if it does not match the schema.
|
|
25
26
|
*/
|
|
26
|
-
loose
|
|
27
|
+
loose: boolean;
|
|
27
28
|
/**
|
|
28
29
|
* If enabled, the config file will be skipped if it does not exist.
|
|
29
30
|
*/
|
|
30
|
-
optional
|
|
31
|
+
optional: boolean;
|
|
31
32
|
/**
|
|
32
33
|
* If enabled, an empty config file will be created when it does not exist.
|
|
33
34
|
* Included files are never created.
|
|
34
35
|
*/
|
|
35
|
-
create
|
|
36
|
+
create: boolean;
|
|
36
37
|
/**
|
|
37
38
|
* Used to mark files that are included, auto-loaded, etc.
|
|
38
39
|
* @internal
|
|
39
40
|
*/
|
|
40
|
-
[kInit]
|
|
41
|
+
[kInit]: FileLoadInit;
|
|
41
42
|
}
|
|
42
43
|
declare const kInit: unique symbol;
|
|
43
44
|
export interface ManagerOptions {
|
|
@@ -57,37 +58,45 @@ export interface ManagerOptions {
|
|
|
57
58
|
/**
|
|
58
59
|
* Manager for configuration files
|
|
59
60
|
*/
|
|
60
|
-
export declare class Manager<
|
|
61
|
-
load: [path: string, config: FileData];
|
|
62
|
-
post_load: [path: string, config: FileData];
|
|
61
|
+
export declare class Manager<Schema extends z.ZodObject, LoadOpts extends LoadOptions = LoadOptions, out In extends z.input<Schema> = z.input<Schema>, out FileData = z.output<ReturnType<typeof z.deepPartial<Schema>>>> extends EventEmitter<{
|
|
62
|
+
load: [path: string, config: FileData, options: Partial<LoadOpts>];
|
|
63
|
+
post_load: [path: string, config: FileData, options: Partial<LoadOpts>];
|
|
63
64
|
load_error: [path: string, stage: 'read' | 'create' | 'parse', error: Error];
|
|
64
65
|
create: [path: string];
|
|
65
66
|
change: [];
|
|
66
67
|
write: [path: string, data: FileData];
|
|
67
68
|
reload: [];
|
|
68
69
|
}> {
|
|
70
|
+
readonly schema: Schema;
|
|
69
71
|
protected options: ManagerOptions;
|
|
70
|
-
readonly
|
|
71
|
-
readonly
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
72
|
+
readonly fileSchema: ReturnType<typeof z.deepPartial<Schema>>;
|
|
73
|
+
protected readonly _files: Map<string, File<FileData, LoadOpts>>;
|
|
74
|
+
get filePaths(): MapIterator<string>;
|
|
75
|
+
get files(): IteratorObject<Readonly<File<FileData, LoadOpts>>>;
|
|
76
|
+
configAt(path: string): Readonly<FileData> | undefined;
|
|
77
|
+
readonly data: z.output<Schema>;
|
|
78
|
+
constructor(schema: Schema, options?: ManagerOptions);
|
|
79
|
+
/**
|
|
80
|
+
* Narrow the type of this manager's load options
|
|
81
|
+
*/
|
|
82
|
+
$loadOptions<O extends object>(): Manager<Schema, LoadOpts & O, In, FileData>;
|
|
83
|
+
$assertLoadOptions<O extends object>(): asserts this is Manager<Schema, LoadOpts & O, In, FileData>;
|
|
75
84
|
/**
|
|
76
85
|
* A fresh config with only the schema's defaults applied.
|
|
77
86
|
*/
|
|
78
|
-
get defaults(): z.output<
|
|
79
|
-
get<const K extends string | number = FlattenKeys<z.output<
|
|
80
|
-
set<const K extends string | number = FlattenKeys<z.output<
|
|
87
|
+
get defaults(): z.output<Schema> & In;
|
|
88
|
+
get<const K extends string | number = FlattenKeys<z.output<Schema>>>(key: K): GetByString<z.output<Schema>, K>;
|
|
89
|
+
set<const K extends string | number = FlattenKeys<z.output<Schema>>, V = GetByString<z.output<Schema>, K>>(key: K, value: V): void;
|
|
81
90
|
/**
|
|
82
91
|
* Deeply merge `config` into the current config. Arrays are replaced rather than combined.
|
|
83
92
|
*/
|
|
84
93
|
merge(config: PartialRecursive<In>): void;
|
|
85
94
|
/** Replace the current config with `config`, applying defaults for anything missing. */
|
|
86
95
|
replace(config: In): void;
|
|
87
|
-
loadFile(path: string, options: LoadOpts): void;
|
|
96
|
+
loadFile(path: string, options: Partial<LoadOpts>): void;
|
|
88
97
|
/** Get the entry for `path`, adding one for a file that has not been loaded */
|
|
89
|
-
protected fileAt(path: string): File<FileData>;
|
|
90
|
-
protected writeFile(file: File<FileData>): void;
|
|
98
|
+
protected fileAt(path: string): File<FileData, LoadOpts>;
|
|
99
|
+
protected writeFile(file: File<FileData, LoadOpts>): void;
|
|
91
100
|
/**
|
|
92
101
|
* Replace the contents of the config file at `path` and write it,
|
|
93
102
|
* then rebuild the current config from the defaults and all loaded files.
|
|
@@ -108,7 +117,7 @@ export declare class Manager<Shape extends Readonly<Record<string, z.ZodType>>,
|
|
|
108
117
|
/**
|
|
109
118
|
* Load the files from `defaultPaths`. Missing files are skipped unless `options` says otherwise.
|
|
110
119
|
*/
|
|
111
|
-
loadDefaults(options
|
|
120
|
+
loadDefaults(options?: Partial<LoadOpts>): void;
|
|
112
121
|
/**
|
|
113
122
|
* The file a change of the given type is written to:
|
|
114
123
|
* the most recently loaded file of that type, or the path `loadDefaults` would use for it.
|
|
@@ -118,6 +127,6 @@ export declare class Manager<Shape extends Readonly<Record<string, z.ZodType>>,
|
|
|
118
127
|
* Merge `config` into the current config and save it.
|
|
119
128
|
*/
|
|
120
129
|
update(config: PartialRecursive<In>, type?: FileType): void;
|
|
121
|
-
reloadFiles(options
|
|
130
|
+
reloadFiles(options?: Partial<LoadOpts>): void;
|
|
122
131
|
}
|
|
123
132
|
export {};
|
package/dist/manager.js
CHANGED
|
@@ -145,18 +145,35 @@ let Manager = (() => {
|
|
|
145
145
|
__esDecorate(this, null, _get_defaultPaths_decorators, { kind: "getter", name: "defaultPaths", static: false, private: false, access: { has: obj => "defaultPaths" in obj, get: obj => obj.defaultPaths }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
146
146
|
if (_metadata) Object.defineProperty(this, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
|
|
147
147
|
}
|
|
148
|
-
|
|
149
|
-
|
|
148
|
+
schema = __runInitializers(this, _instanceExtraInitializers);
|
|
149
|
+
options;
|
|
150
150
|
fileSchema;
|
|
151
|
-
|
|
151
|
+
_files = new Map();
|
|
152
|
+
get filePaths() {
|
|
153
|
+
return this._files.keys();
|
|
154
|
+
}
|
|
155
|
+
get files() {
|
|
156
|
+
return this._files.values().map(f => structuredClone(f));
|
|
157
|
+
}
|
|
158
|
+
configAt(path) {
|
|
159
|
+
return structuredClone(this._files.get(path)?.data);
|
|
160
|
+
}
|
|
152
161
|
data;
|
|
153
|
-
constructor(
|
|
162
|
+
constructor(schema, options = {}) {
|
|
154
163
|
super({ captureRejections: true });
|
|
164
|
+
this.schema = schema;
|
|
155
165
|
this.options = options;
|
|
156
|
-
this.
|
|
157
|
-
this.fileSchema = z.deepPartial(stripDefaults(z.object(options.enableIncludes ? { ...shape, include } : shape)));
|
|
166
|
+
this.fileSchema = z.deepPartial(stripDefaults(z.object(options.enableIncludes ? { ...schema.shape, include } : schema.shape)));
|
|
158
167
|
this.data = this.schema.parse(this.defaults);
|
|
159
168
|
}
|
|
169
|
+
/**
|
|
170
|
+
* Narrow the type of this manager's load options
|
|
171
|
+
*/
|
|
172
|
+
$loadOptions() {
|
|
173
|
+
this.$assertLoadOptions();
|
|
174
|
+
return this;
|
|
175
|
+
}
|
|
176
|
+
$assertLoadOptions() { }
|
|
160
177
|
/**
|
|
161
178
|
* A fresh config with only the schema's defaults applied.
|
|
162
179
|
*/
|
|
@@ -184,7 +201,7 @@ let Manager = (() => {
|
|
|
184
201
|
this.emit('change');
|
|
185
202
|
}
|
|
186
203
|
loadFile(path, options) {
|
|
187
|
-
if (this.
|
|
204
|
+
if (this._files.has(path))
|
|
188
205
|
return;
|
|
189
206
|
let json;
|
|
190
207
|
try {
|
|
@@ -220,15 +237,16 @@ let Manager = (() => {
|
|
|
220
237
|
this.emit('load_error', path, 'parse', e);
|
|
221
238
|
file = json;
|
|
222
239
|
}
|
|
223
|
-
this.
|
|
240
|
+
this._files.set(path, {
|
|
224
241
|
auto: false,
|
|
225
242
|
wasIncluded: false,
|
|
226
243
|
type: typeFromPath(path),
|
|
227
244
|
...(options[kInit] || {}),
|
|
228
245
|
data: file,
|
|
229
246
|
path,
|
|
247
|
+
options,
|
|
230
248
|
});
|
|
231
|
-
this.emit('load', path, file);
|
|
249
|
+
this.emit('load', path, file, options);
|
|
232
250
|
this.merge(file);
|
|
233
251
|
if (canInclude(this.options, file))
|
|
234
252
|
for (const include of file.include ?? []) {
|
|
@@ -239,11 +257,11 @@ let Manager = (() => {
|
|
|
239
257
|
[kInit]: { ...options[kInit], wasIncluded: true },
|
|
240
258
|
});
|
|
241
259
|
}
|
|
242
|
-
this.emit('post_load', path, file);
|
|
260
|
+
this.emit('post_load', path, file, options);
|
|
243
261
|
}
|
|
244
262
|
/** Get the entry for `path`, adding one for a file that has not been loaded */
|
|
245
263
|
fileAt(path) {
|
|
246
|
-
const existing = this.
|
|
264
|
+
const existing = this._files.get(path);
|
|
247
265
|
if (existing)
|
|
248
266
|
return existing;
|
|
249
267
|
const file = {
|
|
@@ -252,8 +270,9 @@ let Manager = (() => {
|
|
|
252
270
|
type: typeFromPath(path),
|
|
253
271
|
auto: false,
|
|
254
272
|
wasIncluded: false,
|
|
273
|
+
options: {},
|
|
255
274
|
};
|
|
256
|
-
this.
|
|
275
|
+
this._files.set(path, file);
|
|
257
276
|
return file;
|
|
258
277
|
}
|
|
259
278
|
writeFile(file) {
|
|
@@ -270,7 +289,7 @@ let Manager = (() => {
|
|
|
270
289
|
file.data = this.fileSchema.parse(config);
|
|
271
290
|
this.writeFile(file);
|
|
272
291
|
this.replace(this.defaults);
|
|
273
|
-
for (const file of this.
|
|
292
|
+
for (const file of this._files.values())
|
|
274
293
|
this.merge(file.data);
|
|
275
294
|
}
|
|
276
295
|
/**
|
|
@@ -298,7 +317,7 @@ let Manager = (() => {
|
|
|
298
317
|
/**
|
|
299
318
|
* Load the files from `defaultPaths`. Missing files are skipped unless `options` says otherwise.
|
|
300
319
|
*/
|
|
301
|
-
loadDefaults(options) {
|
|
320
|
+
loadDefaults(options = {}) {
|
|
302
321
|
for (const { path, type } of this.defaultPaths) {
|
|
303
322
|
this.loadFile(path, { optional: true, ...options, [kInit]: { type, auto: true } });
|
|
304
323
|
}
|
|
@@ -308,7 +327,7 @@ let Manager = (() => {
|
|
|
308
327
|
* the most recently loaded file of that type, or the path `loadDefaults` would use for it.
|
|
309
328
|
*/
|
|
310
329
|
findPath(type) {
|
|
311
|
-
const loaded = this.
|
|
330
|
+
const loaded = this._files
|
|
312
331
|
.values()
|
|
313
332
|
.filter(file => (!type || file.type == type) && !file.wasIncluded)
|
|
314
333
|
.map(file => file.path)
|
|
@@ -326,12 +345,12 @@ let Manager = (() => {
|
|
|
326
345
|
update(config, type) {
|
|
327
346
|
this.updateFile(this.findPath(type), config);
|
|
328
347
|
}
|
|
329
|
-
reloadFiles(options) {
|
|
330
|
-
const files = this.
|
|
348
|
+
reloadFiles(options = {}) {
|
|
349
|
+
const files = this._files
|
|
331
350
|
.values()
|
|
332
351
|
.filter(file => !file.wasIncluded)
|
|
333
352
|
.toArray();
|
|
334
|
-
this.
|
|
353
|
+
this._files.clear();
|
|
335
354
|
this.replace(this.defaults);
|
|
336
355
|
for (const file of files) {
|
|
337
356
|
this.loadFile(file.path, { ...options, [kInit]: { type: file.type, auto: file.auto } });
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@james-pre/config",
|
|
3
|
-
"version": "1.0
|
|
3
|
+
"version": "1.1.0",
|
|
4
4
|
"description": "Manage configuration files",
|
|
5
5
|
"funding": {
|
|
6
6
|
"type": "individual",
|
|
@@ -20,8 +20,8 @@
|
|
|
20
20
|
"scripts": {
|
|
21
21
|
"format:check": "prettier --check .",
|
|
22
22
|
"format": "prettier --write .",
|
|
23
|
-
"build": "tsc",
|
|
24
|
-
"lint": "tsc --noEmit && eslint src",
|
|
23
|
+
"build": "tsc -b",
|
|
24
|
+
"lint": "tsc -b --noEmit && eslint src",
|
|
25
25
|
"prepublishOnly": "npx tsc"
|
|
26
26
|
},
|
|
27
27
|
"repository": {
|