envzod 1.0.1 → 1.1.1

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 CHANGED
@@ -1,5 +1,35 @@
1
1
  # Changelog
2
2
 
3
+ ## 1.1.1
4
+
5
+ - Fix branding — error output now correctly shows `envzod` instead of `zod-env`
6
+
7
+ ## 1.1.0
8
+
9
+ ### CLI
10
+ - `npx envzod check` — validate env before deployment, works in CI/CD pipelines
11
+ - `--env` flag — specify custom env file path (default: `.env`)
12
+ - `--config` flag — specify custom config file path (default: `envzod.config`)
13
+ - TypeScript config support — CLI auto-detects `envzod.config.ts` before `.js`
14
+
15
+ ### Next.js helper (`envzod/next`)
16
+ - `createNextEnv` — Next.js-specific helper with server/client schema split
17
+ - `server` schema — auto-sourced from `process.env`, no key repetition needed
18
+ - `client` schema — explicit `runtimeEnv` only for `NEXT_PUBLIC_*` keys (webpack/Turbopack requirement)
19
+ - `bail` option — call `process.exit(1)` instead of throwing to avoid Next.js stack trace noise (default: `false`)
20
+
21
+ ### Improvements
22
+ - Validation errors now include full field details in the thrown error — visible in Next.js error overlays, not just terminal
23
+
24
+ ---
25
+
26
+ ## 1.0.1
27
+
28
+ - Fixed Next.js support — added `source` option to explicitly pass `process.env` keys
29
+ - Without `source`, Next.js/Turbopack could not statically inline `NEXT_PUBLIC_*` vars on the client side
30
+
31
+ ---
32
+
3
33
  ## 1.0.0 — Initial Release
4
34
 
5
35
  - `createEnv(schema, options?)` — validate environment variables against a Zod schema
package/LICENSE CHANGED
@@ -1,21 +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.
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 CHANGED
@@ -1,62 +1,57 @@
1
1
  # envzod
2
2
 
3
- Universal, type-safe environment variable validation powered by [Zod](https://zod.dev).
3
+ [![npm](https://img.shields.io/npm/v/envzod)](https://www.npmjs.com/package/envzod)
4
+ [![license](https://img.shields.io/npm/l/envzod)](./LICENSE)
4
5
 
5
- Works with **Next.js, Express, Fastify, Remix, Bun**any Node.js project.
6
+ **Your app should crash at startup if the env is wrong not three requests into production.**
6
7
 
7
- ---
8
-
9
- ## Why
8
+ `envzod` validates environment variables against a Zod schema at boot time. You get a typed object back. If anything is missing or invalid, it throws with a clear error that tells you exactly what's wrong.
10
9
 
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
- - `envzod` validates your env at boot and gives you a **fully typed object** — no casting needed
10
+ - **Typed** — `env.PORT` is `number`, not `string | undefined`
11
+ - **Fail-fast** crashes at startup with a readable error, not deep in a request handler
12
+ - **CLI check** `npx envzod check` validates before deploy, works in CI/CD
13
+ - **Next.js ready** — server/client split via `envzod/next`, no schema duplication
14
14
 
15
15
  ---
16
16
 
17
17
  ## Install
18
18
 
19
19
  ```bash
20
- npm install envzod
21
- # or
20
+ npm install envzod # zod is a peer dep — install it separately if you haven't
22
21
  pnpm add envzod
23
- # or
24
- yarn add envzod
25
22
  ```
26
23
 
27
- > `zod` is a peer dependency and is installed automatically (npm 7+, pnpm, yarn).
28
-
29
24
  ---
30
25
 
31
- ## Basic Usage
26
+ ## Basic usage
32
27
 
33
28
  ```ts
34
29
  // env.ts
35
- import { createEnv } from 'envzod'
36
- import { z } from 'zod'
30
+ import { createEnv } from "envzod";
31
+ import { z } from "zod";
37
32
 
38
33
  export const env = createEnv({
39
34
  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"
35
+ PORT: z.coerce.number().default(3000),
36
+ NODE_ENV: z.enum(["development", "test", "production"]),
37
+ JWT_SECRET: z.string().min(32),
38
+ });
49
39
  ```
50
40
 
51
- ---
41
+ ```ts
42
+ // anywhere in your app
43
+ import { env } from "./env";
52
44
 
53
- ## Error Output
45
+ env.PORT // number — not string, not undefined
46
+ env.DATABASE_URL // string
47
+ env.NODE_ENV // "development" | "test" | "production"
48
+ ```
54
49
 
55
- When validation fails, `envzod` prints a clear, readable error and throws:
50
+ If validation fails at startup you get this — not a runtime crash 10 minutes later:
56
51
 
57
52
  ```
58
53
  ╔════════════════════════════════════════════╗
59
- zod-env: Invalid Environment
54
+ envzod: Invalid Environment
60
55
  ╚════════════════════════════════════════════╝
61
56
 
62
57
  ✗ DATABASE_URL
@@ -67,131 +62,173 @@ When validation fails, `envzod` prints a clear, readable error and throws:
67
62
  String must contain at least 32 character(s)
68
63
  Got: "tooshort"
69
64
 
70
- ✗ NODE_ENV
71
- Invalid enum value. Expected 'development' | 'test' | 'production'
72
- Got: "prod"
73
-
74
65
  Fix the above and restart your server.
75
66
  ```
76
67
 
77
68
  ---
78
69
 
79
- ## Options
70
+ ## CLI — validate before you deploy
80
71
 
81
- ```ts
82
- const env = createEnv(schema, {
83
- // Custom env source. Defaults to process.env
84
- source: myCustomObject,
72
+ ```bash
73
+ npx envzod check
74
+ npx envzod check --env .env.production
75
+ npx envzod check --config envzod.config # tries .ts then .js
76
+ ```
85
77
 
86
- // Log "✅ zod-env: N variables validated" on success (good for dev)
87
- verbose: true,
78
+ Reads your `envzod.config.ts` (or `.js`) and validates against your env file. Exits with code 1 on failure.
88
79
 
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
- })
80
+ **envzod.config.ts:**
81
+
82
+ ```ts
83
+ import { z } from "zod";
84
+
85
+ export default {
86
+ DATABASE_URL: z.string().url(),
87
+ PORT: z.coerce.number().default(3000),
88
+ JWT_SECRET: z.string().min(32),
89
+ };
94
90
  ```
95
91
 
96
- ### `onError` signature
92
+ **GitHub Actions:**
97
93
 
98
- ```ts
99
- type EnvValidationError = {
100
- field: string
101
- message: string
102
- received: string | undefined
103
- }
94
+ ```yaml
95
+ - name: Validate environment
96
+ run: npx envzod check --env .env.production
104
97
  ```
105
98
 
99
+ The deploy fails here — not after the pod starts and serves broken responses.
100
+
106
101
  ---
107
102
 
108
- ## Framework Examples
103
+ ## Next.js
104
+
105
+ Next.js has a hard constraint: `NEXT_PUBLIC_*` variables must be referenced as literal strings (`process.env.NEXT_PUBLIC_FOO`) for webpack/Turbopack to inline them into the client bundle. Dynamic access doesn't work.
109
106
 
110
- ### Next.js (App Router)
107
+ `createNextEnv` handles this with a server/client split:
108
+ - **Server vars** — auto-sourced from `process.env`, no repetition
109
+ - **Client vars** — you provide explicit `process.env.KEY` references (unavoidable webpack requirement)
111
110
 
112
- > **Important:** Next.js only inlines `NEXT_PUBLIC_*` vars when referenced **explicitly** (e.g. `process.env.NEXT_PUBLIC_FOO`). Passing the whole `process.env` object doesn't work on the client side. Always use the `source` option and list each var individually.
111
+ **Step 1 define schema once in `envzod.config.ts`:**
113
112
 
114
113
  ```ts
115
- // src/env.ts
116
- import { createEnv } from 'envzod'
117
- import { z } from 'zod'
114
+ import { z } from "zod";
118
115
 
119
- export const env = createEnv(
120
- {
121
- // Server-only vars
122
- DATABASE_URL: z.string().url(),
123
- JWT_SECRET: z.string().min(32),
124
- NODE_ENV: z.enum(['development', 'test', 'production']).default('development'),
116
+ export const server = {
117
+ DATABASE_URL: z.string().url(),
118
+ JWT_SECRET: z.string().min(32),
119
+ NODE_ENV: z.enum(["development", "test", "production"]).default("development"),
120
+ };
125
121
 
126
- // Public vars (accessible in browser)
127
- NEXT_PUBLIC_API_URL: z.string().url(),
128
- },
129
- {
130
- // Must explicitly list each var so Next.js/Turbopack can statically inline them
131
- source: {
132
- DATABASE_URL: process.env.DATABASE_URL,
133
- JWT_SECRET: process.env.JWT_SECRET,
134
- NODE_ENV: process.env.NODE_ENV,
135
- NEXT_PUBLIC_API_URL: process.env.NEXT_PUBLIC_API_URL,
136
- },
137
- verbose: process.env.NODE_ENV === 'development',
122
+ export const client = {
123
+ NEXT_PUBLIC_API_URL: z.string().url(),
124
+ };
125
+ ```
126
+
127
+ **Step 2 — wire it in `env.ts`:**
128
+
129
+ ```ts
130
+ import { createNextEnv } from "envzod/next";
131
+ import { server, client } from "./envzod.config";
132
+
133
+ export const env = createNextEnv({
134
+ server,
135
+ client,
136
+ runtimeEnv: {
137
+ NEXT_PUBLIC_API_URL: process.env.NEXT_PUBLIC_API_URL,
138
+ // server keys are auto-sourced — only client keys go here
138
139
  },
139
- )
140
+ verbose: process.env.NODE_ENV === "development",
141
+ bail: true, // process.exit(1) instead of throwing — avoids Next.js stack trace noise
142
+ });
143
+ ```
144
+
145
+ The CLI reads from `envzod.config.ts` automatically — same schema, no duplication:
146
+
147
+ ```bash
148
+ npx envzod check
140
149
  ```
141
150
 
142
- ### Express
151
+ ---
152
+
153
+ ## Express / Node
143
154
 
144
155
  ```ts
145
- // src/env.ts
146
- import { createEnv } from 'envzod'
147
- import { z } from 'zod'
156
+ import { createEnv } from "envzod";
157
+ import { z } from "zod";
148
158
 
149
159
  export const env = createEnv({
150
- PORT: z.coerce.number().default(3000),
160
+ PORT: z.coerce.number().default(3000),
151
161
  DATABASE_URL: z.string().url(),
152
- JWT_SECRET: z.string().min(32),
153
- }, {
154
- verbose: process.env.NODE_ENV === 'development',
155
- })
156
-
157
- // app.ts
158
- import { env } from './env'
159
- app.listen(env.PORT)
162
+ JWT_SECRET: z.string().min(32),
163
+ });
164
+
165
+ app.listen(env.PORT);
160
166
  ```
161
167
 
162
- ### Bun
168
+ ---
169
+
170
+ ## Bun
163
171
 
164
172
  ```ts
165
- // env.ts
166
- import { createEnv } from 'envzod'
167
- import { z } from 'zod'
173
+ import { createEnv } from "envzod";
174
+ import { z } from "zod";
168
175
 
169
- export const env = createEnv({
170
- PORT: z.coerce.number().default(3000),
171
- DATABASE_URL: z.string().url(),
172
- JWT_SECRET: z.string().min(32),
173
- }, {
174
- source: Bun.env,
175
- verbose: Bun.env.NODE_ENV === 'development',
176
- })
176
+ export const env = createEnv(
177
+ {
178
+ PORT: z.coerce.number().default(3000),
179
+ DATABASE_URL: z.string().url(),
180
+ },
181
+ { source: Bun.env },
182
+ );
177
183
  ```
178
184
 
179
185
  ---
180
186
 
181
- ## TypeScript
187
+ ## Options
188
+
189
+ ### `createEnv(schema, options?)`
190
+
191
+ | Option | Type | Default | Description |
192
+ |--------|------|---------|-------------|
193
+ | `source` | `Record<string, string \| undefined>` | `process.env` | Custom env source |
194
+ | `verbose` | `boolean` | `false` | Log `✅ envzod: N variables validated` on success |
195
+ | `onError` | `(errors: EnvValidationError[]) => void` | — | Called before throwing — use for Sentry, logging |
196
+
197
+ ### `createNextEnv(options)` — `envzod/next`
182
198
 
183
- `envzod` uses the `InferEnv<T>` utility type to derive the return type from your schema. No manual type annotations needed.
199
+ | Option | Type | Default | Description |
200
+ |--------|------|---------|-------------|
201
+ | `server` | `EnvSchema` | `{}` | Server-only vars, auto-sourced from `process.env` |
202
+ | `client` | `EnvSchema` | `{}` | Client vars — must all start with `NEXT_PUBLIC_` |
203
+ | `runtimeEnv` | `{ [K in keyof client]: string \| undefined }` | required | Explicit refs for each client key |
204
+ | `verbose` | `boolean` | `false` | Log success summary |
205
+ | `onError` | `(errors: EnvValidationError[]) => void` | — | Called before throwing |
206
+ | `bail` | `boolean` | `false` | Call `process.exit(1)` instead of throwing |
207
+
208
+ ### Error shape
184
209
 
185
210
  ```ts
186
- import type { InferEnv } from 'envzod'
187
- import { z } from 'zod'
211
+ type EnvValidationError = {
212
+ field: string;
213
+ message: string;
214
+ received: string | undefined;
215
+ };
216
+ ```
217
+
218
+ ---
219
+
220
+ ## TypeScript
221
+
222
+ ```ts
223
+ import type { InferEnv } from "envzod";
224
+ import { z } from "zod";
188
225
 
189
226
  const schema = {
190
- PORT: z.coerce.number(),
191
- NODE_ENV: z.enum(['development', 'production']),
192
- }
227
+ PORT: z.coerce.number(),
228
+ NODE_ENV: z.enum(["development", "production"]),
229
+ };
193
230
 
194
- type Env = InferEnv<typeof schema>
231
+ type Env = InferEnv<typeof schema>;
195
232
  // { PORT: number; NODE_ENV: "development" | "production" }
196
233
  ```
197
234
 
@@ -199,13 +236,19 @@ type Env = InferEnv<typeof schema>
199
236
 
200
237
  ## vs t3-env
201
238
 
202
- | Feature | envzod | t3-env |
203
- |---|---|---|
204
- | Framework | Universal | Next.js focused |
205
- | Setup | `createEnv(schema)` | Separate client/server schemas |
206
- | Dependencies | zod only | Next.js types + more |
207
- | Bundle | CJS + ESM | ESM only |
208
- | Error output | Pretty box | Zod default |
239
+ Both libraries solve the same problem. Key differences:
240
+
241
+ | | envzod | t3-env |
242
+ |--|--------|--------|
243
+ | Works outside Next.js | Yes | Limited |
244
+ | Next.js server/client split | `envzod/next` | Built-in |
245
+ | Server var repetition | None (auto-sourced) | Required |
246
+ | Client var repetition | Required (webpack constraint — same for both) | Required |
247
+ | CLI pre-deploy check | Yes — TS + JS config | No |
248
+ | Error output | Formatted box, field-level | Zod default |
249
+ | Output formats | CJS + ESM | ESM only |
250
+
251
+ The client var repetition (`runtimeEnv`) is a webpack/Turbopack hard requirement — no library can eliminate it.
209
252
 
210
253
  ---
211
254