@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 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?: boolean;
27
+ loose: boolean;
27
28
  /**
28
29
  * If enabled, the config file will be skipped if it does not exist.
29
30
  */
30
- optional?: boolean;
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?: boolean;
36
+ create: boolean;
36
37
  /**
37
38
  * Used to mark files that are included, auto-loaded, etc.
38
39
  * @internal
39
40
  */
40
- [kInit]?: FileLoadInit;
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<Shape extends Readonly<Record<string, z.ZodType>>, LoadOpts extends LoadOptions = LoadOptions, out In extends z.input<z.ZodObject<Shape>> = z.input<z.ZodObject<Shape>>, out FileData = z.output<ReturnType<typeof z.deepPartial<z.ZodObject<Shape>>>>> extends EventEmitter<{
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 schema: z.ZodObject<Shape>;
71
- readonly fileSchema: ReturnType<typeof z.deepPartial<z.ZodObject<Shape>>>;
72
- protected files: Map<string, File<FileData>>;
73
- readonly data: z.output<z.ZodObject<Shape>>;
74
- constructor(shape: Shape, options?: ManagerOptions);
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<z.ZodObject<Shape>> & In;
79
- get<const K extends string | number = FlattenKeys<z.output<z.ZodObject<Shape>>>>(key: K): GetByString<z.output<z.ZodObject<Shape>>, K>;
80
- set<const K extends string | number = FlattenKeys<z.output<z.ZodObject<Shape>>>, V = GetByString<z.output<z.ZodObject<Shape>>, K>>(key: K, value: V): void;
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: LoadOpts): void;
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: LoadOpts): void;
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
- options = __runInitializers(this, _instanceExtraInitializers);
149
- schema;
148
+ schema = __runInitializers(this, _instanceExtraInitializers);
149
+ options;
150
150
  fileSchema;
151
- files = new Map();
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(shape, options = {}) {
162
+ constructor(schema, options = {}) {
154
163
  super({ captureRejections: true });
164
+ this.schema = schema;
155
165
  this.options = options;
156
- this.schema = z.object(shape);
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.files.has(path))
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.files.set(path, {
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.files.get(path);
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.files.set(path, file);
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.files.values())
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.files
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.files
348
+ reloadFiles(options = {}) {
349
+ const files = this._files
331
350
  .values()
332
351
  .filter(file => !file.wasIncluded)
333
352
  .toArray();
334
- this.files.clear();
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.1",
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": {