bini-env 1.1.2 → 2.0.1

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/dist/index.d.ts CHANGED
@@ -1,34 +1,28 @@
1
1
  import { Context } from 'hono';
2
- import { Plugin } from 'vite';
3
- export { Plugin } from 'vite';
4
2
 
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
3
  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
- }
4
+ type EnvSource = Record<string, unknown>;
5
+ type EnvInput = HonoContext | EnvSource | undefined | null;
25
6
  declare const biniLogger: {
26
7
  readonly info: (msg: string) => void;
27
8
  readonly warn: (msg: string) => void;
28
9
  readonly error: (msg: string, err?: unknown) => void;
29
10
  };
30
- declare function getEnv(c: HonoContext, key: string): string | undefined;
31
- declare function requireEnv(c: HonoContext, key: string): string;
32
- declare function biniEnv(options?: Readonly<BiniEnvPluginOptions>): Plugin;
11
+ /**
12
+ * Reads one variable. Returns `undefined` when it is missing or is not a string
13
+ * (Cloudflare bindings such as KV namespaces are not env strings).
14
+ */
15
+ declare function getEnv(source: EnvInput, key: string): string | undefined;
16
+ /** Reads one variable and throws (after logging) when it is missing or empty. */
17
+ declare function requireEnv(source: EnvInput, key: string): string;
18
+ interface EnvCheck {
19
+ ok: boolean;
20
+ /** Names only, never values. */
21
+ missing: string[];
22
+ }
23
+ /** Checks several variables at once and logs every missing name in one message. */
24
+ declare function checkEnv(source: EnvInput, keys: readonly string[]): EnvCheck;
25
+ /** Requires several variables at once. Throws one error listing all that are missing. */
26
+ declare function requireEnvs<K extends string>(source: EnvInput, keys: readonly K[]): Record<K, string>;
33
27
 
34
- export { type BiniEnvPluginOptions, type HonoContext, biniEnv, biniLogger, getEnv, requireEnv };
28
+ export { type EnvCheck, type EnvInput, type EnvSource, type HonoContext, biniLogger, checkEnv, getEnv, requireEnv, requireEnvs };
package/dist/index.js CHANGED
@@ -1,13 +1,6 @@
1
- 'use strict';
1
+ import { env } from 'hono/adapter';
2
2
 
3
- var adapter = require('hono/adapter');
4
-
5
- var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
6
- get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
7
- }) : x)(function(x) {
8
- if (typeof require !== "undefined") return require.apply(this, arguments);
9
- throw Error('Dynamic require of "' + x + '" is not supported');
10
- });
3
+ // src/index.ts
11
4
  var C = {
12
5
  CYAN: "\x1B[36m",
13
6
  RESET: "\x1B[0m",
@@ -16,91 +9,73 @@ var C = {
16
9
  BOLD: "\x1B[1m",
17
10
  DIM: "\x1B[2m"
18
11
  };
19
- function _ts() {
12
+ function timestamp() {
20
13
  return `${C.DIM}${(/* @__PURE__ */ new Date()).toLocaleTimeString("en-US", { hour12: false })}${C.RESET}`;
21
14
  }
22
15
  var biniLogger = {
23
16
  info(msg) {
24
- console.log(`${_ts()} ${C.CYAN}${C.BOLD}[bini-env]${C.RESET} ${msg}`);
17
+ console.log(`${timestamp()} ${C.CYAN}${C.BOLD}[bini-env]${C.RESET} ${msg}`);
25
18
  },
26
19
  warn(msg) {
27
- console.warn(
28
- `${_ts()} ${C.YELLOW}${C.BOLD}(!) [bini-env]${C.RESET} ${C.YELLOW}${msg}${C.RESET}`
29
- );
20
+ console.warn(`${timestamp()} ${C.YELLOW}${C.BOLD}(!) [bini-env]${C.RESET} ${C.YELLOW}${msg}${C.RESET}`);
30
21
  },
31
22
  error(msg, err) {
32
23
  const detail = err instanceof Error ? `
33
24
  ${C.DIM}${err.message}${C.RESET}` : "";
34
- console.error(
35
- `${_ts()} ${C.RED}${C.BOLD}[bini-env] error${C.RESET} ${msg}${detail}`
36
- );
25
+ console.error(`${timestamp()} ${C.RED}${C.BOLD}[bini-env] error${C.RESET} ${msg}${detail}`);
37
26
  }
38
27
  };
39
- function getEnv(c, key) {
40
- const honoEnvs = adapter.env(c);
41
- return honoEnvs[key];
28
+ function isContext(value) {
29
+ if (typeof value !== "object" || value === null) return false;
30
+ const v = value;
31
+ return typeof v.req === "object" && v.req !== null && typeof v.get === "function";
32
+ }
33
+ function runtimeEnv() {
34
+ const g = globalThis;
35
+ if (g.process?.env) return g.process.env;
36
+ try {
37
+ const obj = g.Deno?.env?.toObject?.();
38
+ if (obj) return obj;
39
+ } catch {
40
+ }
41
+ return void 0;
42
42
  }
43
- function requireEnv(c, key) {
44
- const val = getEnv(c, key);
45
- if (!val) {
43
+ function toEnvObject(source) {
44
+ if (isContext(source)) return env(source);
45
+ return source ?? runtimeEnv();
46
+ }
47
+ function getEnv(source, key) {
48
+ const value = toEnvObject(source)?.[key];
49
+ return typeof value === "string" ? value : void 0;
50
+ }
51
+ function requireEnv(source, key) {
52
+ const value = getEnv(source, key);
53
+ if (!value) {
46
54
  biniLogger.error(
47
55
  `Missing required environment variable: "${key}"
48
- ${C.DIM}-> Set it in your platform's env config or hosting dashboard.${C.RESET}`
56
+ ${C.DIM}-> Set it in your hosting dashboard (or in .env for dev, preview and bini-server).${C.RESET}`
49
57
  );
50
58
  throw new Error(`[bini-env] Missing required environment variable: "${key}"`);
51
59
  }
52
- return val;
60
+ return value;
53
61
  }
54
- var DEFAULT_PREFIXES = ["BINI_", "VITE_"];
55
- function detectEnvFiles(root) {
56
- try {
57
- const fs = __require("fs");
58
- const path = __require("path");
59
- const nodeEnv = process.env.NODE_ENV ?? "production";
60
- return [
61
- ".env.local",
62
- `.env.${nodeEnv}.local`,
63
- `.env.${nodeEnv}`,
64
- ".env"
65
- ].filter((f) => fs.existsSync(path.join(root, f)));
66
- } catch {
67
- return [];
62
+ function checkEnv(source, keys) {
63
+ const missing = keys.filter((k) => !getEnv(source, k));
64
+ if (missing.length > 0) {
65
+ biniLogger.error(`Missing environment variables: ${missing.join(", ")}`);
68
66
  }
67
+ return { ok: missing.length === 0, missing };
69
68
  }
70
- function printBanner(server, mode, root) {
71
- const original = server.printUrls.bind(server);
72
- server.printUrls = () => {
73
- console.log(`
74
- ${C.CYAN}\xDF Bini.js${C.RESET} (${mode})`);
75
- const found = detectEnvFiles(root);
76
- if (found.length > 0) {
77
- console.log(` \x1B[32m\u279C\x1B[0m Environments: ${found.join(", ")}`);
78
- }
79
- original();
80
- };
81
- }
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
- ];
87
- return {
88
- name: "bini-env",
89
- config() {
90
- return { envPrefix };
91
- },
92
- configureServer(server) {
93
- printBanner(server, "dev", server.config.root);
94
- },
95
- configurePreviewServer(server) {
96
- printBanner(server, "preview", server.config.root);
97
- }
98
- };
69
+ function requireEnvs(source, keys) {
70
+ const { missing } = checkEnv(source, keys);
71
+ if (missing.length > 0) {
72
+ throw new Error(`[bini-env] Missing environment variables: ${missing.join(", ")}`);
73
+ }
74
+ const out = {};
75
+ for (const k of keys) out[k] = getEnv(source, k);
76
+ return out;
99
77
  }
100
78
 
101
- exports.biniEnv = biniEnv;
102
- exports.biniLogger = biniLogger;
103
- exports.getEnv = getEnv;
104
- exports.requireEnv = requireEnv;
79
+ export { biniLogger, checkEnv, getEnv, requireEnv, requireEnvs };
105
80
  //# sourceMappingURL=index.js.map
106
81
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts"],"names":["env"],"mappings":"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","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":["honoEnv"],"mappings":"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","file":"index.js","sourcesContent":["import { env as honoEnv } from 'hono/adapter';\nimport type { Context } from 'hono';\n\nexport type HonoContext = Context;\nexport type EnvSource = Record<string, unknown>;\nexport type EnvInput = HonoContext | EnvSource | undefined | null;\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\nfunction timestamp(): 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(`${timestamp()} ${C.CYAN}${C.BOLD}[bini-env]${C.RESET} ${msg}`);\n },\n warn(msg: string): void {\n console.warn(`${timestamp()} ${C.YELLOW}${C.BOLD}(!) [bini-env]${C.RESET} ${C.YELLOW}${msg}${C.RESET}`);\n },\n error(msg: string, err?: unknown): void {\n const detail = err instanceof Error ? `\\n ${C.DIM}${err.message}${C.RESET}` : '';\n console.error(`${timestamp()} ${C.RED}${C.BOLD}[bini-env] error${C.RESET} ${msg}${detail}`);\n },\n} as const;\n\nfunction isContext(value: unknown): value is HonoContext {\n if (typeof value !== 'object' || value === null) return false;\n const v = value as { req?: unknown; get?: unknown };\n return typeof v.req === 'object' && v.req !== null && typeof v.get === 'function';\n}\n\n/** Best-effort read of the current runtime's environment when no source is given. */\nfunction runtimeEnv(): EnvSource | undefined {\n const g = globalThis as {\n process?: { env?: EnvSource };\n Deno?: { env?: { toObject?: () => EnvSource } };\n };\n if (g.process?.env) return g.process.env;\n try {\n const obj = g.Deno?.env?.toObject?.();\n if (obj) return obj;\n } catch {\n // Deno.env needs --allow-env; treat as unavailable\n }\n return undefined;\n}\n\nfunction toEnvObject(source: EnvInput): EnvSource | undefined {\n if (isContext(source)) return honoEnv(source) as EnvSource;\n return source ?? runtimeEnv();\n}\n\n/**\n * Reads one variable. Returns `undefined` when it is missing or is not a string\n * (Cloudflare bindings such as KV namespaces are not env strings).\n */\nexport function getEnv(source: EnvInput, key: string): string | undefined {\n const value = toEnvObject(source)?.[key];\n return typeof value === 'string' ? value : undefined;\n}\n\n/** Reads one variable and throws (after logging) when it is missing or empty. */\nexport function requireEnv(source: EnvInput, key: string): string {\n const value = getEnv(source, key);\n if (!value) {\n biniLogger.error(\n `Missing required environment variable: \"${key}\"\\n` +\n ` ${C.DIM}-> Set it in your hosting dashboard (or in .env for dev, preview and bini-server).${C.RESET}`,\n );\n throw new Error(`[bini-env] Missing required environment variable: \"${key}\"`);\n }\n return value;\n}\n\nexport interface EnvCheck {\n ok: boolean;\n /** Names only, never values. */\n missing: string[];\n}\n\n/** Checks several variables at once and logs every missing name in one message. */\nexport function checkEnv(source: EnvInput, keys: readonly string[]): EnvCheck {\n const missing = keys.filter((k) => !getEnv(source, k));\n if (missing.length > 0) {\n biniLogger.error(`Missing environment variables: ${missing.join(', ')}`);\n }\n return { ok: missing.length === 0, missing };\n}\n\n/** Requires several variables at once. Throws one error listing all that are missing. */\nexport function requireEnvs<K extends string>(source: EnvInput, keys: readonly K[]): Record<K, string> {\n const { missing } = checkEnv(source, keys);\n if (missing.length > 0) {\n throw new Error(`[bini-env] Missing environment variables: ${missing.join(', ')}`);\n }\n const out = {} as Record<K, string>;\n for (const k of keys) out[k] = getEnv(source, k) as string;\n return out;\n}"]}
package/dist/node.d.ts ADDED
@@ -0,0 +1,8 @@
1
+ declare function envFilesFor(root: string, mode: string): string[];
2
+ interface LoadEnvResult {
3
+ files: string[];
4
+ injected: number;
5
+ }
6
+ declare function loadEnvFiles(root?: string, mode?: string): LoadEnvResult;
7
+
8
+ export { type LoadEnvResult, envFilesFor, loadEnvFiles };
package/dist/node.js ADDED
@@ -0,0 +1,37 @@
1
+ import { existsSync, readFileSync } from 'fs';
2
+ import path from 'path';
3
+ import { parseEnv } from 'util';
4
+
5
+ // src/node.ts
6
+ function envFilesFor(root, mode) {
7
+ return [`.env.${mode}.local`, `.env.${mode}`, ".env.local", ".env"].map((file) => path.join(root, file)).filter((file) => existsSync(file));
8
+ }
9
+ function loadEnvFiles(root = process.cwd(), mode = process.env.NODE_ENV ?? "production") {
10
+ const files = envFilesFor(root, mode);
11
+ let injected = 0;
12
+ for (const file of files) {
13
+ let raw;
14
+ try {
15
+ raw = readFileSync(file, "utf8");
16
+ } catch {
17
+ continue;
18
+ }
19
+ if (raw.charCodeAt(0) === 65279) raw = raw.slice(1);
20
+ let parsed;
21
+ try {
22
+ parsed = parseEnv(raw);
23
+ } catch {
24
+ continue;
25
+ }
26
+ for (const [key, value] of Object.entries(parsed)) {
27
+ if (value === void 0 || process.env[key] !== void 0) continue;
28
+ process.env[key] = value;
29
+ injected++;
30
+ }
31
+ }
32
+ return { files, injected };
33
+ }
34
+
35
+ export { envFilesFor, loadEnvFiles };
36
+ //# sourceMappingURL=node.js.map
37
+ //# sourceMappingURL=node.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/node.ts"],"names":[],"mappings":";;;;;AAIO,SAAS,WAAA,CAAY,MAAc,IAAA,EAAwB;AAChE,EAAA,OAAO,CAAC,CAAA,KAAA,EAAQ,IAAI,CAAA,MAAA,CAAA,EAAU,CAAA,KAAA,EAAQ,IAAI,CAAA,CAAA,EAAI,YAAA,EAAc,MAAM,CAAA,CAC/D,GAAA,CAAI,CAAC,SAAS,IAAA,CAAK,IAAA,CAAK,IAAA,EAAM,IAAI,CAAC,CAAA,CACnC,OAAO,CAAC,IAAA,KAAS,UAAA,CAAW,IAAI,CAAC,CAAA;AACtC;AAOO,SAAS,YAAA,CACd,OAAe,OAAA,CAAQ,GAAA,IACvB,IAAA,GAAe,OAAA,CAAQ,GAAA,CAAI,QAAA,IAAY,YAAA,EACxB;AACf,EAAA,MAAM,KAAA,GAAQ,WAAA,CAAY,IAAA,EAAM,IAAI,CAAA;AACpC,EAAA,IAAI,QAAA,GAAW,CAAA;AAEf,EAAA,KAAA,MAAW,QAAQ,KAAA,EAAO;AACxB,IAAA,IAAI,GAAA;AACJ,IAAA,IAAI;AACF,MAAA,GAAA,GAAM,YAAA,CAAa,MAAM,MAAM,CAAA;AAAA,IACjC,CAAA,CAAA,MAAQ;AACN,MAAA;AAAA,IACF;AAEA,IAAA,IAAI,GAAA,CAAI,WAAW,CAAC,CAAA,KAAM,OAAQ,GAAA,GAAM,GAAA,CAAI,MAAM,CAAC,CAAA;AAEnD,IAAA,IAAI,MAAA;AACJ,IAAA,IAAI;AACF,MAAA,MAAA,GAAS,SAAS,GAAG,CAAA;AAAA,IACvB,CAAA,CAAA,MAAQ;AACN,MAAA;AAAA,IACF;AAEA,IAAA,KAAA,MAAW,CAAC,GAAA,EAAK,KAAK,KAAK,MAAA,CAAO,OAAA,CAAQ,MAAM,CAAA,EAAG;AACjD,MAAA,IAAI,UAAU,MAAA,IAAa,OAAA,CAAQ,GAAA,CAAI,GAAG,MAAM,MAAA,EAAW;AAC3D,MAAA,OAAA,CAAQ,GAAA,CAAI,GAAG,CAAA,GAAI,KAAA;AACnB,MAAA,QAAA,EAAA;AAAA,IACF;AAAA,EACF;AAEA,EAAA,OAAO,EAAE,OAAO,QAAA,EAAS;AAC3B","file":"node.js","sourcesContent":["import { existsSync, readFileSync } from 'node:fs';\nimport path from 'node:path';\nimport { parseEnv } from 'node:util';\n\nexport function envFilesFor(root: string, mode: string): string[] {\n return [`.env.${mode}.local`, `.env.${mode}`, '.env.local', '.env']\n .map((file) => path.join(root, file))\n .filter((file) => existsSync(file));\n}\n\nexport interface LoadEnvResult {\n files: string[];\n injected: number;\n}\n\nexport function loadEnvFiles(\n root: string = process.cwd(),\n mode: string = process.env.NODE_ENV ?? 'production',\n): LoadEnvResult {\n const files = envFilesFor(root, mode);\n let injected = 0;\n\n for (const file of files) {\n let raw: string;\n try {\n raw = readFileSync(file, 'utf8');\n } catch {\n continue;\n }\n\n if (raw.charCodeAt(0) === 0xfeff) raw = raw.slice(1);\n\n let parsed: Record<string, string | undefined>;\n try {\n parsed = parseEnv(raw) as Record<string, string | undefined>;\n } catch {\n continue;\n }\n\n for (const [key, value] of Object.entries(parsed)) {\n if (value === undefined || process.env[key] !== undefined) continue;\n process.env[key] = value;\n injected++;\n }\n }\n\n return { files, injected };\n}"]}
package/dist/vite.d.ts ADDED
@@ -0,0 +1,6 @@
1
+ import { Plugin } from 'vite';
2
+ export { Plugin } from 'vite';
3
+
4
+ declare function biniEnv(): Plugin;
5
+
6
+ export { biniEnv };
package/dist/vite.js ADDED
@@ -0,0 +1,104 @@
1
+ import path from 'path';
2
+ import { existsSync } from 'fs';
3
+ import { loadEnv } from 'vite';
4
+
5
+ // src/vite.ts
6
+ var ENV_PREFIXES = ["BINI_", "VITE_"];
7
+ var SECRET_NAME = /SECRET|PASSWORD|PASSWD|PRIVATE|TOKEN/i;
8
+ var C = {
9
+ CYAN: "\x1B[36m",
10
+ YELLOW: "\x1B[33m",
11
+ GREEN: "\x1B[32m",
12
+ BOLD: "\x1B[1m",
13
+ DIM: "\x1B[2m",
14
+ RESET: "\x1B[0m"
15
+ };
16
+ var biniLogger = {
17
+ warn(msg) {
18
+ const ts = `${C.DIM}${(/* @__PURE__ */ new Date()).toLocaleTimeString("en-US", { hour12: false })}${C.RESET}`;
19
+ console.warn(`${ts} ${C.YELLOW}${C.BOLD}(!) [bini-env]${C.RESET} ${C.YELLOW}${msg}${C.RESET}`);
20
+ }
21
+ };
22
+ function envFilesFor(root, mode) {
23
+ return [`.env.${mode}.local`, `.env.${mode}`, ".env.local", ".env"].map((file) => path.join(root, file)).filter((file) => existsSync(file));
24
+ }
25
+ var warnedPublicSecrets = /* @__PURE__ */ new Set();
26
+ function injectEnvIntoProcess(mode, envDir) {
27
+ const loaded = loadEnv(mode, envDir, "");
28
+ let injected = 0;
29
+ for (const [key, value] of Object.entries(loaded)) {
30
+ const isPrefixed = ENV_PREFIXES.some((p) => key.startsWith(p));
31
+ if (isPrefixed && SECRET_NAME.test(key) && !warnedPublicSecrets.has(key)) {
32
+ warnedPublicSecrets.add(key);
33
+ biniLogger.warn(
34
+ `"${key}" starts with a public prefix (${ENV_PREFIXES.join(" / ")}) so it is bundled into client code. Rename it without the prefix if it is a secret.`
35
+ );
36
+ }
37
+ if (value === void 0 || process.env[key] !== void 0) continue;
38
+ process.env[key] = value;
39
+ injected++;
40
+ }
41
+ return injected;
42
+ }
43
+ function hideReadyLine(logger) {
44
+ const originalInfo = logger.info.bind(logger);
45
+ logger.info = ((msg, options) => {
46
+ if (typeof msg === "string" && msg.includes("ready in")) return;
47
+ originalInfo(msg, options);
48
+ });
49
+ }
50
+ function printBanner(server, label) {
51
+ const { root, mode } = server.config;
52
+ const originalPrintUrls = server.printUrls.bind(server);
53
+ server.printUrls = () => {
54
+ console.log(`
55
+ ${C.CYAN}\xDF Bini.js${C.RESET} (${label})`);
56
+ const files = envFilesFor(root, mode).map((f) => path.basename(f));
57
+ if (files.length > 0) {
58
+ console.log(` ${C.GREEN}\u279C${C.RESET} Environments: ${files.join(", ")}`);
59
+ }
60
+ originalPrintUrls();
61
+ };
62
+ }
63
+ function biniEnv() {
64
+ return {
65
+ name: "bini-env",
66
+ enforce: "pre",
67
+ config(userConfig, { command, mode }) {
68
+ if (userConfig.envDir !== false) {
69
+ const root = path.resolve(userConfig.root ?? process.cwd());
70
+ const envDir = typeof userConfig.envDir === "string" ? path.resolve(root, userConfig.envDir) : root;
71
+ try {
72
+ if (command === "serve") {
73
+ injectEnvIntoProcess(mode, envDir);
74
+ } else {
75
+ const loaded = loadEnv(mode, envDir, [...ENV_PREFIXES]);
76
+ for (const key of Object.keys(loaded)) {
77
+ if (SECRET_NAME.test(key) && !warnedPublicSecrets.has(key)) {
78
+ warnedPublicSecrets.add(key);
79
+ biniLogger.warn(
80
+ `"${key}" starts with a public prefix (${ENV_PREFIXES.join(" / ")}) so it is bundled into client code. Rename it without the prefix if it is a secret.`
81
+ );
82
+ }
83
+ }
84
+ }
85
+ } catch (err) {
86
+ biniLogger.warn(`Failed to load .env files: ${err.message}`);
87
+ }
88
+ }
89
+ return { envPrefix: [...ENV_PREFIXES] };
90
+ },
91
+ configureServer(server) {
92
+ hideReadyLine(server.config.logger);
93
+ printBanner(server, "dev");
94
+ },
95
+ configurePreviewServer(server) {
96
+ hideReadyLine(server.config.logger);
97
+ printBanner(server, "preview");
98
+ }
99
+ };
100
+ }
101
+
102
+ export { biniEnv };
103
+ //# sourceMappingURL=vite.js.map
104
+ //# sourceMappingURL=vite.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/vite.ts"],"names":[],"mappings":"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","file":"vite.js","sourcesContent":["import path from 'node:path';\nimport { existsSync } from 'node:fs';\nimport { loadEnv } from 'vite';\nimport type { Logger, Plugin, PreviewServer, UserConfig, ViteDevServer } from 'vite';\n\nconst ENV_PREFIXES = ['BINI_', 'VITE_'] as const;\nconst SECRET_NAME = /SECRET|PASSWORD|PASSWD|PRIVATE|TOKEN/i;\n\nconst C = {\n CYAN: '\\x1b[36m',\n YELLOW: '\\x1b[33m',\n GREEN: '\\x1b[32m',\n BOLD: '\\x1b[1m',\n DIM: '\\x1b[2m',\n RESET: '\\x1b[0m',\n} as const;\n\nconst biniLogger = {\n warn(msg: string): void {\n const ts = `${C.DIM}${new Date().toLocaleTimeString('en-US', { hour12: false })}${C.RESET}`;\n console.warn(`${ts} ${C.YELLOW}${C.BOLD}(!) [bini-env]${C.RESET} ${C.YELLOW}${msg}${C.RESET}`);\n },\n} as const;\n\nfunction envFilesFor(root: string, mode: string): string[] {\n return [`.env.${mode}.local`, `.env.${mode}`, '.env.local', '.env']\n .map((file) => path.join(root, file))\n .filter((file) => existsSync(file));\n}\n\nconst warnedPublicSecrets = new Set<string>();\n\nfunction injectEnvIntoProcess(mode: string, envDir: string): number {\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\n if (isPrefixed && SECRET_NAME.test(key) && !warnedPublicSecrets.has(key)) {\n warnedPublicSecrets.add(key);\n biniLogger.warn(\n `\"${key}\" starts with a public prefix (${ENV_PREFIXES.join(' / ')}) so it is bundled into client code. ` +\n `Rename it without the prefix if it is a secret.`,\n );\n }\n\n if (value === undefined || process.env[key] !== undefined) continue;\n process.env[key] = value;\n injected++;\n }\n\n return injected;\n}\n\nfunction hideReadyLine(logger: Logger): void {\n const originalInfo = logger.info.bind(logger);\n logger.info = ((msg: string, options?: Parameters<Logger['info']>[1]) => {\n if (typeof msg === 'string' && msg.includes('ready in')) return;\n originalInfo(msg, options);\n }) as Logger['info'];\n}\n\nfunction printBanner(server: ViteDevServer | PreviewServer, label: 'dev' | 'preview'): void {\n const { root, mode } = server.config;\n const originalPrintUrls = server.printUrls.bind(server);\n\n server.printUrls = () => {\n console.log(`\\n ${C.CYAN}ß Bini.js${C.RESET} (${label})`);\n const files = envFilesFor(root, mode).map((f) => path.basename(f));\n if (files.length > 0) {\n console.log(` ${C.GREEN}➜${C.RESET} Environments: ${files.join(', ')}`);\n }\n originalPrintUrls();\n };\n}\n\nexport function biniEnv(): Plugin {\n return {\n name: 'bini-env',\n enforce: 'pre',\n\n config(userConfig: UserConfig, { command, mode }): UserConfig {\n if (userConfig.envDir !== false) {\n const root = path.resolve(userConfig.root ?? process.cwd());\n const envDir =\n typeof userConfig.envDir === 'string'\n ? path.resolve(root, userConfig.envDir)\n : root;\n\n try {\n if (command === 'serve') {\n injectEnvIntoProcess(mode, envDir);\n } else {\n const loaded = loadEnv(mode, envDir, [...ENV_PREFIXES]);\n for (const key of Object.keys(loaded)) {\n if (SECRET_NAME.test(key) && !warnedPublicSecrets.has(key)) {\n warnedPublicSecrets.add(key);\n biniLogger.warn(\n `\"${key}\" starts with a public prefix (${ENV_PREFIXES.join(' / ')}) so it is bundled into client code. ` +\n `Rename it without the prefix if it is a secret.`,\n );\n }\n }\n }\n } catch (err) {\n biniLogger.warn(`Failed to load .env files: ${(err as Error).message}`);\n }\n }\n return { envPrefix: [...ENV_PREFIXES] };\n },\n\n configureServer(server) {\n hideReadyLine(server.config.logger);\n printBanner(server, 'dev');\n },\n\n configurePreviewServer(server) {\n hideReadyLine(server.config.logger);\n printBanner(server, 'preview');\n },\n };\n}\n\nexport type { Plugin } from 'vite';"]}
package/package.json CHANGED
@@ -1,15 +1,23 @@
1
1
  {
2
2
  "name": "bini-env",
3
- "version": "1.1.2",
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
- "main": "./dist/index.cjs",
3
+ "version": "2.0.1",
4
+ "description": "Universal environment variable loader and Vite plugin for Bini.js",
5
+ "type": "module",
6
+ "main": "./dist/index.js",
6
7
  "module": "./dist/index.js",
7
8
  "types": "./dist/index.d.ts",
8
9
  "exports": {
9
10
  ".": {
10
11
  "types": "./dist/index.d.ts",
11
- "import": "./dist/index.js",
12
- "require": "./dist/index.cjs"
12
+ "default": "./dist/index.js"
13
+ },
14
+ "./vite": {
15
+ "types": "./dist/vite.d.ts",
16
+ "default": "./dist/vite.js"
17
+ },
18
+ "./node": {
19
+ "types": "./dist/node.d.ts",
20
+ "default": "./dist/node.js"
13
21
  }
14
22
  },
15
23
  "files": [
@@ -17,13 +25,16 @@
17
25
  "README.md",
18
26
  "LICENSE"
19
27
  ],
20
- "sideEffects": true,
28
+ "sideEffects": [
29
+ "./dist/vite.js"
30
+ ],
21
31
  "scripts": {
22
32
  "build": "tsup",
23
33
  "dev": "tsup --watch",
24
- "prepublishOnly": "npm run build",
34
+ "prepublishOnly": "npm run build && npm run check:edge",
25
35
  "test": "node --test",
26
- "type-check": "tsc --noEmit"
36
+ "type-check": "tsc --noEmit",
37
+ "check:edge": "node -e \"const fs=require('fs');const s=fs.readFileSync('dist/index.js','utf8');const re=/(?:from\\s*['\\\"]|require\\(\\s*['\\\"])(node:|vite)/;if(re.test(s)){console.error('[bini-env] edge entry leaks node:/vite imports:');console.error(s.match(re)[0]);process.exit(1)}else{console.log('[bini-env] dist/index.js is edge-safe')}\""
27
38
  },
28
39
  "keywords": [
29
40
  "vite",
@@ -65,18 +76,18 @@
65
76
  "optional": false
66
77
  },
67
78
  "vite": {
68
- "optional": false
79
+ "optional": true
69
80
  }
70
81
  },
71
82
  "devDependencies": {
72
- "@types/node": "26.1.2",
83
+ "@types/node": "^22.10.0",
73
84
  "hono": "^4.8.4",
74
85
  "tsup": "^8.5.1",
75
86
  "typescript": "^5.8.3",
76
87
  "vite": "^8.0.8"
77
88
  },
78
89
  "engines": {
79
- "node": ">=20.19.0 || >=22.12.0"
90
+ "node": ">=20.12.0"
80
91
  },
81
92
  "publishConfig": {
82
93
  "access": "public",
package/dist/index.d.mts DELETED
@@ -1,34 +0,0 @@
1
- import { Context } from 'hono';
2
- import { Plugin } from 'vite';
3
- export { Plugin } from 'vite';
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
- 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
- declare const biniLogger: {
26
- readonly info: (msg: string) => void;
27
- readonly warn: (msg: string) => void;
28
- readonly error: (msg: string, err?: unknown) => void;
29
- };
30
- declare function getEnv(c: HonoContext, key: string): string | undefined;
31
- declare function requireEnv(c: HonoContext, key: string): string;
32
- declare function biniEnv(options?: Readonly<BiniEnvPluginOptions>): Plugin;
33
-
34
- export { type BiniEnvPluginOptions, type HonoContext, biniEnv, biniLogger, getEnv, requireEnv };