@bymax-one/nest-config 1.0.0

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.
@@ -0,0 +1,571 @@
1
+ import * as _nestjs_common from '@nestjs/common';
2
+ import { DynamicModule } from '@nestjs/common';
3
+ import { z } from 'zod';
4
+
5
+ /**
6
+ * @fileoverview Foundational schema shape types for the two-level environment
7
+ * configuration convention: top-level keys are namespaces (Zod object schemas)
8
+ * whose leaves are the individual environment-derived values. Everything the
9
+ * package builds (the `defineEnv` factory, the source-name mapping, the
10
+ * validator, and the `ConfigService` dot-path accessor) is expressed in terms
11
+ * of these types.
12
+ * @layer Types
13
+ */
14
+
15
+ /**
16
+ * A single environment-derived leaf schema.
17
+ *
18
+ * Any Zod type may be a leaf (`z.string()`, `z.coerce.number()`, `z.enum(...)`,
19
+ * and so on). Leaves live one level below a namespace and never at the top
20
+ * level of a shape.
21
+ */
22
+ type EnvLeaf = z.ZodType;
23
+ /**
24
+ * A configuration namespace: a Zod object schema that groups related leaves
25
+ * under one top-level key (for example `server`, `database`, or `auth`).
26
+ */
27
+ type EnvNamespace = z.ZodObject;
28
+ /**
29
+ * The shape accepted by `defineEnv`.
30
+ *
31
+ * Encodes the two-level convention as a record of namespace keys, each mapped
32
+ * to a Zod object schema. A bare leaf at the top level is intentionally not
33
+ * assignable, which keeps the namespace boundary explicit and greppable.
34
+ */
35
+ type EnvShape = Record<string, EnvNamespace>;
36
+ /**
37
+ * The parsed, validated output type produced by a shape.
38
+ *
39
+ * Mirrors what Zod infers for `z.object(shape)`, so applied coercions and
40
+ * defaults are reflected exactly. Used to type the `infer` phantom accessor on
41
+ * the schema returned by `defineEnv`.
42
+ *
43
+ * @typeParam TShape - The env shape whose composed output is inferred.
44
+ */
45
+ type EnvOutput<TShape extends EnvShape> = z.infer<z.ZodObject<TShape>>;
46
+ /**
47
+ * The schema returned by `defineEnv`.
48
+ *
49
+ * The composed Zod object augmented with a phantom `infer` accessor that
50
+ * carries the inferred configuration type for ergonomic extraction via
51
+ * `typeof schema.infer`. The property exists only in the type system; reading
52
+ * it at runtime is not part of the contract.
53
+ *
54
+ * @typeParam TShape - The env shape the schema was composed from.
55
+ */
56
+ type EnvSchema<TShape extends EnvShape = EnvShape> = z.ZodObject<TShape> & {
57
+ /** Phantom accessor exposing the inferred config type; use `typeof schema.infer`. */
58
+ readonly infer: EnvOutput<TShape>;
59
+ };
60
+ /**
61
+ * The union of every valid `namespace.leaf` dot-path for a config object.
62
+ *
63
+ * Given a parsed config type whose top-level keys are namespaces and whose
64
+ * second-level keys are leaves, this resolves to the union of `` `${namespace}.${leaf}` ``
65
+ * strings. Inference is deliberately fixed at exactly two levels (namespace and
66
+ * leaf) with no recursive conditional types, so the compiler cost stays flat
67
+ * even for large schemas. Deeper nesting stays typed through `getAll()` but is
68
+ * intentionally not addressable through a dot-path; this is a documented limit
69
+ * of the two-level convention, not an oversight.
70
+ *
71
+ * @typeParam TConfig - The parsed, two-level configuration object type.
72
+ * @example
73
+ * ```typescript
74
+ * type AppConfig = { server: { port: number }; database: { url: string } };
75
+ * type Paths = Path<AppConfig>; // 'server.port' | 'database.url'
76
+ * ```
77
+ */
78
+ type Path<TConfig> = {
79
+ [Namespace in keyof TConfig & string]: TConfig[Namespace] extends Record<string, unknown> ? {
80
+ [Leaf in keyof TConfig[Namespace] & string]: `${Namespace}.${Leaf}`;
81
+ }[keyof TConfig[Namespace] & string] : never;
82
+ }[keyof TConfig & string];
83
+ /**
84
+ * The leaf value type addressed by a `namespace.leaf` path.
85
+ *
86
+ * Splits the path on its single dot and walks exactly the two fixed levels to
87
+ * resolve the leaf type, so `PathValue<AppConfig, 'server.port'>` is `number`.
88
+ * The split is a single template-literal inference with no recursion, matching
89
+ * the flat-cost guarantee of {@link Path}.
90
+ *
91
+ * @typeParam TConfig - The parsed, two-level configuration object type.
92
+ * @typeParam TPath - A path drawn from {@link Path}<TConfig>.
93
+ * @example
94
+ * ```typescript
95
+ * type AppConfig = { server: { port: number }; database: { url: string } };
96
+ * type Port = PathValue<AppConfig, 'server.port'>; // number
97
+ * ```
98
+ */
99
+ type PathValue<TConfig, TPath extends Path<TConfig>> = TPath extends `${infer Namespace}.${infer Leaf}` ? Namespace extends keyof TConfig ? Leaf extends keyof TConfig[Namespace] ? TConfig[Namespace][Leaf] : never : never : never;
100
+
101
+ /**
102
+ * @fileoverview Value-free error model for the configuration validation
103
+ * pipeline: the frozen issue-code catalog, the ConfigIssue shape, and the
104
+ * BymaxConfigValidationError aggregate. The error carries every violation as a
105
+ * structured, immutable issue list and renders a multi-line report that never
106
+ * echoes a raw source value.
107
+ * @layer Error
108
+ */
109
+ /**
110
+ * Frozen catalog of stable, machine-readable configuration error codes.
111
+ *
112
+ * `VALIDATION` is the top-level code carried by the thrown error; the remaining
113
+ * three classify individual issues. Exported once and referenced everywhere so
114
+ * no code string is ever inlined at a use site.
115
+ *
116
+ * @example
117
+ * ```typescript
118
+ * if (issue.code === ConfigErrorCode.MISSING) {
119
+ * // handle an absent required variable
120
+ * }
121
+ * ```
122
+ */
123
+ declare const ConfigErrorCode: Readonly<{
124
+ /** Top-level error code: one or more schema violations were collected. */
125
+ readonly VALIDATION: "BYMAX_CONFIG_VALIDATION";
126
+ /** Issue code: a required variable is absent from the source. */
127
+ readonly MISSING: "BYMAX_CONFIG_MISSING";
128
+ /** Issue code: a variable is present but violates its constraint. */
129
+ readonly INVALID: "BYMAX_CONFIG_INVALID";
130
+ /** Issue code: strict mode found a source variable matching no declared leaf. */
131
+ readonly UNKNOWN_KEY: "BYMAX_CONFIG_UNKNOWN_KEY";
132
+ }>;
133
+ /** The stable top-level code carried by {@link BymaxConfigValidationError}. */
134
+ type ConfigValidationCode = typeof ConfigErrorCode.VALIDATION;
135
+ /** Machine-readable code classifying a single {@link ConfigIssue}. */
136
+ type ConfigIssueCode = typeof ConfigErrorCode.MISSING | typeof ConfigErrorCode.INVALID | typeof ConfigErrorCode.UNKNOWN_KEY;
137
+ /**
138
+ * A single configuration violation, described entirely without any raw value.
139
+ *
140
+ * Every field is value-free by contract: `message` states the expected
141
+ * constraint, never the received input, so an issue can be logged or serialized
142
+ * without leaking a secret.
143
+ */
144
+ interface ConfigIssue {
145
+ /** Nested config path, e.g. `database.url`; the namespace alone (e.g. `database`) for an unknown-key issue. */
146
+ readonly path: string;
147
+ /** Resolved environment variable name, e.g. `DATABASE_URL`. */
148
+ readonly variable: string;
149
+ /** Stable machine-readable classification. */
150
+ readonly code: ConfigIssueCode;
151
+ /** Human-readable constraint description, value-free. */
152
+ readonly message: string;
153
+ }
154
+ /**
155
+ * Aggregated, value-free configuration validation error.
156
+ *
157
+ * Thrown once at bootstrap when the source fails the schema. It carries every
158
+ * violation in an immutable, structured {@link ConfigIssue} list and a
159
+ * human-readable message that lists the offending variables and their expected
160
+ * constraints, never their received values.
161
+ *
162
+ * @example
163
+ * ```typescript
164
+ * try {
165
+ * validateEnv(schema, source);
166
+ * } catch (error) {
167
+ * if (error instanceof BymaxConfigValidationError) {
168
+ * for (const issue of error.issues) report(issue.variable, issue.code);
169
+ * }
170
+ * }
171
+ * ```
172
+ */
173
+ declare class BymaxConfigValidationError extends Error {
174
+ /** The stable top-level error code. */
175
+ readonly code: ConfigValidationCode;
176
+ /** The immutable, aggregated list of collected violations. */
177
+ readonly issues: ReadonlyArray<ConfigIssue>;
178
+ /**
179
+ * Build the aggregated error from a list of value-free issues.
180
+ *
181
+ * @param issues - Every collected violation; copied and frozen so neither the
182
+ * caller nor consumer code can mutate the reported list.
183
+ */
184
+ constructor(issues: ReadonlyArray<ConfigIssue>);
185
+ }
186
+
187
+ /**
188
+ * @fileoverview Public options contract for the BymaxConfigModule dynamic
189
+ * module: the schema to validate against, an optional raw source (defaulting to
190
+ * the process environment inside the provider factory), an optional value-free
191
+ * observability hook invoked before the fail-fast throw, and an optional strict
192
+ * unknown-key flag. This file declares only types and carries no runtime code.
193
+ * @layer Types
194
+ */
195
+
196
+ /**
197
+ * Registration options for {@link BymaxConfigModule}.
198
+ *
199
+ * Passed to `forRoot` directly or produced by a `forRootAsync` factory. Only the
200
+ * schema is required; the remaining fields tune the source, observability, and
201
+ * strictness of the single bootstrap-time validation.
202
+ *
203
+ * @typeParam TSchema - The schema produced by `defineEnv`, carrying the inferred
204
+ * configuration type.
205
+ * @example
206
+ * ```typescript
207
+ * BymaxConfigModule.forRoot({ schema: envSchema, strict: true });
208
+ * ```
209
+ */
210
+ interface BymaxConfigModuleOptions<TSchema extends EnvSchema = EnvSchema> {
211
+ /** Schema produced by `defineEnv`. Required. */
212
+ readonly schema: TSchema;
213
+ /**
214
+ * Raw source record. Defaults to the process environment in the provider
215
+ * factory. Injectable for tests and tooling; a supplied source is used
216
+ * verbatim, with no environment fallback or merge.
217
+ */
218
+ readonly source?: Record<string, string | undefined>;
219
+ /**
220
+ * Observability hook invoked once with the structured, value-free issue list
221
+ * immediately before the validation error is thrown. It is observability
222
+ * only and cannot suppress the failure: the error still propagates even when
223
+ * the hook itself throws.
224
+ */
225
+ readonly onValidationError?: (issues: ReadonlyArray<ConfigIssue>) => void;
226
+ /**
227
+ * When true, source variables that match a declared namespace prefix but no
228
+ * declared leaf produce `BYMAX_CONFIG_UNKNOWN_KEY` issues. Defaults to false.
229
+ */
230
+ readonly strict?: boolean;
231
+ }
232
+
233
+ /**
234
+ * Extra registration options that shape the module without being injected.
235
+ *
236
+ * `setExtras` keeps these out of the {@link BYMAX_CONFIG_OPTIONS} provider, so a
237
+ * consumer of the options token never sees `isGlobal`; it only influences the
238
+ * generated `DynamicModule`.
239
+ */
240
+ interface BymaxConfigModuleExtras {
241
+ /**
242
+ * Register the module globally by mapping to `DynamicModule.global`. The
243
+ * builder applies the `true` default before the transform runs, so this is
244
+ * required at the extras level; consumers see it optional through the
245
+ * generated `OPTIONS_TYPE` (a `Partial` of the extras). Defaults to true.
246
+ */
247
+ readonly isGlobal: boolean;
248
+ }
249
+ /**
250
+ * The generated dynamic-module artifacts.
251
+ *
252
+ * `MODULE_OPTIONS_TOKEN` is the {@link BYMAX_CONFIG_OPTIONS} Symbol (bound
253
+ * through `optionsInjectionToken`), so the config provider factory and the
254
+ * generated options provider share one token. `OPTIONS_TYPE` and
255
+ * `ASYNC_OPTIONS_TYPE` type the `forRoot` / `forRootAsync` parameters, including
256
+ * the `isGlobal` extra.
257
+ */
258
+ declare const ConfigurableModuleClass: _nestjs_common.ConfigurableModuleCls<BymaxConfigModuleOptions<EnvSchema>, "forRoot", "create", BymaxConfigModuleExtras>;
259
+ declare const OPTIONS_TYPE: BymaxConfigModuleOptions<EnvSchema> & Partial<BymaxConfigModuleExtras>;
260
+ declare const ASYNC_OPTIONS_TYPE: _nestjs_common.ConfigurableModuleAsyncOptions<BymaxConfigModuleOptions<EnvSchema>, "create"> & Partial<BymaxConfigModuleExtras>;
261
+ /** Parameter type of `BymaxConfigModule.forRoot`: the options plus the `isGlobal` extra. */
262
+ type BymaxConfigForRootOptions = typeof OPTIONS_TYPE;
263
+ /** Parameter type of `BymaxConfigModule.forRootAsync`: async options plus the `isGlobal` extra. */
264
+ type BymaxConfigForRootAsyncOptions = typeof ASYNC_OPTIONS_TYPE;
265
+
266
+ /**
267
+ * @fileoverview BymaxConfigModule: the dynamic module consumers import once via
268
+ * `forRoot` / `forRootAsync`. It extends the generated ConfigurableModuleClass,
269
+ * inheriting the `isGlobal` extra mapping, and augments each returned definition
270
+ * with the validated, deep-frozen BYMAX_CONFIG provider and the typed
271
+ * ConfigService accessor, so the config is produced (and validation runs) as
272
+ * part of module resolution and both surfaces are exported for consumers.
273
+ * @layer Module
274
+ */
275
+
276
+ /**
277
+ * NestJS dynamic module exposing typed, validated environment configuration.
278
+ *
279
+ * Import it once (typically in `AppModule`) with `forRoot` for a static schema
280
+ * or `forRootAsync` when the source is resolved through other providers. It is
281
+ * global by default; pass `isGlobal: false` to scope it to a submodule. Either
282
+ * entry point registers and exports the frozen {@link BYMAX_CONFIG} provider.
283
+ *
284
+ * @example
285
+ * ```typescript
286
+ * @Module({ imports: [BymaxConfigModule.forRoot({ schema: envSchema })] })
287
+ * export class AppModule {}
288
+ * ```
289
+ */
290
+ declare class BymaxConfigModule extends ConfigurableModuleClass {
291
+ /**
292
+ * Register the module synchronously from a static schema.
293
+ *
294
+ * @param options - The module options plus the optional `isGlobal` extra.
295
+ * @returns The dynamic module augmented with the frozen config provider.
296
+ */
297
+ static forRoot(options: BymaxConfigForRootOptions): DynamicModule;
298
+ /**
299
+ * Register the module asynchronously, resolving options through a factory.
300
+ *
301
+ * @param options - The async options with `useFactory` and explicit `inject`.
302
+ * @returns The dynamic module augmented with the frozen config provider.
303
+ */
304
+ static forRootAsync(options: BymaxConfigForRootAsyncOptions): DynamicModule;
305
+ /**
306
+ * Append the config provider and typed accessor to a generated definition.
307
+ *
308
+ * Registers the frozen {@link BYMAX_CONFIG} provider and the {@link ConfigService}
309
+ * accessor (which injects that token), then exports both so consumers can
310
+ * inject either the raw frozen object or the typed service.
311
+ *
312
+ * @param definition - The dynamic module produced by the builder base class.
313
+ * @returns A definition that provides and exports {@link BYMAX_CONFIG} and
314
+ * {@link ConfigService}.
315
+ */
316
+ private static withConfigProvider;
317
+ }
318
+
319
+ /**
320
+ * @fileoverview ConfigService: the typed, injectable accessor over the frozen
321
+ * BYMAX_CONFIG object. It is the recommended consumption surface for the
322
+ * validated configuration, offering compile-time dot-path inference (`get`),
323
+ * the frozen root (`getAll`), and a declared-path presence check (`has`). The
324
+ * config is injected explicitly through the module-local Symbol token, so the
325
+ * service carries no decorator metadata and needs no `emitDecoratorMetadata`.
326
+ * @layer Service
327
+ */
328
+
329
+ /**
330
+ * Typed accessor over the validated, deep-frozen configuration object.
331
+ *
332
+ * Wraps the frozen config registered under {@link BYMAX_CONFIG} and exposes it
333
+ * through dot-path access limited to the two-level `namespace.leaf` convention.
334
+ * Because validation completed at bootstrap, every declared leaf either passed
335
+ * validation or received its default (or the process never started), so `get`
336
+ * never throws for a declared path.
337
+ *
338
+ * @typeParam TConfig - The parsed, two-level configuration object type.
339
+ * @example
340
+ * ```typescript
341
+ * @Injectable()
342
+ * export class InvoiceService {
343
+ * public constructor(
344
+ * @Inject(ConfigService) private readonly config: ConfigService<AppConfig>,
345
+ * ) {}
346
+ *
347
+ * public connect(): Connection {
348
+ * return connect(this.config.get('database.url'), this.config.get('server.port'));
349
+ * }
350
+ * }
351
+ * ```
352
+ */
353
+ declare class ConfigService<TConfig> {
354
+ private readonly config;
355
+ /** Precomputed `namespace.leaf` to value lookup, built once at construction. */
356
+ private readonly leaves;
357
+ /**
358
+ * Bind the accessor to the frozen configuration object.
359
+ *
360
+ * Builds a flat `namespace.leaf` lookup once so `get`/`has` are constant-time
361
+ * and never rebuild a map per call. Iteration over entries (rather than dynamic
362
+ * bracket indexing) avoids introducing a non-literal property sink.
363
+ *
364
+ * @param config - The validated, deep-frozen config injected via {@link BYMAX_CONFIG}.
365
+ */
366
+ constructor(config: Readonly<TConfig>);
367
+ /**
368
+ * Read a leaf value by its `namespace.leaf` dot-path.
369
+ *
370
+ * The return type is inferred from the path, so `get('server.port')` is typed
371
+ * `number` with no cast. Never throws for a declared path: validation ran once
372
+ * at bootstrap. An optional leaf without a default may still resolve to
373
+ * `undefined` when its type includes it.
374
+ *
375
+ * @typeParam TPath - A declared path drawn from {@link Path}<TConfig>.
376
+ * @param path - The two-level dot-path to resolve.
377
+ * @returns The leaf value, typed as {@link PathValue}<TConfig, TPath>.
378
+ * @example
379
+ * ```typescript
380
+ * const port = config.get('server.port'); // number
381
+ * ```
382
+ */
383
+ get<TPath extends Path<TConfig>>(path: TPath): PathValue<TConfig, TPath>;
384
+ /**
385
+ * Return the whole validated configuration as its deep-frozen root.
386
+ *
387
+ * Yields the same frozen reference the module registered, fully typed for
388
+ * arbitrary depth, which is the escape hatch for nesting deeper than the
389
+ * two-level dot-path inference covers.
390
+ *
391
+ * @returns The frozen configuration root.
392
+ * @example
393
+ * ```typescript
394
+ * const everything = config.getAll(); // Readonly<AppConfig>
395
+ * ```
396
+ */
397
+ getAll(): Readonly<TConfig>;
398
+ /**
399
+ * Report whether a declared path resolves to a defined value.
400
+ *
401
+ * Returns `false` when the leaf resolved to `undefined` (an optional leaf
402
+ * without a default), distinguishing "declared but unset" from "declared and
403
+ * set" without a truthiness pitfall on falsy values such as `0` or `''`.
404
+ *
405
+ * @param path - The two-level dot-path to check.
406
+ * @returns True when the resolved value is not `undefined`.
407
+ * @example
408
+ * ```typescript
409
+ * if (config.has('redis.url')) connectRedis(config.get('redis.url'));
410
+ * ```
411
+ */
412
+ has(path: Path<TConfig>): boolean;
413
+ /**
414
+ * Resolve a two-level dot-path to its raw leaf value.
415
+ *
416
+ * Looks the path up in the precomputed `namespace.leaf` map, so an unset leaf
417
+ * (or, defensively, an unexpected path) yields `undefined` instead of throwing.
418
+ *
419
+ * @param path - The declared two-level dot-path.
420
+ * @returns The leaf value as `unknown`, or `undefined` when the leaf is unset.
421
+ */
422
+ private resolve;
423
+ }
424
+
425
+ /**
426
+ * @fileoverview Symbol dependency-injection tokens for the configuration
427
+ * module. Both tokens are module-local Symbols, never strings and never
428
+ * registered in the global symbol registry, so their identity is unique across
429
+ * realms and cannot collide with another provider's token. Every injection site
430
+ * references these constants through an explicit `@Inject(...)`.
431
+ * @layer Constants
432
+ */
433
+ /**
434
+ * Injection token for the resolved {@link BymaxConfigModuleOptions}.
435
+ *
436
+ * The `forRoot` value provider and the `forRootAsync` factory both register the
437
+ * options object under this token; the config provider factory injects it to
438
+ * read the schema, source, hook, and strict flag.
439
+ *
440
+ * @example
441
+ * ```typescript
442
+ * constructor(@Inject(BYMAX_CONFIG_OPTIONS) options: BymaxConfigModuleOptions) {}
443
+ * ```
444
+ */
445
+ declare const BYMAX_CONFIG_OPTIONS: unique symbol;
446
+ /**
447
+ * Injection token for the deep-frozen, validated configuration object.
448
+ *
449
+ * The provider factory registers the parsed, frozen config under this token and
450
+ * the module exports it, so consumers (and the typed accessor) can inject the
451
+ * validated config directly for factory-style wiring.
452
+ *
453
+ * @example
454
+ * ```typescript
455
+ * constructor(@Inject(BYMAX_CONFIG) config: Readonly<AppConfig>) {}
456
+ * ```
457
+ */
458
+ declare const BYMAX_CONFIG: unique symbol;
459
+
460
+ /**
461
+ * @fileoverview The `defineEnv` factory: a thin, typed wrapper over
462
+ * `z.object(...)` that establishes the two-level namespace convention of the
463
+ * package and exposes the inferred configuration type through a phantom
464
+ * accessor. It composes the caller's schemas as-is and never rewrites, clones,
465
+ * or wraps them, so coercion and defaults stay explicit consumer choices.
466
+ * @layer Utility
467
+ */
468
+
469
+ /**
470
+ * Compose environment namespaces into a single typed schema.
471
+ *
472
+ * Wraps the passed shape in `z.object(...)` and returns it augmented with the
473
+ * `infer` phantom accessor, so the configuration type is extracted ergonomically
474
+ * via `typeof schema.infer`. The caller's namespace and leaf schemas are reused
475
+ * unchanged: `defineEnv` documents and encourages `z.coerce.*` for the
476
+ * string-shaped environment, but never injects coercion or defaults itself.
477
+ *
478
+ * @typeParam TShape - The two-level shape: namespace keys mapped to Zod objects.
479
+ * @param shape - Top-level namespaces, each a Zod object of leaf schemas.
480
+ * @returns The composed Zod object schema carrying the `infer` type helper.
481
+ * @example
482
+ * ```typescript
483
+ * import { defineEnv } from '@bymax-one/nest-config';
484
+ * import { z } from 'zod';
485
+ *
486
+ * export const envSchema = defineEnv({
487
+ * server: z.object({
488
+ * port: z.coerce.number().int().min(1).max(65535).default(3000),
489
+ * }),
490
+ * database: z.object({ url: z.url() }),
491
+ * });
492
+ *
493
+ * export type AppConfig = typeof envSchema.infer;
494
+ * ```
495
+ */
496
+ declare function defineEnv<TShape extends EnvShape>(shape: TShape): EnvSchema<TShape>;
497
+
498
+ /**
499
+ * @fileoverview The BYMAX_CONFIG provider factory: the fail-fast heart of the
500
+ * module. It resolves the source (defaulting to `process.env`, using a custom
501
+ * source verbatim), runs the single-pass validator, and on success returns the
502
+ * deep-frozen typed config. On failure it invokes the value-free observability
503
+ * hook exactly once before rethrowing the aggregated error; the hook is
504
+ * observability only and can never suppress or replace the failure, so a
505
+ * misconfigured process is stopped before any consumer provider is constructed.
506
+ * @layer Provider
507
+ */
508
+
509
+ /**
510
+ * Validate the source against the schema, freeze the result, or fail fast.
511
+ *
512
+ * Resolves the source to the supplied record or `process.env`, runs exactly one
513
+ * validation pass, and returns the deep-frozen typed output. On a validation
514
+ * failure the hook runs once with the issues and the aggregated error is
515
+ * rethrown unchanged, aborting module resolution before any consumer provider
516
+ * is instantiated.
517
+ *
518
+ * @typeParam TShape - The two-level schema shape.
519
+ * @param options - The resolved module options (schema, source, hook, strict).
520
+ * @returns The validated, deep-frozen configuration output.
521
+ * @throws {BymaxConfigValidationError} When the source fails the schema.
522
+ * @example
523
+ * ```typescript
524
+ * const config = createValidatedConfig({ schema: envSchema });
525
+ * config.server.port; // typed, validated, frozen
526
+ * ```
527
+ */
528
+ declare function createValidatedConfig<TShape extends EnvShape>(options: Omit<BymaxConfigModuleOptions, 'schema'> & {
529
+ readonly schema: EnvSchema<TShape>;
530
+ }): Readonly<EnvOutput<TShape>>;
531
+
532
+ /**
533
+ * @fileoverview Deterministic mapping between nested config paths and flat
534
+ * environment variable names. Each leaf resolves to exactly one variable:
535
+ * SCREAMING_SNAKE_CASE of the joined path (`database.url` reads `DATABASE_URL`),
536
+ * unless the leaf declares a `meta({ env })` override, which wins. This is an
537
+ * internal module consumed by the validator; it is not part of the public API.
538
+ * @layer Utility
539
+ */
540
+
541
+ /**
542
+ * The resolved binding between one config leaf and its source variable name.
543
+ */
544
+ interface SourceBinding {
545
+ /** Nested config path, e.g. `database.url`. */
546
+ readonly path: string;
547
+ /** Resolved environment variable name, e.g. `DATABASE_URL`. */
548
+ readonly variable: string;
549
+ }
550
+ /**
551
+ * Resolve the source variable name of every leaf in a `defineEnv` schema.
552
+ *
553
+ * Walks the two-level schema (namespaces then leaves) and yields one binding
554
+ * per leaf. The variable name is the `meta({ env })` override when present,
555
+ * otherwise the SCREAMING_SNAKE_CASE derivation of the joined path. The result
556
+ * is total: every declared leaf maps to exactly one variable, in declaration
557
+ * order.
558
+ *
559
+ * @typeParam TShape - The two-level shape the schema was composed from.
560
+ * @param schema - A schema produced by `defineEnv`.
561
+ * @returns The ordered list of leaf-to-variable bindings, typed `readonly`.
562
+ * @example
563
+ * ```typescript
564
+ * const schema = defineEnv({ database: z.object({ url: z.url() }) });
565
+ * resolveSourceNames(schema);
566
+ * // => [{ path: 'database.url', variable: 'DATABASE_URL' }]
567
+ * ```
568
+ */
569
+ declare function resolveSourceNames<TShape extends EnvShape = EnvShape>(schema: EnvSchema<TShape>): readonly SourceBinding[];
570
+
571
+ export { BYMAX_CONFIG, BYMAX_CONFIG_OPTIONS, BymaxConfigModule, type BymaxConfigModuleOptions, BymaxConfigValidationError, ConfigErrorCode, type ConfigIssue, type ConfigIssueCode, ConfigService, type ConfigValidationCode, type EnvLeaf, type EnvNamespace, type EnvOutput, type EnvSchema, type EnvShape, type Path, type PathValue, type SourceBinding, createValidatedConfig, defineEnv, resolveSourceNames };