@pawells/config 2.3.1 → 3.0.1

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/README.md CHANGED
@@ -1,206 +1,309 @@
1
1
  # @pawells/config
2
2
 
3
- [![CI](https://github.com/PhillipAWells/common/actions/workflows/ci.yml/badge.svg)](https://github.com/PhillipAWells/common/actions/workflows/ci.yml)
3
+ [![CI](https://github.com/PhillipAWells/config/actions/workflows/ci.yml/badge.svg)](https://github.com/PhillipAWells/config/actions/workflows/ci.yml)
4
+ [![npm](https://img.shields.io/npm/v/@pawells/config)](https://www.npmjs.com/package/@pawells/config)
4
5
  [![Node](https://img.shields.io/badge/node-%3E%3D22-brightgreen)](https://nodejs.org)
5
- [![npm version](https://img.shields.io/npm/v/@pawells/config.svg)](https://www.npmjs.com/package/@pawells/config)
6
- [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](./LICENSE)
6
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](../../LICENSE)
7
7
 
8
8
  ## Description
9
9
 
10
- Runtime configuration manager with Zod schema validation. Provides a singleton `ConfigManager` class for registering typed configuration schemas, setting values from multiple sources (defaults and runtime overrides), and retrieving strongly-typed configuration values with automatic validation. Supports environment variables, CLI arguments, and programmatic configuration.
10
+ `@pawells/config` is the core package of the `@pawells/config-workspace` suite. It provides:
11
+
12
+ - A **global singleton** (`ConfigManager`) and an **instance-based** (`ScopedConfigManager`) configuration manager, both backed by [Zod](https://zod.dev) schema validation.
13
+ - A **schema factory** (`RegisterConfigSchema`) that derives typed, namespaced accessor objects from a Zod object schema, eliminating the need to call `ConfigManager.Register` for every key individually.
14
+ - A **`Secret()` wrapper** that marks Zod field schemas as sensitive — secret values are automatically redacted in templates and error messages.
15
+ - An **abstract base class** (`ConfigProvider`) and two interfaces (`IConfigProvider`, `ISyncConfigProvider`) for building custom providers.
16
+ - A structured **error hierarchy** rooted at `ConfigError`.
17
+
18
+ Values are resolved through a three-tier precedence model:
19
+
20
+ ```
21
+ Registered defaults < Provider values < Runtime overrides (Set)
22
+ ```
23
+
24
+ See the [workspace README](../../README.md) for an end-to-end quick start. See [CHANGELOG.md](../../CHANGELOG.md) for version history.
11
25
 
12
26
  ## Requirements
13
27
 
14
- - **Node.js**: 22.0.0 or later
15
- - **TypeScript**: 6.0.0 or later (if consuming from source)
16
- - **zod**: ^4.4.3 (peer dependency, required at runtime for schema validation)
28
+ - Node.js `>=22.0.0`
29
+ - `zod` `^4.4.3` (peer dependency, installed automatically)
17
30
 
18
31
  ## Installation
19
32
 
20
- Install from npm:
33
+ ```sh
34
+ yarn add @pawells/config
35
+ ```
36
+
37
+ ## Quick Start
21
38
 
22
- ```bash
23
- npm install @pawells/config
39
+ ```typescript
40
+ import { z } from 'zod';
41
+ import { ConfigManager, RegisterConfigSchema, Secret } from '@pawells/config';
42
+ import { ConfigEnvironmentProvider } from '@pawells/config-provider-env';
43
+
44
+ // 1. Register providers BEFORE importing schema modules.
45
+ // Provider values are merged into schemas at registration time.
46
+ await ConfigEnvironmentProvider.Register({ path: '.env' });
47
+
48
+ // 2. Define and register a schema.
49
+ // The prefix 'DATABASE_' is derived from the name 'Database'.
50
+ const DatabaseConfig = RegisterConfigSchema('Database', z.object({
51
+ HOST: z.string().min(1).default('localhost'),
52
+ PORT: z.coerce.number().int().positive().default(5432),
53
+ PASSWORD: Secret(z.string().min(1)).default(''),
54
+ }));
55
+
56
+ // 3. Retrieve validated, typed values.
57
+ const host = DatabaseConfig.Get('HOST'); // string
58
+ const port = DatabaseConfig.Get('PORT'); // number
59
+
60
+ // 4. Redact secrets for safe logging.
61
+ console.log(DatabaseConfig.Redact());
62
+ // → { HOST: 'localhost', PORT: 5432, PASSWORD: '***' }
24
63
  ```
25
64
 
26
- Or with yarn:
65
+ ## API Reference
27
66
 
28
- ```bash
29
- yarn add @pawells/config
67
+ ### `RegisterConfigSchema(name, schema)`
68
+
69
+ Registers all fields of a Zod object schema with `ConfigManager` and returns a typed accessor object.
70
+
71
+ ```typescript
72
+ function RegisterConfigSchema<TSchema extends z.ZodRawShape>(
73
+ name: string,
74
+ schema: z.ZodObject<TSchema>
75
+ ): IConfigSchemaObject<z.infer<typeof schema>>
30
76
  ```
31
77
 
32
- ## Quick Start
78
+ - `name` — Human-readable namespace. The env-var prefix is derived as `name.toUpperCase() + '_'` (e.g. `'Database'` → `DATABASE_`).
79
+ - `schema` — A `z.object(...)` schema. Every field should declare a `.default(...)` value.
33
80
 
34
- ### Using CreateConfigSchema (Recommended)
81
+ **Returns** an `IConfigSchemaObject` with the following members:
35
82
 
36
- The `CreateConfigSchema` factory is the recommended pattern for most use cases. It provides automatic environment variable parsing with a prefix and strongly-typed access to configuration values.
83
+ | Member | Signature | Description |
84
+ |---|---|---|
85
+ | `name` | `string` | The namespace name passed to `RegisterConfigSchema`. |
86
+ | `Get` | `Get<K>(key: K): TConfig[K]` | Retrieve the typed, validated value for `key`. Resolves DEFAULT → providers → OVERRIDE. |
87
+ | `Set` | `Set<K>(key: K, value: TConfig[K], source?: TConfigSource): void` | Set a value. Target is `'OVERRIDE'` by default. |
88
+ | `Validate` | `Validate<K>(key: K, value: unknown): boolean` | Check whether a value satisfies the field schema without setting it. |
89
+ | `IsSecret` | `IsSecret(key: TKeys): boolean` | Returns `true` if the field was wrapped with `Secret()`. |
90
+ | `GetSecretKeys` | `GetSecretKeys(): Array<TKeys>` | Returns all keys marked with `Secret()` in schema insertion order. |
91
+ | `Redact` | `Redact(): Record<string, unknown>` | Returns all resolved values; secret fields are replaced with `'***'`. Unset/unregistered keys are omitted. |
92
+
93
+ **Important:** Call `ConfigManager.RegisterProvider` for all providers before any module that calls `RegisterConfigSchema` is imported. Provider values are captured when schemas are registered.
94
+
95
+ ---
96
+
97
+ ### `Secret(schema)`
98
+
99
+ Marks a Zod field schema as secret using Zod v4's `globalRegistry` metadata. The TypeScript inferred type of the schema is unchanged.
37
100
 
38
101
  ```typescript
39
- import { z } from 'zod/v4';
40
- import { CreateConfigSchema } from '@pawells/config';
41
-
42
- // Define your configuration schema
43
- const SERVICE_CONFIG_SCHEMA = z.object({
44
- PORT: z.coerce.number().int().positive().default(3000),
45
- HOST: z.string().default('localhost'),
46
- DEBUG: z.boolean().default(false),
47
- DATABASE_URL: z.string().url(),
48
- API_TIMEOUT: z.coerce.number().int().positive().default(30000),
102
+ function Secret<T extends z.ZodTypeAny>(schema: T): T
103
+ ```
104
+
105
+ ```typescript
106
+ import { Secret } from '@pawells/config';
107
+ import { z } from 'zod';
108
+
109
+ // In a schema definition:
110
+ const schema = z.object({
111
+ API_KEY: Secret(z.string().min(32)).default(''),
49
112
  });
113
+ ```
50
114
 
51
- // Create a typed config object with 'SERVICE_' prefix
52
- export const ServiceConfig = CreateConfigSchema(SERVICE_CONFIG_SCHEMA, 'SERVICE_');
115
+ Secret fields are:
116
+ - Blanked (empty value) in `.env` template output (`ConfigEnvironmentProvider.Save` in template mode).
117
+ - Written as `null` in JSON template output (`ConfigJSONProvider.Save` in template mode).
118
+ - Replaced with `'***'` by `IConfigSchemaObject.Redact()`.
119
+ - Sanitized from `ConfigValidationError` messages and causes on validation failure.
53
120
 
54
- // Register all schemas
55
- ServiceConfig.Register();
121
+ ---
56
122
 
57
- // Parse environment variables (SERVICE_PORT, SERVICE_HOST, SERVICE_DEBUG, etc.)
58
- ServiceConfig.ParseENV();
123
+ ### `ConfigManager`
59
124
 
60
- // Get typed values
61
- const port = ServiceConfig.Get('PORT'); // number, typed
62
- const host = ServiceConfig.Get('HOST'); // string, typed
63
- const debug = ServiceConfig.Get('DEBUG'); // boolean, typed
125
+ A static singleton configuration manager. All state is shared across the process for the lifetime of the module.
64
126
 
65
- console.log(`Server running on ${host}:${port}`);
127
+ ```typescript
128
+ class ConfigManager {
129
+ static Register(key: string, schema: z.ZodTypeAny, defaultValue: unknown): void
130
+ static Get(key: string, source?: TConfigSource): TConfigValueTypes
131
+ static Set<T extends TConfigValueTypes>(key: string, value: T, target?: TConfigSource): void
132
+ static GetSchema(key: string): z.ZodTypeAny
133
+ static async RegisterProvider(provider: IConfigProvider): Promise<void>
134
+ static RegisterSyncProvider(provider: ISyncConfigProvider): void
135
+ static async Save(provider: IConfigProvider, options: SaveOptions): Promise<void>
136
+ static RegisterNamespace(name: string, prefix: string): void
137
+ static SetValidationWarningHandler(handler: ((key: string, providerName: string) => void) | undefined): void
138
+ static Reset(): void
139
+ }
66
140
  ```
67
141
 
68
- ### Retrieving and Validating Values
142
+ | Method | Description |
143
+ |---|---|
144
+ | `Register(key, schema, defaultValue)` | Register a key with its Zod schema and initial default. Throws `ConfigRegistrationError` if re-registered with a different schema. Throws `ConfigValidationError` if `defaultValue` fails. |
145
+ | `Get(key, source?)` | Retrieve the resolved value for `key`. Pass `'DEFAULT'` or `'OVERRIDE'` to read a specific tier only. Throws `ConfigNotRegisteredError` or `ConfigNotSetError` if unavailable. |
146
+ | `Set(key, value, target?)` | Set a value on the `'OVERRIDE'` tier (default) or `'DEFAULT'` tier. Throws `ConfigNotRegisteredError` or `ConfigValidationError`. |
147
+ | `GetSchema(key)` | Return the registered Zod schema for `key`. Throws `ConfigNotRegisteredError`. |
148
+ | `RegisterProvider(provider)` | Async. Register an `IConfigProvider`; calls `provider.Load()` immediately. Provider values are the middle tier. Last-registered wins for duplicate keys. |
149
+ | `RegisterSyncProvider(provider)` | Register an `ISyncConfigProvider`; calls `provider.LoadSync()` immediately. Use only when `await` is not available. |
150
+ | `Save(provider, options)` | Async. Build a `ConfigSaveEntry[]` for every registered key and delegate to `provider.Save()`. |
151
+ | `RegisterNamespace(name, prefix)` | Record a `prefix → sectionName` mapping used by `Save()` to split keys into section/field components. Called automatically by `RegisterConfigSchema`. |
152
+ | `SetValidationWarningHandler(handler)` | Set a callback invoked when a provider value fails schema validation. Silent by default. Pass `undefined` to clear. |
153
+ | `Reset()` | Clear all state. Intended for test isolation only. |
69
154
 
70
- ```typescript
71
- // Get a typed configuration value (guaranteed type-safe)
72
- const timeout = ServiceConfig.Get('API_TIMEOUT'); // number
155
+ **`TConfigSource`** — `'DEFAULT' | 'OVERRIDE'`
156
+
157
+ **`TConfigValueTypes`** the full union accepted by all schemas:
158
+ `string | number | boolean | Date | string[] | number[] | boolean[] | undefined | null`
73
159
 
74
- // Validate a value before setting it
75
- const isValid = ServiceConfig.Validate('PORT', '8080'); // true if valid
160
+ **`TConfig`** `Map<string, TConfigValueTypes>`
76
161
 
77
- // Set configuration values at runtime
78
- ServiceConfig.Set('DEBUG', true, 'OVERRIDE');
162
+ ---
163
+
164
+ ### `ScopedConfigManager`
165
+
166
+ An instance-based configuration manager. Each instance maintains fully independent state, making it suitable for test isolation and multi-tenant scenarios. The public API mirrors `ConfigManager` exactly as instance methods.
167
+
168
+ ```typescript
169
+ const config = new ScopedConfigManager();
170
+ config.Register('PORT', z.coerce.number(), 3000);
171
+ await config.RegisterProvider(myProvider);
172
+ const port = config.Get('PORT'); // 3000
79
173
  ```
80
174
 
81
- ### Advanced: Using ConfigManager Directly
175
+ `ScopedConfigManager` exposes: `Register`, `Get`, `Set`, `GetSchema`, `RegisterProvider`, `RegisterSyncProvider`, `Save`, `RegisterNamespace`, `SetValidationWarningHandler`, `Reset`.
82
176
 
83
- For advanced scenarios where you need direct schema control or custom management, use `ConfigManager`:
177
+ ---
178
+
179
+ ### `ConfigProvider` (abstract base class)
180
+
181
+ Extend this class to build a custom provider. Both `Load` and `Save` are abstract and must be implemented.
84
182
 
85
183
  ```typescript
86
- import { ConfigManager } from '@pawells/config';
87
- import { z } from 'zod/v4';
184
+ abstract class ConfigProvider<
185
+ TOptions extends TConfigProviderOptions = TConfigProviderOptions,
186
+ TLoadOptions = unknown,
187
+ TSaveOptions extends TConfigProviderSaveOptions = TConfigProviderSaveOptions
188
+ > {
189
+ readonly Name: string
190
+ abstract Load(options?: TLoadOptions): Promise<Record<string, unknown>>
191
+ abstract Save(entries: readonly ConfigSaveEntry[], options?: TSaveOptions): Promise<void>
192
+ }
193
+ ```
88
194
 
89
- // Register individual schemas
90
- ConfigManager.Register('DATABASE_URL', z.string().url(), 'postgresql://localhost/mydb');
91
- ConfigManager.Register('API_KEY', z.string().min(32), 'default-key');
195
+ ```typescript
196
+ import { ConfigProvider, type TConfigProviderOptions, type ConfigSaveEntry } from '@pawells/config';
92
197
 
93
- // Set and retrieve values
94
- ConfigManager.Set('DATABASE_URL', process.env.DATABASE_URL);
95
- const dbUrl = ConfigManager.Get('DATABASE_URL');
198
+ class RedisProvider extends ConfigProvider {
199
+ async Load(): Promise<Record<string, unknown>> {
200
+ // Return a flat key-value record; keys must be fully qualified.
201
+ return { APP_HOST: 'redis-host' };
202
+ }
203
+
204
+ async Save(_entries: readonly ConfigSaveEntry[]): Promise<void> {
205
+ // Implement if this provider supports writes.
206
+ }
207
+ }
96
208
  ```
97
209
 
98
- ### Error Handling
210
+ ---
211
+
212
+ ### Provider interfaces
213
+
214
+ **`IConfigProvider`** — async providers (preferred):
99
215
 
100
216
  ```typescript
101
- import {
102
- ConfigurationNotRegisteredError,
103
- ConfigurationNotSetError,
104
- ConfigurationError,
105
- } from '@pawells/config';
217
+ interface IConfigProvider {
218
+ readonly Name: string
219
+ Load(): Promise<Record<string, unknown>>
220
+ Save(entries: readonly ConfigSaveEntry[], options?: SaveOptions): Promise<void>
221
+ }
222
+ ```
106
223
 
107
- try {
108
- // Attempt to retrieve a value
109
- const value = ServiceConfig.Get('UNKNOWN_KEY');
110
- } catch (error) {
111
- if (error instanceof ConfigurationNotRegisteredError) {
112
- console.error('Configuration key not registered:', error.message);
113
- }
114
- if (error instanceof ConfigurationNotSetError) {
115
- console.error('Configuration value not set:', error.message);
116
- }
117
- if (error instanceof ConfigurationError) {
118
- console.error('Validation failed:', error.message);
119
- }
224
+ **`ISyncConfigProvider`** — synchronous read-only providers (escape hatch):
225
+
226
+ ```typescript
227
+ interface ISyncConfigProvider {
228
+ readonly Name: string
229
+ LoadSync(): Record<string, unknown>
120
230
  }
121
231
  ```
122
232
 
123
- ## API Reference
233
+ ---
124
234
 
125
- ### CreateConfigSchema Factory (Recommended)
235
+ ### `SaveOptions`
126
236
 
127
- The `CreateConfigSchema` factory is the primary recommended API for most use cases. It creates a strongly-typed configuration schema object from a Zod schema.
237
+ Passed to `ConfigManager.Save` and `IConfigProvider.Save`:
128
238
 
129
- **Signature:**
130
239
  ```typescript
131
- CreateConfigSchema<TSchema extends z.ZodRawShape>(
132
- schema: z.ZodObject<TSchema>,
133
- prefix: string
134
- ): IConfigSchemaObject<z.infer<typeof schema>>
240
+ interface SaveOptions {
241
+ path: string // Destination file path
242
+ useCurrentValues?: boolean // false (default) = registered defaults; true = live resolved values
243
+ }
135
244
  ```
136
245
 
137
- **Parameters:**
138
- - `schema` — A Zod object schema defining your configuration structure
139
- - `prefix` Environment variable prefix (e.g., `'SERVICE_'` for `SERVICE_PORT`, `SERVICE_HOST`)
140
-
141
- **Returns:** An `IConfigSchemaObject` with strongly-typed methods for configuration management.
142
-
143
- ### IConfigSchemaObject Methods
144
-
145
- These methods are returned by `CreateConfigSchema` and provide a strongly-typed interface to your configuration.
146
-
147
- | Method | Signature | Description |
148
- |--------|-----------|-------------|
149
- | `Register` | `Register(): void` | Register all schema fields with `ConfigManager`, extracting default values from the schema. Call once at application startup. |
150
- | `Get` | `Get<K extends TKeys>(key: K): TConfig[K]` | Retrieve a typed configuration value by key. The return type is automatically inferred from your schema. |
151
- | `Set` | `Set<K extends TKeys>(key: K, value: TConfig[K], source?: TConfigSource): void` | Set a configuration value with optional source (`'DEFAULT'` or `'OVERRIDE'`, defaults to `'OVERRIDE'`). Validates the value against the schema. |
152
- | `Validate` | `Validate<K extends TKeys>(key: K, value: unknown): boolean` | Validate a value against its schema without setting it. Returns `true` if valid, `false` otherwise. |
153
- | `ParseENV` | `ParseENV(): void` | Parse environment variables matching the configured prefix and set them as overrides. Prefixed variables are automatically discovered and parsed according to the schema. |
154
-
155
- ### ConfigManager (Advanced)
156
-
157
- For advanced scenarios requiring direct schema control or custom configuration management, use `ConfigManager` directly:
158
-
159
- | Method | Signature | Description |
160
- |--------|-----------|-------------|
161
- | `Register` | `Register(key: string, schema: ZodType, defaultValue: unknown): void` | Register a configuration key with a Zod schema and default value. Validates the default value against the schema. |
162
- | `Set` | `Set<T>(key: string, value: T, target?: 'DEFAULT' \| 'OVERRIDE'): void` | Set a configuration value and validate against its schema. Default target is 'OVERRIDE'. |
163
- | `Get` | `Get(key: string, source?: 'DEFAULT' \| 'OVERRIDE'): TConfigValueTypes` | Retrieve a configuration value by key, validated against its schema. Returns resolved value merging defaults and overrides. |
164
- | `GetSchema` | `GetSchema(key: string): ZodTypeAny` | Retrieve the Zod schema for a configuration key for custom validation. |
165
- | `Reset` | `Reset(): void` | Clear all registered schemas and values (for testing). Internal API. |
166
-
167
- ### Error Types
168
-
169
- | Error | Description |
170
- |-------|-------------|
171
- | `ConfigurationAlreadyRegisteredError` | Thrown when attempting to register a key that already exists with a different schema. |
172
- | `ConfigurationNotRegisteredError` | Thrown when accessing or setting a key that was not registered with a schema. |
173
- | `ConfigurationError` | Thrown when configuration validation fails during registration, set, or get operations. Includes cause chain from Zod validation errors. |
174
- | `ConfigurationNotSetError` | Thrown when retrieving a configuration value that was never set. |
175
-
176
- ### Type Exports
177
-
178
- | Type | Description |
179
- |------|-------------|
180
- | `TConfigValueTypes` | Union type of all supported configuration value types: `string`, `number`, `boolean`, `Date`, `string[]`, `number[]`, `boolean[]`, `undefined`, or `null`. |
181
- | `TConfig` | Internal map type: `Map<string, TConfigValueTypes>`. |
182
- | `TConfigSource` | Literal union type: `'DEFAULT'` or `'OVERRIDE'`. Controls which source layer a value is stored in or retrieved from. |
183
-
184
- ### Utility Functions
185
-
186
- | Export | Purpose |
187
- |--------|---------|
188
- | `Secret<T>(value: T)` | Mark a configuration value as sensitive (secrets are redacted in GetSchema().Redact()) |
189
-
190
- > **⚠️ Security Warning**
191
- >
192
- > Do not store sensitive values (API keys, database passwords, tokens, PII) directly as string literals in your code. Always load sensitive configuration from environment variables or secure vaults at startup.
193
- >
194
- > When storing environment variable overrides, ensure the process environment itself is protected from inspection (e.g., via debugger or memory dumps).
195
- >
196
- > Example:
197
- > ```typescript
198
- > // ✓ Good: Load from environment at startup
199
- > ConfigManager.Set('DATABASE_PASSWORD', process.env.DB_PASSWORD);
200
- >
201
- > // ✗ Bad: Hardcoded secrets
202
- > ConfigManager.Register('DATABASE_PASSWORD', z.string(), 'hardcoded-password');
203
- > ```
246
+ ---
247
+
248
+ ### `ConfigSaveEntry` / `IConfigSaveEntry`
249
+
250
+ One entry per registered key, built by `ConfigManager.Save` and passed to `provider.Save`:
251
+
252
+ | Field | Type | Description |
253
+ |---|---|---|
254
+ | `key` | `string` | Fully-qualified key (e.g. `DATABASE_HOST`) |
255
+ | `section` | `string` | Namespace section (e.g. `DATABASE`); empty string if no namespace |
256
+ | `field` | `string` | Field within the section (e.g. `HOST`); equals `key` when section is empty |
257
+ | `value` | `unknown` | Default or live value, depending on `useCurrentValues` |
258
+ | `isSecret` | `boolean` | `true` if the field was wrapped with `Secret()` |
259
+ | `description` | `string \| undefined` | From Zod `.describe()` annotation |
260
+
261
+ ---
262
+
263
+ ### Error classes
264
+
265
+ All errors extend `ConfigError`, which in turn extends `BaseError` from `@pawells/typescript-common`.
266
+
267
+ | Class | Error code | Thrown when |
268
+ |---|---|---|
269
+ | `ConfigError` | `CONFIG_ERROR` | General configuration error; base class for all others |
270
+ | `ConfigRegistrationError` | `CONFIG_REGISTRATION_ERROR` | A key is registered a second time with a different schema |
271
+ | `ConfigNotSetError` | `CONFIG_NOT_SET_ERROR` | `Get()` is called for a key with no value in the requested tier |
272
+ | `ConfigNotRegisteredError` | `CONFIG_NOT_REGISTERED` | `Get()`, `Set()`, or `GetSchema()` is called for an unregistered key |
273
+ | `ConfigValidationError` | `CONFIG_VALIDATION_ERROR` | A value fails its Zod schema at `Register()`, `Set()`, or `Get()` time |
274
+
275
+ ```typescript
276
+ import { ConfigError, ConfigNotRegisteredError, ConfigNotSetError, ConfigValidationError } from '@pawells/config';
277
+
278
+ try {
279
+ const value = ConfigManager.Get('MISSING_KEY');
280
+ } catch (error) {
281
+ if (error instanceof ConfigNotRegisteredError) {
282
+ // Key was never registered
283
+ } else if (error instanceof ConfigNotSetError) {
284
+ // Key is registered but has no value in the requested tier
285
+ } else if (error instanceof ConfigValidationError) {
286
+ // Value failed schema validation; check error.cause for Zod details
287
+ } else if (error instanceof ConfigError) {
288
+ // Any other configuration error
289
+ }
290
+ }
291
+ ```
292
+
293
+ ---
294
+
295
+ ### Schema constants and utilities
296
+
297
+ | Export | Description |
298
+ |---|---|
299
+ | `CONFIG_VALUES_TYPES_SCHEMA` | Zod union schema accepting all supported config value types |
300
+ | `AssertConfigValueType(value)` | Asserts that `value` is a `TConfigValueTypes`; throws `ZodError` otherwise |
301
+ | `CONFIG_PROVIDER_OPTIONS_SCHEMA` | Zod schema for `{ name: string }` base provider options |
302
+ | `TConfigProviderOptions` | Inferred type of `CONFIG_PROVIDER_OPTIONS_SCHEMA` |
303
+ | `AssertConfigProviderOptions(options)` | Asserts conformance; throws `ZodError` otherwise |
304
+ | `ValidateConfigProviderOptions(options)` | Returns `true` if options conform; `false` otherwise |
305
+ | `CONFIG_PROVIDER_SAVE_OPTIONS_SCHEMA` | Zod schema for `{ useCurrentValues?: boolean }` |
306
+ | `TConfigProviderSaveOptions` | Inferred type of `CONFIG_PROVIDER_SAVE_OPTIONS_SCHEMA` |
204
307
 
205
308
  ## License
206
309
 
package/dist/errors.d.ts CHANGED
@@ -1,16 +1,16 @@
1
1
  /**
2
2
  * Custom error classes for configuration management.
3
3
  *
4
- * Exports: {@link ConfigurationAlreadyRegisteredError}, {@link ConfigurationNotRegisteredError},
5
- * {@link ConfigurationError}, and {@link ConfigurationNotSetError}.
4
+ * Exports: {@link ConfigRegistrationError}, {@link ConfigNotRegisteredError},
5
+ * {@link ConfigError}, and {@link ConfigNotSetError}.
6
6
  */
7
+ import { BaseError, type TErrorMetadata } from '@pawells/typescript-common';
7
8
  /**
8
9
  * Abstract base class for configuration errors.
9
10
  * Automatically sets the error name to the class constructor name.
10
11
  */
11
- declare abstract class ConfigurationBaseError extends Error {
12
- abstract readonly Code: string;
13
- constructor(message: string);
12
+ export declare class ConfigError extends BaseError {
13
+ constructor(message: string, metadata?: TErrorMetadata);
14
14
  }
15
15
  /**
16
16
  * Error thrown when attempting to register a configuration key that already exists.
@@ -18,51 +18,43 @@ declare abstract class ConfigurationBaseError extends Error {
18
18
  * @param key - The configuration key that was already registered
19
19
  *
20
20
  * @example
21
- * throw new ConfigurationAlreadyRegisteredError('DATABASE_URL');
21
+ * throw new ConfigRegistrationError('DATABASE_URL');
22
22
  */
23
- export declare class ConfigurationAlreadyRegisteredError extends ConfigurationBaseError {
24
- readonly Code = "CONFIGURATION_ALREADY_REGISTERED";
25
- constructor(key: string);
23
+ export declare class ConfigRegistrationError extends ConfigError {
24
+ constructor(key: string, metadata?: TErrorMetadata);
26
25
  }
27
26
  /**
28
- * Error thrown when attempting to access a configuration key that was not registered.
27
+ * Error thrown when a required configuration value is not set.
29
28
  *
30
- * @param key - The configuration key that is not registered
29
+ * @param key - The configuration key that is not set
31
30
  *
32
31
  * @example
33
- * throw new ConfigurationNotRegisteredError('UNKNOWN_KEY');
32
+ * throw new ConfigNotSetError('DATABASE_URL');
34
33
  */
35
- export declare class ConfigurationNotRegisteredError extends ConfigurationBaseError {
36
- readonly Code = "CONFIGURATION_NOT_REGISTERED";
37
- constructor(key: string);
34
+ export declare class ConfigNotSetError extends ConfigError {
35
+ constructor(key: string, metadata?: TErrorMetadata);
38
36
  }
39
37
  /**
40
- * Error thrown when configuration validation fails.
38
+ * Error thrown when a configuration key is not registered.
41
39
  *
42
- * @param key - The configuration key that failed validation
43
- * @param message - Detailed error message about the validation failure
44
- * @param options - Optional options object with cause
40
+ * @param key - The configuration key that is not registered
45
41
  *
46
42
  * @example
47
- * throw new ConfigurationError('PORT', 'Must be a positive number');
43
+ * throw new ConfigNotRegisteredError('DATABASE_URL');
48
44
  */
49
- export declare class ConfigurationError extends ConfigurationBaseError {
50
- readonly Code = "CONFIGURATION_ERROR";
51
- constructor(key: string, message: string, options?: {
52
- cause?: Error;
53
- });
45
+ export declare class ConfigNotRegisteredError extends ConfigError {
46
+ constructor(key: string, metadata?: TErrorMetadata);
54
47
  }
55
48
  /**
56
- * Error thrown when a required configuration value is not set.
49
+ * Error thrown when a configuration value fails schema validation.
57
50
  *
58
- * @param key - The configuration key that is not set
51
+ * @param key - The configuration key that failed validation
52
+ * @param validationMessage - The validation error message describing why the value is invalid
59
53
  *
60
54
  * @example
61
- * throw new ConfigurationNotSetError('DATABASE_URL');
55
+ * throw new ConfigValidationError('PORT', 'Expected a number between 1 and 65535');
62
56
  */
63
- export declare class ConfigurationNotSetError extends ConfigurationBaseError {
64
- readonly Code = "CONFIGURATION_NOT_SET";
65
- constructor(key: string);
57
+ export declare class ConfigValidationError extends ConfigError {
58
+ constructor(key: string, validationMessage: string, metadata?: TErrorMetadata);
66
59
  }
67
- export {};
68
60
  //# sourceMappingURL=errors.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH;;;GAGG;AACH,uBAAe,sBAAuB,SAAQ,KAAK;IAClD,kBAAyB,IAAI,EAAE,MAAM,CAAC;gBAE1B,OAAO,EAAE,MAAM;CAI3B;AAED;;;;;;;GAOG;AACH,qBAAa,mCAAoC,SAAQ,sBAAsB;IAC9E,SAAgB,IAAI,sCAAsC;gBAE9C,GAAG,EAAE,MAAM;CAGvB;AAED;;;;;;;GAOG;AACH,qBAAa,+BAAgC,SAAQ,sBAAsB;IAC1E,SAAgB,IAAI,kCAAkC;gBAE1C,GAAG,EAAE,MAAM;CAGvB;AAED;;;;;;;;;GASG;AACH,qBAAa,kBAAmB,SAAQ,sBAAsB;IAC7D,SAAgB,IAAI,yBAAyB;gBAEjC,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE;QAAE,KAAK,CAAC,EAAE,KAAK,CAAA;KAAE;CAMrE;AAED;;;;;;;GAOG;AACH,qBAAa,wBAAyB,SAAQ,sBAAsB;IACnE,SAAgB,IAAI,2BAA2B;gBAEnC,GAAG,EAAE,MAAM;CAGvB"}
1
+ {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,SAAS,EAAE,KAAK,cAAc,EAAE,MAAM,4BAA4B,CAAC;AAE5E;;;GAGG;AACH,qBAAa,WAAY,SAAQ,SAAS;gBAC7B,OAAO,EAAE,MAAM,EAAE,QAAQ,GAAE,cAAmB;CAI1D;AAED;;;;;;;GAOG;AACH,qBAAa,uBAAwB,SAAQ,WAAW;gBAC3C,GAAG,EAAE,MAAM,EAAE,QAAQ,GAAE,cAAmB;CAItD;AAED;;;;;;;GAOG;AACH,qBAAa,iBAAkB,SAAQ,WAAW;gBACrC,GAAG,EAAE,MAAM,EAAE,QAAQ,GAAE,cAAmB;CAItD;AAED;;;;;;;GAOG;AACH,qBAAa,wBAAyB,SAAQ,WAAW;gBAC5C,GAAG,EAAE,MAAM,EAAE,QAAQ,GAAE,cAAmB;CAItD;AAED;;;;;;;;GAQG;AACH,qBAAa,qBAAsB,SAAQ,WAAW;gBACzC,GAAG,EAAE,MAAM,EAAE,iBAAiB,EAAE,MAAM,EAAE,QAAQ,GAAE,cAAmB;CAIjF"}