@ontrails/config 1.0.0-beta.14 → 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.
- package/.turbo/turbo-lint.log +1 -1
- package/CHANGELOG.md +7 -0
- package/README.md +20 -28
- package/dist/app-config.d.ts +4 -4
- package/dist/app-config.d.ts.map +1 -1
- package/dist/app-config.js +12 -7
- package/dist/app-config.js.map +1 -1
- package/dist/compose.d.ts +11 -15
- package/dist/compose.d.ts.map +1 -1
- package/dist/compose.js +7 -9
- package/dist/compose.js.map +1 -1
- package/dist/config-layer.d.ts +3 -3
- package/dist/config-resource.d.ts +3 -0
- package/dist/config-resource.d.ts.map +1 -0
- package/dist/{config-provision.js → config-resource.js} +6 -6
- package/dist/config-resource.js.map +1 -0
- package/dist/define-config.d.ts +9 -9
- package/dist/define-config.d.ts.map +1 -1
- package/dist/define-config.js +9 -12
- package/dist/define-config.js.map +1 -1
- package/dist/{generate → derive}/env.d.ts +1 -1
- package/dist/derive/env.d.ts.map +1 -0
- package/dist/{generate → derive}/env.js +3 -3
- package/dist/derive/env.js.map +1 -0
- package/dist/{generate → derive}/example.d.ts +1 -1
- package/dist/derive/example.d.ts.map +1 -0
- package/dist/{generate → derive}/example.js +1 -1
- package/dist/derive/example.js.map +1 -0
- package/dist/{generate → derive}/helpers.d.ts +1 -1
- package/dist/derive/helpers.d.ts.map +1 -0
- package/dist/{generate → derive}/helpers.js +1 -1
- package/dist/derive/helpers.js.map +1 -0
- package/dist/derive/index.d.ts +4 -0
- package/dist/derive/index.d.ts.map +1 -0
- package/dist/derive/index.js +4 -0
- package/dist/derive/index.js.map +1 -0
- package/dist/{generate → derive}/json-schema.d.ts +1 -1
- package/dist/derive/json-schema.d.ts.map +1 -0
- package/dist/{generate → derive}/json-schema.js +1 -1
- package/dist/derive/json-schema.js.map +1 -0
- package/dist/{describe.d.ts → derive-fields.d.ts} +2 -2
- package/dist/derive-fields.d.ts.map +1 -0
- package/dist/{describe.js → derive-fields.js} +2 -2
- package/dist/derive-fields.js.map +1 -0
- package/dist/{explain.d.ts → derive-provenance.d.ts} +5 -5
- package/dist/derive-provenance.d.ts.map +1 -0
- package/dist/{explain.js → derive-provenance.js} +3 -3
- package/dist/derive-provenance.js.map +1 -0
- package/dist/index.d.ts +7 -8
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6 -7
- package/dist/index.js.map +1 -1
- package/dist/registry.d.ts +2 -2
- package/dist/resolve.d.ts +6 -6
- package/dist/resolve.d.ts.map +1 -1
- package/dist/resolve.js +7 -7
- package/dist/resolve.js.map +1 -1
- package/dist/trails/config-check.d.ts +1 -1
- package/dist/trails/config-check.d.ts.map +1 -1
- package/dist/trails/config-check.js +3 -3
- package/dist/trails/config-check.js.map +1 -1
- package/dist/trails/config-describe.d.ts +1 -1
- package/dist/trails/config-describe.d.ts.map +1 -1
- package/dist/trails/config-describe.js +5 -5
- package/dist/trails/config-describe.js.map +1 -1
- package/dist/trails/config-explain.d.ts +1 -1
- package/dist/trails/config-explain.d.ts.map +1 -1
- package/dist/trails/config-explain.js +8 -8
- package/dist/trails/config-explain.js.map +1 -1
- package/dist/trails/config-init.d.ts +1 -1
- package/dist/trails/config-init.d.ts.map +1 -1
- package/dist/trails/config-init.js +7 -7
- package/dist/trails/config-init.js.map +1 -1
- package/package.json +2 -2
- package/src/__tests__/app-config.test.ts +19 -6
- package/src/__tests__/compose.test.ts +15 -15
- package/src/__tests__/config-check.test.ts +7 -7
- package/src/__tests__/config-describe.test.ts +7 -7
- package/src/__tests__/config-explain.test.ts +7 -7
- package/src/__tests__/config-init.test.ts +7 -7
- package/src/__tests__/{config-provision.test.ts → config-resource.test.ts} +12 -12
- package/src/__tests__/define-config.test.ts +24 -18
- package/src/__tests__/{describe.test.ts → derive-fields.test.ts} +11 -11
- package/src/__tests__/{explain.test.ts → derive-provenance.test.ts} +18 -18
- package/src/__tests__/{generate.test.ts → derive.test.ts} +30 -30
- package/src/__tests__/resolve.test.ts +31 -31
- package/src/app-config.ts +23 -12
- package/src/compose.ts +16 -22
- package/src/{config-provision.ts → config-resource.ts} +5 -5
- package/src/define-config.ts +12 -15
- package/src/{generate → derive}/env.ts +3 -3
- package/src/{generate → derive}/example.ts +1 -1
- package/src/{generate → derive}/helpers.ts +1 -1
- package/src/derive/index.ts +3 -0
- package/src/{generate → derive}/json-schema.ts +1 -1
- package/src/{describe.ts → derive-fields.ts} +1 -1
- package/src/{explain.ts → derive-provenance.ts} +6 -6
- package/src/index.ts +12 -18
- package/src/registry.ts +2 -2
- package/src/resolve.ts +13 -13
- package/src/trails/config-check.ts +3 -3
- package/src/trails/config-describe.ts +5 -5
- package/src/trails/config-explain.ts +13 -13
- package/src/trails/config-init.ts +10 -10
- package/tsconfig.tests.json +10 -0
- package/tsconfig.tsbuildinfo +1 -1
- package/dist/config-gate.d.ts +0 -11
- package/dist/config-gate.d.ts.map +0 -1
- package/dist/config-gate.js +0 -6
- package/dist/config-gate.js.map +0 -1
- package/dist/config-provision.d.ts +0 -3
- package/dist/config-provision.d.ts.map +0 -1
- package/dist/config-provision.js.map +0 -1
- package/dist/config-service.d.ts +0 -3
- package/dist/config-service.d.ts.map +0 -1
- package/dist/config-service.js +0 -26
- package/dist/config-service.js.map +0 -1
- package/dist/describe.d.ts.map +0 -1
- package/dist/describe.js.map +0 -1
- package/dist/explain.d.ts.map +0 -1
- package/dist/explain.js.map +0 -1
- package/dist/generate/env.d.ts.map +0 -1
- package/dist/generate/env.js.map +0 -1
- package/dist/generate/example.d.ts.map +0 -1
- package/dist/generate/example.js.map +0 -1
- package/dist/generate/helpers.d.ts.map +0 -1
- package/dist/generate/helpers.js.map +0 -1
- package/dist/generate/index.d.ts +0 -4
- package/dist/generate/index.d.ts.map +0 -1
- package/dist/generate/index.js +0 -4
- package/dist/generate/index.js.map +0 -1
- package/dist/generate/json-schema.d.ts.map +0 -1
- package/dist/generate/json-schema.js.map +0 -1
- package/src/__tests__/config-gate.test.ts +0 -53
- package/src/config-gate.ts +0 -15
- package/src/generate/index.ts +0 -3
package/src/compose.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Config composition utilities for
|
|
2
|
+
* Config composition utilities for resources.
|
|
3
3
|
*
|
|
4
|
-
* Collects config schemas from
|
|
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
|
|
15
|
-
export interface
|
|
16
|
-
readonly
|
|
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
|
-
/**
|
|
21
|
-
|
|
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
|
|
31
|
+
* Collect config schemas from resources that declare them.
|
|
35
32
|
*
|
|
36
|
-
* Returns entries keyed by
|
|
37
|
-
*
|
|
33
|
+
* Returns entries keyed by resource ID for composition into `defineConfig`.
|
|
34
|
+
* Resources without a `config` schema are excluded.
|
|
38
35
|
*/
|
|
39
|
-
export const
|
|
40
|
-
|
|
41
|
-
):
|
|
42
|
-
|
|
36
|
+
export const collectResourceConfigs = (
|
|
37
|
+
resources: readonly ResourceWithOptionalConfig[]
|
|
38
|
+
): ResourceConfigEntry[] =>
|
|
39
|
+
resources
|
|
43
40
|
.filter(
|
|
44
41
|
(
|
|
45
42
|
svc
|
|
46
|
-
): svc is
|
|
43
|
+
): svc is ResourceWithOptionalConfig & { readonly config: z.ZodType } =>
|
|
47
44
|
svc.config !== undefined
|
|
48
45
|
)
|
|
49
|
-
.map((svc) => ({
|
|
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
|
|
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
|
|
6
|
-
* global registry so trails can access it through `
|
|
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,
|
|
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
|
|
14
|
+
export const configResource = resource<ConfigState>('config', {
|
|
15
15
|
create: () => {
|
|
16
16
|
const state = getConfigState();
|
|
17
17
|
if (state === undefined) {
|
package/src/define-config.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Trails-specific config wrapper — `appConfig('trails', ...)` with
|
|
3
|
-
* framework conventions for
|
|
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 {
|
|
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
|
|
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
|
|
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
|
|
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
|
-
*
|
|
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
|
-
|
|
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
|
|
118
|
+
return deriveConfig({
|
|
122
119
|
base: options.base as Record<string, unknown> | undefined,
|
|
123
120
|
env: envRecord,
|
|
124
|
-
|
|
125
|
-
|
|
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
|
},
|
|
@@ -15,7 +15,7 @@ import {
|
|
|
15
15
|
formatValue,
|
|
16
16
|
getDefault,
|
|
17
17
|
getDescription,
|
|
18
|
-
|
|
18
|
+
deriveFieldByPath,
|
|
19
19
|
unwrap,
|
|
20
20
|
zodTypeName,
|
|
21
21
|
zodTypeToJsonSchema,
|
|
@@ -76,7 +76,7 @@ const collectEnvEntries = (
|
|
|
76
76
|
if (!fieldMeta.env) {
|
|
77
77
|
continue;
|
|
78
78
|
}
|
|
79
|
-
const fieldSchema =
|
|
79
|
+
const fieldSchema = deriveFieldByPath(schema, path);
|
|
80
80
|
if (!fieldSchema) {
|
|
81
81
|
continue;
|
|
82
82
|
}
|
|
@@ -96,7 +96,7 @@ const collectEnvEntries = (
|
|
|
96
96
|
* Lists each env var with its type, default, and whether it is a secret.
|
|
97
97
|
* Returns an empty string when no env bindings are present.
|
|
98
98
|
*/
|
|
99
|
-
export const
|
|
99
|
+
export const deriveConfigEnvExample = (
|
|
100
100
|
schema: z.ZodObject<Record<string, z.ZodType>>
|
|
101
101
|
): string => {
|
|
102
102
|
const entries = collectEnvEntries(schema, collectConfigMeta(schema));
|
|
@@ -216,7 +216,7 @@ const formatters: Record<
|
|
|
216
216
|
*
|
|
217
217
|
* Includes descriptions as comments, defaults shown, deprecated fields annotated.
|
|
218
218
|
*/
|
|
219
|
-
export const
|
|
219
|
+
export const deriveConfigExample = (
|
|
220
220
|
schema: z.ZodObject<Record<string, z.ZodType>>,
|
|
221
221
|
format: ExampleFormat
|
|
222
222
|
): string => formatters[format](schema);
|
|
@@ -86,7 +86,7 @@ export const getObjectShape = (
|
|
|
86
86
|
};
|
|
87
87
|
|
|
88
88
|
/** Resolve a dot-separated path to the field schema within an object. */
|
|
89
|
-
export const
|
|
89
|
+
export const deriveFieldByPath = (
|
|
90
90
|
schema: z.ZodObject<Record<string, z.ZodType>>,
|
|
91
91
|
path: string
|
|
92
92
|
): z.ZodType | undefined => {
|
|
@@ -112,7 +112,7 @@ const buildSchemaProperties = (
|
|
|
112
112
|
* Includes descriptions, defaults, deprecated annotations, and constraints.
|
|
113
113
|
* Produces JSON Schema Draft 2020-12.
|
|
114
114
|
*/
|
|
115
|
-
export const
|
|
115
|
+
export const deriveConfigJsonSchema = (
|
|
116
116
|
schema: z.ZodObject<Record<string, z.ZodType>>,
|
|
117
117
|
options?: { readonly description?: string; readonly title?: string }
|
|
118
118
|
): Record<string, unknown> => {
|
|
@@ -228,7 +228,7 @@ const walkShapeLevel = (
|
|
|
228
228
|
*
|
|
229
229
|
* Returns a structured catalog suitable for CLI rendering or agent inspection.
|
|
230
230
|
*/
|
|
231
|
-
export const
|
|
231
|
+
export const deriveConfigFields = (
|
|
232
232
|
schema: z.ZodObject<Record<string, z.ZodType>>
|
|
233
233
|
): readonly FieldDescription[] => {
|
|
234
234
|
const configMeta = collectConfigMeta(schema);
|
|
@@ -18,15 +18,15 @@ import { getAtPath, isZodObject, unwrapToBase, zodDef } from './zod-utils.js';
|
|
|
18
18
|
export interface ProvenanceEntry {
|
|
19
19
|
readonly path: string;
|
|
20
20
|
readonly value: unknown;
|
|
21
|
-
readonly source: 'default' | 'base' | '
|
|
21
|
+
readonly source: 'default' | 'base' | 'profile' | 'local' | 'env';
|
|
22
22
|
readonly redacted: boolean;
|
|
23
23
|
}
|
|
24
24
|
|
|
25
25
|
/** Options for explaining config provenance. */
|
|
26
|
-
export interface
|
|
26
|
+
export interface DeriveConfigProvenanceOptions<T extends z.ZodType> {
|
|
27
27
|
readonly schema: T;
|
|
28
28
|
readonly base?: Record<string, unknown>;
|
|
29
|
-
readonly
|
|
29
|
+
readonly profile?: Record<string, unknown>;
|
|
30
30
|
readonly local?: Record<string, unknown>;
|
|
31
31
|
readonly env?: Record<string, string | undefined>;
|
|
32
32
|
readonly resolved: Record<string, unknown>;
|
|
@@ -135,8 +135,8 @@ const collectLeafPaths = (schema: z.ZodType, prefix: string): string[] => {
|
|
|
135
135
|
* Used for debugging — answers "where did this value come from?"
|
|
136
136
|
*
|
|
137
137
|
*/
|
|
138
|
-
export const
|
|
139
|
-
options:
|
|
138
|
+
export const deriveConfigProvenance = <T extends z.ZodType>(
|
|
139
|
+
options: DeriveConfigProvenanceOptions<T>
|
|
140
140
|
): readonly ProvenanceEntry[] => {
|
|
141
141
|
const objSchema = options.schema as unknown as z.ZodObject<
|
|
142
142
|
Record<string, z.ZodType>
|
|
@@ -146,7 +146,7 @@ export const explainConfig = <T extends z.ZodType>(
|
|
|
146
146
|
|
|
147
147
|
const sources: readonly SourceEntry[] = [
|
|
148
148
|
['local', options.local],
|
|
149
|
-
['
|
|
149
|
+
['profile', options.profile],
|
|
150
150
|
['base', options.base],
|
|
151
151
|
];
|
|
152
152
|
|
package/src/index.ts
CHANGED
|
@@ -1,20 +1,15 @@
|
|
|
1
1
|
export {
|
|
2
2
|
appConfig,
|
|
3
3
|
type AppConfig,
|
|
4
|
-
type
|
|
4
|
+
type AppConfigDeriveProvenanceOptions,
|
|
5
5
|
type AppConfigOptions,
|
|
6
6
|
type ConfigFormat,
|
|
7
7
|
type ResolveOptions,
|
|
8
8
|
} from './app-config.js';
|
|
9
9
|
export { collectConfigMeta } from './collect.js';
|
|
10
|
-
export {
|
|
11
|
-
collectProvisionConfigs,
|
|
12
|
-
collectServiceConfigs,
|
|
13
|
-
type ProvisionConfigEntry,
|
|
14
|
-
type ServiceConfigEntry,
|
|
15
|
-
} from './compose.js';
|
|
10
|
+
export { collectResourceConfigs, type ResourceConfigEntry } from './compose.js';
|
|
16
11
|
export { defineConfig, type DefineConfigOptions } from './define-config.js';
|
|
17
|
-
export {
|
|
12
|
+
export { deriveConfigFields, type FieldDescription } from './derive-fields.js';
|
|
18
13
|
export {
|
|
19
14
|
checkConfig,
|
|
20
15
|
type CheckResult,
|
|
@@ -22,17 +17,16 @@ export {
|
|
|
22
17
|
} from './doctor.js';
|
|
23
18
|
export { env, secret, deprecated, type ConfigFieldMeta } from './extensions.js';
|
|
24
19
|
export {
|
|
25
|
-
|
|
26
|
-
type
|
|
20
|
+
deriveConfigProvenance,
|
|
21
|
+
type DeriveConfigProvenanceOptions,
|
|
27
22
|
type ProvenanceEntry,
|
|
28
|
-
} from './
|
|
23
|
+
} from './derive-provenance.js';
|
|
29
24
|
export {
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
} from './
|
|
34
|
-
export {
|
|
35
|
-
export { configProvision } from './config-provision.js';
|
|
25
|
+
deriveConfigEnvExample,
|
|
26
|
+
deriveConfigExample,
|
|
27
|
+
deriveConfigJsonSchema,
|
|
28
|
+
} from './derive/index.js';
|
|
29
|
+
export { configResource } from './config-resource.js';
|
|
36
30
|
export {
|
|
37
31
|
clearConfigState,
|
|
38
32
|
type ConfigState,
|
|
@@ -41,7 +35,7 @@ export {
|
|
|
41
35
|
} from './registry.js';
|
|
42
36
|
export { deepMerge } from './merge.js';
|
|
43
37
|
export { configRef, isConfigRef, type ConfigRef } from './ref.js';
|
|
44
|
-
export {
|
|
38
|
+
export { deriveConfig, type DeriveConfigOptions } from './resolve.js';
|
|
45
39
|
export { configCheck } from './trails/config-check.js';
|
|
46
40
|
export { configDescribe } from './trails/config-describe.js';
|
|
47
41
|
export { configExplain } from './trails/config-explain.js';
|
package/src/registry.ts
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* Module-level config state registry.
|
|
3
3
|
*
|
|
4
4
|
* Config is resolved once at bootstrap (two-phase init per ADR-010) and
|
|
5
|
-
* registered here so `
|
|
5
|
+
* registered here so `configResource` can expose it to trailheads. This is
|
|
6
6
|
* a process-level singleton — config resolution is inherently global.
|
|
7
7
|
*/
|
|
8
8
|
import type { z } from 'zod';
|
|
@@ -12,7 +12,7 @@ export interface ConfigState {
|
|
|
12
12
|
readonly schema: z.ZodObject<Record<string, z.ZodType>>;
|
|
13
13
|
readonly resolved: Record<string, unknown>;
|
|
14
14
|
readonly base?: Record<string, unknown>;
|
|
15
|
-
readonly
|
|
15
|
+
readonly profile?: Record<string, unknown>;
|
|
16
16
|
readonly local?: Record<string, unknown>;
|
|
17
17
|
readonly env?: Record<string, string | undefined>;
|
|
18
18
|
}
|
package/src/resolve.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Config resolution engine — merges config from multiple sources through
|
|
3
|
-
* a deterministic stack: defaults → base →
|
|
3
|
+
* a deterministic stack: defaults → base → profile → local → env.
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
import type { z } from 'zod';
|
|
@@ -16,11 +16,11 @@ import { zodDef } from './zod-utils.js';
|
|
|
16
16
|
// ---------------------------------------------------------------------------
|
|
17
17
|
|
|
18
18
|
/** Options for resolving config through the full stack. */
|
|
19
|
-
export interface
|
|
19
|
+
export interface DeriveConfigOptions<T extends z.ZodType> {
|
|
20
20
|
readonly schema: T;
|
|
21
21
|
readonly base?: Record<string, unknown> | undefined;
|
|
22
|
-
readonly
|
|
23
|
-
readonly
|
|
22
|
+
readonly profiles?: Record<string, Record<string, unknown>> | undefined;
|
|
23
|
+
readonly profile?: string | undefined;
|
|
24
24
|
readonly localOverrides?: Record<string, unknown> | undefined;
|
|
25
25
|
readonly env?: Record<string, string | undefined> | undefined;
|
|
26
26
|
}
|
|
@@ -219,11 +219,11 @@ const applyEnvOverrides = (
|
|
|
219
219
|
// Merge pipeline
|
|
220
220
|
// ---------------------------------------------------------------------------
|
|
221
221
|
|
|
222
|
-
/** Apply the layered merge: base →
|
|
222
|
+
/** Apply the layered merge: base → profile → local overrides. */
|
|
223
223
|
const mergeLayers = (
|
|
224
224
|
base: Record<string, unknown> | undefined,
|
|
225
|
-
|
|
226
|
-
|
|
225
|
+
profiles: Record<string, Record<string, unknown>> | undefined,
|
|
226
|
+
profile: string | undefined,
|
|
227
227
|
localOverrides: Record<string, unknown> | undefined
|
|
228
228
|
): Record<string, unknown> => {
|
|
229
229
|
let merged: Record<string, unknown> = {};
|
|
@@ -231,7 +231,7 @@ const mergeLayers = (
|
|
|
231
231
|
merged = deepMerge(merged, base);
|
|
232
232
|
}
|
|
233
233
|
|
|
234
|
-
const selected =
|
|
234
|
+
const selected = profile && profiles ? profiles[profile] : undefined;
|
|
235
235
|
if (selected) {
|
|
236
236
|
merged = deepMerge(merged, selected);
|
|
237
237
|
}
|
|
@@ -253,16 +253,16 @@ const formatValidationError = (
|
|
|
253
253
|
// ---------------------------------------------------------------------------
|
|
254
254
|
|
|
255
255
|
/**
|
|
256
|
-
*
|
|
256
|
+
* Derive config through the full stack: defaults → base → profile → local → env.
|
|
257
257
|
* Returns `Result.ok` with the validated config, or `Result.err` on validation failure.
|
|
258
258
|
*/
|
|
259
|
-
export const
|
|
260
|
-
options:
|
|
259
|
+
export const deriveConfig = <T extends z.ZodType>(
|
|
260
|
+
options: DeriveConfigOptions<T>
|
|
261
261
|
): Result<z.infer<T>, Error> => {
|
|
262
262
|
let merged = mergeLayers(
|
|
263
263
|
options.base,
|
|
264
|
-
options.
|
|
265
|
-
options.
|
|
264
|
+
options.profiles,
|
|
265
|
+
options.profile,
|
|
266
266
|
options.localOverrides
|
|
267
267
|
);
|
|
268
268
|
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
import { Result, trail } from '@ontrails/core';
|
|
8
8
|
import { z } from 'zod';
|
|
9
9
|
|
|
10
|
-
import {
|
|
10
|
+
import { configResource } from '../config-resource.js';
|
|
11
11
|
import { checkConfig } from '../doctor.js';
|
|
12
12
|
import { deepMerge } from '../merge.js';
|
|
13
13
|
|
|
@@ -33,7 +33,7 @@ const mergeValues = (
|
|
|
33
33
|
|
|
34
34
|
export const configCheck = trail('config.check', {
|
|
35
35
|
blaze: (input, ctx) => {
|
|
36
|
-
const state =
|
|
36
|
+
const state = configResource.from(ctx);
|
|
37
37
|
const effective = mergeValues(state.resolved, input.values);
|
|
38
38
|
const checked = checkConfig(state.schema, effective);
|
|
39
39
|
return Result.ok({
|
|
@@ -56,5 +56,5 @@ export const configCheck = trail('config.check', {
|
|
|
56
56
|
intent: 'read',
|
|
57
57
|
meta: { category: 'infrastructure' },
|
|
58
58
|
output: outputSchema,
|
|
59
|
-
|
|
59
|
+
resources: [configResource],
|
|
60
60
|
});
|
|
@@ -7,8 +7,8 @@
|
|
|
7
7
|
import { Result, trail } from '@ontrails/core';
|
|
8
8
|
import { z } from 'zod';
|
|
9
9
|
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
10
|
+
import { configResource } from '../config-resource.js';
|
|
11
|
+
import { deriveConfigFields } from '../derive-fields.js';
|
|
12
12
|
|
|
13
13
|
const fieldSchema = z.object({
|
|
14
14
|
deprecated: z.string().optional(),
|
|
@@ -26,8 +26,8 @@ const outputSchema = z.object({
|
|
|
26
26
|
|
|
27
27
|
export const configDescribe = trail('config.describe', {
|
|
28
28
|
blaze: (_input, ctx) => {
|
|
29
|
-
const state =
|
|
30
|
-
const fields =
|
|
29
|
+
const state = configResource.from(ctx);
|
|
30
|
+
const fields = deriveConfigFields(state.schema);
|
|
31
31
|
return Result.ok({ fields: [...fields] });
|
|
32
32
|
},
|
|
33
33
|
examples: [
|
|
@@ -40,5 +40,5 @@ export const configDescribe = trail('config.describe', {
|
|
|
40
40
|
intent: 'read',
|
|
41
41
|
meta: { category: 'infrastructure' },
|
|
42
42
|
output: outputSchema,
|
|
43
|
-
|
|
43
|
+
resources: [configResource],
|
|
44
44
|
});
|
|
@@ -7,9 +7,9 @@
|
|
|
7
7
|
import { Result, trail } from '@ontrails/core';
|
|
8
8
|
import { z } from 'zod';
|
|
9
9
|
|
|
10
|
-
import {
|
|
11
|
-
import type {
|
|
12
|
-
import {
|
|
10
|
+
import { configResource } from '../config-resource.js';
|
|
11
|
+
import type { DeriveConfigProvenanceOptions } from '../derive-provenance.js';
|
|
12
|
+
import { deriveConfigProvenance } from '../derive-provenance.js';
|
|
13
13
|
import type { ConfigState } from '../registry.js';
|
|
14
14
|
|
|
15
15
|
const provenanceEntrySchema = z.object({
|
|
@@ -34,11 +34,11 @@ const filterByPath = (
|
|
|
34
34
|
)
|
|
35
35
|
: entries;
|
|
36
36
|
|
|
37
|
-
/** Build
|
|
37
|
+
/** Build DeriveConfigProvenanceOptions from ConfigState, omitting undefined source overrides. */
|
|
38
38
|
const toExplainOptions = (
|
|
39
39
|
state: ConfigState
|
|
40
|
-
):
|
|
41
|
-
const base:
|
|
40
|
+
): DeriveConfigProvenanceOptions<typeof state.schema> => {
|
|
41
|
+
const base: DeriveConfigProvenanceOptions<typeof state.schema> = {
|
|
42
42
|
resolved: state.resolved,
|
|
43
43
|
schema: state.schema,
|
|
44
44
|
};
|
|
@@ -51,14 +51,14 @@ const toExplainOptions = (
|
|
|
51
51
|
/** Enrich explain options with env and source overrides from state. */
|
|
52
52
|
const enrichOptions = (
|
|
53
53
|
state: ConfigState,
|
|
54
|
-
options:
|
|
55
|
-
):
|
|
54
|
+
options: DeriveConfigProvenanceOptions<typeof state.schema>
|
|
55
|
+
): DeriveConfigProvenanceOptions<typeof state.schema> => {
|
|
56
56
|
let enriched = options;
|
|
57
57
|
if (state.env) {
|
|
58
58
|
enriched = { ...enriched, env: state.env };
|
|
59
59
|
}
|
|
60
|
-
if (state.
|
|
61
|
-
enriched = { ...enriched,
|
|
60
|
+
if (state.profile) {
|
|
61
|
+
enriched = { ...enriched, profile: state.profile };
|
|
62
62
|
}
|
|
63
63
|
if (state.local) {
|
|
64
64
|
enriched = { ...enriched, local: state.local };
|
|
@@ -68,9 +68,9 @@ const enrichOptions = (
|
|
|
68
68
|
|
|
69
69
|
export const configExplain = trail('config.explain', {
|
|
70
70
|
blaze: (input, ctx) => {
|
|
71
|
-
const state =
|
|
71
|
+
const state = configResource.from(ctx);
|
|
72
72
|
const options = enrichOptions(state, toExplainOptions(state));
|
|
73
|
-
const entries =
|
|
73
|
+
const entries = deriveConfigProvenance(options);
|
|
74
74
|
const filtered = filterByPath(entries, input.path);
|
|
75
75
|
return Result.ok({ entries: [...filtered] });
|
|
76
76
|
},
|
|
@@ -89,5 +89,5 @@ export const configExplain = trail('config.explain', {
|
|
|
89
89
|
intent: 'read',
|
|
90
90
|
meta: { category: 'infrastructure' },
|
|
91
91
|
output: outputSchema,
|
|
92
|
-
|
|
92
|
+
resources: [configResource],
|
|
93
93
|
});
|
|
@@ -14,12 +14,12 @@ import { Result, trail } from '@ontrails/core';
|
|
|
14
14
|
import type { z } from 'zod';
|
|
15
15
|
import { z as zod } from 'zod';
|
|
16
16
|
|
|
17
|
-
import {
|
|
17
|
+
import { configResource } from '../config-resource.js';
|
|
18
18
|
import {
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
} from '../
|
|
19
|
+
deriveConfigEnvExample,
|
|
20
|
+
deriveConfigExample,
|
|
21
|
+
deriveConfigJsonSchema,
|
|
22
|
+
} from '../derive/index.js';
|
|
23
23
|
|
|
24
24
|
const formatEnum = zod.enum(['toml', 'json', 'jsonc', 'yaml']);
|
|
25
25
|
|
|
@@ -34,13 +34,13 @@ const collectArtifacts = (
|
|
|
34
34
|
schema: z.ZodObject<Record<string, z.ZodType>>
|
|
35
35
|
): [string, string][] => {
|
|
36
36
|
const artifacts: [string, string][] = [];
|
|
37
|
-
const envContent =
|
|
37
|
+
const envContent = deriveConfigEnvExample(schema);
|
|
38
38
|
if (envContent.length > 0) {
|
|
39
39
|
artifacts.push(['.env.example', envContent]);
|
|
40
40
|
}
|
|
41
41
|
artifacts.push([
|
|
42
42
|
'.schema.json',
|
|
43
|
-
JSON.stringify(
|
|
43
|
+
JSON.stringify(deriveConfigJsonSchema(schema), null, 2),
|
|
44
44
|
]);
|
|
45
45
|
return artifacts;
|
|
46
46
|
};
|
|
@@ -63,9 +63,9 @@ const writeArtifacts = async (
|
|
|
63
63
|
|
|
64
64
|
export const configInit = trail('config.init', {
|
|
65
65
|
blaze: async (input, ctx) => {
|
|
66
|
-
const state =
|
|
66
|
+
const state = configResource.from(ctx);
|
|
67
67
|
const schema = state.schema as z.ZodObject<Record<string, z.ZodType>>;
|
|
68
|
-
const content =
|
|
68
|
+
const content = deriveConfigExample(schema, input.format);
|
|
69
69
|
|
|
70
70
|
if (input.dir) {
|
|
71
71
|
const writtenFiles = await writeArtifacts(input.dir, schema);
|
|
@@ -92,5 +92,5 @@ export const configInit = trail('config.init', {
|
|
|
92
92
|
intent: 'write',
|
|
93
93
|
meta: { category: 'infrastructure' },
|
|
94
94
|
output: outputSchema,
|
|
95
|
-
|
|
95
|
+
resources: [configResource],
|
|
96
96
|
});
|
package/tsconfig.tsbuildinfo
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"root":["./src/app-config.ts","./src/collect.ts","./src/compose.ts","./src/config-
|
|
1
|
+
{"root":["./src/app-config.ts","./src/collect.ts","./src/compose.ts","./src/config-resource.ts","./src/define-config.ts","./src/derive-fields.ts","./src/derive-provenance.ts","./src/doctor.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/derive/env.ts","./src/derive/example.ts","./src/derive/helpers.ts","./src/derive/index.ts","./src/derive/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"}
|
package/dist/config-gate.d.ts
DELETED
|
@@ -1,11 +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
|
-
export declare const configGate: Gate;
|
|
11
|
-
//# sourceMappingURL=config-gate.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"config-gate.d.ts","sourceRoot":"","sources":["../src/config-gate.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,gBAAgB,CAAC;AAE3C,eAAO,MAAM,UAAU,EAAE,IAIxB,CAAC"}
|
package/dist/config-gate.js
DELETED
package/dist/config-gate.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"config-gate.js","sourceRoot":"","sources":["../src/config-gate.ts"],"names":[],"mappings":"AAUA,MAAM,CAAC,MAAM,UAAU,GAAS;IAC9B,WAAW,EAAE,+DAA+D;IAC5E,IAAI,EAAE,QAAQ;IACd,IAAI,EAAE,CAAC,MAAM,EAAE,IAAI,EAAE,EAAE,CAAC,CAAC,KAAK,EAAE,GAAG,EAAE,EAAE,CAAC,IAAI,CAAC,KAAK,EAAE,GAAG,CAAC;CACzD,CAAC"}
|