bini-env 1.0.8 → 1.0.10
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/README.md +112 -82
- package/dist/index.d.mts +7 -1
- package/dist/index.d.ts +7 -1
- package/dist/index.js +170 -143
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +170 -139
- package/dist/index.mjs.map +1 -1
- package/package.json +15 -27
package/README.md
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
# bini-env
|
|
2
2
|
|
|
3
|
-

|
|
4
4
|

|
|
5
5
|

|
|
6
|
-

|
|
7
|
+

|
|
8
|
+

|
|
9
9
|
|
|
10
|
-
**Zero-config environment variable system + Vite plugin for Bini.js**
|
|
11
|
-
Loads `.env` in development, uses host-provided variables in production, and works across Node.js, Bun, Deno, and
|
|
10
|
+
**Zero-config environment variable system + Vite plugin for Bini.js**
|
|
11
|
+
Loads `.env` in development, uses host-provided variables in production, and works across Node.js, Bun, Deno, Vercel Edge, Netlify Edge, and Cloudflare Workers — without leaking secrets or adding runtime cost.
|
|
12
12
|
|
|
13
13
|
---
|
|
14
14
|
|
|
@@ -16,24 +16,25 @@ Loads `.env` in development, uses host-provided variables in production, and wor
|
|
|
16
16
|
|
|
17
17
|
This library **does NOT magically make env vars safe**.
|
|
18
18
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
19
|
+
- Anything exposed to the client (`import.meta.env`) is **public**.
|
|
20
|
+
- Only server-side code (`getEnv`, `requireEnv`) can safely access secrets.
|
|
21
|
+
- Misconfigured prefixes = **data leak**.
|
|
22
22
|
|
|
23
|
-
If you don
|
|
23
|
+
If you don't understand this, stop and fix that first.
|
|
24
24
|
|
|
25
25
|
---
|
|
26
26
|
|
|
27
27
|
## ✨ Features
|
|
28
28
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
29
|
+
- **Universal API** — `getEnv()` / `requireEnv()` work across all runtimes
|
|
30
|
+
- **Zero-config `.env` loading** in development
|
|
31
|
+
- **Edge-safe** — no static `fs`, `path`, or `dotenv` imports in bundles
|
|
32
|
+
- **Auto server restart** — dev server restarts when `.env` files are created or changed
|
|
33
|
+
- **Strict production behavior** — no file reads, no dotenv
|
|
34
|
+
- **Prefix control** — supports `BINI_`, `VITE_`, or custom
|
|
35
|
+
- **Tree-shakeable** — no dead code in client bundles
|
|
36
|
+
- **Typed** — full TypeScript support
|
|
37
|
+
- **Fast** — single load in dev, zero overhead in prod
|
|
37
38
|
|
|
38
39
|
---
|
|
39
40
|
|
|
@@ -49,7 +50,7 @@ yarn add bini-env
|
|
|
49
50
|
|
|
50
51
|
---
|
|
51
52
|
|
|
52
|
-
## 🚀 Quick Start
|
|
53
|
+
## 🚀 Quick Start
|
|
53
54
|
|
|
54
55
|
```ts
|
|
55
56
|
// vite.config.ts
|
|
@@ -61,15 +62,13 @@ export default defineConfig({
|
|
|
61
62
|
});
|
|
62
63
|
```
|
|
63
64
|
|
|
64
|
-
Done.
|
|
65
|
-
|
|
66
|
-
If this doesn’t work, your project setup is broken — not the plugin.
|
|
65
|
+
Done. If this doesn't work, your project setup is broken — not the plugin.
|
|
67
66
|
|
|
68
67
|
---
|
|
69
68
|
|
|
70
69
|
## 🔐 Environment Rules (Read This Twice)
|
|
71
70
|
|
|
72
|
-
> ✅
|
|
71
|
+
> ✅ Both `BINI_` and `VITE_` prefixes work out of the box — no extra config needed.
|
|
73
72
|
|
|
74
73
|
### Client (PUBLIC)
|
|
75
74
|
|
|
@@ -78,16 +77,12 @@ BINI_PUBLIC_API_URL=https://api.example.com
|
|
|
78
77
|
VITE_ANALYTICS_ID=UA-XXXX
|
|
79
78
|
```
|
|
80
79
|
|
|
81
|
-
Accessible via:
|
|
82
|
-
|
|
83
80
|
```ts
|
|
84
81
|
import.meta.env.BINI_PUBLIC_API_URL
|
|
85
82
|
```
|
|
86
83
|
|
|
87
84
|
👉 **Never put secrets here. Ever.**
|
|
88
85
|
|
|
89
|
-
---
|
|
90
|
-
|
|
91
86
|
### Server (PRIVATE)
|
|
92
87
|
|
|
93
88
|
```env
|
|
@@ -96,25 +91,59 @@ DATABASE_URL=postgres://...
|
|
|
96
91
|
```
|
|
97
92
|
|
|
98
93
|
```ts
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
const
|
|
94
|
+
// getEnv and requireEnv are auto-imported in API routes
|
|
95
|
+
const pass = requireEnv('SMTP_PASS'); // throws if missing
|
|
96
|
+
const db = getEnv('DATABASE_URL'); // returns undefined if missing
|
|
102
97
|
```
|
|
103
98
|
|
|
104
|
-
👉 If this leaks, it
|
|
99
|
+
👉 If this leaks, it's your fault, not the library's.
|
|
100
|
+
|
|
101
|
+
---
|
|
102
|
+
|
|
103
|
+
## 🌍 Platform Support
|
|
104
|
+
|
|
105
|
+
`getEnv` and `requireEnv` work identically across every supported platform. Write your API routes once — they run everywhere unchanged.
|
|
106
|
+
|
|
107
|
+
| Platform | Runtime | Where vars come from | How `getEnv` reads them |
|
|
108
|
+
| ------------------ | ------------- | ------------------------------------- | ----------------------- |
|
|
109
|
+
| Node.js | Node | `.env` file / system env | `process.env` ✅ |
|
|
110
|
+
| Bun | Bun | `.env` file / system env | `process.env` ✅ |
|
|
111
|
+
| Vercel Edge | V8 isolate | Project settings → Environment Vars | `process.env` ✅ |
|
|
112
|
+
| Netlify Edge | Deno | Site settings → Environment Vars | `Deno.env.get()` ✅ |
|
|
113
|
+
| Cloudflare Workers | V8 isolate | `wrangler.toml [vars]` / dashboard | `process.env` ✅ |
|
|
114
|
+
| Deno Deploy | Deno | Project settings → Environment Vars | `Deno.env.get()` ✅ |
|
|
115
|
+
|
|
116
|
+
> **Cloudflare note:** Variables set in `wrangler.toml [vars]` or the dashboard work with `getEnv` at the top level. Secrets added via `wrangler secret put` are only available inside the fetch handler via `c.env` — this is a Cloudflare Workers architecture constraint, not a bini-env limitation.
|
|
105
117
|
|
|
106
118
|
---
|
|
107
119
|
|
|
108
120
|
## 🧠 How It Actually Works
|
|
109
121
|
|
|
110
|
-
| Mode | Behavior
|
|
111
|
-
| ---------------- |
|
|
112
|
-
| Dev (`vite dev`) | Loads `.env` once via dynamic dotenv
|
|
113
|
-
| Preview | Same as dev
|
|
114
|
-
| Production | Uses `process.env` / host injection only
|
|
115
|
-
| Edge
|
|
122
|
+
| Mode | Behavior |
|
|
123
|
+
| ---------------- | ------------------------------------------------- |
|
|
124
|
+
| Dev (`vite dev`) | Loads `.env` once via dynamic dotenv |
|
|
125
|
+
| Preview | Same as dev |
|
|
126
|
+
| Production | Uses `process.env` / host injection only |
|
|
127
|
+
| Deno / Netlify Edge | Uses native `Deno.env` |
|
|
128
|
+
| Vercel / Cloudflare Edge | Uses `process.env` injected by the host |
|
|
116
129
|
|
|
117
|
-
**
|
|
130
|
+
`fs`, `path`, and `dotenv` are **never imported at the top level**. They are dynamically imported at runtime, inside Node-only guards, so edge bundlers never trace them into your bundle.
|
|
131
|
+
|
|
132
|
+
---
|
|
133
|
+
|
|
134
|
+
## 🔄 Auto Server Restart
|
|
135
|
+
|
|
136
|
+
The dev server automatically restarts when any `.env*` file is created or changed in your project root — no manual restart needed.
|
|
137
|
+
|
|
138
|
+
```
|
|
139
|
+
12:00:01 [bini-env] .env.local changed — restarting server
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
Covered events:
|
|
143
|
+
- Creating a new `.env`, `.env.local`, `.env.production`, etc.
|
|
144
|
+
- Editing any existing `.env*` file
|
|
145
|
+
|
|
146
|
+
Editor swap files (`.env.swp`, `.env~`, `.env.bak`) are ignored.
|
|
118
147
|
|
|
119
148
|
---
|
|
120
149
|
|
|
@@ -122,18 +151,15 @@ const pass = requireEnv('SMTP_PASS');
|
|
|
122
151
|
|
|
123
152
|
```ts
|
|
124
153
|
biniEnv({
|
|
125
|
-
enabled: true,
|
|
154
|
+
enabled : true,
|
|
126
155
|
clearViteHeader: true,
|
|
127
|
-
logo: 'ß',
|
|
128
|
-
envPrefix: ['BINI_', 'VITE_']
|
|
156
|
+
logo : 'ß',
|
|
157
|
+
envPrefix : ['BINI_', 'VITE_'],
|
|
158
|
+
loadInPreview : true,
|
|
129
159
|
});
|
|
130
160
|
```
|
|
131
161
|
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
If you change `envPrefix`, you are changing what gets exposed to the browser.
|
|
135
|
-
|
|
136
|
-
Break this → you leak secrets.
|
|
162
|
+
> **Critical:** If you change `envPrefix`, you are changing what gets exposed to the browser. Break this → you leak secrets.
|
|
137
163
|
|
|
138
164
|
---
|
|
139
165
|
|
|
@@ -141,38 +167,43 @@ Break this → you leak secrets.
|
|
|
141
167
|
|
|
142
168
|
### `getEnv(key)`
|
|
143
169
|
|
|
144
|
-
Returns `string | undefined`.
|
|
170
|
+
Returns `string | undefined`. Auto-imported in API routes.
|
|
145
171
|
|
|
146
|
-
|
|
172
|
+
Lookup order:
|
|
173
|
+
1. `Deno.env.get(key)` — Deno / Netlify Edge
|
|
174
|
+
2. `process.env[key]` — Node, Bun, Vercel Edge, Cloudflare Workers
|
|
175
|
+
3. `import.meta.env[key]` — Vite client (prefixed variants only)
|
|
147
176
|
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
---
|
|
177
|
+
```ts
|
|
178
|
+
const debug = getEnv('DEBUG_MODE');
|
|
179
|
+
```
|
|
153
180
|
|
|
154
181
|
### `requireEnv(key)`
|
|
155
182
|
|
|
156
|
-
Same as `getEnv` but throws:
|
|
183
|
+
Same as `getEnv` but throws if missing:
|
|
157
184
|
|
|
158
|
-
```
|
|
185
|
+
```
|
|
159
186
|
[bini-env] Missing required environment variable: "SMTP_PASS"
|
|
187
|
+
→ In development: add it to your .env file.
|
|
188
|
+
→ In production: set it in your hosting dashboard.
|
|
160
189
|
```
|
|
161
190
|
|
|
162
|
-
Use this for anything critical.
|
|
191
|
+
Use this for anything critical that must exist at startup.
|
|
163
192
|
|
|
164
|
-
|
|
193
|
+
```ts
|
|
194
|
+
const dbUrl = requireEnv('DATABASE_URL');
|
|
195
|
+
```
|
|
165
196
|
|
|
166
197
|
### `biniEnv(options)`
|
|
167
198
|
|
|
168
|
-
Vite plugin.
|
|
169
|
-
|
|
170
|
-
If your plugin order is wrong and things break, that’s on your config.
|
|
199
|
+
Vite plugin. Handles `.env` loading, terminal output, and dev server watching.
|
|
171
200
|
|
|
172
201
|
---
|
|
173
202
|
|
|
174
203
|
## 📂 Env File Resolution Order
|
|
175
204
|
|
|
205
|
+
Higher entries take priority:
|
|
206
|
+
|
|
176
207
|
1. `.env.local`
|
|
177
208
|
2. `.env.[mode].local`
|
|
178
209
|
3. `.env.[mode]`
|
|
@@ -196,22 +227,23 @@ If you see overhead in production, you did something wrong.
|
|
|
196
227
|
|
|
197
228
|
## 🔥 Common Failure Modes
|
|
198
229
|
|
|
199
|
-
###
|
|
200
|
-
|
|
201
|
-
You forgot the prefix.
|
|
230
|
+
### "Env is undefined"
|
|
231
|
+
You forgot the prefix (`BINI_` or `VITE_`) on a client-side var, or you're reading an unprefixed secret from `import.meta.env`.
|
|
202
232
|
|
|
203
|
-
###
|
|
233
|
+
### "Works in dev, broken in prod"
|
|
234
|
+
You relied on a `.env` file in production. Set vars in your hosting dashboard.
|
|
204
235
|
|
|
205
|
-
|
|
236
|
+
### "Secrets leaked"
|
|
237
|
+
You exposed them via `BINI_` or `VITE_` prefix. Those are public. Use unprefixed vars for secrets.
|
|
206
238
|
|
|
207
|
-
###
|
|
239
|
+
### "Works on Node, broken on Vercel/Netlify/Cloudflare"
|
|
240
|
+
You had the old version with static `fs`/`path` imports. Upgrade — this version is edge-safe.
|
|
208
241
|
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
### 4. “Types not found”
|
|
212
|
-
|
|
213
|
-
Add:
|
|
242
|
+
### "Cloudflare secret not found"
|
|
243
|
+
Secrets added via `wrangler secret put` are only available via `c.env` inside your handler. Use `wrangler.toml [vars]` for anything `getEnv` needs at the top level.
|
|
214
244
|
|
|
245
|
+
### "Types not found"
|
|
246
|
+
Add to your `tsconfig` or entry file:
|
|
215
247
|
```ts
|
|
216
248
|
/// <reference types="vite/client" />
|
|
217
249
|
```
|
|
@@ -223,24 +255,22 @@ Add:
|
|
|
223
255
|
This library is intentionally simple.
|
|
224
256
|
|
|
225
257
|
If you need:
|
|
258
|
+
- Secret rotation
|
|
259
|
+
- Encrypted envs
|
|
260
|
+
- Runtime validation schemas
|
|
226
261
|
|
|
227
|
-
|
|
228
|
-
* encrypted envs
|
|
229
|
-
* runtime validation schemas
|
|
230
|
-
|
|
231
|
-
That’s **your job**, not this package.
|
|
262
|
+
That's **your job**, not this package.
|
|
232
263
|
|
|
233
264
|
---
|
|
234
265
|
|
|
235
266
|
## 🤝 Contributing
|
|
236
267
|
|
|
237
268
|
PRs welcome — but:
|
|
269
|
+
- No bloat
|
|
270
|
+
- No magic
|
|
271
|
+
- No runtime cost
|
|
238
272
|
|
|
239
|
-
|
|
240
|
-
* No magic
|
|
241
|
-
* No runtime cost
|
|
242
|
-
|
|
243
|
-
If it slows startup or increases bundle size, it’s getting rejected.
|
|
273
|
+
If it slows startup or increases bundle size, it's getting rejected.
|
|
244
274
|
|
|
245
275
|
---
|
|
246
276
|
|
|
@@ -250,4 +280,4 @@ MIT © Bini.js Team
|
|
|
250
280
|
|
|
251
281
|
---
|
|
252
282
|
|
|
253
|
-
**Ship fast. Leak nothing. Blame config, not tooling.**
|
|
283
|
+
**Ship fast. Leak nothing. Blame config, not tooling.**
|
package/dist/index.d.mts
CHANGED
|
@@ -8,16 +8,22 @@ interface BiniEnvPluginOptions {
|
|
|
8
8
|
readonly logo?: string;
|
|
9
9
|
readonly envPrefix?: string | string[];
|
|
10
10
|
readonly loadInPreview?: boolean;
|
|
11
|
+
/** @deprecated Suppression is always applied at module load; this option is ignored. */
|
|
11
12
|
readonly suppressDotenvLogs?: boolean;
|
|
12
13
|
}
|
|
13
14
|
interface DetectedEnvFile {
|
|
14
15
|
readonly name: string;
|
|
15
16
|
readonly path: string;
|
|
16
17
|
}
|
|
18
|
+
declare const biniLogger: {
|
|
19
|
+
readonly info: (msg: string) => void;
|
|
20
|
+
readonly warn: (msg: string) => void;
|
|
21
|
+
readonly error: (msg: string, err?: unknown) => void;
|
|
22
|
+
};
|
|
17
23
|
declare function getEnv(key: string): string | undefined;
|
|
18
24
|
declare function requireEnv(key: string): string;
|
|
19
25
|
declare function loadEnv(projectRoot?: string): Promise<void>;
|
|
20
26
|
declare function detectEnvFiles(projectRoot?: string): DetectedEnvFile[];
|
|
21
27
|
declare function biniEnv(options?: Readonly<BiniEnvPluginOptions>): Plugin;
|
|
22
28
|
|
|
23
|
-
export { type BiniEnvPluginOptions, type DetectedEnvFile, biniEnv, detectEnvFiles, getEnv, loadEnv, requireEnv, suppressDotenvLogsGlobally };
|
|
29
|
+
export { type BiniEnvPluginOptions, type DetectedEnvFile, biniEnv, biniLogger, detectEnvFiles, getEnv, loadEnv, requireEnv, suppressDotenvLogsGlobally };
|
package/dist/index.d.ts
CHANGED
|
@@ -8,16 +8,22 @@ interface BiniEnvPluginOptions {
|
|
|
8
8
|
readonly logo?: string;
|
|
9
9
|
readonly envPrefix?: string | string[];
|
|
10
10
|
readonly loadInPreview?: boolean;
|
|
11
|
+
/** @deprecated Suppression is always applied at module load; this option is ignored. */
|
|
11
12
|
readonly suppressDotenvLogs?: boolean;
|
|
12
13
|
}
|
|
13
14
|
interface DetectedEnvFile {
|
|
14
15
|
readonly name: string;
|
|
15
16
|
readonly path: string;
|
|
16
17
|
}
|
|
18
|
+
declare const biniLogger: {
|
|
19
|
+
readonly info: (msg: string) => void;
|
|
20
|
+
readonly warn: (msg: string) => void;
|
|
21
|
+
readonly error: (msg: string, err?: unknown) => void;
|
|
22
|
+
};
|
|
17
23
|
declare function getEnv(key: string): string | undefined;
|
|
18
24
|
declare function requireEnv(key: string): string;
|
|
19
25
|
declare function loadEnv(projectRoot?: string): Promise<void>;
|
|
20
26
|
declare function detectEnvFiles(projectRoot?: string): DetectedEnvFile[];
|
|
21
27
|
declare function biniEnv(options?: Readonly<BiniEnvPluginOptions>): Plugin;
|
|
22
28
|
|
|
23
|
-
export { type BiniEnvPluginOptions, type DetectedEnvFile, biniEnv, detectEnvFiles, getEnv, loadEnv, requireEnv, suppressDotenvLogsGlobally };
|
|
29
|
+
export { type BiniEnvPluginOptions, type DetectedEnvFile, biniEnv, biniLogger, detectEnvFiles, getEnv, loadEnv, requireEnv, suppressDotenvLogsGlobally };
|