@pawells/config 2.3.0 → 3.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.
package/README.md CHANGED
@@ -1,199 +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)
28
+ - Node.js `>=22.0.0`
29
+ - `zod` `^4.4.3` (peer dependency, installed automatically)
16
30
 
17
31
  ## Installation
18
32
 
19
- Install from npm:
33
+ ```sh
34
+ yarn add @pawells/config
35
+ ```
36
+
37
+ ## Quick Start
20
38
 
21
- ```bash
22
- 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: '***' }
23
63
  ```
24
64
 
25
- Or with yarn:
65
+ ## API Reference
26
66
 
27
- ```bash
28
- 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>>
29
76
  ```
30
77
 
31
- ## 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.
32
80
 
33
- ### Using CreateConfigSchema (Recommended)
81
+ **Returns** an `IConfigSchemaObject` with the following members:
34
82
 
35
- 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.
36
100
 
37
101
  ```typescript
38
- import { z } from 'zod/v4';
39
- import { CreateConfigSchema } from '@pawells/config';
40
-
41
- // Define your configuration schema
42
- const SERVICE_CONFIG_SCHEMA = z.object({
43
- PORT: z.coerce.number().int().positive().default(3000),
44
- HOST: z.string().default('localhost'),
45
- DEBUG: z.boolean().default(false),
46
- DATABASE_URL: z.string().url(),
47
- 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(''),
48
112
  });
113
+ ```
49
114
 
50
- // Create a typed config object with 'SERVICE_' prefix
51
- 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.
52
120
 
53
- // Register all schemas
54
- ServiceConfig.Register();
121
+ ---
55
122
 
56
- // Parse environment variables (SERVICE_PORT, SERVICE_HOST, SERVICE_DEBUG, etc.)
57
- ServiceConfig.ParseENV();
123
+ ### `ConfigManager`
58
124
 
59
- // Get typed values
60
- const port = ServiceConfig.Get('PORT'); // number, typed
61
- const host = ServiceConfig.Get('HOST'); // string, typed
62
- 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.
63
126
 
64
- 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
+ }
65
140
  ```
66
141
 
67
- ### 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. |
68
154
 
69
- ```typescript
70
- // Get a typed configuration value (guaranteed type-safe)
71
- 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`
72
159
 
73
- // Validate a value before setting it
74
- const isValid = ServiceConfig.Validate('PORT', '8080'); // true if valid
160
+ **`TConfig`** `Map<string, TConfigValueTypes>`
75
161
 
76
- // Set configuration values at runtime
77
- 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
78
173
  ```
79
174
 
80
- ### Advanced: Using ConfigManager Directly
175
+ `ScopedConfigManager` exposes: `Register`, `Get`, `Set`, `GetSchema`, `RegisterProvider`, `RegisterSyncProvider`, `Save`, `RegisterNamespace`, `SetValidationWarningHandler`, `Reset`.
81
176
 
82
- 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.
83
182
 
84
183
  ```typescript
85
- import { ConfigManager } from '@pawells/config';
86
- 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
+ ```
87
194
 
88
- // Register individual schemas
89
- ConfigManager.Register('DATABASE_URL', z.string().url(), 'postgresql://localhost/mydb');
90
- ConfigManager.Register('API_KEY', z.string().min(32), 'default-key');
195
+ ```typescript
196
+ import { ConfigProvider, type TConfigProviderOptions, type ConfigSaveEntry } from '@pawells/config';
91
197
 
92
- // Set and retrieve values
93
- ConfigManager.Set('DATABASE_URL', process.env.DATABASE_URL);
94
- 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
+ }
95
208
  ```
96
209
 
97
- ### Error Handling
210
+ ---
211
+
212
+ ### Provider interfaces
213
+
214
+ **`IConfigProvider`** — async providers (preferred):
98
215
 
99
216
  ```typescript
100
- import {
101
- ConfigurationNotRegisteredError,
102
- ConfigurationNotSetError,
103
- ConfigurationError,
104
- } 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
+ ```
105
223
 
106
- try {
107
- // Attempt to retrieve a value
108
- const value = ServiceConfig.Get('UNKNOWN_KEY');
109
- } catch (error) {
110
- if (error instanceof ConfigurationNotRegisteredError) {
111
- console.error('Configuration key not registered:', error.message);
112
- }
113
- if (error instanceof ConfigurationNotSetError) {
114
- console.error('Configuration value not set:', error.message);
115
- }
116
- if (error instanceof ConfigurationError) {
117
- console.error('Validation failed:', error.message);
118
- }
224
+ **`ISyncConfigProvider`** — synchronous read-only providers (escape hatch):
225
+
226
+ ```typescript
227
+ interface ISyncConfigProvider {
228
+ readonly Name: string
229
+ LoadSync(): Record<string, unknown>
119
230
  }
120
231
  ```
121
232
 
122
- ## API Reference
233
+ ---
123
234
 
124
- ### CreateConfigSchema Factory (Recommended)
235
+ ### `SaveOptions`
125
236
 
126
- 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`:
127
238
 
128
- **Signature:**
129
239
  ```typescript
130
- CreateConfigSchema<TSchema extends z.ZodRawShape>(
131
- schema: z.ZodObject<TSchema>,
132
- prefix: string
133
- ): 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
+ }
134
244
  ```
135
245
 
136
- **Parameters:**
137
- - `schema` — A Zod object schema defining your configuration structure
138
- - `prefix` Environment variable prefix (e.g., `'SERVICE_'` for `SERVICE_PORT`, `SERVICE_HOST`)
139
-
140
- **Returns:** An `IConfigSchemaObject` with strongly-typed methods for configuration management.
141
-
142
- ### IConfigSchemaObject Methods
143
-
144
- These methods are returned by `CreateConfigSchema` and provide a strongly-typed interface to your configuration.
145
-
146
- | Method | Signature | Description |
147
- |--------|-----------|-------------|
148
- | `Register` | `Register(): void` | Register all schema fields with `ConfigManager`, extracting default values from the schema. Call once at application startup. |
149
- | `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. |
150
- | `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. |
151
- | `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. |
152
- | `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. |
153
-
154
- ### ConfigManager (Advanced)
155
-
156
- For advanced scenarios requiring direct schema control or custom configuration management, use `ConfigManager` directly:
157
-
158
- | Method | Signature | Description |
159
- |--------|-----------|-------------|
160
- | `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. |
161
- | `Set` | `Set<T>(key: string, value: T, target?: 'DEFAULT' \| 'OVERRIDE'): void` | Set a configuration value and validate against its schema. Default target is 'OVERRIDE'. |
162
- | `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. |
163
- | `GetSchema` | `GetSchema(key: string): ZodTypeAny` | Retrieve the Zod schema for a configuration key for custom validation. |
164
- | `Reset` | `Reset(): void` | Clear all registered schemas and values (for testing). Internal API. |
165
-
166
- ### Error Types
167
-
168
- | Error | Description |
169
- |-------|-------------|
170
- | `ConfigurationAlreadyRegisteredError` | Thrown when attempting to register a key that already exists with a different schema. |
171
- | `ConfigurationNotRegisteredError` | Thrown when accessing or setting a key that was not registered with a schema. |
172
- | `ConfigurationError` | Thrown when configuration validation fails during registration, set, or get operations. Includes cause chain from Zod validation errors. |
173
- | `ConfigurationNotSetError` | Thrown when retrieving a configuration value that was never set. |
174
-
175
- ### Type Exports
176
-
177
- | Type | Description |
178
- |------|-------------|
179
- | `TConfigValueTypes` | Union type of all supported configuration value types: `string`, `number`, `boolean`, `Date`, `string[]`, `number[]`, `boolean[]`, `undefined`, or `null`. |
180
- | `TConfig` | Internal map type: `Map<string, TConfigValueTypes>`. |
181
- | `TConfigSource` | Literal union type: `'DEFAULT'` or `'OVERRIDE'`. Controls which source layer a value is stored in or retrieved from. |
182
-
183
- > **⚠️ Security Warning**
184
- >
185
- > 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.
186
- >
187
- > When storing environment variable overrides, ensure the process environment itself is protected from inspection (e.g., via debugger or memory dumps).
188
- >
189
- > Example:
190
- > ```typescript
191
- > // Good: Load from environment at startup
192
- > ConfigManager.Set('DATABASE_PASSWORD', process.env.DB_PASSWORD);
193
- >
194
- > // Bad: Hardcoded secrets
195
- > ConfigManager.Register('DATABASE_PASSWORD', z.string(), 'hardcoded-password');
196
- > ```
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` |
197
307
 
198
308
  ## License
199
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"}