@zh-moody/safe-env 0.3.3 → 0.3.4

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.en.md ADDED
@@ -0,0 +1,148 @@
1
+ # @zh-moody/safe-env 🛡️
2
+
3
+ [![npm version](https://img.shields.io/npm/v/@zh-moody/safe-env.svg?style=flat-square)](https://www.npmjs.com/package/@zh-moody/safe-env)
4
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg?style=flat-square)](https://opensource.org/licenses/MIT)
5
+ [![GitHub stars](https://img.shields.io/github/stars/zhMoody/safe-env?style=flat-square)](https://github.com/zhMoody/safe-env)
6
+
7
+ [简体中文](./README.md) | English
8
+
9
+ **Say goodbye to `undefined`! Intercept all configuration risks at the first line of your app.**
10
+
11
+ Whether you are writing Vue, React, or Node.js, environment variables are often a source of production incidents. `safe-env` ensures your app always runs on the expected configuration through strong-typed Schema validation and runtime protection.
12
+
13
+ ---
14
+
15
+ ### 🚀 Core Features
16
+
17
+ - **Build-time Validation**: Provides a Vite plugin to intercept invalid configurations during development or build.
18
+ - **Sensitive Data Masking**: Supports `.secret()` to ensure keys and passwords are masked as `********` in logs or error tables.
19
+ - **Runtime Immutability**: Parsed config objects are deep-frozen with `Object.freeze` by default, preventing any illegal runtime modification.
20
+ - **Monorepo Ready**: Supports `cwd` parameter to explicitly specify the `.env` directory, ideal for complex project architectures.
21
+ - **IDE Enhancement**: Supports `.description()` to view variable usage and documentation directly via hover in your code.
22
+ - **Rigorous Type Parsing**: Built-in `s.array()`, `s.boolean()` with enhanced conversion, and `.transform()` for chainable piping.
23
+ - **Ultra-lightweight**: Only **1.9 KB** after Gzip, with zero runtime dependencies.
24
+
25
+ ---
26
+
27
+ ### 📦 Installation
28
+
29
+ ```bash
30
+ npm install @zh-moody/safe-env
31
+ ```
32
+
33
+ ---
34
+
35
+ ### 🚀 Quick Start
36
+
37
+ #### 🔹 [Vite / React / Vue]
38
+ For frontend projects, use the Vite plugin for **build-time validation**.
39
+
40
+ **1. Configure Vite Plugin (`vite.config.ts`):**
41
+ ```typescript
42
+ import { viteSafeEnv } from '@zh-moody/safe-env/vite';
43
+ import { schema } from './src/env';
44
+
45
+ export default {
46
+ plugins: [
47
+ viteSafeEnv(schema)
48
+ ]
49
+ }
50
+ ```
51
+
52
+ **2. Define and Export Config (`src/env.ts`):**
53
+ ```typescript
54
+ import { safeEnv, s } from '@zh-moody/safe-env';
55
+
56
+ export const schema = {
57
+ VITE_API_URL: s.string().url().description("Backend API Base URL"),
58
+ VITE_PORT: s.number(3000).description("Development Server Port"),
59
+ };
60
+
61
+ export const env = safeEnv(schema, {
62
+ source: import.meta.env
63
+ });
64
+ ```
65
+
66
+ > **💡 Best Practice: Prevent Vite Type Pollution**
67
+ > To completely disable the insecure original `import.meta.env.XXX` hints, modify `src/vite-env.d.ts`:
68
+ > ```typescript
69
+ > interface ImportMetaEnv {
70
+ > [key: string]: never;
71
+ > }
72
+ > ```
73
+
74
+ ---
75
+
76
+ #### 🔸 [Node.js / Server-side]
77
+ In Node.js, the library automatically looks for and parses `.env` files on disk.
78
+
79
+ **1. Define Config (`src/config.ts`):**
80
+ ```typescript
81
+ import { safeEnv, s } from '@zh-moody/safe-env';
82
+
83
+ const config = safeEnv({
84
+ DB_PASSWORD: s.string().secret().description("Database Password"),
85
+ DB_PORT: s.number(5432).min(1).max(65535),
86
+ }, {
87
+ // Explicitly specify the .env directory for Monorepo or specific deployments
88
+ // cwd: '/path/to/project-root'
89
+ });
90
+
91
+ export default config;
92
+ ```
93
+
94
+ ---
95
+
96
+ ### 🛠️ API Reference
97
+
98
+ #### 1. Define Fields (`s.xxx`)
99
+ - `s.string(default?)`: String field. Required if no default value.
100
+ - `s.number(default?)`: Automatically converted to `number` and validated.
101
+ - `s.boolean(default?)`: Supports `"true"`, `"1"`, `"yes"`, `"on"` as `true`.
102
+ - `s.array(default?, separator?)`: Splits string by separator (default `,`) into an array.
103
+ - `s.enum(options, default?)`: Value must be one of the provided options.
104
+
105
+ #### 2. Validation & Enhancement (Chainable)
106
+
107
+ - **`.secret()`**: Masks the value as `********` in error reports.
108
+ ```typescript
109
+ PASSWORD: s.string().secret()
110
+ ```
111
+ - **`.url()` / `.email()`**: Built-in format validation.
112
+ ```typescript
113
+ API_URL: s.string().url()
114
+ ```
115
+ - **`.regex(pattern, msg?)`**: Custom regex validation.
116
+ ```typescript
117
+ VERSION: s.string().regex(/^v\d+\.\d+\.\d+$/, "Invalid format")
118
+ ```
119
+ - **`.description(text)`**: Adds a description for IDE hover hints.
120
+ ```typescript
121
+ PORT: s.number(3000).description("Server Port")
122
+ ```
123
+ - **`.transform(fn)`**: Custom data transformation, supports multi-level piping.
124
+ ```typescript
125
+ NAME: s.string().transform(v => v.trim()).transform(v => v.toUpperCase())
126
+ ```
127
+ - **`.from(key)`**: Maps environment variable name (Alias).
128
+ ```typescript
129
+ port: s.number().from('VITE_SERVER_PORT')
130
+ ```
131
+ - **`.min(n)` / `.max(n)`**: Constraints for number values.
132
+ ```typescript
133
+ PORT: s.number().min(1024).max(65535)
134
+ ```
135
+ - **`.validate(fn, msg?)`**: Fully custom validation logic.
136
+ ```typescript
137
+ INTERNAL_URL: s.string().validate(v => v.endsWith('.internal.com'), 'Must be internal')
138
+ ```
139
+
140
+ ---
141
+
142
+ ### 🎨 Error Reporting
143
+ When validation fails, `safe-env` outputs a structured, adaptive table in the console showing: **Key / Error Reason / Current Value (Masked)**.
144
+
145
+ ---
146
+
147
+ ### 📄 License
148
+ [MIT License](./LICENSE) - Copyright (c) 2026 Moody.
package/README.md CHANGED
@@ -4,19 +4,22 @@
4
4
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg?style=flat-square)](https://opensource.org/licenses/MIT)
5
5
  [![GitHub stars](https://img.shields.io/github/stars/zhMoody/safe-env?style=flat-square)](https://github.com/zhMoody/safe-env)
6
6
 
7
- **告别 `undefined`!在应用启动的第一行,拦截所有错误配置。**
7
+ 简体中文 | [English](./README.en.md)
8
8
 
9
- 无论你在写 Vue、React 还是 Node.js,环境变量配置永远是 Bug 的温床。`safe-env` 通过强类型 Schema 校验,确保你的应用在拥有正确配置的前提下才启动。
9
+ **告别 `undefined`!在应用启动的第一行,拦截所有配置隐患。**
10
+
11
+ 无论你在写 Vue、React 还是 Node.js,环境变量配置永远是生产事故的高发区。`safe-env` 通过强类型 Schema 校验与运行时保护,确保你的应用始终运行在预期的配置之上。
10
12
 
11
13
  ---
12
14
 
13
15
  ### 🚀 核心特性
14
16
 
15
- - **构建时预校验**:提供 Vite 插件,在 `npm run dev` 或 `build` 的瞬间拦截错误,无需打开浏览器。
16
- - **IDE 增强**:支持 `.description()` 字段,直接在代码中通过悬停查看变量用途。
17
- - **更强的数据转换**:内置 `s.array()`, `s.boolean()` 增强转换,支持 `.transform()` 链式 Pipe 处理。
18
- - **内置常用规则**:提供 `.url()`, `.email()`, `.regex()` 等高频校验,无需依赖 zod。
19
- - **类型安全**:自动推断配置对象类型,拥有完美的 IDE 补全。
17
+ - **构建时预校验**:提供 Vite 插件,在开发启动或打包瞬间拦截非法配置。
18
+ - **敏感数据脱敏**:支持 `.secret()` 标记,确保密钥等敏感信息不会泄露在日志或报错表格中。
19
+ - **运行时深度冻结**:解析后的配置对象默认开启 `Object.freeze`,杜绝任何运行时的非法篡改。
20
+ - **Monorepo 精准定位**:支持 `cwd` 参数,可显式指定配置文件检索目录,适配复杂的项目架构。
21
+ - **IDE 增强**:支持 `.description()`,在代码中通过悬停直接查看变量用途与文档。
22
+ - **严谨的类型解析**:内置 `s.array()`, `s.boolean()` 增强转换,支持 `.transform()` 链式 Pipe 处理。
20
23
  - **极致轻量**:Gzip 压缩后仅 **1.9 KB**,零运行时依赖。
21
24
 
22
25
  ---
@@ -25,21 +28,6 @@
25
28
 
26
29
  ```bash
27
30
  npm install @zh-moody/safe-env
28
- # 或者
29
- pnpm add @zh-moody/safe-env
30
- ```
31
-
32
- ---
33
-
34
- ### 🛠️ 准备工作 (Prerequisites)
35
-
36
- 在项目根目录下创建一个 `.env` 文件(这是本库解析数据的来源):
37
-
38
- ```bash
39
- # .env 示例
40
- VITE_API_URL=https://api.com
41
- VITE_PORT=3000
42
- VITE_FEATURES=auth,storage
43
31
  ```
44
32
 
45
33
  ---
@@ -52,11 +40,11 @@ VITE_FEATURES=auth,storage
52
40
  **1. 配置 Vite 插件 (`vite.config.ts`):**
53
41
  ```typescript
54
42
  import { viteSafeEnv } from '@zh-moody/safe-env/vite';
55
- import { schema } from './src/env'; // 建议将 schema 提取到独立文件
43
+ import { schema } from './src/env';
56
44
 
57
45
  export default {
58
46
  plugins: [
59
- viteSafeEnv(schema) // 构建时校验,配置错误直接停止构建
47
+ viteSafeEnv(schema)
60
48
  ]
61
49
  }
62
50
  ```
@@ -68,7 +56,6 @@ import { safeEnv, s } from '@zh-moody/safe-env';
68
56
  export const schema = {
69
57
  VITE_API_URL: s.string().url().description("后端 API 地址"),
70
58
  VITE_PORT: s.number(3000).description("服务端口"),
71
- VITE_FEATURES: s.array().description("启用的特性列表")
72
59
  };
73
60
 
74
61
  export const env = safeEnv(schema, {
@@ -76,19 +63,29 @@ export const env = safeEnv(schema, {
76
63
  });
77
64
  ```
78
65
 
66
+ > **💡 最佳实践:防止 Vite 类型污染**
67
+ > 为彻底禁用 `import.meta.env.XXX` 的原生提示,建议修改 `src/vite-env.d.ts`:
68
+ > ```typescript
69
+ > interface ImportMetaEnv {
70
+ > [key: string]: never;
71
+ > }
72
+ > ```
73
+
79
74
  ---
80
75
 
81
76
  #### 🔸 [Node.js / 服务端] 使用
82
- 在后端,库会自动寻找并解析磁盘上的 `.env` 文件。
77
+ 在后端环境,库会自动检索并解析磁盘上的 `.env` 系列文件。
83
78
 
84
79
  **1. 定义配置 (`src/config.ts`):**
85
80
  ```typescript
86
81
  import { safeEnv, s } from '@zh-moody/safe-env';
87
82
 
88
83
  const config = safeEnv({
89
- DB_HOST: s.string('localhost').description("数据库主机"),
84
+ DB_PASSWORD: s.string().secret().description("数据库密码"),
90
85
  DB_PORT: s.number(5432).min(1).max(65535),
91
- ADMIN_EMAIL: s.string().email()
86
+ }, {
87
+ // 在 Monorepo 或特定部署环境下,可显式指定 .env 所在目录
88
+ // cwd: '/path/to/project-root'
92
89
  });
93
90
 
94
91
  export default config;
@@ -100,66 +97,52 @@ export default config;
100
97
 
101
98
  #### 1. 定义字段 (`s.xxx`)
102
99
  - `s.string(default?)`: 字符串。若无默认值则必填。
103
- - `s.number(default?)`: 数字。自动将字符串转为 `number`。
104
- - `s.boolean(default?)`: 布尔型。支持将 `"true"`, `"1"`, `"yes"`, `"on"` 解析为 `true`(大小写无关)。
100
+ - `s.number(default?)`: 数字。自动转换为 `number` 类型并校验合法性。
101
+ - `s.boolean(default?)`: 布尔型。支持将 `"true"`, `"1"`, `"yes"`, `"on"` 解析为 `true`。
105
102
  - `s.array(default?, separator?)`: 数组型。支持将字符串按分隔符(默认 `,`)拆分为数组。
106
- - `s.enum(options, default?)`: 枚举。值必须在数组中。
103
+ - `s.enum(options, default?)`: 枚举。值必须在预设数组中。
107
104
 
108
105
  #### 2. 校验与增强 (链式调用)
109
106
 
110
107
  每个字段都可以通过链式调用进行深度定制:
111
108
 
112
- - **`.url()`**: 校验是否为合法 URL 格式。
109
+ - **`.secret()`**: 标记敏感数据,报错时该值会以 `********` 遮罩显示。
113
110
  ```typescript
114
- API_URL: s.string().url()
111
+ PASSWORD: s.string().secret()
115
112
  ```
116
- - **`.email()`**: 校验是否为合法邮箱格式。
113
+ - **`.url()` / `.email()`**: 常用格式校验。
117
114
  ```typescript
118
- CONTACT: s.string().email()
115
+ API_URL: s.string().url()
119
116
  ```
120
117
  - **`.regex(pattern, msg?)`**: 自定义正则校验。
121
118
  ```typescript
122
- VERSION: s.string().regex(/^v\d+\.\d+\.\d+$/, "版本号必须以 v 开头")
119
+ VERSION: s.string().regex(/^v\d+\.\d+\.\d+$/, "格式错误")
123
120
  ```
124
- - **`.description(text)`**: 添加变量描述。该描述会映射到 IDE 的悬停提示中。
121
+ - **`.description(text)`**: 添加变量描述,映射到 IDE 悬停提示中。
125
122
  ```typescript
126
- PORT: s.number(3000).description("本地服务器端口")
123
+ PORT: s.number(3000).description("服务端口")
127
124
  ```
128
- - **`.transform(fn)`**: 自定义数据转换,支持多级链式 Pipe。
125
+ - **`.transform(fn)`**: 自定义数据转换,支持多级 Pipe。
129
126
  ```typescript
130
- // 示例 1: 拿到字符串 -> 去空格 -> 转大写
131
127
  NAME: s.string().transform(v => v.trim()).transform(v => v.toUpperCase())
132
-
133
- // 示例 2: 将逗号分隔的数字字符串转为数字数组
134
- SCORES: s.array().transform(arr => arr.map(Number))
135
128
  ```
136
129
  - **`.from(key)`**: 映射环境变量名(别名)。
137
130
  ```typescript
138
- // 即使环境变量叫 VITE_SERVER_PORT,代码里也可以叫 port
139
131
  port: s.number().from('VITE_SERVER_PORT')
140
132
  ```
141
133
  - **`.min(n)` / `.max(n)`**: 限制数字取值范围。
142
134
  ```typescript
143
- // 端口必须在 1024-65535 之间
144
135
  PORT: s.number().min(1024).max(65535)
145
136
  ```
146
- - **`.validate(fn, msg?)`**: 完全自定义的校验逻辑。
137
+ - **`.validate(fn, msg?)`**: 完全自定义的逻辑校验。
147
138
  ```typescript
148
- // 必须是特定的内部域名
149
- INTERNAL_URL: s.string().validate(v => v.endsWith('.internal.com'), 'Must be an internal URL')
139
+ INTERNAL_URL: s.string().validate(v => v.endsWith('.internal.com'), 'Must be internal')
150
140
  ```
151
141
 
152
- #### 3. 加载规则 (Env Priority)
153
- `safe-env` 遵循标准的优先级顺序(从低到高):
154
- 1. `.env` (基础配置)
155
- 2. `.env.[mode]` (环境配置,如 `.env.development`)
156
- 3. `.env.local` (本地覆盖)
157
- 4. `.env.[mode].local` (环境特定的本地覆盖)
158
-
159
142
  ---
160
143
 
161
144
  ### 🎨 错误报告
162
- 当校验失败时,`safe-env` 会在控制台打印精美的表格,清晰展示:**Key / 错误原因 / 当前值**。
145
+ 当校验失败时,`safe-env` 会在控制台输出结构化的自适应表格,清晰展示:**Key / 错误原因 / 当前值(已脱敏)**。
163
146
 
164
147
  ---
165
148
 
@@ -1 +1 @@
1
- "use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }var _fs = require('fs'); var _fs2 = _interopRequireDefault(_fs);var _path = require('path'); var _path2 = _interopRequireDefault(_path);function s(i){let r={},c=i.split(/\r?\n/);for(let f of c){let n=f.trim();if(!n||n.startsWith("#"))continue;let e=n.indexOf("=");if(e==-1)continue;let l=n.slice(0,e).trim(),t=n.slice(e+1).trim();(t.startsWith('"')&&t.endsWith('"')||t.startsWith("'")&&t.endsWith("'"))&&(t=t.slice(1,-1)),r[l]=t}return r}function h(i=".env"){try{let r=_path2.default.resolve(process.cwd(),i);if(_fs2.default.existsSync(r))return s(_fs2.default.readFileSync(r,"utf-8"))}catch (e2){}return{}}exports.a = s; exports.b = h;
1
+ "use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }var _fs = require('fs'); var _fs2 = _interopRequireDefault(_fs);var _path = require('path'); var _path2 = _interopRequireDefault(_path);function o(s){let i={},r=s.split(/\r?\n/);for(let f of r){let n=f.trim();if(!n||n.startsWith("#"))continue;let e=n.indexOf("=");if(e==-1)continue;let l=n.slice(0,e).trim(),t=n.slice(e+1).trim();(t.startsWith('"')&&t.endsWith('"')||t.startsWith("'")&&t.endsWith("'"))&&(t=t.slice(1,-1)),i[l]=t}return i}function g(s=".env",i){try{let r=_path2.default.resolve(i||process.cwd(),s);if(_fs2.default.existsSync(r))return o(_fs2.default.readFileSync(r,"utf-8"))}catch (e2){}return{}}exports.a = o; exports.b = g;
@@ -0,0 +1,3 @@
1
+ import{b as y}from"./chunk-WZTPUQ3S.js";function $(m){let e={r:"\x1B[31m",g:"\x1B[32m",y:"\x1B[33m",b:"\x1B[1m",res:"\x1B[0m",d:"\x1B[2m",cy:"\x1B[36m"},d=Math.max(80,process.stdout.columns||80)-10,f=Math.floor(d*.3),u=Math.floor(d*.5),l=(r,a)=>{let t=String(r),o=()=>t.length+(t.match(/[^\x00-\xff]/g)||[]).length;for(;o()>a;)t=t.slice(0,-1);return t+" ".repeat(a-o())};console.error(`
2
+ ${e.r}${e.b}\u274C SafeEnv Validation Failed${e.res}`),console.error(` ${e.b}${l("Key",f)} \u2502 ${l("Error",u)} \u2502 Value${e.res}`),console.error(e.d+"\u2500".repeat(d+5)+e.res),m.forEach(r=>{let a=r.isSecret?"********":r.value===void 0?"undefined":`"${r.value}"`,t=r.isSecret?e.y:r.value===void 0?e.d:e.cy;console.error(` ${e.y}${l(r.key,f)}${e.res} \u2502 ${e.r}${l(r.error,u)}${e.res} \u2502 ${t}${a}${e.res}`)}),console.error(e.d+"\u2500".repeat(d+5)+e.res),console.error(` ${e.g}\u{1F4A1} Tip: Check your .env files or schema definitions.${e.res}
3
+ `)}function b(m,e={}){let{loadProcessEnv:d=!0,source:f,prefix:u="",cwd:l}=e,r;if(f!==void 0)r=f||{};else{let o=e.mode||(typeof process<"u"?process.env.NODE_ENV:"development"),s=[".env",`.env.${o}`,".env.local",`.env.${o}.local`],i={};for(let c of s)i={...i,...y(c,l)};r={...i,...d&&typeof process<"u"?process.env:{}}}let a={},t=[];for(let o in m){let s=m[o],i=s.sourceKey;if(!i){let n=u+o;r[n]!==void 0?i=n:r[o]!==void 0?i=o:i=u?n:o}let c=r[i];try{let n;if(c===void 0||c===""&&s.default!==void 0){if(s.required&&c===void 0)throw new Error("Required field missing");n=s.default}else n=s.parse(c);if(n!==void 0&&s.metadata){let{min:p,max:v,validate:E}=s.metadata;if(typeof n=="number"){if(p!==void 0&&n<p)throw new Error(`Below min ${p}`);if(v!==void 0&&n>v)throw new Error(`Above max ${v}`)}if(E&&!E.fn(n))throw new Error(E.message)}a[o]=n}catch(n){t.push({key:i,error:n.message,value:c,isSecret:s.metadata?.isSecret})}}if(t.length>0){$(t);let o=typeof process<"u"&&!!process.exit,s=typeof process<"u"&&process.env.NODE_ENV==="test";if(o&&!f&&!s)process.exit(1);else throw new Error("SafeEnv: Configuration validation failed.")}return Object.freeze(a)}export{$ as a,b};
@@ -0,0 +1,3 @@
1
+ "use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; }var _chunkMODJPD2Xcjs = require('./chunk-MODJPD2X.cjs');function $(m){let e={r:"\x1B[31m",g:"\x1B[32m",y:"\x1B[33m",b:"\x1B[1m",res:"\x1B[0m",d:"\x1B[2m",cy:"\x1B[36m"},d=Math.max(80,process.stdout.columns||80)-10,f=Math.floor(d*.3),u=Math.floor(d*.5),l=(r,a)=>{let t=String(r),o=()=>t.length+(t.match(/[^\x00-\xff]/g)||[]).length;for(;o()>a;)t=t.slice(0,-1);return t+" ".repeat(a-o())};console.error(`
2
+ ${e.r}${e.b}\u274C SafeEnv Validation Failed${e.res}`),console.error(` ${e.b}${l("Key",f)} \u2502 ${l("Error",u)} \u2502 Value${e.res}`),console.error(e.d+"\u2500".repeat(d+5)+e.res),m.forEach(r=>{let a=r.isSecret?"********":r.value===void 0?"undefined":`"${r.value}"`,t=r.isSecret?e.y:r.value===void 0?e.d:e.cy;console.error(` ${e.y}${l(r.key,f)}${e.res} \u2502 ${e.r}${l(r.error,u)}${e.res} \u2502 ${t}${a}${e.res}`)}),console.error(e.d+"\u2500".repeat(d+5)+e.res),console.error(` ${e.g}\u{1F4A1} Tip: Check your .env files or schema definitions.${e.res}
3
+ `)}function b(m,e={}){let{loadProcessEnv:d=!0,source:f,prefix:u="",cwd:l}=e,r;if(f!==void 0)r=f||{};else{let o=e.mode||(typeof process<"u"?process.env.NODE_ENV:"development"),s=[".env",`.env.${o}`,".env.local",`.env.${o}.local`],i={};for(let c of s)i={...i,..._chunkMODJPD2Xcjs.b.call(void 0, c,l)};r={...i,...d&&typeof process<"u"?process.env:{}}}let a={},t=[];for(let o in m){let s=m[o],i=s.sourceKey;if(!i){let n=u+o;r[n]!==void 0?i=n:r[o]!==void 0?i=o:i=u?n:o}let c=r[i];try{let n;if(c===void 0||c===""&&s.default!==void 0){if(s.required&&c===void 0)throw new Error("Required field missing");n=s.default}else n=s.parse(c);if(n!==void 0&&s.metadata){let{min:p,max:v,validate:E}=s.metadata;if(typeof n=="number"){if(p!==void 0&&n<p)throw new Error(`Below min ${p}`);if(v!==void 0&&n>v)throw new Error(`Above max ${v}`)}if(E&&!E.fn(n))throw new Error(E.message)}a[o]=n}catch(n){t.push({key:i,error:n.message,value:c,isSecret:_optionalChain([s, 'access', _ => _.metadata, 'optionalAccess', _2 => _2.isSecret])})}}if(t.length>0){$(t);let o=typeof process<"u"&&!!process.exit,s=typeof process<"u"&&process.env.NODE_ENV==="test";if(o&&!f&&!s)process.exit(1);else throw new Error("SafeEnv: Configuration validation failed.")}return Object.freeze(a)}exports.a = $; exports.b = b;
@@ -0,0 +1 @@
1
+ import c from"fs";import a from"path";function o(s){let i={},r=s.split(/\r?\n/);for(let f of r){let n=f.trim();if(!n||n.startsWith("#"))continue;let e=n.indexOf("=");if(e==-1)continue;let l=n.slice(0,e).trim(),t=n.slice(e+1).trim();(t.startsWith('"')&&t.endsWith('"')||t.startsWith("'")&&t.endsWith("'"))&&(t=t.slice(1,-1)),i[l]=t}return i}function g(s=".env",i){try{let r=a.resolve(i||process.cwd(),s);if(c.existsSync(r))return o(c.readFileSync(r,"utf-8"))}catch{}return{}}export{o as a,g as b};
@@ -1 +1 @@
1
- "use strict";Object.defineProperty(exports, "__esModule", {value: true});function t(n=".env"){return{}}exports.loadDotEnv = t;
1
+ "use strict";Object.defineProperty(exports, "__esModule", {value: true});function r(n=".env",t){return{}}exports.loadDotEnv = r;
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * 浏览器端不需要读取文件,所以返回空对象
3
3
  */
4
- declare function loadDotEnv(_filePath?: string): Record<string, string>;
4
+ declare function loadDotEnv(_filePath?: string, _cwd?: string): Record<string, string>;
5
5
 
6
6
  export { loadDotEnv };
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * 浏览器端不需要读取文件,所以返回空对象
3
3
  */
4
- declare function loadDotEnv(_filePath?: string): Record<string, string>;
4
+ declare function loadDotEnv(_filePath?: string, _cwd?: string): Record<string, string>;
5
5
 
6
6
  export { loadDotEnv };
@@ -1 +1 @@
1
- function t(n=".env"){return{}}export{t as loadDotEnv};
1
+ function r(n=".env",t){return{}}export{r as loadDotEnv};
package/dist/fs-node.cjs CHANGED
@@ -1 +1 @@
1
- "use strict";Object.defineProperty(exports, "__esModule", {value: true});var _chunkFH2DFX4Jcjs = require('./chunk-FH2DFX4J.cjs');exports.loadDotEnv = _chunkFH2DFX4Jcjs.b;
1
+ "use strict";Object.defineProperty(exports, "__esModule", {value: true});var _chunkMODJPD2Xcjs = require('./chunk-MODJPD2X.cjs');exports.loadDotEnv = _chunkMODJPD2Xcjs.b;
@@ -1,3 +1,3 @@
1
- declare function loadDotEnv(filePath?: string): Record<string, string>;
1
+ declare function loadDotEnv(filePath?: string, cwd?: string): Record<string, string>;
2
2
 
3
3
  export { loadDotEnv };
package/dist/fs-node.d.ts CHANGED
@@ -1,3 +1,3 @@
1
- declare function loadDotEnv(filePath?: string): Record<string, string>;
1
+ declare function loadDotEnv(filePath?: string, cwd?: string): Record<string, string>;
2
2
 
3
3
  export { loadDotEnv };
package/dist/fs-node.js CHANGED
@@ -1 +1 @@
1
- import{b as a}from"./chunk-POVTEBYN.js";export{a as loadDotEnv};
1
+ import{b as a}from"./chunk-WZTPUQ3S.js";export{a as loadDotEnv};
package/dist/index.cjs CHANGED
@@ -1 +1 @@
1
- "use strict";Object.defineProperty(exports, "__esModule", {value: true});var _chunkGCSZ35F2cjs = require('./chunk-GCSZ35F2.cjs');var _chunkFH2DFX4Jcjs = require('./chunk-FH2DFX4J.cjs');function a(n,i,e,o=[]){return{type:n,default:i,required:i===void 0,parse:e,metadata:o.length?{options:o}:{},from(t){return this.sourceKey=t,this},validate(t,r="Custom validation failed"){return this.metadata={...this.metadata,validate:{fn:t,message:r}},this},min(t){return this.metadata={...this.metadata,min:t},this},max(t){return this.metadata={...this.metadata,max:t},this},transform(t){let r=this.parse;return this.parse=s=>t(r(s)),this},url(){return this.validate(t=>{try{return new URL(String(t)),!0}catch (e2){return!1}},"Invalid URL format")},email(){let t=/^[\w-\.]+@([\w-]+\.)+[\w-]{2,4}$/;return this.validate(r=>t.test(String(r)),"Invalid email format")},regex(t,r="Value does not match pattern"){return this.validate(s=>t.test(String(s)),r)},description(t){return this.metadata={...this.metadata,description:t},this}}}var f={string:n=>a("string",n,i=>String(i)),number:n=>a("number",n,i=>{let e=Number(i);if(isNaN(e))throw new Error(`Invalid number: ${i}`);return e}),boolean:n=>a("boolean",n,i=>{if(typeof i=="boolean")return i;let e=String(i).toLowerCase();return e==="true"||e==="1"||e==="yes"||e==="on"}),enum:(n,i)=>a("enum",i,e=>{if(!n.includes(e))throw new Error(`Value "${e}" is not one of: ${n.join(", ")}`);return e},n),array:(n,i=",")=>a("array",n,e=>Array.isArray(e)?e:typeof e!="string"?[]:e.split(i).map(o=>o.trim()).filter(Boolean))};exports.parseDotEnv = _chunkFH2DFX4Jcjs.a; exports.reportErrors = _chunkGCSZ35F2cjs.a; exports.s = f; exports.safeEnv = _chunkGCSZ35F2cjs.b;
1
+ "use strict";Object.defineProperty(exports, "__esModule", {value: true});var _chunkWXZ32K3Gcjs = require('./chunk-WXZ32K3G.cjs');var _chunkMODJPD2Xcjs = require('./chunk-MODJPD2X.cjs');function a(n,t,e,o=[]){return{type:n,default:t,required:t===void 0,parse:e,metadata:o.length?{options:o}:{},from(i){return this.sourceKey=i,this},validate(i,r="Custom validation failed"){return this.metadata={...this.metadata,validate:{fn:i,message:r}},this},min(i){return this.metadata={...this.metadata,min:i},this},max(i){return this.metadata={...this.metadata,max:i},this},transform(i){let r=this.parse;return this.parse=s=>i(r(s)),this},secret(){return this.metadata={...this.metadata,isSecret:!0},this},url(){return this.validate(i=>{try{return new URL(String(i)),!0}catch (e2){return!1}},"Invalid URL format")},email(){let i=/^[\w-\.]+@([\w-]+\.)+[\w-]{2,4}$/;return this.validate(r=>i.test(String(r)),"Invalid email format")},regex(i,r="Value does not match pattern"){return this.validate(s=>i.test(String(s)),r)},description(i){return this.metadata={...this.metadata,description:i},this}}}var m={string:n=>a("string",n,t=>String(t)),number:n=>a("number",n,t=>{let e=Number(t);if(isNaN(e))throw new Error(`Invalid number: ${t}`);return e}),boolean:n=>a("boolean",n,t=>{if(typeof t=="boolean")return t;if(t===void 0||t==="")return!1;let e=String(t).toLowerCase().trim();if(e==="true"||e==="1"||e==="yes"||e==="on")return!0;if(e==="false"||e==="0"||e==="no"||e==="off")return!1;throw new Error(`Invalid boolean: ${t}`)}),enum:(n,t)=>a("enum",t,e=>{if(!n.includes(e))throw new Error(`Value "${e}" is not one of: ${n.join(", ")}`);return e},n),array:(n,t=",")=>a("array",n,e=>Array.isArray(e)?e:typeof e!="string"?[]:e.split(t).map(o=>o.trim()).filter(Boolean))};exports.parseDotEnv = _chunkMODJPD2Xcjs.a; exports.reportErrors = _chunkWXZ32K3Gcjs.a; exports.s = m; exports.safeEnv = _chunkWXZ32K3Gcjs.b;
package/dist/index.d.cts CHANGED
@@ -1,5 +1,5 @@
1
- import { F as FieldDefinition, S as Schema, I as InferSchema, E as EnvError } from './types-B59cqDDx.cjs';
2
- export { B as BaseType } from './types-B59cqDDx.cjs';
1
+ import { F as FieldDefinition, S as Schema, I as InferSchema, E as EnvError } from './types-CFipqnoe.cjs';
2
+ export { B as BaseType } from './types-CFipqnoe.cjs';
3
3
 
4
4
  declare const s: {
5
5
  string: (defaultValue?: string) => FieldDefinition<string>;
@@ -14,8 +14,9 @@ interface SafeEnvOptions {
14
14
  loadProcessEnv?: boolean;
15
15
  source?: Record<string, any>;
16
16
  prefix?: string;
17
+ cwd?: string;
17
18
  }
18
- declare function safeEnv<T extends Schema>(schema: T, options?: SafeEnvOptions): InferSchema<T>;
19
+ declare function safeEnv<T extends Schema>(schema: T, options?: SafeEnvOptions): Readonly<InferSchema<T>>;
19
20
 
20
21
  /***
21
22
  * 将 .env 内容字符串解析为对象
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { F as FieldDefinition, S as Schema, I as InferSchema, E as EnvError } from './types-B59cqDDx.js';
2
- export { B as BaseType } from './types-B59cqDDx.js';
1
+ import { F as FieldDefinition, S as Schema, I as InferSchema, E as EnvError } from './types-CFipqnoe.js';
2
+ export { B as BaseType } from './types-CFipqnoe.js';
3
3
 
4
4
  declare const s: {
5
5
  string: (defaultValue?: string) => FieldDefinition<string>;
@@ -14,8 +14,9 @@ interface SafeEnvOptions {
14
14
  loadProcessEnv?: boolean;
15
15
  source?: Record<string, any>;
16
16
  prefix?: string;
17
+ cwd?: string;
17
18
  }
18
- declare function safeEnv<T extends Schema>(schema: T, options?: SafeEnvOptions): InferSchema<T>;
19
+ declare function safeEnv<T extends Schema>(schema: T, options?: SafeEnvOptions): Readonly<InferSchema<T>>;
19
20
 
20
21
  /***
21
22
  * 将 .env 内容字符串解析为对象
package/dist/index.js CHANGED
@@ -1 +1 @@
1
- import{a as u,b as m}from"./chunk-NRXIL5NF.js";import{a as l}from"./chunk-POVTEBYN.js";function a(n,i,e,o=[]){return{type:n,default:i,required:i===void 0,parse:e,metadata:o.length?{options:o}:{},from(t){return this.sourceKey=t,this},validate(t,r="Custom validation failed"){return this.metadata={...this.metadata,validate:{fn:t,message:r}},this},min(t){return this.metadata={...this.metadata,min:t},this},max(t){return this.metadata={...this.metadata,max:t},this},transform(t){let r=this.parse;return this.parse=s=>t(r(s)),this},url(){return this.validate(t=>{try{return new URL(String(t)),!0}catch{return!1}},"Invalid URL format")},email(){let t=/^[\w-\.]+@([\w-]+\.)+[\w-]{2,4}$/;return this.validate(r=>t.test(String(r)),"Invalid email format")},regex(t,r="Value does not match pattern"){return this.validate(s=>t.test(String(s)),r)},description(t){return this.metadata={...this.metadata,description:t},this}}}var f={string:n=>a("string",n,i=>String(i)),number:n=>a("number",n,i=>{let e=Number(i);if(isNaN(e))throw new Error(`Invalid number: ${i}`);return e}),boolean:n=>a("boolean",n,i=>{if(typeof i=="boolean")return i;let e=String(i).toLowerCase();return e==="true"||e==="1"||e==="yes"||e==="on"}),enum:(n,i)=>a("enum",i,e=>{if(!n.includes(e))throw new Error(`Value "${e}" is not one of: ${n.join(", ")}`);return e},n),array:(n,i=",")=>a("array",n,e=>Array.isArray(e)?e:typeof e!="string"?[]:e.split(i).map(o=>o.trim()).filter(Boolean))};export{l as parseDotEnv,u as reportErrors,f as s,m as safeEnv};
1
+ import{a as l,b as u}from"./chunk-UONNQTFU.js";import{a as f}from"./chunk-WZTPUQ3S.js";function a(n,t,e,o=[]){return{type:n,default:t,required:t===void 0,parse:e,metadata:o.length?{options:o}:{},from(i){return this.sourceKey=i,this},validate(i,r="Custom validation failed"){return this.metadata={...this.metadata,validate:{fn:i,message:r}},this},min(i){return this.metadata={...this.metadata,min:i},this},max(i){return this.metadata={...this.metadata,max:i},this},transform(i){let r=this.parse;return this.parse=s=>i(r(s)),this},secret(){return this.metadata={...this.metadata,isSecret:!0},this},url(){return this.validate(i=>{try{return new URL(String(i)),!0}catch{return!1}},"Invalid URL format")},email(){let i=/^[\w-\.]+@([\w-]+\.)+[\w-]{2,4}$/;return this.validate(r=>i.test(String(r)),"Invalid email format")},regex(i,r="Value does not match pattern"){return this.validate(s=>i.test(String(s)),r)},description(i){return this.metadata={...this.metadata,description:i},this}}}var m={string:n=>a("string",n,t=>String(t)),number:n=>a("number",n,t=>{let e=Number(t);if(isNaN(e))throw new Error(`Invalid number: ${t}`);return e}),boolean:n=>a("boolean",n,t=>{if(typeof t=="boolean")return t;if(t===void 0||t==="")return!1;let e=String(t).toLowerCase().trim();if(e==="true"||e==="1"||e==="yes"||e==="on")return!0;if(e==="false"||e==="0"||e==="no"||e==="off")return!1;throw new Error(`Invalid boolean: ${t}`)}),enum:(n,t)=>a("enum",t,e=>{if(!n.includes(e))throw new Error(`Value "${e}" is not one of: ${n.join(", ")}`);return e},n),array:(n,t=",")=>a("array",n,e=>Array.isArray(e)?e:typeof e!="string"?[]:e.split(t).map(o=>o.trim()).filter(Boolean))};export{f as parseDotEnv,l as reportErrors,m as s,u as safeEnv};
@@ -9,6 +9,7 @@ interface FieldDefinition<T = any, D extends string = string> {
9
9
  max?: number;
10
10
  options?: T[];
11
11
  description?: string;
12
+ isSecret?: boolean;
12
13
  validate?: {
13
14
  fn: (val: T) => boolean;
14
15
  message: string;
@@ -20,6 +21,7 @@ interface FieldDefinition<T = any, D extends string = string> {
20
21
  min: (val: number) => FieldDefinition<T, D>;
21
22
  max: (val: number) => FieldDefinition<T, D>;
22
23
  transform: <U>(fn: (val: T) => U) => FieldDefinition<U, D>;
24
+ secret: () => FieldDefinition<T, D>;
23
25
  url: () => FieldDefinition<T, D>;
24
26
  email: () => FieldDefinition<T, D>;
25
27
  regex: (pattern: RegExp, message?: string) => FieldDefinition<T, D>;
@@ -30,6 +32,7 @@ interface EnvError {
30
32
  key: string;
31
33
  error: string;
32
34
  value: any;
35
+ isSecret?: boolean;
33
36
  }
34
37
  type InferSchema<T> = {
35
38
  [K in keyof T]: T[K] extends FieldDefinition<infer U, infer D> ? string extends D ? U : U & {
@@ -9,6 +9,7 @@ interface FieldDefinition<T = any, D extends string = string> {
9
9
  max?: number;
10
10
  options?: T[];
11
11
  description?: string;
12
+ isSecret?: boolean;
12
13
  validate?: {
13
14
  fn: (val: T) => boolean;
14
15
  message: string;
@@ -20,6 +21,7 @@ interface FieldDefinition<T = any, D extends string = string> {
20
21
  min: (val: number) => FieldDefinition<T, D>;
21
22
  max: (val: number) => FieldDefinition<T, D>;
22
23
  transform: <U>(fn: (val: T) => U) => FieldDefinition<U, D>;
24
+ secret: () => FieldDefinition<T, D>;
23
25
  url: () => FieldDefinition<T, D>;
24
26
  email: () => FieldDefinition<T, D>;
25
27
  regex: (pattern: RegExp, message?: string) => FieldDefinition<T, D>;
@@ -30,6 +32,7 @@ interface EnvError {
30
32
  key: string;
31
33
  error: string;
32
34
  value: any;
35
+ isSecret?: boolean;
33
36
  }
34
37
  type InferSchema<T> = {
35
38
  [K in keyof T]: T[K] extends FieldDefinition<infer U, infer D> ? string extends D ? U : U & {
package/dist/vite.cjs CHANGED
@@ -1 +1 @@
1
- "use strict";Object.defineProperty(exports, "__esModule", {value: true});var _chunkGCSZ35F2cjs = require('./chunk-GCSZ35F2.cjs');require('./chunk-FH2DFX4J.cjs');var _vite = require('vite');function m(t,i={}){return{name:"vite-plugin-safe-env",configResolved(r){let{envDir:o=r.root,prefix:e=r.envPrefix||"VITE_"}=i,s=r.mode,a=_vite.loadEnv.call(void 0, s,o,e);try{_chunkGCSZ35F2cjs.b.call(void 0, t,{source:a,prefix:Array.isArray(e)?e[0]:e,loadProcessEnv:!1})}catch (e2){process.exit(1)}}}}exports.viteSafeEnv = m;
1
+ "use strict";Object.defineProperty(exports, "__esModule", {value: true});var _chunkWXZ32K3Gcjs = require('./chunk-WXZ32K3G.cjs');require('./chunk-MODJPD2X.cjs');var _vite = require('vite');function m(t,i={}){return{name:"vite-plugin-safe-env",configResolved(r){let{envDir:o=r.root,prefix:e=r.envPrefix||"VITE_"}=i,s=r.mode,a=_vite.loadEnv.call(void 0, s,o,e);try{_chunkWXZ32K3Gcjs.b.call(void 0, t,{source:a,prefix:Array.isArray(e)?e[0]:e,loadProcessEnv:!1})}catch (e2){process.exit(1)}}}}exports.viteSafeEnv = m;
package/dist/vite.d.cts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { Plugin } from 'vite';
2
- import { S as Schema } from './types-B59cqDDx.cjs';
2
+ import { S as Schema } from './types-CFipqnoe.cjs';
3
3
 
4
4
  interface ViteSafeEnvOptions {
5
5
  /**
package/dist/vite.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { Plugin } from 'vite';
2
- import { S as Schema } from './types-B59cqDDx.js';
2
+ import { S as Schema } from './types-CFipqnoe.js';
3
3
 
4
4
  interface ViteSafeEnvOptions {
5
5
  /**
package/dist/vite.js CHANGED
@@ -1 +1 @@
1
- import{b as n}from"./chunk-NRXIL5NF.js";import"./chunk-POVTEBYN.js";import{loadEnv as f}from"vite";function m(t,i={}){return{name:"vite-plugin-safe-env",configResolved(r){let{envDir:o=r.root,prefix:e=r.envPrefix||"VITE_"}=i,s=r.mode,a=f(s,o,e);try{n(t,{source:a,prefix:Array.isArray(e)?e[0]:e,loadProcessEnv:!1})}catch{process.exit(1)}}}}export{m as viteSafeEnv};
1
+ import{b as n}from"./chunk-UONNQTFU.js";import"./chunk-WZTPUQ3S.js";import{loadEnv as f}from"vite";function m(t,i={}){return{name:"vite-plugin-safe-env",configResolved(r){let{envDir:o=r.root,prefix:e=r.envPrefix||"VITE_"}=i,s=r.mode,a=f(s,o,e);try{n(t,{source:a,prefix:Array.isArray(e)?e[0]:e,loadProcessEnv:!1})}catch{process.exit(1)}}}}export{m as viteSafeEnv};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zh-moody/safe-env",
3
- "version": "0.3.3",
3
+ "version": "0.3.4",
4
4
  "description": "Type-safe environment variables for Node.js and Browser with schema validation.",
5
5
  "author": "Moody",
6
6
  "license": "MIT",
@@ -1,5 +0,0 @@
1
- "use strict";Object.defineProperty(exports, "__esModule", {value: true});var _chunkFH2DFX4Jcjs = require('./chunk-FH2DFX4J.cjs');function $(a){let e={r:"\x1B[31m",g:"\x1B[32m",y:"\x1B[33m",b:"\x1B[1m",res:"\x1B[0m",d:"\x1B[2m"};console.error(`
2
- ${e.r}${e.b}\u274C SafeEnv Error${e.res}
3
- ${e.d}${"".padEnd(40,"-")}${e.res}`),a.forEach(i=>{let d=i.value===void 0?"undefined":`"${i.value}"`;console.error(`${e.y}${i.key}${e.res} ${e.r}${i.error}${e.res} ${e.d}${d}${e.res}`)}),console.error(`${e.d}${"".padEnd(40,"-")}${e.res}
4
- ${e.g}\u{1F4A1} Check your .env files.${e.res}
5
- `)}function h(a,e={}){let{loadProcessEnv:i=!0,source:d,prefix:p=""}=e,f;if(d!==void 0)f=d||{};else{let n=e.mode||(typeof process<"u"?process.env.NODE_ENV:"development"),o=[".env",`.env.${n}`,".env.local",`.env.${n}.local`],s={};for(let t of o)s={...s,..._chunkFH2DFX4Jcjs.b.call(void 0, t)};f={...s,...i&&typeof process<"u"?process.env:{}}}let E={},c=[];for(let n in a){let o=a[n],s=o.sourceKey;if(!s){let r=p+n;f[r]!==void 0?s=r:f[n]!==void 0?s=n:s=p?r:n}let t=f[s];try{let r;if(t===void 0||t===""&&o.default!==void 0){if(o.required&&t===void 0)throw new Error("Required field missing");r=o.default}else r=o.parse(t);if(r!==void 0&&o.metadata){let{min:l,max:u,validate:m}=o.metadata;if(typeof r=="number"){if(l!==void 0&&r<l)throw new Error(`Below min ${l}`);if(u!==void 0&&r>u)throw new Error(`Above max ${u}`)}if(m&&!m.fn(r))throw new Error(m.message)}E[n]=r}catch(r){c.push({key:s,error:r.message,value:t})}}if(c.length>0){$(c);let n=typeof process<"u"&&!!process.exit,o=typeof process<"u"&&process.env.NODE_ENV==="test";if(n&&!d&&!o)process.exit(1);else throw new Error("SafeEnv: Configuration validation failed.")}return E}exports.a = $; exports.b = h;
@@ -1,5 +0,0 @@
1
- import{b as v}from"./chunk-POVTEBYN.js";function $(a){let e={r:"\x1B[31m",g:"\x1B[32m",y:"\x1B[33m",b:"\x1B[1m",res:"\x1B[0m",d:"\x1B[2m"};console.error(`
2
- ${e.r}${e.b}\u274C SafeEnv Error${e.res}
3
- ${e.d}${"".padEnd(40,"-")}${e.res}`),a.forEach(i=>{let d=i.value===void 0?"undefined":`"${i.value}"`;console.error(`${e.y}${i.key}${e.res} ${e.r}${i.error}${e.res} ${e.d}${d}${e.res}`)}),console.error(`${e.d}${"".padEnd(40,"-")}${e.res}
4
- ${e.g}\u{1F4A1} Check your .env files.${e.res}
5
- `)}function h(a,e={}){let{loadProcessEnv:i=!0,source:d,prefix:p=""}=e,f;if(d!==void 0)f=d||{};else{let n=e.mode||(typeof process<"u"?process.env.NODE_ENV:"development"),o=[".env",`.env.${n}`,".env.local",`.env.${n}.local`],s={};for(let t of o)s={...s,...v(t)};f={...s,...i&&typeof process<"u"?process.env:{}}}let E={},c=[];for(let n in a){let o=a[n],s=o.sourceKey;if(!s){let r=p+n;f[r]!==void 0?s=r:f[n]!==void 0?s=n:s=p?r:n}let t=f[s];try{let r;if(t===void 0||t===""&&o.default!==void 0){if(o.required&&t===void 0)throw new Error("Required field missing");r=o.default}else r=o.parse(t);if(r!==void 0&&o.metadata){let{min:l,max:u,validate:m}=o.metadata;if(typeof r=="number"){if(l!==void 0&&r<l)throw new Error(`Below min ${l}`);if(u!==void 0&&r>u)throw new Error(`Above max ${u}`)}if(m&&!m.fn(r))throw new Error(m.message)}E[n]=r}catch(r){c.push({key:s,error:r.message,value:t})}}if(c.length>0){$(c);let n=typeof process<"u"&&!!process.exit,o=typeof process<"u"&&process.env.NODE_ENV==="test";if(n&&!d&&!o)process.exit(1);else throw new Error("SafeEnv: Configuration validation failed.")}return E}export{$ as a,h as b};
@@ -1 +0,0 @@
1
- import o from"fs";import a from"path";function s(i){let r={},c=i.split(/\r?\n/);for(let f of c){let n=f.trim();if(!n||n.startsWith("#"))continue;let e=n.indexOf("=");if(e==-1)continue;let l=n.slice(0,e).trim(),t=n.slice(e+1).trim();(t.startsWith('"')&&t.endsWith('"')||t.startsWith("'")&&t.endsWith("'"))&&(t=t.slice(1,-1)),r[l]=t}return r}function h(i=".env"){try{let r=a.resolve(process.cwd(),i);if(o.existsSync(r))return s(o.readFileSync(r,"utf-8"))}catch{}return{}}export{s as a,h as b};