@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.
- package/dist/defaults.d.ts +15 -0
- package/dist/defaults.js +129 -0
- package/dist/manager.js +4 -70
- package/package.json +1 -1
|
@@ -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 };
|
package/dist/defaults.js
ADDED
|
@@ -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(
|
|
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(
|
|
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. */
|