zcb 0.3.4 → 0.3.5

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.
Files changed (97) hide show
  1. package/README.md +1 -1
  2. package/dist/cjs/cli.cjs +1 -1
  3. package/dist/cjs/cli.cjs.map +1 -1
  4. package/dist/cjs/index.cjs +1 -1
  5. package/dist/cjs/index.cjs.map +1 -1
  6. package/dist/cjs/templates/config.ts.hbs +1 -1
  7. package/dist/esm/cli.mjs +1 -1
  8. package/dist/esm/cli.mjs.map +1 -1
  9. package/dist/esm/index.mjs +1 -1
  10. package/dist/esm/index.mjs.map +1 -1
  11. package/dist/esm/templates/config.ts.hbs +1 -1
  12. package/dist/production.analysis.txt +25 -25
  13. package/dist/types/cjs/cli.d.cts +7 -1
  14. package/dist/types/cjs/cli.d.cts.map +1 -1
  15. package/dist/types/cjs/createConfigBuilder.d.cts +3 -3
  16. package/dist/types/cjs/createConfigBuilder.d.cts.map +1 -1
  17. package/dist/types/cjs/createConfigParser.d.cts.map +1 -1
  18. package/dist/types/cjs/transformers/runExperiments.d.cts.map +1 -1
  19. package/dist/types/cjs/utils/arrayHasInvalidDefaults.d.cts +2 -2
  20. package/dist/types/cjs/utils/arrayHasInvalidDefaults.d.cts.map +1 -1
  21. package/dist/types/cjs/utils/collateDefaultValues.d.cts +2 -2
  22. package/dist/types/cjs/utils/collateDefaultValues.d.cts.map +1 -1
  23. package/dist/types/cjs/utils/collateObjectPropertyDefaults.d.cts +2 -2
  24. package/dist/types/cjs/utils/collateObjectPropertyDefaults.d.cts.map +1 -1
  25. package/dist/types/cjs/utils/importValidateTransformWriteConfig.d.cts +2 -2
  26. package/dist/types/cjs/utils/importValidateTransformWriteConfig.d.cts.map +1 -1
  27. package/dist/types/cjs/utils/isInvalidPropertyOverride.d.cts.map +1 -1
  28. package/dist/types/cjs/utils/isPropertyReservedWord.d.cts.map +1 -1
  29. package/dist/types/cjs/utils/isSchemaValid.d.cts +2 -2
  30. package/dist/types/cjs/utils/isSchemaValid.d.cts.map +1 -1
  31. package/dist/types/cjs/utils/isValidPropertyDefinition.d.cts +2 -2
  32. package/dist/types/cjs/utils/isValidPropertyDefinition.d.cts.map +1 -1
  33. package/dist/types/cjs/utils/objectPropertyHasDefaults.d.cts +2 -2
  34. package/dist/types/cjs/utils/objectPropertyHasDefaults.d.cts.map +1 -1
  35. package/dist/types/cjs/utils/recordHasInvalidDefaults.d.cts +2 -2
  36. package/dist/types/cjs/utils/recordHasInvalidDefaults.d.cts.map +1 -1
  37. package/dist/types/cjs/utils/transformConfig.d.cts.map +1 -1
  38. package/dist/types/cjs/utils/transformWriteConfig.d.cts.map +1 -1
  39. package/dist/types/esm/cli.d.ts +7 -1
  40. package/dist/types/esm/cli.d.ts.map +1 -1
  41. package/dist/types/esm/createConfigBuilder.d.ts +3 -3
  42. package/dist/types/esm/createConfigBuilder.d.ts.map +1 -1
  43. package/dist/types/esm/createConfigParser.d.ts.map +1 -1
  44. package/dist/types/esm/transformers/runExperiments.d.ts.map +1 -1
  45. package/dist/types/esm/utils/arrayHasInvalidDefaults.d.ts +2 -2
  46. package/dist/types/esm/utils/arrayHasInvalidDefaults.d.ts.map +1 -1
  47. package/dist/types/esm/utils/collateDefaultValues.d.ts +2 -2
  48. package/dist/types/esm/utils/collateDefaultValues.d.ts.map +1 -1
  49. package/dist/types/esm/utils/collateObjectPropertyDefaults.d.ts +2 -2
  50. package/dist/types/esm/utils/collateObjectPropertyDefaults.d.ts.map +1 -1
  51. package/dist/types/esm/utils/importValidateTransformWriteConfig.d.ts +2 -2
  52. package/dist/types/esm/utils/importValidateTransformWriteConfig.d.ts.map +1 -1
  53. package/dist/types/esm/utils/isInvalidPropertyOverride.d.ts.map +1 -1
  54. package/dist/types/esm/utils/isPropertyReservedWord.d.ts.map +1 -1
  55. package/dist/types/esm/utils/isSchemaValid.d.ts +2 -2
  56. package/dist/types/esm/utils/isSchemaValid.d.ts.map +1 -1
  57. package/dist/types/esm/utils/isValidPropertyDefinition.d.ts +2 -2
  58. package/dist/types/esm/utils/isValidPropertyDefinition.d.ts.map +1 -1
  59. package/dist/types/esm/utils/objectPropertyHasDefaults.d.ts +2 -2
  60. package/dist/types/esm/utils/objectPropertyHasDefaults.d.ts.map +1 -1
  61. package/dist/types/esm/utils/recordHasInvalidDefaults.d.ts +2 -2
  62. package/dist/types/esm/utils/recordHasInvalidDefaults.d.ts.map +1 -1
  63. package/dist/types/esm/utils/transformConfig.d.ts.map +1 -1
  64. package/dist/types/esm/utils/transformWriteConfig.d.ts.map +1 -1
  65. package/dist/types/tsconfig.build.tsbuildinfo +1 -1
  66. package/package.json +45 -43
  67. package/pnpm-workspace.yaml +4 -0
  68. package/src/__testUtils__/builtConfig.ts +1 -1
  69. package/src/__testUtils__/configBuilder.ts +28 -22
  70. package/src/__testUtils__/schema.ts +2 -2
  71. package/src/cli.ts +9 -3
  72. package/src/createConfigBuilder.test.ts +27 -23
  73. package/src/createConfigBuilder.ts +40 -42
  74. package/src/createConfigParser.ts +1 -1
  75. package/src/createConfigReader.ts +2 -2
  76. package/src/templates/config.ts.hbs +1 -1
  77. package/src/transformers/cloneNonEnumerableValues.ts +1 -1
  78. package/src/transformers/runExperiments.ts +16 -5
  79. package/src/utils/arrayHasInvalidDefaults.ts +3 -3
  80. package/src/utils/collateDefaultValues.ts +5 -5
  81. package/src/utils/collateObjectPropertyDefaults.ts +4 -2
  82. package/src/utils/importValidateTransformWriteConfig.ts +25 -13
  83. package/src/utils/isInvalidPropertyOverride.ts +1 -1
  84. package/src/utils/isPropertyReservedWord.ts +1 -1
  85. package/src/utils/isSchemaValid.ts +2 -2
  86. package/src/utils/isValidPropertyDefinition.ts +3 -3
  87. package/src/utils/objectPropertyHasDefaults.ts +3 -3
  88. package/src/utils/recordHasInvalidDefaults.ts +3 -3
  89. package/src/utils/transformConfig.ts +16 -4
  90. package/src/utils/transformWriteConfig.test.ts +1 -1
  91. package/src/utils/transformWriteConfig.ts +6 -4
  92. package/dist/types/cjs/utils/jsonStringifyReplacer.d.cts +0 -109
  93. package/dist/types/cjs/utils/jsonStringifyReplacer.d.cts.map +0 -1
  94. package/dist/types/esm/utils/jsonStringifyReplacer.d.ts +0 -109
  95. package/dist/types/esm/utils/jsonStringifyReplacer.d.ts.map +0 -1
  96. package/src/__snapshots__/createConfigBuilder.test.ts.snap +0 -30
  97. package/src/utils/jsonStringifyReplacer.ts +0 -6
package/package.json CHANGED
@@ -1,19 +1,22 @@
1
1
  {
2
2
  "name": "zcb",
3
3
  "description": "Build configs with type safety from zod schema.",
4
- "version": "0.3.4",
4
+ "version": "0.3.5",
5
5
  "author": "miami-man",
6
6
  "license": "MIT",
7
7
  "homepage": "https://github.com/badbatch/zod-config-builder",
8
- "repository": "badbatch/zod-config-builder",
8
+ "repository": {
9
+ "type": "git",
10
+ "url": "badbatch/zod-config-builder"
11
+ },
9
12
  "bugs": "https://github.com/badbatch/zod-config-builder/issues",
10
13
  "type": "module",
11
14
  "bin": {
12
15
  "zcb": "./bin/zcb.js"
13
16
  },
14
- "main": "./dist/cjs/index.cjs",
17
+ "main": "./dist/cjs/index.mjs",
15
18
  "module": "./dist/esm/index.mjs",
16
- "types": "./dist/types/cjs/index.d.cts",
19
+ "types": "./dist/types/cjs/index.d.ts",
17
20
  "imports": {
18
21
  "#*": "./src/*"
19
22
  },
@@ -42,56 +45,55 @@
42
45
  "@types/fs-extra": "^11.0.4",
43
46
  "@types/json-schema": "^7.0.15",
44
47
  "@types/lodash-es": "^4.17.12",
45
- "@types/shelljs": "^0.8.17",
46
- "@types/uuid": "^10.0.0",
47
- "@types/yargs": "^17.0.33",
48
- "core-js": "^3.45.0",
49
- "fs-extra": "^11.3.1",
50
- "handlebars": "^4.7.8",
48
+ "@types/shelljs": "^0.10.0",
49
+ "@types/uuid": "^11.0.0",
50
+ "@types/yargs": "^17.0.35",
51
+ "core-js": "^3.49.0",
52
+ "fs-extra": "^11.3.6",
53
+ "handlebars": "^4.7.9",
51
54
  "json-schema": "^0.4.0",
52
- "lodash-es": "^4.17.21",
55
+ "lodash-es": "^4.18.1",
53
56
  "shelljs": "^0.10.0",
54
57
  "ts-node": "^10.9.2",
55
58
  "ts-toolbelt": "^9.6.0",
56
- "type-fest": "^4.41.0",
57
- "uuid": "^11.1.0",
59
+ "type-fest": "^5.8.0",
60
+ "uuid": "^14.0.1",
58
61
  "yargs": "^18.0.0",
59
- "zod": "^3.25.76",
60
- "zod-to-json-schema": "^3.24.6"
62
+ "zod": "^4.4.3",
63
+ "zod-to-json-schema": "^3.25.2"
61
64
  },
62
65
  "devDependencies": {
63
- "@commitlint/cli": "^19.8.1",
64
- "@jest/globals": "^29.7.0",
65
- "@repodog/cli": "^1.6.45",
66
- "@repodog/commitlint-config": "^19.0.5",
67
- "@repodog/eslint-config": "^9.0.18",
68
- "@repodog/eslint-config-jest": "^9.0.6",
69
- "@repodog/jest-config": "^29.0.5",
70
- "@repodog/markdownlint-config": "^1.2.5",
71
- "@repodog/prettier-config": "^3.0.3",
72
- "@repodog/rollup-config": "^4.0.11",
73
- "@repodog/swc-config": "^1.0.12",
74
- "@repodog/syncpack-config": "^13.0.4",
75
- "@repodog/ts-config": "^5.0.5",
76
- "@rollup/plugin-swc": "^0.3.0",
77
- "@swc/core": "^1.13.3",
66
+ "@commitlint/cli": "^21.2.1",
67
+ "@jest/globals": "^30.4.1",
68
+ "@repodog/cli": "^1.6.59",
69
+ "@repodog/commitlint-config": "^21.0.2",
70
+ "@repodog/eslint-config": "^9.0.29",
71
+ "@repodog/eslint-config-jest": "^9.0.9",
72
+ "@repodog/jest-config": "^30.0.4",
73
+ "@repodog/markdownlint-config": "^1.2.7",
74
+ "@repodog/prettier-config": "^3.0.5",
75
+ "@repodog/rollup-config": "^4.0.17",
76
+ "@repodog/swc-config": "^1.0.15",
77
+ "@repodog/syncpack-config": "^15.0.3",
78
+ "@repodog/ts-config": "^6.0.3",
79
+ "@rollup/plugin-swc": "^0.4.1",
80
+ "@swc/core": "^1.15.43",
78
81
  "@swc/jest": "^0.2.39",
79
- "@types/jest": "^29.5.13",
82
+ "@types/jest": "^30.0.0",
80
83
  "@types/node": "^24.2.1",
81
- "cts-types": "^0.0.10",
82
- "del-cli": "^6.0.0",
83
- "eslint": "^9.33.0",
84
+ "cts-types": "^0.0.11",
85
+ "del-cli": "^7.0.0",
86
+ "eslint": "^9.39.4",
84
87
  "generate-changelog": "^1.8.0",
85
88
  "husky": "^9.1.7",
86
- "jest": "^29.7.0",
87
- "jest-environment-jsdom": "^29.7.0",
88
- "jest-environment-node": "^29.7.0",
89
- "markdownlint-cli2": "^0.18.1",
90
- "prettier": "^3.6.2",
91
- "rollup": "^4.46.2",
89
+ "jest": "^30.4.2",
90
+ "jest-environment-node": "^30.4.1",
91
+ "markdownlint-cli2": "^0.23.0",
92
+ "prettier": "^3.9.5",
93
+ "rollup": "^4.62.2",
92
94
  "suppress-experimental-warnings": "^2.0.0",
93
- "syncpack": "^13.0.4",
94
- "typescript": "^5.9.2"
95
+ "syncpack": "^15.3.2",
96
+ "typescript": "^6.0.3"
95
97
  },
96
98
  "keywords": [
97
99
  "config",
@@ -113,7 +115,7 @@
113
115
  "lint:code": "eslint .",
114
116
  "lint:docs": "markdownlint-cli2 --config \".markdownlint.json\" \"**/*.md\" \"!**/node_modules/**\"",
115
117
  "repodog": "repodog",
116
- "syncpack": "syncpack format && syncpack list-mismatches && syncpack lint-semver-ranges",
118
+ "syncpack": "syncpack format --check && syncpack lint",
117
119
  "test": "COMPILER=swc node --require=suppress-experimental-warnings --experimental-vm-modules node_modules/jest/bin/jest.js",
118
120
  "type-check": "tsc --noEmit",
119
121
  "validate": "pnpm run syncpack && pnpm run build && pnpm run lint && pnpm run type-check && pnpm run test",
@@ -0,0 +1,4 @@
1
+ allowBuilds:
2
+ '@swc/core': false
3
+ core-js: false
4
+ unrs-resolver: false
@@ -1,4 +1,4 @@
1
- /* eslint-disable prettier/prettier, import-x/no-default-export, unicorn/numeric-separators-style, unicorn/no-null */
1
+ /* eslint-disable import-x/no-default-export, unicorn/numeric-separators-style, unicorn/no-null */
2
2
  /* This file is autogenerated, do not edit directly, your changes will not perist. */
3
3
 
4
4
  export default {
@@ -17,6 +17,32 @@ const pageBuilder = createConfigBuilder<PageType>(pageSchema);
17
17
  const sectionBuilder = createConfigBuilder<SectionType>(sectionSchema);
18
18
  const subsectionBuilder = sectionBuilder.$fork();
19
19
 
20
+ const contactDetailsPage = pageBuilder
21
+ .name('contactDetails')
22
+ .sections([
23
+ sectionBuilder.name('header').$flush(),
24
+ sectionBuilder
25
+ .name('body')
26
+ .sections([subsectionBuilder.name('main').$flush(), subsectionBuilder.name('sidebar').$flush()])
27
+ .$flush(),
28
+ sectionBuilder.name('footer').$flush(),
29
+ ])
30
+ .$flush();
31
+
32
+ const personalDetailPage = pageBuilder
33
+ .name('personalDetails')
34
+ .sections([
35
+ sectionBuilder.name('header').$flush(),
36
+ sectionBuilder
37
+ .name('body')
38
+ .sections([subsectionBuilder.name('main').$flush(), subsectionBuilder.name('sidebar').$flush()])
39
+ .$flush(),
40
+ sectionBuilder.name('footer').$flush(),
41
+ ])
42
+ .$flush();
43
+
44
+ // Required for how builder is executed
45
+ // eslint-disable-next-line unicorn/no-top-level-side-effects
20
46
  configBuilder
21
47
  .countryCode('GB')
22
48
  // Want to test using null value
@@ -29,28 +55,8 @@ configBuilder
29
55
  languageCodes?.length && countryCode ? languageCodes.map(code => `${code}_${countryCode}`) : [],
30
56
  )
31
57
  .pages({
32
- contactDetails: pageBuilder
33
- .name('contactDetails')
34
- .sections([
35
- sectionBuilder.name('header').$flush(),
36
- sectionBuilder
37
- .name('body')
38
- .sections([subsectionBuilder.name('main').$flush(), subsectionBuilder.name('sidebar').$flush()])
39
- .$flush(),
40
- sectionBuilder.name('footer').$flush(),
41
- ])
42
- .$flush(),
43
- personalDetails: pageBuilder
44
- .name('personalDetails')
45
- .sections([
46
- sectionBuilder.name('header').$flush(),
47
- sectionBuilder
48
- .name('body')
49
- .sections([subsectionBuilder.name('main').$flush(), subsectionBuilder.name('sidebar').$flush()])
50
- .$flush(),
51
- sectionBuilder.name('footer').$flush(),
52
- ])
53
- .$flush(),
58
+ contactDetails: contactDetailsPage,
59
+ personalDetails: personalDetailPage,
54
60
  })
55
61
  .routes([routeBuilder.page('personalDetails').$flush(), routeBuilder.page('contactDetails').$flush()])
56
62
  .timeouts({ apollo: 10_000 })
@@ -48,9 +48,9 @@ export const configSchema = z.object({
48
48
  languageCodes: z.array(z.enum(languageCodes)).optional(),
49
49
  locales: z.array(z.string().regex(/[a-z]{2}_[A-Z]{2}/)).optional(),
50
50
  name: z.string().optional(),
51
- pages: z.record(pageSchema).optional(),
51
+ pages: z.record(z.string(), pageSchema).optional(),
52
52
  routes: z.array(routeSchema).optional(),
53
- timeouts: z.record(z.number()).optional(),
53
+ timeouts: z.record(z.string(), z.number()).optional(),
54
54
  timezone: z.enum(timezones).optional(),
55
55
  });
56
56
 
package/src/cli.ts CHANGED
@@ -5,9 +5,15 @@ import yargs, { type Argv } from 'yargs';
5
5
  import { hideBin } from 'yargs/helpers';
6
6
  import { importValidateTransformWriteConfig } from './utils/importValidateTransformWriteConfig.ts';
7
7
 
8
- export type Commands = 'build' | 'watch';
8
+ export type CliCommands = 'build' | 'watch';
9
9
 
10
- const generateArguments = (cmdYargs: Argv) =>
10
+ export type CliArguments = Argv<{
11
+ 'experiments-callback-file'?: string;
12
+ 'input-file': string;
13
+ 'output-file': string;
14
+ }>;
15
+
16
+ const generateArguments = (cmdYargs: Argv): CliArguments =>
11
17
  cmdYargs
12
18
  .positional('input-file', {
13
19
  demandOption: true,
@@ -24,7 +30,7 @@ const generateArguments = (cmdYargs: Argv) =>
24
30
  type: 'string',
25
31
  });
26
32
 
27
- export const cli = () => {
33
+ export const cli = (): void => {
28
34
  // yargs does not provide a way to pass generic to type args.
29
35
  // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
30
36
  const argv = yargs(hideBin(process.argv)) as Argv<{
@@ -1,5 +1,5 @@
1
1
  import { kebabCase } from 'lodash-es';
2
- import { ZodType, z } from 'zod';
2
+ import { z } from 'zod';
3
3
  import {
4
4
  type ConfigType,
5
5
  type PageType,
@@ -112,13 +112,14 @@ describe('createConfigBuilder', () => {
112
112
  __experiment: 'FEAT_ALPHA@0.0.1',
113
113
  // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
114
114
  pages: expect.objectContaining({
115
- // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
115
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, unicorn/max-nested-calls
116
116
  contactDetails: expect.objectContaining({
117
117
  __experiment: 'FEAT_BRAVO@0.0.1',
118
118
  }),
119
119
  }),
120
120
  // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
121
121
  routes: expect.arrayContaining([
122
+ // eslint-disable-next-line unicorn/max-nested-calls
122
123
  expect.objectContaining({
123
124
  __experiment: 'FEAT_CHARLIE@0.0.1',
124
125
  }),
@@ -153,13 +154,14 @@ describe('createConfigBuilder', () => {
153
154
  __disabled: true,
154
155
  // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
155
156
  pages: expect.objectContaining({
156
- // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
157
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, unicorn/max-nested-calls
157
158
  contactDetails: expect.objectContaining({
158
159
  __disabled: true,
159
160
  }),
160
161
  }),
161
162
  // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
162
163
  routes: expect.arrayContaining([
164
+ // eslint-disable-next-line unicorn/max-nested-calls
163
165
  expect.objectContaining({
164
166
  __disabled: true,
165
167
  }),
@@ -223,7 +225,7 @@ describe('createConfigBuilder', () => {
223
225
  const { createConfigBuilder } = await import('./createConfigBuilder.ts');
224
226
 
225
227
  const extendedSchema = configSchema.extend({
226
- flags: z.record(z.boolean()).optional().default({ alpha: true, bravo: false, charlie: false }),
228
+ flags: z.record(z.string(), z.boolean()).optional().default({ alpha: true, bravo: false, charlie: false }),
227
229
  });
228
230
 
229
231
  const config = createConfigBuilder<z.infer<typeof extendedSchema>>(extendedSchema);
@@ -329,7 +331,7 @@ describe('createConfigBuilder', () => {
329
331
  const { createConfigBuilder } = await import('./createConfigBuilder.ts');
330
332
 
331
333
  const extendedSchema = configSchema.extend({
332
- flags: z.record(z.boolean().default(true)).optional(),
334
+ flags: z.record(z.string(), z.boolean().default(true)).optional(),
333
335
  });
334
336
 
335
337
  expect(() => createConfigBuilder<z.infer<typeof extendedSchema>>(extendedSchema)).toThrow(
@@ -635,27 +637,29 @@ describe('createConfigBuilder', () => {
635
637
  });
636
638
 
637
639
  describe('when a user stringifies the config', () => {
638
- describe('when a config value is a zod schema', () => {
639
- it('should stringify the value to JSON schema correctly', async () => {
640
- const { createConfigBuilder } = await import('./createConfigBuilder.ts');
641
-
642
- const extendedSchema = configSchema.extend({
643
- schema: z.instanceof(ZodType),
644
- });
645
-
646
- const config = createConfigBuilder<z.infer<typeof extendedSchema>>(extendedSchema);
640
+ it('should stringify the value to JSON correctly', async () => {
641
+ const { createConfigBuilder } = await import('./createConfigBuilder.ts');
642
+ const config = createConfigBuilder<ConfigType>(configSchema);
647
643
 
648
- config.schema(
649
- z
650
- .object({
651
- alpha: z.string(),
652
- bravo: z.boolean(),
653
- })
654
- .optional(),
644
+ config
645
+ .countryCode('GB')
646
+ .languageCodes(['en'])
647
+ .locales(({ countryCode, languageCodes }) =>
648
+ // eslint-disable-next-line jest/no-conditional-in-test
649
+ languageCodes?.length && countryCode ? languageCodes.map(code => `${code}_${countryCode}`) : [],
655
650
  );
656
651
 
657
- expect(config.$toJson()).toMatchSnapshot();
658
- });
652
+ expect(config.$toJson()).toMatchInlineSnapshot(`
653
+ "{
654
+ "countryCode": "GB",
655
+ "languageCodes": [
656
+ "en"
657
+ ],
658
+ "locales": [
659
+ "en_GB"
660
+ ]
661
+ }"
662
+ `);
659
663
  });
660
664
  });
661
665
  });
@@ -1,8 +1,6 @@
1
- import { type JSONSchema7 } from 'json-schema';
2
1
  import { cloneDeep, merge } from 'lodash-es';
3
2
  import { v4 as uuidV4 } from 'uuid';
4
- import { type ZodError, type z } from 'zod';
5
- import { zodToJsonSchema } from 'zod-to-json-schema';
3
+ import { ZodError, z } from 'zod';
6
4
  import { cloneNonEnumerableValues } from './transformers/cloneNonEnumerableValues.ts';
7
5
  import { collateDefaultValues } from './utils/collateDefaultValues.ts';
8
6
  import { isDerivedValueCallback } from './utils/isDerivedValueCallback.ts';
@@ -10,7 +8,6 @@ import { isInvalidPropertyOverride } from './utils/isInvalidPropertyOverride.ts'
10
8
  import { RESERVED_KEYWORDS, isPropertyReservedWord } from './utils/isPropertyReservedWord.ts';
11
9
  import { isSchemaValid } from './utils/isSchemaValid.ts';
12
10
  import { isValidValue } from './utils/isValidValue.ts';
13
- import { jsonStringifyReplacer } from './utils/jsonStringifyReplacer.ts';
14
11
  import { transformConfigSync } from './utils/transformConfig.ts';
15
12
 
16
13
  export type ConfigBuilder<ZodTypes> = {
@@ -20,7 +17,7 @@ export type ConfigBuilder<ZodTypes> = {
20
17
  ) => ConfigBuilder<ZodTypes>;
21
18
  } & {
22
19
  $disable: () => ConfigBuilder<ZodTypes>;
23
- $errors: () => ZodError['errors'];
20
+ $errors: () => ZodError['issues'];
24
21
  $experiment: (key: string) => ConfigBuilder<ZodTypes>;
25
22
  $extend: (configBuilder: ConfigBuilder<ZodTypes>) => ConfigBuilder<ZodTypes>;
26
23
  $flush: () => ZodTypes;
@@ -38,7 +35,7 @@ export type CreateConfigBuilderOptions = {
38
35
  };
39
36
 
40
37
  export const createConfigBuilder = <ZodTypes>(
41
- zodSchema: z.ZodSchema,
38
+ zodSchema: z.ZodType,
42
39
  options: CreateConfigBuilderOptions = {},
43
40
  derivedValueCallbacks: Partial<
44
41
  Record<
@@ -58,9 +55,7 @@ export const createConfigBuilder = <ZodTypes>(
58
55
 
59
56
  type DerivedValueCallback<K extends keyof Config = keyof Config> = (c: Config) => Config[K];
60
57
 
61
- // Added to simplify type returned from zodToJsonSchema.
62
- // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
63
- const jsonSchema = zodToJsonSchema(zodSchema) as JSONSchema7;
58
+ const jsonSchema = z.toJSONSchema(zodSchema);
64
59
 
65
60
  if (isSchemaValid(jsonSchema)) {
66
61
  throw new Error(`The root type of a config schema must be "object", but received "${String(jsonSchema.type)}"`);
@@ -68,29 +63,31 @@ export const createConfigBuilder = <ZodTypes>(
68
63
 
69
64
  const uuid = options.overrides?.uuid ?? uuidV4;
70
65
 
71
- const addNonEnumeralBaseProperties = (conf: Config) => {
72
- Object.defineProperty(conf, '__zcb', {
73
- configurable: false,
74
- enumerable: false,
75
- value: true,
76
- });
77
-
78
- Object.defineProperty(conf, '__id', {
79
- configurable: false,
80
- enumerable: false,
81
- value: uuid(),
82
- });
83
-
84
- if (options.type) {
85
- Object.defineProperty(conf, '__type', {
66
+ const addNonEnumeralBaseProperties = (conf: Config): void => {
67
+ Object.defineProperties(conf, {
68
+ __id: {
86
69
  configurable: false,
87
70
  enumerable: false,
88
- value: options.type,
89
- });
90
- }
71
+ value: uuid(),
72
+ },
73
+ __zcb: {
74
+ configurable: false,
75
+ enumerable: false,
76
+ value: true,
77
+ },
78
+ ...(options.type
79
+ ? {
80
+ __type: {
81
+ configurable: false,
82
+ enumerable: false,
83
+ value: options.type,
84
+ },
85
+ }
86
+ : undefined),
87
+ });
91
88
  };
92
89
 
93
- const createInitialConfig = () => {
90
+ const createInitialConfig = (): Config => {
94
91
  // Not casting here as loads of downstream impacts that make
95
92
  // subsequent typing really difficult.
96
93
  // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
@@ -102,7 +99,7 @@ export const createConfigBuilder = <ZodTypes>(
102
99
  let config = createInitialConfig();
103
100
  let callbacks: Partial<Record<keyof Config, DerivedValueCallback>> = { ...derivedValueCallbacks };
104
101
 
105
- // Aimed at making it easier for Typescript to derive type.
102
+ // Aimed at making it easier for TypeScript to derive type.
106
103
  // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
107
104
  const configBuilder = {
108
105
  $disable: () => {
@@ -119,9 +116,7 @@ export const createConfigBuilder = <ZodTypes>(
119
116
  zodSchema.parse(configBuilder.$values());
120
117
  return [];
121
118
  } catch (error: unknown) {
122
- // This will always be an instance of ZodError.
123
- // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
124
- return (error as ZodError).errors;
119
+ return error instanceof ZodError ? error.issues : [];
125
120
  }
126
121
  },
127
122
  $experiment: (key: string) => {
@@ -136,7 +131,7 @@ export const createConfigBuilder = <ZodTypes>(
136
131
  $extend: (builder: ConfigBuilder<Config>) => {
137
132
  config = transformConfigSync<Config>(builder.$values(), [cloneNonEnumerableValues]);
138
133
  // @ts-expect-error private property
139
- // As __callbacks is private property, Typescript doesn't know what type is.
134
+ // As __callbacks is private property, TypeScript doesn't know what type is.
140
135
  // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
141
136
  callbacks = { ...builder.__callbacks } as Partial<Record<keyof Config, DerivedValueCallback>>;
142
137
  },
@@ -146,7 +141,7 @@ export const createConfigBuilder = <ZodTypes>(
146
141
  return values;
147
142
  },
148
143
  $fork: () => createConfigBuilder<Config>(zodSchema, options, derivedValueCallbacks, initialValues),
149
- $toJson: () => JSON.stringify(configBuilder.$values(), jsonStringifyReplacer, 2),
144
+ $toJson: () => JSON.stringify(configBuilder.$values(), undefined, 2),
150
145
  $validate: () => {
151
146
  try {
152
147
  zodSchema.parse(configBuilder.$values());
@@ -157,13 +152,15 @@ export const createConfigBuilder = <ZodTypes>(
157
152
  }
158
153
  },
159
154
  $values: () => {
160
- for (const property in callbacks) {
161
- const callback = callbacks[property];
155
+ for (const propertyName in callbacks) {
156
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
157
+ const castPropertyName = propertyName as keyof Config;
158
+ // eslint-disable-next-line unicorn/no-unsafe-property-key
159
+ const callback = callbacks[castPropertyName];
162
160
 
163
161
  if (callback) {
164
- // Typescript not inferring property is key of Config.
165
- // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
166
- config[property as keyof Config] = callback(config);
162
+ // eslint-disable-next-line unicorn/no-unsafe-property-key
163
+ config[castPropertyName] = callback(config);
167
164
  }
168
165
  }
169
166
 
@@ -190,12 +187,12 @@ export const createConfigBuilder = <ZodTypes>(
190
187
  // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
191
188
  const castPropertyName = propertyName as keyof Config;
192
189
 
193
- if (!jsonSchema.required?.includes(propertyName) && !(propertyName in config)) {
190
+ if (!jsonSchema.required?.includes(propertyName) && !Object.hasOwn(config, propertyName)) {
194
191
  // @ts-expect-error Optional properties we want to exist in the config with undefined initial values.
195
192
  config[propertyName] = undefined;
196
193
  }
197
194
 
198
- // Aimed at making it easier for Typescript to derive type.
195
+ // Aimed at making it easier for TypeScript to derive type.
199
196
  // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
200
197
  configBuilder[castPropertyName] = ((value: Config[keyof Config] | DerivedValueCallback, override?: boolean) => {
201
198
  if (isInvalidPropertyOverride(config[castPropertyName], override)) {
@@ -206,7 +203,6 @@ export const createConfigBuilder = <ZodTypes>(
206
203
  );
207
204
  }
208
205
 
209
- let propertyValue: Config[keyof Config];
210
206
  const MAX_DEPTH = 1;
211
207
 
212
208
  if (!isValidValue(value)) {
@@ -217,6 +213,8 @@ export const createConfigBuilder = <ZodTypes>(
217
213
  );
218
214
  }
219
215
 
216
+ let propertyValue: Config[keyof Config];
217
+
220
218
  if (isDerivedValueCallback<DerivedValueCallback>(value)) {
221
219
  callbacks[castPropertyName] = value;
222
220
  propertyValue = value(config);
@@ -5,7 +5,7 @@ import { transformConfig } from './utils/transformConfig.ts';
5
5
  export const createConfigParser = async <Config extends object>(
6
6
  config: Config,
7
7
  { experimentsCallback }: ConfigParserOptions = {},
8
- ) => {
8
+ ): Promise<Config> => {
9
9
  const handlers: TransformConfigHandler[] = [];
10
10
 
11
11
  if (experimentsCallback) {
@@ -9,7 +9,7 @@ export interface ConfigReader<Config extends object> {
9
9
  }
10
10
 
11
11
  export const createConfigReader = <Config extends object>(config: Config): ConfigReader<Config> => {
12
- // Aimed at reducing the amount of work for typescript to resolve type.
12
+ // Aimed at reducing the amount of work for TypeScript to resolve type.
13
13
  // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
14
14
  return {
15
15
  read: <P extends Path<Config>>(path: P, variables?: Record<string, string | number>) => {
@@ -32,7 +32,7 @@ export const createConfigReader = <Config extends object>(config: Config): Confi
32
32
  }
33
33
 
34
34
  return Object.keys(variables).reduce<string>((acc, key) => {
35
- return acc.replace(new RegExp(`{{${key}}}`), String(variables[key]));
35
+ return acc.replace(new RegExp(`{{${key}}}`), () => String(variables[key]));
36
36
  }, output);
37
37
  },
38
38
  scope: <S extends Scope<Config>>(scope: S) => {
@@ -1,4 +1,4 @@
1
- /* eslint-disable prettier/prettier, import-x/no-default-export, unicorn/numeric-separators-style, unicorn/no-null */
1
+ /* eslint-disable import-x/no-default-export, unicorn/numeric-separators-style, unicorn/no-null */
2
2
  /* This file is autogenerated, do not edit directly, your changes will not perist. */
3
3
 
4
4
  export default {{{ config }}} as const;
@@ -10,7 +10,7 @@ export const cloneNonEnumerableValues: TransformConfigHandlerSync = <
10
10
  config: Config2,
11
11
  ) => {
12
12
  for (const nonEnumerableKey of NON_ENUMERABLE_KEYS) {
13
- if (nonEnumerableKey in config) {
13
+ if (Object.hasOwn(config, nonEnumerableKey)) {
14
14
  Object.defineProperty(clone, nonEnumerableKey, {
15
15
  configurable: false,
16
16
  enumerable: false,
@@ -1,16 +1,27 @@
1
- import { type AnyRecord, type Buckets, type RunExperimentsCallback, type TransformConfigHandler } from '../types.ts';
1
+ import {
2
+ type AnyRecord,
3
+ type Buckets,
4
+ type RunExperimentsCallback,
5
+ type TransformConfigHandler,
6
+ type TransformConfigHandlerReturnType,
7
+ } from '../types.ts';
2
8
 
3
9
  export const runExperiments =
4
10
  (callback: RunExperimentsCallback): TransformConfigHandler =>
5
- async <Config extends AnyRecord>(clone: Config, config: Config) => {
11
+ async <Config extends AnyRecord>(
12
+ clone: Config,
13
+ config: Config,
14
+ ): Promise<TransformConfigHandlerReturnType<Config>> => {
6
15
  if ('__experiment' in config && typeof config.__experiment === 'object') {
7
- // As __experiment is private property, Typescript doesn't know what type is.
16
+ // As __experiment is private property, TypeScript doesn't know what type is.
8
17
  // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
9
18
  const { buckets, id } = config.__experiment as { buckets: Buckets<Config>; id: string };
10
19
  const bucket = await callback(id);
11
20
 
12
- if (bucket && bucket in buckets && buckets[bucket]) {
13
- return buckets[bucket];
21
+ if (Object.hasOwn(buckets, bucket)) {
22
+ // The check above essentially makes sure the value cannot be null
23
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
24
+ return buckets[bucket]!;
14
25
  }
15
26
  }
16
27
 
@@ -1,6 +1,6 @@
1
- import { type JSONSchema7 } from 'json-schema';
1
+ import { type JSONSchema } from 'zod/v4/core';
2
2
 
3
- export const arrayHasInvalidDefaults = (propertyDefinition: JSONSchema7) => {
3
+ export const arrayHasInvalidDefaults = (propertyDefinition: JSONSchema.JSONSchema): boolean => {
4
4
  const { items } = propertyDefinition;
5
- return items && typeof items === 'object' && 'default' in items;
5
+ return !!items && typeof items === 'object' && 'default' in items;
6
6
  };