@geekmidas/envkit 0.0.8 → 0.1.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.
Files changed (57) hide show
  1. package/README.md +228 -174
  2. package/dist/EnvironmentBuilder-DHfDXJUm.d.mts +131 -0
  3. package/dist/EnvironmentBuilder-DfmYRBm-.mjs +83 -0
  4. package/dist/EnvironmentBuilder-DfmYRBm-.mjs.map +1 -0
  5. package/dist/EnvironmentBuilder-W2wku49g.cjs +95 -0
  6. package/dist/EnvironmentBuilder-W2wku49g.cjs.map +1 -0
  7. package/dist/EnvironmentBuilder-Xuf2Dd9u.d.cts +131 -0
  8. package/dist/EnvironmentBuilder.cjs +4 -0
  9. package/dist/EnvironmentBuilder.d.cts +2 -0
  10. package/dist/EnvironmentBuilder.d.mts +2 -0
  11. package/dist/EnvironmentBuilder.mjs +3 -0
  12. package/dist/{EnvironmentParser-cnxuy7lw.cjs → EnvironmentParser-Bt246UeP.cjs} +1 -1
  13. package/dist/{EnvironmentParser-cnxuy7lw.cjs.map → EnvironmentParser-Bt246UeP.cjs.map} +1 -1
  14. package/dist/{EnvironmentParser-B8--woiB.d.cts → EnvironmentParser-CVWU1ooT.d.mts} +1 -1
  15. package/dist/{EnvironmentParser-STvN_RCc.mjs → EnvironmentParser-c06agx31.mjs} +1 -1
  16. package/dist/{EnvironmentParser-STvN_RCc.mjs.map → EnvironmentParser-c06agx31.mjs.map} +1 -1
  17. package/dist/{EnvironmentParser-C_9v2BDw.d.mts → EnvironmentParser-tV-JjCg7.d.cts} +1 -1
  18. package/dist/EnvironmentParser.cjs +1 -1
  19. package/dist/EnvironmentParser.d.cts +1 -1
  20. package/dist/EnvironmentParser.d.mts +1 -1
  21. package/dist/EnvironmentParser.mjs +1 -1
  22. package/dist/SnifferEnvironmentParser.cjs +1 -1
  23. package/dist/SnifferEnvironmentParser.cjs.map +1 -1
  24. package/dist/SnifferEnvironmentParser.d.cts +1 -1
  25. package/dist/SnifferEnvironmentParser.d.mts +1 -1
  26. package/dist/SnifferEnvironmentParser.mjs +1 -1
  27. package/dist/SnifferEnvironmentParser.mjs.map +1 -1
  28. package/dist/SstEnvironmentBuilder-BuFw1hCe.cjs +125 -0
  29. package/dist/SstEnvironmentBuilder-BuFw1hCe.cjs.map +1 -0
  30. package/dist/SstEnvironmentBuilder-CjURMGjW.d.mts +177 -0
  31. package/dist/SstEnvironmentBuilder-D4oSo_KX.d.cts +177 -0
  32. package/dist/SstEnvironmentBuilder-DEa3lTUB.mjs +108 -0
  33. package/dist/SstEnvironmentBuilder-DEa3lTUB.mjs.map +1 -0
  34. package/dist/SstEnvironmentBuilder.cjs +7 -0
  35. package/dist/SstEnvironmentBuilder.d.cts +3 -0
  36. package/dist/SstEnvironmentBuilder.d.mts +3 -0
  37. package/dist/SstEnvironmentBuilder.mjs +4 -0
  38. package/dist/index.cjs +5 -2
  39. package/dist/index.d.cts +3 -2
  40. package/dist/index.d.mts +3 -2
  41. package/dist/index.mjs +3 -2
  42. package/dist/sst.cjs +13 -114
  43. package/dist/sst.cjs.map +1 -1
  44. package/dist/sst.d.cts +14 -93
  45. package/dist/sst.d.mts +14 -93
  46. package/dist/sst.mjs +10 -112
  47. package/dist/sst.mjs.map +1 -1
  48. package/docs/async-secrets-design.md +355 -0
  49. package/package.json +6 -2
  50. package/src/EnvironmentBuilder.ts +196 -0
  51. package/src/SnifferEnvironmentParser.ts +7 -5
  52. package/src/SstEnvironmentBuilder.ts +298 -0
  53. package/src/__tests__/EnvironmentBuilder.spec.ts +274 -0
  54. package/src/__tests__/SstEnvironmentBuilder.spec.ts +373 -0
  55. package/src/__tests__/sst.spec.ts +1 -1
  56. package/src/index.ts +12 -0
  57. package/src/sst.ts +45 -207
package/README.md CHANGED
@@ -1,84 +1,51 @@
1
1
  # @geekmidas/envkit
2
2
 
3
- A type-safe environment configuration parser that uses Zod schemas to validate and parse environment variables or configuration objects.
3
+ Type-safe environment configuration utilities for parsing environment variables and building environment records from typed resources.
4
4
 
5
5
  ## Features
6
6
 
7
- - 🔒 **Type-safe**: Full TypeScript support with automatic type inference
8
- - **Schema validation**: Uses Zod schemas for robust validation
9
- - 🏗️ **Nested configuration**: Support for complex, nested configuration structures
10
- - 🚨 **Error aggregation**: Collects and reports all validation errors at once
11
- - 🔍 **Path-based access**: Uses lodash's `get` and `set` for deep object access
12
- - 🎯 **Zero dependencies**: Only depends on Zod and minimal lodash utilities
7
+ - **EnvironmentParser**: Parse and validate environment variables with Zod schemas
8
+ - **EnvironmentBuilder**: Build environment records from type-discriminated objects
9
+ - **SstEnvironmentBuilder**: SST-specific builder with built-in resolvers for AWS resources
10
+ - Full TypeScript support with automatic type inference
11
+ - Nested configuration support
12
+ - Error aggregation and reporting
13
13
 
14
14
  ## Installation
15
15
 
16
16
  ```bash
17
- npm install @geekmidas/envkit zod
18
- # or
19
- yarn add @geekmidas/envkit zod
20
- # or
21
17
  pnpm add @geekmidas/envkit zod
22
18
  ```
23
19
 
24
- ## Quick Start
20
+ ## EnvironmentParser
21
+
22
+ Parse and validate environment variables using Zod schemas.
23
+
24
+ ### Basic Usage
25
25
 
26
26
  ```typescript
27
27
  import { EnvironmentParser } from '@geekmidas/envkit';
28
- import { z } from 'zod';
29
28
 
30
- // Create parser with your config object (e.g., process.env)
31
29
  const parser = new EnvironmentParser(process.env);
32
30
 
33
- // Define your configuration schema
34
31
  const config = parser.create((get) => ({
35
32
  port: get('PORT').string().transform(Number).default(3000),
36
33
  database: {
37
34
  host: get('DATABASE_HOST').string(),
38
35
  port: get('DATABASE_PORT').string().transform(Number),
39
36
  name: get('DATABASE_NAME').string(),
40
- user: get('DATABASE_USER').string(),
41
- password: get('DATABASE_PASSWORD').string()
42
37
  },
43
38
  api: {
44
39
  key: get('API_KEY').string().min(32),
45
40
  url: get('API_URL').url(),
46
- timeout: get('API_TIMEOUT').string().transform(Number).default(5000)
47
41
  }
48
42
  }));
49
43
 
50
- // Parse and validate
51
- try {
52
- const parsedConfig = config.parse();
53
- console.log('Configuration loaded successfully:', parsedConfig);
54
- } catch (error) {
55
- console.error('Configuration validation failed:', error);
56
- process.exit(1);
57
- }
58
- ```
59
-
60
- ## Usage
61
-
62
- ### Basic Configuration
63
-
64
- The simplest use case is parsing flat environment variables:
65
-
66
- ```typescript
67
- const parser = new EnvironmentParser(process.env);
68
-
69
- const config = parser.create((get) => ({
70
- appName: get('APP_NAME').string(),
71
- port: get('PORT').string().transform(Number),
72
- isProduction: get('NODE_ENV').string().transform(env => env === 'production')
73
- }));
74
-
75
- const { appName, port, isProduction } = config.parse();
44
+ const parsedConfig = config.parse();
76
45
  ```
77
46
 
78
47
  ### Nested Configuration
79
48
 
80
- Create deeply nested configuration structures:
81
-
82
49
  ```typescript
83
50
  const config = parser.create((get) => ({
84
51
  server: {
@@ -87,79 +54,25 @@ const config = parser.create((get) => ({
87
54
  ssl: {
88
55
  enabled: get('SSL_ENABLED').string().transform(v => v === 'true'),
89
56
  certPath: get('SSL_CERT_PATH').string().optional(),
90
- keyPath: get('SSL_KEY_PATH').string().optional()
91
- }
92
- },
93
- features: {
94
- authentication: get('FEATURE_AUTH').string().transform(v => v === 'true'),
95
- rateLimit: get('FEATURE_RATE_LIMIT').string().transform(v => v === 'true'),
96
- cache: {
97
- enabled: get('CACHE_ENABLED').string().transform(v => v === 'true'),
98
- ttl: get('CACHE_TTL').string().transform(Number).default(3600)
99
57
  }
100
58
  }
101
59
  }));
102
60
  ```
103
61
 
104
- ### Using Different Config Sources
105
-
106
- While `process.env` is the most common source, you can use any object:
107
-
108
- ```typescript
109
- // From a JSON file
110
- import configJson from './config.json';
111
- const parser = new EnvironmentParser(configJson);
112
-
113
- // From a custom object
114
- const customConfig = {
115
- API_URL: 'https://api.example.com',
116
- API_KEY: 'secret-key-123'
117
- };
118
- const parser = new EnvironmentParser(customConfig);
119
-
120
- // Combining multiple sources
121
- const mergedConfig = {
122
- ...defaultConfig,
123
- ...process.env
124
- };
125
- const parser = new EnvironmentParser(mergedConfig);
126
- ```
127
-
128
- ### Advanced Validation
129
-
130
- Leverage Zod's full validation capabilities:
131
-
132
- ```typescript
133
- const config = parser.create((get) => ({
134
- email: get('ADMIN_EMAIL').string().email(),
135
- webhook: get('WEBHOOK_URL').url(),
136
- retries: get('MAX_RETRIES').string().transform(Number).int().min(0).max(10),
137
- allowedOrigins: get('ALLOWED_ORIGINS')
138
- .string()
139
- .transform(origins => origins.split(','))
140
- .refine(origins => origins.every(o => o.startsWith('http')), {
141
- message: 'All origins must be valid URLs'
142
- }),
143
- logLevel: get('LOG_LEVEL')
144
- .enum(['debug', 'info', 'warn', 'error'])
145
- .default('info')
146
- }));
147
- ```
148
-
149
62
  ### Error Handling
150
63
 
151
- The parser aggregates all validation errors and throws a single `ZodError`:
64
+ The parser aggregates all validation errors:
152
65
 
153
66
  ```typescript
67
+ import { z } from 'zod';
68
+
154
69
  try {
155
70
  const config = parser.create((get) => ({
156
71
  required1: get('MISSING_VAR_1').string(),
157
72
  required2: get('MISSING_VAR_2').string(),
158
- invalid: get('INVALID_NUMBER').string().transform(Number)
159
73
  })).parse();
160
74
  } catch (error) {
161
75
  if (error instanceof z.ZodError) {
162
- console.error('Configuration errors:');
163
76
  error.errors.forEach(err => {
164
77
  console.error(`- ${err.path.join('.')}: ${err.message}`);
165
78
  });
@@ -167,129 +80,270 @@ try {
167
80
  }
168
81
  ```
169
82
 
170
- ### Type Safety
83
+ ## EnvironmentBuilder
171
84
 
172
- The parsed configuration is fully typed:
85
+ A generic builder for creating environment variables from objects with type-discriminated values.
86
+
87
+ ### Basic Usage
173
88
 
174
89
  ```typescript
175
- const config = parser.create((get) => ({
176
- port: get('PORT').string().transform(Number),
177
- features: {
178
- auth: get('FEATURE_AUTH').string().transform(v => v === 'true')
90
+ import { EnvironmentBuilder } from '@geekmidas/envkit';
91
+
92
+ const env = new EnvironmentBuilder(
93
+ {
94
+ apiKey: { type: 'secret', value: 'xyz' },
95
+ appName: 'my-app',
96
+ },
97
+ {
98
+ // Resolver receives value without 'type' key
99
+ secret: (key, value) => ({ [key]: value.value }),
179
100
  }
180
- }));
101
+ ).build();
181
102
 
182
- const parsed = config.parse();
183
- // TypeScript knows: parsed.port is number, parsed.features.auth is boolean
103
+ // Result: { API_KEY: 'xyz', APP_NAME: 'my-app' }
184
104
  ```
185
105
 
186
- ## API Reference
106
+ ### How It Works
187
107
 
188
- ### `EnvironmentParser`
108
+ 1. **Plain string values** are passed through with key transformation to `UPPER_SNAKE_CASE`
109
+ 2. **Object values** with a `type` property are matched against resolvers
110
+ 3. **Resolvers** receive values without the `type` key
111
+ 4. **Root-level keys** from resolver output are transformed to `UPPER_SNAKE_CASE`
189
112
 
190
- The main class for creating configuration parsers.
191
-
192
- #### Constructor
113
+ ### Multiple Resolvers
193
114
 
194
115
  ```typescript
195
- new EnvironmentParser(config: Record<string, unknown>)
116
+ const env = new EnvironmentBuilder(
117
+ {
118
+ secret: { type: 'secret', value: 'my-secret' },
119
+ database: { type: 'postgres', host: 'localhost', port: 5432 },
120
+ bucket: { type: 'bucket', name: 'my-bucket' },
121
+ },
122
+ {
123
+ secret: (key, value) => ({ [key]: value.value }),
124
+ postgres: (key, value) => ({
125
+ [`${key}Host`]: value.host,
126
+ [`${key}Port`]: value.port,
127
+ }),
128
+ bucket: (key, value) => ({ [`${key}Name`]: value.name }),
129
+ }
130
+ ).build();
131
+
132
+ // Result:
133
+ // {
134
+ // SECRET: 'my-secret',
135
+ // DATABASE_HOST: 'localhost',
136
+ // DATABASE_PORT: 5432,
137
+ // BUCKET_NAME: 'my-bucket',
138
+ // }
196
139
  ```
197
140
 
198
- - `config`: The configuration object to parse (typically `process.env`)
141
+ ### Typed Resolvers
142
+
143
+ Resolver keys and values are type-checked based on the input record:
199
144
 
200
- #### Methods
145
+ ```typescript
146
+ const env = new EnvironmentBuilder(
147
+ {
148
+ auth: { type: 'auth0' as const, domain: 'example.auth0.com', clientId: 'abc' },
149
+ },
150
+ {
151
+ // TypeScript enforces 'auth0' resolver exists and value has correct shape
152
+ auth0: (key, value) => ({
153
+ [`${key}Domain`]: value.domain,
154
+ [`${key}ClientId`]: value.clientId,
155
+ }),
156
+ }
157
+ ).build();
158
+
159
+ // Result: { AUTH_DOMAIN: 'example.auth0.com', AUTH_CLIENT_ID: 'abc' }
160
+ ```
201
161
 
202
- ##### `create<T>(schemaBuilder: (get: GetFunction) => T): ConfigParser<T>`
162
+ ### Handling Unmatched Values
203
163
 
204
- Creates a configuration parser with the specified schema.
164
+ ```typescript
165
+ const env = new EnvironmentBuilder(
166
+ { unknown: { type: 'unknown-type', data: 'test' } },
167
+ {},
168
+ {
169
+ onUnmatchedValue: (key, value) => {
170
+ console.warn(`No resolver for "${key}":`, value);
171
+ },
172
+ }
173
+ ).build();
174
+ ```
205
175
 
206
- - `schemaBuilder`: A function that receives a `get` function and returns the schema definition
207
- - Returns: A `ConfigParser` instance
176
+ ## SstEnvironmentBuilder
208
177
 
209
- ### `ConfigParser`
178
+ SST-specific builder with built-in resolvers for AWS resources.
210
179
 
211
- The configuration parser returned by `EnvironmentParser.create()`.
180
+ ### Basic Usage
212
181
 
213
- #### Methods
182
+ ```typescript
183
+ import { SstEnvironmentBuilder, ResourceType } from '@geekmidas/envkit/sst';
214
184
 
215
- ##### `parse(): T`
185
+ const env = new SstEnvironmentBuilder({
186
+ database: {
187
+ type: ResourceType.Postgres,
188
+ host: 'db.example.com',
189
+ port: 5432,
190
+ database: 'myapp',
191
+ username: 'admin',
192
+ password: 'secret',
193
+ },
194
+ apiKey: {
195
+ type: ResourceType.Secret,
196
+ value: 'super-secret',
197
+ },
198
+ appName: 'my-app',
199
+ }).build();
200
+
201
+ // Result:
202
+ // {
203
+ // DATABASE_HOST: 'db.example.com',
204
+ // DATABASE_PORT: 5432,
205
+ // DATABASE_NAME: 'myapp',
206
+ // DATABASE_USERNAME: 'admin',
207
+ // DATABASE_PASSWORD: 'secret',
208
+ // API_KEY: 'super-secret',
209
+ // APP_NAME: 'my-app',
210
+ // }
211
+ ```
216
212
 
217
- Parses and validates the configuration.
213
+ ### Supported Resource Types
218
214
 
219
- - Returns: The parsed configuration object
220
- - Throws: `ZodError` if validation fails
215
+ | Resource Type | Properties | Output |
216
+ |--------------|------------|--------|
217
+ | `Secret` / `SSTSecret` | `value` | `{key}: value` |
218
+ | `Postgres` / `SSTPostgres` | `host`, `port`, `database`, `username`, `password` | `{key}Host`, `{key}Port`, `{key}Name`, `{key}Username`, `{key}Password` |
219
+ | `Bucket` / `SSTBucket` | `name` | `{key}Name` |
220
+ | `SnsTopic` | `arn` | `{key}Arn` |
221
+ | `ApiGatewayV2` | - | No output (noop) |
222
+ | `Function` | - | No output (noop) |
223
+ | `Vpc` | - | No output (noop) |
221
224
 
222
- ### `GetFunction`
225
+ ### Custom Resolvers
223
226
 
224
- The function passed to the schema builder for accessing configuration values.
227
+ Add custom resolvers alongside built-in SST resolvers:
225
228
 
226
229
  ```typescript
227
- type GetFunction = (path: string) => ZodTypeAny
230
+ const env = new SstEnvironmentBuilder(
231
+ {
232
+ database: { type: ResourceType.Postgres, /* ... */ },
233
+ custom: { type: 'my-custom' as const, data: 'custom-data' },
234
+ },
235
+ {
236
+ // Custom resolver merged with SST resolvers
237
+ 'my-custom': (key, value) => ({ [`${key}Data`]: value.data }),
238
+ }
239
+ ).build();
240
+
241
+ // Result includes both SST resources and custom type
228
242
  ```
229
243
 
230
- - `path`: The path to the configuration value (supports nested paths with dots)
231
- - Returns: A Zod schema that will be used to validate the value at the specified path
244
+ ### Resource Type Enum
245
+
246
+ ```typescript
247
+ import { ResourceType } from '@geekmidas/envkit/sst';
248
+
249
+ // Legacy format (dot notation)
250
+ ResourceType.Postgres // 'sst.aws.Postgres'
251
+ ResourceType.Secret // 'sst.sst.Secret'
252
+ ResourceType.Bucket // 'sst.aws.Bucket'
253
+
254
+ // Modern format (colon notation)
255
+ ResourceType.SSTPostgres // 'sst:aws:Postgres'
256
+ ResourceType.SSTSecret // 'sst:sst:Secret'
257
+ ResourceType.SSTBucket // 'sst:aws:Bucket'
258
+ ResourceType.SnsTopic // 'sst:aws:SnsTopic'
259
+ ```
232
260
 
233
- ## Best Practices
261
+ ### Using with SST Resource
234
262
 
235
- 1. **Define configuration at startup**: Parse your configuration once at application startup and export the result:
263
+ Combine `SstEnvironmentBuilder` with `EnvironmentParser` to create a type-safe configuration from SST resources:
236
264
 
237
265
  ```typescript
238
266
  // config.ts
239
267
  import { EnvironmentParser } from '@geekmidas/envkit';
240
- import { z } from 'zod';
268
+ import { SstEnvironmentBuilder } from '@geekmidas/envkit/sst';
269
+ import { Resource } from 'sst';
241
270
 
242
- const parser = new EnvironmentParser(process.env);
271
+ // Build environment variables from SST resources
272
+ const env = new SstEnvironmentBuilder(Resource as {}).build();
273
+
274
+ // Create parser with the normalized environment
275
+ export const envParser = new EnvironmentParser(env);
243
276
 
244
- export const config = parser.create((get) => ({
245
- // ... your schema
277
+ // Define your configuration schema
278
+ export const config = envParser.create((get) => ({
279
+ database: {
280
+ host: get('DATABASE_HOST').string(),
281
+ port: get('DATABASE_PORT').number(),
282
+ name: get('DATABASE_NAME').string(),
283
+ username: get('DATABASE_USERNAME').string(),
284
+ password: get('DATABASE_PASSWORD').string(),
285
+ },
286
+ apiKey: get('API_KEY').string(),
246
287
  })).parse();
288
+ ```
289
+
290
+ This pattern allows you to:
291
+ 1. Normalize SST resources (Postgres, Secrets, Buckets, etc.) into flat environment variables
292
+ 2. Parse and validate them with Zod schemas
293
+ 3. Get full TypeScript type inference for your configuration
247
294
 
248
- // Other files can import the typed config
249
- import { config } from './config';
295
+ ## API Reference
296
+
297
+ ### EnvironmentParser
298
+
299
+ ```typescript
300
+ class EnvironmentParser {
301
+ constructor(config: Record<string, unknown>);
302
+ create<T>(schemaBuilder: (get: GetFunction) => T): ConfigParser<T>;
303
+ }
250
304
  ```
251
305
 
252
- 2. **Use meaningful defaults**: Provide sensible defaults for optional configuration:
306
+ ### EnvironmentBuilder
253
307
 
254
308
  ```typescript
255
- const config = parser.create((get) => ({
256
- port: get('PORT').string().transform(Number).default(3000),
257
- logLevel: get('LOG_LEVEL').enum(['debug', 'info', 'warn', 'error']).default('info')
258
- }));
309
+ class EnvironmentBuilder<TRecord, TResolvers> {
310
+ constructor(
311
+ record: TRecord,
312
+ resolvers: TResolvers,
313
+ options?: EnvironmentBuilderOptions
314
+ );
315
+ build(): EnvRecord;
316
+ }
317
+
318
+ interface EnvironmentBuilderOptions {
319
+ onUnmatchedValue?: (key: string, value: unknown) => void;
320
+ }
259
321
  ```
260
322
 
261
- 3. **Group related configuration**: Organize your configuration into logical groups:
323
+ ### SstEnvironmentBuilder
262
324
 
263
325
  ```typescript
264
- const config = parser.create((get) => ({
265
- server: { /* server config */ },
266
- database: { /* database config */ },
267
- features: { /* feature flags */ },
268
- thirdParty: { /* external service config */ }
269
- }));
326
+ class SstEnvironmentBuilder<TRecord> {
327
+ constructor(
328
+ record: TRecord,
329
+ additionalResolvers?: CustomResolvers<TRecord>,
330
+ options?: EnvironmentBuilderOptions
331
+ );
332
+ build(): EnvRecord;
333
+ }
270
334
  ```
271
335
 
272
- 4. **Document your configuration**: Add comments to explain complex validations:
336
+ ### environmentCase
273
337
 
274
338
  ```typescript
275
- const config = parser.create((get) => ({
276
- // Maximum number of concurrent connections
277
- maxConnections: get('MAX_CONNECTIONS')
278
- .string()
279
- .transform(Number)
280
- .int()
281
- .min(1)
282
- .max(1000)
283
- .default(100),
284
-
285
- // Comma-separated list of allowed origins
286
- allowedOrigins: get('ALLOWED_ORIGINS')
287
- .string()
288
- .transform(origins => origins.split(',').map(o => o.trim()))
289
- .default(['http://localhost:3000'])
290
- }));
339
+ function environmentCase(name: string): string;
340
+
341
+ // Examples:
342
+ environmentCase('myVariable') // 'MY_VARIABLE'
343
+ environmentCase('apiUrl') // 'API_URL'
344
+ environmentCase('databaseName') // 'DATABASE_NAME'
291
345
  ```
292
346
 
293
347
  ## License
294
348
 
295
- MIT
349
+ MIT
@@ -0,0 +1,131 @@
1
+ //#region src/EnvironmentBuilder.d.ts
2
+ /**
3
+ * Converts a string to environment variable case format (UPPER_SNAKE_CASE).
4
+ * Numbers following underscores are preserved without the underscore.
5
+ *
6
+ * @param name - The string to convert
7
+ * @returns The converted string in environment variable format
8
+ *
9
+ * @example
10
+ * environmentCase('myVariable') // 'MY_VARIABLE'
11
+ * environmentCase('apiV2') // 'APIV2'
12
+ */
13
+ declare function environmentCase(name: string): string;
14
+ /**
15
+ * A record of environment variable names to their values.
16
+ * Values can be primitives or nested records.
17
+ */
18
+ interface EnvRecord {
19
+ [key: string]: EnvValue;
20
+ }
21
+ /**
22
+ * Represents a value that can be stored in an environment record.
23
+ * Can be a primitive value or a nested record of environment values.
24
+ */
25
+ type EnvValue = string | number | boolean | EnvRecord;
26
+ /**
27
+ * A resolver function that converts a typed value into environment variables.
28
+ *
29
+ * @template T - The type of value this resolver handles (without the `type` key)
30
+ * @param key - The key name from the input record
31
+ * @param value - The value to resolve (without the `type` key)
32
+ * @returns A record of environment variable names to their values
33
+ */
34
+ type EnvironmentResolver<T = any> = (key: string, value: T) => EnvRecord;
35
+ /**
36
+ * A map of type discriminator strings to their resolver functions.
37
+ */
38
+ type Resolvers = Record<string, EnvironmentResolver<any>>;
39
+ /**
40
+ * Options for configuring the EnvironmentBuilder.
41
+ */
42
+ interface EnvironmentBuilderOptions {
43
+ /**
44
+ * Handler called when a value's type doesn't match any registered resolver.
45
+ * Defaults to console.warn.
46
+ */
47
+ onUnmatchedValue?: (key: string, value: unknown) => void;
48
+ }
49
+ /**
50
+ * Input value type - either a string or an object with a `type` discriminator.
51
+ */
52
+ type InputValue = string | {
53
+ type: string;
54
+ [key: string]: unknown;
55
+ };
56
+ /**
57
+ * Base type for typed input values with a specific type discriminator.
58
+ */
59
+ type TypedInputValue<TType extends string = string> = {
60
+ type: TType;
61
+ [key: string]: unknown;
62
+ };
63
+ /**
64
+ * Extracts the `type` string value from an input value.
65
+ */
66
+ type ExtractType<T> = T extends {
67
+ type: infer U extends string;
68
+ } ? U : never;
69
+ /**
70
+ * Removes the `type` key from an object type.
71
+ */
72
+ type OmitType<T> = T extends {
73
+ type: string;
74
+ } ? Omit<T, 'type'> : never;
75
+ /**
76
+ * Extracts all unique `type` values from a record (excluding plain strings).
77
+ */
78
+ type AllTypeValues<TRecord extends Record<string, InputValue>> = { [K in keyof TRecord]: ExtractType<TRecord[K]> }[keyof TRecord];
79
+ /**
80
+ * For a given type value, finds the corresponding value type (without `type` key).
81
+ */
82
+ type ValueForType<TRecord extends Record<string, InputValue>, TType extends string> = { [K in keyof TRecord]: TRecord[K] extends {
83
+ type: TType;
84
+ } ? OmitType<TRecord[K]> : never }[keyof TRecord];
85
+ /**
86
+ * Generates typed resolvers based on the input record.
87
+ * Keys are the `type` values, values are resolver functions receiving the value without `type`.
88
+ */
89
+ type TypedResolvers<TRecord extends Record<string, InputValue>> = { [TType in AllTypeValues<TRecord>]: EnvironmentResolver<ValueForType<TRecord, TType>> };
90
+ /**
91
+ * A generic, extensible class for building environment variables from
92
+ * objects with type-discriminated values.
93
+ *
94
+ * @template TRecord - The input record type for type inference
95
+ * @template TResolvers - The resolvers type (defaults to TypedResolvers<TRecord>)
96
+ *
97
+ * @example
98
+ * ```typescript
99
+ * const env = new EnvironmentBuilder(
100
+ * {
101
+ * apiKey: { type: 'secret', value: 'xyz' },
102
+ * appName: 'my-app'
103
+ * },
104
+ * {
105
+ * // `value` is typed as { value: string } (without `type`)
106
+ * secret: (key, value) => ({ [key]: value.value }),
107
+ * }
108
+ * ).build();
109
+ * // { API_KEY: 'xyz', APP_NAME: 'my-app' }
110
+ * ```
111
+ */
112
+ declare class EnvironmentBuilder<TRecord extends Record<string, InputValue> = Record<string, InputValue>, TResolvers extends Resolvers = TypedResolvers<TRecord>> {
113
+ private readonly record;
114
+ private readonly resolvers;
115
+ private readonly options;
116
+ constructor(record: TRecord, resolvers: TResolvers, options?: EnvironmentBuilderOptions);
117
+ /**
118
+ * Build environment variables from the input record.
119
+ *
120
+ * - Plain string values are passed through with key transformation
121
+ * - Object values with a `type` property are matched against resolvers
122
+ * - Resolvers receive values without the `type` key
123
+ * - Only root-level keys are transformed to UPPER_SNAKE_CASE
124
+ *
125
+ * @returns A record of environment variables
126
+ */
127
+ build(): EnvRecord;
128
+ }
129
+ //#endregion
130
+ export { EnvRecord, EnvValue, EnvironmentBuilder, EnvironmentBuilderOptions, EnvironmentResolver, InputValue, Resolvers, TypedInputValue, TypedResolvers, environmentCase };
131
+ //# sourceMappingURL=EnvironmentBuilder-DHfDXJUm.d.mts.map