@mr-aftab-ahmad-khan/envy 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md ADDED
@@ -0,0 +1,9 @@
1
+ # Changelog
2
+
3
+ ## [0.1.0] — 2026-05-15
4
+
5
+ ### Added
6
+
7
+ - Zod-validated `createEnv({ server, client, clientPrefix, runtimeEnv })` with read-only proxy and client/server isolation.
8
+ - Framework adapters: `envy/next`, `envy/vite`, `envy/astro`.
9
+ - CLI: `envy validate` and `envy generate-example` for `.env.example` scaffolding.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 envy contributors
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,532 @@
1
+ # envy
2
+
3
+ [![npm version](https://img.shields.io/npm/v/%40mr-aftab-ahmad-khan%2Fenvy.svg)](https://www.npmjs.com/package/@mr-aftab-ahmad-khan/envy)
4
+ [![bundle size](https://img.shields.io/bundlephobia/minzip/%40mr-aftab-ahmad-khan%2Fenvy)](https://bundlephobia.com/package/envy)
5
+ [![license](https://img.shields.io/npm/l/%40mr-aftab-ahmad-khan%2Fenvy.svg)](./LICENSE)
6
+ [![TypeScript](https://img.shields.io/badge/types-TypeScript-blue.svg)](https://www.typescriptlang.org/)
7
+
8
+ **Type-safe environment variables for any JavaScript runtime.** `envy` is a framework-agnostic, Zod-powered loader that turns `process.env` into a strongly-typed, validated object at boot. `env.PORT` is a `number`. `env.DATABASE_URL` is a non-empty URL string. Defaults flow into the inferred type. Missing or malformed variables are reported all-at-once before your app starts.
9
+
10
+ Unlike `dotenv` (which gives you `string | undefined` everywhere) or `t3-env` (which is glued to Next.js + React), `envy` is a tiny zero-runtime-dependency core with optional adapters for **Next.js**, **Vite**, and **Astro**, a CLI for CI validation and `.env.example` generation, and a strict server/client boundary that throws at runtime if you ever try to read a secret in the browser.
11
+
12
+ ---
13
+
14
+ ## Installation
15
+
16
+ ```bash
17
+ npm install @mr-aftab-ahmad-khan/envy zod
18
+ # or
19
+ pnpm add @mr-aftab-ahmad-khan/envy zod
20
+ # or
21
+ yarn add @mr-aftab-ahmad-khan/envy zod
22
+ ```
23
+
24
+ `zod` is a peer dependency — `envy` itself has no runtime dependencies.
25
+
26
+ ---
27
+
28
+ ## Quick Start
29
+
30
+ ```ts
31
+ import { createEnv } from "@mr-aftab-ahmad-khan/envy";
32
+ import { z } from "zod";
33
+
34
+ export const env = createEnv({
35
+ server: {
36
+ DATABASE_URL: z.string().url(),
37
+ PORT: z.coerce.number().default(3000),
38
+ },
39
+ });
40
+
41
+ console.log(env.DATABASE_URL); // string
42
+ console.log(env.PORT); // number
43
+ ```
44
+
45
+ If `DATABASE_URL` is missing, the process exits with a clear error listing every problem at once.
46
+
47
+ ---
48
+
49
+ ## Core Usage Examples
50
+
51
+ ### 1. Basic server-only env
52
+
53
+ ```ts
54
+ import { createEnv } from "@mr-aftab-ahmad-khan/envy";
55
+ import { z } from "zod";
56
+
57
+ export const env = createEnv({
58
+ server: {
59
+ DATABASE_URL: z.string().url(),
60
+ PORT: z.coerce.number(),
61
+ DEBUG: z.coerce.boolean(),
62
+ API_BASE: z.string().url(),
63
+ },
64
+ });
65
+
66
+ env.DATABASE_URL; // string
67
+ env.PORT; // number
68
+ env.DEBUG; // boolean
69
+ env.API_BASE; // string
70
+ ```
71
+
72
+ ### 2. Defaults and coercion
73
+
74
+ ```ts
75
+ import { createEnv } from "@mr-aftab-ahmad-khan/envy";
76
+ import { z } from "zod";
77
+
78
+ export const env = createEnv({
79
+ server: {
80
+ PORT: z.coerce.number().default(3000),
81
+ LOG_LEVEL: z.enum(["debug", "info", "warn", "error"]).default("info"),
82
+ FEATURE_X_ENABLED: z.coerce.boolean().default(false),
83
+ },
84
+ });
85
+
86
+ env.PORT; // number (3000 if PORT is missing)
87
+ env.LOG_LEVEL; // "debug" | "info" | "warn" | "error"
88
+ env.FEATURE_X_ENABLED; // boolean
89
+ ```
90
+
91
+ ### 3. Custom onValidationError hook
92
+
93
+ ```ts
94
+ import { createEnv, EnvValidationError } from "@mr-aftab-ahmad-khan/envy";
95
+ import { z } from "zod";
96
+
97
+ export const env = createEnv({
98
+ server: { DATABASE_URL: z.string().url() },
99
+ onValidationError: (err: EnvValidationError) => {
100
+ console.error("envy: cannot start, missing required env");
101
+ for (const v of err.invalid) {
102
+ console.error(` - ${v.name}`);
103
+ }
104
+ process.exit(1);
105
+ },
106
+ });
107
+ ```
108
+
109
+ ### 4. skipValidation in Vitest setup
110
+
111
+ ```ts
112
+ // vitest.setup.ts
113
+ import { beforeAll } from "vitest";
114
+
115
+ beforeAll(() => {
116
+ process.env.DATABASE_URL = "postgres://localhost/test";
117
+ process.env.SKIP_ENV_VALIDATION = "1";
118
+ });
119
+
120
+ // src/env.ts
121
+ import { createEnv } from "@mr-aftab-ahmad-khan/envy";
122
+ import { z } from "zod";
123
+
124
+ export const env = createEnv({
125
+ server: { DATABASE_URL: z.string().url() },
126
+ skipValidation: !!process.env.SKIP_ENV_VALIDATION,
127
+ });
128
+ ```
129
+
130
+ ### 5. Shared env in a utility module
131
+
132
+ ```ts
133
+ // src/env.ts
134
+ import { createEnv } from "@mr-aftab-ahmad-khan/envy/next";
135
+ import { z } from "zod";
136
+
137
+ export const env = createEnv({
138
+ server: { DATABASE_URL: z.string().url() },
139
+ client: { NEXT_PUBLIC_APP_URL: z.string().url() },
140
+ });
141
+
142
+ // src/lib/api.ts (imported by both server and client code)
143
+ import { env } from "../env";
144
+
145
+ export function getApiBase() {
146
+ return env.NEXT_PUBLIC_APP_URL; // safe everywhere
147
+ }
148
+ ```
149
+
150
+ ### 6. Combining multiple env files via runtimeEnv
151
+
152
+ ```ts
153
+ import { createEnv } from "@mr-aftab-ahmad-khan/envy";
154
+ import { z } from "zod";
155
+
156
+ const stage = process.env.NODE_ENV ?? "development";
157
+ const overlay = stage === "production" ? process.env : { ...process.env, DEBUG: "true" };
158
+
159
+ export const env = createEnv({
160
+ server: {
161
+ DATABASE_URL: z.string().url(),
162
+ DEBUG: z.coerce.boolean().default(false),
163
+ },
164
+ runtimeEnv: overlay,
165
+ });
166
+ ```
167
+
168
+ ---
169
+
170
+ ## Framework Integration Examples
171
+
172
+ ### Next.js App Router
173
+
174
+ ```ts
175
+ // src/env.ts
176
+ import { createEnv } from "@mr-aftab-ahmad-khan/envy/next";
177
+ import { z } from "zod";
178
+
179
+ export const env = createEnv({
180
+ server: {
181
+ DATABASE_URL: z.string().url(),
182
+ NEXTAUTH_SECRET: z.string().min(32),
183
+ STRIPE_SECRET_KEY: z.string().startsWith("sk_"),
184
+ },
185
+ client: {
186
+ NEXT_PUBLIC_APP_URL: z.string().url(),
187
+ NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY: z.string().startsWith("pk_"),
188
+ },
189
+ });
190
+ ```
191
+
192
+ ```tsx
193
+ // app/page.tsx (Server Component)
194
+ import { env } from "@/env";
195
+
196
+ export default function Page() {
197
+ const url = process.env.NODE_ENV === "production"
198
+ ? env.NEXT_PUBLIC_APP_URL
199
+ : "http://localhost:3000";
200
+ return <main>{url}</main>;
201
+ }
202
+ ```
203
+
204
+ ```tsx
205
+ // app/components/StripeButton.tsx
206
+ "use client";
207
+ import { env } from "@/env";
208
+
209
+ export function StripeButton() {
210
+ return <button data-key={env.NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY}>Pay</button>;
211
+ }
212
+ ```
213
+
214
+ ### Vite + React
215
+
216
+ ```ts
217
+ // src/env.ts
218
+ import { createEnv } from "@mr-aftab-ahmad-khan/envy/vite";
219
+ import { z } from "zod";
220
+
221
+ export const env = createEnv({
222
+ client: {
223
+ VITE_API_BASE: z.string().url(),
224
+ VITE_SENTRY_DSN: z.string().url().optional(),
225
+ },
226
+ runtimeEnv: import.meta.env,
227
+ });
228
+ ```
229
+
230
+ ```tsx
231
+ // src/App.tsx
232
+ import { env } from "./env";
233
+
234
+ export function App() {
235
+ return <pre>{env.VITE_API_BASE}</pre>;
236
+ }
237
+ ```
238
+
239
+ ### Plain Express
240
+
241
+ ```ts
242
+ // src/env.ts
243
+ import { createEnv } from "@mr-aftab-ahmad-khan/envy";
244
+ import { z } from "zod";
245
+
246
+ export const env = createEnv({
247
+ server: {
248
+ PORT: z.coerce.number().default(3000),
249
+ DATABASE_URL: z.string().url(),
250
+ },
251
+ });
252
+
253
+ // src/server.ts
254
+ import express from "express";
255
+ import { env } from "./env";
256
+
257
+ const app = express();
258
+ app.listen(env.PORT, () => console.log(`listening on ${env.PORT}`));
259
+ ```
260
+
261
+ ---
262
+
263
+ ## Configuration Reference
264
+
265
+ | Option | Type | Default | Description |
266
+ | ------------------------ | -------------------------------------- | ------------------ | --------------------------------------------------------------------------- |
267
+ | `server` | `Record<string, ZodTypeAny>` | `{}` | Server-only schemas. Never accessible on the client. |
268
+ | `client` | `Record<string, ZodTypeAny>` | `{}` | Client-exposed schemas. Must match `clientPrefix` (when set by an adapter). |
269
+ | `runtimeEnv` | `Record<string, string \| undefined>` | `process.env` | Source of raw values. |
270
+ | `clientPrefix` | `string` | `undefined` | Prefix required on every `client` key. Set by adapters. |
271
+ | `skipValidation` | `boolean` | `false` | Bypass Zod parsing; values returned as-is. |
272
+ | `onValidationError` | `(err: EnvValidationError) => void` | `undefined` | Called instead of throwing. |
273
+ | `isServer` | `boolean` | `typeof window === "undefined"` | Controls whether server vars can be read. |
274
+ | `emptyStringAsUndefined` | `boolean` | `true` | Coerce `""` to `undefined` so defaults fire. |
275
+
276
+ ---
277
+
278
+ ## Error Handling
279
+
280
+ When validation fails, `envy` throws an `EnvValidationError`:
281
+
282
+ ```
283
+ Invalid environment variables (2 issues):
284
+ - DATABASE_URL: Invalid url (received: "not-a-url")
285
+ - PORT: Expected number, received string (received: "abc")
286
+ ```
287
+
288
+ Catch it programmatically:
289
+
290
+ ```ts
291
+ import { createEnv, EnvValidationError } from "@mr-aftab-ahmad-khan/envy";
292
+
293
+ try {
294
+ const env = createEnv({ /* ... */ });
295
+ } catch (err) {
296
+ if (err instanceof EnvValidationError) {
297
+ for (const v of err.invalid) {
298
+ console.error(v.name, v.received, v.issues);
299
+ }
300
+ }
301
+ process.exit(1);
302
+ }
303
+ ```
304
+
305
+ `EnvValidationError` shape:
306
+
307
+ ```ts
308
+ class EnvValidationError extends Error {
309
+ readonly invalid: ReadonlyArray<{
310
+ name: string;
311
+ received: unknown;
312
+ issues: ReadonlyArray<ZodIssue>;
313
+ }>;
314
+ }
315
+ ```
316
+
317
+ ---
318
+
319
+ ## TypeScript Types
320
+
321
+ ```ts
322
+ import type {
323
+ CreateEnvOptions,
324
+ Env,
325
+ InferEnv,
326
+ InvalidVar,
327
+ ZodRecord,
328
+ } from "@mr-aftab-ahmad-khan/envy";
329
+
330
+ const env = createEnv({ server: { PORT: z.coerce.number() } });
331
+
332
+ type MyEnv = InferEnv<typeof env>;
333
+ // { readonly PORT: number }
334
+ ```
335
+
336
+ ---
337
+
338
+ ## CLI Reference
339
+
340
+ ### `envy validate`
341
+
342
+ Validates the current environment against your schema. Exits with code `1` on failure — perfect for CI.
343
+
344
+ ```bash
345
+ envy validate
346
+ envy validate --schema src/env.ts --env .env.production
347
+ ```
348
+
349
+ | Flag | Default | Description |
350
+ | -------------- | ----------- | ------------------------------------------ |
351
+ | `--schema, -s` | `src/env.ts`| Path to schema module (must export `schema = { server, client }`) |
352
+ | `--env, -e` | `.env` | Path to `.env` file |
353
+
354
+ ### `envy generate-example`
355
+
356
+ Generates a `.env.example` from the schema, using `.default()` values or sensible placeholders.
357
+
358
+ ```bash
359
+ envy generate-example
360
+ envy generate-example --out .env.example.local
361
+ ```
362
+
363
+ | Flag | Default | Description |
364
+ | ----------- | -------------- | -------------------------- |
365
+ | `--out, -o` | `.env.example` | Output path |
366
+
367
+ Schemas must be exported as:
368
+
369
+ ```ts
370
+ // src/env.ts
371
+ import { z } from "zod";
372
+
373
+ export const schema = {
374
+ server: { DATABASE_URL: z.string().url() },
375
+ client: { NEXT_PUBLIC_URL: z.string().url() },
376
+ };
377
+ ```
378
+
379
+ ---
380
+
381
+ ## Real-World Recipe — Full Next.js Production Setup
382
+
383
+ ```ts
384
+ // src/env.ts
385
+ import { createEnv } from "@mr-aftab-ahmad-khan/envy/next";
386
+ import { z } from "zod";
387
+
388
+ export const schema = {
389
+ server: {
390
+ DATABASE_URL: z.string().url().describe("postgres://..."),
391
+ NEXTAUTH_SECRET: z.string().min(32),
392
+ STRIPE_SECRET_KEY: z.string().startsWith("sk_"),
393
+ },
394
+ client: {
395
+ NEXT_PUBLIC_APP_URL: z.string().url(),
396
+ NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY: z.string().startsWith("pk_"),
397
+ },
398
+ };
399
+
400
+ export const env = createEnv(schema);
401
+ ```
402
+
403
+ ```bash
404
+ # .env.local
405
+ DATABASE_URL=postgres://app:pass@localhost:5432/app
406
+ NEXTAUTH_SECRET=ab92e7da6c0f4d39b1c6a8a2f0b1c4e3
407
+ STRIPE_SECRET_KEY=sk_test_abc123
408
+ NEXT_PUBLIC_APP_URL=http://localhost:3000
409
+ NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY=pk_test_abc123
410
+ ```
411
+
412
+ ```bash
413
+ # .env.example (generated by `envy generate-example`)
414
+ # --- server-only ---
415
+ DATABASE_URL=postgres://...
416
+ NEXTAUTH_SECRET=your-value-here
417
+ STRIPE_SECRET_KEY=your-value-here
418
+
419
+ # --- exposed to client ---
420
+ NEXT_PUBLIC_APP_URL=https://example.com
421
+ NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY=your-value-here
422
+ ```
423
+
424
+ ```json
425
+ // package.json
426
+ {
427
+ "scripts": {
428
+ "build": "envy validate && next build",
429
+ "start": "next start"
430
+ }
431
+ }
432
+ ```
433
+
434
+ ```tsx
435
+ // app/page.tsx (Server Component)
436
+ import { env } from "@/env";
437
+ import Stripe from "stripe";
438
+
439
+ const stripe = new Stripe(env.STRIPE_SECRET_KEY);
440
+
441
+ export default async function Page() {
442
+ const balance = await stripe.balance.retrieve();
443
+ return <pre>{JSON.stringify(balance, null, 2)}</pre>;
444
+ }
445
+ ```
446
+
447
+ ```tsx
448
+ // app/checkout/CheckoutButton.tsx
449
+ "use client";
450
+ import { env } from "@/env";
451
+
452
+ export function CheckoutButton() {
453
+ return <button data-pk={env.NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY}>Pay</button>;
454
+ }
455
+ ```
456
+
457
+ ```yaml
458
+ # .github/workflows/deploy.yml
459
+ name: deploy
460
+ on: [push]
461
+ jobs:
462
+ build:
463
+ runs-on: ubuntu-latest
464
+ steps:
465
+ - uses: actions/checkout@v4
466
+ - uses: actions/setup-node@v4
467
+ with: { node-version: 20 }
468
+ - run: npm ci
469
+ - run: npx envy validate
470
+ env:
471
+ DATABASE_URL: ${{ secrets.DATABASE_URL }}
472
+ NEXTAUTH_SECRET: ${{ secrets.NEXTAUTH_SECRET }}
473
+ STRIPE_SECRET_KEY: ${{ secrets.STRIPE_SECRET_KEY }}
474
+ NEXT_PUBLIC_APP_URL: ${{ vars.NEXT_PUBLIC_APP_URL }}
475
+ NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY: ${{ vars.NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY }}
476
+ - run: npm run build
477
+ ```
478
+
479
+ ---
480
+
481
+ ## Migration Guide from dotenv
482
+
483
+ **Before — `dotenv`:**
484
+
485
+ ```ts
486
+ import "dotenv/config";
487
+
488
+ const port = parseInt(process.env.PORT ?? "3000", 10);
489
+ const dbUrl = process.env.DATABASE_URL;
490
+ if (!dbUrl) throw new Error("DATABASE_URL missing");
491
+
492
+ app.listen(port);
493
+ ```
494
+
495
+ **After — `envy`:**
496
+
497
+ ```ts
498
+ import { createEnv } from "@mr-aftab-ahmad-khan/envy";
499
+ import { z } from "zod";
500
+
501
+ const env = createEnv({
502
+ server: {
503
+ PORT: z.coerce.number().default(3000),
504
+ DATABASE_URL: z.string().url(),
505
+ },
506
+ });
507
+
508
+ app.listen(env.PORT);
509
+ ```
510
+
511
+ - `process.env.PORT` (`string | undefined`) → `env.PORT` (`number`).
512
+ - Missing `DATABASE_URL` fails before listen with every error printed at once.
513
+
514
+ ---
515
+
516
+ ## Comparison Table
517
+
518
+ | Feature | dotenv | t3-env | **envy** |
519
+ | ----------------------- | :----: | :----: | :------: |
520
+ | TypeScript types | ❌ | ✅ | ✅ |
521
+ | Framework agnostic | ✅ | ❌ | ✅ |
522
+ | Zod validation | ❌ | ✅ | ✅ |
523
+ | CLI tooling | ❌ | ❌ | ✅ |
524
+ | Client/server boundary | ❌ | ✅ | ✅ |
525
+ | Bundle size | 6 KB | 14 KB | **3 KB** |
526
+ | Custom error handler | ❌ | ❌ | ✅ |
527
+
528
+ ---
529
+
530
+ ## License
531
+
532
+ MIT
package/dist/astro.cjs ADDED
@@ -0,0 +1,153 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/adapters/astro.ts
21
+ var astro_exports = {};
22
+ __export(astro_exports, {
23
+ EnvValidationError: () => EnvValidationError,
24
+ InvalidAccessError: () => InvalidAccessError,
25
+ createEnv: () => createEnv2
26
+ });
27
+ module.exports = __toCommonJS(astro_exports);
28
+
29
+ // src/errors.ts
30
+ var EnvValidationError = class _EnvValidationError extends Error {
31
+ invalid;
32
+ constructor(invalid) {
33
+ super(_EnvValidationError.formatMessage(invalid));
34
+ this.name = "EnvValidationError";
35
+ this.invalid = invalid;
36
+ Object.setPrototypeOf(this, _EnvValidationError.prototype);
37
+ }
38
+ static formatMessage(invalid) {
39
+ const lines = [
40
+ `Invalid environment variables (${invalid.length} issue${invalid.length === 1 ? "" : "s"}):`
41
+ ];
42
+ for (const v of invalid) {
43
+ const received = v.received === void 0 ? "<missing>" : JSON.stringify(v.received);
44
+ for (const issue of v.issues) {
45
+ lines.push(` - ${v.name}: ${issue.message} (received: ${received})`);
46
+ }
47
+ }
48
+ return lines.join("\n");
49
+ }
50
+ };
51
+ var InvalidAccessError = class _InvalidAccessError extends Error {
52
+ constructor(varName) {
53
+ super(
54
+ `Attempted to access server-only env var "${varName}" on the client. Move it to the "server" object only if access is required server-side.`
55
+ );
56
+ this.name = "InvalidAccessError";
57
+ Object.setPrototypeOf(this, _InvalidAccessError.prototype);
58
+ }
59
+ };
60
+
61
+ // src/core.ts
62
+ var DEFAULT_RUNTIME_ENV = typeof process !== "undefined" && process.env ? process.env : {};
63
+ function isServerDefault() {
64
+ return typeof window === "undefined";
65
+ }
66
+ function createEnv(opts) {
67
+ const {
68
+ server = {},
69
+ client = {},
70
+ runtimeEnv = DEFAULT_RUNTIME_ENV,
71
+ clientPrefix,
72
+ skipValidation = false,
73
+ onValidationError,
74
+ isServer = isServerDefault(),
75
+ emptyStringAsUndefined = true
76
+ } = opts;
77
+ if (clientPrefix) {
78
+ for (const key of Object.keys(client)) {
79
+ if (!key.startsWith(clientPrefix)) {
80
+ throw new Error(
81
+ `Client variable "${key}" must start with prefix "${clientPrefix}".`
82
+ );
83
+ }
84
+ }
85
+ for (const key of Object.keys(server)) {
86
+ if (key.startsWith(clientPrefix)) {
87
+ throw new Error(
88
+ `Server variable "${key}" must NOT start with the client prefix "${clientPrefix}".`
89
+ );
90
+ }
91
+ }
92
+ }
93
+ const parsed = {};
94
+ const invalid = [];
95
+ const validate = (name, schema) => {
96
+ let raw = runtimeEnv[name];
97
+ if (emptyStringAsUndefined && raw === "") raw = void 0;
98
+ const result = schema.safeParse(raw);
99
+ if (result.success) {
100
+ parsed[name] = result.data;
101
+ } else {
102
+ invalid.push({ name, received: raw, issues: result.error.issues });
103
+ }
104
+ };
105
+ if (!skipValidation) {
106
+ for (const [name, schema] of Object.entries(server)) validate(name, schema);
107
+ for (const [name, schema] of Object.entries(client)) validate(name, schema);
108
+ if (invalid.length > 0) {
109
+ const err = new EnvValidationError(invalid);
110
+ if (onValidationError) {
111
+ onValidationError(err);
112
+ return parsed;
113
+ }
114
+ throw err;
115
+ }
116
+ } else {
117
+ for (const name of Object.keys(server)) parsed[name] = runtimeEnv[name];
118
+ for (const name of Object.keys(client)) parsed[name] = runtimeEnv[name];
119
+ }
120
+ const serverKeys = new Set(Object.keys(server));
121
+ const proxy = new Proxy(parsed, {
122
+ get(target, prop) {
123
+ if (typeof prop !== "string") return Reflect.get(target, prop);
124
+ if (!isServer && serverKeys.has(prop)) {
125
+ throw new InvalidAccessError(prop);
126
+ }
127
+ return target[prop];
128
+ },
129
+ set() {
130
+ throw new Error("Env is read-only");
131
+ },
132
+ deleteProperty() {
133
+ throw new Error("Env is read-only");
134
+ }
135
+ });
136
+ return proxy;
137
+ }
138
+
139
+ // src/adapters/astro.ts
140
+ function createEnv2(opts) {
141
+ return createEnv({
142
+ ...opts,
143
+ clientPrefix: "PUBLIC_",
144
+ isServer: typeof window === "undefined"
145
+ });
146
+ }
147
+ // Annotate the CommonJS export names for ESM import in node:
148
+ 0 && (module.exports = {
149
+ EnvValidationError,
150
+ InvalidAccessError,
151
+ createEnv
152
+ });
153
+ //# sourceMappingURL=astro.cjs.map