zcb 0.1.2 → 0.1.3

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/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "zcb",
3
3
  "description": "Build configs with type safety from zod schema.",
4
- "version": "0.1.2",
4
+ "version": "0.1.3",
5
5
  "author": "Dylan Aubrey",
6
6
  "license": "MIT",
7
7
  "homepage": "https://github.com/badbatch/zod-config-builder",
@@ -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 { arrayHasInvalidDefaults } from './utils/arrayHasInvalidDefaults.ts';
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,6 +30,10 @@ 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,
38
39
  derivedValueCallbacks: Partial<
@@ -45,22 +46,44 @@ export const createConfigBuilder = <ZodTypes>(
45
46
  > = {},
46
47
  initialValues: Partial<{
47
48
  [Key in keyof ZodTypes]: ZodTypes[Key];
48
- }> = {}
49
+ }> = {},
50
+ options: CreateConfigBuilderOptions = {}
49
51
  ): ConfigBuilder<ZodTypes> => {
50
52
  type Config = {
51
53
  [Key in keyof ZodTypes]: ZodTypes[Key];
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
- Object.defineProperty(config, NonEmumeralProperties.ZCB, {
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 = cloneDeep(defaultValues) as 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, derivedValueCallbacks, initialValues, options),
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 castProperty = propertyName as keyof Config;
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[castProperty] = ((value: Config[keyof Config] | DerivedValueCallback, override?: boolean) => {
191
- // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
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
- castProperty
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
- castProperty
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[castProperty] = value;
188
+ callbacks[castPropertyName] = value;
213
189
  propertyValue = value(config);
214
190
  } else {
215
191
  propertyValue = value;
216
192
  }
217
193
 
218
- config[castProperty] = propertyValue;
194
+ config[castPropertyName] = propertyValue;
219
195
  return configBuilder;
220
196
  }) as ConfigBuilder<Config>[keyof Config];
221
197
  }
package/src/types.ts CHANGED
@@ -19,6 +19,7 @@ export enum NonEmumeralProperties {
19
19
  CALLBACKS = '__callbacks',
20
20
  DISABLED = '__disabled',
21
21
  EXPERIMENT = '__experiment',
22
+ TYPE = '__type',
22
23
  ZCB = '__zcb',
23
24
  }
24
25
 
@@ -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
+ };