bini-env 1.1.1 → 2.0.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/README.md CHANGED
@@ -5,10 +5,10 @@
5
5
  ![license](https://img.shields.io/npm/l/bini-env?style=flat-square)
6
6
  ![vite](https://img.shields.io/badge/vite-8.x-646CFF?style=flat-square&logo=vite)
7
7
  ![hono](https://img.shields.io/badge/hono-4.x-E36002?style=flat-square&logo=hono)
8
- ![typescript](https://img.shields.io/badge/TypeScript-ready-3178C6?style=flat-square&logo=typescript)
8
+ ![typescript](https://img.shields.io/badge/TypeScript-5.x-3178C6?style=flat-square&logo=typescript)
9
9
  ![node](https://img.shields.io/badge/node-%3E%3D20.19-339933?style=flat-square&logo=node.js)
10
10
 
11
- **Zero-config environment variable system + Vite plugin for Bini.js**
11
+ **Environment variable system + Vite plugin for Bini.js**
12
12
 
13
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.
14
14
 
@@ -18,10 +18,11 @@ Powered by [Hono](https://hono.dev) — reads env vars from the Hono request con
18
18
 
19
19
  - **Hono-native** — `getEnv(c, key)` / `requireEnv(c, key)` read directly from the Hono request context
20
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
21
+ - **Zero-config dev secrets** — non-prefixed `.env` values are always mirrored into `process.env` automatically during `vite dev` / `vite preview`, so Node-hosted Hono routes can read them with no manual loop
22
22
  - **Vite-native dev** — `.env` loading, file watching, and server restarts are handled by Vite itself
23
+ - **`envDir`-aware** — respects a custom `envDir` or `root` in your `vite.config.ts` instead of always assuming the working directory
23
24
  - **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
+ - **Fixed, predictable prefixes** — only `BINI_` and `VITE_` are ever exposed to the browser; there's no config surface that can accidentally widen it
25
26
  - **Tree-shakeable** — nothing lands in the client bundle that shouldn't
26
27
  - **Typed** — full TypeScript support with an exported `HonoContext` type
27
28
 
@@ -55,6 +56,8 @@ export default defineConfig({
55
56
  })
56
57
  ```
57
58
 
59
+ `biniEnv()` takes no options — there's nothing to configure.
60
+
58
61
  **2. Read env vars in your Hono handlers**
59
62
 
60
63
  ```ts
@@ -88,6 +91,8 @@ app.post('/hello', async (c) => {
88
91
  export default app
89
92
  ```
90
93
 
94
+ That's it — no manual `loadEnv` loop in `vite.config.ts` needed. `MY_API_KEY` just needs to exist in your `.env` file with no prefix, and `requireEnv` will find it during dev and preview.
95
+
91
96
  ---
92
97
 
93
98
  ## Usage Pattern
@@ -128,55 +133,127 @@ app.post('/example', async (c) => {
128
133
 
129
134
  ---
130
135
 
131
- ## Environment Prefixes
136
+ ## How It Works in Dev
132
137
 
133
- ### `BINI_` — server-side vars
138
+ When you run `vite dev` (or `vite preview`), `biniEnv()` automatically loads your `.env*` files with Vite's own `loadEnv` and mirrors every **non-prefixed** variable into `process.env`. This is what lets `getEnv` / `requireEnv` resolve server-side secrets inside Hono API routes without any extra setup — Hono's `env(c)` adapter reads from `process.env` on Node, and `process.env` is now populated for you.
134
139
 
135
- `BINI_` is bini-env's own prefix. Variables are **not** exposed to `import.meta.env` by default, making it the right choice for server-side config and secrets.
140
+ It reads `.env` files from the same place Vite does: your configured `envDir` if you've set one, otherwise your `root`, otherwise the working directory. A custom `envDir` in `vite.config.ts` is respected automatically. If you've set `envDir: false` in your Vite config to disable `.env` loading entirely, `bini-env` respects that too and skips the mirror.
136
141
 
137
- To additionally expose a `BINI_` var to the client, add it to `envPrefix` explicitly:
142
+ It happens silently in the background when it succeeds — no terminal log, no banner line, nothing to configure. Your existing dev server output (the `ß Bini.js` banner, detected `.env` files, local/network URLs) stays exactly as it was:
138
143
 
139
- ```ts
140
- biniEnv({ envPrefix: ['BINI_', 'VITE_', 'MY_PUBLIC_'] })
144
+ ```
145
+ ß Bini.js (dev)
146
+ ➜ Environments: .env.local, .env
147
+ ➜ Local: http://localhost:3000/
148
+ ➜ Network: http://192.168.1.7:3000/
149
+ ```
150
+
151
+ If loading or mirroring the `.env` file fails for any reason, `bini-env` prints a warning — but the dev server still boots either way, it never throws:
152
+
153
+ ```
154
+ 10:45:55 (!) [bini-env] Failed to inject .env into process.env: <reason>
141
155
  ```
142
156
 
143
- ### `VITE_` — public client vars
157
+ This happens automatically — there is no flag to turn it off. It's core behavior of `biniEnv()`, not an opt-in feature.
144
158
 
145
- `VITE_` is Vite's built-in prefix. Any var starting with `VITE_` is bundled into your client-side JavaScript and is publicly visible.
159
+ **Precedence:** a value already present in `process.env` (set by your OS, shell, or CI) always wins. `.env` file values only fill in variables that aren't already set — they never override real environment configuration.
146
160
 
147
- ```env
148
- VITE_ANALYTICS_ID=UA-XXXX # bundled into client JS — public
149
- VITE_API_URL=https://api.example.com # bundled into client JS — public
161
+ **What gets mirrored:**
162
+
163
+ | Variable | Mirrored into `process.env`? |
164
+ |----------|------------------------------|
165
+ | No prefix (`DATABASE_URL`) | ✅ Yes |
166
+ | `BINI_` / `VITE_` prefixed | ❌ No — already handled via `import.meta.env` |
167
+ | Empty value (`KEY=`) | ❌ No — skipped, so `requireEnv` still fails loudly on a blank placeholder |
168
+ | Already set in the OS/CI environment | ❌ No — existing value is kept |
169
+
170
+ **Scope:**
171
+
172
+ - Runs only for `vite dev` and `vite preview` (`command === 'serve'`)
173
+ - **Never** runs for `vite build` — production bundles and `process.env` at build time are untouched
174
+ - **Production** uses your hosting platform's native env injection (`bini-server`, Vercel, Netlify, Cloudflare, etc.) — nothing extra to do there
175
+ - This behavior is always on — there is no option to disable it
176
+
177
+ **Migrating from a manual loop:** if your project already has this in `vite.config.ts`, you can delete it — `biniEnv()` now does it for you:
178
+
179
+ ```ts
180
+ // vite.config.ts — no longer needed, remove this
181
+ const env = loadEnv(mode, process.cwd(), '')
182
+ for (const [key, value] of Object.entries(env)) {
183
+ if (!key.startsWith('BINI_') && !key.startsWith('VITE_')) {
184
+ process.env[key] = value
185
+ }
186
+ }
150
187
  ```
151
188
 
152
189
  ```ts
153
- // accessible anywhere, including the browser
154
- import.meta.env.VITE_ANALYTICS_ID
190
+ // vite.config.ts — this is all you need now
191
+ export default defineConfig({
192
+ plugins: [biniEnv()]
193
+ })
155
194
  ```
156
195
 
157
- ### Choosing the right prefix
196
+ ---
158
197
 
159
- ```env
160
- # ✓ use VITE_ for anything the browser needs
161
- VITE_API_URL=https://api.example.com
198
+ ## Environment Prefixes
199
+
200
+ ### The Truth About Prefixes
201
+
202
+ **Both `BINI_` and `VITE_` work exactly the same way.** Neither is special, and neither can be changed — `biniEnv()` always exposes exactly these two prefixes to the browser, nothing more.
162
203
 
163
- # ✓ use BINI_ for server-side config
164
- BINI_FEATURE_FLAG=true
204
+ | Prefix | Exposed to Browser? |
205
+ |--------|--------------------|
206
+ | `BINI_` | **Yes** — always |
207
+ | `VITE_` | **Yes** — always (Vite's own default prefix) |
208
+ | No prefix | **No** — mirrored into `process.env` in dev/preview instead |
165
209
 
166
- # ✓ no prefix for secrets — read via getEnv(ctx, key) only
210
+ ### What `biniEnv()` Actually Does
211
+
212
+ ```ts
213
+ // biniEnv() always sets envPrefix to:
214
+ envPrefix: ['BINI_', 'VITE_']
215
+
216
+ // This tells Vite: "Expose ALL variables starting with BINI_ or VITE_ to import.meta.env"
217
+ // Everything else is mirrored into process.env during vite dev / preview
218
+ ```
219
+
220
+ **This means:**
221
+ - ✅ `BINI_API_KEY` → available in browser via `import.meta.env.BINI_API_KEY`
222
+ - ✅ `VITE_API_URL` → available in browser via `import.meta.env.VITE_API_URL`
223
+ - ❌ `DATABASE_URL` → NOT available in browser (no prefix) — available server-side via `getEnv`/`requireEnv`
224
+
225
+ ### How to Keep Secrets Secret
226
+
227
+ **Use no prefix for secrets:**
228
+
229
+ ```env
230
+ # ✅ NOT exposed to browser — mirrored into process.env for server-side use only
167
231
  DATABASE_URL=postgres://...
168
232
  STRIPE_SECRET_KEY=sk_live_...
233
+ AWS_SECRET_KEY=...
169
234
  ```
170
235
 
171
- > Avoid defining the same variable under two different prefixes — your code will silently read whichever one it finds first.
236
+ That's the whole rule. Since the prefix list is fixed, there's no config mistake that can widen what gets exposed — anything without `BINI_` or `VITE_` in front of it simply never reaches the browser.
172
237
 
173
- ### Prefix summary
238
+ ### Choosing the Right Prefix
174
239
 
175
- | Prefix | Exposed to browser | Use for |
176
- |--------|--------------------|---------|
177
- | `BINI_` | No (unless added to `envPrefix`) | Server-side config |
178
- | `VITE_` | Yes, always | Public client config |
179
- | No prefix | No | Secrets — server only |
240
+ ```env
241
+ # ✅ Use no prefix for secrets
242
+ DATABASE_URL=postgres://...
243
+ STRIPE_SECRET_KEY=sk_live_...
244
+
245
+ # ✅ Use BINI_ or VITE_ for public config
246
+ BINI_API_URL=https://api.example.com
247
+ VITE_ANALYTICS_ID=UA-XXXX
248
+ ```
249
+
250
+ ### Prefix Summary
251
+
252
+ | Prefix | Exposed to Browser? | Mirrored to `process.env` (dev/preview)? | Use For |
253
+ |--------|--------------------|-------------------------------------------|---------|
254
+ | No prefix | ❌ Never | ✅ Yes (unless empty or already set) | **SECRETS** — API keys, database URLs, tokens |
255
+ | `BINI_` | ✅ Always | ❌ No | Public config |
256
+ | `VITE_` | ✅ Always | ❌ No | Public config (Vite's default) |
180
257
 
181
258
  ---
182
259
 
@@ -186,8 +263,8 @@ STRIPE_SECRET_KEY=sk_live_...
186
263
 
187
264
  | Platform | Runtime | Env source | How Hono reads it |
188
265
  |----------|---------|------------|-------------------|
189
- | Node.js | Node | System env / platform dashboard | `process.env` ✅ |
190
- | Bun | Bun | System env / platform dashboard | `process.env` ✅ |
266
+ | Node.js | Node | System env / platform dashboard / `bini-env` dev mirror | `process.env` ✅ |
267
+ | Bun | Bun | System env / platform dashboard / `bini-env` dev mirror | `process.env` ✅ |
191
268
  | Vercel Edge | V8 isolate | Project settings → Env Vars | `process.env` ✅ |
192
269
  | Netlify Edge | Deno | Site settings → Env Vars | `Deno.env.get()` ✅ |
193
270
  | Cloudflare Workers | V8 isolate | `wrangler.toml` / dashboard | CF bindings via `c.env` ✅ |
@@ -201,16 +278,21 @@ STRIPE_SECRET_KEY=sk_live_...
201
278
 
202
279
  ### The plugin — `biniEnv()`
203
280
 
204
- 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.
281
+ Does three things: tells Vite to expose `BINI_` and `VITE_` prefixed vars to `import.meta.env`, mirrors non-prefixed `.env` values into `process.env` during `vite dev` / `vite preview` so Hono routes can read them, and prints the `ß Bini.js` banner with detected `.env` files when the dev or preview server starts.
205
282
 
206
283
  ```ts
207
284
  // what biniEnv() does internally
208
- config() {
209
- return { envPrefix: ['BINI_', 'VITE_', ...yourExtras] }
285
+ config(userConfig, { command, mode }) {
286
+ if (command === 'serve') {
287
+ const envDir = userConfig.envDir ?? userConfig.root ?? process.cwd()
288
+ // mirrors non-prefixed, non-empty .env values into process.env
289
+ // silent on success, warns on failure — no opt-out
290
+ }
291
+ return { envPrefix: ['BINI_', 'VITE_'] }
210
292
  }
211
293
  ```
212
294
 
213
- On server start you will see:
295
+ The `process.env` mirror runs silently — it doesn't add any lines to the banner. On server start you will see the same banner as before:
214
296
 
215
297
  ```
216
298
  ß Bini.js (dev)
@@ -219,11 +301,11 @@ On server start you will see:
219
301
  ➜ Network: http://192.168.1.7:3000/
220
302
  ```
221
303
 
222
- 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.
304
+ Vite handles the rest 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.
223
305
 
224
306
  ### The env functions — `getEnv` / `requireEnv`
225
307
 
226
- 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.
308
+ Both functions read exclusively from `env(c)` via `hono/adapter` — the Hono request context. There is no direct `process.env` access inside `getEnv`/`requireEnv` themselves; on Node, Hono's adapter reads from `process.env`, which is why the dev-mode mirror above is what makes non-prefixed secrets resolvable there. Every read is request-scoped and resolved by Hono's adapter for the current platform.
227
309
 
228
310
  `dotenv` is never used. In production, vars are set in your hosting platform's environment config.
229
311
 
@@ -280,19 +362,15 @@ On failure, the terminal will show:
280
362
  -> Set it in your platform's env config or hosting dashboard.
281
363
  ```
282
364
 
283
- ### `biniEnv(options?)`
365
+ ### `biniEnv()`
284
366
 
285
- Vite plugin. Registers `BINI_` and `VITE_` as env prefixes and optionally adds more.
367
+ Vite plugin. Takes no options. Exposes `BINI_` and `VITE_` prefixed vars to `import.meta.env`, and mirrors every other (non-empty) `.env` value into `process.env` during dev/preview.
286
368
 
287
369
  ```ts
288
370
  biniEnv()
289
- // or with extra prefixes
290
- biniEnv({ envPrefix: ['MY_PUBLIC_'] })
291
371
  ```
292
372
 
293
- | Option | Type | Default | Description |
294
- |--------|------|---------|-------------|
295
- | `envPrefix` | `string \| string[]` | `[]` | Extra prefixes to expose to `import.meta.env`, in addition to `BINI_` and `VITE_` |
373
+ There's nothing to configure — no prefix list to extend, no flag to disable the `process.env` mirror. It reads `.env` files from your `envDir` (or `root`, or the working directory), never runs on `vite build`, never overrides a value already set in `process.env`, and skips the mirror entirely if you've set `envDir: false`.
296
374
 
297
375
  ### `biniLogger`
298
376
 
@@ -325,27 +403,85 @@ function readDbConfig(c: HonoContext) {
325
403
 
326
404
  ---
327
405
 
406
+ ## Security Best Practices
407
+
408
+ ### Rule 1: Never Prefix Secrets
409
+ ```env
410
+ # ❌ BAD - This will be exposed to the browser!
411
+ BINI_DATABASE_URL=postgres://...
412
+
413
+ # ✅ GOOD - Not exposed to browser, mirrored into process.env for server-side use
414
+ DATABASE_URL=postgres://...
415
+ ```
416
+
417
+ ### Rule 2: Use No Prefix for Anything Secret
418
+ ```env
419
+ # ✅ Safe - Not exposed, available server-side via getEnv/requireEnv
420
+ DATABASE_URL=postgres://...
421
+ STRIPE_SECRET=sk_live_...
422
+ AWS_SECRET_KEY=...
423
+ ```
424
+
425
+ ### Rule 3: Use `BINI_` or `VITE_` for Public Data
426
+ ```env
427
+ # ✅ Safe - Public data
428
+ BINI_API_URL=https://api.example.com
429
+ VITE_GA_ID=UA-XXXXX
430
+ ```
431
+
432
+ ### Rule 4: Don't Leave Secret Placeholders Empty
433
+
434
+ An empty value (`API_KEY=`) is skipped by the `process.env` mirror, so `requireEnv` will correctly throw instead of silently succeeding with an empty string. Fill in real values, or remove the line entirely if it's genuinely unused.
435
+
436
+ ### Rule 5: Let OS/CI Env Vars Win in Dev
437
+
438
+ The `process.env` mirror never overrides a variable that's already set. If you need to override a `.env` value locally (e.g. pointing at a different database), export it in your shell before starting the dev server rather than editing `.env`:
439
+
440
+ ```bash
441
+ DATABASE_URL=postgres://staging... pnpm dev
442
+ ```
443
+
444
+ ---
445
+
328
446
  ## Performance
329
447
 
330
448
  | Metric | Dev | Prod |
331
449
  |--------|-----|------|
332
- | File reads | 0 | 0 |
333
- | Runtime cost | ~0ms | 0 |
450
+ | File reads | 1 (`loadEnv`, cached by Vite) | 0 |
451
+ | Runtime cost | ~0ms (mirror runs once at server start) | 0 |
334
452
  | Bundle impact | Minimal | Tree-shaken |
335
453
 
336
- No dotenv. No disk reads. No caching layer. `getEnv` is a direct call to Hono's adapter on every invocation — request-scoped and correct.
454
+ No dotenv. No per-request disk reads. No caching layer beyond Vite's own. `getEnv` is a direct call to Hono's adapter on every invocation — request-scoped and correct. The `process.env` mirror runs exactly once, when the dev or preview server starts.
337
455
 
338
456
  ---
339
457
 
340
458
  ## Troubleshooting
341
459
 
460
+ ### "My BINI_ var is exposed to the browser!"
461
+
462
+ Yes — by design. `BINI_` is always exposed, the same as `VITE_`. Use no prefix for secrets.
463
+
342
464
  ### "Env var is undefined in production"
343
465
 
344
- 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.).
466
+ Vars set in `.env` files are only loaded by Vite (and mirrored into `process.env`) during local development and preview. In production, set your variables in your hosting platform's environment dashboard (Vercel, Netlify, Cloudflare, etc.).
345
467
 
346
468
  ### "Works in dev, undefined in prod"
347
469
 
348
- Same as above. Local dev works because Vite loads `.env` files automatically. Production requires platform-level configuration.
470
+ Same as above. Local dev works because `biniEnv()` loads `.env` files and mirrors non-prefixed vars into `process.env` automatically. Production requires platform-level configuration.
471
+
472
+ ### "My .env value isn't taking effect in dev"
473
+
474
+ Two likely causes:
475
+ - Check your shell and CI environment first — the `process.env` mirror never overrides a variable that's already set there. Unset it in your shell, or override it directly in your shell instead of `.env`.
476
+ - Check the value isn't empty (`KEY=`) — empty values are skipped on purpose.
477
+
478
+ ### "requireEnv still throws even though my key is in .env"
479
+
480
+ If the value is `KEY=` with nothing after the `=`, it's treated as unset and skipped by design — this is intentional so a blank placeholder doesn't silently pass as configured. Give it a real value.
481
+
482
+ ### "bini-env isn't reading my .env file from the right folder"
483
+
484
+ `biniEnv()` reads from your Vite `envDir` if you've set one, otherwise your `root`, otherwise the working directory — the same resolution Vite itself uses. Double check `envDir`/`root` in `vite.config.ts` if your `.env` lives somewhere non-standard.
349
485
 
350
486
  ### "Cloudflare secret not found"
351
487
 
@@ -373,7 +509,7 @@ Add to your `tsconfig.json` or entry file:
373
509
 
374
510
  | Tool | Version | Notes |
375
511
  |------|---------|-------|
376
- | Vite | 8.x | `envPrefix` hook is fully compatible; empty prefix array is never passed |
512
+ | Vite | 8.x | `envPrefix` hook and `loadEnv` are fully compatible; empty prefix array is never passed |
377
513
  | Hono | 4.x | `hono/adapter` `env()` is stable across all 4.x releases |
378
514
  | TypeScript | 5.x | Full type support via exported `HonoContext` |
379
515
  | Node.js | ≥ 20.19 | Minimum required version |
package/dist/index.d.mts CHANGED
@@ -2,26 +2,7 @@ import { Context } from 'hono';
2
2
  import { Plugin } from 'vite';
3
3
  export { Plugin } from 'vite';
4
4
 
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
5
  type HonoContext = Context;
21
- interface BiniEnvPluginOptions {
22
- /** Extra prefixes to expose to import.meta.env, in addition to BINI_ and VITE_. */
23
- readonly envPrefix?: string | string[];
24
- }
25
6
  declare const biniLogger: {
26
7
  readonly info: (msg: string) => void;
27
8
  readonly warn: (msg: string) => void;
@@ -29,6 +10,6 @@ declare const biniLogger: {
29
10
  };
30
11
  declare function getEnv(c: HonoContext, key: string): string | undefined;
31
12
  declare function requireEnv(c: HonoContext, key: string): string;
32
- declare function biniEnv(options?: Readonly<BiniEnvPluginOptions>): Plugin;
13
+ declare function biniEnv(): Plugin;
33
14
 
34
- export { type BiniEnvPluginOptions, type HonoContext, biniEnv, biniLogger, getEnv, requireEnv };
15
+ export { type HonoContext, biniEnv, biniLogger, getEnv, requireEnv };
package/dist/index.d.ts CHANGED
@@ -2,26 +2,7 @@ import { Context } from 'hono';
2
2
  import { Plugin } from 'vite';
3
3
  export { Plugin } from 'vite';
4
4
 
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
5
  type HonoContext = Context;
21
- interface BiniEnvPluginOptions {
22
- /** Extra prefixes to expose to import.meta.env, in addition to BINI_ and VITE_. */
23
- readonly envPrefix?: string | string[];
24
- }
25
6
  declare const biniLogger: {
26
7
  readonly info: (msg: string) => void;
27
8
  readonly warn: (msg: string) => void;
@@ -29,6 +10,6 @@ declare const biniLogger: {
29
10
  };
30
11
  declare function getEnv(c: HonoContext, key: string): string | undefined;
31
12
  declare function requireEnv(c: HonoContext, key: string): string;
32
- declare function biniEnv(options?: Readonly<BiniEnvPluginOptions>): Plugin;
13
+ declare function biniEnv(): Plugin;
33
14
 
34
- export { type BiniEnvPluginOptions, type HonoContext, biniEnv, biniLogger, getEnv, requireEnv };
15
+ export { type HonoContext, biniEnv, biniLogger, getEnv, requireEnv };
package/dist/index.js CHANGED
@@ -51,7 +51,7 @@ function requireEnv(c, key) {
51
51
  }
52
52
  return val;
53
53
  }
54
- var DEFAULT_PREFIXES = ["BINI_", "VITE_"];
54
+ var ENV_PREFIXES = ["BINI_", "VITE_"];
55
55
  function detectEnvFiles(root) {
56
56
  try {
57
57
  const fs = __require("fs");
@@ -67,6 +67,21 @@ function detectEnvFiles(root) {
67
67
  return [];
68
68
  }
69
69
  }
70
+ function injectEnvIntoProcess(mode, envDir) {
71
+ if (typeof process === "undefined" || !process.env) return 0;
72
+ const { loadEnv } = __require("vite");
73
+ const loaded = loadEnv(mode, envDir, "");
74
+ let injected = 0;
75
+ for (const [key, value] of Object.entries(loaded)) {
76
+ const isPrefixed = ENV_PREFIXES.some((p) => key.startsWith(p));
77
+ if (isPrefixed) continue;
78
+ if (!value) continue;
79
+ if (process.env[key] !== void 0) continue;
80
+ process.env[key] = value;
81
+ injected++;
82
+ }
83
+ return injected;
84
+ }
70
85
  function printBanner(server, mode, root) {
71
86
  const original = server.printUrls.bind(server);
72
87
  server.printUrls = () => {
@@ -79,15 +94,21 @@ function printBanner(server, mode, root) {
79
94
  original();
80
95
  };
81
96
  }
82
- function biniEnv(options = {}) {
83
- const extraPrefixes = options.envPrefix ? Array.isArray(options.envPrefix) ? options.envPrefix : [options.envPrefix] : [];
84
- const envPrefix = [
85
- .../* @__PURE__ */ new Set([...DEFAULT_PREFIXES, ...extraPrefixes])
86
- ];
97
+ function biniEnv() {
87
98
  return {
88
99
  name: "bini-env",
89
- config() {
90
- return { envPrefix };
100
+ config(userConfig, { command, mode }) {
101
+ if (command === "serve" && userConfig.envDir !== false) {
102
+ const envDir = typeof userConfig.envDir === "string" ? userConfig.envDir : userConfig.root ?? process.cwd();
103
+ try {
104
+ injectEnvIntoProcess(mode, envDir);
105
+ } catch (err) {
106
+ biniLogger.warn(
107
+ `Failed to inject .env into process.env: ${err.message}`
108
+ );
109
+ }
110
+ }
111
+ return { envPrefix: [...ENV_PREFIXES] };
91
112
  },
92
113
  configureServer(server) {
93
114
  printBanner(server, "dev", server.config.root);
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts"],"names":["env"],"mappings":";;;;;;;;;;AAmBA,IAAM,CAAA,GAAI;AAAA,EACR,IAAA,EAAQ,UAAA;AAAA,EACR,KAAA,EAAQ,SAAA;AAAA,EACR,MAAA,EAAQ,UAAA;AAAA,EACR,GAAA,EAAQ,UAAA;AAAA,EACR,IAAA,EAAQ,SAAA;AAAA,EACR,GAAA,EAAQ;AACV,CAAA;AAqBA,SAAS,GAAA,GAAc;AACrB,EAAA,OAAO,GAAG,CAAA,CAAE,GAAG,CAAA,EAAA,iBAAG,IAAI,MAAK,EAAE,kBAAA,CAAmB,OAAA,EAAS,EAAE,QAAQ,KAAA,EAAO,CAAC,CAAA,EAAG,EAAE,KAAK,CAAA,CAAA;AACvF;AAEO,IAAM,UAAA,GAAa;AAAA,EACxB,KAAK,GAAA,EAAmB;AACtB,IAAA,OAAA,CAAQ,GAAA,CAAI,CAAA,EAAG,GAAA,EAAK,IAAI,CAAA,CAAE,IAAI,CAAA,EAAG,CAAA,CAAE,IAAI,CAAA,UAAA,EAAa,CAAA,CAAE,KAAK,CAAA,CAAA,EAAI,GAAG,CAAA,CAAE,CAAA;AAAA,EACtE,CAAA;AAAA,EACA,KAAK,GAAA,EAAmB;AACtB,IAAA,OAAA,CAAQ,IAAA;AAAA,MACN,GAAG,GAAA,EAAK,IAAI,CAAA,CAAE,MAAM,GAAG,CAAA,CAAE,IAAI,iBAAiB,CAAA,CAAE,KAAK,IAAI,CAAA,CAAE,MAAM,GAAG,GAAG,CAAA,EAAG,EAAE,KAAK,CAAA;AAAA,KACnF;AAAA,EACF,CAAA;AAAA,EACA,KAAA,CAAM,KAAa,GAAA,EAAqB;AACtC,IAAA,MAAM,MAAA,GACJ,eAAe,KAAA,GAAQ;AAAA,IAAA,EAAS,CAAA,CAAE,GAAG,CAAA,EAAG,GAAA,CAAI,OAAO,CAAA,EAAG,CAAA,CAAE,KAAK,CAAA,CAAA,GAAK,EAAA;AACpE,IAAA,OAAA,CAAQ,KAAA;AAAA,MACN,CAAA,EAAG,GAAA,EAAK,CAAA,CAAA,EAAI,EAAE,GAAG,CAAA,EAAG,CAAA,CAAE,IAAI,mBAAmB,CAAA,CAAE,KAAK,CAAA,CAAA,EAAI,GAAG,GAAG,MAAM,CAAA;AAAA,KACtE;AAAA,EACF;AACF;AAaO,SAAS,MAAA,CAAO,GAAgB,GAAA,EAAiC;AACtE,EAAA,MAAM,QAAA,GAAWA,YAAI,CAAC,CAAA;AACtB,EAAA,OAAO,SAAS,GAAG,CAAA;AACrB;AASO,SAAS,UAAA,CAAW,GAAgB,GAAA,EAAqB;AAC9D,EAAA,MAAM,GAAA,GAAM,MAAA,CAAO,CAAA,EAAG,GAAG,CAAA;AAEzB,EAAA,IAAI,CAAC,GAAA,EAAK;AACR,IAAA,UAAA,CAAW,KAAA;AAAA,MACT,2CAA2C,GAAG,CAAA;AAAA,EAAA,EACzC,CAAA,CAAE,GAAG,CAAA,6DAAA,EAAgE,CAAA,CAAE,KAAK,CAAA;AAAA,KACnF;AAEA,IAAA,MAAM,IAAI,KAAA,CAAM,CAAA,mDAAA,EAAsD,GAAG,CAAA,CAAA,CAAG,CAAA;AAAA,EAC9E;AAEA,EAAA,OAAO,GAAA;AACT;AAUA,IAAM,gBAAA,GAAmB,CAAC,OAAA,EAAS,OAAO,CAAA;AAE1C,SAAS,eAAe,IAAA,EAAwB;AAC9C,EAAA,IAAI;AAEF,IAAA,MAAM,EAAA,GAAO,UAAQ,IAAS,CAAA;AAE9B,IAAA,MAAM,IAAA,GAAO,UAAQ,MAAW,CAAA;AAChC,IAAA,MAAM,OAAA,GAAU,OAAA,CAAQ,GAAA,CAAI,QAAA,IAAY,YAAA;AACxC,IAAA,OAAO;AAAA,MACL,YAAA;AAAA,MACA,QAAQ,OAAO,CAAA,MAAA,CAAA;AAAA,MACf,QAAQ,OAAO,CAAA,CAAA;AAAA,MACf;AAAA,KACF,CAAE,MAAA,CAAO,CAAA,CAAA,KAAK,EAAA,CAAG,UAAA,CAAW,KAAK,IAAA,CAAK,IAAA,EAAM,CAAC,CAAC,CAAC,CAAA;AAAA,EACjD,CAAA,CAAA,MAAQ;AACN,IAAA,OAAO,EAAC;AAAA,EACV;AACF;AAEA,SAAS,WAAA,CAAY,MAAA,EAAuC,IAAA,EAAyB,IAAA,EAAoB;AACvG,EAAA,MAAM,QAAA,GAAW,MAAA,CAAO,SAAA,CAAU,IAAA,CAAK,MAAM,CAAA;AAC7C,EAAA,MAAA,CAAO,YAAY,MAAM;AACvB,IAAA,OAAA,CAAQ,GAAA,CAAI;AAAA,EAAA,EAAO,EAAE,IAAI,CAAA,YAAA,EAAY,EAAE,KAAK,CAAA,EAAA,EAAK,IAAI,CAAA,CAAA,CAAG,CAAA;AACxD,IAAA,MAAM,KAAA,GAAQ,eAAe,IAAI,CAAA;AACjC,IAAA,IAAI,KAAA,CAAM,SAAS,CAAA,EAAG;AACpB,MAAA,OAAA,CAAQ,IAAI,CAAA,uCAAA,EAAqC,KAAA,CAAM,IAAA,CAAK,IAAI,CAAC,CAAA,CAAE,CAAA;AAAA,IACrE;AACA,IAAA,QAAA,EAAS;AAAA,EACX,CAAA;AACF;AAEO,SAAS,OAAA,CAAQ,OAAA,GAA0C,EAAC,EAAW;AAC5E,EAAA,MAAM,aAAA,GAAgB,OAAA,CAAQ,SAAA,GACzB,KAAA,CAAM,QAAQ,OAAA,CAAQ,SAAS,CAAA,GAAI,OAAA,CAAQ,SAAA,GAAY,CAAC,OAAA,CAAQ,SAAS,IAC1E,EAAC;AAEL,EAAA,MAAM,SAAA,GAAY;AAAA,IAChB,uBAAO,GAAA,CAAI,CAAC,GAAG,gBAAA,EAAkB,GAAG,aAAa,CAAC;AAAA,GACpD;AAEA,EAAA,OAAO;AAAA,IACL,IAAA,EAAM,UAAA;AAAA,IAEN,MAAA,GAAqB;AACnB,MAAA,OAAO,EAAE,SAAA,EAAU;AAAA,IACrB,CAAA;AAAA,IAEA,gBAAgB,MAAA,EAAQ;AACtB,MAAA,WAAA,CAAY,MAAA,EAAQ,KAAA,EAAO,MAAA,CAAO,MAAA,CAAO,IAAI,CAAA;AAAA,IAC/C,CAAA;AAAA,IAEA,uBAAuB,MAAA,EAAQ;AAC7B,MAAA,WAAA,CAAY,MAAA,EAAQ,SAAA,EAAW,MAAA,CAAO,MAAA,CAAO,IAAI,CAAA;AAAA,IACnD;AAAA,GACF;AACF","file":"index.js","sourcesContent":["/**\n * bini-env/src/index.ts\n *\n * Two responsibilities only:\n * 1. Tell Vite which env prefixes to expose to import.meta.env (biniEnv plugin)\n * 2. Read env vars from the Hono request context (getEnv / requireEnv)\n *\n * process.env is never used. All runtime env access goes through Hono's env() adapter,\n * which handles Node, Bun, Cloudflare Workers, Deno, and Vercel Edge natively.\n */\n\nimport { env } from 'hono/adapter';\nimport type { Context } from 'hono';\nimport type { Plugin, UserConfig, ViteDevServer, PreviewServer } from 'vite';\n\n/* ==========================================================================\n ANSI helpers — used by biniLogger only\n ========================================================================== */\n\nconst C = {\n CYAN : '\\x1b[36m',\n RESET : '\\x1b[0m',\n YELLOW: '\\x1b[33m',\n RED : '\\x1b[31m',\n BOLD : '\\x1b[1m',\n DIM : '\\x1b[2m',\n} as const;\n\n/* ==========================================================================\n Public types\n ========================================================================== */\n\n/**\n * Accepts any Hono Context. Typed against Hono's own Context rather than a\n * loose duck type so misuse is caught at the call site.\n */\nexport type HonoContext = Context;\n\nexport interface BiniEnvPluginOptions {\n /** Extra prefixes to expose to import.meta.env, in addition to BINI_ and VITE_. */\n readonly envPrefix?: string | string[];\n}\n\n/* ==========================================================================\n Logger\n ========================================================================== */\n\nfunction _ts(): string {\n return `${C.DIM}${new Date().toLocaleTimeString('en-US', { hour12: false })}${C.RESET}`;\n}\n\nexport const biniLogger = {\n info(msg: string): void {\n console.log(`${_ts()} ${C.CYAN}${C.BOLD}[bini-env]${C.RESET} ${msg}`);\n },\n warn(msg: string): void {\n console.warn(\n `${_ts()} ${C.YELLOW}${C.BOLD}(!) [bini-env]${C.RESET} ${C.YELLOW}${msg}${C.RESET}`,\n );\n },\n error(msg: string, err?: unknown): void {\n const detail =\n err instanceof Error ? `\\n ${C.DIM}${err.message}${C.RESET}` : '';\n console.error(\n `${_ts()} ${C.RED}${C.BOLD}[bini-env] error${C.RESET} ${msg}${detail}`,\n );\n },\n} as const;\n\n/* ==========================================================================\n getEnv\n\n Reads exclusively from Hono's env() adapter. No process.env fallback.\n Hono handles all platform differences:\n Node / Bun → process.env\n Cloudflare → c.env bindings\n Deno / Netlify → Deno.env.get()\n Vercel Edge → process.env (V8 Node compat)\n ========================================================================== */\n\nexport function getEnv(c: HonoContext, key: string): string | undefined {\n const honoEnvs = env(c) as Record<string, string | undefined>;\n return honoEnvs[key];\n}\n\n/* ==========================================================================\n requireEnv\n\n Same as getEnv but throws immediately if the variable is missing or empty.\n Always handle the thrown error in your route's catch block.\n ========================================================================== */\n\nexport function requireEnv(c: HonoContext, key: string): string {\n const val = getEnv(c, key);\n\n if (!val) {\n biniLogger.error(\n `Missing required environment variable: \"${key}\"\\n` +\n ` ${C.DIM}-> Set it in your platform's env config or hosting dashboard.${C.RESET}`,\n );\n\n throw new Error(`[bini-env] Missing required environment variable: \"${key}\"`);\n }\n\n return val;\n}\n\n/* ==========================================================================\n biniEnv — Vite plugin\n\n Tells Vite which prefixes to expose to import.meta.env.\n Prints the ß Bini.js banner on dev and preview server start.\n Vite handles .env file loading, watching, server restarts, and HMR natively.\n ========================================================================== */\n\nconst DEFAULT_PREFIXES = ['BINI_', 'VITE_'] as const;\n\nfunction detectEnvFiles(root: string): string[] {\n try {\n // eslint-disable-next-line @typescript-eslint/no-require-imports\n const fs = require('node:fs') as typeof import('fs');\n // eslint-disable-next-line @typescript-eslint/no-require-imports\n const path = require('node:path') as typeof import('path');\n const nodeEnv = process.env.NODE_ENV ?? 'production';\n return [\n '.env.local',\n `.env.${nodeEnv}.local`,\n `.env.${nodeEnv}`,\n '.env',\n ].filter(f => fs.existsSync(path.join(root, f)));\n } catch {\n return [];\n }\n}\n\nfunction printBanner(server: ViteDevServer | PreviewServer, mode: 'dev' | 'preview', root: string): void {\n const original = server.printUrls.bind(server);\n server.printUrls = () => {\n console.log(`\\n ${C.CYAN}ß Bini.js${C.RESET} (${mode})`);\n const found = detectEnvFiles(root);\n if (found.length > 0) {\n console.log(` \\x1b[32m➜\\x1b[0m Environments: ${found.join(', ')}`);\n }\n original();\n };\n}\n\nexport function biniEnv(options: Readonly<BiniEnvPluginOptions> = {}): Plugin {\n const extraPrefixes = options.envPrefix\n ? (Array.isArray(options.envPrefix) ? options.envPrefix : [options.envPrefix])\n : [];\n\n const envPrefix = [\n ...new Set([...DEFAULT_PREFIXES, ...extraPrefixes]),\n ];\n\n return {\n name: 'bini-env',\n\n config(): UserConfig {\n return { envPrefix };\n },\n\n configureServer(server) {\n printBanner(server, 'dev', server.config.root);\n },\n\n configurePreviewServer(server) {\n printBanner(server, 'preview', server.config.root);\n },\n };\n}\n\n/* ==========================================================================\n Re-exports\n ========================================================================== */\n\nexport type { Plugin } from 'vite';"]}
1
+ {"version":3,"sources":["../src/index.ts"],"names":["env"],"mappings":"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","file":"index.js","sourcesContent":["import { env } from 'hono/adapter';\nimport type { Context } from 'hono';\nimport type { Plugin, UserConfig, ViteDevServer, PreviewServer } from 'vite';\n\nconst C = {\n CYAN : '\\x1b[36m',\n RESET : '\\x1b[0m',\n YELLOW: '\\x1b[33m',\n RED : '\\x1b[31m',\n BOLD : '\\x1b[1m',\n DIM : '\\x1b[2m',\n} as const;\n\nexport type HonoContext = Context;\n\nfunction _ts(): string {\n return `${C.DIM}${new Date().toLocaleTimeString('en-US', { hour12: false })}${C.RESET}`;\n}\n\nexport const biniLogger = {\n info(msg: string): void {\n console.log(`${_ts()} ${C.CYAN}${C.BOLD}[bini-env]${C.RESET} ${msg}`);\n },\n warn(msg: string): void {\n console.warn(\n `${_ts()} ${C.YELLOW}${C.BOLD}(!) [bini-env]${C.RESET} ${C.YELLOW}${msg}${C.RESET}`,\n );\n },\n error(msg: string, err?: unknown): void {\n const detail =\n err instanceof Error ? `\\n ${C.DIM}${err.message}${C.RESET}` : '';\n console.error(\n `${_ts()} ${C.RED}${C.BOLD}[bini-env] error${C.RESET} ${msg}${detail}`,\n );\n },\n} as const;\n\nexport function getEnv(c: HonoContext, key: string): string | undefined {\n const honoEnvs = env(c) as Record<string, string | undefined>;\n return honoEnvs[key];\n}\n\nexport function requireEnv(c: HonoContext, key: string): string {\n const val = getEnv(c, key);\n\n if (!val) {\n biniLogger.error(\n `Missing required environment variable: \"${key}\"\\n` +\n ` ${C.DIM}-> Set it in your platform's env config or hosting dashboard.${C.RESET}`,\n );\n\n throw new Error(`[bini-env] Missing required environment variable: \"${key}\"`);\n }\n\n return val;\n}\n\nconst ENV_PREFIXES = ['BINI_', 'VITE_'] as const;\n\nfunction detectEnvFiles(root: string): string[] {\n try {\n const fs = require('node:fs') as typeof import('fs');\n const path = require('node:path') as typeof import('path');\n const nodeEnv = process.env.NODE_ENV ?? 'production';\n return [\n '.env.local',\n `.env.${nodeEnv}.local`,\n `.env.${nodeEnv}`,\n '.env',\n ].filter(f => fs.existsSync(path.join(root, f)));\n } catch {\n return [];\n }\n}\n\nfunction injectEnvIntoProcess(mode: string, envDir: string): number {\n if (typeof process === 'undefined' || !process.env) return 0;\n\n const { loadEnv } = require('vite') as typeof import('vite');\n const loaded = loadEnv(mode, envDir, '');\n let injected = 0;\n\n for (const [key, value] of Object.entries(loaded)) {\n const isPrefixed = ENV_PREFIXES.some((p) => key.startsWith(p));\n if (isPrefixed) continue;\n if (!value) continue;\n if (process.env[key] !== undefined) continue;\n process.env[key] = value;\n injected++;\n }\n\n return injected;\n}\n\nfunction printBanner(server: ViteDevServer | PreviewServer, mode: 'dev' | 'preview', root: string): void {\n const original = server.printUrls.bind(server);\n server.printUrls = () => {\n console.log(`\\n ${C.CYAN}ß Bini.js${C.RESET} (${mode})`);\n const found = detectEnvFiles(root);\n if (found.length > 0) {\n console.log(` \\x1b[32m➜\\x1b[0m Environments: ${found.join(', ')}`);\n }\n original();\n };\n}\n\nexport function biniEnv(): Plugin {\n return {\n name: 'bini-env',\n\n config(userConfig: UserConfig, { command, mode }): UserConfig {\n if (command === 'serve' && userConfig.envDir !== false) {\n const envDir =\n typeof userConfig.envDir === 'string'\n ? userConfig.envDir\n : userConfig.root ?? process.cwd();\n\n try {\n injectEnvIntoProcess(mode, envDir);\n } catch (err) {\n biniLogger.warn(\n `Failed to inject .env into process.env: ${(err as Error).message}`,\n );\n }\n }\n\n return { envPrefix: [...ENV_PREFIXES] };\n },\n\n configureServer(server) {\n printBanner(server, 'dev', server.config.root);\n },\n\n configurePreviewServer(server) {\n printBanner(server, 'preview', server.config.root);\n },\n };\n}\n\nexport type { Plugin } from 'vite';"]}
package/dist/index.mjs CHANGED
@@ -49,7 +49,7 @@ function requireEnv(c, key) {
49
49
  }
50
50
  return val;
51
51
  }
52
- var DEFAULT_PREFIXES = ["BINI_", "VITE_"];
52
+ var ENV_PREFIXES = ["BINI_", "VITE_"];
53
53
  function detectEnvFiles(root) {
54
54
  try {
55
55
  const fs = __require("fs");
@@ -65,6 +65,21 @@ function detectEnvFiles(root) {
65
65
  return [];
66
66
  }
67
67
  }
68
+ function injectEnvIntoProcess(mode, envDir) {
69
+ if (typeof process === "undefined" || !process.env) return 0;
70
+ const { loadEnv } = __require("vite");
71
+ const loaded = loadEnv(mode, envDir, "");
72
+ let injected = 0;
73
+ for (const [key, value] of Object.entries(loaded)) {
74
+ const isPrefixed = ENV_PREFIXES.some((p) => key.startsWith(p));
75
+ if (isPrefixed) continue;
76
+ if (!value) continue;
77
+ if (process.env[key] !== void 0) continue;
78
+ process.env[key] = value;
79
+ injected++;
80
+ }
81
+ return injected;
82
+ }
68
83
  function printBanner(server, mode, root) {
69
84
  const original = server.printUrls.bind(server);
70
85
  server.printUrls = () => {
@@ -77,15 +92,21 @@ function printBanner(server, mode, root) {
77
92
  original();
78
93
  };
79
94
  }
80
- function biniEnv(options = {}) {
81
- const extraPrefixes = options.envPrefix ? Array.isArray(options.envPrefix) ? options.envPrefix : [options.envPrefix] : [];
82
- const envPrefix = [
83
- .../* @__PURE__ */ new Set([...DEFAULT_PREFIXES, ...extraPrefixes])
84
- ];
95
+ function biniEnv() {
85
96
  return {
86
97
  name: "bini-env",
87
- config() {
88
- return { envPrefix };
98
+ config(userConfig, { command, mode }) {
99
+ if (command === "serve" && userConfig.envDir !== false) {
100
+ const envDir = typeof userConfig.envDir === "string" ? userConfig.envDir : userConfig.root ?? process.cwd();
101
+ try {
102
+ injectEnvIntoProcess(mode, envDir);
103
+ } catch (err) {
104
+ biniLogger.warn(
105
+ `Failed to inject .env into process.env: ${err.message}`
106
+ );
107
+ }
108
+ }
109
+ return { envPrefix: [...ENV_PREFIXES] };
89
110
  },
90
111
  configureServer(server) {
91
112
  printBanner(server, "dev", server.config.root);
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts"],"names":[],"mappings":"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","file":"index.mjs","sourcesContent":["/**\n * bini-env/src/index.ts\n *\n * Two responsibilities only:\n * 1. Tell Vite which env prefixes to expose to import.meta.env (biniEnv plugin)\n * 2. Read env vars from the Hono request context (getEnv / requireEnv)\n *\n * process.env is never used. All runtime env access goes through Hono's env() adapter,\n * which handles Node, Bun, Cloudflare Workers, Deno, and Vercel Edge natively.\n */\n\nimport { env } from 'hono/adapter';\nimport type { Context } from 'hono';\nimport type { Plugin, UserConfig, ViteDevServer, PreviewServer } from 'vite';\n\n/* ==========================================================================\n ANSI helpers — used by biniLogger only\n ========================================================================== */\n\nconst C = {\n CYAN : '\\x1b[36m',\n RESET : '\\x1b[0m',\n YELLOW: '\\x1b[33m',\n RED : '\\x1b[31m',\n BOLD : '\\x1b[1m',\n DIM : '\\x1b[2m',\n} as const;\n\n/* ==========================================================================\n Public types\n ========================================================================== */\n\n/**\n * Accepts any Hono Context. Typed against Hono's own Context rather than a\n * loose duck type so misuse is caught at the call site.\n */\nexport type HonoContext = Context;\n\nexport interface BiniEnvPluginOptions {\n /** Extra prefixes to expose to import.meta.env, in addition to BINI_ and VITE_. */\n readonly envPrefix?: string | string[];\n}\n\n/* ==========================================================================\n Logger\n ========================================================================== */\n\nfunction _ts(): string {\n return `${C.DIM}${new Date().toLocaleTimeString('en-US', { hour12: false })}${C.RESET}`;\n}\n\nexport const biniLogger = {\n info(msg: string): void {\n console.log(`${_ts()} ${C.CYAN}${C.BOLD}[bini-env]${C.RESET} ${msg}`);\n },\n warn(msg: string): void {\n console.warn(\n `${_ts()} ${C.YELLOW}${C.BOLD}(!) [bini-env]${C.RESET} ${C.YELLOW}${msg}${C.RESET}`,\n );\n },\n error(msg: string, err?: unknown): void {\n const detail =\n err instanceof Error ? `\\n ${C.DIM}${err.message}${C.RESET}` : '';\n console.error(\n `${_ts()} ${C.RED}${C.BOLD}[bini-env] error${C.RESET} ${msg}${detail}`,\n );\n },\n} as const;\n\n/* ==========================================================================\n getEnv\n\n Reads exclusively from Hono's env() adapter. No process.env fallback.\n Hono handles all platform differences:\n Node / Bun → process.env\n Cloudflare → c.env bindings\n Deno / Netlify → Deno.env.get()\n Vercel Edge → process.env (V8 Node compat)\n ========================================================================== */\n\nexport function getEnv(c: HonoContext, key: string): string | undefined {\n const honoEnvs = env(c) as Record<string, string | undefined>;\n return honoEnvs[key];\n}\n\n/* ==========================================================================\n requireEnv\n\n Same as getEnv but throws immediately if the variable is missing or empty.\n Always handle the thrown error in your route's catch block.\n ========================================================================== */\n\nexport function requireEnv(c: HonoContext, key: string): string {\n const val = getEnv(c, key);\n\n if (!val) {\n biniLogger.error(\n `Missing required environment variable: \"${key}\"\\n` +\n ` ${C.DIM}-> Set it in your platform's env config or hosting dashboard.${C.RESET}`,\n );\n\n throw new Error(`[bini-env] Missing required environment variable: \"${key}\"`);\n }\n\n return val;\n}\n\n/* ==========================================================================\n biniEnv — Vite plugin\n\n Tells Vite which prefixes to expose to import.meta.env.\n Prints the ß Bini.js banner on dev and preview server start.\n Vite handles .env file loading, watching, server restarts, and HMR natively.\n ========================================================================== */\n\nconst DEFAULT_PREFIXES = ['BINI_', 'VITE_'] as const;\n\nfunction detectEnvFiles(root: string): string[] {\n try {\n // eslint-disable-next-line @typescript-eslint/no-require-imports\n const fs = require('node:fs') as typeof import('fs');\n // eslint-disable-next-line @typescript-eslint/no-require-imports\n const path = require('node:path') as typeof import('path');\n const nodeEnv = process.env.NODE_ENV ?? 'production';\n return [\n '.env.local',\n `.env.${nodeEnv}.local`,\n `.env.${nodeEnv}`,\n '.env',\n ].filter(f => fs.existsSync(path.join(root, f)));\n } catch {\n return [];\n }\n}\n\nfunction printBanner(server: ViteDevServer | PreviewServer, mode: 'dev' | 'preview', root: string): void {\n const original = server.printUrls.bind(server);\n server.printUrls = () => {\n console.log(`\\n ${C.CYAN}ß Bini.js${C.RESET} (${mode})`);\n const found = detectEnvFiles(root);\n if (found.length > 0) {\n console.log(` \\x1b[32m➜\\x1b[0m Environments: ${found.join(', ')}`);\n }\n original();\n };\n}\n\nexport function biniEnv(options: Readonly<BiniEnvPluginOptions> = {}): Plugin {\n const extraPrefixes = options.envPrefix\n ? (Array.isArray(options.envPrefix) ? options.envPrefix : [options.envPrefix])\n : [];\n\n const envPrefix = [\n ...new Set([...DEFAULT_PREFIXES, ...extraPrefixes]),\n ];\n\n return {\n name: 'bini-env',\n\n config(): UserConfig {\n return { envPrefix };\n },\n\n configureServer(server) {\n printBanner(server, 'dev', server.config.root);\n },\n\n configurePreviewServer(server) {\n printBanner(server, 'preview', server.config.root);\n },\n };\n}\n\n/* ==========================================================================\n Re-exports\n ========================================================================== */\n\nexport type { Plugin } from 'vite';"]}
1
+ {"version":3,"sources":["../src/index.ts"],"names":[],"mappings":";;;;;;;;AAIA,IAAM,CAAA,GAAI;AAAA,EACR,IAAA,EAAQ,UAAA;AAAA,EACR,KAAA,EAAQ,SAAA;AAAA,EACR,MAAA,EAAQ,UAAA;AAAA,EACR,GAAA,EAAQ,UAAA;AAAA,EACR,IAAA,EAAQ,SAAA;AAAA,EACR,GAAA,EAAQ;AACV,CAAA;AAIA,SAAS,GAAA,GAAc;AACrB,EAAA,OAAO,GAAG,CAAA,CAAE,GAAG,CAAA,EAAA,iBAAG,IAAI,MAAK,EAAE,kBAAA,CAAmB,OAAA,EAAS,EAAE,QAAQ,KAAA,EAAO,CAAC,CAAA,EAAG,EAAE,KAAK,CAAA,CAAA;AACvF;AAEO,IAAM,UAAA,GAAa;AAAA,EACxB,KAAK,GAAA,EAAmB;AACtB,IAAA,OAAA,CAAQ,GAAA,CAAI,CAAA,EAAG,GAAA,EAAK,IAAI,CAAA,CAAE,IAAI,CAAA,EAAG,CAAA,CAAE,IAAI,CAAA,UAAA,EAAa,CAAA,CAAE,KAAK,CAAA,CAAA,EAAI,GAAG,CAAA,CAAE,CAAA;AAAA,EACtE,CAAA;AAAA,EACA,KAAK,GAAA,EAAmB;AACtB,IAAA,OAAA,CAAQ,IAAA;AAAA,MACN,GAAG,GAAA,EAAK,IAAI,CAAA,CAAE,MAAM,GAAG,CAAA,CAAE,IAAI,iBAAiB,CAAA,CAAE,KAAK,IAAI,CAAA,CAAE,MAAM,GAAG,GAAG,CAAA,EAAG,EAAE,KAAK,CAAA;AAAA,KACnF;AAAA,EACF,CAAA;AAAA,EACA,KAAA,CAAM,KAAa,GAAA,EAAqB;AACtC,IAAA,MAAM,MAAA,GACJ,eAAe,KAAA,GAAQ;AAAA,IAAA,EAAS,CAAA,CAAE,GAAG,CAAA,EAAG,GAAA,CAAI,OAAO,CAAA,EAAG,CAAA,CAAE,KAAK,CAAA,CAAA,GAAK,EAAA;AACpE,IAAA,OAAA,CAAQ,KAAA;AAAA,MACN,CAAA,EAAG,GAAA,EAAK,CAAA,CAAA,EAAI,EAAE,GAAG,CAAA,EAAG,CAAA,CAAE,IAAI,mBAAmB,CAAA,CAAE,KAAK,CAAA,CAAA,EAAI,GAAG,GAAG,MAAM,CAAA;AAAA,KACtE;AAAA,EACF;AACF;AAEO,SAAS,MAAA,CAAO,GAAgB,GAAA,EAAiC;AACtE,EAAA,MAAM,QAAA,GAAW,IAAI,CAAC,CAAA;AACtB,EAAA,OAAO,SAAS,GAAG,CAAA;AACrB;AAEO,SAAS,UAAA,CAAW,GAAgB,GAAA,EAAqB;AAC9D,EAAA,MAAM,GAAA,GAAM,MAAA,CAAO,CAAA,EAAG,GAAG,CAAA;AAEzB,EAAA,IAAI,CAAC,GAAA,EAAK;AACR,IAAA,UAAA,CAAW,KAAA;AAAA,MACT,2CAA2C,GAAG,CAAA;AAAA,EAAA,EACzC,CAAA,CAAE,GAAG,CAAA,6DAAA,EAAgE,CAAA,CAAE,KAAK,CAAA;AAAA,KACnF;AAEA,IAAA,MAAM,IAAI,KAAA,CAAM,CAAA,mDAAA,EAAsD,GAAG,CAAA,CAAA,CAAG,CAAA;AAAA,EAC9E;AAEA,EAAA,OAAO,GAAA;AACT;AAEA,IAAM,YAAA,GAAe,CAAC,OAAA,EAAS,OAAO,CAAA;AAEtC,SAAS,eAAe,IAAA,EAAwB;AAC9C,EAAA,IAAI;AACF,IAAA,MAAM,EAAA,GAAO,UAAQ,IAAS,CAAA;AAC9B,IAAA,MAAM,IAAA,GAAO,UAAQ,MAAW,CAAA;AAChC,IAAA,MAAM,OAAA,GAAU,OAAA,CAAQ,GAAA,CAAI,QAAA,IAAY,YAAA;AACxC,IAAA,OAAO;AAAA,MACL,YAAA;AAAA,MACA,QAAQ,OAAO,CAAA,MAAA,CAAA;AAAA,MACf,QAAQ,OAAO,CAAA,CAAA;AAAA,MACf;AAAA,KACF,CAAE,MAAA,CAAO,CAAA,CAAA,KAAK,EAAA,CAAG,UAAA,CAAW,KAAK,IAAA,CAAK,IAAA,EAAM,CAAC,CAAC,CAAC,CAAA;AAAA,EACjD,CAAA,CAAA,MAAQ;AACN,IAAA,OAAO,EAAC;AAAA,EACV;AACF;AAEA,SAAS,oBAAA,CAAqB,MAAc,MAAA,EAAwB;AAClE,EAAA,IAAI,OAAO,OAAA,KAAY,WAAA,IAAe,CAAC,OAAA,CAAQ,KAAK,OAAO,CAAA;AAE3D,EAAA,MAAM,EAAE,OAAA,EAAQ,GAAI,SAAA,CAAQ,MAAM,CAAA;AAClC,EAAA,MAAM,MAAA,GAAS,OAAA,CAAQ,IAAA,EAAM,MAAA,EAAQ,EAAE,CAAA;AACvC,EAAA,IAAI,QAAA,GAAW,CAAA;AAEf,EAAA,KAAA,MAAW,CAAC,GAAA,EAAK,KAAK,KAAK,MAAA,CAAO,OAAA,CAAQ,MAAM,CAAA,EAAG;AACjD,IAAA,MAAM,UAAA,GAAa,aAAa,IAAA,CAAK,CAAC,MAAM,GAAA,CAAI,UAAA,CAAW,CAAC,CAAC,CAAA;AAC7D,IAAA,IAAI,UAAA,EAAY;AAChB,IAAA,IAAI,CAAC,KAAA,EAAO;AACZ,IAAA,IAAI,OAAA,CAAQ,GAAA,CAAI,GAAG,CAAA,KAAM,MAAA,EAAW;AACpC,IAAA,OAAA,CAAQ,GAAA,CAAI,GAAG,CAAA,GAAI,KAAA;AACnB,IAAA,QAAA,EAAA;AAAA,EACF;AAEA,EAAA,OAAO,QAAA;AACT;AAEA,SAAS,WAAA,CAAY,MAAA,EAAuC,IAAA,EAAyB,IAAA,EAAoB;AACvG,EAAA,MAAM,QAAA,GAAW,MAAA,CAAO,SAAA,CAAU,IAAA,CAAK,MAAM,CAAA;AAC7C,EAAA,MAAA,CAAO,YAAY,MAAM;AACvB,IAAA,OAAA,CAAQ,GAAA,CAAI;AAAA,EAAA,EAAO,EAAE,IAAI,CAAA,YAAA,EAAY,EAAE,KAAK,CAAA,EAAA,EAAK,IAAI,CAAA,CAAA,CAAG,CAAA;AACxD,IAAA,MAAM,KAAA,GAAQ,eAAe,IAAI,CAAA;AACjC,IAAA,IAAI,KAAA,CAAM,SAAS,CAAA,EAAG;AACpB,MAAA,OAAA,CAAQ,IAAI,CAAA,uCAAA,EAAqC,KAAA,CAAM,IAAA,CAAK,IAAI,CAAC,CAAA,CAAE,CAAA;AAAA,IACrE;AACA,IAAA,QAAA,EAAS;AAAA,EACX,CAAA;AACF;AAEO,SAAS,OAAA,GAAkB;AAChC,EAAA,OAAO;AAAA,IACL,IAAA,EAAM,UAAA;AAAA,IAEN,MAAA,CAAO,UAAA,EAAwB,EAAE,OAAA,EAAS,MAAK,EAAe;AAC5D,MAAA,IAAI,OAAA,KAAY,OAAA,IAAW,UAAA,CAAW,MAAA,KAAW,KAAA,EAAO;AACtD,QAAA,MAAM,MAAA,GACJ,OAAO,UAAA,CAAW,MAAA,KAAW,QAAA,GACzB,WAAW,MAAA,GACX,UAAA,CAAW,IAAA,IAAQ,OAAA,CAAQ,GAAA,EAAI;AAErC,QAAA,IAAI;AACF,UAAA,oBAAA,CAAqB,MAAM,MAAM,CAAA;AAAA,QACnC,SAAS,GAAA,EAAK;AACZ,UAAA,UAAA,CAAW,IAAA;AAAA,YACT,CAAA,wCAAA,EAA4C,IAAc,OAAO,CAAA;AAAA,WACnE;AAAA,QACF;AAAA,MACF;AAEA,MAAA,OAAO,EAAE,SAAA,EAAW,CAAC,GAAG,YAAY,CAAA,EAAE;AAAA,IACxC,CAAA;AAAA,IAEA,gBAAgB,MAAA,EAAQ;AACtB,MAAA,WAAA,CAAY,MAAA,EAAQ,KAAA,EAAO,MAAA,CAAO,MAAA,CAAO,IAAI,CAAA;AAAA,IAC/C,CAAA;AAAA,IAEA,uBAAuB,MAAA,EAAQ;AAC7B,MAAA,WAAA,CAAY,MAAA,EAAQ,SAAA,EAAW,MAAA,CAAO,MAAA,CAAO,IAAI,CAAA;AAAA,IACnD;AAAA,GACF;AACF","file":"index.mjs","sourcesContent":["import { env } from 'hono/adapter';\nimport type { Context } from 'hono';\nimport type { Plugin, UserConfig, ViteDevServer, PreviewServer } from 'vite';\n\nconst C = {\n CYAN : '\\x1b[36m',\n RESET : '\\x1b[0m',\n YELLOW: '\\x1b[33m',\n RED : '\\x1b[31m',\n BOLD : '\\x1b[1m',\n DIM : '\\x1b[2m',\n} as const;\n\nexport type HonoContext = Context;\n\nfunction _ts(): string {\n return `${C.DIM}${new Date().toLocaleTimeString('en-US', { hour12: false })}${C.RESET}`;\n}\n\nexport const biniLogger = {\n info(msg: string): void {\n console.log(`${_ts()} ${C.CYAN}${C.BOLD}[bini-env]${C.RESET} ${msg}`);\n },\n warn(msg: string): void {\n console.warn(\n `${_ts()} ${C.YELLOW}${C.BOLD}(!) [bini-env]${C.RESET} ${C.YELLOW}${msg}${C.RESET}`,\n );\n },\n error(msg: string, err?: unknown): void {\n const detail =\n err instanceof Error ? `\\n ${C.DIM}${err.message}${C.RESET}` : '';\n console.error(\n `${_ts()} ${C.RED}${C.BOLD}[bini-env] error${C.RESET} ${msg}${detail}`,\n );\n },\n} as const;\n\nexport function getEnv(c: HonoContext, key: string): string | undefined {\n const honoEnvs = env(c) as Record<string, string | undefined>;\n return honoEnvs[key];\n}\n\nexport function requireEnv(c: HonoContext, key: string): string {\n const val = getEnv(c, key);\n\n if (!val) {\n biniLogger.error(\n `Missing required environment variable: \"${key}\"\\n` +\n ` ${C.DIM}-> Set it in your platform's env config or hosting dashboard.${C.RESET}`,\n );\n\n throw new Error(`[bini-env] Missing required environment variable: \"${key}\"`);\n }\n\n return val;\n}\n\nconst ENV_PREFIXES = ['BINI_', 'VITE_'] as const;\n\nfunction detectEnvFiles(root: string): string[] {\n try {\n const fs = require('node:fs') as typeof import('fs');\n const path = require('node:path') as typeof import('path');\n const nodeEnv = process.env.NODE_ENV ?? 'production';\n return [\n '.env.local',\n `.env.${nodeEnv}.local`,\n `.env.${nodeEnv}`,\n '.env',\n ].filter(f => fs.existsSync(path.join(root, f)));\n } catch {\n return [];\n }\n}\n\nfunction injectEnvIntoProcess(mode: string, envDir: string): number {\n if (typeof process === 'undefined' || !process.env) return 0;\n\n const { loadEnv } = require('vite') as typeof import('vite');\n const loaded = loadEnv(mode, envDir, '');\n let injected = 0;\n\n for (const [key, value] of Object.entries(loaded)) {\n const isPrefixed = ENV_PREFIXES.some((p) => key.startsWith(p));\n if (isPrefixed) continue;\n if (!value) continue;\n if (process.env[key] !== undefined) continue;\n process.env[key] = value;\n injected++;\n }\n\n return injected;\n}\n\nfunction printBanner(server: ViteDevServer | PreviewServer, mode: 'dev' | 'preview', root: string): void {\n const original = server.printUrls.bind(server);\n server.printUrls = () => {\n console.log(`\\n ${C.CYAN}ß Bini.js${C.RESET} (${mode})`);\n const found = detectEnvFiles(root);\n if (found.length > 0) {\n console.log(` \\x1b[32m➜\\x1b[0m Environments: ${found.join(', ')}`);\n }\n original();\n };\n}\n\nexport function biniEnv(): Plugin {\n return {\n name: 'bini-env',\n\n config(userConfig: UserConfig, { command, mode }): UserConfig {\n if (command === 'serve' && userConfig.envDir !== false) {\n const envDir =\n typeof userConfig.envDir === 'string'\n ? userConfig.envDir\n : userConfig.root ?? process.cwd();\n\n try {\n injectEnvIntoProcess(mode, envDir);\n } catch (err) {\n biniLogger.warn(\n `Failed to inject .env into process.env: ${(err as Error).message}`,\n );\n }\n }\n\n return { envPrefix: [...ENV_PREFIXES] };\n },\n\n configureServer(server) {\n printBanner(server, 'dev', server.config.root);\n },\n\n configurePreviewServer(server) {\n printBanner(server, 'preview', server.config.root);\n },\n };\n}\n\nexport type { Plugin } from 'vite';"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "bini-env",
3
- "version": "1.1.1",
3
+ "version": "2.0.0",
4
4
  "description": "Universal environment variable loader and Vite plugin for Bini.js — powered by Hono, works across Node.js, Bun, Deno, Vercel Edge, Netlify Edge, and Cloudflare Workers",
5
5
  "main": "./dist/index.cjs",
6
6
  "module": "./dist/index.js",
@@ -69,10 +69,10 @@
69
69
  }
70
70
  },
71
71
  "devDependencies": {
72
- "@types/node": "^25.6.0",
72
+ "@types/node": "26.1.2",
73
73
  "hono": "^4.8.4",
74
74
  "tsup": "^8.5.1",
75
- "typescript": "^5.9.3",
75
+ "typescript": "^5.8.3",
76
76
  "vite": "^8.0.8"
77
77
  },
78
78
  "engines": {