@ontrails/config 1.0.0-beta.14 → 1.0.0-beta.16

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 (163) hide show
  1. package/CHANGELOG.md +41 -0
  2. package/README.md +20 -28
  3. package/package.json +10 -2
  4. package/src/app-config.ts +23 -12
  5. package/src/compose.ts +16 -22
  6. package/src/{config-provision.ts → config-resource.ts} +5 -5
  7. package/src/define-config.ts +18 -18
  8. package/src/{generate → derive}/env.ts +3 -3
  9. package/src/{generate → derive}/example.ts +1 -1
  10. package/src/{generate → derive}/helpers.ts +1 -1
  11. package/src/derive/index.ts +3 -0
  12. package/src/{generate → derive}/json-schema.ts +1 -1
  13. package/src/{describe.ts → derive-fields.ts} +1 -1
  14. package/src/{explain.ts → derive-provenance.ts} +6 -6
  15. package/src/extensions.ts +1 -1
  16. package/src/index.ts +12 -19
  17. package/src/registry.ts +2 -2
  18. package/src/resolve.ts +17 -15
  19. package/src/trails/config-check.ts +3 -3
  20. package/src/trails/config-describe.ts +5 -5
  21. package/src/trails/config-explain.ts +13 -13
  22. package/src/trails/config-init.ts +10 -10
  23. package/.turbo/turbo-build.log +0 -1
  24. package/.turbo/turbo-lint.log +0 -3
  25. package/.turbo/turbo-typecheck.log +0 -1
  26. package/dist/app-config.d.ts +0 -65
  27. package/dist/app-config.d.ts.map +0 -1
  28. package/dist/app-config.js +0 -172
  29. package/dist/app-config.js.map +0 -1
  30. package/dist/collect.d.ts +0 -11
  31. package/dist/collect.d.ts.map +0 -1
  32. package/dist/collect.js +0 -81
  33. package/dist/collect.js.map +0 -1
  34. package/dist/compose.d.ts +0 -30
  35. package/dist/compose.d.ts.map +0 -1
  36. package/dist/compose.js +0 -21
  37. package/dist/compose.js.map +0 -1
  38. package/dist/config-gate.d.ts +0 -11
  39. package/dist/config-gate.d.ts.map +0 -1
  40. package/dist/config-gate.js +0 -6
  41. package/dist/config-gate.js.map +0 -1
  42. package/dist/config-layer.d.ts +0 -11
  43. package/dist/config-layer.d.ts.map +0 -1
  44. package/dist/config-layer.js +0 -6
  45. package/dist/config-layer.js.map +0 -1
  46. package/dist/config-provision.d.ts +0 -3
  47. package/dist/config-provision.d.ts.map +0 -1
  48. package/dist/config-provision.js +0 -26
  49. package/dist/config-provision.js.map +0 -1
  50. package/dist/config-service.d.ts +0 -3
  51. package/dist/config-service.d.ts.map +0 -1
  52. package/dist/config-service.js +0 -26
  53. package/dist/config-service.js.map +0 -1
  54. package/dist/define-config.d.ts +0 -61
  55. package/dist/define-config.d.ts.map +0 -1
  56. package/dist/define-config.js +0 -90
  57. package/dist/define-config.js.map +0 -1
  58. package/dist/describe.d.ts +0 -25
  59. package/dist/describe.d.ts.map +0 -1
  60. package/dist/describe.js +0 -147
  61. package/dist/describe.js.map +0 -1
  62. package/dist/doctor.d.ts +0 -27
  63. package/dist/doctor.d.ts.map +0 -1
  64. package/dist/doctor.js +0 -131
  65. package/dist/doctor.js.map +0 -1
  66. package/dist/explain.d.ts +0 -30
  67. package/dist/explain.d.ts.map +0 -1
  68. package/dist/explain.js +0 -101
  69. package/dist/explain.js.map +0 -1
  70. package/dist/extensions.d.ts +0 -38
  71. package/dist/extensions.d.ts.map +0 -1
  72. package/dist/extensions.js +0 -35
  73. package/dist/extensions.js.map +0 -1
  74. package/dist/generate/env.d.ts +0 -15
  75. package/dist/generate/env.d.ts.map +0 -1
  76. package/dist/generate/env.js +0 -65
  77. package/dist/generate/env.js.map +0 -1
  78. package/dist/generate/example.d.ts +0 -16
  79. package/dist/generate/example.d.ts.map +0 -1
  80. package/dist/generate/example.js +0 -138
  81. package/dist/generate/example.js.map +0 -1
  82. package/dist/generate/helpers.d.ts +0 -35
  83. package/dist/generate/helpers.d.ts.map +0 -1
  84. package/dist/generate/helpers.js +0 -116
  85. package/dist/generate/helpers.js.map +0 -1
  86. package/dist/generate/index.d.ts +0 -4
  87. package/dist/generate/index.d.ts.map +0 -1
  88. package/dist/generate/index.js +0 -4
  89. package/dist/generate/index.js.map +0 -1
  90. package/dist/generate/json-schema.d.ts +0 -18
  91. package/dist/generate/json-schema.d.ts.map +0 -1
  92. package/dist/generate/json-schema.js +0 -97
  93. package/dist/generate/json-schema.js.map +0 -1
  94. package/dist/index.d.ts +0 -21
  95. package/dist/index.d.ts.map +0 -1
  96. package/dist/index.js +0 -21
  97. package/dist/index.js.map +0 -1
  98. package/dist/merge.d.ts +0 -16
  99. package/dist/merge.d.ts.map +0 -1
  100. package/dist/merge.js +0 -34
  101. package/dist/merge.js.map +0 -1
  102. package/dist/ref.d.ts +0 -29
  103. package/dist/ref.d.ts.map +0 -1
  104. package/dist/ref.js +0 -30
  105. package/dist/ref.js.map +0 -1
  106. package/dist/registry.d.ts +0 -24
  107. package/dist/registry.d.ts.map +0 -1
  108. package/dist/registry.js +0 -12
  109. package/dist/registry.js.map +0 -1
  110. package/dist/resolve.d.ts +0 -21
  111. package/dist/resolve.d.ts.map +0 -1
  112. package/dist/resolve.js +0 -177
  113. package/dist/resolve.js.map +0 -1
  114. package/dist/secret-heuristics.d.ts +0 -10
  115. package/dist/secret-heuristics.d.ts.map +0 -1
  116. package/dist/secret-heuristics.js +0 -11
  117. package/dist/secret-heuristics.js.map +0 -1
  118. package/dist/trails/config-check.d.ts +0 -11
  119. package/dist/trails/config-check.d.ts.map +0 -1
  120. package/dist/trails/config-check.js +0 -53
  121. package/dist/trails/config-check.js.map +0 -1
  122. package/dist/trails/config-describe.d.ts +0 -12
  123. package/dist/trails/config-describe.d.ts.map +0 -1
  124. package/dist/trails/config-describe.js +0 -41
  125. package/dist/trails/config-describe.js.map +0 -1
  126. package/dist/trails/config-explain.d.ts +0 -8
  127. package/dist/trails/config-explain.d.ts.map +0 -1
  128. package/dist/trails/config-explain.js +0 -74
  129. package/dist/trails/config-explain.js.map +0 -1
  130. package/dist/trails/config-init.d.ts +0 -9
  131. package/dist/trails/config-init.d.ts.map +0 -1
  132. package/dist/trails/config-init.js +0 -78
  133. package/dist/trails/config-init.js.map +0 -1
  134. package/dist/workspace.d.ts +0 -9
  135. package/dist/workspace.d.ts.map +0 -1
  136. package/dist/workspace.js +0 -49
  137. package/dist/workspace.js.map +0 -1
  138. package/dist/zod-utils.d.ts +0 -14
  139. package/dist/zod-utils.d.ts.map +0 -1
  140. package/dist/zod-utils.js +0 -41
  141. package/dist/zod-utils.js.map +0 -1
  142. package/src/__tests__/app-config.test.ts +0 -329
  143. package/src/__tests__/compose.test.ts +0 -59
  144. package/src/__tests__/config-check.test.ts +0 -171
  145. package/src/__tests__/config-describe.test.ts +0 -154
  146. package/src/__tests__/config-explain.test.ts +0 -167
  147. package/src/__tests__/config-gate.test.ts +0 -53
  148. package/src/__tests__/config-init.test.ts +0 -210
  149. package/src/__tests__/config-provision.test.ts +0 -87
  150. package/src/__tests__/define-config.test.ts +0 -263
  151. package/src/__tests__/describe.test.ts +0 -158
  152. package/src/__tests__/doctor.test.ts +0 -172
  153. package/src/__tests__/explain.test.ts +0 -139
  154. package/src/__tests__/extensions.test.ts +0 -134
  155. package/src/__tests__/generate.test.ts +0 -269
  156. package/src/__tests__/ref.test.ts +0 -35
  157. package/src/__tests__/resolve.test.ts +0 -246
  158. package/src/__tests__/workspace.test.ts +0 -64
  159. package/src/config-gate.ts +0 -15
  160. package/src/generate/index.ts +0 -3
  161. package/src/workspace.ts +0 -56
  162. package/tsconfig.json +0 -9
  163. package/tsconfig.tsbuildinfo +0 -1
@@ -1,246 +0,0 @@
1
- import { describe, expect, test } from 'bun:test';
2
- import { z } from 'zod';
3
-
4
- import { env } from '../extensions.js';
5
- import { resolveConfig } from '../resolve.js';
6
-
7
- const baseSchema = z.object({
8
- debug: z.boolean().default(false),
9
- host: z.string().default('localhost'),
10
- port: z.number().default(3000),
11
- });
12
-
13
- describe('resolveConfig', () => {
14
- describe('schema defaults', () => {
15
- test('applies schema defaults when no other source provides values', () => {
16
- const result = resolveConfig({ schema: baseSchema });
17
-
18
- expect(result.isOk()).toBe(true);
19
- expect(result.unwrap()).toEqual({
20
- debug: false,
21
- host: 'localhost',
22
- port: 3000,
23
- });
24
- });
25
- });
26
-
27
- describe('base config', () => {
28
- test('overrides schema defaults', () => {
29
- const result = resolveConfig({
30
- base: { host: 'example.com', port: 8080 },
31
- schema: baseSchema,
32
- });
33
-
34
- expect(result.isOk()).toBe(true);
35
- const value = result.unwrap();
36
- expect(value.host).toBe('example.com');
37
- expect(value.port).toBe(8080);
38
- // Schema default preserved for unspecified fields
39
- expect(value.debug).toBe(false);
40
- });
41
- });
42
-
43
- describe('loadouts', () => {
44
- test('overrides base config for matching loadout', () => {
45
- const result = resolveConfig({
46
- base: { host: 'example.com', port: 8080 },
47
- loadout: 'production',
48
- loadouts: {
49
- production: { host: 'prod.example.com', port: 443 },
50
- },
51
- schema: baseSchema,
52
- });
53
-
54
- expect(result.isOk()).toBe(true);
55
- const value = result.unwrap();
56
- expect(value.host).toBe('prod.example.com');
57
- expect(value.port).toBe(443);
58
- expect(value.debug).toBe(false);
59
- });
60
-
61
- test('silently ignores unrecognized loadout (base only)', () => {
62
- const result = resolveConfig({
63
- base: { host: 'example.com' },
64
- loadout: 'staging',
65
- loadouts: {
66
- production: { host: 'prod.example.com' },
67
- },
68
- schema: baseSchema,
69
- });
70
-
71
- expect(result.isOk()).toBe(true);
72
- expect(result.unwrap().host).toBe('example.com');
73
- });
74
- });
75
-
76
- describe('local overrides', () => {
77
- test('deep-merge on top of loadout', () => {
78
- const nestedSchema = z.object({
79
- db: z
80
- .object({
81
- host: z.string().default('localhost'),
82
- port: z.number().default(5432),
83
- })
84
- .default({}),
85
- });
86
-
87
- const result = resolveConfig({
88
- base: { db: { host: 'db.example.com', port: 5432 } },
89
- loadout: 'production',
90
- loadouts: {
91
- production: { db: { host: 'prod-db.example.com' } },
92
- },
93
- localOverrides: { db: { port: 9999 } },
94
- schema: nestedSchema,
95
- });
96
-
97
- expect(result.isOk()).toBe(true);
98
- const value = result.unwrap();
99
- expect(value.db.host).toBe('prod-db.example.com');
100
- expect(value.db.port).toBe(9999);
101
- });
102
- });
103
-
104
- describe('env var overrides', () => {
105
- test('overrides all other sources', () => {
106
- const schema = z.object({
107
- host: env(z.string(), 'APP_HOST').default('localhost'),
108
- port: env(z.number(), 'APP_PORT').default(3000),
109
- });
110
-
111
- const result = resolveConfig({
112
- base: { host: 'example.com', port: 8080 },
113
- env: { APP_HOST: 'env-host.example.com', APP_PORT: '9090' },
114
- schema,
115
- });
116
-
117
- expect(result.isOk()).toBe(true);
118
- const value = result.unwrap();
119
- expect(value.host).toBe('env-host.example.com');
120
- expect(value.port).toBe(9090);
121
- });
122
-
123
- test('coerces string to number', () => {
124
- const schema = z.object({
125
- port: env(z.number(), 'PORT').default(3000),
126
- });
127
-
128
- const result = resolveConfig({
129
- env: { PORT: '8080' },
130
- schema,
131
- });
132
-
133
- expect(result.isOk()).toBe(true);
134
- expect(result.unwrap().port).toBe(8080);
135
- });
136
-
137
- test('rejects non-numeric string with Zod error instead of NaN', () => {
138
- const schema = z.object({
139
- port: env(z.number(), 'PORT').default(3000),
140
- });
141
-
142
- const result = resolveConfig({
143
- env: { PORT: 'abc' },
144
- schema,
145
- });
146
-
147
- expect(result.isErr()).toBe(true);
148
- });
149
-
150
- test('coerces string to boolean', () => {
151
- const schema = z.object({
152
- debug: env(z.boolean(), 'DEBUG').default(false),
153
- verbose: env(z.boolean(), 'VERBOSE').default(false),
154
- });
155
-
156
- const trueValues = resolveConfig({
157
- env: { DEBUG: 'true', VERBOSE: '1' },
158
- schema,
159
- });
160
- expect(trueValues.isOk()).toBe(true);
161
- expect(trueValues.unwrap().debug).toBe(true);
162
- expect(trueValues.unwrap().verbose).toBe(true);
163
-
164
- const falseValues = resolveConfig({
165
- env: { DEBUG: 'false', VERBOSE: '0' },
166
- schema,
167
- });
168
- expect(falseValues.isOk()).toBe(true);
169
- expect(falseValues.unwrap().debug).toBe(false);
170
- expect(falseValues.unwrap().verbose).toBe(false);
171
- });
172
- });
173
-
174
- describe('validation', () => {
175
- test('missing required field returns Result.err', () => {
176
- const schema = z.object({
177
- required: z.string(),
178
- });
179
-
180
- const result = resolveConfig({ schema });
181
-
182
- expect(result.isErr()).toBe(true);
183
- });
184
- });
185
-
186
- describe('mutation safety', () => {
187
- test('repeated resolve() calls do not mutate the original base', () => {
188
- const schema = z.object({
189
- host: env(z.string(), 'APP_HOST').default('localhost'),
190
- });
191
- const base = { host: 'dev-host' };
192
-
193
- const first = resolveConfig({
194
- base,
195
- env: { APP_HOST: 'env-host' },
196
- schema,
197
- });
198
- expect(first.isOk()).toBe(true);
199
- expect(first.unwrap().host).toBe('env-host');
200
-
201
- const second = resolveConfig({ base, schema });
202
- expect(second.isOk()).toBe(true);
203
- expect(second.unwrap().host).toBe('dev-host');
204
- expect(base.host).toBe('dev-host');
205
- });
206
- });
207
-
208
- describe('full stack', () => {
209
- test('all 5 sources compose correctly', () => {
210
- const schema = z.object({
211
- apiUrl: env(z.string(), 'API_URL').default('http://localhost'),
212
- debug: env(z.boolean(), 'DEBUG').default(false),
213
- local: z.string().default('default-local'),
214
- name: z.string().default('app'),
215
- port: z.number().default(3000),
216
- });
217
-
218
- const result = resolveConfig({
219
- // base overrides name
220
- base: { name: 'my-app', port: 8080 },
221
- // env overrides debug and apiUrl
222
- env: { API_URL: 'https://api.prod.com', DEBUG: 'true' },
223
- // loadout overrides port
224
- loadout: 'production',
225
- loadouts: { production: { port: 443 } },
226
- // local overrides local
227
- localOverrides: { local: 'my-local-value' },
228
- schema,
229
- });
230
-
231
- expect(result.isOk()).toBe(true);
232
- const value = result.unwrap();
233
- // Schema default
234
- // (nothing left at just default — name was overridden by base)
235
- // Base
236
- expect(value.name).toBe('my-app');
237
- // Loadout overrides base port
238
- expect(value.port).toBe(443);
239
- // Local overrides
240
- expect(value.local).toBe('my-local-value');
241
- // Env overrides
242
- expect(value.apiUrl).toBe('https://api.prod.com');
243
- expect(value.debug).toBe(true);
244
- });
245
- });
246
- });
@@ -1,64 +0,0 @@
1
- import { afterEach, beforeEach, describe, expect, test } from 'bun:test';
2
- import { mkdtemp, readdir, readFile, rm } from 'node:fs/promises';
3
- import { join } from 'node:path';
4
- import { tmpdir } from 'node:os';
5
-
6
- import { ensureWorkspace } from '../workspace.js';
7
-
8
- describe('ensureWorkspace', () => {
9
- let root: string;
10
-
11
- beforeEach(async () => {
12
- root = await mkdtemp(join(tmpdir(), 'trails-ws-'));
13
- });
14
-
15
- afterEach(async () => {
16
- await rm(root, { force: true, recursive: true });
17
- });
18
-
19
- describe('directory creation', () => {
20
- test('creates .trails/ directory', async () => {
21
- await ensureWorkspace(root);
22
- const entries = await readdir(join(root, '.trails'));
23
- expect(entries).toContain('config');
24
- expect(entries).toContain('dev');
25
- expect(entries).toContain('generated');
26
- });
27
-
28
- test('creates all expected subdirectories', async () => {
29
- await ensureWorkspace(root);
30
- const config = await readdir(join(root, '.trails', 'config'));
31
- expect(config).toBeDefined();
32
- const dev = await readdir(join(root, '.trails', 'dev'));
33
- expect(dev).toBeDefined();
34
- const generated = await readdir(join(root, '.trails', 'generated'));
35
- expect(generated).toBeDefined();
36
- });
37
- });
38
-
39
- describe('.gitignore', () => {
40
- test('writes .gitignore on first run', async () => {
41
- await ensureWorkspace(root);
42
- const content = await readFile(
43
- join(root, '.trails', '.gitignore'),
44
- 'utf8'
45
- );
46
- expect(content).toContain('config/');
47
- expect(content).toContain('dev/');
48
- expect(content).toContain('generated/');
49
- });
50
-
51
- test('does not overwrite existing .gitignore', async () => {
52
- await ensureWorkspace(root);
53
- const customContent = '# custom\n*\n';
54
- await Bun.write(join(root, '.trails', '.gitignore'), customContent);
55
-
56
- await ensureWorkspace(root);
57
- const content = await readFile(
58
- join(root, '.trails', '.gitignore'),
59
- 'utf8'
60
- );
61
- expect(content).toBe(customContent);
62
- });
63
- });
64
- });
@@ -1,15 +0,0 @@
1
- /**
2
- * Config gate — attaches resolved config to the execution context.
3
- *
4
- * For v1, the gate is a pass-through: config resolution happens at
5
- * bootstrap time and the provision pipeline (TRL-91) injects the resolved
6
- * config before any trail runs. The gate reserves a named slot so future
7
- * versions can add per-trail config overrides or validation.
8
- */
9
- import type { Gate } from '@ontrails/core';
10
-
11
- export const configGate: Gate = {
12
- description: 'Ensures resolved config is available in the execution context',
13
- name: 'config',
14
- wrap: (_trail, impl) => (input, ctx) => impl(input, ctx),
15
- };
@@ -1,3 +0,0 @@
1
- export { generateEnvExample } from './env.js';
2
- export { generateExample } from './example.js';
3
- export { generateJsonSchema } from './json-schema.js';
package/src/workspace.ts DELETED
@@ -1,56 +0,0 @@
1
- /**
2
- * Ensure the `.trails/` workspace directory exists with proper structure.
3
- *
4
- * Auto-creates on first framework operation. The workspace holds local
5
- * config overrides, development state, and generated artifacts that
6
- * should not be committed to source control.
7
- */
8
- import { mkdir } from 'node:fs/promises';
9
- import { join } from 'node:path';
10
-
11
- const WORKSPACE_DIRS = ['config', 'dev', 'generated'] as const;
12
-
13
- const GITIGNORE_CONTENT = [
14
- '# Local config overrides',
15
- 'config/',
16
- '',
17
- '# Development state',
18
- 'dev/',
19
- '',
20
- '# Generated artifacts',
21
- 'generated/',
22
- '',
23
- '# Shared Trails database',
24
- 'trails.db',
25
- 'trails.db-shm',
26
- 'trails.db-wal',
27
- '',
28
- ].join('\n');
29
-
30
- /**
31
- * Write `.gitignore` inside the workspace directory if it does not already exist.
32
- */
33
- const writeGitignoreIfMissing = async (trailsDir: string): Promise<void> => {
34
- const gitignorePath = join(trailsDir, '.gitignore');
35
- const file = Bun.file(gitignorePath);
36
- if (!(await file.exists())) {
37
- await Bun.write(gitignorePath, GITIGNORE_CONTENT);
38
- }
39
- };
40
-
41
- /**
42
- * Ensure the `.trails/` workspace directory exists with proper structure.
43
- *
44
- * Creates `config/`, `dev/`, and `generated/` subdirectories plus a
45
- * `.gitignore` that excludes local-only files. Safe to call repeatedly —
46
- * existing files are never overwritten.
47
- */
48
- export const ensureWorkspace = async (root: string): Promise<void> => {
49
- const trailsDir = join(root, '.trails');
50
-
51
- await Promise.all(
52
- WORKSPACE_DIRS.map((d) => mkdir(join(trailsDir, d), { recursive: true }))
53
- );
54
-
55
- await writeGitignoreIfMissing(trailsDir);
56
- };
package/tsconfig.json DELETED
@@ -1,9 +0,0 @@
1
- {
2
- "extends": "../../tsconfig.json",
3
- "compilerOptions": {
4
- "outDir": "dist",
5
- "rootDir": "src"
6
- },
7
- "include": ["src"],
8
- "exclude": ["**/__tests__/**", "**/*.test.ts", "dist"]
9
- }
@@ -1 +0,0 @@
1
- {"root":["./src/app-config.ts","./src/collect.ts","./src/compose.ts","./src/config-gate.ts","./src/config-provision.ts","./src/define-config.ts","./src/describe.ts","./src/doctor.ts","./src/explain.ts","./src/extensions.ts","./src/index.ts","./src/merge.ts","./src/ref.ts","./src/registry.ts","./src/resolve.ts","./src/secret-heuristics.ts","./src/workspace.ts","./src/zod-utils.ts","./src/generate/env.ts","./src/generate/example.ts","./src/generate/helpers.ts","./src/generate/index.ts","./src/generate/json-schema.ts","./src/trails/config-check.ts","./src/trails/config-describe.ts","./src/trails/config-explain.ts","./src/trails/config-init.ts"],"version":"5.9.3"}