zod-conf 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.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Simon Bobrov
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,217 @@
1
+ # zod-conf
2
+
3
+ Type-safe configuration management with Zod schemas and environment variables.
4
+
5
+ ## Features
6
+
7
+ - **Type-safe** - Full TypeScript support with Zod schema validation
8
+ - **Env-first** - Bind schema fields directly to environment variables
9
+ - **Simple API** - Intuitive, Zod-native API that feels familiar
10
+ - **Zero compromise** - All Zod features work - transformations, refinements, and more
11
+ - **Lightweight** - Only Zod as peer dependency, no extra bloat
12
+ - **Universal** - Works with process.env, dotenv, or any environment loader
13
+
14
+ ## Installation
15
+
16
+ ```bash
17
+ npm install zod-conf zod
18
+ # or
19
+ yarn add zod-conf zod
20
+ # or
21
+ pnpm add zod-conf zod
22
+ ```
23
+
24
+ ## Quick Start
25
+
26
+ ```typescript
27
+ import zc from 'zod-conf';
28
+
29
+ // define your configuration schema
30
+ const schema = zc.define({
31
+ port: zc.env('PORT').number().default(3000),
32
+ host: zc.env('HOST').string().default('localhost'),
33
+ debug: zc.env('DEBUG').boolean().default(false),
34
+ });
35
+
36
+ // load configuration from environment
37
+ const config = schema.load({
38
+ env: process.env,
39
+ });
40
+
41
+ console.log(config);
42
+ // { port: 3000, host: 'localhost', debug: false }
43
+ ```
44
+
45
+ ## Usage
46
+
47
+ ### Basic Types
48
+
49
+ ```typescript
50
+ const schema = zc.define({
51
+ // string
52
+ apiUrl: zc.env('API_URL').string(),
53
+
54
+ // number
55
+ port: zc.env('PORT').number(),
56
+
57
+ // boolean
58
+ isProduction: zc.env('NODE_ENV').boolean(),
59
+
60
+ // enum
61
+ logLevel: zc.env('LOG_LEVEL').enum(['debug', 'info', 'warn', 'error']),
62
+ });
63
+ ```
64
+
65
+ ### Defaults and Optional Values
66
+
67
+ ```typescript
68
+ const schema = zc.define({
69
+ // with default value
70
+ port: zc.env('PORT').number().default(3000),
71
+
72
+ // optional field
73
+ apiKey: zc.env('API_KEY').string().optional(),
74
+
75
+ // nullable field
76
+ proxyUrl: zc.env('PROXY_URL').string().nullable(),
77
+ });
78
+ ```
79
+
80
+ ### Nested Objects
81
+
82
+ ```typescript
83
+ const schema = zc.define({
84
+ server: zc.object({
85
+ host: zc.env('SERVER_HOST').string().default('localhost'),
86
+ port: zc.env('SERVER_PORT').number().default(3000),
87
+ }),
88
+ database: zc.object({
89
+ url: zc.env('DATABASE_URL').string(),
90
+ pool: zc.object({
91
+ min: zc.env('DB_POOL_MIN').number().default(1),
92
+ max: zc.env('DB_POOL_MAX').number().default(10),
93
+ }),
94
+ }),
95
+ });
96
+ ```
97
+
98
+ ### TypeScript Enums
99
+
100
+ ```typescript
101
+ enum LogLevel {
102
+ DEBUG = 'debug',
103
+ INFO = 'info',
104
+ WARN = 'warn',
105
+ ERROR = 'error',
106
+ }
107
+
108
+ const schema = zc.define({
109
+ logLevel: zc.env('LOG_LEVEL').enum(LogLevel).default(LogLevel.INFO),
110
+ });
111
+ ```
112
+
113
+ ### Loading Configuration
114
+
115
+ ```typescript
116
+ // from process.env
117
+ const config = schema.load({ env: process.env });
118
+
119
+ // from .env file (using dotenv)
120
+ import { config as dotenvConfig } from 'dotenv';
121
+
122
+ const env = dotenvConfig();
123
+ const config = schema.load({ env: env.parsed });
124
+
125
+ // from custom source
126
+ const config = schema.load({
127
+ env: {
128
+ PORT: '8080',
129
+ HOST: '0.0.0.0',
130
+ },
131
+ });
132
+ ```
133
+
134
+ ### Error Handling
135
+
136
+ The error-handling is similar to Zod's standard behavior:
137
+
138
+ ```typescript
139
+ // throws on validation error
140
+ try {
141
+ const config = schema.load({ env: process.env });
142
+ } catch (error) {
143
+ console.error('Configuration validation failed:', error);
144
+ }
145
+
146
+ // safe parsing without throwing
147
+ const result = schema.safeLoad({ env: process.env });
148
+ if (result.success) {
149
+ console.log('Config:', result.data);
150
+ } else {
151
+ console.error('Validation errors:', result.error);
152
+ }
153
+ ```
154
+
155
+ ### Advanced: Transformations
156
+
157
+ All Zod transformations work seamlessly:
158
+
159
+ ```typescript
160
+ const schema = zc.define({
161
+ port: zc.env('PORT')
162
+ .number()
163
+ .default(3000)
164
+ .transform(p => Math.max(1024, p)), // Ensure port is at least 1024
165
+
166
+ apiUrl: zc.env('API_URL')
167
+ .string()
168
+ .transform(url => url.replace(/\/$/, '')), // Remove trailing slash
169
+ });
170
+ ```
171
+
172
+ ## API Reference
173
+
174
+ ### `zc.define(shape)`
175
+
176
+ Creates a configuration schema with environment variable bindings.
177
+
178
+ - **shape**: An object where each value is created using `zc.env()` or `zc.object()`
179
+ - **returns**: A `ZodConfSchema` instance with `load()` and `safeLoad()` methods
180
+
181
+ ### `zc.env(key)`
182
+
183
+ Binds a schema field to an environment variable.
184
+
185
+ - **key**: The environment variable name
186
+ - **returns**: An object with methods for different types:
187
+ - `.string()` - String value
188
+ - `.number()` - Numeric value (auto-converted)
189
+ - `.boolean()` - Boolean value (accepts 'true'/'false')
190
+ - `.enum(values)` - Enum value (string array or TypeScript enum)
191
+
192
+ ### `zc.object(shape)`
193
+
194
+ Creates a nested object schema.
195
+
196
+ - **shape**: An object where values are `zc.env()` or nested `zc.object()` calls
197
+ - **returns**: A Zod object schema
198
+
199
+ ### `schema.load(params)`
200
+
201
+ Loads and validates configuration.
202
+
203
+ - **params.env**: Environment variables object
204
+ - **returns**: Validated configuration object
205
+ - **throws**: ZodError if validation fails
206
+
207
+ ### `schema.safeLoad(params)`
208
+
209
+ Safely loads configuration without throwing.
210
+
211
+ - **params.env**: Environment variables object
212
+ - **returns**: `{ success: true, data: T }` or `{ success: false, error: ZodError }`
213
+
214
+ ## License
215
+
216
+ MIT
217
+
@@ -0,0 +1,20 @@
1
+ import { boolean, number, string, enum as zenum } from 'zod';
2
+ export declare const zc: {
3
+ define: <T extends import("zod").ZodRawShape>(shape: T) => import("./values/define.js").ZodConfSchema<T>;
4
+ env: (key: string) => {
5
+ string: () => import("zod").ZodString;
6
+ number: () => import("zod").ZodCoercedNumber<unknown>;
7
+ boolean: () => import("zod").ZodCoercedBoolean<unknown>;
8
+ enum: {
9
+ <const T extends readonly string[]>(values: T): import("zod").ZodEnum<{ [k_1 in T[number]]: k_1; } extends infer T_1 ? { [k in keyof T_1]: T_1[k]; } : never>;
10
+ <const T extends Readonly<Record<string, string | number>>>(entries: T): import("zod").ZodEnum<T>;
11
+ };
12
+ };
13
+ object: <T extends import("zod").ZodRawShape>(shape: T) => import("zod").ZodObject<T>;
14
+ string: typeof string;
15
+ boolean: typeof boolean;
16
+ number: typeof number;
17
+ enum: typeof zenum;
18
+ };
19
+ export default zc;
20
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,IAAI,KAAK,EAAE,MAAM,KAAK,CAAC;AAK7D,eAAO,MAAM,EAAE;;;;;;;;;;;;;;;;CAWd,CAAC;AAEF,eAAe,EAAE,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,16 @@
1
+ import { boolean, number, string, enum as zenum } from 'zod';
2
+ import { define } from './values/define.js';
3
+ import { env } from './values/env.js';
4
+ import { object } from './values/object.js';
5
+ export const zc = {
6
+ // core values
7
+ define,
8
+ env,
9
+ object,
10
+ // re-export common zod values for convenience
11
+ string,
12
+ boolean,
13
+ number,
14
+ enum: zenum,
15
+ };
16
+ export default zc;
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,IAAI,KAAK,EAAE,MAAM,KAAK,CAAC;AAC7D,OAAO,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAC;AAC5C,OAAO,EAAE,GAAG,EAAE,MAAM,iBAAiB,CAAC;AACtC,OAAO,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAC;AAE5C,MAAM,CAAC,MAAM,EAAE,GAAG;IAEhB,MAAM;IACN,GAAG;IACH,MAAM;IAGN,MAAM;IACN,OAAO;IACP,MAAM;IACN,IAAI,EAAE,KAAK;CACZ,CAAC;AAEF,eAAe,EAAE,CAAC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=index.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.test.d.ts","sourceRoot":"","sources":["../src/index.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,372 @@
1
+ import { strictEqual, deepStrictEqual } from 'node:assert';
2
+ import { test } from 'node:test';
3
+ import zc from './index.js';
4
+ test('zod-conf configuration parsing', async (t) => {
5
+ await t.test('parses string environment variables', () => {
6
+ const schema = zc.define({
7
+ apiUrl: zc.env('API_URL').string(),
8
+ });
9
+ const config = schema.load({
10
+ env: { API_URL: 'https://api.example.com' },
11
+ });
12
+ strictEqual(config.apiUrl, 'https://api.example.com');
13
+ });
14
+ await t.test('parses number environment variables', () => {
15
+ const schema = zc.define({
16
+ port: zc.env('PORT').number(),
17
+ });
18
+ const config = schema.load({
19
+ env: { PORT: '3000' },
20
+ });
21
+ strictEqual(config.port, 3000);
22
+ });
23
+ await t.test('parses boolean environment variables', () => {
24
+ const schema = zc.define({
25
+ debug: zc.env('DEBUG').boolean(),
26
+ verbose: zc.env('VERBOSE').boolean(),
27
+ });
28
+ const config = schema.load({
29
+ env: { DEBUG: 'true', VERBOSE: 'false' },
30
+ });
31
+ strictEqual(config.debug, true);
32
+ strictEqual(config.verbose, false);
33
+ });
34
+ await t.test('parses enum environment variables with string array', () => {
35
+ const schema = zc.define({
36
+ logLevel: zc.env('LOG_LEVEL').enum(['info', 'debug', 'error']),
37
+ });
38
+ const config = schema.load({
39
+ env: { LOG_LEVEL: 'debug' },
40
+ });
41
+ strictEqual(config.logLevel, 'debug');
42
+ });
43
+ await t.test('parses enum environment variables with string enum', () => {
44
+ let LogLevel;
45
+ (function (LogLevel) {
46
+ LogLevel["INFO"] = "info";
47
+ LogLevel["DEBUG"] = "debug";
48
+ LogLevel["ERROR"] = "error";
49
+ })(LogLevel || (LogLevel = {}));
50
+ const schema = zc.define({
51
+ logLevel: zc.env('LOG_LEVEL').enum(LogLevel),
52
+ });
53
+ const config = schema.load({
54
+ env: { LOG_LEVEL: 'info' },
55
+ });
56
+ strictEqual(config.logLevel, 'info');
57
+ });
58
+ await t.test('parses enum environment variables with numeric enum', () => {
59
+ let Priority;
60
+ (function (Priority) {
61
+ Priority[Priority["LOW"] = 1] = "LOW";
62
+ Priority[Priority["MEDIUM"] = 2] = "MEDIUM";
63
+ Priority[Priority["HIGH"] = 3] = "HIGH";
64
+ Priority[Priority["URGENT"] = 4] = "URGENT";
65
+ })(Priority || (Priority = {}));
66
+ const schema = zc.define({
67
+ priority: zc.env('PRIORITY').enum(Priority),
68
+ });
69
+ const config = schema.load({
70
+ env: { PRIORITY: '3' },
71
+ });
72
+ strictEqual(config.priority, 3);
73
+ });
74
+ await t.test('parses enum environment variables with mixed enum', () => {
75
+ let Status;
76
+ (function (Status) {
77
+ Status[Status["INACTIVE"] = 0] = "INACTIVE";
78
+ Status[Status["ACTIVE"] = 1] = "ACTIVE";
79
+ Status["PENDING"] = "pending";
80
+ Status["ARCHIVED"] = "archived";
81
+ })(Status || (Status = {}));
82
+ const schema = zc.define({
83
+ statusNum: zc.env('STATUS_NUM').enum(Status),
84
+ statusStr: zc.env('STATUS_STR').enum(Status),
85
+ });
86
+ const config = schema.load({
87
+ env: {
88
+ STATUS_NUM: '1',
89
+ STATUS_STR: 'pending',
90
+ },
91
+ });
92
+ strictEqual(config.statusNum, 1);
93
+ strictEqual(config.statusStr, 'pending');
94
+ });
95
+ await t.test('handles default values', () => {
96
+ const schema = zc.define({
97
+ host: zc.env('HOST').string().default('localhost'),
98
+ port: zc.env('PORT').number().default(8080),
99
+ secure: zc.env('SECURE').boolean().default(false),
100
+ });
101
+ const config = schema.load({
102
+ env: {},
103
+ });
104
+ strictEqual(config.host, 'localhost');
105
+ strictEqual(config.port, 8080);
106
+ strictEqual(config.secure, false);
107
+ });
108
+ await t.test('handles optional values', () => {
109
+ const schema = zc.define({
110
+ optionalKey: zc.env('OPTIONAL_KEY').string().optional(),
111
+ });
112
+ const config = schema.load({
113
+ env: {},
114
+ });
115
+ strictEqual(config.optionalKey, undefined);
116
+ });
117
+ await t.test('handles nested objects', () => {
118
+ const schema = zc.define({
119
+ server: zc.object({
120
+ host: zc.env('SERVER_HOST').string().default('localhost'),
121
+ port: zc.env('SERVER_PORT').number().default(3000),
122
+ }),
123
+ database: zc.object({
124
+ url: zc.env('DB_URL').string(),
125
+ pool: zc.object({
126
+ min: zc.env('DB_POOL_MIN').number().default(1),
127
+ max: zc.env('DB_POOL_MAX').number().default(10),
128
+ }),
129
+ }),
130
+ });
131
+ const config = schema.load({
132
+ env: {
133
+ SERVER_HOST: '0.0.0.0',
134
+ SERVER_PORT: '4000',
135
+ DB_URL: 'postgres://localhost/mydb',
136
+ DB_POOL_MAX: '20',
137
+ },
138
+ });
139
+ deepStrictEqual(config, {
140
+ server: {
141
+ host: '0.0.0.0',
142
+ port: 4000,
143
+ },
144
+ database: {
145
+ url: 'postgres://localhost/mydb',
146
+ pool: {
147
+ min: 1,
148
+ max: 20,
149
+ },
150
+ },
151
+ });
152
+ });
153
+ await t.test('handles complex configuration', () => {
154
+ const schema = zc.define({
155
+ app: zc.object({
156
+ name: zc.env('APP_NAME').string().default('MyApp'),
157
+ version: zc.env('APP_VERSION').string().default('1.0.0'),
158
+ debug: zc.env('DEBUG').boolean().default(false),
159
+ }),
160
+ server: zc.object({
161
+ host: zc.env('HOST').string().default('localhost'),
162
+ port: zc.env('PORT').number().default(3000),
163
+ ssl: zc.object({
164
+ enabled: zc.env('SSL_ENABLED').boolean().default(false),
165
+ cert: zc.env('SSL_CERT').string().optional(),
166
+ key: zc.env('SSL_KEY').string().optional(),
167
+ }),
168
+ }),
169
+ features: zc.object({
170
+ auth: zc.env('FEATURE_AUTH').boolean().default(true),
171
+ analytics: zc.env('FEATURE_ANALYTICS').boolean().default(false),
172
+ }),
173
+ });
174
+ const config = schema.load({
175
+ env: {
176
+ APP_NAME: 'TestApp',
177
+ DEBUG: 'true',
178
+ PORT: '5000',
179
+ SSL_ENABLED: 'true',
180
+ SSL_CERT: '/path/to/cert.pem',
181
+ SSL_KEY: '/path/to/key.pem',
182
+ FEATURE_ANALYTICS: 'true',
183
+ },
184
+ });
185
+ deepStrictEqual(config, {
186
+ app: {
187
+ name: 'TestApp',
188
+ version: '1.0.0',
189
+ debug: true,
190
+ },
191
+ server: {
192
+ host: 'localhost',
193
+ port: 5000,
194
+ ssl: {
195
+ enabled: true,
196
+ cert: '/path/to/cert.pem',
197
+ key: '/path/to/key.pem',
198
+ },
199
+ },
200
+ features: {
201
+ auth: true,
202
+ analytics: true,
203
+ },
204
+ });
205
+ });
206
+ await t.test('safeLoad returns success for valid config', () => {
207
+ const schema = zc.define({
208
+ apiKey: zc.env('API_KEY').string(),
209
+ });
210
+ const result = schema.safeLoad({
211
+ env: { API_KEY: 'secret-key' },
212
+ });
213
+ strictEqual(result.success, true);
214
+ if (result.success) {
215
+ strictEqual(result.data.apiKey, 'secret-key');
216
+ }
217
+ });
218
+ await t.test('safeLoad returns error for invalid config', () => {
219
+ const schema = zc.define({
220
+ port: zc.env('PORT').number(),
221
+ });
222
+ const result = schema.safeLoad({
223
+ env: { PORT: 'not-a-number' },
224
+ });
225
+ strictEqual(result.success, false);
226
+ if (!result.success) {
227
+ strictEqual(result.error.issues.length > 0, true);
228
+ }
229
+ });
230
+ await t.test('handles fields without env metadata', () => {
231
+ const schema = zc.define({
232
+ // this field has no env binding, just a plain zod schema
233
+ plainField: zc.object({
234
+ nestedPlain: zc.env('SHOULD_WORK').string().default('default'),
235
+ }),
236
+ });
237
+ const config = schema.load({
238
+ env: {},
239
+ });
240
+ deepStrictEqual(config, {
241
+ plainField: {
242
+ nestedPlain: 'default',
243
+ },
244
+ });
245
+ });
246
+ await t.test('load throws error for invalid config', () => {
247
+ const schema = zc.define({
248
+ requiredField: zc.env('REQUIRED').string(),
249
+ });
250
+ let error;
251
+ try {
252
+ schema.load({
253
+ env: {},
254
+ });
255
+ }
256
+ catch (e) {
257
+ error = e;
258
+ }
259
+ strictEqual(error !== undefined, true);
260
+ strictEqual(error.issues !== undefined, true);
261
+ });
262
+ await t.test('handles enum with non-integer number strings', () => {
263
+ const schema = zc.define({
264
+ value: zc.env('VALUE').enum(['a', 'b', 'c']),
265
+ });
266
+ const config = schema.load({
267
+ env: { VALUE: 'a' },
268
+ });
269
+ strictEqual(config.value, 'a');
270
+ });
271
+ await t.test('handles enum with decimal number string', () => {
272
+ const schema = zc.define({
273
+ value: zc.env('DECIMAL_VALUE').enum(['3.14', 'pi', 'e']),
274
+ });
275
+ // this should pass the string through since "3.14" is not an integer
276
+ const config = schema.load({
277
+ env: { DECIMAL_VALUE: '3.14' },
278
+ });
279
+ strictEqual(config.value, '3.14');
280
+ });
281
+ await t.test('handles undefined env values for enum', () => {
282
+ const schema = zc.define({
283
+ optionalEnum: zc.env('OPT_ENUM').enum(['a', 'b']).optional(),
284
+ });
285
+ const config = schema.load({
286
+ env: {},
287
+ });
288
+ strictEqual(config.optionalEnum, undefined);
289
+ });
290
+ await t.test('handles schema without innerType or schema properties', () => {
291
+ // this tests the branch where we break out of the while loop
292
+ const schema = zc.define({
293
+ value: zc.env('VALUE').string(),
294
+ });
295
+ const config = schema.load({
296
+ env: { VALUE: 'test' },
297
+ });
298
+ strictEqual(config.value, 'test');
299
+ });
300
+ await t.test('handles deeply wrapped schemas with transformations', () => {
301
+ const schema = zc.define({
302
+ transformed: zc
303
+ .env('TRANSFORMED')
304
+ .string()
305
+ .optional()
306
+ .default('default')
307
+ .transform((v) => v.toUpperCase()),
308
+ });
309
+ const config = schema.load({
310
+ env: {},
311
+ });
312
+ strictEqual(config.transformed, 'DEFAULT');
313
+ });
314
+ await t.test('handles schema with no env metadata at all', async () => {
315
+ // create a plain zod schema without env bindings
316
+ const z = await import('zod');
317
+ const plainSchema = z.object({
318
+ plainField: z.string().default('plain'),
319
+ });
320
+ // mix it with env-bound fields
321
+ const schema = zc.define({
322
+ envField: zc.env('ENV_FIELD').string().default('env'),
323
+ plainNested: plainSchema.shape.plainField,
324
+ });
325
+ const config = schema.load({
326
+ env: {},
327
+ });
328
+ strictEqual(config.envField, 'env');
329
+ // plain fields without env binding will still use their Zod defaults
330
+ strictEqual(config.plainNested, 'plain');
331
+ });
332
+ await t.test('env proxy preserves non-schema properties and methods', async () => {
333
+ const envSchema = zc.env('TEST').string();
334
+ // test that metadata is stored in WeakMap (we can't directly access it from here)
335
+ // but we can verify the schema works correctly
336
+ const schema = zc.define({
337
+ test: envSchema,
338
+ });
339
+ const config = schema.load({ env: { TEST: 'value' } });
340
+ strictEqual(config.test, 'value');
341
+ // call methods that don't return schemas
342
+ const parsed = envSchema.parse('test');
343
+ strictEqual(parsed, 'test');
344
+ // access description property
345
+ const withDesc = zc.env('DESC').string().describe('A test field');
346
+ strictEqual(withDesc.description, 'A test field');
347
+ // test safeParse which returns a result object, not a schema
348
+ const result = zc.env('SAFE').number().safeParse('123');
349
+ strictEqual(result.success, true);
350
+ if (result.success) {
351
+ strictEqual(result.data, 123);
352
+ }
353
+ });
354
+ await t.test('env proxy handles methods returning non-objects', () => {
355
+ const schema = zc.env('TEST').string();
356
+ // test parse method returns the parsed value (not a schema)
357
+ const parsed = schema.parse('test');
358
+ strictEqual(parsed, 'test');
359
+ // test safeParse returns a result object (not a schema)
360
+ const result = schema.safeParse('value');
361
+ strictEqual(result.success, true);
362
+ if (result.success) {
363
+ strictEqual(result.data, 'value');
364
+ }
365
+ // test that optional() and nullable() still work (these are NOT deprecated)
366
+ const optionalSchema = schema.optional();
367
+ const nullableSchema = schema.nullable();
368
+ // verify they can parse undefined/null respectively
369
+ strictEqual(optionalSchema.parse(undefined), undefined);
370
+ strictEqual(nullableSchema.parse(null), null);
371
+ });
372
+ });
@@ -0,0 +1,78 @@
1
+ import { ZodObject, type ZodRawShape, type infer as ZodInfer } from 'zod';
2
+ type Env = Record<string, string | undefined>;
3
+ export type LoadParams = {
4
+ env: Env;
5
+ };
6
+ export declare class ZodConfSchema<T extends ZodRawShape> {
7
+ private shape;
8
+ private schema;
9
+ constructor(shape: T, schema: ZodObject<T>);
10
+ private loadValue;
11
+ /**
12
+ * Loads and validates configuration from environment variables.
13
+ * Throws a ZodError if validation fails.
14
+ *
15
+ * @param input - Object containing environment variables
16
+ * @param input.env - Environment variables as key-value pairs
17
+ * @returns Validated configuration object
18
+ * @throws {ZodError} If validation fails
19
+ *
20
+ * @example
21
+ * ```typescript
22
+ * const config = schema.load({ env: process.env });
23
+ * ```
24
+ */
25
+ load(input: LoadParams): ZodInfer<ZodObject<T>>;
26
+ /**
27
+ * Safely loads and validates configuration without throwing.
28
+ * Returns a result object with either success or error.
29
+ *
30
+ * @param input - Object containing environment variables
31
+ * @param input.env - Environment variables as key-value pairs
32
+ * @returns SafeParseReturnType with either { success: true, data } or { success: false, error }
33
+ *
34
+ * @example
35
+ * ```typescript
36
+ * const result = schema.safeLoad({ env: process.env });
37
+ *
38
+ * if (result.success) {
39
+ * console.log('Config:', result.data);
40
+ * } else {
41
+ * console.error('Errors:', result.error.issues);
42
+ * }
43
+ * ```
44
+ */
45
+ safeLoad(input: LoadParams): import("zod").ZodSafeParseResult<import("zod/v4/core").$InferObjectOutput<T, {}>>;
46
+ }
47
+ /**
48
+ * Creates a configuration schema with environment variable bindings.
49
+ *
50
+ * @param shape - An object defining the configuration structure where each field
51
+ * uses `env()` to bind to environment variables or `object()` for nesting
52
+ * @returns A ZodConfSchema instance with `load()` and `safeLoad()` methods
53
+ *
54
+ * @example
55
+ * ```typescript
56
+ * // define a configuration schema
57
+ * const schema = define({
58
+ * port: env('PORT').number().default(3000),
59
+ * host: env('HOST').string().default('localhost'),
60
+ * database: object({
61
+ * url: env('DATABASE_URL').string(),
62
+ * poolSize: env('DB_POOL_SIZE').number().default(10),
63
+ * }),
64
+ * });
65
+ *
66
+ * // load configuration from environment
67
+ * const config = schema.load({ env: process.env });
68
+ *
69
+ * // safe load without throwing
70
+ * const result = schema.safeLoad({ env: process.env });
71
+ * if (result.success) {
72
+ * console.log(result.data);
73
+ * }
74
+ * ```
75
+ */
76
+ export declare const define: <T extends ZodRawShape>(shape: T) => ZodConfSchema<T>;
77
+ export {};
78
+ //# sourceMappingURL=define.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"define.d.ts","sourceRoot":"","sources":["../../src/values/define.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAU,KAAK,WAAW,EAAgB,KAAK,KAAK,IAAI,QAAQ,EAAE,MAAM,KAAK,CAAC;AAGhG,KAAK,GAAG,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAC;AAE9C,MAAM,MAAM,UAAU,GAAG;IACvB,GAAG,EAAE,GAAG,CAAC;CACV,CAAC;AAEF,qBAAa,aAAa,CAAC,CAAC,SAAS,WAAW;IAE5C,OAAO,CAAC,KAAK;IACb,OAAO,CAAC,MAAM;gBADN,KAAK,EAAE,CAAC,EACR,MAAM,EAAE,SAAS,CAAC,CAAC,CAAC;IAG9B,OAAO,CAAC,SAAS;IA6EjB;;;;;;;;;;;;;OAaG;IACH,IAAI,CAAC,KAAK,EAAE,UAAU,GAAG,QAAQ,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC;IAU/C;;;;;;;;;;;;;;;;;;OAkBG;IACH,QAAQ,CAAC,KAAK,EAAE,UAAU;CAK3B;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,eAAO,MAAM,MAAM,GAAI,CAAC,SAAS,WAAW,EAAE,OAAO,CAAC,KAAG,aAAa,CAAC,CAAC,CAEvE,CAAC"}
@@ -0,0 +1,156 @@
1
+ import { ZodObject, object } from 'zod';
2
+ import { _envMetadata } from './env-metadata.js';
3
+ export class ZodConfSchema {
4
+ shape;
5
+ schema;
6
+ constructor(shape, schema) {
7
+ this.shape = shape;
8
+ this.schema = schema;
9
+ }
10
+ loadValue(shape, env) {
11
+ const input = {};
12
+ for (const key in shape) {
13
+ const schema = shape[key];
14
+ if (schema instanceof ZodObject) {
15
+ // for nested objects, recurse
16
+ input[key] = this.loadValue(schema.shape, env);
17
+ }
18
+ else {
19
+ let currentSchema = schema;
20
+ let envKey;
21
+ let envType;
22
+ // traverse wrapped schemas to find metadata
23
+ while (currentSchema && !envKey) {
24
+ const metadata = _envMetadata.get(currentSchema);
25
+ if (metadata) {
26
+ // found metadata -> use it
27
+ envKey = metadata.key;
28
+ envType = metadata.type;
29
+ break;
30
+ }
31
+ // check if this is a wrapped schema (default, optional, etc.)
32
+ const def = currentSchema._def;
33
+ if (def?.innerType) {
34
+ // zod wrappers like optional, default, nullable, etc.
35
+ currentSchema = def.innerType;
36
+ }
37
+ else if (def?.schema) {
38
+ // zod effects
39
+ currentSchema = def.schema;
40
+ }
41
+ else {
42
+ // end of the chain, no metadata found
43
+ break;
44
+ }
45
+ }
46
+ if (envKey) {
47
+ const value = env[envKey];
48
+ switch (envType) {
49
+ case 'string':
50
+ input[key] = value;
51
+ break;
52
+ case 'number':
53
+ input[key] = value ? Number(value) : undefined;
54
+ break;
55
+ case 'boolean':
56
+ input[key] = value === 'true' ? true : value === 'false' ? false : undefined;
57
+ break;
58
+ case 'enum': {
59
+ const numValue = Number(value);
60
+ // guess if enum is number-based
61
+ if (value && !isNaN(numValue) && Number.isInteger(numValue)) {
62
+ input[key] = numValue;
63
+ }
64
+ else {
65
+ input[key] = value;
66
+ }
67
+ break;
68
+ }
69
+ default:
70
+ input[key] = undefined;
71
+ }
72
+ }
73
+ else {
74
+ input[key] = undefined;
75
+ }
76
+ }
77
+ }
78
+ return input;
79
+ }
80
+ /**
81
+ * Loads and validates configuration from environment variables.
82
+ * Throws a ZodError if validation fails.
83
+ *
84
+ * @param input - Object containing environment variables
85
+ * @param input.env - Environment variables as key-value pairs
86
+ * @returns Validated configuration object
87
+ * @throws {ZodError} If validation fails
88
+ *
89
+ * @example
90
+ * ```typescript
91
+ * const config = schema.load({ env: process.env });
92
+ * ```
93
+ */
94
+ load(input) {
95
+ const result = this.safeLoad(input);
96
+ if (!result.success) {
97
+ throw result.error;
98
+ }
99
+ return result.data;
100
+ }
101
+ /**
102
+ * Safely loads and validates configuration without throwing.
103
+ * Returns a result object with either success or error.
104
+ *
105
+ * @param input - Object containing environment variables
106
+ * @param input.env - Environment variables as key-value pairs
107
+ * @returns SafeParseReturnType with either { success: true, data } or { success: false, error }
108
+ *
109
+ * @example
110
+ * ```typescript
111
+ * const result = schema.safeLoad({ env: process.env });
112
+ *
113
+ * if (result.success) {
114
+ * console.log('Config:', result.data);
115
+ * } else {
116
+ * console.error('Errors:', result.error.issues);
117
+ * }
118
+ * ```
119
+ */
120
+ safeLoad(input) {
121
+ const value = this.loadValue(this.shape, input.env);
122
+ return this.schema.safeParse(value);
123
+ }
124
+ }
125
+ /**
126
+ * Creates a configuration schema with environment variable bindings.
127
+ *
128
+ * @param shape - An object defining the configuration structure where each field
129
+ * uses `env()` to bind to environment variables or `object()` for nesting
130
+ * @returns A ZodConfSchema instance with `load()` and `safeLoad()` methods
131
+ *
132
+ * @example
133
+ * ```typescript
134
+ * // define a configuration schema
135
+ * const schema = define({
136
+ * port: env('PORT').number().default(3000),
137
+ * host: env('HOST').string().default('localhost'),
138
+ * database: object({
139
+ * url: env('DATABASE_URL').string(),
140
+ * poolSize: env('DB_POOL_SIZE').number().default(10),
141
+ * }),
142
+ * });
143
+ *
144
+ * // load configuration from environment
145
+ * const config = schema.load({ env: process.env });
146
+ *
147
+ * // safe load without throwing
148
+ * const result = schema.safeLoad({ env: process.env });
149
+ * if (result.success) {
150
+ * console.log(result.data);
151
+ * }
152
+ * ```
153
+ */
154
+ export const define = (shape) => {
155
+ return new ZodConfSchema(shape, object(shape));
156
+ };
@@ -0,0 +1 @@
1
+ {"version":3,"file":"define.js","sourceRoot":"","sources":["../../src/values/define.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,6 @@
1
+ import { type ZodType } from 'zod';
2
+ export declare const _envMetadata: WeakMap<ZodType<unknown, unknown, import("zod/v4/core").$ZodTypeInternals<unknown, unknown>>, {
3
+ key: string;
4
+ type: string;
5
+ }>;
6
+ //# sourceMappingURL=env-metadata.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"env-metadata.d.ts","sourceRoot":"","sources":["../../src/values/env-metadata.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,OAAO,EAAE,MAAM,KAAK,CAAC;AAGnC,eAAO,MAAM,YAAY;SAA+B,MAAM;UAAQ,MAAM;EAAK,CAAC"}
@@ -0,0 +1,3 @@
1
+ // the metadata stores env key and type for each schema
2
+ // we can use it when building the input object
3
+ export const _envMetadata = new WeakMap();
@@ -0,0 +1 @@
1
+ {"version":3,"file":"env-metadata.js","sourceRoot":"","sources":["../../src/values/env-metadata.ts"],"names":[],"mappings":"AAKA,MAAM,CAAC,MAAM,YAAY,GAAG,IAAI,OAAO,EAA0C,CAAC"}
@@ -0,0 +1,41 @@
1
+ import { type util, type ZodEnum } from 'zod';
2
+ import { coerce } from 'zod';
3
+ /**
4
+ * Binds a Zod schema to an environment variable.
5
+ *
6
+ * @param key - The name of the environment variable to bind to
7
+ * @returns An object with methods to create different types of schemas:
8
+ * - `string()` - Creates a string schema
9
+ * - `number()` - Creates a number schema (auto-coerced from string)
10
+ * - `boolean()` - Creates a boolean schema (accepts 'true'/'false')
11
+ * - `enum(values)` - Creates an enum schema (string array or TypeScript enum)
12
+ *
13
+ * @example
14
+ * ```typescript
15
+ * // string environment variable
16
+ * const apiUrl = env('API_URL').string();
17
+ *
18
+ * // number with default
19
+ * const port = env('PORT').number().default(3000);
20
+ *
21
+ * // boolean
22
+ * const debug = env('DEBUG').boolean().default(false);
23
+ *
24
+ * // enum with string array
25
+ * const logLevel = env('LOG_LEVEL').enum(['info', 'debug', 'error']);
26
+ *
27
+ * // enum with TypeScript enum
28
+ * enum Level { INFO = 'info', DEBUG = 'debug' }
29
+ * const level = env('LOG_LEVEL').enum(Level);
30
+ * ```
31
+ */
32
+ export declare const env: (key: string) => {
33
+ string: () => import("zod").ZodString;
34
+ number: () => coerce.ZodCoercedNumber<unknown>;
35
+ boolean: () => coerce.ZodCoercedBoolean<unknown>;
36
+ enum: {
37
+ <const T extends readonly string[]>(values: T): ZodEnum<util.ToEnum<T[number]>>;
38
+ <const T extends Readonly<Record<string, string | number>>>(entries: T): ZodEnum<T>;
39
+ };
40
+ };
41
+ //# sourceMappingURL=env.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"env.d.ts","sourceRoot":"","sources":["../../src/values/env.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,IAAI,EAAE,KAAK,OAAO,EAAgB,MAAM,KAAK,CAAC;AAC5D,OAAO,EAAE,MAAM,EAAyB,MAAM,KAAK,CAAC;AAGpD;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,eAAO,MAAM,GAAG,GAAI,KAAK,MAAM;;;;;eA2CC,CAAC,SAAS,SAAS,MAAM,EAAE,UAAU,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC;eAExE,CAAC,6DAA4B,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC;;CAS1E,CAAC"}
@@ -0,0 +1,71 @@
1
+ import { coerce, string, enum as zenum } from 'zod';
2
+ import { _envMetadata } from './env-metadata.js';
3
+ /**
4
+ * Binds a Zod schema to an environment variable.
5
+ *
6
+ * @param key - The name of the environment variable to bind to
7
+ * @returns An object with methods to create different types of schemas:
8
+ * - `string()` - Creates a string schema
9
+ * - `number()` - Creates a number schema (auto-coerced from string)
10
+ * - `boolean()` - Creates a boolean schema (accepts 'true'/'false')
11
+ * - `enum(values)` - Creates an enum schema (string array or TypeScript enum)
12
+ *
13
+ * @example
14
+ * ```typescript
15
+ * // string environment variable
16
+ * const apiUrl = env('API_URL').string();
17
+ *
18
+ * // number with default
19
+ * const port = env('PORT').number().default(3000);
20
+ *
21
+ * // boolean
22
+ * const debug = env('DEBUG').boolean().default(false);
23
+ *
24
+ * // enum with string array
25
+ * const logLevel = env('LOG_LEVEL').enum(['info', 'debug', 'error']);
26
+ *
27
+ * // enum with TypeScript enum
28
+ * enum Level { INFO = 'info', DEBUG = 'debug' }
29
+ * const level = env('LOG_LEVEL').enum(Level);
30
+ * ```
31
+ */
32
+ export const env = (key) => {
33
+ const wrap = (schema, envKey, envType) => {
34
+ // create a proxy to preserve metadata through method chaining
35
+ const proxied = new Proxy(schema, {
36
+ get(target, prop) {
37
+ const value = target[prop];
38
+ // if it's a method, wrap the result to preserve metadata
39
+ // e.g. .default(), .optional(), etc.
40
+ if (typeof value === 'function') {
41
+ return (...args) => {
42
+ // call original method
43
+ const result = value.apply(target, args);
44
+ if (result && typeof result === 'object' && result._def) {
45
+ // if result is a new chained Zod schema, wrap it in a new proxy
46
+ return wrap(result, envKey, envType);
47
+ }
48
+ return result;
49
+ };
50
+ }
51
+ // regular property, just return it
52
+ return value;
53
+ },
54
+ });
55
+ // remember metadata for the proxied object
56
+ _envMetadata.set(proxied, { key: envKey, type: envType });
57
+ return proxied;
58
+ };
59
+ return {
60
+ string: () => wrap(string(), key, 'string'),
61
+ number: () => wrap(coerce.number(), key, 'number'),
62
+ boolean: () => wrap(coerce.boolean(), key, 'boolean'),
63
+ enum: (() => {
64
+ // implementation
65
+ function enumMethod(values) {
66
+ return wrap(zenum(values), key, 'enum');
67
+ }
68
+ return enumMethod;
69
+ })(),
70
+ };
71
+ };
@@ -0,0 +1 @@
1
+ {"version":3,"file":"env.js","sourceRoot":"","sources":["../../src/values/env.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,IAAI,KAAK,EAAE,MAAM,KAAK,CAAC;AACpD,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AA+BjD,MAAM,CAAC,MAAM,GAAG,GAAG,CAAC,GAAW,EAAE,EAAE;IACjC,MAAM,IAAI,GAAG,CAAoB,MAAS,EAAE,MAAc,EAAE,OAAe,EAAK,EAAE;QAEhF,MAAM,OAAO,GAAG,IAAI,KAAK,CAAC,MAAM,EAAE;YAChC,GAAG,CAAC,MAAM,EAAE,IAAI;gBACd,MAAM,KAAK,GAAI,MAAc,CAAC,IAAI,CAAC,CAAC;gBAIpC,IAAI,OAAO,KAAK,KAAK,UAAU,EAAE,CAAC;oBAChC,OAAO,CAAC,GAAG,IAAW,EAAE,EAAE;wBAExB,MAAM,MAAM,GAAG,KAAK,CAAC,KAAK,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;wBAEzC,IAAI,MAAM,IAAI,OAAO,MAAM,KAAK,QAAQ,IAAI,MAAM,CAAC,IAAI,EAAE,CAAC;4BAExD,OAAO,IAAI,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC,CAAC;wBACvC,CAAC;wBAED,OAAO,MAAM,CAAC;oBAChB,CAAC,CAAC;gBACJ,CAAC;gBAGD,OAAO,KAAK,CAAC;YACf,CAAC;SACF,CAAM,CAAC;QAGR,YAAY,CAAC,GAAG,CAAC,OAAO,EAAE,EAAE,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC,CAAC;QAE1D,OAAO,OAAO,CAAC;IACjB,CAAC,CAAC;IAEF,OAAO;QACL,MAAM,EAAE,GAAG,EAAE,CAAC,IAAI,CAAC,MAAM,EAAE,EAAE,GAAG,EAAE,QAAQ,CAAC;QAC3C,MAAM,EAAE,GAAG,EAAE,CAAC,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE,EAAE,GAAG,EAAE,QAAQ,CAAC;QAClD,OAAO,EAAE,GAAG,EAAE,CAAC,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,EAAE,GAAG,EAAE,SAAS,CAAC;QACrD,IAAI,EAAE,CAAC,GAAG,EAAE;YASV,SAAS,UAAU,CAAC,MAAW;gBAC7B,OAAO,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,EAAE,GAAG,EAAE,MAAM,CAAC,CAAC;YAC1C,CAAC;YAED,OAAO,UAAU,CAAC;QACpB,CAAC,CAAC,EAAE;KACL,CAAC;AACJ,CAAC,CAAC"}
@@ -0,0 +1,34 @@
1
+ import { type ZodObject } from 'zod';
2
+ import { type ZodRawShape } from 'zod';
3
+ /**
4
+ * Creates a nested object schema for grouping related configuration.
5
+ * This is a convenience wrapper around Zod's object() for consistency.
6
+ *
7
+ * @param shape - An object where values are environment bindings or nested objects
8
+ * @returns A Zod object schema
9
+ *
10
+ * @example
11
+ * ```typescript
12
+ * const schema = define({
13
+ * // group related configuration
14
+ * server: object({
15
+ * host: env('SERVER_HOST').string().default('localhost'),
16
+ * port: env('SERVER_PORT').number().default(3000),
17
+ * }),
18
+ *
19
+ * // nested objects
20
+ * database: object({
21
+ * connection: object({
22
+ * url: env('DATABASE_URL').string(),
23
+ * timeout: env('DB_TIMEOUT').number().default(5000),
24
+ * }),
25
+ * pool: object({
26
+ * min: env('DB_POOL_MIN').number().default(1),
27
+ * max: env('DB_POOL_MAX').number().default(10),
28
+ * }),
29
+ * }),
30
+ * });
31
+ * ```
32
+ */
33
+ export declare const object: <T extends ZodRawShape>(shape: T) => ZodObject<T>;
34
+ //# sourceMappingURL=object.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"object.d.ts","sourceRoot":"","sources":["../../src/values/object.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,SAAS,EAAE,MAAM,KAAK,CAAC;AACrC,OAAO,EAAE,KAAK,WAAW,EAAE,MAAM,KAAK,CAAC;AAGvC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,eAAO,MAAM,MAAM,GAAI,CAAC,SAAS,WAAW,EAAE,OAAO,CAAC,KAAG,SAAS,CAAC,CAAC,CAEnE,CAAC"}
@@ -0,0 +1,34 @@
1
+ import { object as zobject } from 'zod';
2
+ /**
3
+ * Creates a nested object schema for grouping related configuration.
4
+ * This is a convenience wrapper around Zod's object() for consistency.
5
+ *
6
+ * @param shape - An object where values are environment bindings or nested objects
7
+ * @returns A Zod object schema
8
+ *
9
+ * @example
10
+ * ```typescript
11
+ * const schema = define({
12
+ * // group related configuration
13
+ * server: object({
14
+ * host: env('SERVER_HOST').string().default('localhost'),
15
+ * port: env('SERVER_PORT').number().default(3000),
16
+ * }),
17
+ *
18
+ * // nested objects
19
+ * database: object({
20
+ * connection: object({
21
+ * url: env('DATABASE_URL').string(),
22
+ * timeout: env('DB_TIMEOUT').number().default(5000),
23
+ * }),
24
+ * pool: object({
25
+ * min: env('DB_POOL_MIN').number().default(1),
26
+ * max: env('DB_POOL_MAX').number().default(10),
27
+ * }),
28
+ * }),
29
+ * });
30
+ * ```
31
+ */
32
+ export const object = (shape) => {
33
+ return zobject(shape);
34
+ };
@@ -0,0 +1 @@
1
+ {"version":3,"file":"object.js","sourceRoot":"","sources":["../../src/values/object.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,MAAM,IAAI,OAAO,EAAE,MAAM,KAAK,CAAC;AAgCxC,MAAM,CAAC,MAAM,MAAM,GAAG,CAAwB,KAAQ,EAAgB,EAAE;IACtE,OAAO,OAAO,CAAC,KAAK,CAAC,CAAC;AACxB,CAAC,CAAC"}
package/package.json ADDED
@@ -0,0 +1,80 @@
1
+ {
2
+ "name": "zod-conf",
3
+ "version": "1.0.0",
4
+ "description": "Type-safe configuration management with Zod schemas and environment variables",
5
+ "license": "MIT",
6
+ "author": "smnbbrv",
7
+ "type": "module",
8
+ "main": "./dist/index.js",
9
+ "module": "./dist/index.js",
10
+ "types": "./dist/index.d.ts",
11
+ "exports": {
12
+ ".": {
13
+ "types": "./dist/index.d.ts",
14
+ "import": "./dist/index.js"
15
+ }
16
+ },
17
+ "files": [
18
+ "dist",
19
+ "README.md",
20
+ "LICENSE"
21
+ ],
22
+ "keywords": [
23
+ "zod",
24
+ "config",
25
+ "configuration",
26
+ "env",
27
+ "environment",
28
+ "schema",
29
+ "validation",
30
+ "typescript"
31
+ ],
32
+ "repository": {
33
+ "type": "git",
34
+ "url": "git+https://github.com/smnbbrv/zod-conf.git"
35
+ },
36
+ "bugs": {
37
+ "url": "https://github.com/smnbbrv/zod-conf/issues"
38
+ },
39
+ "homepage": "https://github.com/smnbbrv/zod-conf#readme",
40
+ "scripts": {
41
+ "build": "tsc --project tsconfig.build.json",
42
+ "clean": "rm -rf dist",
43
+ "prebuild": "npm run clean",
44
+ "prepublishOnly": "npm run build && npm run test && npm run lint",
45
+ "lint": "run-s lint:*",
46
+ "lint:eslint": "eslint . --report-unused-disable-directives --max-warnings 0",
47
+ "lint:tsc": "tsc",
48
+ "test": "npx tsx --test --experimental-test-coverage src/index.test.ts",
49
+ "prepare": "husky",
50
+ "semantic-release": "semantic-release"
51
+ },
52
+ "publishConfig": {
53
+ "access": "public",
54
+ "provenance": true
55
+ },
56
+ "peerDependencies": {
57
+ "zod": "^4.0.0"
58
+ },
59
+ "devDependencies": {
60
+ "@commitlint/cli": "^19.8.1",
61
+ "@commitlint/config-conventional": "^19.8.1",
62
+ "@semantic-release/changelog": "^6.0.3",
63
+ "@semantic-release/git": "^10.0.1",
64
+ "@types/node": "^24.3.1",
65
+ "@typescript-eslint/eslint-plugin": "^8.38.0",
66
+ "dotenv": "^17.2.2",
67
+ "eslint": "^9.32.0",
68
+ "eslint-config-prettier": "^10.1.8",
69
+ "eslint-import-resolver-typescript": "^4.4.4",
70
+ "eslint-plugin-import": "^2.32.0",
71
+ "eslint-plugin-prettier": "^5.5.3",
72
+ "husky": "^9.1.7",
73
+ "npm-run-all": "^4.1.5",
74
+ "semantic-release": "^24.2.7",
75
+ "tsx": "^4.20.5",
76
+ "typescript": "^5.9.2",
77
+ "typescript-eslint": "^8.38.0",
78
+ "zod": "^4.0.13"
79
+ }
80
+ }