@dotenvage/node 0.1.5

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.
@@ -0,0 +1,271 @@
1
+ # Next.js Integration with dotenvage
2
+
3
+ This directory contains utilities for integrating dotenvage with
4
+ Next.js applications, ensuring that encrypted environment variables
5
+ are properly loaded before Next.js processes them.
6
+
7
+ ## The Problem
8
+
9
+ Next.js loads environment variables from `.env*` files using
10
+ `@next/env` **before** your `next.config.mjs` runs. If your `.env`
11
+ files contain encrypted values (like `ENC[AGE:...]`), Next.js will try
12
+ to use those encrypted values directly, which won't work.
13
+
14
+ Additionally, for `NEXT_PUBLIC_*` variables that need to be available
15
+ in Edge Runtime (middleware), Next.js must inline them at build time.
16
+ If the values are encrypted when Next.js reads them, they'll be
17
+ inlined as encrypted strings, which is useless.
18
+
19
+ ## The Solution
20
+
21
+ We leverage the fact that `@next/env` **does not overwrite** existing
22
+ `process.env` values. By loading and decrypting environment variables
23
+ **before** Next.js starts, we ensure:
24
+
25
+ 1. Encrypted values are decrypted and set in `process.env` first
26
+ 2. When `@next/env` runs, it sees existing values and preserves them
27
+ 3. Decrypted values are available for Next.js to inline into the build
28
+
29
+ ## Quick Start
30
+
31
+ **For Edge Runtime (middleware) support (recommended):**
32
+
33
+ Use the `dotenvage-next` bin script to start Next.js:
34
+
35
+ ```json
36
+ {
37
+ "scripts": {
38
+ "dev": "dotenvage-next dev",
39
+ "build": "dotenvage-next build"
40
+ }
41
+ }
42
+ ```
43
+
44
+ And wrap your config:
45
+
46
+ ```javascript
47
+ import { withDotenvage } from "@dotenvage/node/nextjs/config";
48
+
49
+ export default withDotenvage({
50
+ // Your Next.js config
51
+ });
52
+ ```
53
+
54
+ **For server-side only (no Edge Runtime):**
55
+
56
+ Use `loadEnv()` in your config file:
57
+
58
+ ```javascript
59
+ import { loadEnv } from "@dotenvage/node/nextjs";
60
+
61
+ loadEnv();
62
+
63
+ export default {
64
+ // Your Next.js config
65
+ };
66
+ ```
67
+
68
+ ## Files
69
+
70
+ ### `config.mjs`
71
+
72
+ A configuration wrapper for API consistency. This is mainly for
73
+ convenience when using the `dotenvage-next` wrapper script.
74
+
75
+ **Note:** For Edge Runtime (middleware) support, you MUST use the
76
+ `dotenvage-next` wrapper script. For server-side only (no Edge
77
+ Runtime), you can use `loadEnv()` directly (see `loader.mjs` below).
78
+
79
+ **Usage in `next.config.mjs`:**
80
+
81
+ ```javascript
82
+ import { withDotenvage } from "@dotenvage/node/nextjs/config";
83
+ import nextMDX from "@next/mdx";
84
+
85
+ const nextConfig = {
86
+ // Your Next.js config
87
+ };
88
+
89
+ const withMDX = nextMDX({
90
+ // MDX options
91
+ });
92
+
93
+ export default withDotenvage(withMDX(nextConfig));
94
+ ```
95
+
96
+ **Note:** When using `dotenvage-next` wrapper script, env vars are
97
+ already loaded, so this wrapper is just a pass-through for
98
+ consistency.
99
+
100
+ ### `loader.mjs` (For server-side only, no Edge Runtime)
101
+
102
+ A standard loader for use in `next.config.mjs` when you don't use Edge
103
+ Runtime (middleware). Use this when you only need server-side code to
104
+ access encrypted environment variables.
105
+
106
+ **Important:** This works for server-side code (API routes, server
107
+ components) because `loadEnv()` runs in `next.config.mjs` and can
108
+ decrypt values before server-side code accesses them. However, it does
109
+ NOT work for Edge Runtime (middleware) because Next.js inlines
110
+ `NEXT_PUBLIC_*` variables at build time, BEFORE `next.config.mjs`
111
+ runs.
112
+
113
+ For Edge Runtime support, you MUST use the `dotenvage-next` wrapper
114
+ script.
115
+
116
+ **Usage in `next.config.mjs` (server-side only):**
117
+
118
+ ```javascript
119
+ import { loadEnv } from "@dotenvage/node/nextjs";
120
+
121
+ // Load and decrypt environment variables
122
+ loadEnv();
123
+
124
+ export default {
125
+ // Your Next.js config
126
+ };
127
+ ```
128
+
129
+ **Note:** When using `loadEnv()` in the config file, `@next/env` will
130
+ have already loaded encrypted values, but `loadEnv()` overwrites them
131
+ with decrypted values, so server-side code sees the decrypted values.
132
+
133
+ ### `nextjs-preinit.mjs`
134
+
135
+ A pre-initialization loader that must run **before** Next.js starts.
136
+ This is critical for ensuring `NEXT_PUBLIC_*` variables are available
137
+ in Edge Runtime.
138
+
139
+ **Usage options:**
140
+
141
+ 1. **Via Node.js `-r` flag:**
142
+
143
+ ```json
144
+ {
145
+ "scripts": {
146
+ "dev": "node -r @dotenvage/node/nextjs/preinit node_modules/.bin/next dev",
147
+ "build": "node -r @dotenvage/node/nextjs/preinit node_modules/.bin/next build"
148
+ }
149
+ }
150
+ ```
151
+
152
+ 1. **Via wrapper script (recommended):**
153
+
154
+ See `wrapper.mjs` below.
155
+
156
+ ### `nextjs-wrapper.mjs`
157
+
158
+ A wrapper script that loads environment variables and then starts
159
+ Next.js. This is the easiest way to ensure proper loading order.
160
+
161
+ **The wrapper automatically:**
162
+
163
+ - Detects your package manager (pnpm/npm/yarn) by checking for lock
164
+ files
165
+ - Uses the correct package manager's `exec` command to run Next.js
166
+ - Handles path resolution automatically for all package manager setups
167
+ - Works reliably in monorepos and pnpm workspaces
168
+
169
+ **Usage - Update your `package.json`:**
170
+
171
+ ```json
172
+ {
173
+ "scripts": {
174
+ "dev": "dotenvage-next dev",
175
+ "build": "dotenvage-next build"
176
+ }
177
+ }
178
+ ```
179
+
180
+ That's it! No need to create local wrapper scripts - the
181
+ `dotenvage-next` bin script handles everything automatically. The
182
+ `dotenvage-next` command is installed when you install
183
+ `@dotenvage/node`.
184
+
185
+ ## Complete Integration Example
186
+
187
+ 1. **Install dotenvage:**
188
+
189
+ ```bash
190
+ npm install @dotenvage/node
191
+ ```
192
+
193
+ 2. **Encrypt your `.env` files:**
194
+
195
+ ```bash
196
+ npx dotenvage encrypt .env.local
197
+ ```
198
+
199
+ 3. **Set your encryption key as an environment variable:**
200
+
201
+ ```bash
202
+ export EKG_AGE_KEY=your-key-name
203
+ # or
204
+ export DOTENVAGE_AGE_KEY="your-age-key-string"
205
+ # or
206
+ export AGE_KEY="your-age-key-string"
207
+ ```
208
+
209
+ 4. **Create a wrapper script** (see `wrapper.mjs` example above, or
210
+ use the one in this directory)
211
+
212
+ 5. **Update your `next.config.mjs`** (optional, for additional
213
+ safety):
214
+
215
+ ```javascript
216
+ import { loadEnv } from "@dotenvage/node/nextjs";
217
+
218
+ // This is a backup - the preinit loader should have already loaded
219
+ // but this ensures variables are available if preinit wasn't used
220
+ loadEnv();
221
+
222
+ export default {
223
+ // Your Next.js config
224
+ env: {
225
+ // Explicitly expose critical variables to ensure they're inlined
226
+ NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY:
227
+ process.env.NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY || "",
228
+ },
229
+ };
230
+ ```
231
+
232
+ ## How It Works
233
+
234
+ 1. **Pre-init loader runs first** → Decrypts `.env` files → Sets
235
+ values in `process.env`
236
+ 2. **Next.js starts** → `@next/env` runs → Sees existing values →
237
+ Doesn't overwrite
238
+ 3. **Next.js builds** → Inlines `NEXT_PUBLIC_*` variables → Uses
239
+ decrypted values ✅
240
+ 4. **Edge Runtime** → Has access to decrypted `NEXT_PUBLIC_*`
241
+ variables ✅
242
+
243
+ ## Troubleshooting
244
+
245
+ ### Variables still encrypted in Edge Runtime
246
+
247
+ - Make sure you're using the pre-init loader (not just the standard
248
+ loader)
249
+ - Verify the wrapper script is being used in your `package.json`
250
+ scripts
251
+ - Check that the encryption key is set correctly
252
+
253
+ ### Variables not loading
254
+
255
+ - Check that your encryption key environment variable is set
256
+ - Verify your `.env` files contain encrypted values (starting with
257
+ `ENC[AGE:`)
258
+ - Check console output for error messages from the loader
259
+
260
+ ### Build fails
261
+
262
+ - In production/Vercel, ensure the encryption key is set as an
263
+ environment variable
264
+ - The pre-init loader will fail hard in production if it can't decrypt
265
+ files
266
+ - Check Vercel environment variables section in your project settings
267
+
268
+ ## See Also
269
+
270
+ - [dotenvage main documentation](../../README.md)
271
+ - [Next.js environment variables documentation](https://nextjs.org/docs/app/building-your-application/configuring/environment-variables)
@@ -0,0 +1,32 @@
1
+ /**
2
+ * Next.js configuration wrapper that automatically loads encrypted environment variables
3
+ *
4
+ * @module @dotenvage/node/nextjs/config
5
+ */
6
+
7
+ import type { NextConfig } from 'next'
8
+
9
+ /**
10
+ * Wraps a Next.js configuration object to ensure dotenvage is loaded.
11
+ * Environment variables are automatically loaded when this module is imported.
12
+ *
13
+ * @param config - Next.js config object or function
14
+ * @returns The same config (pass-through wrapper)
15
+ */
16
+ export function withDotenvage(
17
+ config: NextConfig | ((phase: string) => NextConfig),
18
+ ): NextConfig | ((phase: string) => NextConfig)
19
+
20
+ /**
21
+ * Wraps a Next.js configuration function to ensure dotenvage is loaded.
22
+ * Useful when you need to access the build phase.
23
+ *
24
+ * @param configFn - Next.js config function
25
+ * @returns The same function (pass-through wrapper)
26
+ */
27
+ export function withDotenvageConfig(
28
+ configFn: (
29
+ phase: string,
30
+ defaultConfig: NextConfig,
31
+ ) => NextConfig | Promise<NextConfig>,
32
+ ): (phase: string, defaultConfig: NextConfig) => NextConfig | Promise<NextConfig>
@@ -0,0 +1,59 @@
1
+ /**
2
+ * Next.js configuration wrapper for dotenvage integration.
3
+ *
4
+ * This wrapper can be used in two ways:
5
+ *
6
+ * 1. **With wrapper script (recommended for Edge Runtime):**
7
+ * Use `dotenvage-next` bin script to start Next.js:
8
+ * "dev": "dotenvage-next dev"
9
+ * "build": "dotenvage-next build"
10
+ * The wrapper script loads env vars BEFORE Next.js starts, ensuring
11
+ * they're available everywhere including Edge Runtime.
12
+ *
13
+ * 2. **Without wrapper script (server-side only, no Edge Runtime):**
14
+ * Use `loadEnv()` from `@dotenvage/node/nextjs` in your config file:
15
+ * import { loadEnv } from '@dotenvage/node/nextjs'
16
+ * loadEnv()
17
+ * Note: This only works for server-side code, not Edge Runtime/middleware.
18
+ *
19
+ * Usage in next.config.mjs:
20
+ * import { withDotenvage } from '@dotenvage/node/nextjs/config'
21
+ * import nextMDX from '@next/mdx'
22
+ *
23
+ * const nextConfig = {
24
+ * // your config
25
+ * }
26
+ *
27
+ * export default withDotenvage(nextConfig)
28
+ *
29
+ * IMPORTANT: When using `dotenvage-next` wrapper script, env vars are
30
+ * already loaded, so this wrapper is just a pass-through for consistency.
31
+ * When NOT using the wrapper script, use `loadEnv()` directly in your config.
32
+ */
33
+
34
+ /**
35
+ * Wraps a Next.js configuration object.
36
+ * This is mainly for convenience and API consistency.
37
+ * When using `dotenvage-next` wrapper script, env vars are already loaded.
38
+ *
39
+ * @param {import('next').NextConfig | ((phase: string) => import('next').NextConfig)} config - Next.js config object or function
40
+ * @returns {import('next').NextConfig | ((phase: string) => import('next').NextConfig)} The same config (pass-through)
41
+ */
42
+ export function withDotenvage(config) {
43
+ // Pass-through wrapper for API consistency
44
+ // When using `dotenvage-next` wrapper script, env vars are already loaded
45
+ return config;
46
+ }
47
+
48
+ /**
49
+ * Wraps a Next.js configuration function to ensure dotenvage is loaded.
50
+ * Useful when you need to access the build phase.
51
+ *
52
+ * @param {(phase: string, defaultConfig: import('next').NextConfig) => import('next').NextConfig} configFn - Next.js config function
53
+ * @returns {(phase: string, defaultConfig: import('next').NextConfig) => import('next').NextConfig} The same function (pass-through)
54
+ */
55
+ export function withDotenvageConfig(configFn) {
56
+ // Pass-through wrapper for API consistency
57
+ // When using `dotenvage-next` wrapper script, env vars are already loaded
58
+ return configFn;
59
+ }
@@ -0,0 +1,22 @@
1
+ /**
2
+ * Next.js environment variable loader using dotenvage
3
+ *
4
+ * @module @dotenvage/node/nextjs
5
+ */
6
+
7
+ /**
8
+ * Loads environment variables from encrypted .env files
9
+ * This runs automatically when this module is imported
10
+ * Always loads from dotenvage in all environments (local, Vercel CI, production)
11
+ */
12
+ export function loadEnv(): void
13
+
14
+ /**
15
+ * Get all environment variables (for testing/debugging)
16
+ */
17
+ export function getAllEnvVars(): Record<string, string>
18
+
19
+ /**
20
+ * Get variable names (for testing/debugging)
21
+ */
22
+ export function getEnvVarNames(): string[]
@@ -0,0 +1,117 @@
1
+ /**
2
+ * Next.js environment variable loader using dotenvage
3
+ *
4
+ * This module loads encrypted .env files at runtime, making environment
5
+ * variables available to the Next.js application.
6
+ *
7
+ * It automatically loads encrypted .env files in all environments:
8
+ * - Local development (loads from .env, .env.local, etc.)
9
+ * - Vercel CI (loads from committed encrypted .env files)
10
+ * - Production builds (loads from committed encrypted .env files)
11
+ *
12
+ * For Vercel CI, you only need to set the encryption key as an environment variable:
13
+ * - EKG_AGE_KEY (for key name-based lookup: ekg/dr-rs-ekg)
14
+ * - DOTENVAGE_AGE_KEY or AGE_KEY (for direct key string)
15
+ *
16
+ * Encrypted .env files are committed to git and loaded at runtime.
17
+ * No need to sync environment variables to Vercel separately.
18
+ *
19
+ * Usage in next.config.mjs:
20
+ * import { loadEnv } from '@dotenvage/node/nextjs'
21
+ * loadEnv()
22
+ */
23
+ import * as dotenvage from "../index.js";
24
+
25
+ let loaded = false;
26
+
27
+ /**
28
+ * Loads environment variables from encrypted .env files
29
+ * This runs automatically when this module is imported
30
+ * Always loads from dotenvage in all environments (local, Vercel CI, production)
31
+ */
32
+ export function loadEnv() {
33
+ // Only load once
34
+ if (loaded) {
35
+ return;
36
+ }
37
+
38
+ try {
39
+ // Create loader and load environment variables from encrypted .env files
40
+ // This mutates process.env with decrypted values
41
+ // Works in all environments: local, Vercel CI, production
42
+ const loader = dotenvage.JsEnvLoader.new();
43
+ loader.load();
44
+
45
+ const variableNames = loader.getAllVariableNames();
46
+ const isProduction = process.env.NODE_ENV === "production";
47
+
48
+ if (!isProduction || process.env.VERCEL) {
49
+ console.log(
50
+ `✓ Loaded ${variableNames.length} environment variables from dotenvage`
51
+ );
52
+ }
53
+
54
+ loaded = true;
55
+ } catch (error) {
56
+ // Always show errors - we need to know if loading fails
57
+ const errorMessage =
58
+ error instanceof Error ? error.message : String(error);
59
+ console.error(
60
+ "❌ Failed to load environment variables from dotenvage:",
61
+ errorMessage
62
+ );
63
+ console.error("");
64
+ console.error(" Make sure one of these is set:");
65
+ console.error(
66
+ " - EKG_AGE_KEY (for key name-based lookup: ekg/dr-rs-ekg)"
67
+ );
68
+ console.error(" - DOTENVAGE_AGE_KEY (direct key string)");
69
+ console.error(" - AGE_KEY (direct key string)");
70
+ console.error(
71
+ " - EKG_AGE_KEY_NAME in .env file (points to key file location)"
72
+ );
73
+ console.error("");
74
+
75
+ // In production/Vercel, fail hard - we need env vars to work
76
+ if (process.env.NODE_ENV === "production" || process.env.VERCEL) {
77
+ console.error(
78
+ " This is a production build. Environment variables are required!"
79
+ );
80
+ process.exit(1);
81
+ }
82
+
83
+ // In development, warn but continue (might be missing key for testing)
84
+ console.warn(
85
+ " Continuing in development mode, but some features may not work."
86
+ );
87
+
88
+ // Mark as loaded to prevent retry loops
89
+ loaded = true;
90
+ }
91
+ }
92
+
93
+ /**
94
+ * Get all environment variables (for testing/debugging)
95
+ */
96
+ export function getAllEnvVars() {
97
+ try {
98
+ const loader = dotenvage.JsEnvLoader.new();
99
+ return loader.getAllVariables();
100
+ } catch (error) {
101
+ console.error("Error getting environment variables:", error);
102
+ return {};
103
+ }
104
+ }
105
+
106
+ /**
107
+ * Get variable names (for testing/debugging)
108
+ */
109
+ export function getEnvVarNames() {
110
+ try {
111
+ const loader = dotenvage.JsEnvLoader.new();
112
+ return loader.getAllVariableNames();
113
+ } catch (error) {
114
+ console.error("Error getting variable names:", error);
115
+ return [];
116
+ }
117
+ }
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Pre-initialization environment variable loader for Next.js
3
+ *
4
+ * This module loads encrypted .env files BEFORE Next.js loads its own .env files.
5
+ * It must be loaded before Next.js starts (via -r flag, require hook, or wrapper script).
6
+ *
7
+ * @module @dotenvage/node/nextjs/preinit
8
+ */
9
+
10
+ // This module has side effects (loads environment variables)
11
+ // TypeScript declaration for importing the module
12
+ declare const _default: void
13
+ export default _default
@@ -0,0 +1,106 @@
1
+ /**
2
+ * Pre-initialization environment variable loader for Next.js
3
+ *
4
+ * This module loads encrypted .env files BEFORE Next.js loads its own .env files.
5
+ * It must be loaded before Next.js starts (via -r flag, require hook, or wrapper script).
6
+ *
7
+ * IMPORTANT: Next.js's @next/env does NOT overwrite existing process.env values.
8
+ * By loading encrypted env vars FIRST, we ensure:
9
+ * 1. Encrypted values are decrypted and set in process.env
10
+ * 2. When @next/env runs, it sees existing values and doesn't overwrite them
11
+ * 3. Decrypted values are preserved throughout the Next.js build/runtime
12
+ *
13
+ * This is critical for Edge Runtime (middleware) where NEXT_PUBLIC_* variables
14
+ * need to be inlined at build time.
15
+ *
16
+ * Usage:
17
+ * - Via -r flag: node -r @dotenvage/node/nextjs/preinit node_modules/.bin/next dev
18
+ * - Via wrapper script: See wrapper.mjs
19
+ */
20
+ import * as dotenvage from "../index.js";
21
+
22
+ let loaded = false;
23
+
24
+ /**
25
+ * Loads environment variables from encrypted .env files
26
+ * This runs immediately when this module is imported
27
+ *
28
+ * Note: @next/env respects existing process.env values and won't overwrite them,
29
+ * so by loading first, our decrypted values take precedence over the encrypted .env files.
30
+ */
31
+ function loadEnvPreinit() {
32
+ // Only load once
33
+ if (loaded) {
34
+ return;
35
+ }
36
+
37
+ try {
38
+ // Store which variables existed before loading (for debugging)
39
+ const existingVars = new Set(Object.keys(process.env));
40
+
41
+ // Create loader and load environment variables from encrypted .env files
42
+ // This mutates process.env with decrypted values
43
+ const loader = dotenvage.JsEnvLoader.new();
44
+ loader.load();
45
+
46
+ const variableNames = loader.getAllVariableNames();
47
+ const isProduction = process.env.NODE_ENV === "production";
48
+
49
+ // Check which variables we loaded that didn't exist before
50
+ const newlyLoadedVars = variableNames.filter(
51
+ (name) => !existingVars.has(name)
52
+ );
53
+ const overwrittenVars = variableNames.filter((name) =>
54
+ existingVars.has(name)
55
+ );
56
+
57
+ if (!isProduction || process.env.VERCEL) {
58
+ console.log(
59
+ `✓ Pre-initialized ${variableNames.length} environment variables from dotenvage`
60
+ );
61
+ if (
62
+ overwrittenVars.length > 0 &&
63
+ process.env.NODE_ENV === "development"
64
+ ) {
65
+ console.log(
66
+ ` Note: ${overwrittenVars.length} variables already existed and were preserved`
67
+ );
68
+ }
69
+ }
70
+
71
+ loaded = true;
72
+ } catch (error) {
73
+ // Always show errors - we need to know if loading fails
74
+ const errorMessage =
75
+ error instanceof Error ? error.message : String(error);
76
+ console.error(
77
+ "❌ Failed to pre-initialize environment variables from dotenvage:",
78
+ errorMessage
79
+ );
80
+ console.error("");
81
+ console.error(" Make sure one of these is set:");
82
+ console.error(
83
+ " - EKG_AGE_KEY (for key name-based lookup: ekg/dr-rs-ekg)"
84
+ );
85
+ console.error(" - DOTENVAGE_AGE_KEY (direct key string)");
86
+ console.error(" - AGE_KEY (direct key string)");
87
+ console.error("");
88
+
89
+ // In production/Vercel, fail hard - we need env vars to work
90
+ if (process.env.NODE_ENV === "production" || process.env.VERCEL) {
91
+ console.error(
92
+ " This is a production build. Environment variables are required!"
93
+ );
94
+ process.exit(1);
95
+ }
96
+
97
+ // In development, warn but continue
98
+ console.warn(
99
+ " Continuing in development mode, but some features may not work."
100
+ );
101
+ loaded = true;
102
+ }
103
+ }
104
+
105
+ // Auto-load when this module is imported
106
+ loadEnvPreinit();