@entwico/zod-conf 1.1.2
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 +21 -0
- package/README.md +261 -0
- package/dist/index.d.ts +21 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +15 -0
- package/dist/values/define.d.ts +23 -0
- package/dist/values/define.d.ts.map +1 -0
- package/dist/values/define.js +101 -0
- package/dist/values/env-metadata.d.ts +6 -0
- package/dist/values/env-metadata.d.ts.map +1 -0
- package/dist/values/env-metadata.js +1 -0
- package/dist/values/env.d.ts +12 -0
- package/dist/values/env.d.ts.map +1 -0
- package/dist/values/env.js +34 -0
- package/dist/values/object.d.ts +4 -0
- package/dist/values/object.d.ts.map +1 -0
- package/dist/values/object.js +4 -0
- package/package.json +76 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025-2026 entwico GmbH
|
|
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,261 @@
|
|
|
1
|
+
# zod-conf
|
|
2
|
+
|
|
3
|
+
Type-safe configuration management with Zod schemas and environment variables.
|
|
4
|
+
|
|
5
|
+
Not affiliated with the official Zod library.
|
|
6
|
+
|
|
7
|
+
## Features
|
|
8
|
+
|
|
9
|
+
- **Type-safe** - Full TypeScript support with Zod schema validation
|
|
10
|
+
- **Multi-source** - Load from env vars, config files (e.g. YAML), or any plain object
|
|
11
|
+
- **Env-first** - Bind schema fields directly to environment variables
|
|
12
|
+
- **Simple API** - Intuitive, Zod-native API that feels familiar
|
|
13
|
+
- **Zero compromise** - All Zod features work - transformations, refinements, and more
|
|
14
|
+
- **Lightweight** - Only Zod as peer dependency, no extra bloat
|
|
15
|
+
- **Universal** - Works with process.env, dotenv, or any environment loader
|
|
16
|
+
|
|
17
|
+
## Installation
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
npm install @entwico/zod-conf zod
|
|
21
|
+
# or
|
|
22
|
+
yarn add @entwico/zod-conf zod
|
|
23
|
+
# or
|
|
24
|
+
pnpm add @entwico/zod-conf zod
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## Quick Start
|
|
28
|
+
|
|
29
|
+
```typescript
|
|
30
|
+
import zc from '@entwico/zod-conf';
|
|
31
|
+
|
|
32
|
+
// define your configuration schema
|
|
33
|
+
const schema = zc.define({
|
|
34
|
+
port: zc.env('PORT').number().default(3000),
|
|
35
|
+
host: zc.env('HOST').string().default('localhost'),
|
|
36
|
+
debug: zc.env('DEBUG').boolean().default(false),
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
// load configuration from environment
|
|
40
|
+
const config = schema.load({
|
|
41
|
+
env: process.env,
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
console.log(config);
|
|
45
|
+
// { port: 3000, host: 'localhost', debug: false }
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
## Usage
|
|
49
|
+
|
|
50
|
+
### Basic Types
|
|
51
|
+
|
|
52
|
+
```typescript
|
|
53
|
+
const schema = zc.define({
|
|
54
|
+
// string
|
|
55
|
+
apiUrl: zc.env('API_URL').string(),
|
|
56
|
+
|
|
57
|
+
// number
|
|
58
|
+
port: zc.env('PORT').number(),
|
|
59
|
+
|
|
60
|
+
// boolean
|
|
61
|
+
isProduction: zc.env('NODE_ENV').boolean(),
|
|
62
|
+
|
|
63
|
+
// enum
|
|
64
|
+
logLevel: zc.env('LOG_LEVEL').enum(['debug', 'info', 'warn', 'error']),
|
|
65
|
+
});
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
### Defaults and Optional Values
|
|
69
|
+
|
|
70
|
+
```typescript
|
|
71
|
+
const schema = zc.define({
|
|
72
|
+
// with default value
|
|
73
|
+
port: zc.env('PORT').number().default(3000),
|
|
74
|
+
|
|
75
|
+
// optional field
|
|
76
|
+
apiKey: zc.env('API_KEY').string().optional(),
|
|
77
|
+
|
|
78
|
+
// nullable field
|
|
79
|
+
proxyUrl: zc.env('PROXY_URL').string().nullable(),
|
|
80
|
+
});
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
### Nested Objects
|
|
84
|
+
|
|
85
|
+
```typescript
|
|
86
|
+
const schema = zc.define({
|
|
87
|
+
server: zc.object({
|
|
88
|
+
host: zc.env('SERVER_HOST').string().default('localhost'),
|
|
89
|
+
port: zc.env('SERVER_PORT').number().default(3000),
|
|
90
|
+
}),
|
|
91
|
+
database: zc.object({
|
|
92
|
+
url: zc.env('DATABASE_URL').string(),
|
|
93
|
+
pool: zc.object({
|
|
94
|
+
min: zc.env('DB_POOL_MIN').number().default(1),
|
|
95
|
+
max: zc.env('DB_POOL_MAX').number().default(10),
|
|
96
|
+
}),
|
|
97
|
+
}),
|
|
98
|
+
});
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
### TypeScript Enums
|
|
102
|
+
|
|
103
|
+
```typescript
|
|
104
|
+
enum LogLevel {
|
|
105
|
+
DEBUG = 'debug',
|
|
106
|
+
INFO = 'info',
|
|
107
|
+
WARN = 'warn',
|
|
108
|
+
ERROR = 'error',
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
const schema = zc.define({
|
|
112
|
+
logLevel: zc.env('LOG_LEVEL').enum(LogLevel).default(LogLevel.INFO),
|
|
113
|
+
});
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
### Loading Configuration
|
|
117
|
+
|
|
118
|
+
```typescript
|
|
119
|
+
// from process.env
|
|
120
|
+
const config = schema.load({ env: process.env });
|
|
121
|
+
|
|
122
|
+
// from .env file (using dotenv)
|
|
123
|
+
import { config as dotenvConfig } from 'dotenv';
|
|
124
|
+
|
|
125
|
+
const env = dotenvConfig();
|
|
126
|
+
const config = schema.load({ env: env.parsed });
|
|
127
|
+
|
|
128
|
+
// from custom source
|
|
129
|
+
const config = schema.load({
|
|
130
|
+
env: {
|
|
131
|
+
PORT: '8080',
|
|
132
|
+
HOST: '0.0.0.0',
|
|
133
|
+
},
|
|
134
|
+
});
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
### Multiple Configuration Sources
|
|
138
|
+
|
|
139
|
+
You can load from multiple sources. Loaders are processed left-to-right; later loaders override earlier ones.
|
|
140
|
+
|
|
141
|
+
```typescript
|
|
142
|
+
import { parse } from 'yaml';
|
|
143
|
+
import { readFileSync } from 'fs';
|
|
144
|
+
|
|
145
|
+
const yamlConfig = parse(readFileSync('config.yml', 'utf8'));
|
|
146
|
+
|
|
147
|
+
// yaml values are used as base, env vars override
|
|
148
|
+
const config = schema.load({ values: yamlConfig }, { env: process.env });
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
You can also layer multiple objects for defaults:
|
|
152
|
+
|
|
153
|
+
```typescript
|
|
154
|
+
const defaults = { port: 3000, host: 'localhost' };
|
|
155
|
+
const yamlConfig = parse(readFileSync('config.yml', 'utf8'));
|
|
156
|
+
|
|
157
|
+
const config = schema.load({ values: defaults }, { values: yamlConfig }, { env: process.env });
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
The `values` loader maps values by property name and passes them directly to Zod — no string coercion is needed since YAML/JSON already preserves types. This also means you can use types that env vars can't represent, like arrays:
|
|
161
|
+
|
|
162
|
+
```typescript
|
|
163
|
+
import { z } from 'zod';
|
|
164
|
+
|
|
165
|
+
const schema = zc.define({
|
|
166
|
+
host: zc.env('HOST').string().default('localhost'),
|
|
167
|
+
port: zc.env('PORT').number().default(3000),
|
|
168
|
+
allowedOrigins: z.array(z.string()),
|
|
169
|
+
servers: z.array(z.object({ host: z.string(), port: z.number() })),
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
// arrays come from yaml, scalar fields can be overridden by env
|
|
173
|
+
const config = schema.load({ values: yamlConfig }, { env: process.env });
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
### Error Handling
|
|
177
|
+
|
|
178
|
+
The error-handling is similar to Zod's standard behavior:
|
|
179
|
+
|
|
180
|
+
```typescript
|
|
181
|
+
// throws on validation error
|
|
182
|
+
try {
|
|
183
|
+
const config = schema.load({ env: process.env });
|
|
184
|
+
} catch (error) {
|
|
185
|
+
console.error('Configuration validation failed:', error);
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
// safe parsing without throwing
|
|
189
|
+
const result = schema.safeLoad({ env: process.env });
|
|
190
|
+
if (result.success) {
|
|
191
|
+
console.log('Config:', result.data);
|
|
192
|
+
} else {
|
|
193
|
+
console.error('Validation errors:', result.error);
|
|
194
|
+
}
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
### Advanced: Transformations
|
|
198
|
+
|
|
199
|
+
All Zod transformations work seamlessly:
|
|
200
|
+
|
|
201
|
+
```typescript
|
|
202
|
+
const schema = zc.define({
|
|
203
|
+
port: zc
|
|
204
|
+
.env('PORT')
|
|
205
|
+
.number()
|
|
206
|
+
.default(3000)
|
|
207
|
+
.transform((p) => Math.max(1024, p)), // Ensure port is at least 1024
|
|
208
|
+
|
|
209
|
+
apiUrl: zc
|
|
210
|
+
.env('API_URL')
|
|
211
|
+
.string()
|
|
212
|
+
.transform((url) => url.replace(/\/$/, '')), // Remove trailing slash
|
|
213
|
+
});
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
## API Reference
|
|
217
|
+
|
|
218
|
+
### `zc.define(shape)`
|
|
219
|
+
|
|
220
|
+
Creates a configuration schema with environment variable bindings.
|
|
221
|
+
|
|
222
|
+
- **shape**: An object where each value is created using `zc.env()` or `zc.object()`
|
|
223
|
+
- **returns**: A `ZodConfSchema` instance with `load()` and `safeLoad()` methods
|
|
224
|
+
|
|
225
|
+
### `zc.env(key)`
|
|
226
|
+
|
|
227
|
+
Binds a schema field to an environment variable.
|
|
228
|
+
|
|
229
|
+
- **key**: The environment variable name
|
|
230
|
+
- **returns**: An object with methods for different types:
|
|
231
|
+
- `.string()` - String value
|
|
232
|
+
- `.number()` - Numeric value (auto-converted)
|
|
233
|
+
- `.boolean()` - Boolean value (accepts 'true'/'false')
|
|
234
|
+
- `.enum(values)` - Enum value (string array or TypeScript enum)
|
|
235
|
+
|
|
236
|
+
### `zc.object(shape)`
|
|
237
|
+
|
|
238
|
+
Creates a nested object schema.
|
|
239
|
+
|
|
240
|
+
- **shape**: An object where values are `zc.env()` or nested `zc.object()` calls
|
|
241
|
+
- **returns**: A Zod object schema
|
|
242
|
+
|
|
243
|
+
### `schema.load(...loaders)`
|
|
244
|
+
|
|
245
|
+
Loads and validates configuration from one or more loaders.
|
|
246
|
+
|
|
247
|
+
- **loaders**: One or more loader objects, processed left-to-right (later overrides earlier)
|
|
248
|
+
- `{ env: Record<string, string | undefined> }` - Load from environment variables
|
|
249
|
+
- `{ values: Record<string, unknown> }` - Load from a plain object (e.g. parsed YAML/JSON)
|
|
250
|
+
- **returns**: Validated configuration object
|
|
251
|
+
- **throws**: ZodError if validation fails
|
|
252
|
+
|
|
253
|
+
### `schema.safeLoad(...loaders)`
|
|
254
|
+
|
|
255
|
+
Safely loads configuration without throwing. Same loader arguments as `load()`.
|
|
256
|
+
|
|
257
|
+
- **returns**: `{ success: true, data: T }` or `{ success: false, error: ZodError }`
|
|
258
|
+
|
|
259
|
+
## License
|
|
260
|
+
|
|
261
|
+
MIT
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { boolean, number, string, enum as zenum } from 'zod';
|
|
2
|
+
export { ZodConfSchema, type Loader, type EnvLoader, type ValuesLoader } from './values/define.js';
|
|
3
|
+
export declare const zc: {
|
|
4
|
+
define: <T extends import("zod").ZodRawShape>(shape: T) => import("./values/define.js").ZodConfSchema<T>;
|
|
5
|
+
env: (key: string) => {
|
|
6
|
+
string: () => import("zod").ZodString;
|
|
7
|
+
number: () => import("zod").ZodCoercedNumber<unknown>;
|
|
8
|
+
boolean: () => import("zod").ZodCoercedBoolean<unknown>;
|
|
9
|
+
enum: {
|
|
10
|
+
<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>;
|
|
11
|
+
<const T extends Readonly<Record<string, string | number>>>(entries: T): import("zod").ZodEnum<T>;
|
|
12
|
+
};
|
|
13
|
+
};
|
|
14
|
+
object: <T extends import("zod").ZodRawShape>(shape: T) => import("zod").ZodObject<T>;
|
|
15
|
+
string: typeof string;
|
|
16
|
+
boolean: typeof boolean;
|
|
17
|
+
number: typeof number;
|
|
18
|
+
enum: typeof zenum;
|
|
19
|
+
};
|
|
20
|
+
export default zc;
|
|
21
|
+
//# 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,OAAO,EAAE,aAAa,EAAE,KAAK,MAAM,EAAE,KAAK,SAAS,EAAE,KAAK,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAEnG,eAAO,MAAM,EAAE;;;;;;;;;;;;;;;;CAWd,CAAC;AAEF,eAAe,EAAE,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
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 { ZodConfSchema } from './values/define.js';
|
|
6
|
+
export const zc = {
|
|
7
|
+
define,
|
|
8
|
+
env,
|
|
9
|
+
object,
|
|
10
|
+
string,
|
|
11
|
+
boolean,
|
|
12
|
+
number,
|
|
13
|
+
enum: zenum,
|
|
14
|
+
};
|
|
15
|
+
export default zc;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { ZodObject, type ZodRawShape, type infer as ZodInfer } from 'zod';
|
|
2
|
+
type Env = Record<string, string | undefined>;
|
|
3
|
+
export type EnvLoader = {
|
|
4
|
+
env: Env;
|
|
5
|
+
values?: never | undefined;
|
|
6
|
+
};
|
|
7
|
+
export type ValuesLoader = {
|
|
8
|
+
values: Record<string, unknown>;
|
|
9
|
+
env?: never | undefined;
|
|
10
|
+
};
|
|
11
|
+
export type Loader = EnvLoader | ValuesLoader;
|
|
12
|
+
export declare class ZodConfSchema<T extends ZodRawShape> {
|
|
13
|
+
private shape;
|
|
14
|
+
private schema;
|
|
15
|
+
constructor(shape: T, schema: ZodObject<T>);
|
|
16
|
+
private resolveEnvValue;
|
|
17
|
+
private loadValue;
|
|
18
|
+
load(...loaders: [Loader, ...Loader[]]): ZodInfer<ZodObject<T>>;
|
|
19
|
+
safeLoad(...loaders: [Loader, ...Loader[]]): import("zod").ZodSafeParseResult<import("zod/v4/core").$InferObjectOutput<T, {}>>;
|
|
20
|
+
}
|
|
21
|
+
export declare const define: <T extends ZodRawShape>(shape: T) => ZodConfSchema<T>;
|
|
22
|
+
export {};
|
|
23
|
+
//# 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,SAAS,GAAG;IAAE,GAAG,EAAE,GAAG,CAAC;IAAC,MAAM,CAAC,EAAE,KAAK,GAAG,SAAS,CAAA;CAAE,CAAC;AACjE,MAAM,MAAM,YAAY,GAAG;IAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAAC,GAAG,CAAC,EAAE,KAAK,GAAG,SAAS,CAAA;CAAE,CAAC;AACxF,MAAM,MAAM,MAAM,GAAG,SAAS,GAAG,YAAY,CAAC;AAE9C,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,eAAe;IA2DvB,OAAO,CAAC,SAAS;IA6DjB,IAAI,CAAC,GAAG,OAAO,EAAE,CAAC,MAAM,EAAE,GAAG,MAAM,EAAE,CAAC,GAAG,QAAQ,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC;IA8B/D,QAAQ,CAAC,GAAG,OAAO,EAAE,CAAC,MAAM,EAAE,GAAG,MAAM,EAAE,CAAC;CAK3C;AA4BD,eAAO,MAAM,MAAM,GAAI,CAAC,SAAS,WAAW,EAAE,OAAO,CAAC,KAAG,aAAa,CAAC,CAAC,CAEvE,CAAC"}
|
|
@@ -0,0 +1,101 @@
|
|
|
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
|
+
resolveEnvValue(schema, env) {
|
|
11
|
+
let currentSchema = schema;
|
|
12
|
+
let envKey;
|
|
13
|
+
let envType;
|
|
14
|
+
while (currentSchema && !envKey) {
|
|
15
|
+
const metadata = _envMetadata.get(currentSchema);
|
|
16
|
+
if (metadata) {
|
|
17
|
+
envKey = metadata.key;
|
|
18
|
+
envType = metadata.type;
|
|
19
|
+
break;
|
|
20
|
+
}
|
|
21
|
+
const def = currentSchema._def;
|
|
22
|
+
if (def?.innerType) {
|
|
23
|
+
currentSchema = def.innerType;
|
|
24
|
+
}
|
|
25
|
+
else if (def?.schema) {
|
|
26
|
+
currentSchema = def.schema;
|
|
27
|
+
}
|
|
28
|
+
else {
|
|
29
|
+
break;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
if (!envKey) {
|
|
33
|
+
return undefined;
|
|
34
|
+
}
|
|
35
|
+
const value = env[envKey];
|
|
36
|
+
switch (envType) {
|
|
37
|
+
case 'string':
|
|
38
|
+
return value;
|
|
39
|
+
case 'number':
|
|
40
|
+
return value ? Number(value) : undefined;
|
|
41
|
+
case 'boolean':
|
|
42
|
+
return value === 'true' ? true : value === 'false' ? false : undefined;
|
|
43
|
+
case 'enum': {
|
|
44
|
+
const numValue = Number(value);
|
|
45
|
+
if (value && !isNaN(numValue) && Number.isInteger(numValue)) {
|
|
46
|
+
return numValue;
|
|
47
|
+
}
|
|
48
|
+
return value;
|
|
49
|
+
}
|
|
50
|
+
default:
|
|
51
|
+
return undefined;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
loadValue(shape, loaders) {
|
|
55
|
+
const input = {};
|
|
56
|
+
for (const key in shape) {
|
|
57
|
+
const schema = shape[key];
|
|
58
|
+
if (schema instanceof ZodObject) {
|
|
59
|
+
const subLoaders = loaders.map((loader) => {
|
|
60
|
+
if ('values' in loader && loader.values) {
|
|
61
|
+
const nested = loader.values[key];
|
|
62
|
+
return { values: nested && typeof nested === 'object' ? nested : {} };
|
|
63
|
+
}
|
|
64
|
+
return loader;
|
|
65
|
+
});
|
|
66
|
+
input[key] = this.loadValue(schema.shape, subLoaders);
|
|
67
|
+
}
|
|
68
|
+
else {
|
|
69
|
+
for (const loader of loaders) {
|
|
70
|
+
if ('values' in loader && loader.values) {
|
|
71
|
+
const value = loader.values[key];
|
|
72
|
+
if (value !== undefined) {
|
|
73
|
+
input[key] = value;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
else if ('env' in loader && loader.env) {
|
|
77
|
+
const value = this.resolveEnvValue(schema, loader.env);
|
|
78
|
+
if (value !== undefined) {
|
|
79
|
+
input[key] = value;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
return input;
|
|
86
|
+
}
|
|
87
|
+
load(...loaders) {
|
|
88
|
+
const result = this.safeLoad(...loaders);
|
|
89
|
+
if (!result.success) {
|
|
90
|
+
throw result.error;
|
|
91
|
+
}
|
|
92
|
+
return result.data;
|
|
93
|
+
}
|
|
94
|
+
safeLoad(...loaders) {
|
|
95
|
+
const value = this.loadValue(this.shape, loaders);
|
|
96
|
+
return this.schema.safeParse(value);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
export const define = (shape) => {
|
|
100
|
+
return new ZodConfSchema(shape, object(shape));
|
|
101
|
+
};
|
|
@@ -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 @@
|
|
|
1
|
+
export const _envMetadata = new WeakMap();
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { type util, type ZodEnum } from 'zod';
|
|
2
|
+
import { coerce } from 'zod';
|
|
3
|
+
export declare const env: (key: string) => {
|
|
4
|
+
string: () => import("zod").ZodString;
|
|
5
|
+
number: () => coerce.ZodCoercedNumber<unknown>;
|
|
6
|
+
boolean: () => coerce.ZodCoercedBoolean<unknown>;
|
|
7
|
+
enum: {
|
|
8
|
+
<const T extends readonly string[]>(values: T): ZodEnum<util.ToEnum<T[number]>>;
|
|
9
|
+
<const T extends Readonly<Record<string, string | number>>>(entries: T): ZodEnum<T>;
|
|
10
|
+
};
|
|
11
|
+
};
|
|
12
|
+
//# 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;AAgCpD,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,34 @@
|
|
|
1
|
+
import { coerce, string, enum as zenum } from 'zod';
|
|
2
|
+
import { _envMetadata } from './env-metadata.js';
|
|
3
|
+
export const env = (key) => {
|
|
4
|
+
const wrap = (schema, envKey, envType) => {
|
|
5
|
+
const proxied = new Proxy(schema, {
|
|
6
|
+
get(target, prop) {
|
|
7
|
+
const value = target[prop];
|
|
8
|
+
if (typeof value === 'function') {
|
|
9
|
+
return (...args) => {
|
|
10
|
+
const result = value.apply(target, args);
|
|
11
|
+
if (result && typeof result === 'object' && result._def) {
|
|
12
|
+
return wrap(result, envKey, envType);
|
|
13
|
+
}
|
|
14
|
+
return result;
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
return value;
|
|
18
|
+
},
|
|
19
|
+
});
|
|
20
|
+
_envMetadata.set(proxied, { key: envKey, type: envType });
|
|
21
|
+
return proxied;
|
|
22
|
+
};
|
|
23
|
+
return {
|
|
24
|
+
string: () => wrap(string(), key, 'string'),
|
|
25
|
+
number: () => wrap(coerce.number(), key, 'number'),
|
|
26
|
+
boolean: () => wrap(coerce.boolean(), key, 'boolean'),
|
|
27
|
+
enum: (() => {
|
|
28
|
+
function enumMethod(values) {
|
|
29
|
+
return wrap(zenum(values), key, 'enum');
|
|
30
|
+
}
|
|
31
|
+
return enumMethod;
|
|
32
|
+
})(),
|
|
33
|
+
};
|
|
34
|
+
};
|
|
@@ -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;AAiCvC,eAAO,MAAM,MAAM,GAAI,CAAC,SAAS,WAAW,EAAE,OAAO,CAAC,KAAG,SAAS,CAAC,CAAC,CAEnE,CAAC"}
|
package/package.json
ADDED
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@entwico/zod-conf",
|
|
3
|
+
"version": "1.1.2",
|
|
4
|
+
"description": "Type-safe configuration management with Zod schemas and environment variables",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"author": "admin@entwico.com",
|
|
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": "https://github.com/entwico/zod-conf"
|
|
35
|
+
},
|
|
36
|
+
"scripts": {
|
|
37
|
+
"build": "tsc --project tsconfig.build.json",
|
|
38
|
+
"clean": "rm -rf dist",
|
|
39
|
+
"prebuild": "npm run clean",
|
|
40
|
+
"prepublishOnly": "npm run build && npm run test && npm run lint",
|
|
41
|
+
"validate": "run-s lint typecheck test",
|
|
42
|
+
"lint": "eslint . --report-unused-disable-directives --max-warnings 0",
|
|
43
|
+
"typecheck": "tsc",
|
|
44
|
+
"test": "npx tsx --test --experimental-test-coverage src/index.test.ts",
|
|
45
|
+
"prepare": "husky",
|
|
46
|
+
"semantic-release": "semantic-release"
|
|
47
|
+
},
|
|
48
|
+
"publishConfig": {
|
|
49
|
+
"access": "public",
|
|
50
|
+
"provenance": true
|
|
51
|
+
},
|
|
52
|
+
"peerDependencies": {
|
|
53
|
+
"zod": "^4.0.0"
|
|
54
|
+
},
|
|
55
|
+
"devDependencies": {
|
|
56
|
+
"@commitlint/cli": "^20.4.1",
|
|
57
|
+
"@commitlint/config-conventional": "^20.4.1",
|
|
58
|
+
"@semantic-release/changelog": "^6.0.3",
|
|
59
|
+
"@semantic-release/git": "^10.0.1",
|
|
60
|
+
"@types/node": "^25.2.0",
|
|
61
|
+
"@typescript-eslint/eslint-plugin": "^8.54.0",
|
|
62
|
+
"dotenv": "^17.2.3",
|
|
63
|
+
"eslint": "^9.39.2",
|
|
64
|
+
"eslint-config-prettier": "^10.1.8",
|
|
65
|
+
"eslint-import-resolver-typescript": "^4.4.4",
|
|
66
|
+
"eslint-plugin-import": "^2.32.0",
|
|
67
|
+
"eslint-plugin-prettier": "^5.5.5",
|
|
68
|
+
"husky": "^9.1.7",
|
|
69
|
+
"npm-run-all": "^4.1.5",
|
|
70
|
+
"semantic-release": "^25.0.3",
|
|
71
|
+
"tsx": "^4.21.0",
|
|
72
|
+
"typescript": "^5.9.3",
|
|
73
|
+
"typescript-eslint": "^8.54.0",
|
|
74
|
+
"zod": "^4.3.6"
|
|
75
|
+
}
|
|
76
|
+
}
|