@james-pre/config 1.2.0 → 1.2.1

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.
@@ -0,0 +1,15 @@
1
+ import * as z from 'zod';
2
+ /**
3
+ * Remove `.default()` and `.prefault()` from a schema and everything nested within it.
4
+ */
5
+ export declare function strip(schema: z.ZodType): z.ZodType;
6
+ /**
7
+ * Compute a schema's default value from `.default()` on it and on anything nested within it.
8
+ * Members without a default are left out, so the result is not necessarily a complete config.
9
+ */
10
+ export declare function of(schema: z.ZodType): unknown;
11
+ /**
12
+ * Fill in defaults for anything missing from `value` in place, including within records, maps, arrays, and tuples.
13
+ */
14
+ declare function _with(schema: z.ZodType, value: unknown): unknown;
15
+ export { _with as with };
@@ -0,0 +1,129 @@
1
+ import * as z from 'zod';
2
+ /**
3
+ * Remove `.default()` and `.prefault()` from a schema and everything nested within it.
4
+ */
5
+ export function strip(schema) {
6
+ const def = schema._zod.def;
7
+ switch (def.type) {
8
+ case 'default':
9
+ case 'prefault':
10
+ return strip(def.innerType);
11
+ case 'object':
12
+ return z.core.clone(schema, {
13
+ ...def,
14
+ shape: Object.fromEntries(Object.entries(def.shape).map(([key, member]) => [key, strip(member)])),
15
+ catchall: def.catchall && strip(def.catchall),
16
+ });
17
+ case 'array':
18
+ return z.core.clone(schema, { ...def, element: strip(def.element) });
19
+ case 'record':
20
+ case 'map':
21
+ case 'set':
22
+ return z.core.clone(schema, { ...def, valueType: strip(def.valueType) });
23
+ case 'union':
24
+ return z.core.clone(schema, { ...def, options: def.options.map(strip) });
25
+ case 'tuple':
26
+ return z.core.clone(schema, {
27
+ ...def,
28
+ items: def.items.map(strip),
29
+ rest: def.rest && strip(def.rest),
30
+ });
31
+ case 'optional':
32
+ case 'nullable':
33
+ case 'readonly':
34
+ case 'nonoptional':
35
+ case 'catch':
36
+ return z.core.clone(schema, { ...def, innerType: strip(def.innerType) });
37
+ default:
38
+ return schema;
39
+ }
40
+ }
41
+ /**
42
+ * Compute a schema's default value from `.default()` on it and on anything nested within it.
43
+ * Members without a default are left out, so the result is not necessarily a complete config.
44
+ */
45
+ export function of(schema) {
46
+ const def = schema._zod.def;
47
+ switch (def.type) {
48
+ case 'default':
49
+ case 'prefault':
50
+ case 'catch':
51
+ try {
52
+ return schema.parse(undefined);
53
+ }
54
+ catch {
55
+ return of(def.innerType);
56
+ }
57
+ case 'object': {
58
+ const value = {};
59
+ for (const [key, member] of Object.entries(def.shape)) {
60
+ const inner = of(member);
61
+ if (inner !== undefined)
62
+ value[key] = inner;
63
+ }
64
+ return Object.keys(value).length ? value : undefined;
65
+ }
66
+ default:
67
+ return def.innerType ? of(def.innerType) : undefined;
68
+ }
69
+ }
70
+ /**
71
+ * Fill in defaults for anything missing from `value` in place, including within records, maps, arrays, and tuples.
72
+ */
73
+ function _with(schema, value) {
74
+ const def = schema._zod.def;
75
+ switch (def.type) {
76
+ case 'default':
77
+ case 'prefault':
78
+ case 'catch':
79
+ return value === undefined ? of(schema) : _with(def.innerType, value);
80
+ case 'optional':
81
+ case 'nullable':
82
+ case 'readonly':
83
+ case 'nonoptional':
84
+ return value == null ? value : _with(def.innerType, value);
85
+ case 'object': {
86
+ if (typeof value != 'object' || value === null)
87
+ return value;
88
+ const object = value;
89
+ for (const [key, member] of Object.entries(def.shape)) {
90
+ const filled = _with(member, object[key]);
91
+ if (filled !== undefined)
92
+ object[key] = filled;
93
+ }
94
+ return object;
95
+ }
96
+ case 'record': {
97
+ if (typeof value != 'object' || value === null)
98
+ return value;
99
+ const record = value;
100
+ for (const key of Object.keys(record))
101
+ record[key] = _with(def.valueType, record[key]);
102
+ return record;
103
+ }
104
+ case 'map':
105
+ if (!(value instanceof Map))
106
+ return value;
107
+ for (const [key, entry] of value)
108
+ value.set(key, _with(def.valueType, entry));
109
+ return value;
110
+ case 'array':
111
+ if (!Array.isArray(value))
112
+ return value;
113
+ for (let i = 0; i < value.length; i++)
114
+ value[i] = _with(def.element, value[i]);
115
+ return value;
116
+ case 'tuple':
117
+ if (!Array.isArray(value))
118
+ return value;
119
+ for (let i = 0; i < value.length; i++) {
120
+ const item = i < def.items.length ? def.items[i] : def.rest;
121
+ if (item)
122
+ value[i] = _with(item, value[i]);
123
+ }
124
+ return value;
125
+ default:
126
+ return value;
127
+ }
128
+ }
129
+ export { _with as with };
package/dist/manager.js CHANGED
@@ -39,6 +39,7 @@ import { homedir } from 'node:os';
39
39
  import { dirname, join, resolve, sep } from 'node:path';
40
40
  import { deepAssign, getByString, memoize, setByString } from 'utilium';
41
41
  import * as z from 'zod';
42
+ import * as defaults from './defaults.js';
42
43
  const kInit = Symbol.for('LoadOptions:init');
43
44
  const include = z.string().array().optional();
44
45
  function canInclude(options, file) {
@@ -63,74 +64,6 @@ function typeFromPath(path) {
63
64
  function withExtension(path) {
64
65
  return path.endsWith('.json') ? path : path + '.json';
65
66
  }
66
- /**
67
- * Remove `.default()` and `.prefault()` from a schema and everything nested within it.
68
- */
69
- function stripDefaults(schema) {
70
- const def = schema._zod.def;
71
- switch (def.type) {
72
- case 'default':
73
- case 'prefault':
74
- return stripDefaults(def.innerType);
75
- case 'object':
76
- return z.core.clone(schema, {
77
- ...def,
78
- shape: Object.fromEntries(Object.entries(def.shape).map(([key, member]) => [key, stripDefaults(member)])),
79
- catchall: def.catchall && stripDefaults(def.catchall),
80
- });
81
- case 'array':
82
- return z.core.clone(schema, { ...def, element: stripDefaults(def.element) });
83
- case 'record':
84
- case 'map':
85
- case 'set':
86
- return z.core.clone(schema, { ...def, valueType: stripDefaults(def.valueType) });
87
- case 'union':
88
- return z.core.clone(schema, { ...def, options: def.options.map(stripDefaults) });
89
- case 'tuple':
90
- return z.core.clone(schema, {
91
- ...def,
92
- items: def.items.map(stripDefaults),
93
- rest: def.rest && stripDefaults(def.rest),
94
- });
95
- case 'optional':
96
- case 'nullable':
97
- case 'readonly':
98
- case 'nonoptional':
99
- case 'catch':
100
- return z.core.clone(schema, { ...def, innerType: stripDefaults(def.innerType) });
101
- default:
102
- return schema;
103
- }
104
- }
105
- /**
106
- * Compute a schema's default value from `.default()` on it and on anything nested within it.
107
- * Members without a default are left out, so the result is not necessarily a complete config.
108
- */
109
- function defaultsOf(schema) {
110
- const def = schema._zod.def;
111
- switch (def.type) {
112
- case 'default':
113
- case 'prefault':
114
- case 'catch':
115
- try {
116
- return schema.parse(undefined);
117
- }
118
- catch {
119
- return defaultsOf(def.innerType);
120
- }
121
- case 'object': {
122
- const value = {};
123
- for (const [key, member] of Object.entries(def.shape)) {
124
- const inner = defaultsOf(member);
125
- if (inner !== undefined)
126
- value[key] = inner;
127
- }
128
- return Object.keys(value).length ? value : undefined;
129
- }
130
- default:
131
- return def.innerType ? defaultsOf(def.innerType) : undefined;
132
- }
133
- }
134
67
  /**
135
68
  * Manager for configuration files
136
69
  */
@@ -163,7 +96,7 @@ let Manager = (() => {
163
96
  super({ captureRejections: true });
164
97
  this.schema = schema;
165
98
  this.options = options;
166
- this.fileSchema = z.deepPartial(stripDefaults(z.object(options.enableIncludes ? { ...schema.shape, include } : schema.shape)));
99
+ this.fileSchema = z.deepPartial(defaults.strip(z.object(options.enableIncludes ? { ...schema.shape, include } : schema.shape)));
167
100
  this.data = this.schema.parse(this.defaults);
168
101
  }
169
102
  /**
@@ -178,7 +111,7 @@ let Manager = (() => {
178
111
  * A fresh config with only the schema's defaults applied.
179
112
  */
180
113
  get defaults() {
181
- return structuredClone(defaultsOf(this.schema) ?? {});
114
+ return structuredClone(defaults.of(this.schema) ?? {});
182
115
  }
183
116
  get(key) {
184
117
  return getByString(this.data, key);
@@ -191,6 +124,7 @@ let Manager = (() => {
191
124
  */
192
125
  merge(config) {
193
126
  deepAssign(this.data, structuredClone(config), { replaceArrays: true });
127
+ defaults.with(this.schema, this.data);
194
128
  this.emit('change');
195
129
  }
196
130
  /** Replace the current config with `config`, applying defaults for anything missing. */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@james-pre/config",
3
- "version": "1.2.0",
3
+ "version": "1.2.1",
4
4
  "description": "Manage configuration files",
5
5
  "author": "James Prevett <jp@jamespre.dev> (https://jamespre.dev)",
6
6
  "license": "LGPL-3.0-or-later",