bini-env 1.1.0 → 1.1.2
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 +272 -167
- package/dist/index.d.mts +21 -19
- package/dist/index.d.ts +21 -19
- package/dist/index.js +49 -178
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +50 -176
- package/dist/index.mjs.map +1 -1
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -3,43 +3,31 @@
|
|
|
3
3
|

|
|
4
4
|

|
|
5
5
|

|
|
6
|
-

|
|
7
|
+

|
|
8
|
+

|
|
9
9
|

|
|
10
10
|
|
|
11
|
-
**
|
|
12
|
-
Powered by [Hono](https://hono.dev) — reads env vars from the request context so variables are always resolved from the correct runtime binding, whether you're on Node.js, Bun, Deno, Vercel Edge, Netlify Edge, or Cloudflare Workers.
|
|
11
|
+
**Environment variable system + Vite plugin for Bini.js**
|
|
13
12
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
## ⚠️ Before You Use This
|
|
17
|
-
|
|
18
|
-
This library **does NOT magically make env vars safe**.
|
|
19
|
-
|
|
20
|
-
- Anything exposed to the client (`import.meta.env`) is **public**.
|
|
21
|
-
- Only server-side code (`getEnv`, `requireEnv`) can safely access secrets.
|
|
22
|
-
- Misconfigured prefixes = **data leak**.
|
|
23
|
-
|
|
24
|
-
If you don't understand this, stop and fix that first.
|
|
13
|
+
Powered by [Hono](https://hono.dev) — reads env vars from the Hono request context so variables are always resolved from the correct runtime binding, whether you're on Node.js, Bun, Deno, Vercel Edge, Netlify Edge, or Cloudflare Workers.
|
|
25
14
|
|
|
26
15
|
---
|
|
27
16
|
|
|
28
|
-
##
|
|
17
|
+
## Features
|
|
29
18
|
|
|
30
|
-
- **Hono-native
|
|
31
|
-
- **Universal runtime
|
|
32
|
-
- **Zero dotenv** — no `.env`
|
|
33
|
-
- **
|
|
34
|
-
- **
|
|
35
|
-
- **Prefix control** —
|
|
36
|
-
- **Tree-shakeable** —
|
|
37
|
-
- **Typed** — full TypeScript support with exported `HonoContext`
|
|
38
|
-
- **Fast** — zero overhead; no file reads in production
|
|
19
|
+
- **Hono-native** — `getEnv(c, key)` / `requireEnv(c, key)` read directly from the Hono request context
|
|
20
|
+
- **Universal runtime** — CF Workers, Node, Bun, Deno, Vercel Edge, Netlify Edge — Hono's adapter handles all of it
|
|
21
|
+
- **Zero dotenv** — no `.env` parsing at runtime; vars come from the host platform
|
|
22
|
+
- **Vite-native dev** — `.env` loading, file watching, and server restarts are handled by Vite itself
|
|
23
|
+
- **Dev banner** — prints `ß Bini.js (dev/preview)` and lists detected `.env` files on server start
|
|
24
|
+
- **Prefix control** — `BINI_` and `VITE_` included out of the box, extend with your own
|
|
25
|
+
- **Tree-shakeable** — nothing lands in the client bundle that shouldn't
|
|
26
|
+
- **Typed** — full TypeScript support with an exported `HonoContext` type
|
|
39
27
|
|
|
40
28
|
---
|
|
41
29
|
|
|
42
|
-
##
|
|
30
|
+
## Installation
|
|
43
31
|
|
|
44
32
|
```bash
|
|
45
33
|
pnpm add bini-env hono
|
|
@@ -53,7 +41,9 @@ yarn add bini-env hono
|
|
|
53
41
|
|
|
54
42
|
---
|
|
55
43
|
|
|
56
|
-
##
|
|
44
|
+
## Quick Start
|
|
45
|
+
|
|
46
|
+
**1. Register the Vite plugin**
|
|
57
47
|
|
|
58
48
|
```ts
|
|
59
49
|
// vite.config.ts
|
|
@@ -65,18 +55,34 @@ export default defineConfig({
|
|
|
65
55
|
})
|
|
66
56
|
```
|
|
67
57
|
|
|
58
|
+
**2. Read env vars in your Hono handlers**
|
|
59
|
+
|
|
68
60
|
```ts
|
|
69
|
-
// src/app/api/
|
|
61
|
+
// src/app/api/hello.ts
|
|
70
62
|
import { Hono } from 'hono'
|
|
71
63
|
import { getEnv, requireEnv } from 'bini-env'
|
|
72
64
|
|
|
73
65
|
const app = new Hono()
|
|
74
66
|
|
|
75
|
-
app.
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
67
|
+
app.post('/hello', async (c) => {
|
|
68
|
+
try {
|
|
69
|
+
const ctx = c as any
|
|
70
|
+
|
|
71
|
+
// requireEnv throws if the var is missing — fail fast on required config
|
|
72
|
+
const apiKey = requireEnv(ctx, 'MY_API_KEY')
|
|
73
|
+
|
|
74
|
+
// getEnv returns undefined if missing — use ?? to provide a default
|
|
75
|
+
const appName = getEnv(ctx, 'APP_NAME') ?? 'World'
|
|
76
|
+
const timeout = parseInt(getEnv(ctx, 'TIMEOUT_MS') ?? '5000')
|
|
77
|
+
|
|
78
|
+
return c.json({ message: `Hello, ${appName}!` })
|
|
79
|
+
|
|
80
|
+
} catch (error: any) {
|
|
81
|
+
if (error.message?.includes('[bini-env] Missing required')) {
|
|
82
|
+
return c.json({ error: error.message }, 500)
|
|
83
|
+
}
|
|
84
|
+
return c.json({ error: 'Something went wrong.' }, 500)
|
|
85
|
+
}
|
|
80
86
|
})
|
|
81
87
|
|
|
82
88
|
export default app
|
|
@@ -84,155 +90,233 @@ export default app
|
|
|
84
90
|
|
|
85
91
|
---
|
|
86
92
|
|
|
87
|
-
##
|
|
93
|
+
## Usage Pattern
|
|
88
94
|
|
|
89
|
-
|
|
95
|
+
**Always pass `c` explicitly.** Cast it once at the top of the handler, then use `ctx` throughout.
|
|
90
96
|
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
97
|
+
```ts
|
|
98
|
+
app.post('/example', async (c) => {
|
|
99
|
+
try {
|
|
100
|
+
const ctx = c as any
|
|
101
|
+
|
|
102
|
+
// Required vars — handler throws immediately if missing
|
|
103
|
+
const dbUrl = requireEnv(ctx, 'DATABASE_URL')
|
|
104
|
+
const apiKey = requireEnv(ctx, 'STRIPE_SECRET_KEY')
|
|
105
|
+
|
|
106
|
+
// Optional vars — fall back to sensible defaults
|
|
107
|
+
const model = getEnv(ctx, 'AI_MODEL') ?? 'gpt-4o'
|
|
108
|
+
const region = getEnv(ctx, 'AWS_REGION') ?? 'us-east-1'
|
|
109
|
+
const maxRetries = parseInt(getEnv(ctx, 'MAX_RETRIES') ?? '3')
|
|
110
|
+
const debug = getEnv(ctx, 'DEBUG_MODE') === 'true'
|
|
111
|
+
|
|
112
|
+
// ... rest of handler
|
|
113
|
+
|
|
114
|
+
} catch (error: any) {
|
|
115
|
+
if (error.message?.includes('[bini-env] Missing required')) {
|
|
116
|
+
return c.json({ error: error.message }, 500)
|
|
117
|
+
}
|
|
118
|
+
return c.json({ error: 'Something went wrong.' }, 500)
|
|
119
|
+
}
|
|
120
|
+
})
|
|
96
121
|
```
|
|
97
122
|
|
|
123
|
+
**The pattern in three steps:**
|
|
124
|
+
|
|
125
|
+
1. `const ctx = c as any` — once, at the top of the handler
|
|
126
|
+
2. `requireEnv(ctx, 'KEY')` — for vars the handler cannot run without
|
|
127
|
+
3. `getEnv(ctx, 'KEY') ?? 'default'` — for optional vars with sensible defaults
|
|
128
|
+
|
|
129
|
+
---
|
|
130
|
+
|
|
131
|
+
## Environment Prefixes
|
|
132
|
+
|
|
133
|
+
### The Truth About Prefixes
|
|
134
|
+
|
|
135
|
+
**Both `BINI_` and `VITE_` work exactly the same way.** Neither is special.
|
|
136
|
+
|
|
137
|
+
| Prefix | Exposed to Browser? | Default Behavior |
|
|
138
|
+
|--------|--------------------|------------------|
|
|
139
|
+
| `BINI_` | **Yes** | Included in `envPrefix` by default |
|
|
140
|
+
| `VITE_` | **Yes** | Vite's default prefix (always included) |
|
|
141
|
+
| Any prefix you add | **Yes** | If you add it to `envPrefix` |
|
|
142
|
+
| No prefix | **No** | Not exposed to browser |
|
|
143
|
+
|
|
144
|
+
### What `biniEnv()` Actually Does
|
|
145
|
+
|
|
98
146
|
```ts
|
|
99
|
-
|
|
147
|
+
// biniEnv() sets envPrefix to:
|
|
148
|
+
envPrefix: ['BINI_', 'VITE_']
|
|
149
|
+
|
|
150
|
+
// This tells Vite: "Expose ALL variables starting with BINI_ or VITE_ to import.meta.env"
|
|
100
151
|
```
|
|
101
152
|
|
|
102
|
-
|
|
153
|
+
**This means:**
|
|
154
|
+
- ✅ `BINI_API_KEY` → available in browser via `import.meta.env.BINI_API_KEY`
|
|
155
|
+
- ✅ `VITE_API_URL` → available in browser via `import.meta.env.VITE_API_URL`
|
|
156
|
+
- ❌ `DATABASE_URL` → NOT available in browser (no prefix)
|
|
157
|
+
|
|
158
|
+
### How to Keep Secrets Secret
|
|
103
159
|
|
|
104
|
-
|
|
160
|
+
**Use no prefix for secrets:**
|
|
105
161
|
|
|
106
162
|
```env
|
|
107
|
-
|
|
163
|
+
# ✅ NOT exposed to browser
|
|
108
164
|
DATABASE_URL=postgres://...
|
|
165
|
+
STRIPE_SECRET_KEY=sk_live_...
|
|
166
|
+
AWS_SECRET_KEY=...
|
|
109
167
|
```
|
|
110
168
|
|
|
169
|
+
**If you want `BINI_` to be private, remove it from `envPrefix`:**
|
|
170
|
+
|
|
111
171
|
```ts
|
|
112
|
-
//
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
const db = getEnv(ctx, 'DATABASE_URL') // returns undefined if missing
|
|
172
|
+
// vite.config.ts
|
|
173
|
+
biniEnv({ envPrefix: ['VITE_'] }) // Only VITE_ exposed
|
|
174
|
+
// BINI_ is no longer exposed to the browser
|
|
116
175
|
```
|
|
117
176
|
|
|
118
|
-
|
|
177
|
+
### Choosing the Right Prefix
|
|
119
178
|
|
|
120
|
-
|
|
179
|
+
Since `BINI_` and `VITE_` are both exposed by default:
|
|
121
180
|
|
|
122
|
-
|
|
181
|
+
```env
|
|
182
|
+
# ✅ Use no prefix for secrets
|
|
183
|
+
DATABASE_URL=postgres://...
|
|
184
|
+
STRIPE_SECRET_KEY=sk_live_...
|
|
123
185
|
|
|
124
|
-
|
|
186
|
+
# ✅ Use any prefix for public config
|
|
187
|
+
BINI_API_URL=https://api.example.com
|
|
188
|
+
VITE_ANALYTICS_ID=UA-XXXX
|
|
189
|
+
PUBLIC_APP_NAME=MyApp # If you add PUBLIC_ to envPrefix
|
|
190
|
+
```
|
|
125
191
|
|
|
126
|
-
|
|
127
|
-
| -------------------- | ------------- | ----------------------------------- | ---------------------------- |
|
|
128
|
-
| Node.js | Node | System env / platform dashboard | `process.env` ✅ |
|
|
129
|
-
| Bun | Bun | System env / platform dashboard | `process.env` ✅ |
|
|
130
|
-
| Vercel Edge | V8 isolate | Project settings → Environment Vars | `process.env` ✅ |
|
|
131
|
-
| Netlify Edge | Deno | Site settings → Environment Vars | `Deno.env.get()` ✅ |
|
|
132
|
-
| Cloudflare Workers | V8 isolate | `wrangler.toml [vars]` / dashboard | CF bindings via `c.env` ✅ |
|
|
133
|
-
| Deno Deploy | Deno | Project settings → Environment Vars | `Deno.env.get()` ✅ |
|
|
192
|
+
### Prefix Summary
|
|
134
193
|
|
|
135
|
-
|
|
194
|
+
| Prefix | Exposed to Browser? | Use For |
|
|
195
|
+
|--------|--------------------|---------|
|
|
196
|
+
| No prefix | ❌ Never | **SECRETS** — API keys, database URLs, tokens |
|
|
197
|
+
| `BINI_` | ✅ Yes (by default) | Public config (same as VITE_) |
|
|
198
|
+
| `VITE_` | ✅ Yes (always) | Public config (Vite's default) |
|
|
199
|
+
| Custom prefix | ✅ If in `envPrefix` | Public config you want to control |
|
|
136
200
|
|
|
137
201
|
---
|
|
138
202
|
|
|
139
|
-
##
|
|
203
|
+
## Platform Support
|
|
140
204
|
|
|
141
|
-
`getEnv(c
|
|
205
|
+
`getEnv` and `requireEnv` delegate to Hono's `env(c)` adapter, which reads from the correct source on every supported platform automatically. Your code never changes regardless of where it deploys.
|
|
142
206
|
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
207
|
+
| Platform | Runtime | Env source | How Hono reads it |
|
|
208
|
+
|----------|---------|------------|-------------------|
|
|
209
|
+
| Node.js | Node | System env / platform dashboard | `process.env` ✅ |
|
|
210
|
+
| Bun | Bun | System env / platform dashboard | `process.env` ✅ |
|
|
211
|
+
| Vercel Edge | V8 isolate | Project settings → Env Vars | `process.env` ✅ |
|
|
212
|
+
| Netlify Edge | Deno | Site settings → Env Vars | `Deno.env.get()` ✅ |
|
|
213
|
+
| Cloudflare Workers | V8 isolate | `wrangler.toml` / dashboard | CF bindings via `c.env` ✅ |
|
|
214
|
+
| Deno Deploy | Deno | Project settings → Env Vars | `Deno.env.get()` ✅ |
|
|
146
215
|
|
|
147
|
-
`
|
|
216
|
+
> **Cloudflare note:** Secrets set via `wrangler secret put` are only available inside the fetch handler via `c.env` — this is a Workers architecture constraint. `getEnv(ctx, key)` reads them correctly as long as you pass `c`.
|
|
148
217
|
|
|
149
218
|
---
|
|
150
219
|
|
|
151
|
-
##
|
|
220
|
+
## How It Works
|
|
152
221
|
|
|
153
|
-
The
|
|
222
|
+
### The plugin — `biniEnv()`
|
|
154
223
|
|
|
155
|
-
|
|
156
|
-
|
|
224
|
+
Does two things: tells Vite which env prefixes to expose to `import.meta.env`, and prints the `ß Bini.js` banner with detected `.env` files when the dev or preview server starts.
|
|
225
|
+
|
|
226
|
+
```ts
|
|
227
|
+
// what biniEnv() does internally
|
|
228
|
+
config() {
|
|
229
|
+
return { envPrefix: ['BINI_', 'VITE_', ...yourExtras] }
|
|
230
|
+
}
|
|
157
231
|
```
|
|
158
232
|
|
|
159
|
-
|
|
160
|
-
- Creating a new `.env`, `.env.local`, `.env.production`, etc.
|
|
161
|
-
- Editing any existing `.env*` file
|
|
233
|
+
On server start you will see:
|
|
162
234
|
|
|
163
|
-
|
|
235
|
+
```
|
|
236
|
+
ß Bini.js (dev)
|
|
237
|
+
➜ Environments: .env.local, .env
|
|
238
|
+
➜ Local: http://localhost:3000/
|
|
239
|
+
➜ Network: http://192.168.1.7:3000/
|
|
240
|
+
```
|
|
164
241
|
|
|
165
|
-
|
|
242
|
+
Vite handles everything else natively: loading `.env`, `.env.local`, `.env.[mode]` files during dev, watching and restarting on change, injecting prefixed vars into `import.meta.env` at build time, and HMR when env files change. bini-env does not reimplement any of that.
|
|
166
243
|
|
|
167
|
-
|
|
244
|
+
### The env functions — `getEnv` / `requireEnv`
|
|
168
245
|
|
|
169
|
-
|
|
170
|
-
biniEnv({
|
|
171
|
-
enabled : true,
|
|
172
|
-
clearViteHeader: true,
|
|
173
|
-
logo : 'ß',
|
|
174
|
-
envPrefix : ['BINI_', 'VITE_'],
|
|
175
|
-
loadInPreview : true,
|
|
176
|
-
})
|
|
177
|
-
```
|
|
246
|
+
Both functions read exclusively from `env(c)` via `hono/adapter` — the Hono request context. There are no `process.env` fallbacks. Every read is request-scoped and resolved by Hono's adapter for the current platform.
|
|
178
247
|
|
|
179
|
-
|
|
248
|
+
`dotenv` is never used. In production, vars are set in your hosting platform's environment config.
|
|
180
249
|
|
|
181
250
|
---
|
|
182
251
|
|
|
183
|
-
##
|
|
252
|
+
## API Reference
|
|
184
253
|
|
|
185
254
|
### `getEnv(c, key)`
|
|
186
255
|
|
|
187
|
-
Returns `string | undefined`. Reads from Hono
|
|
256
|
+
Returns `string | undefined`. Reads from the Hono request context.
|
|
188
257
|
|
|
189
258
|
```ts
|
|
190
|
-
|
|
191
|
-
const ctx = c as any
|
|
192
|
-
const debug = getEnv(ctx, 'DEBUG_MODE')
|
|
193
|
-
```
|
|
259
|
+
app.get('/config', async (c) => {
|
|
260
|
+
const ctx = c as any
|
|
194
261
|
|
|
195
|
-
|
|
262
|
+
const region = getEnv(ctx, 'AWS_REGION') ?? 'us-east-1'
|
|
263
|
+
const logLevel = getEnv(ctx, 'LOG_LEVEL') ?? 'info'
|
|
264
|
+
const debug = getEnv(ctx, 'DEBUG_MODE') === 'true'
|
|
196
265
|
|
|
197
|
-
|
|
198
|
-
|
|
266
|
+
return c.json({ region, logLevel, debug })
|
|
267
|
+
})
|
|
199
268
|
```
|
|
200
269
|
|
|
201
270
|
### `requireEnv(c, key)`
|
|
202
271
|
|
|
203
|
-
|
|
272
|
+
Returns `string`. Throws immediately if the variable is missing or empty. Logs a descriptive error to the terminal on failure.
|
|
204
273
|
|
|
205
|
-
```
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
274
|
+
```ts
|
|
275
|
+
app.post('/send-email', async (c) => {
|
|
276
|
+
try {
|
|
277
|
+
const ctx = c as any
|
|
278
|
+
|
|
279
|
+
const smtpHost = requireEnv(ctx, 'SMTP_HOST')
|
|
280
|
+
const smtpPass = requireEnv(ctx, 'SMTP_PASS')
|
|
281
|
+
const smtpPort = parseInt(getEnv(ctx, 'SMTP_PORT') ?? '587')
|
|
282
|
+
|
|
283
|
+
// ... send email
|
|
284
|
+
|
|
285
|
+
return c.json({ sent: true })
|
|
286
|
+
|
|
287
|
+
} catch (error: any) {
|
|
288
|
+
if (error.message?.includes('[bini-env] Missing required')) {
|
|
289
|
+
return c.json({ error: error.message }, 500)
|
|
290
|
+
}
|
|
291
|
+
return c.json({ error: 'Failed to send email.' }, 500)
|
|
292
|
+
}
|
|
293
|
+
})
|
|
209
294
|
```
|
|
210
295
|
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
296
|
+
On failure, the terminal will show:
|
|
297
|
+
|
|
298
|
+
```
|
|
299
|
+
[bini-env] error Missing required environment variable: "SMTP_HOST"
|
|
300
|
+
-> Set it in your platform's env config or hosting dashboard.
|
|
215
301
|
```
|
|
216
302
|
|
|
217
|
-
### `
|
|
303
|
+
### `biniEnv(options?)`
|
|
218
304
|
|
|
219
|
-
|
|
305
|
+
Vite plugin. Registers `BINI_` and `VITE_` as env prefixes and optionally adds more.
|
|
220
306
|
|
|
221
307
|
```ts
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
return next()
|
|
226
|
-
})
|
|
308
|
+
biniEnv()
|
|
309
|
+
// or with extra prefixes
|
|
310
|
+
biniEnv({ envPrefix: ['MY_PUBLIC_'] })
|
|
227
311
|
```
|
|
228
312
|
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
313
|
+
| Option | Type | Default | Description |
|
|
314
|
+
|--------|------|---------|-------------|
|
|
315
|
+
| `envPrefix` | `string \| string[]` | `[]` | Extra prefixes to expose to `import.meta.env`, in addition to `BINI_` and `VITE_` |
|
|
232
316
|
|
|
233
317
|
### `biniLogger`
|
|
234
318
|
|
|
235
|
-
|
|
319
|
+
Vite-style terminal logger. Use it in your own Bini.js plugins or server-side code.
|
|
236
320
|
|
|
237
321
|
```ts
|
|
238
322
|
import { biniLogger } from 'bini-env'
|
|
@@ -244,102 +328,123 @@ biniLogger.error('Something broke', error)
|
|
|
244
328
|
|
|
245
329
|
### `HonoContext`
|
|
246
330
|
|
|
247
|
-
Exported
|
|
331
|
+
Exported type (`Context` from Hono). Use it to type helper functions that group env reads, so you only cast `c as any` once per entry point.
|
|
248
332
|
|
|
249
333
|
```ts
|
|
250
334
|
import type { HonoContext } from 'bini-env'
|
|
251
335
|
|
|
252
|
-
function
|
|
253
|
-
|
|
336
|
+
function readDbConfig(c: HonoContext) {
|
|
337
|
+
const ctx = c as any
|
|
338
|
+
return {
|
|
339
|
+
url: requireEnv(ctx, 'DATABASE_URL'),
|
|
340
|
+
poolSize: parseInt(getEnv(ctx, 'DB_POOL_SIZE') ?? '10'),
|
|
341
|
+
ssl: getEnv(ctx, 'DB_SSL') !== 'false',
|
|
342
|
+
}
|
|
254
343
|
}
|
|
255
344
|
```
|
|
256
345
|
|
|
257
346
|
---
|
|
258
347
|
|
|
259
|
-
##
|
|
348
|
+
## Security Best Practices
|
|
260
349
|
|
|
261
|
-
|
|
350
|
+
### Rule 1: Never Prefix Secrets
|
|
351
|
+
```env
|
|
352
|
+
# ❌ BAD - This will be exposed to the browser!
|
|
353
|
+
BINI_DATABASE_URL=postgres://...
|
|
262
354
|
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
4. `.env`
|
|
355
|
+
# ✅ GOOD - Not exposed to browser
|
|
356
|
+
DATABASE_URL=postgres://...
|
|
357
|
+
```
|
|
267
358
|
|
|
268
|
-
|
|
359
|
+
### Rule 2: Use No Prefix for Anything Secret
|
|
360
|
+
```env
|
|
361
|
+
# ✅ Safe - Not exposed
|
|
362
|
+
DATABASE_URL=postgres://...
|
|
363
|
+
STRIPE_SECRET=sk_live_...
|
|
364
|
+
AWS_SECRET_KEY=...
|
|
365
|
+
```
|
|
366
|
+
|
|
367
|
+
### Rule 3: Use Any Prefix for Public Data
|
|
368
|
+
```env
|
|
369
|
+
# ✅ Safe - Public data
|
|
370
|
+
BINI_API_URL=https://api.example.com
|
|
371
|
+
VITE_GA_ID=UA-XXXXX
|
|
372
|
+
PUBLIC_APP_NAME=MyApp
|
|
373
|
+
```
|
|
374
|
+
|
|
375
|
+
### Rule 4: Remove `BINI_` from envPrefix if You Want It Private
|
|
376
|
+
```ts
|
|
377
|
+
// To make BINI_ private, remove it from envPrefix
|
|
378
|
+
biniEnv({ envPrefix: ['VITE_'] })
|
|
379
|
+
```
|
|
269
380
|
|
|
270
381
|
---
|
|
271
382
|
|
|
272
|
-
##
|
|
383
|
+
## Performance
|
|
273
384
|
|
|
274
|
-
| Metric
|
|
275
|
-
|
|
276
|
-
| File
|
|
277
|
-
| Runtime
|
|
278
|
-
| Bundle
|
|
385
|
+
| Metric | Dev | Prod |
|
|
386
|
+
|--------|-----|------|
|
|
387
|
+
| File reads | 0 | 0 |
|
|
388
|
+
| Runtime cost | ~0ms | 0 |
|
|
389
|
+
| Bundle impact | Minimal | Tree-shaken |
|
|
279
390
|
|
|
280
|
-
No dotenv. No disk reads. No
|
|
391
|
+
No dotenv. No disk reads. No caching layer. `getEnv` is a direct call to Hono's adapter on every invocation — request-scoped and correct.
|
|
281
392
|
|
|
282
393
|
---
|
|
283
394
|
|
|
284
|
-
##
|
|
395
|
+
## Troubleshooting
|
|
396
|
+
|
|
397
|
+
### "My BINI_ var is exposed to the browser!"
|
|
398
|
+
|
|
399
|
+
Yes — by design. `BINI_` is included in `envPrefix` by default. Use no prefix for secrets.
|
|
285
400
|
|
|
286
|
-
### "Env is undefined in production"
|
|
287
|
-
You relied on a `.env` file in production. Set vars in your hosting dashboard — bini-env does not read `.env` files at runtime.
|
|
401
|
+
### "Env var is undefined in production"
|
|
288
402
|
|
|
289
|
-
|
|
290
|
-
Same as above. Production vars come from the host, not from files.
|
|
403
|
+
Vars set in `.env` files are only loaded by Vite during local development. In production, set your variables in your hosting platform's environment dashboard (Vercel, Netlify, Cloudflare, etc.).
|
|
291
404
|
|
|
292
|
-
### "
|
|
293
|
-
|
|
405
|
+
### "Works in dev, undefined in prod"
|
|
406
|
+
|
|
407
|
+
Same as above. Local dev works because Vite loads `.env` files automatically. Production requires platform-level configuration.
|
|
294
408
|
|
|
295
409
|
### "Cloudflare secret not found"
|
|
296
|
-
|
|
410
|
+
|
|
411
|
+
Secrets set via `wrangler secret put` are only available via `c.env` inside a handler — which is exactly what `getEnv(ctx, key)` reads. Ensure you are passing `c` to the function.
|
|
297
412
|
|
|
298
413
|
### "TypeScript error: Context not assignable to HonoContext"
|
|
299
|
-
|
|
414
|
+
|
|
415
|
+
Hono 4.12+ added a symbol to `HonoRequest` that can break structural assignability in strict TypeScript projects. Cast once per handler:
|
|
300
416
|
|
|
301
417
|
```ts
|
|
302
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
303
418
|
const ctx = c as any
|
|
304
|
-
const key = requireEnv(ctx, 'MY_KEY')
|
|
305
419
|
```
|
|
306
420
|
|
|
307
421
|
### "Types not found"
|
|
308
|
-
|
|
422
|
+
|
|
423
|
+
Add to your `tsconfig.json` or entry file:
|
|
424
|
+
|
|
309
425
|
```ts
|
|
310
426
|
/// <reference types="vite/client" />
|
|
311
427
|
```
|
|
312
428
|
|
|
313
429
|
---
|
|
314
430
|
|
|
315
|
-
##
|
|
316
|
-
|
|
317
|
-
This library is intentionally simple.
|
|
431
|
+
## Compatibility
|
|
318
432
|
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
433
|
+
| Tool | Version | Notes |
|
|
434
|
+
|------|---------|-------|
|
|
435
|
+
| Vite | 8.x | `envPrefix` hook is fully compatible; empty prefix array is never passed |
|
|
436
|
+
| Hono | 4.x | `hono/adapter` `env()` is stable across all 4.x releases |
|
|
437
|
+
| TypeScript | 5.x | Full type support via exported `HonoContext` |
|
|
438
|
+
| Node.js | ≥ 20.19 | Minimum required version |
|
|
325
439
|
|
|
326
440
|
---
|
|
327
441
|
|
|
328
|
-
##
|
|
329
|
-
|
|
330
|
-
PRs welcome — but:
|
|
331
|
-
- No bloat
|
|
332
|
-
- No magic
|
|
333
|
-
- No runtime cost
|
|
442
|
+
## Contributing
|
|
334
443
|
|
|
335
|
-
|
|
444
|
+
PRs are welcome. The goal of this library is to stay minimal and runtime-cost-free. Contributions that add unnecessary complexity, increase bundle size, or introduce startup overhead will not be accepted.
|
|
336
445
|
|
|
337
446
|
---
|
|
338
447
|
|
|
339
|
-
##
|
|
448
|
+
## License
|
|
340
449
|
|
|
341
450
|
MIT © Bini.js Team
|
|
342
|
-
|
|
343
|
-
---
|
|
344
|
-
|
|
345
|
-
**Ship fast. Leak nothing. Blame config, not tooling.**
|
package/dist/index.d.mts
CHANGED
|
@@ -1,32 +1,34 @@
|
|
|
1
|
+
import { Context } from 'hono';
|
|
1
2
|
import { Plugin } from 'vite';
|
|
2
3
|
export { Plugin } from 'vite';
|
|
3
|
-
export { Context } from 'hono';
|
|
4
4
|
|
|
5
|
-
/**
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
5
|
+
/**
|
|
6
|
+
* bini-env/src/index.ts
|
|
7
|
+
*
|
|
8
|
+
* Two responsibilities only:
|
|
9
|
+
* 1. Tell Vite which env prefixes to expose to import.meta.env (biniEnv plugin)
|
|
10
|
+
* 2. Read env vars from the Hono request context (getEnv / requireEnv)
|
|
11
|
+
*
|
|
12
|
+
* process.env is never used. All runtime env access goes through Hono's env() adapter,
|
|
13
|
+
* which handles Node, Bun, Cloudflare Workers, Deno, and Vercel Edge natively.
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Accepts any Hono Context. Typed against Hono's own Context rather than a
|
|
18
|
+
* loose duck type so misuse is caught at the call site.
|
|
19
|
+
*/
|
|
20
|
+
type HonoContext = Context;
|
|
9
21
|
interface BiniEnvPluginOptions {
|
|
10
|
-
|
|
11
|
-
readonly clearViteHeader?: boolean;
|
|
12
|
-
readonly logo?: string;
|
|
22
|
+
/** Extra prefixes to expose to import.meta.env, in addition to BINI_ and VITE_. */
|
|
13
23
|
readonly envPrefix?: string | string[];
|
|
14
|
-
readonly loadInPreview?: boolean;
|
|
15
|
-
}
|
|
16
|
-
interface DetectedEnvFile {
|
|
17
|
-
readonly name: string;
|
|
18
|
-
readonly path: string;
|
|
19
24
|
}
|
|
20
25
|
declare const biniLogger: {
|
|
21
26
|
readonly info: (msg: string) => void;
|
|
22
27
|
readonly warn: (msg: string) => void;
|
|
23
28
|
readonly error: (msg: string, err?: unknown) => void;
|
|
24
29
|
};
|
|
25
|
-
declare function
|
|
26
|
-
declare function
|
|
27
|
-
declare function getEnv(c: HonoContext | string, key?: string): string | undefined;
|
|
28
|
-
declare function requireEnv(c: HonoContext | string, key?: string): string;
|
|
29
|
-
declare function detectEnvFiles(projectRoot?: string): DetectedEnvFile[];
|
|
30
|
+
declare function getEnv(c: HonoContext, key: string): string | undefined;
|
|
31
|
+
declare function requireEnv(c: HonoContext, key: string): string;
|
|
30
32
|
declare function biniEnv(options?: Readonly<BiniEnvPluginOptions>): Plugin;
|
|
31
33
|
|
|
32
|
-
export { type BiniEnvPluginOptions, type
|
|
34
|
+
export { type BiniEnvPluginOptions, type HonoContext, biniEnv, biniLogger, getEnv, requireEnv };
|