@ontrails/config 1.0.0-beta.15 → 1.0.0-beta.17

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 (139) hide show
  1. package/CHANGELOG.md +42 -0
  2. package/README.md +2 -2
  3. package/package.json +10 -2
  4. package/src/define-config.ts +6 -3
  5. package/src/extensions.ts +1 -1
  6. package/src/index.ts +0 -1
  7. package/src/registry.ts +1 -1
  8. package/src/resolve.ts +4 -2
  9. package/.turbo/turbo-build.log +0 -1
  10. package/.turbo/turbo-lint.log +0 -3
  11. package/.turbo/turbo-typecheck.log +0 -1
  12. package/dist/app-config.d.ts +0 -65
  13. package/dist/app-config.d.ts.map +0 -1
  14. package/dist/app-config.js +0 -177
  15. package/dist/app-config.js.map +0 -1
  16. package/dist/collect.d.ts +0 -11
  17. package/dist/collect.d.ts.map +0 -1
  18. package/dist/collect.js +0 -81
  19. package/dist/collect.js.map +0 -1
  20. package/dist/compose.d.ts +0 -26
  21. package/dist/compose.d.ts.map +0 -1
  22. package/dist/compose.js +0 -19
  23. package/dist/compose.js.map +0 -1
  24. package/dist/config-layer.d.ts +0 -11
  25. package/dist/config-layer.d.ts.map +0 -1
  26. package/dist/config-layer.js +0 -6
  27. package/dist/config-layer.js.map +0 -1
  28. package/dist/config-resource.d.ts +0 -3
  29. package/dist/config-resource.d.ts.map +0 -1
  30. package/dist/config-resource.js +0 -26
  31. package/dist/config-resource.js.map +0 -1
  32. package/dist/define-config.d.ts +0 -61
  33. package/dist/define-config.d.ts.map +0 -1
  34. package/dist/define-config.js +0 -87
  35. package/dist/define-config.js.map +0 -1
  36. package/dist/derive/env.d.ts +0 -15
  37. package/dist/derive/env.d.ts.map +0 -1
  38. package/dist/derive/env.js +0 -65
  39. package/dist/derive/env.js.map +0 -1
  40. package/dist/derive/example.d.ts +0 -16
  41. package/dist/derive/example.d.ts.map +0 -1
  42. package/dist/derive/example.js +0 -138
  43. package/dist/derive/example.js.map +0 -1
  44. package/dist/derive/helpers.d.ts +0 -35
  45. package/dist/derive/helpers.d.ts.map +0 -1
  46. package/dist/derive/helpers.js +0 -116
  47. package/dist/derive/helpers.js.map +0 -1
  48. package/dist/derive/index.d.ts +0 -4
  49. package/dist/derive/index.d.ts.map +0 -1
  50. package/dist/derive/index.js +0 -4
  51. package/dist/derive/index.js.map +0 -1
  52. package/dist/derive/json-schema.d.ts +0 -18
  53. package/dist/derive/json-schema.d.ts.map +0 -1
  54. package/dist/derive/json-schema.js +0 -97
  55. package/dist/derive/json-schema.js.map +0 -1
  56. package/dist/derive-fields.d.ts +0 -25
  57. package/dist/derive-fields.d.ts.map +0 -1
  58. package/dist/derive-fields.js +0 -147
  59. package/dist/derive-fields.js.map +0 -1
  60. package/dist/derive-provenance.d.ts +0 -30
  61. package/dist/derive-provenance.d.ts.map +0 -1
  62. package/dist/derive-provenance.js +0 -101
  63. package/dist/derive-provenance.js.map +0 -1
  64. package/dist/doctor.d.ts +0 -27
  65. package/dist/doctor.d.ts.map +0 -1
  66. package/dist/doctor.js +0 -131
  67. package/dist/doctor.js.map +0 -1
  68. package/dist/extensions.d.ts +0 -38
  69. package/dist/extensions.d.ts.map +0 -1
  70. package/dist/extensions.js +0 -35
  71. package/dist/extensions.js.map +0 -1
  72. package/dist/index.d.ts +0 -20
  73. package/dist/index.d.ts.map +0 -1
  74. package/dist/index.js +0 -20
  75. package/dist/index.js.map +0 -1
  76. package/dist/merge.d.ts +0 -16
  77. package/dist/merge.d.ts.map +0 -1
  78. package/dist/merge.js +0 -34
  79. package/dist/merge.js.map +0 -1
  80. package/dist/ref.d.ts +0 -29
  81. package/dist/ref.d.ts.map +0 -1
  82. package/dist/ref.js +0 -30
  83. package/dist/ref.js.map +0 -1
  84. package/dist/registry.d.ts +0 -24
  85. package/dist/registry.d.ts.map +0 -1
  86. package/dist/registry.js +0 -12
  87. package/dist/registry.js.map +0 -1
  88. package/dist/resolve.d.ts +0 -21
  89. package/dist/resolve.d.ts.map +0 -1
  90. package/dist/resolve.js +0 -177
  91. package/dist/resolve.js.map +0 -1
  92. package/dist/secret-heuristics.d.ts +0 -10
  93. package/dist/secret-heuristics.d.ts.map +0 -1
  94. package/dist/secret-heuristics.js +0 -11
  95. package/dist/secret-heuristics.js.map +0 -1
  96. package/dist/trails/config-check.d.ts +0 -11
  97. package/dist/trails/config-check.d.ts.map +0 -1
  98. package/dist/trails/config-check.js +0 -53
  99. package/dist/trails/config-check.js.map +0 -1
  100. package/dist/trails/config-describe.d.ts +0 -12
  101. package/dist/trails/config-describe.d.ts.map +0 -1
  102. package/dist/trails/config-describe.js +0 -41
  103. package/dist/trails/config-describe.js.map +0 -1
  104. package/dist/trails/config-explain.d.ts +0 -8
  105. package/dist/trails/config-explain.d.ts.map +0 -1
  106. package/dist/trails/config-explain.js +0 -74
  107. package/dist/trails/config-explain.js.map +0 -1
  108. package/dist/trails/config-init.d.ts +0 -9
  109. package/dist/trails/config-init.d.ts.map +0 -1
  110. package/dist/trails/config-init.js +0 -78
  111. package/dist/trails/config-init.js.map +0 -1
  112. package/dist/workspace.d.ts +0 -9
  113. package/dist/workspace.d.ts.map +0 -1
  114. package/dist/workspace.js +0 -49
  115. package/dist/workspace.js.map +0 -1
  116. package/dist/zod-utils.d.ts +0 -14
  117. package/dist/zod-utils.d.ts.map +0 -1
  118. package/dist/zod-utils.js +0 -41
  119. package/dist/zod-utils.js.map +0 -1
  120. package/src/__tests__/app-config.test.ts +0 -342
  121. package/src/__tests__/compose.test.ts +0 -59
  122. package/src/__tests__/config-check.test.ts +0 -171
  123. package/src/__tests__/config-describe.test.ts +0 -154
  124. package/src/__tests__/config-explain.test.ts +0 -167
  125. package/src/__tests__/config-init.test.ts +0 -210
  126. package/src/__tests__/config-resource.test.ts +0 -87
  127. package/src/__tests__/define-config.test.ts +0 -269
  128. package/src/__tests__/derive-fields.test.ts +0 -158
  129. package/src/__tests__/derive-provenance.test.ts +0 -139
  130. package/src/__tests__/derive.test.ts +0 -269
  131. package/src/__tests__/doctor.test.ts +0 -172
  132. package/src/__tests__/extensions.test.ts +0 -134
  133. package/src/__tests__/ref.test.ts +0 -35
  134. package/src/__tests__/resolve.test.ts +0 -246
  135. package/src/__tests__/workspace.test.ts +0 -64
  136. package/src/workspace.ts +0 -56
  137. package/tsconfig.json +0 -9
  138. package/tsconfig.tests.json +0 -10
  139. package/tsconfig.tsbuildinfo +0 -1
@@ -1,342 +0,0 @@
1
- import { describe, expect, test, beforeEach, afterEach } from 'bun:test';
2
- import { mkdir, mkdtemp, rm } from 'node:fs/promises';
3
- import { join } from 'node:path';
4
- import { tmpdir } from 'node:os';
5
- import { z } from 'zod';
6
-
7
- import type { AppConfig } from '../app-config.js';
8
- import { appConfig } from '../app-config.js';
9
- import { deriveConfigFields } from '../derive-fields.js';
10
- import { checkConfig } from '../doctor.js';
11
- import { configRef } from '../ref.js';
12
-
13
- const testSchema = z.object({
14
- output: z.string().describe('Output directory').default('./output'),
15
- verbose: z.boolean().default(false),
16
- });
17
-
18
- const createJsonConfig = () =>
19
- appConfig('myapp', {
20
- formats: ['json'],
21
- schema: testSchema,
22
- });
23
-
24
- const writeJsonConfig = (filePath: string, output: string) =>
25
- Bun.write(filePath, JSON.stringify({ output }));
26
-
27
- const resolveOutput = async (
28
- config: AppConfig<typeof testSchema>,
29
- filePath: string
30
- ): Promise<string> => {
31
- const result = await config.resolve({ path: filePath });
32
- expect(result.isOk()).toBe(true);
33
- return result.unwrap().output;
34
- };
35
-
36
- describe('appConfig()', () => {
37
- describe('creation', () => {
38
- test('returns an object with the provided name', () => {
39
- const config = appConfig('myapp', { schema: testSchema });
40
- expect(config.name).toBe('myapp');
41
- });
42
-
43
- test('returns the provided schema', () => {
44
- const config = appConfig('myapp', { schema: testSchema });
45
- expect(config.schema).toBe(testSchema);
46
- });
47
-
48
- test('defaults formats to toml, json, jsonc, yaml when not specified', () => {
49
- const config = appConfig('myapp', { schema: testSchema });
50
- expect(config.formats).toEqual(['toml', 'json', 'jsonc', 'yaml']);
51
- });
52
-
53
- test('uses provided formats', () => {
54
- const config = appConfig('myapp', {
55
- formats: ['json', 'jsonc'],
56
- schema: testSchema,
57
- });
58
- expect(config.formats).toEqual(['json', 'jsonc']);
59
- });
60
-
61
- test('defaults dotfile to false when not specified', () => {
62
- const config = appConfig('myapp', { schema: testSchema });
63
- expect(config.dotfile).toBe(false);
64
- });
65
-
66
- test('uses provided dotfile value', () => {
67
- const config = appConfig('myapp', {
68
- dotfile: true,
69
- schema: testSchema,
70
- });
71
- expect(config.dotfile).toBe(true);
72
- });
73
- });
74
-
75
- describe('resolve() with explicit path', () => {
76
- let tempDir: string;
77
-
78
- beforeEach(async () => {
79
- tempDir = await mkdtemp(join(tmpdir(), 'trails-config-'));
80
- });
81
-
82
- afterEach(async () => {
83
- await rm(tempDir, { force: true, recursive: true });
84
- });
85
-
86
- test('reads and validates a JSON config file', async () => {
87
- const filePath = join(tempDir, 'myapp.config.json');
88
- await Bun.write(
89
- filePath,
90
- JSON.stringify({ output: './dist', verbose: true })
91
- );
92
-
93
- const config = createJsonConfig();
94
- const result = await config.resolve({ path: filePath });
95
-
96
- expect(result.isOk()).toBe(true);
97
- expect(result.unwrap()).toEqual({ output: './dist', verbose: true });
98
- });
99
-
100
- test('reads and validates a TOML config file', async () => {
101
- const filePath = join(tempDir, 'myapp.config.toml');
102
- await Bun.write(filePath, 'output = "./build"\nverbose = true\n');
103
-
104
- const config = appConfig('myapp', {
105
- formats: ['toml'],
106
- schema: testSchema,
107
- });
108
- const result = await config.resolve({ path: filePath });
109
-
110
- expect(result.isOk()).toBe(true);
111
- expect(result.unwrap()).toEqual({ output: './build', verbose: true });
112
- });
113
-
114
- test('applies schema defaults for missing fields', async () => {
115
- const filePath = join(tempDir, 'myapp.config.json');
116
- await Bun.write(filePath, JSON.stringify({}));
117
-
118
- const config = createJsonConfig();
119
- const result = await config.resolve({ path: filePath });
120
-
121
- expect(result.isOk()).toBe(true);
122
- expect(result.unwrap()).toEqual({ output: './output', verbose: false });
123
- });
124
-
125
- test('re-reads config files after they change on disk', async () => {
126
- const filePath = join(tempDir, 'myapp.config.json');
127
- await writeJsonConfig(filePath, './first');
128
-
129
- const config = createJsonConfig();
130
-
131
- expect(await resolveOutput(config, filePath)).toBe('./first');
132
-
133
- await Bun.sleep(10);
134
- await writeJsonConfig(filePath, './second');
135
-
136
- expect(await resolveOutput(config, filePath)).toBe('./second');
137
- });
138
-
139
- test('returns Result.err when file does not exist', async () => {
140
- const filePath = join(tempDir, 'nonexistent.json');
141
-
142
- const config = createJsonConfig();
143
- const result = await config.resolve({ path: filePath });
144
-
145
- expect(result.isErr()).toBe(true);
146
- });
147
-
148
- test('returns Result.err when file has invalid content', async () => {
149
- const filePath = join(tempDir, 'myapp.config.json');
150
- await Bun.write(
151
- filePath,
152
- JSON.stringify({ output: 42, verbose: 'not-a-bool' })
153
- );
154
-
155
- const config = createJsonConfig();
156
- const result = await config.resolve({ path: filePath });
157
-
158
- expect(result.isErr()).toBe(true);
159
- });
160
- });
161
-
162
- describe('resolve() with discovery', () => {
163
- let tempDir: string;
164
-
165
- beforeEach(async () => {
166
- tempDir = await mkdtemp(join(tmpdir(), 'trails-config-'));
167
- });
168
-
169
- afterEach(async () => {
170
- await rm(tempDir, { force: true, recursive: true });
171
- });
172
-
173
- test('discovers config file in cwd', async () => {
174
- const filePath = join(tempDir, 'myapp.config.json');
175
- await writeJsonConfig(filePath, './found');
176
-
177
- const config = createJsonConfig();
178
- const result = await config.resolve({ cwd: tempDir });
179
-
180
- expect(result.isOk()).toBe(true);
181
- expect(result.unwrap().output).toBe('./found');
182
- });
183
-
184
- test('discovers config file by walking up directories', async () => {
185
- const nested = join(tempDir, 'a', 'b', 'c');
186
- await mkdir(nested, { recursive: true });
187
-
188
- const filePath = join(tempDir, 'myapp.config.json');
189
- await Bun.write(filePath, JSON.stringify({ output: './parent' }));
190
-
191
- const config = appConfig('myapp', {
192
- formats: ['json'],
193
- schema: testSchema,
194
- });
195
- const result = await config.resolve({ cwd: nested });
196
-
197
- expect(result.isOk()).toBe(true);
198
- expect(result.unwrap().output).toBe('./parent');
199
- });
200
-
201
- test('tries formats in order', async () => {
202
- await Bun.write(
203
- join(tempDir, 'myapp.config.toml'),
204
- 'output = "./from-toml"\n'
205
- );
206
- await Bun.write(
207
- join(tempDir, 'myapp.config.json'),
208
- JSON.stringify({ output: './from-json' })
209
- );
210
-
211
- const config = appConfig('myapp', {
212
- formats: ['toml', 'json'],
213
- schema: testSchema,
214
- });
215
- const result = await config.resolve({ cwd: tempDir });
216
-
217
- expect(result.isOk()).toBe(true);
218
- expect(result.unwrap().output).toBe('./from-toml');
219
- });
220
-
221
- test('includes jsonc in the default discovery set', async () => {
222
- await Bun.write(
223
- join(tempDir, 'myapp.config.jsonc'),
224
- '{\n // comment\n "output": "./from-jsonc"\n}\n'
225
- );
226
-
227
- const config = appConfig('myapp', { schema: testSchema });
228
- const result = await config.resolve({ cwd: tempDir });
229
-
230
- expect(result.isOk()).toBe(true);
231
- expect(result.unwrap().output).toBe('./from-jsonc');
232
- });
233
-
234
- test('returns Result.err when no config file found', async () => {
235
- const config = appConfig('myapp', { schema: testSchema });
236
- const result = await config.resolve({ cwd: tempDir });
237
-
238
- expect(result.isErr()).toBe(true);
239
- });
240
- });
241
-
242
- describe('file naming conventions', () => {
243
- let tempDir: string;
244
-
245
- beforeEach(async () => {
246
- tempDir = await mkdtemp(join(tmpdir(), 'trails-config-'));
247
- });
248
-
249
- afterEach(async () => {
250
- await rm(tempDir, { force: true, recursive: true });
251
- });
252
-
253
- test('dotfile: true searches for .myapprc.* files', async () => {
254
- await Bun.write(
255
- join(tempDir, '.myapprc.json'),
256
- JSON.stringify({ output: './dotfile' })
257
- );
258
-
259
- const config = appConfig('myapp', {
260
- dotfile: true,
261
- formats: ['json'],
262
- schema: testSchema,
263
- });
264
- const result = await config.resolve({ cwd: tempDir });
265
-
266
- expect(result.isOk()).toBe(true);
267
- expect(result.unwrap().output).toBe('./dotfile');
268
- });
269
-
270
- test('dotfile: false searches for myapp.config.* files', async () => {
271
- await Bun.write(
272
- join(tempDir, 'myapp.config.json'),
273
- JSON.stringify({ output: './standard' })
274
- );
275
-
276
- const config = appConfig('myapp', {
277
- dotfile: false,
278
- formats: ['json'],
279
- schema: testSchema,
280
- });
281
- const result = await config.resolve({ cwd: tempDir });
282
-
283
- expect(result.isOk()).toBe(true);
284
- expect(result.unwrap().output).toBe('./standard');
285
- });
286
-
287
- test('dotfile: true does NOT find myapp.config.* files', async () => {
288
- await Bun.write(
289
- join(tempDir, 'myapp.config.json'),
290
- JSON.stringify({ output: './nope' })
291
- );
292
-
293
- const config = appConfig('myapp', {
294
- dotfile: true,
295
- formats: ['json'],
296
- schema: testSchema,
297
- });
298
- const result = await config.resolve({ cwd: tempDir });
299
-
300
- expect(result.isErr()).toBe(true);
301
- });
302
- });
303
-
304
- describe('method delegations', () => {
305
- test('describe() returns same result as deriveConfigFields(schema)', () => {
306
- const config = appConfig('myapp', { schema: testSchema });
307
- const methodResult = config.describe();
308
- const standaloneResult = deriveConfigFields(testSchema);
309
-
310
- expect(methodResult).toEqual(standaloneResult);
311
- });
312
-
313
- test('check() returns same result as checkConfig(schema, values)', () => {
314
- const config = appConfig('myapp', { schema: testSchema });
315
- const values = { output: './dist', verbose: true };
316
-
317
- const methodResult = config.check(values);
318
- const standaloneResult = checkConfig(testSchema, values);
319
-
320
- expect(methodResult).toEqual(standaloneResult);
321
- });
322
-
323
- test('ref() returns same result as configRef(path)', () => {
324
- const config = appConfig('myapp', { schema: testSchema });
325
- const methodResult = config.ref('output');
326
- const standaloneResult = configRef('output');
327
-
328
- expect(methodResult).toEqual(standaloneResult);
329
- });
330
-
331
- test('explain() delegates to deriveConfigProvenance with bound schema', () => {
332
- const config = appConfig('myapp', { schema: testSchema });
333
- const resolved = { output: './dist', verbose: true };
334
-
335
- const result = config.explain({ resolved });
336
-
337
- expect(result.length).toBeGreaterThan(0);
338
- expect(result[0]?.path).toBeDefined();
339
- expect(result[0]?.source).toBeDefined();
340
- });
341
- });
342
- });
@@ -1,59 +0,0 @@
1
- import { describe, expect, test } from 'bun:test';
2
-
3
- import { z } from 'zod';
4
-
5
- import { collectResourceConfigs } from '../compose.js';
6
-
7
- // ---------------------------------------------------------------------------
8
- // Tests
9
- // ---------------------------------------------------------------------------
10
-
11
- describe('collectResourceConfigs', () => {
12
- test('extracts config schemas from resources that declare them', () => {
13
- const dbSchema = z.object({ url: z.string().url() });
14
- const cacheSchema = z.object({ ttl: z.number() });
15
-
16
- const resources = [
17
- { config: dbSchema, id: 'db.main' },
18
- { config: cacheSchema, id: 'cache.main' },
19
- ];
20
-
21
- const entries = collectResourceConfigs(resources);
22
-
23
- expect(entries).toHaveLength(2);
24
- expect(entries[0]).toEqual({ resourceId: 'db.main', schema: dbSchema });
25
- expect(entries[1]).toEqual({
26
- resourceId: 'cache.main',
27
- schema: cacheSchema,
28
- });
29
- });
30
-
31
- test('excludes resources without config', () => {
32
- const schema = z.object({ url: z.string() });
33
-
34
- const resources = [
35
- { config: schema, id: 'db.main' },
36
- { id: 'counter.main' },
37
- { config: undefined, id: 'logger.main' },
38
- ];
39
-
40
- const entries = collectResourceConfigs(resources);
41
-
42
- expect(entries).toHaveLength(1);
43
- expect(entries[0]?.resourceId).toBe('db.main');
44
- });
45
-
46
- test('returns empty array when no resources have config', () => {
47
- const resources = [{ id: 'counter.main' }, { id: 'logger.main' }];
48
-
49
- const entries = collectResourceConfigs(resources);
50
-
51
- expect(entries).toEqual([]);
52
- });
53
-
54
- test('returns empty array for empty input', () => {
55
- const entries = collectResourceConfigs([]);
56
-
57
- expect(entries).toEqual([]);
58
- });
59
- });
@@ -1,171 +0,0 @@
1
- import { describe, expect, test } from 'bun:test';
2
- import { createResourceLookup } from '@ontrails/core';
3
- import type { TrailContext } from '@ontrails/core';
4
- import { z } from 'zod';
5
-
6
- import { configCheck } from '../trails/config-check.js';
7
- import type { ConfigState } from '../registry.js';
8
-
9
- /**
10
- * Build a TrailContext with configResource resolved in extensions.
11
- */
12
- const buildCtx = (state: ConfigState): TrailContext => {
13
- const extensions = { config: state };
14
- const ctx: TrailContext = {
15
- abortSignal: AbortSignal.timeout(5000),
16
- cwd: '/tmp',
17
- env: {},
18
- extensions,
19
- requestId: 'test',
20
- resource: undefined as unknown as TrailContext['resource'],
21
- workspaceRoot: '/tmp',
22
- };
23
- const withLookup = {
24
- ...ctx,
25
- resource: createResourceLookup(() => withLookup),
26
- };
27
- return withLookup;
28
- };
29
-
30
- describe('config.check trail', () => {
31
- describe('identity', () => {
32
- test('has id "config.check"', () => {
33
- expect(configCheck.id).toBe('config.check');
34
- });
35
-
36
- test('has kind "trail"', () => {
37
- expect(configCheck.kind).toBe('trail');
38
- });
39
-
40
- test('has intent "read"', () => {
41
- expect(configCheck.intent).toBe('read');
42
- });
43
-
44
- test('has infrastructure meta', () => {
45
- expect(configCheck.meta).toEqual({ category: 'infrastructure' });
46
- });
47
-
48
- test('has output schema', () => {
49
- expect(configCheck.output).toBeDefined();
50
- });
51
-
52
- test('declares configResource dependency', () => {
53
- expect(configCheck.resources).toBeDefined();
54
- expect(configCheck.resources?.length).toBe(1);
55
- });
56
- });
57
-
58
- describe('examples', () => {
59
- test('has at least one example', () => {
60
- expect(configCheck.examples?.length).toBeGreaterThanOrEqual(1);
61
- });
62
- });
63
-
64
- describe('wired behavior', () => {
65
- test('reports valid when all required fields present', async () => {
66
- const schema = z.object({
67
- host: z.string().default('localhost'),
68
- port: z.number().default(3000),
69
- });
70
- const state: ConfigState = {
71
- resolved: { host: 'localhost', port: 3000 },
72
- schema,
73
- };
74
- const ctx = buildCtx(state);
75
- const result = await configCheck.blaze({ values: {} }, ctx);
76
-
77
- expect(result.isOk()).toBe(true);
78
- const value = result.unwrap();
79
- expect(value.valid).toBe(true);
80
- expect(value.diagnostics.length).toBeGreaterThan(0);
81
- });
82
-
83
- test('reports missing for required fields without values', async () => {
84
- const schema = z.object({
85
- host: z.string(),
86
- port: z.number(),
87
- });
88
- const state: ConfigState = {
89
- resolved: {},
90
- schema,
91
- };
92
- const ctx = buildCtx(state);
93
- const result = await configCheck.blaze({ values: {} }, ctx);
94
-
95
- expect(result.isOk()).toBe(true);
96
- const value = result.unwrap();
97
- expect(value.valid).toBe(false);
98
- const missing = value.diagnostics.filter((d) => d.status === 'missing');
99
- expect(missing.length).toBe(2);
100
- });
101
-
102
- test('uses input values when provided to override resolved', async () => {
103
- const schema = z.object({
104
- port: z.number(),
105
- });
106
- const state: ConfigState = {
107
- resolved: {},
108
- schema,
109
- };
110
- const ctx = buildCtx(state);
111
- const result = await configCheck.blaze({ values: { port: 8080 } }, ctx);
112
-
113
- expect(result.isOk()).toBe(true);
114
- const value = result.unwrap();
115
- expect(value.valid).toBe(true);
116
- expect(value.diagnostics.length).toBe(1);
117
- expect(value.diagnostics[0]?.status).toBe('valid');
118
- });
119
-
120
- test('deep merges nested input overrides with resolved values', async () => {
121
- const schema = z.object({
122
- db: z.object({
123
- host: z.string(),
124
- port: z.number(),
125
- }),
126
- });
127
- const state: ConfigState = {
128
- resolved: { db: { host: 'localhost', port: 5432 } },
129
- schema,
130
- };
131
- const ctx = buildCtx(state);
132
- const result = await configCheck.blaze(
133
- { values: { db: { port: 6543 } } },
134
- ctx
135
- );
136
-
137
- expect(result.isOk()).toBe(true);
138
- expect(result.unwrap().valid).toBe(true);
139
- expect(result.unwrap().diagnostics).toEqual([
140
- expect.objectContaining({
141
- path: 'db.host',
142
- status: 'valid',
143
- value: 'localhost',
144
- }),
145
- expect.objectContaining({
146
- path: 'db.port',
147
- status: 'valid',
148
- value: 6543,
149
- }),
150
- ]);
151
- });
152
-
153
- test('reports default status for fields using defaults', async () => {
154
- const schema = z.object({
155
- port: z.number().default(3000),
156
- });
157
- const state: ConfigState = {
158
- resolved: {},
159
- schema,
160
- };
161
- const ctx = buildCtx(state);
162
- const result = await configCheck.blaze({ values: {} }, ctx);
163
-
164
- expect(result.isOk()).toBe(true);
165
- const defaults = result
166
- .unwrap()
167
- .diagnostics.filter((d) => d.status === 'default');
168
- expect(defaults.length).toBe(1);
169
- });
170
- });
171
- });