envzod 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/CHANGELOG.md ADDED
@@ -0,0 +1,11 @@
1
+ # Changelog
2
+
3
+ ## 1.0.0 — Initial Release
4
+
5
+ - `createEnv(schema, options?)` — validate environment variables against a Zod schema
6
+ - Full TypeScript inference — types flow from schema automatically
7
+ - Pretty box-style error output with field names, messages, and received values
8
+ - `verbose` mode — logs success summary in development
9
+ - `onError` hook — custom error handling (Sentry, logging, etc.)
10
+ - `source` option — custom env source instead of `process.env`
11
+ - Dual CJS + ESM build with TypeScript declarations
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024 Sridhar-C-25
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,199 @@
1
+ # envzod
2
+
3
+ Universal, type-safe environment variable validation powered by [Zod](https://zod.dev).
4
+
5
+ Works with **Next.js, Express, Fastify, Remix, Bun** — any Node.js project.
6
+
7
+ ---
8
+
9
+ ## Why
10
+
11
+ - `process.env` values are all `string | undefined` — no types, no validation
12
+ - Errors surface at runtime deep in your app instead of at startup
13
+ - `zod-env` validates your env at boot and gives you a **fully typed object** — no casting needed
14
+
15
+ ---
16
+
17
+ ## Install
18
+
19
+ ```bash
20
+ npm install envzod
21
+ # or
22
+ pnpm add envzod
23
+ # or
24
+ yarn add envzod
25
+ ```
26
+
27
+ > `zod` is a peer dependency and is installed automatically (npm 7+, pnpm, yarn).
28
+
29
+ ---
30
+
31
+ ## Basic Usage
32
+
33
+ ```ts
34
+ // env.ts
35
+ import { createEnv } from 'envzod'
36
+ import { z } from 'zod'
37
+
38
+ export const env = createEnv({
39
+ DATABASE_URL: z.string().url(),
40
+ PORT: z.coerce.number().default(3000),
41
+ NODE_ENV: z.enum(['development', 'test', 'production']),
42
+ JWT_SECRET: z.string().min(32),
43
+ })
44
+
45
+ // Fully typed — no casting needed
46
+ env.PORT // number
47
+ env.DATABASE_URL // string
48
+ env.NODE_ENV // "development" | "test" | "production"
49
+ ```
50
+
51
+ ---
52
+
53
+ ## Error Output
54
+
55
+ When validation fails, `zod-env` prints a clear, readable error and throws:
56
+
57
+ ```
58
+ ╔════════════════════════════════════════════╗
59
+ ║ zod-env: Invalid Environment ║
60
+ ╚════════════════════════════════════════════╝
61
+
62
+ ✗ DATABASE_URL
63
+ Invalid url
64
+ Got: "localhost/mydb"
65
+
66
+ ✗ JWT_SECRET
67
+ String must contain at least 32 character(s)
68
+ Got: "tooshort"
69
+
70
+ ✗ NODE_ENV
71
+ Invalid enum value. Expected 'development' | 'test' | 'production'
72
+ Got: "prod"
73
+
74
+ Fix the above and restart your server.
75
+ ```
76
+
77
+ ---
78
+
79
+ ## Options
80
+
81
+ ```ts
82
+ const env = createEnv(schema, {
83
+ // Custom env source. Defaults to process.env
84
+ source: myCustomObject,
85
+
86
+ // Log "✅ zod-env: N variables validated" on success (good for dev)
87
+ verbose: true,
88
+
89
+ // Called with structured errors before throwing — use for Sentry, logging, etc.
90
+ onError: (errors) => {
91
+ Sentry.captureException(new Error('Invalid env'), { extra: { errors } })
92
+ },
93
+ })
94
+ ```
95
+
96
+ ### `onError` signature
97
+
98
+ ```ts
99
+ type EnvValidationError = {
100
+ field: string
101
+ message: string
102
+ received: string | undefined
103
+ }
104
+ ```
105
+
106
+ ---
107
+
108
+ ## Framework Examples
109
+
110
+ ### Next.js (App Router)
111
+
112
+ ```ts
113
+ // src/env.ts
114
+ import { createEnv } from 'envzod'
115
+ import { z } from 'zod'
116
+
117
+ export const env = createEnv({
118
+ DATABASE_URL: z.string().url(),
119
+ JWT_SECRET: z.string().min(32),
120
+ NODE_ENV: z.enum(['development', 'test', 'production']).default('development'),
121
+ NEXT_PUBLIC_API_URL: z.string().url(),
122
+ }, {
123
+ verbose: process.env.NODE_ENV === 'development',
124
+ })
125
+ ```
126
+
127
+ ### Express
128
+
129
+ ```ts
130
+ // src/env.ts
131
+ import { createEnv } from 'envzod'
132
+ import { z } from 'zod'
133
+
134
+ export const env = createEnv({
135
+ PORT: z.coerce.number().default(3000),
136
+ DATABASE_URL: z.string().url(),
137
+ JWT_SECRET: z.string().min(32),
138
+ }, {
139
+ verbose: process.env.NODE_ENV === 'development',
140
+ })
141
+
142
+ // app.ts
143
+ import { env } from './env'
144
+ app.listen(env.PORT)
145
+ ```
146
+
147
+ ### Bun
148
+
149
+ ```ts
150
+ // env.ts
151
+ import { createEnv } from 'envzod'
152
+ import { z } from 'zod'
153
+
154
+ export const env = createEnv({
155
+ PORT: z.coerce.number().default(3000),
156
+ DATABASE_URL: z.string().url(),
157
+ JWT_SECRET: z.string().min(32),
158
+ }, {
159
+ source: Bun.env,
160
+ verbose: Bun.env.NODE_ENV === 'development',
161
+ })
162
+ ```
163
+
164
+ ---
165
+
166
+ ## TypeScript
167
+
168
+ `zod-env` uses the `InferEnv<T>` utility type to derive the return type from your schema. No manual type annotations needed.
169
+
170
+ ```ts
171
+ import type { InferEnv } from 'envzod'
172
+ import { z } from 'zod'
173
+
174
+ const schema = {
175
+ PORT: z.coerce.number(),
176
+ NODE_ENV: z.enum(['development', 'production']),
177
+ }
178
+
179
+ type Env = InferEnv<typeof schema>
180
+ // { PORT: number; NODE_ENV: "development" | "production" }
181
+ ```
182
+
183
+ ---
184
+
185
+ ## vs t3-env
186
+
187
+ | Feature | zod-env | t3-env |
188
+ |---|---|---|
189
+ | Framework | Universal | Next.js focused |
190
+ | Setup | `createEnv(schema)` | Separate client/server schemas |
191
+ | Dependencies | zod only | Next.js types + more |
192
+ | Bundle | CJS + ESM | ESM only |
193
+ | Error output | Pretty box | Zod default |
194
+
195
+ ---
196
+
197
+ ## License
198
+
199
+ MIT
@@ -0,0 +1,25 @@
1
+ import { ZodTypeAny, z } from 'zod';
2
+
3
+ /** A record of field names to Zod types */
4
+ type EnvSchema = Record<string, ZodTypeAny>;
5
+ /** Infer the output type of an env schema */
6
+ type InferEnv<T extends EnvSchema> = {
7
+ [K in keyof T]: z.infer<T[K]>;
8
+ };
9
+ interface EnvOptions<T extends EnvSchema> {
10
+ /** Custom env source. Defaults to process.env */
11
+ source?: Record<string, string | undefined>;
12
+ /** Log a success summary table in development. Defaults to false */
13
+ verbose?: boolean;
14
+ /** Called with formatted error string before throwing. Use for custom logging/alerting */
15
+ onError?: (errors: EnvValidationError[]) => void;
16
+ }
17
+ interface EnvValidationError {
18
+ field: string;
19
+ message: string;
20
+ received: string | undefined;
21
+ }
22
+
23
+ declare function createEnv<T extends EnvSchema>(schema: T, options?: EnvOptions<T>): InferEnv<T>;
24
+
25
+ export { type EnvOptions, type EnvSchema, type EnvValidationError, type InferEnv, createEnv };
@@ -0,0 +1,25 @@
1
+ import { ZodTypeAny, z } from 'zod';
2
+
3
+ /** A record of field names to Zod types */
4
+ type EnvSchema = Record<string, ZodTypeAny>;
5
+ /** Infer the output type of an env schema */
6
+ type InferEnv<T extends EnvSchema> = {
7
+ [K in keyof T]: z.infer<T[K]>;
8
+ };
9
+ interface EnvOptions<T extends EnvSchema> {
10
+ /** Custom env source. Defaults to process.env */
11
+ source?: Record<string, string | undefined>;
12
+ /** Log a success summary table in development. Defaults to false */
13
+ verbose?: boolean;
14
+ /** Called with formatted error string before throwing. Use for custom logging/alerting */
15
+ onError?: (errors: EnvValidationError[]) => void;
16
+ }
17
+ interface EnvValidationError {
18
+ field: string;
19
+ message: string;
20
+ received: string | undefined;
21
+ }
22
+
23
+ declare function createEnv<T extends EnvSchema>(schema: T, options?: EnvOptions<T>): InferEnv<T>;
24
+
25
+ export { type EnvOptions, type EnvSchema, type EnvValidationError, type InferEnv, createEnv };
package/dist/index.js ADDED
@@ -0,0 +1,70 @@
1
+ 'use strict';
2
+
3
+ var zod = require('zod');
4
+
5
+ // src/format.ts
6
+ var BOX_WIDTH = 44;
7
+ function pad(text, width) {
8
+ return text + " ".repeat(Math.max(0, width - text.length));
9
+ }
10
+ function formatErrors(errors) {
11
+ const lines = [];
12
+ const title = " zod-env: Invalid Environment ";
13
+ lines.push(`\u2554${"\u2550".repeat(BOX_WIDTH)}\u2557`);
14
+ lines.push(`\u2551${pad(title, BOX_WIDTH)}\u2551`);
15
+ lines.push(`\u255A${"\u2550".repeat(BOX_WIDTH)}\u255D`);
16
+ lines.push("");
17
+ for (const err of errors) {
18
+ lines.push(` \u2717 ${err.field}`);
19
+ lines.push(` ${err.message}`);
20
+ if (err.received !== void 0) {
21
+ lines.push(` Got: "${err.received}"`);
22
+ }
23
+ lines.push("");
24
+ }
25
+ lines.push(" Fix the above and restart your server.");
26
+ return lines.join("\n");
27
+ }
28
+ function formatSuccess(count) {
29
+ return `\u2705 zod-env: ${count} variable${count === 1 ? "" : "s"} validated`;
30
+ }
31
+ function validate(schema, source) {
32
+ const shape = {};
33
+ for (const key of Object.keys(schema)) {
34
+ shape[key] = schema[key];
35
+ }
36
+ const zodObject = zod.z.object(shape);
37
+ const result = zodObject.safeParse(source);
38
+ if (result.success) {
39
+ return { success: true, data: result.data };
40
+ }
41
+ const errors = result.error.issues.map((issue) => {
42
+ const field = issue.path[0]?.toString() ?? "unknown";
43
+ return {
44
+ field,
45
+ message: issue.message,
46
+ received: source[field]
47
+ };
48
+ });
49
+ return { success: false, errors };
50
+ }
51
+
52
+ // src/index.ts
53
+ function createEnv(schema, options) {
54
+ const source = options?.source ?? process.env;
55
+ const result = validate(schema, source);
56
+ if (!result.success) {
57
+ const formatted = formatErrors(result.errors);
58
+ console.error(formatted);
59
+ options?.onError?.(result.errors);
60
+ throw new Error("zod-env: environment validation failed");
61
+ }
62
+ if (options?.verbose) {
63
+ console.log(formatSuccess(Object.keys(schema).length));
64
+ }
65
+ return result.data;
66
+ }
67
+
68
+ exports.createEnv = createEnv;
69
+ //# sourceMappingURL=index.js.map
70
+ //# sourceMappingURL=index.js.map
package/dist/index.mjs ADDED
@@ -0,0 +1,68 @@
1
+ import { z } from 'zod';
2
+
3
+ // src/format.ts
4
+ var BOX_WIDTH = 44;
5
+ function pad(text, width) {
6
+ return text + " ".repeat(Math.max(0, width - text.length));
7
+ }
8
+ function formatErrors(errors) {
9
+ const lines = [];
10
+ const title = " zod-env: Invalid Environment ";
11
+ lines.push(`\u2554${"\u2550".repeat(BOX_WIDTH)}\u2557`);
12
+ lines.push(`\u2551${pad(title, BOX_WIDTH)}\u2551`);
13
+ lines.push(`\u255A${"\u2550".repeat(BOX_WIDTH)}\u255D`);
14
+ lines.push("");
15
+ for (const err of errors) {
16
+ lines.push(` \u2717 ${err.field}`);
17
+ lines.push(` ${err.message}`);
18
+ if (err.received !== void 0) {
19
+ lines.push(` Got: "${err.received}"`);
20
+ }
21
+ lines.push("");
22
+ }
23
+ lines.push(" Fix the above and restart your server.");
24
+ return lines.join("\n");
25
+ }
26
+ function formatSuccess(count) {
27
+ return `\u2705 zod-env: ${count} variable${count === 1 ? "" : "s"} validated`;
28
+ }
29
+ function validate(schema, source) {
30
+ const shape = {};
31
+ for (const key of Object.keys(schema)) {
32
+ shape[key] = schema[key];
33
+ }
34
+ const zodObject = z.object(shape);
35
+ const result = zodObject.safeParse(source);
36
+ if (result.success) {
37
+ return { success: true, data: result.data };
38
+ }
39
+ const errors = result.error.issues.map((issue) => {
40
+ const field = issue.path[0]?.toString() ?? "unknown";
41
+ return {
42
+ field,
43
+ message: issue.message,
44
+ received: source[field]
45
+ };
46
+ });
47
+ return { success: false, errors };
48
+ }
49
+
50
+ // src/index.ts
51
+ function createEnv(schema, options) {
52
+ const source = options?.source ?? process.env;
53
+ const result = validate(schema, source);
54
+ if (!result.success) {
55
+ const formatted = formatErrors(result.errors);
56
+ console.error(formatted);
57
+ options?.onError?.(result.errors);
58
+ throw new Error("zod-env: environment validation failed");
59
+ }
60
+ if (options?.verbose) {
61
+ console.log(formatSuccess(Object.keys(schema).length));
62
+ }
63
+ return result.data;
64
+ }
65
+
66
+ export { createEnv };
67
+ //# sourceMappingURL=index.mjs.map
68
+ //# sourceMappingURL=index.mjs.map
package/package.json ADDED
@@ -0,0 +1,74 @@
1
+ {
2
+ "name": "envzod",
3
+ "version": "1.0.0",
4
+ "description": "Universal, type-safe environment variable validation powered by Zod. Zero config, works everywhere.",
5
+ "main": "./dist/index.js",
6
+ "module": "./dist/index.mjs",
7
+ "types": "./dist/index.d.ts",
8
+ "sideEffects": false,
9
+ "exports": {
10
+ ".": {
11
+ "types": "./dist/index.d.ts",
12
+ "import": "./dist/index.mjs",
13
+ "require": "./dist/index.js"
14
+ }
15
+ },
16
+ "files": [
17
+ "dist/index.js",
18
+ "dist/index.mjs",
19
+ "dist/index.d.ts",
20
+ "dist/index.d.mts",
21
+ "LICENSE",
22
+ "CHANGELOG.md"
23
+ ],
24
+ "scripts": {
25
+ "build": "tsup",
26
+ "dev": "tsup --watch",
27
+ "test": "vitest run",
28
+ "test:watch": "vitest",
29
+ "typecheck": "tsc --noEmit",
30
+ "prepublishOnly": "npm run typecheck && npm test && npm run build"
31
+ },
32
+ "engines": {
33
+ "node": ">=16.0.0"
34
+ },
35
+ "peerDependencies": {
36
+ "zod": ">=3.0.0"
37
+ },
38
+ "peerDependenciesMeta": {
39
+ "zod": {
40
+ "optional": false
41
+ }
42
+ },
43
+ "devDependencies": {
44
+ "@types/node": "^20.0.0",
45
+ "tsup": "^8.0.0",
46
+ "typescript": "^5.0.0",
47
+ "vitest": "^1.0.0",
48
+ "zod": "^3.22.0"
49
+ },
50
+ "keywords": [
51
+ "zod",
52
+ "env",
53
+ "environment",
54
+ "validation",
55
+ "typescript",
56
+ "nodejs",
57
+ "nextjs",
58
+ "process.env",
59
+ "env-validation",
60
+ "type-safe",
61
+ "dotenv",
62
+ "schema"
63
+ ],
64
+ "author": "Sridhar-C-25",
65
+ "license": "MIT",
66
+ "repository": {
67
+ "type": "git",
68
+ "url": "https://github.com/Sridhar-C-25/zod-env"
69
+ },
70
+ "bugs": {
71
+ "url": "https://github.com/Sridhar-C-25/zod-env/issues"
72
+ },
73
+ "homepage": "https://github.com/Sridhar-C-25/zod-env#readme"
74
+ }