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

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 (141) hide show
  1. package/.turbo/turbo-lint.log +1 -1
  2. package/CHANGELOG.md +18 -0
  3. package/README.md +204 -0
  4. package/dist/app-config.d.ts +4 -4
  5. package/dist/app-config.d.ts.map +1 -1
  6. package/dist/app-config.js +12 -7
  7. package/dist/app-config.js.map +1 -1
  8. package/dist/compose.d.ts +11 -11
  9. package/dist/compose.d.ts.map +1 -1
  10. package/dist/compose.js +7 -7
  11. package/dist/compose.js.map +1 -1
  12. package/dist/config-layer.d.ts +3 -3
  13. package/dist/config-resource.d.ts +3 -0
  14. package/dist/config-resource.d.ts.map +1 -0
  15. package/dist/{config-service.js → config-resource.js} +7 -7
  16. package/dist/config-resource.js.map +1 -0
  17. package/dist/define-config.d.ts +9 -9
  18. package/dist/define-config.d.ts.map +1 -1
  19. package/dist/define-config.js +9 -12
  20. package/dist/define-config.js.map +1 -1
  21. package/dist/{generate → derive}/env.d.ts +1 -1
  22. package/dist/derive/env.d.ts.map +1 -0
  23. package/dist/{generate → derive}/env.js +3 -3
  24. package/dist/derive/env.js.map +1 -0
  25. package/dist/{generate → derive}/example.d.ts +1 -1
  26. package/dist/derive/example.d.ts.map +1 -0
  27. package/dist/{generate → derive}/example.js +21 -19
  28. package/dist/derive/example.js.map +1 -0
  29. package/dist/{generate → derive}/helpers.d.ts +1 -1
  30. package/dist/derive/helpers.d.ts.map +1 -0
  31. package/dist/{generate → derive}/helpers.js +2 -2
  32. package/dist/derive/helpers.js.map +1 -0
  33. package/dist/derive/index.d.ts +4 -0
  34. package/dist/derive/index.d.ts.map +1 -0
  35. package/dist/derive/index.js +4 -0
  36. package/dist/derive/index.js.map +1 -0
  37. package/dist/{generate → derive}/json-schema.d.ts +1 -1
  38. package/dist/derive/json-schema.d.ts.map +1 -0
  39. package/dist/{generate → derive}/json-schema.js +1 -1
  40. package/dist/derive/json-schema.js.map +1 -0
  41. package/dist/{describe.d.ts → derive-fields.d.ts} +2 -2
  42. package/dist/derive-fields.d.ts.map +1 -0
  43. package/dist/{describe.js → derive-fields.js} +2 -2
  44. package/dist/derive-fields.js.map +1 -0
  45. package/dist/{explain.d.ts → derive-provenance.d.ts} +5 -5
  46. package/dist/derive-provenance.d.ts.map +1 -0
  47. package/dist/{explain.js → derive-provenance.js} +8 -21
  48. package/dist/derive-provenance.js.map +1 -0
  49. package/dist/doctor.d.ts.map +1 -1
  50. package/dist/doctor.js +1 -37
  51. package/dist/doctor.js.map +1 -1
  52. package/dist/extensions.d.ts +1 -1
  53. package/dist/extensions.js +1 -1
  54. package/dist/index.d.ts +7 -8
  55. package/dist/index.d.ts.map +1 -1
  56. package/dist/index.js +6 -7
  57. package/dist/index.js.map +1 -1
  58. package/dist/ref.d.ts +8 -3
  59. package/dist/ref.d.ts.map +1 -1
  60. package/dist/ref.js +8 -3
  61. package/dist/ref.js.map +1 -1
  62. package/dist/registry.d.ts +2 -2
  63. package/dist/resolve.d.ts +6 -6
  64. package/dist/resolve.d.ts.map +1 -1
  65. package/dist/resolve.js +11 -8
  66. package/dist/resolve.js.map +1 -1
  67. package/dist/trails/config-check.d.ts +1 -1
  68. package/dist/trails/config-check.d.ts.map +1 -1
  69. package/dist/trails/config-check.js +12 -12
  70. package/dist/trails/config-check.js.map +1 -1
  71. package/dist/trails/config-describe.d.ts +1 -1
  72. package/dist/trails/config-describe.d.ts.map +1 -1
  73. package/dist/trails/config-describe.js +9 -9
  74. package/dist/trails/config-describe.js.map +1 -1
  75. package/dist/trails/config-explain.d.ts +1 -1
  76. package/dist/trails/config-explain.d.ts.map +1 -1
  77. package/dist/trails/config-explain.js +15 -15
  78. package/dist/trails/config-explain.js.map +1 -1
  79. package/dist/trails/config-init.d.ts +1 -1
  80. package/dist/trails/config-init.d.ts.map +1 -1
  81. package/dist/trails/config-init.js +16 -16
  82. package/dist/trails/config-init.js.map +1 -1
  83. package/dist/workspace.d.ts.map +1 -1
  84. package/dist/workspace.js +5 -0
  85. package/dist/workspace.js.map +1 -1
  86. package/package.json +2 -2
  87. package/src/__tests__/app-config.test.ts +19 -6
  88. package/src/__tests__/compose.test.ts +15 -15
  89. package/src/__tests__/config-check.test.ts +7 -7
  90. package/src/__tests__/config-describe.test.ts +7 -7
  91. package/src/__tests__/config-explain.test.ts +7 -7
  92. package/src/__tests__/config-init.test.ts +7 -7
  93. package/src/__tests__/{config-provision.test.ts → config-resource.test.ts} +12 -12
  94. package/src/__tests__/define-config.test.ts +24 -18
  95. package/src/__tests__/{describe.test.ts → derive-fields.test.ts} +11 -11
  96. package/src/__tests__/{explain.test.ts → derive-provenance.test.ts} +18 -18
  97. package/src/__tests__/{generate.test.ts → derive.test.ts} +30 -30
  98. package/src/__tests__/resolve.test.ts +31 -31
  99. package/src/app-config.ts +23 -12
  100. package/src/compose.ts +16 -22
  101. package/src/{config-provision.ts → config-resource.ts} +5 -5
  102. package/src/define-config.ts +12 -15
  103. package/src/{generate → derive}/env.ts +3 -3
  104. package/src/{generate → derive}/example.ts +1 -1
  105. package/src/{generate → derive}/helpers.ts +1 -1
  106. package/src/derive/index.ts +3 -0
  107. package/src/{generate → derive}/json-schema.ts +1 -1
  108. package/src/{describe.ts → derive-fields.ts} +1 -1
  109. package/src/{explain.ts → derive-provenance.ts} +6 -6
  110. package/src/index.ts +12 -18
  111. package/src/registry.ts +2 -2
  112. package/src/resolve.ts +13 -13
  113. package/src/trails/config-check.ts +3 -3
  114. package/src/trails/config-describe.ts +5 -5
  115. package/src/trails/config-explain.ts +13 -13
  116. package/src/trails/config-init.ts +10 -10
  117. package/src/workspace.ts +5 -0
  118. package/tsconfig.tests.json +10 -0
  119. package/tsconfig.tsbuildinfo +1 -1
  120. package/dist/config-service.d.ts +0 -3
  121. package/dist/config-service.d.ts.map +0 -1
  122. package/dist/config-service.js.map +0 -1
  123. package/dist/describe.d.ts.map +0 -1
  124. package/dist/describe.js.map +0 -1
  125. package/dist/explain.d.ts.map +0 -1
  126. package/dist/explain.js.map +0 -1
  127. package/dist/generate/env.d.ts.map +0 -1
  128. package/dist/generate/env.js.map +0 -1
  129. package/dist/generate/example.d.ts.map +0 -1
  130. package/dist/generate/example.js.map +0 -1
  131. package/dist/generate/helpers.d.ts.map +0 -1
  132. package/dist/generate/helpers.js.map +0 -1
  133. package/dist/generate/index.d.ts +0 -4
  134. package/dist/generate/index.d.ts.map +0 -1
  135. package/dist/generate/index.js +0 -4
  136. package/dist/generate/index.js.map +0 -1
  137. package/dist/generate/json-schema.d.ts.map +0 -1
  138. package/dist/generate/json-schema.js.map +0 -1
  139. package/src/__tests__/config-gate.test.ts +0 -53
  140. package/src/config-gate.ts +0 -15
  141. package/src/generate/index.ts +0 -3
@@ -3,10 +3,10 @@ import { z } from 'zod';
3
3
 
4
4
  import { deprecated, env, secret } from '../extensions.js';
5
5
  import {
6
- generateEnvExample,
7
- generateExample,
8
- generateJsonSchema,
9
- } from '../generate/index.js';
6
+ deriveConfigEnvExample,
7
+ deriveConfigExample,
8
+ deriveConfigJsonSchema,
9
+ } from '../derive/index.js';
10
10
 
11
11
  /** Shared test schema used across generator tests. */
12
12
  const testSchema = z.object({
@@ -39,10 +39,10 @@ const nestedSchema = z.object({
39
39
  }),
40
40
  });
41
41
 
42
- describe('generateExample()', () => {
42
+ describe('deriveConfigExample()', () => {
43
43
  describe('TOML format', () => {
44
44
  test('produces valid TOML with comments for descriptions', () => {
45
- const result = generateExample(testSchema, 'toml');
45
+ const result = deriveConfigExample(testSchema, 'toml');
46
46
 
47
47
  expect(result).toContain('# The server hostname');
48
48
  expect(result).toContain('host = "localhost"');
@@ -53,13 +53,13 @@ describe('generateExample()', () => {
53
53
  });
54
54
 
55
55
  test('annotates deprecated fields in TOML', () => {
56
- const result = generateExample(annotatedSchema, 'toml');
56
+ const result = deriveConfigExample(annotatedSchema, 'toml');
57
57
 
58
58
  expect(result).toContain('# DEPRECATED: Use newEndpoint instead');
59
59
  });
60
60
 
61
61
  test('handles nested objects as TOML sections', () => {
62
- const result = generateExample(nestedSchema, 'toml');
62
+ const result = deriveConfigExample(nestedSchema, 'toml');
63
63
 
64
64
  expect(result).toContain('[db]');
65
65
  expect(result).toContain('[server]');
@@ -70,7 +70,7 @@ describe('generateExample()', () => {
70
70
 
71
71
  describe('JSON format', () => {
72
72
  test('produces valid JSON without comments', () => {
73
- const result = generateExample(testSchema, 'json');
73
+ const result = deriveConfigExample(testSchema, 'json');
74
74
  const parsed = JSON.parse(result);
75
75
 
76
76
  expect(parsed).toEqual({
@@ -81,7 +81,7 @@ describe('generateExample()', () => {
81
81
  });
82
82
 
83
83
  test('handles nested objects', () => {
84
- const result = generateExample(nestedSchema, 'json');
84
+ const result = deriveConfigExample(nestedSchema, 'json');
85
85
  const parsed = JSON.parse(result);
86
86
 
87
87
  expect(parsed).toHaveProperty('db');
@@ -92,7 +92,7 @@ describe('generateExample()', () => {
92
92
 
93
93
  describe('JSONC format', () => {
94
94
  test('produces JSON with // comments for descriptions', () => {
95
- const result = generateExample(testSchema, 'jsonc');
95
+ const result = deriveConfigExample(testSchema, 'jsonc');
96
96
 
97
97
  expect(result).toContain('// The server hostname');
98
98
  expect(result).toContain('"host"');
@@ -100,13 +100,13 @@ describe('generateExample()', () => {
100
100
  });
101
101
 
102
102
  test('annotates deprecated fields in JSONC', () => {
103
- const result = generateExample(annotatedSchema, 'jsonc');
103
+ const result = deriveConfigExample(annotatedSchema, 'jsonc');
104
104
 
105
105
  expect(result).toContain('// DEPRECATED: Use newEndpoint instead');
106
106
  });
107
107
 
108
108
  test('handles nested objects', () => {
109
- const result = generateExample(nestedSchema, 'jsonc');
109
+ const result = deriveConfigExample(nestedSchema, 'jsonc');
110
110
 
111
111
  expect(result).toContain('"db"');
112
112
  expect(result).toContain('"host"');
@@ -117,7 +117,7 @@ describe('generateExample()', () => {
117
117
 
118
118
  describe('YAML format', () => {
119
119
  test('produces valid YAML with comments for descriptions', () => {
120
- const result = generateExample(testSchema, 'yaml');
120
+ const result = deriveConfigExample(testSchema, 'yaml');
121
121
 
122
122
  expect(result).toContain('# The server hostname');
123
123
  expect(result).toContain('host: "localhost"');
@@ -128,13 +128,13 @@ describe('generateExample()', () => {
128
128
  });
129
129
 
130
130
  test('annotates deprecated fields in YAML', () => {
131
- const result = generateExample(annotatedSchema, 'yaml');
131
+ const result = deriveConfigExample(annotatedSchema, 'yaml');
132
132
 
133
133
  expect(result).toContain('# DEPRECATED: Use newEndpoint instead');
134
134
  });
135
135
 
136
136
  test('handles nested objects', () => {
137
- const result = generateExample(nestedSchema, 'yaml');
137
+ const result = deriveConfigExample(nestedSchema, 'yaml');
138
138
 
139
139
  expect(result).toContain('db:');
140
140
  expect(result).toContain(' host: "localhost"');
@@ -143,9 +143,9 @@ describe('generateExample()', () => {
143
143
  });
144
144
  });
145
145
 
146
- describe('generateJsonSchema()', () => {
146
+ describe('deriveConfigJsonSchema()', () => {
147
147
  test('produces valid JSON Schema with $schema, type, and properties', () => {
148
- const result = generateJsonSchema(testSchema);
148
+ const result = deriveConfigJsonSchema(testSchema);
149
149
 
150
150
  expect(result.$schema).toBe('https://json-schema.org/draft/2020-12/schema');
151
151
  expect(result.type).toBe('object');
@@ -153,7 +153,7 @@ describe('generateJsonSchema()', () => {
153
153
  });
154
154
 
155
155
  test('includes title and description from options', () => {
156
- const result = generateJsonSchema(testSchema, {
156
+ const result = deriveConfigJsonSchema(testSchema, {
157
157
  description: 'Server configuration',
158
158
  title: 'ServerConfig',
159
159
  });
@@ -163,7 +163,7 @@ describe('generateJsonSchema()', () => {
163
163
  });
164
164
 
165
165
  test('includes descriptions from .describe()', () => {
166
- const result = generateJsonSchema(testSchema);
166
+ const result = deriveConfigJsonSchema(testSchema);
167
167
  const props = result.properties as Record<string, Record<string, unknown>>;
168
168
 
169
169
  expect(props['host']?.description).toBe('The server hostname');
@@ -171,7 +171,7 @@ describe('generateJsonSchema()', () => {
171
171
  });
172
172
 
173
173
  test('includes defaults', () => {
174
- const result = generateJsonSchema(testSchema);
174
+ const result = deriveConfigJsonSchema(testSchema);
175
175
  const props = result.properties as Record<string, Record<string, unknown>>;
176
176
 
177
177
  expect(props['host']?.default).toBe('localhost');
@@ -187,7 +187,7 @@ describe('generateJsonSchema()', () => {
187
187
  name: z.string().describe('A name'),
188
188
  });
189
189
 
190
- const result = generateJsonSchema(enumSchema);
190
+ const result = deriveConfigJsonSchema(enumSchema);
191
191
  const props = result.properties as Record<string, Record<string, unknown>>;
192
192
 
193
193
  expect(props['name']?.type).toBe('string');
@@ -197,21 +197,21 @@ describe('generateJsonSchema()', () => {
197
197
  });
198
198
 
199
199
  test('marks deprecated fields', () => {
200
- const result = generateJsonSchema(annotatedSchema);
200
+ const result = deriveConfigJsonSchema(annotatedSchema);
201
201
  const props = result.properties as Record<string, Record<string, unknown>>;
202
202
 
203
203
  expect(props['oldEndpoint']?.deprecated).toBe(true);
204
204
  });
205
205
 
206
206
  test('lists required fields (those without defaults or optional)', () => {
207
- const result = generateJsonSchema(annotatedSchema);
207
+ const result = deriveConfigJsonSchema(annotatedSchema);
208
208
 
209
209
  expect(result.required).toContain('apiKey');
210
210
  expect(result.required).toContain('oldEndpoint');
211
211
  });
212
212
 
213
213
  test('recurses into nested object fields', () => {
214
- const result = generateJsonSchema(nestedSchema);
214
+ const result = deriveConfigJsonSchema(nestedSchema);
215
215
  const props = result.properties as Record<string, Record<string, unknown>>;
216
216
 
217
217
  expect(props['db']?.type).toBe('object');
@@ -232,9 +232,9 @@ describe('generateJsonSchema()', () => {
232
232
  });
233
233
  });
234
234
 
235
- describe('generateEnvExample()', () => {
235
+ describe('deriveConfigEnvExample()', () => {
236
236
  test('lists env vars with type info', () => {
237
- const result = generateEnvExample(testSchema);
237
+ const result = deriveConfigEnvExample(testSchema);
238
238
 
239
239
  expect(result).toContain('HOST=');
240
240
  expect(result).toContain('PORT=');
@@ -243,14 +243,14 @@ describe('generateEnvExample()', () => {
243
243
  });
244
244
 
245
245
  test('annotates secrets', () => {
246
- const result = generateEnvExample(annotatedSchema);
246
+ const result = deriveConfigEnvExample(annotatedSchema);
247
247
 
248
248
  expect(result).toContain('API_KEY=');
249
249
  expect(result).toContain('secret');
250
250
  });
251
251
 
252
252
  test('shows defaults as comments', () => {
253
- const result = generateEnvExample(testSchema);
253
+ const result = deriveConfigEnvExample(testSchema);
254
254
 
255
255
  expect(result).toContain('default: "localhost"');
256
256
  expect(result).toContain('default: 3000');
@@ -262,7 +262,7 @@ describe('generateEnvExample()', () => {
262
262
  verbose: z.boolean().default(false),
263
263
  });
264
264
 
265
- const result = generateEnvExample(noEnvSchema);
265
+ const result = deriveConfigEnvExample(noEnvSchema);
266
266
 
267
267
  expect(result).toBe('');
268
268
  });
@@ -2,7 +2,7 @@ import { describe, expect, test } from 'bun:test';
2
2
  import { z } from 'zod';
3
3
 
4
4
  import { env } from '../extensions.js';
5
- import { resolveConfig } from '../resolve.js';
5
+ import { deriveConfig } from '../resolve.js';
6
6
 
7
7
  const baseSchema = z.object({
8
8
  debug: z.boolean().default(false),
@@ -10,10 +10,10 @@ const baseSchema = z.object({
10
10
  port: z.number().default(3000),
11
11
  });
12
12
 
13
- describe('resolveConfig', () => {
13
+ describe('deriveConfig', () => {
14
14
  describe('schema defaults', () => {
15
15
  test('applies schema defaults when no other source provides values', () => {
16
- const result = resolveConfig({ schema: baseSchema });
16
+ const result = deriveConfig({ schema: baseSchema });
17
17
 
18
18
  expect(result.isOk()).toBe(true);
19
19
  expect(result.unwrap()).toEqual({
@@ -26,7 +26,7 @@ describe('resolveConfig', () => {
26
26
 
27
27
  describe('base config', () => {
28
28
  test('overrides schema defaults', () => {
29
- const result = resolveConfig({
29
+ const result = deriveConfig({
30
30
  base: { host: 'example.com', port: 8080 },
31
31
  schema: baseSchema,
32
32
  });
@@ -40,12 +40,12 @@ describe('resolveConfig', () => {
40
40
  });
41
41
  });
42
42
 
43
- describe('loadouts', () => {
44
- test('overrides base config for matching loadout', () => {
45
- const result = resolveConfig({
43
+ describe('profiles', () => {
44
+ test('overrides base config for matching profile', () => {
45
+ const result = deriveConfig({
46
46
  base: { host: 'example.com', port: 8080 },
47
- loadout: 'production',
48
- loadouts: {
47
+ profile: 'production',
48
+ profiles: {
49
49
  production: { host: 'prod.example.com', port: 443 },
50
50
  },
51
51
  schema: baseSchema,
@@ -58,11 +58,11 @@ describe('resolveConfig', () => {
58
58
  expect(value.debug).toBe(false);
59
59
  });
60
60
 
61
- test('silently ignores unrecognized loadout (base only)', () => {
62
- const result = resolveConfig({
61
+ test('silently ignores unrecognized profile (base only)', () => {
62
+ const result = deriveConfig({
63
63
  base: { host: 'example.com' },
64
- loadout: 'staging',
65
- loadouts: {
64
+ profile: 'staging',
65
+ profiles: {
66
66
  production: { host: 'prod.example.com' },
67
67
  },
68
68
  schema: baseSchema,
@@ -74,7 +74,7 @@ describe('resolveConfig', () => {
74
74
  });
75
75
 
76
76
  describe('local overrides', () => {
77
- test('deep-merge on top of loadout', () => {
77
+ test('deep-merge on top of profile', () => {
78
78
  const nestedSchema = z.object({
79
79
  db: z
80
80
  .object({
@@ -84,13 +84,13 @@ describe('resolveConfig', () => {
84
84
  .default({}),
85
85
  });
86
86
 
87
- const result = resolveConfig({
87
+ const result = deriveConfig({
88
88
  base: { db: { host: 'db.example.com', port: 5432 } },
89
- loadout: 'production',
90
- loadouts: {
89
+ localOverrides: { db: { port: 9999 } },
90
+ profile: 'production',
91
+ profiles: {
91
92
  production: { db: { host: 'prod-db.example.com' } },
92
93
  },
93
- localOverrides: { db: { port: 9999 } },
94
94
  schema: nestedSchema,
95
95
  });
96
96
 
@@ -108,7 +108,7 @@ describe('resolveConfig', () => {
108
108
  port: env(z.number(), 'APP_PORT').default(3000),
109
109
  });
110
110
 
111
- const result = resolveConfig({
111
+ const result = deriveConfig({
112
112
  base: { host: 'example.com', port: 8080 },
113
113
  env: { APP_HOST: 'env-host.example.com', APP_PORT: '9090' },
114
114
  schema,
@@ -125,7 +125,7 @@ describe('resolveConfig', () => {
125
125
  port: env(z.number(), 'PORT').default(3000),
126
126
  });
127
127
 
128
- const result = resolveConfig({
128
+ const result = deriveConfig({
129
129
  env: { PORT: '8080' },
130
130
  schema,
131
131
  });
@@ -139,7 +139,7 @@ describe('resolveConfig', () => {
139
139
  port: env(z.number(), 'PORT').default(3000),
140
140
  });
141
141
 
142
- const result = resolveConfig({
142
+ const result = deriveConfig({
143
143
  env: { PORT: 'abc' },
144
144
  schema,
145
145
  });
@@ -153,7 +153,7 @@ describe('resolveConfig', () => {
153
153
  verbose: env(z.boolean(), 'VERBOSE').default(false),
154
154
  });
155
155
 
156
- const trueValues = resolveConfig({
156
+ const trueValues = deriveConfig({
157
157
  env: { DEBUG: 'true', VERBOSE: '1' },
158
158
  schema,
159
159
  });
@@ -161,7 +161,7 @@ describe('resolveConfig', () => {
161
161
  expect(trueValues.unwrap().debug).toBe(true);
162
162
  expect(trueValues.unwrap().verbose).toBe(true);
163
163
 
164
- const falseValues = resolveConfig({
164
+ const falseValues = deriveConfig({
165
165
  env: { DEBUG: 'false', VERBOSE: '0' },
166
166
  schema,
167
167
  });
@@ -177,7 +177,7 @@ describe('resolveConfig', () => {
177
177
  required: z.string(),
178
178
  });
179
179
 
180
- const result = resolveConfig({ schema });
180
+ const result = deriveConfig({ schema });
181
181
 
182
182
  expect(result.isErr()).toBe(true);
183
183
  });
@@ -190,7 +190,7 @@ describe('resolveConfig', () => {
190
190
  });
191
191
  const base = { host: 'dev-host' };
192
192
 
193
- const first = resolveConfig({
193
+ const first = deriveConfig({
194
194
  base,
195
195
  env: { APP_HOST: 'env-host' },
196
196
  schema,
@@ -198,7 +198,7 @@ describe('resolveConfig', () => {
198
198
  expect(first.isOk()).toBe(true);
199
199
  expect(first.unwrap().host).toBe('env-host');
200
200
 
201
- const second = resolveConfig({ base, schema });
201
+ const second = deriveConfig({ base, schema });
202
202
  expect(second.isOk()).toBe(true);
203
203
  expect(second.unwrap().host).toBe('dev-host');
204
204
  expect(base.host).toBe('dev-host');
@@ -215,16 +215,16 @@ describe('resolveConfig', () => {
215
215
  port: z.number().default(3000),
216
216
  });
217
217
 
218
- const result = resolveConfig({
218
+ const result = deriveConfig({
219
219
  // base overrides name
220
220
  base: { name: 'my-app', port: 8080 },
221
221
  // env overrides debug and apiUrl
222
222
  env: { API_URL: 'https://api.prod.com', DEBUG: 'true' },
223
- // loadout overrides port
224
- loadout: 'production',
225
- loadouts: { production: { port: 443 } },
226
223
  // local overrides local
227
224
  localOverrides: { local: 'my-local-value' },
225
+ // profile overrides port
226
+ profile: 'production',
227
+ profiles: { production: { port: 443 } },
228
228
  schema,
229
229
  });
230
230
 
@@ -234,7 +234,7 @@ describe('resolveConfig', () => {
234
234
  // (nothing left at just default — name was overridden by base)
235
235
  // Base
236
236
  expect(value.name).toBe('my-app');
237
- // Loadout overrides base port
237
+ // Profile overrides base port
238
238
  expect(value.port).toBe(443);
239
239
  // Local overrides
240
240
  expect(value.local).toBe('my-local-value');
package/src/app-config.ts CHANGED
@@ -9,10 +9,13 @@ import type { z } from 'zod';
9
9
 
10
10
  import type { CheckResult } from './doctor.js';
11
11
  import { checkConfig } from './doctor.js';
12
- import type { FieldDescription } from './describe.js';
13
- import { describeConfig } from './describe.js';
14
- import type { ExplainConfigOptions, ProvenanceEntry } from './explain.js';
15
- import { explainConfig } from './explain.js';
12
+ import type { FieldDescription } from './derive-fields.js';
13
+ import { deriveConfigFields } from './derive-fields.js';
14
+ import type {
15
+ DeriveConfigProvenanceOptions,
16
+ ProvenanceEntry,
17
+ } from './derive-provenance.js';
18
+ import { deriveConfigProvenance } from './derive-provenance.js';
16
19
  import type { ConfigRef } from './ref.js';
17
20
  import { configRef } from './ref.js';
18
21
 
@@ -39,8 +42,8 @@ export interface ResolveOptions {
39
42
  }
40
43
 
41
44
  /** Options for the `explain()` method on AppConfig, excluding schema. */
42
- export type AppConfigExplainOptions = Omit<
43
- ExplainConfigOptions<z.ZodType>,
45
+ export type AppConfigDeriveProvenanceOptions = Omit<
46
+ DeriveConfigProvenanceOptions<z.ZodType>,
44
47
  'schema'
45
48
  >;
46
49
 
@@ -62,7 +65,9 @@ export interface AppConfig<T extends z.ZodType> {
62
65
  ): CheckResult;
63
66
 
64
67
  /** Show which source won for each config field. */
65
- explain(options: AppConfigExplainOptions): readonly ProvenanceEntry[];
68
+ explain(
69
+ options: AppConfigDeriveProvenanceOptions
70
+ ): readonly ProvenanceEntry[];
66
71
 
67
72
  /** Create a lazy reference to a config field for use as a trail input default. */
68
73
  ref(fieldPath: string): ConfigRef;
@@ -72,7 +77,12 @@ export interface AppConfig<T extends z.ZodType> {
72
77
  // Default values
73
78
  // ---------------------------------------------------------------------------
74
79
 
75
- const DEFAULT_FORMATS: readonly ConfigFormat[] = ['toml', 'json', 'yaml'];
80
+ const DEFAULT_FORMATS: readonly ConfigFormat[] = [
81
+ 'toml',
82
+ 'json',
83
+ 'jsonc',
84
+ 'yaml',
85
+ ];
76
86
 
77
87
  // ---------------------------------------------------------------------------
78
88
  // Internal helpers (defined before consumers — no-use-before-define)
@@ -225,7 +235,7 @@ const discoverConfigFile = async (
225
235
  };
226
236
 
227
237
  /** Resolve a config file — either from an explicit path or via discovery. */
228
- const resolveConfig = async <T extends z.ZodType>(
238
+ const resolveAppConfigFile = async <T extends z.ZodType>(
229
239
  name: string,
230
240
  schema: T,
231
241
  formats: readonly ConfigFormat[],
@@ -292,16 +302,17 @@ export const appConfig = <T extends z.ZodType>(
292
302
  return {
293
303
  check: (values, checkOpts) => checkConfig(schema, values, checkOpts),
294
304
  describe: () =>
295
- describeConfig(
305
+ deriveConfigFields(
296
306
  schema as unknown as z.ZodObject<Record<string, z.ZodType>>
297
307
  ),
298
308
  dotfile,
299
- explain: (explainOpts) => explainConfig({ ...explainOpts, schema }),
309
+ explain: (explainOpts) =>
310
+ deriveConfigProvenance({ ...explainOpts, schema }),
300
311
  formats,
301
312
  name,
302
313
  ref: (fieldPath) => configRef(fieldPath),
303
314
  resolve: (resolveOptions?: ResolveOptions) =>
304
- resolveConfig(name, schema, formats, dotfile, resolveOptions),
315
+ resolveAppConfigFile(name, schema, formats, dotfile, resolveOptions),
305
316
  schema,
306
317
  };
307
318
  };
package/src/compose.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  /**
2
- * Config composition utilities for provisions.
2
+ * Config composition utilities for resources.
3
3
  *
4
- * Collects config schemas from provision declarations so they can be
4
+ * Collects config schemas from resource declarations so they can be
5
5
  * composed into a unified config structure via `defineConfig`.
6
6
  */
7
7
 
@@ -11,17 +11,14 @@ import type { z } from 'zod';
11
11
  // Types
12
12
  // ---------------------------------------------------------------------------
13
13
 
14
- /** A provision config schema entry extracted from a provision declaration. */
15
- export interface ProvisionConfigEntry {
16
- readonly provisionId: string;
14
+ /** A resource config schema entry extracted from a resource declaration. */
15
+ export interface ResourceConfigEntry {
16
+ readonly resourceId: string;
17
17
  readonly schema: z.ZodType;
18
18
  }
19
19
 
20
- /** Backward-compatible alias while the migration is in flight. */
21
- export type ServiceConfigEntry = ProvisionConfigEntry;
22
-
23
- /** Minimal shape needed to extract config from a provision-like object. */
24
- interface ProvisionWithOptionalConfig {
20
+ /** Minimal shape needed to extract config from a resource-like object. */
21
+ interface ResourceWithOptionalConfig {
25
22
  readonly id: string;
26
23
  readonly config?: z.ZodType | undefined;
27
24
  }
@@ -31,22 +28,19 @@ interface ProvisionWithOptionalConfig {
31
28
  // ---------------------------------------------------------------------------
32
29
 
33
30
  /**
34
- * Collect config schemas from provisions that declare them.
31
+ * Collect config schemas from resources that declare them.
35
32
  *
36
- * Returns entries keyed by provision ID for composition into `defineConfig`.
37
- * Provisions without a `config` schema are excluded.
33
+ * Returns entries keyed by resource ID for composition into `defineConfig`.
34
+ * Resources without a `config` schema are excluded.
38
35
  */
39
- export const collectProvisionConfigs = (
40
- provisions: readonly ProvisionWithOptionalConfig[]
41
- ): ProvisionConfigEntry[] =>
42
- provisions
36
+ export const collectResourceConfigs = (
37
+ resources: readonly ResourceWithOptionalConfig[]
38
+ ): ResourceConfigEntry[] =>
39
+ resources
43
40
  .filter(
44
41
  (
45
42
  svc
46
- ): svc is ProvisionWithOptionalConfig & { readonly config: z.ZodType } =>
43
+ ): svc is ResourceWithOptionalConfig & { readonly config: z.ZodType } =>
47
44
  svc.config !== undefined
48
45
  )
49
- .map((svc) => ({ provisionId: svc.id, schema: svc.config }));
50
-
51
- /** Backward-compatible alias while the migration is in flight. */
52
- export const collectServiceConfigs = collectProvisionConfigs;
46
+ .map((svc) => ({ resourceId: svc.id, schema: svc.config }));
@@ -1,17 +1,17 @@
1
1
  /**
2
- * Config provision — manages resolved config lifecycle.
2
+ * Config resource — manages resolved config lifecycle.
3
3
  *
4
4
  * The config is resolved during bootstrap (two-phase init per ADR-010)
5
- * and registered via `registerConfigState`. This provision reads from the
6
- * global registry so trails can access it through `configProvision.from(ctx)`.
5
+ * and registered via `registerConfigState`. This resource reads from the
6
+ * global registry so trails can access it through `configResource.from(ctx)`.
7
7
  */
8
- import { InternalError, Result, provision } from '@ontrails/core';
8
+ import { InternalError, Result, resource } from '@ontrails/core';
9
9
  import { z } from 'zod';
10
10
 
11
11
  import type { ConfigState } from './registry.js';
12
12
  import { getConfigState } from './registry.js';
13
13
 
14
- export const configProvision = provision<ConfigState>('config', {
14
+ export const configResource = resource<ConfigState>('config', {
15
15
  create: () => {
16
16
  const state = getConfigState();
17
17
  if (state === undefined) {
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * Trails-specific config wrapper — `appConfig('trails', ...)` with
3
- * framework conventions for loadout selection and local overrides.
3
+ * framework conventions for profile selection and local overrides.
4
4
  */
5
5
 
6
6
  import { existsSync } from 'node:fs';
@@ -9,7 +9,7 @@ import { join } from 'node:path';
9
9
  import type { z } from 'zod';
10
10
 
11
11
  import { appConfig } from './app-config.js';
12
- import { resolveConfig } from './resolve.js';
12
+ import { deriveConfig } from './resolve.js';
13
13
 
14
14
  // ---------------------------------------------------------------------------
15
15
  // Types
@@ -19,14 +19,14 @@ import { resolveConfig } from './resolve.js';
19
19
  export interface DefineConfigOptions<T extends z.ZodType> {
20
20
  readonly schema: T;
21
21
  readonly base?: Partial<z.infer<T>>;
22
- readonly loadouts?: Record<string, Partial<z.infer<T>>>;
22
+ readonly profiles?: Record<string, Partial<z.infer<T>>>;
23
23
  /** When true, fall back to `NODE_ENV` when `TRAILS_ENV` is unset. */
24
24
  readonly envFromNodeEnv?: boolean;
25
25
  }
26
26
 
27
27
  /** Options passed to `resolve()` on a defined config. */
28
28
  interface DefineConfigResolveOptions {
29
- readonly loadout?: string;
29
+ readonly profile?: string;
30
30
  readonly env?: Record<string, string | undefined>;
31
31
  /** Working directory for local overrides discovery. Defaults to `process.cwd()`. */
32
32
  readonly cwd?: string;
@@ -70,7 +70,7 @@ const discoverLocalOverrides = async (
70
70
  * Define Trails app config.
71
71
  *
72
72
  * This is `appConfig('trails', ...)` with the framework's own conventions:
73
- * `TRAILS_ENV` selects the loadout. When `envFromNodeEnv` is true,
73
+ * `TRAILS_ENV` selects the profile. When `envFromNodeEnv` is true,
74
74
  * `NODE_ENV` is used as a fallback when `TRAILS_ENV` is unset.
75
75
  *
76
76
  * @example
@@ -81,7 +81,7 @@ const discoverLocalOverrides = async (
81
81
  * debug: z.boolean().default(false),
82
82
  * }),
83
83
  * base: { port: 8080 },
84
- * loadouts: {
84
+ * profiles: {
85
85
  * production: { debug: false },
86
86
  * test: { debug: true, port: 0 },
87
87
  * },
@@ -93,15 +93,12 @@ const discoverLocalOverrides = async (
93
93
  export const defineConfig = <T extends z.ZodType>(
94
94
  options: DefineConfigOptions<T>
95
95
  ) => {
96
- const config = appConfig('trails', {
97
- formats: ['toml', 'json'],
98
- schema: options.schema,
99
- });
96
+ const config = appConfig('trails', { schema: options.schema });
100
97
 
101
98
  return {
102
99
  ...config,
103
100
  base: options.base,
104
- loadouts: options.loadouts,
101
+ profiles: options.profiles,
105
102
  resolve: async (resolveOpts?: DefineConfigResolveOptions) => {
106
103
  const envRecord = {
107
104
  ...(resolveOpts?.env ?? process.env),
@@ -118,14 +115,14 @@ export const defineConfig = <T extends z.ZodType>(
118
115
  const cwd = resolveOpts?.cwd ?? process.cwd();
119
116
  const localOverrides = await discoverLocalOverrides(cwd, envRecord);
120
117
 
121
- return resolveConfig({
118
+ return deriveConfig({
122
119
  base: options.base as Record<string, unknown> | undefined,
123
120
  env: envRecord,
124
- loadout: resolveOpts?.loadout ?? envRecord['TRAILS_ENV'],
125
- loadouts: options.loadouts as
121
+ localOverrides,
122
+ profile: resolveOpts?.profile ?? envRecord['TRAILS_ENV'],
123
+ profiles: options.profiles as
126
124
  | Record<string, Record<string, unknown>>
127
125
  | undefined,
128
- localOverrides,
129
126
  schema: options.schema,
130
127
  });
131
128
  },