zcb 0.1.2 → 0.2.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/cjs/cli.cjs +1 -0
- package/dist/cjs/cli.cjs.map +1 -1
- package/dist/cjs/index.cjs +71 -52
- package/dist/cjs/index.cjs.map +1 -1
- package/dist/esm/cli.mjs +1 -0
- package/dist/esm/cli.mjs.map +1 -1
- package/dist/esm/index.mjs +72 -53
- package/dist/esm/index.mjs.map +1 -1
- package/dist/types/cjs/createConfigBuilder.d.cts +4 -1
- package/dist/types/cjs/createConfigBuilder.d.cts.map +1 -1
- package/dist/types/cjs/types.d.cts +1 -0
- package/dist/types/cjs/types.d.cts.map +1 -1
- package/dist/types/cjs/utils/collateDefaultValues.d.cts +3 -0
- package/dist/types/cjs/utils/collateDefaultValues.d.cts.map +1 -0
- package/dist/types/esm/createConfigBuilder.d.ts +4 -1
- package/dist/types/esm/createConfigBuilder.d.ts.map +1 -1
- package/dist/types/esm/types.d.ts +1 -0
- package/dist/types/esm/types.d.ts.map +1 -1
- package/dist/types/esm/utils/collateDefaultValues.d.ts +3 -0
- package/dist/types/esm/utils/collateDefaultValues.d.ts.map +1 -0
- package/dist/types/tsconfig.build.tsbuildinfo +1 -1
- package/package.json +1 -1
- package/src/__testUtils__/configBuilder.ts +1 -1
- package/src/createConfigBuilder.test.ts +6 -6
- package/src/createConfigBuilder.ts +43 -67
- package/src/types.ts +1 -0
- package/src/utils/collateDefaultValues.ts +55 -0
package/package.json
CHANGED
|
@@ -12,7 +12,7 @@ import {
|
|
|
12
12
|
} from './schema.ts';
|
|
13
13
|
|
|
14
14
|
const configBuilder = createConfigBuilder<ConfigType>(configSchema);
|
|
15
|
-
const routeBuilder = createConfigBuilder<RouteType>(routeSchema, { path: ({ page }) => kebabCase(page) });
|
|
15
|
+
const routeBuilder = createConfigBuilder<RouteType>(routeSchema, undefined, { path: ({ page }) => kebabCase(page) });
|
|
16
16
|
const pageBuilder = createConfigBuilder<PageType>(pageSchema);
|
|
17
17
|
const sectionBuilder = createConfigBuilder<SectionType>(sectionSchema);
|
|
18
18
|
const subsectionBuilder = sectionBuilder.$fork();
|
|
@@ -62,7 +62,7 @@ describe('createConfigBuilder', () => {
|
|
|
62
62
|
it('should copy over all values to the new config', async () => {
|
|
63
63
|
const { createConfigBuilder } = await import('./createConfigBuilder.ts');
|
|
64
64
|
const config = createConfigBuilder<ConfigType>(configSchema);
|
|
65
|
-
const route = createConfigBuilder<RouteType>(routeSchema, { path: ({ page }) => kebabCase(page) });
|
|
65
|
+
const route = createConfigBuilder<RouteType>(routeSchema, undefined, { path: ({ page }) => kebabCase(page) });
|
|
66
66
|
const page = createConfigBuilder<PageType>(pageSchema);
|
|
67
67
|
|
|
68
68
|
config
|
|
@@ -85,7 +85,7 @@ describe('createConfigBuilder', () => {
|
|
|
85
85
|
it('should copy over all experiments to the new config', async () => {
|
|
86
86
|
const { createConfigBuilder } = await import('./createConfigBuilder.ts');
|
|
87
87
|
const config = createConfigBuilder<ConfigType>(configSchema);
|
|
88
|
-
const route = createConfigBuilder<RouteType>(routeSchema, { path: ({ page }) => kebabCase(page) });
|
|
88
|
+
const route = createConfigBuilder<RouteType>(routeSchema, undefined, { path: ({ page }) => kebabCase(page) });
|
|
89
89
|
const page = createConfigBuilder<PageType>(pageSchema);
|
|
90
90
|
|
|
91
91
|
config
|
|
@@ -121,7 +121,7 @@ describe('createConfigBuilder', () => {
|
|
|
121
121
|
it('should copy over all disabled flags to the new config', async () => {
|
|
122
122
|
const { createConfigBuilder } = await import('./createConfigBuilder.ts');
|
|
123
123
|
const config = createConfigBuilder<ConfigType>(configSchema);
|
|
124
|
-
const route = createConfigBuilder<RouteType>(routeSchema, { path: ({ page }) => kebabCase(page) });
|
|
124
|
+
const route = createConfigBuilder<RouteType>(routeSchema, undefined, { path: ({ page }) => kebabCase(page) });
|
|
125
125
|
const page = createConfigBuilder<PageType>(pageSchema);
|
|
126
126
|
|
|
127
127
|
config
|
|
@@ -156,7 +156,7 @@ describe('createConfigBuilder', () => {
|
|
|
156
156
|
|
|
157
157
|
it('should copy over all derived value callbacks to the new config builder', async () => {
|
|
158
158
|
const { createConfigBuilder } = await import('./createConfigBuilder.ts');
|
|
159
|
-
const route = createConfigBuilder<RouteType>(routeSchema, { path: ({ page }) => kebabCase(page) });
|
|
159
|
+
const route = createConfigBuilder<RouteType>(routeSchema, undefined, { path: ({ page }) => kebabCase(page) });
|
|
160
160
|
route.page('personalDetails');
|
|
161
161
|
const childRoute = createConfigBuilder<RouteType>(routeSchema);
|
|
162
162
|
childRoute.$extend(route);
|
|
@@ -401,7 +401,7 @@ describe('createConfigBuilder', () => {
|
|
|
401
401
|
it('should add the value to the config', async () => {
|
|
402
402
|
const { createConfigBuilder } = await import('./createConfigBuilder.ts');
|
|
403
403
|
const config = createConfigBuilder<ConfigType>(configSchema);
|
|
404
|
-
const route = createConfigBuilder<RouteType>(routeSchema, { path: ({ page }) => kebabCase(page) });
|
|
404
|
+
const route = createConfigBuilder<RouteType>(routeSchema, undefined, { path: ({ page }) => kebabCase(page) });
|
|
405
405
|
const subRoute = route.$fork();
|
|
406
406
|
|
|
407
407
|
config.routes([
|
|
@@ -430,7 +430,7 @@ describe('createConfigBuilder', () => {
|
|
|
430
430
|
it('should be a valid config', async () => {
|
|
431
431
|
const { createConfigBuilder } = await import('./createConfigBuilder.ts');
|
|
432
432
|
const config = createConfigBuilder<ConfigType>(configSchema);
|
|
433
|
-
const route = createConfigBuilder<RouteType>(routeSchema, { path: ({ page }) => kebabCase(page) });
|
|
433
|
+
const route = createConfigBuilder<RouteType>(routeSchema, undefined, { path: ({ page }) => kebabCase(page) });
|
|
434
434
|
|
|
435
435
|
config.routes([
|
|
436
436
|
route.page('personalDetails').$flush(),
|
|
@@ -4,16 +4,13 @@ import { type ZodError, type z } from 'zod';
|
|
|
4
4
|
import { zodToJsonSchema } from 'zod-to-json-schema';
|
|
5
5
|
import { cloneNonEnumerableValues } from './transformers/cloneNonEnumerableValues.ts';
|
|
6
6
|
import { NonEmumeralProperties } from './types.ts';
|
|
7
|
-
import {
|
|
8
|
-
import { collateObjectPropertyDefaults } from './utils/collateObjectPropertyDefaults.ts';
|
|
7
|
+
import { collateDefaultValues } from './utils/collateDefaultValues.ts';
|
|
9
8
|
import { isDerivedValueCallback } from './utils/isDerivedValueCallback.ts';
|
|
10
9
|
import { isInvalidPropertyOverride } from './utils/isInvalidPropertyOverride.ts';
|
|
11
10
|
import { RESERVED_KEYWORDS, isPropertyReservedWord } from './utils/isPropertyReservedWord.ts';
|
|
12
11
|
import { isSchemaValid } from './utils/isSchemaValid.ts';
|
|
13
|
-
import { isValidPropertyDefinition } from './utils/isValidPropertyDefinition.ts';
|
|
14
12
|
import { isValidValue } from './utils/isValidValue.ts';
|
|
15
13
|
import { jsonStringifyReplacer } from './utils/jsonStringifyReplacer.ts';
|
|
16
|
-
import { recordHasInvalidDefaults } from './utils/recordHasInvalidDefaults.ts';
|
|
17
14
|
import { transformConfigSync } from './utils/transformConfig.ts';
|
|
18
15
|
|
|
19
16
|
export type ConfigBuilder<ZodTypes> = {
|
|
@@ -33,8 +30,13 @@ export type ConfigBuilder<ZodTypes> = {
|
|
|
33
30
|
$values: () => ZodTypes;
|
|
34
31
|
};
|
|
35
32
|
|
|
33
|
+
export type CreateConfigBuilderOptions = {
|
|
34
|
+
type?: string;
|
|
35
|
+
};
|
|
36
|
+
|
|
36
37
|
export const createConfigBuilder = <ZodTypes>(
|
|
37
38
|
zodSchema: z.ZodSchema,
|
|
39
|
+
options: CreateConfigBuilderOptions = {},
|
|
38
40
|
derivedValueCallbacks: Partial<
|
|
39
41
|
Record<
|
|
40
42
|
keyof ZodTypes,
|
|
@@ -52,15 +54,36 @@ export const createConfigBuilder = <ZodTypes>(
|
|
|
52
54
|
};
|
|
53
55
|
|
|
54
56
|
type DerivedValueCallback<K extends keyof Config = keyof Config> = (c: Config) => Config[K];
|
|
55
|
-
let config = cloneDeep(initialValues) as Config;
|
|
56
|
-
const defaultValues = {} as Partial<Config>;
|
|
57
57
|
|
|
58
|
-
|
|
59
|
-
configurable: false,
|
|
60
|
-
enumerable: false,
|
|
61
|
-
value: true,
|
|
62
|
-
});
|
|
58
|
+
const jsonSchema = zodToJsonSchema(zodSchema) as JSONSchema7;
|
|
63
59
|
|
|
60
|
+
if (isSchemaValid(jsonSchema)) {
|
|
61
|
+
throw new Error(`The root type of a config schema must be "object", but received "${String(jsonSchema.type)}"`);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const addNonEnumeralBaseProperties = (conf: Config) => {
|
|
65
|
+
Object.defineProperty(conf, NonEmumeralProperties.ZCB, {
|
|
66
|
+
configurable: false,
|
|
67
|
+
enumerable: false,
|
|
68
|
+
value: true,
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
if (options.type) {
|
|
72
|
+
Object.defineProperty(conf, NonEmumeralProperties.TYPE, {
|
|
73
|
+
configurable: false,
|
|
74
|
+
enumerable: false,
|
|
75
|
+
value: options.type,
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
const createInitialConfig = () => {
|
|
81
|
+
const config = merge(cloneDeep(initialValues), collateDefaultValues(jsonSchema)) as Config;
|
|
82
|
+
addNonEnumeralBaseProperties(config);
|
|
83
|
+
return config;
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
let config = createInitialConfig();
|
|
64
87
|
let callbacks: Partial<Record<keyof Config, DerivedValueCallback>> = { ...derivedValueCallbacks };
|
|
65
88
|
|
|
66
89
|
const configBuilder = {
|
|
@@ -97,17 +120,10 @@ export const createConfigBuilder = <ZodTypes>(
|
|
|
97
120
|
},
|
|
98
121
|
$flush: () => {
|
|
99
122
|
const values = configBuilder.$values();
|
|
100
|
-
config =
|
|
101
|
-
|
|
102
|
-
Object.defineProperty(config, NonEmumeralProperties.ZCB, {
|
|
103
|
-
configurable: false,
|
|
104
|
-
enumerable: false,
|
|
105
|
-
value: true,
|
|
106
|
-
});
|
|
107
|
-
|
|
123
|
+
config = createInitialConfig();
|
|
108
124
|
return values;
|
|
109
125
|
},
|
|
110
|
-
$fork: () => createConfigBuilder<Config>(zodSchema, derivedValueCallbacks),
|
|
126
|
+
$fork: () => createConfigBuilder<Config>(zodSchema, options, derivedValueCallbacks, initialValues),
|
|
111
127
|
$toJson: () => JSON.stringify(configBuilder.$values(), jsonStringifyReplacer, 2),
|
|
112
128
|
$validate: () => {
|
|
113
129
|
try {
|
|
@@ -137,12 +153,6 @@ export const createConfigBuilder = <ZodTypes>(
|
|
|
137
153
|
value: callbacks,
|
|
138
154
|
});
|
|
139
155
|
|
|
140
|
-
const jsonSchema = zodToJsonSchema(zodSchema) as JSONSchema7;
|
|
141
|
-
|
|
142
|
-
if (isSchemaValid(jsonSchema)) {
|
|
143
|
-
throw new Error(`The root type of a config schema must be "object", but received "${String(jsonSchema.type)}"`);
|
|
144
|
-
}
|
|
145
|
-
|
|
146
156
|
for (const propertyName in jsonSchema.properties) {
|
|
147
157
|
if (isPropertyReservedWord(propertyName)) {
|
|
148
158
|
throw new Error(
|
|
@@ -152,47 +162,13 @@ export const createConfigBuilder = <ZodTypes>(
|
|
|
152
162
|
);
|
|
153
163
|
}
|
|
154
164
|
|
|
155
|
-
const
|
|
156
|
-
const propertyDefinition = jsonSchema.properties[propertyName];
|
|
157
|
-
|
|
158
|
-
if (isValidPropertyDefinition(propertyDefinition)) {
|
|
159
|
-
if (propertyDefinition.default) {
|
|
160
|
-
defaultValues[castProperty] = propertyDefinition.default as Config[keyof Config];
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
if (propertyDefinition.type === 'array' && arrayHasInvalidDefaults(propertyDefinition)) {
|
|
164
|
-
throw new Error(
|
|
165
|
-
`When setting schema array defaults for the array assigned to "${String(
|
|
166
|
-
castProperty
|
|
167
|
-
)}", set them on the array and not the item.`
|
|
168
|
-
);
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
if (propertyDefinition.type === 'object') {
|
|
172
|
-
if (recordHasInvalidDefaults(propertyDefinition)) {
|
|
173
|
-
throw new Error(
|
|
174
|
-
`When setting schema property defaults for the value of the record assigned to "${String(
|
|
175
|
-
castProperty
|
|
176
|
-
)}", set them on the record and not the value.`
|
|
177
|
-
);
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
const propertyDefaults = collateObjectPropertyDefaults(propertyDefinition);
|
|
181
|
-
|
|
182
|
-
if (propertyDefaults) {
|
|
183
|
-
defaultValues[castProperty] = propertyDefaults as Config[keyof Config];
|
|
184
|
-
}
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
merge(config, cloneDeep(defaultValues));
|
|
188
|
-
}
|
|
165
|
+
const castPropertyName = propertyName as keyof Config;
|
|
189
166
|
|
|
190
|
-
configBuilder[
|
|
191
|
-
|
|
192
|
-
if (isInvalidPropertyOverride(config[castProperty], override)) {
|
|
167
|
+
configBuilder[castPropertyName] = ((value: Config[keyof Config] | DerivedValueCallback, override?: boolean) => {
|
|
168
|
+
if (isInvalidPropertyOverride(config[castPropertyName], override)) {
|
|
193
169
|
throw new Error(
|
|
194
170
|
`A value already exists for "${String(
|
|
195
|
-
|
|
171
|
+
castPropertyName
|
|
196
172
|
)}". You may be trying to add a new values before flushing the old one. If you intended to override the existing value, pass in true as the second argument.`
|
|
197
173
|
);
|
|
198
174
|
}
|
|
@@ -203,19 +179,19 @@ export const createConfigBuilder = <ZodTypes>(
|
|
|
203
179
|
if (!isValidValue(value)) {
|
|
204
180
|
throw new Error(
|
|
205
181
|
`"${String(
|
|
206
|
-
|
|
182
|
+
castPropertyName
|
|
207
183
|
)}" value has a depth greater than ${MAX_DEPTH}. To pass in objects with a depth greater than ${MAX_DEPTH}, create a builder for that config slice.`
|
|
208
184
|
);
|
|
209
185
|
}
|
|
210
186
|
|
|
211
187
|
if (isDerivedValueCallback<DerivedValueCallback>(value)) {
|
|
212
|
-
callbacks[
|
|
188
|
+
callbacks[castPropertyName] = value;
|
|
213
189
|
propertyValue = value(config);
|
|
214
190
|
} else {
|
|
215
191
|
propertyValue = value;
|
|
216
192
|
}
|
|
217
193
|
|
|
218
|
-
config[
|
|
194
|
+
config[castPropertyName] = propertyValue;
|
|
219
195
|
return configBuilder;
|
|
220
196
|
}) as ConfigBuilder<Config>[keyof Config];
|
|
221
197
|
}
|
package/src/types.ts
CHANGED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { type JSONSchema7 } from 'json-schema';
|
|
2
|
+
import { arrayHasInvalidDefaults } from './arrayHasInvalidDefaults.ts';
|
|
3
|
+
import { collateObjectPropertyDefaults } from './collateObjectPropertyDefaults.ts';
|
|
4
|
+
import { RESERVED_KEYWORDS, isPropertyReservedWord } from './isPropertyReservedWord.ts';
|
|
5
|
+
import { isValidPropertyDefinition } from './isValidPropertyDefinition.ts';
|
|
6
|
+
import { recordHasInvalidDefaults } from './recordHasInvalidDefaults.ts';
|
|
7
|
+
|
|
8
|
+
export const collateDefaultValues = <Config>(jsonSchema: JSONSchema7) => {
|
|
9
|
+
const defaultValues = {} as Partial<Config>;
|
|
10
|
+
|
|
11
|
+
for (const propertyName in jsonSchema.properties) {
|
|
12
|
+
if (isPropertyReservedWord(propertyName)) {
|
|
13
|
+
throw new Error(
|
|
14
|
+
`"${propertyName}" is a reserved keyword within the config builder. Please use a different property name. The full list of reserved keywords is: ${[
|
|
15
|
+
...RESERVED_KEYWORDS,
|
|
16
|
+
].join(', ')}`
|
|
17
|
+
);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const castPropertyName = propertyName as keyof Config;
|
|
21
|
+
const propertyDefinition = jsonSchema.properties[propertyName];
|
|
22
|
+
|
|
23
|
+
if (isValidPropertyDefinition(propertyDefinition)) {
|
|
24
|
+
if (propertyDefinition.default) {
|
|
25
|
+
defaultValues[castPropertyName] = propertyDefinition.default as Config[keyof Config];
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
if (propertyDefinition.type === 'array' && arrayHasInvalidDefaults(propertyDefinition)) {
|
|
29
|
+
throw new Error(
|
|
30
|
+
`When setting schema array defaults for the array assigned to "${String(
|
|
31
|
+
castPropertyName
|
|
32
|
+
)}", set them on the array and not the item.`
|
|
33
|
+
);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
if (propertyDefinition.type === 'object') {
|
|
37
|
+
if (recordHasInvalidDefaults(propertyDefinition)) {
|
|
38
|
+
throw new Error(
|
|
39
|
+
`When setting schema property defaults for the value of the record assigned to "${String(
|
|
40
|
+
castPropertyName
|
|
41
|
+
)}", set them on the record and not the value.`
|
|
42
|
+
);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const propertyDefaults = collateObjectPropertyDefaults(propertyDefinition);
|
|
46
|
+
|
|
47
|
+
if (propertyDefaults) {
|
|
48
|
+
defaultValues[castPropertyName] = propertyDefaults as Config[keyof Config];
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
return defaultValues;
|
|
55
|
+
};
|