@zh-moody/safe-env 0.4.1 → 0.4.3

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 CHANGED
@@ -61,10 +61,10 @@ export default {
61
61
  **2. Define and Export Schema (`src/env.ts`):**
62
62
 
63
63
  ```typescript
64
- import { safeEnv, s } from "@zh-moody/safe-env";
64
+ import { safeEnv, s, isUrl } from "@zh-moody/safe-env";
65
65
 
66
66
  export const schema = {
67
- VITE_API_URL: s.string().url().description("Backend API endpoint"),
67
+ VITE_API_URL: s.string().validate(isUrl, "Invalid URL").description("Backend API endpoint"),
68
68
  VITE_PORT: s.number(3000).description("Server port"),
69
69
  };
70
70
 
@@ -128,13 +128,66 @@ export default config;
128
128
  #### 3. Enhancements & Validation (Chaining)
129
129
 
130
130
  - **`.secret()`**: Mask sensitive data in error reports (`********`).
131
- - **`.url()` / `.email()`**: Common format validation.
132
- - **`.regex(pattern, msg?)`**: Custom regex validation.
131
+ ```typescript
132
+ PASSWORD: s.string().secret();
133
+ ```
133
134
  - **`.description(text)`**: Hover hints for IDEs.
135
+ ```typescript
136
+ PORT: s.number(3000).description("Server port");
137
+ ```
138
+ - **`.optional()`**: Explicitly declare this field as optional. Missing values drop back to `undefined` instead of throwing an error.
139
+ ```typescript
140
+ TRACKING_ID: s.string().optional();
141
+ ```
142
+ - **`.requiredIf(fn)`**: Context-Aware validation. Dynamically determine if the field is required based on values parsed so far.
143
+ ```typescript
144
+ // ICP_NUMBER is required ONLY if parsed REGION equals 'CN'
145
+ ICP_NUMBER: s.string().requiredIf((ctx) => ctx.parsed.REGION === 'CN');
146
+ ```
134
147
  - **`.transform(fn)`**: Custom data transformation (Multi-level pipe).
148
+ ```typescript
149
+ NAME: s.string()
150
+ .transform((v) => v.trim())
151
+ .transform((v) => v.toUpperCase());
152
+ ```
135
153
  - **`.from(key)`**: Alias mapping (Environment key mapping).
154
+ ```typescript
155
+ port: s.number().from("VITE_SERVER_PORT");
156
+ ```
136
157
  - **`.min(n)` / `.max(n)`**: Number range constraints.
137
- - **`.validate(fn, msg?)`**: Custom logic validation.
158
+ ```typescript
159
+ PORT: s.number().min(1024).max(65535);
160
+ ```
161
+ - **`.validate(fn, msg?)`**: Custom logic validation. Supports context passing and native pure-function integration (See "Built-in Validation Rules").
162
+ ```typescript
163
+ INTERNAL_URL: s.string().validate(
164
+ (v, ctx) => v.endsWith(".internal.com"),
165
+ "Must be internal",
166
+ );
167
+ ```
168
+
169
+ #### 4. Built-in Validation Rules
170
+
171
+ To support an infinite amount of business validation logic without blowing up bundle sizes, all heavy string validators (like Regex) and transformers are separated from the core class. They are instead provided as **pure, perfectly tree-shakable higher-order functions** that can be imported directly.
172
+
173
+ Seamlessly integrate them using native `validate`/`transform` methods:
174
+
175
+ ```typescript
176
+ import { safeEnv, s, isUrl, isIPv4, isUUID, isJSON, toJSON, trim } from "@zh-moody/safe-env";
177
+
178
+ const schema = {
179
+ // Dirty data processing: Trim whitespaces, then validate IPv4
180
+ HOST: s.string().transform(trim).validate(isIPv4, "Must be valid IPv4"),
181
+ // Serialization mapping natively
182
+ PAYLOAD: s.string().validate(isJSON).transform(toJSON),
183
+ // Common URL link checking
184
+ API_URL: s.string().validate(isUrl, "Must be a valid URL")
185
+ };
186
+ ```
187
+
188
+ Currently, `@zh-moody/safe-env` exports the following utility functions out-of-the-box:
189
+ - **Validators**: `isUrl`, `isEmail`, `isIPv4`, `isUUID`, `isBase64`, `isJSON`, `isHexColor`, `isObjectId`, `matchesRegex(pattern)`
190
+ - **Transformers**: `trim`, `toLowerCase`, `toUpperCase`, `toJSON`
138
191
 
139
192
  ---
140
193
 
package/README.md CHANGED
@@ -147,13 +147,14 @@ export default config;
147
147
  ```typescript
148
148
  PASSWORD: s.string().secret();
149
149
  ```
150
- - **`.url()` / `.email()`**: 常用格式校验。
150
+ - **`.optional()`**: 显式声明该字段为非必填。解析如果不存在,则返回 `undefined` 而不报错。
151
151
  ```typescript
152
- API_URL: s.string().url();
152
+ TRACKING_ID: s.string().optional();
153
153
  ```
154
- - **`.regex(pattern, msg?)`**: 自定义正则校验。
154
+ - **`.requiredIf(fn)`**: 联动校验(Context-Aware)。依据当前已解析的其他配置动态决定该字段是否必填。
155
155
  ```typescript
156
- VERSION: s.string().regex(/^v\d+\.\d+\.\d+$/, "格式错误");
156
+ // 只有当 REGION 明确被设为 CN 时,ICP 备案号才是必填的
157
+ ICP_NUMBER: s.string().requiredIf((ctx) => ctx.parsed.REGION === 'CN');
157
158
  ```
158
159
  - **`.description(text)`**: 添加变量描述,映射到 IDE 悬停提示中。
159
160
  ```typescript
@@ -173,14 +174,36 @@ export default config;
173
174
  ```typescript
174
175
  PORT: s.number().min(1024).max(65535);
175
176
  ```
176
- - **`.validate(fn, msg?)`**: 完全自定义的逻辑校验。
177
+ - **`.validate(fn, msg?)`**: 传入纯函数进行校验。支持上下文透传以及库内置高阶纯函数的直接切入(详情见下文 "内置验证库")。
177
178
  ```typescript
178
179
  INTERNAL_URL: s.string().validate(
179
- (v) => v.endsWith(".internal.com"),
180
+ (v, ctx) => v.endsWith(".internal.com"),
180
181
  "Must be internal",
181
182
  );
182
183
  ```
183
184
 
185
+ #### 4. 内置验证库
186
+
187
+ 为了支持无限的业务规则而不造成构建体积膨胀,所有重型字符串校验(长正则)和转换规则均剔除出了核心类,化作了**支持完美 Tree-Shaking 的扁平化导出的高阶函数**。只有你明确通过 `import` 引用的微小单点代码才会被真正打包!
188
+
189
+ 使用原生 `validate`/`transform` 方法无隙集成它们:
190
+ ```typescript
191
+ import { safeEnv, s, isUrl, isIPv4, isUUID, isJSON, toJSON, trim } from "@zh-moody/safe-env";
192
+
193
+ const schema = {
194
+ // 脏数据清理:先裁剪首尾空格、后校验 IPv4 格式
195
+ HOST: s.string().transform(trim).validate(isIPv4, "必需是合法 IPv4"),
196
+ // 支持链式序列化
197
+ PAYLOAD: s.string().validate(isJSON).transform(toJSON),
198
+ // 甚至只是普通的链接和格式判定
199
+ API_URL: s.string().validate(isUrl, "必须抛出无效链接")
200
+ };
201
+ ```
202
+
203
+ 目前的 `@zh-moody/safe-env` 原生伴随导出了以下优质清洗函数组合,按需使用即可:
204
+ - **Validators (验证器)**: `isUrl`, `isEmail`, `isIPv4`, `isUUID`, `isBase64`, `isJSON`, `isHexColor`, `isObjectId`, `matchesRegex(pattern)`
205
+ - **Transformers (转换器)**: `trim`, `toLowerCase`, `toUpperCase`, `toJSON`
206
+
184
207
  ---
185
208
 
186
209
  ### 🎨 错误报告
@@ -0,0 +1,9 @@
1
+ var F="development",k="serve",M="build",S="VITE_DEV_SERVER",R="VITE_";var C=typeof window<"u"&&typeof window.document<"u";function m(n,o=!0){if(C)return`SafeEnv Validation Failed: ${n.map(t=>`${t.key} (${t.error})`).join(", ")}`;let c={r:"\x1B[31m",g:"\x1B[32m",y:"\x1B[33m",b:"\x1B[1m",res:"\x1B[0m",d:"\x1B[2m",cy:"\x1B[36m"},e=(t,p)=>{let l=String(t),b=()=>{let s=0;for(let h of l){let u=h.codePointAt(0)??0;s+=u>255||u>=126976&&u<=131071?2:1}return s};for(;b()>p;)l=l.slice(0,-1);return l+" ".repeat(p-b())},i=Math.max(80,(typeof process<"u"?process.stdout.columns:80)||80)-10,x=Math.floor(i*.3),v=Math.floor(i*.5),r=o?c:{r:"",g:"",y:"",b:"",res:"",d:"",cy:""},d=`
2
+ ${r.r}${r.b}\u274C SafeEnv Validation Failed${r.res}
3
+ `;return d+=` ${r.b}${e("Key",x)} \u2502 ${e("Error",v)} \u2502 Value${r.res}
4
+ `,d+=r.d+"\u2500".repeat(i+5)+r.res+`
5
+ `,n.forEach(t=>{let p=t.value===void 0?"undefined":t.isSecret?"********":`"${t.value}"`,l=t.value===void 0?r.d:t.isSecret?r.y:r.cy;d+=` ${r.y}${e(t.key,x)}${r.res} \u2502 ${r.r}${e(t.error,v)}${r.res} \u2502 ${l}${p}${r.res}
6
+ `}),d+=r.d+"\u2500".repeat(i+5)+r.res+`
7
+ `,d+=` ${r.g}\u{1F4A1} Tip: Check your .env files or schema definitions.${r.res}
8
+ `,d}function _(n){if(C){console.group("%c \u274C SafeEnv Validation Failed ","background: #fee2e2; color: #b91c1c; font-weight: bold; padding: 4px; border-radius: 2px;");let o=n.reduce((c,e)=>(c[e.key]={"Error Message":e.error,"Current Value":e.value===void 0?"undefined":e.isSecret?"********":e.value},c),{});console.table(o),console.log("%c \u{1F4A1} Tip: Check your .env files or schema definitions. ","color: #059669; font-style: italic;"),console.groupEnd()}else console.error(m(n,!0))}var y={},T=new WeakMap;function P(n){let o=m(n,!1);return new Proxy({},{get(c,e){if(e==="__isSafeEnvError")return!0;if(e==="toJSON")return()=>({error:"SafeEnv Validation Failed"});throw new Error(`[safe-env] Cannot access "${String(e)}" because validation failed:
9
+ ${o}`)},ownKeys(){return[]},getOwnPropertyDescriptor(){}})}var w="[safe-env] Cannot modify read-only environment variables.";function O(n){if(T.has(n))return T.get(n);let o=new Proxy(n,{get(c,e){if(e==="__isSafeEnv")return!0;let i=Reflect.get(c,e);return i!==null&&typeof i=="object"&&!Object.isFrozen(i)?O(i):i},set(){throw new Error(w)},deleteProperty(){throw new Error(w)},defineProperty(){throw new Error(w)},setPrototypeOf(){throw new Error(w)}});return T.set(n,o),o}function I(n,o,c,e){return o||(c[n]!==void 0?n:e&&!n.startsWith(e)?e+n:n)}function W(n,o={}){let{loadProcessEnv:c=!0,prefix:e=R,cwd:i,useCache:x=!0,refreshCache:v=!1,envLoader:r}=o,d=o.mode||(typeof process<"u"?process.env.NODE_ENV:void 0)||F,t=`${e}|${i??""}|${d}`;v&&delete y[t];let p=typeof window<"u",l=typeof process<"u"&&(!!process.env.VITE||!!process.env[S]),b=p||l||"source"in o,s={},h=!1;if("source"in o)o.source===void 0?(h=!0,s={}):s=o.source;else if(x&&!v&&y[t]&&Object.keys(y[t]).length>0)s=y[t];else if(typeof process<"u"&&!p)try{let a={};if(r)for(let f of[".env",`.env.${d}`,".env.local",`.env.${d}.local`])a={...a,...r(f,i)};s={...a,...c?process.env:{}}}catch{s={}}x&&Object.keys(s).length>0&&(y[t]||(y[t]={}),Object.assign(y[t],s));let u={},E=[];for(let a in n){let f=n[a],V=I(a,f.sourceKey,s,e),g=s[V],$={source:s,parsed:u};try{let D=typeof f.required=="function"?f.required($):f.required;if(g===void 0||g===""&&f.default!==void 0){if(D&&g===void 0)throw new Error("Missing required field");u[a]=f.default}else u[a]=f.parse(g,$)}catch(D){E.push({key:V,error:D.message,value:g,isSecret:f.metadata?.isSecret})}}if(E.length>0){if(o.throwOnError){let a=new Error(m(E,!0));throw a.plainMessage=m(E,!1),a}return h||_(E),typeof process<"u"&&process.exit&&!b&&process.exit(1),P(E)}return O(u)}export{F as a,k as b,M as c,S as d,R as e,m as f,_ as g,W as h};
@@ -0,0 +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 g(t,i){let n="",r=1;for(;r<t.length;){if(t[r]==="\\"&&r+1<t.length){n+=t[r+1],r+=2;continue}if(t[r]===i)return n;n+=t[r++]}return null}function m(t){let i={},n=t.split(/\r?\n/);for(let r of n){let o=r.trim();if(!o||o.startsWith("#"))continue;let s=o.indexOf("=");if(s===-1)continue;let c=o.slice(0,s).trim(),e=o.slice(s+1).trim();if(!e){i[c]="";continue}let f=e[0];if(f==='"'||f==="'"){let u=g(e,f);if(u!==null){i[c]=u;continue}}let l=e.indexOf("#");l!==-1&&(e=e.slice(0,l).trim()),i[c]=e}return i}function D(t=".env",i){try{let n=_path2.default.resolve(i||process.cwd(),t);if(_fs2.default.existsSync(n))return m(_fs2.default.readFileSync(n,"utf-8"))}catch (e2){}return{}}exports.a = m; exports.b = D;
@@ -0,0 +1,9 @@
1
+ "use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } } 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 F="development",k= exports.b ="serve",M= exports.c ="build",S= exports.d ="VITE_DEV_SERVER",R= exports.e ="VITE_";var C=typeof window<"u"&&typeof window.document<"u";function m(n,o=!0){if(C)return`SafeEnv Validation Failed: ${n.map(t=>`${t.key} (${t.error})`).join(", ")}`;let c={r:"\x1B[31m",g:"\x1B[32m",y:"\x1B[33m",b:"\x1B[1m",res:"\x1B[0m",d:"\x1B[2m",cy:"\x1B[36m"},e=(t,p)=>{let l=String(t),b=()=>{let s=0;for(let h of l){let u=_nullishCoalesce(h.codePointAt(0), () => (0));s+=u>255||u>=126976&&u<=131071?2:1}return s};for(;b()>p;)l=l.slice(0,-1);return l+" ".repeat(p-b())},i=Math.max(80,(typeof process<"u"?process.stdout.columns:80)||80)-10,x=Math.floor(i*.3),v=Math.floor(i*.5),r=o?c:{r:"",g:"",y:"",b:"",res:"",d:"",cy:""},d=`
2
+ ${r.r}${r.b}\u274C SafeEnv Validation Failed${r.res}
3
+ `;return d+=` ${r.b}${e("Key",x)} \u2502 ${e("Error",v)} \u2502 Value${r.res}
4
+ `,d+=r.d+"\u2500".repeat(i+5)+r.res+`
5
+ `,n.forEach(t=>{let p=t.value===void 0?"undefined":t.isSecret?"********":`"${t.value}"`,l=t.value===void 0?r.d:t.isSecret?r.y:r.cy;d+=` ${r.y}${e(t.key,x)}${r.res} \u2502 ${r.r}${e(t.error,v)}${r.res} \u2502 ${l}${p}${r.res}
6
+ `}),d+=r.d+"\u2500".repeat(i+5)+r.res+`
7
+ `,d+=` ${r.g}\u{1F4A1} Tip: Check your .env files or schema definitions.${r.res}
8
+ `,d}function _(n){if(C){console.group("%c \u274C SafeEnv Validation Failed ","background: #fee2e2; color: #b91c1c; font-weight: bold; padding: 4px; border-radius: 2px;");let o=n.reduce((c,e)=>(c[e.key]={"Error Message":e.error,"Current Value":e.value===void 0?"undefined":e.isSecret?"********":e.value},c),{});console.table(o),console.log("%c \u{1F4A1} Tip: Check your .env files or schema definitions. ","color: #059669; font-style: italic;"),console.groupEnd()}else console.error(m(n,!0))}var y={},T=new WeakMap;function P(n){let o=m(n,!1);return new Proxy({},{get(c,e){if(e==="__isSafeEnvError")return!0;if(e==="toJSON")return()=>({error:"SafeEnv Validation Failed"});throw new Error(`[safe-env] Cannot access "${String(e)}" because validation failed:
9
+ ${o}`)},ownKeys(){return[]},getOwnPropertyDescriptor(){}})}var w="[safe-env] Cannot modify read-only environment variables.";function O(n){if(T.has(n))return T.get(n);let o=new Proxy(n,{get(c,e){if(e==="__isSafeEnv")return!0;let i=Reflect.get(c,e);return i!==null&&typeof i=="object"&&!Object.isFrozen(i)?O(i):i},set(){throw new Error(w)},deleteProperty(){throw new Error(w)},defineProperty(){throw new Error(w)},setPrototypeOf(){throw new Error(w)}});return T.set(n,o),o}function I(n,o,c,e){return o||(c[n]!==void 0?n:e&&!n.startsWith(e)?e+n:n)}function W(n,o={}){let{loadProcessEnv:c=!0,prefix:e=R,cwd:i,useCache:x=!0,refreshCache:v=!1,envLoader:r}=o,d=o.mode||(typeof process<"u"?process.env.NODE_ENV:void 0)||F,t=`${e}|${_nullishCoalesce(i, () => (""))}|${d}`;v&&delete y[t];let p=typeof window<"u",l=typeof process<"u"&&(!!process.env.VITE||!!process.env[S]),b=p||l||"source"in o,s={},h=!1;if("source"in o)o.source===void 0?(h=!0,s={}):s=o.source;else if(x&&!v&&y[t]&&Object.keys(y[t]).length>0)s=y[t];else if(typeof process<"u"&&!p)try{let a={};if(r)for(let f of[".env",`.env.${d}`,".env.local",`.env.${d}.local`])a={...a,...r(f,i)};s={...a,...c?process.env:{}}}catch (e2){s={}}x&&Object.keys(s).length>0&&(y[t]||(y[t]={}),Object.assign(y[t],s));let u={},E=[];for(let a in n){let f=n[a],V=I(a,f.sourceKey,s,e),g=s[V],$={source:s,parsed:u};try{let D=typeof f.required=="function"?f.required($):f.required;if(g===void 0||g===""&&f.default!==void 0){if(D&&g===void 0)throw new Error("Missing required field");u[a]=f.default}else u[a]=f.parse(g,$)}catch(D){E.push({key:V,error:D.message,value:g,isSecret:_optionalChain([f, 'access', _2 => _2.metadata, 'optionalAccess', _3 => _3.isSecret])})}}if(E.length>0){if(o.throwOnError){let a=new Error(m(E,!0));throw a.plainMessage=m(E,!1),a}return h||_(E),typeof process<"u"&&process.exit&&!b&&process.exit(1),P(E)}return O(u)}exports.a = F; exports.b = k; exports.c = M; exports.d = S; exports.e = R; exports.f = m; exports.g = _; exports.h = W;
@@ -0,0 +1 @@
1
+ import d from"fs";import p from"path";function g(t,i){let n="",r=1;for(;r<t.length;){if(t[r]==="\\"&&r+1<t.length){n+=t[r+1],r+=2;continue}if(t[r]===i)return n;n+=t[r++]}return null}function m(t){let i={},n=t.split(/\r?\n/);for(let r of n){let o=r.trim();if(!o||o.startsWith("#"))continue;let s=o.indexOf("=");if(s===-1)continue;let c=o.slice(0,s).trim(),e=o.slice(s+1).trim();if(!e){i[c]="";continue}let f=e[0];if(f==='"'||f==="'"){let u=g(e,f);if(u!==null){i[c]=u;continue}}let l=e.indexOf("#");l!==-1&&(e=e.slice(0,l).trim()),i[c]=e}return i}function D(t=".env",i){try{let n=p.resolve(i||process.cwd(),t);if(d.existsSync(n))return m(d.readFileSync(n,"utf-8"))}catch{}return{}}export{m as a,D as b};
package/dist/fs-node.cjs CHANGED
@@ -1 +1 @@
1
- "use strict";Object.defineProperty(exports, "__esModule", {value: true});var _chunkTR6KYAQIcjs = require('./chunk-TR6KYAQI.cjs');exports.loadDotEnv = _chunkTR6KYAQIcjs.b;
1
+ "use strict";Object.defineProperty(exports, "__esModule", {value: true});var _chunkGHUSMNQTcjs = require('./chunk-GHUSMNQT.cjs');exports.loadDotEnv = _chunkGHUSMNQTcjs.b;
package/dist/fs-node.js CHANGED
@@ -1 +1 @@
1
- import{b as a}from"./chunk-5KPNMU6R.js";export{a as loadDotEnv};
1
+ import{b as a}from"./chunk-Z4SFK232.js";export{a as loadDotEnv};
package/dist/index.cjs CHANGED
@@ -1 +1 @@
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 _chunkTR6KYAQIcjs = require('./chunk-TR6KYAQI.cjs');var _chunkDEDBPEC7cjs = require('./chunk-DEDBPEC7.cjs');function a(r,e,n,o=[]){return{type:r,default:e,required:e===void 0,parse:n,metadata:o.length?{options:o}:{},from(t){return this.sourceKey=t,this},validate(t,i="Custom validation failed"){return this.metadata={...this.metadata,validate:{fn:t,message:i}},this},min(t){return this.metadata={...this.metadata,min:t},this},max(t){return this.metadata={...this.metadata,max:t},this},transform(t){let i=this.parse;return this.parse=s=>t(i(s)),this},secret(){return this.metadata={...this.metadata,isSecret:!0},this},url(){return this.validate(t=>{try{return new URL(String(t)),!0}catch (e2){return!1}},"Invalid URL format")},email(){return this.validate(t=>/^[\w-\.]+@([\w-]+\.)+[\w-]{2,4}$/.test(String(t)),"Invalid email format")},regex(t,i="Value does not match pattern"){return this.validate(s=>t.test(String(s)),i)},description(t){return this.metadata={...this.metadata,description:t},this}}}var h={string:r=>a("string",r,e=>String(e)),number:r=>a("number",r,e=>{let n=Number(e);if(isNaN(n))throw new Error(`Invalid number: ${e}`);return n}),boolean:r=>a("boolean",r,e=>{if(typeof e=="boolean")return e;if(e===void 0||e==="")return!1;let n=String(e).toLowerCase().trim();if(["true","1","yes","on"].includes(n))return!0;if(["false","0","no","off"].includes(n))return!1;throw new Error(`Invalid boolean: ${e}`)}),enum:(r,e)=>a("enum",e,n=>{if(!r.includes(n))throw new Error(`Value "${n}" is not one of: ${r.join(", ")}`);return n},r),array:(r,e=",")=>a("array",r,n=>Array.isArray(n)?n:typeof n!="string"?[]:n.split(e).map(o=>o.trim()).filter(Boolean))};function E(r,e={}){let n=typeof process<"u"&&(_optionalChain([process, 'access', _ => _.release, 'optionalAccess', _2 => _2.name])==="node"||!!_optionalChain([process, 'access', _3 => _3.versions, 'optionalAccess', _4 => _4.node]));return _chunkDEDBPEC7cjs.h.call(void 0, r,{...e,envLoader:n?_chunkTR6KYAQIcjs.b:void 0})}exports.BUILD = _chunkDEDBPEC7cjs.c; exports.DEV = _chunkDEDBPEC7cjs.a; exports.SERVE = _chunkDEDBPEC7cjs.b; exports.VITE_DEV_FLAG = _chunkDEDBPEC7cjs.d; exports.VITE_PREFIX = _chunkDEDBPEC7cjs.e; exports.formatErrorReport = _chunkDEDBPEC7cjs.f; exports.parseDotEnv = _chunkTR6KYAQIcjs.a; exports.reportErrors = _chunkDEDBPEC7cjs.g; exports.s = h; exports.safeEnv = E;
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 _chunkGHUSMNQTcjs = require('./chunk-GHUSMNQT.cjs');var _chunkMTT5NLXMcjs = require('./chunk-MTT5NLXM.cjs');var a=class{constructor(e,n,r,i=[]){this.type=e,this.default=n,this.required=n===void 0,this.parse=r,this.metadata=i.length?{options:i}:{}}from(e){return this.sourceKey=e,this}optional(){return this.required=!1,this}requiredIf(e){return this.required=e,this}validate(e,n="Custom validation failed"){let r=this.parse;return this.parse=(i,o)=>{let s=r(i,o);if(!e(s,o))throw new Error(n);return s},this}min(e){let n=this.parse;return this.parse=(r,i)=>{let o=n(r,i);if(typeof o=="number"&&o<e)throw new Error(`Below min ${e}`);return o},this.metadata={...this.metadata,min:e},this}max(e){let n=this.parse;return this.parse=(r,i)=>{let o=n(r,i);if(typeof o=="number"&&o>e)throw new Error(`Above max ${e}`);return o},this.metadata={...this.metadata,max:e},this}transform(e){let n=this.parse;return this.parse=(r,i)=>e(n(r,i),i),this}secret(){return this.metadata={...this.metadata,isSecret:!0},this}description(e){return this.metadata={...this.metadata,description:e},this}},m= exports.s ={string:t=>new a("string",t,e=>String(e)),number:t=>new a("number",t,e=>{let n=Number(e);if(isNaN(n))throw new Error(`Invalid number value: "${e}"`);return n}),boolean:t=>new a("boolean",t,e=>{if(typeof e=="boolean")return e;if(e==="")return!1;let n=String(e).toLowerCase().trim();if(["true","1","yes","on"].includes(n))return!0;if(["false","0","no","off"].includes(n))return!1;throw new Error(`Invalid boolean value: "${e}"`)}),enum:(t,e)=>new a("enum",e,n=>{if(!t.includes(n))throw new Error(`Value "${n}" is not one of: ${t.join(", ")}`);return n},t),array:(t,e=",")=>new a("array",t,n=>{if(!e)throw new Error("[safe-env] array separator cannot be empty");return Array.isArray(n)?n:typeof n!="string"?[]:n.split(e).map(r=>r.trim()).filter(Boolean)})};var b=t=>{try{return new URL(String(t)),!0}catch (e2){return!1}},D= exports.isEmail =t=>/^[\w-\.]+@([\w-]+\.)+[\w-]{2,4}$/.test(String(t)),S= exports.matchesRegex =t=>e=>t.test(String(e)),T= exports.isIPv4 =t=>/^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/.test(String(t)),E= exports.isUUID =t=>/^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i.test(String(t)),F= exports.isBase64 =t=>/^(?:[A-Za-z0-9+\/]{4})*(?:[A-Za-z0-9+\/]{2}==|[A-Za-z0-9+\/]{3}=)?$/.test(String(t)),C= exports.isJSON =t=>{try{return JSON.parse(String(t)),!0}catch (e3){return!1}},N= exports.isHexColor =t=>/^#?([a-fA-F0-9]{6}|[a-fA-F0-9]{3})$/.test(String(t)),$= exports.isObjectId =t=>/^[0-9a-fA-F]{24}$/.test(String(t)),V= exports.trim =t=>String(t).trim(),v= exports.toLowerCase =t=>String(t).toLowerCase(),k= exports.toUpperCase =t=>String(t).toUpperCase(),A= exports.toJSON =t=>{try{return JSON.parse(String(t))}catch (e4){throw new Error("Invalid JSON string")}};function B(t,e={}){let n=typeof process<"u"&&(_optionalChain([process, 'access', _ => _.release, 'optionalAccess', _2 => _2.name])==="node"||!!_optionalChain([process, 'access', _3 => _3.versions, 'optionalAccess', _4 => _4.node]));return _chunkMTT5NLXMcjs.h.call(void 0, t,{...e,envLoader:n?_chunkGHUSMNQTcjs.b:void 0})}exports.BUILD = _chunkMTT5NLXMcjs.c; exports.DEV = _chunkMTT5NLXMcjs.a; exports.SERVE = _chunkMTT5NLXMcjs.b; exports.VITE_DEV_FLAG = _chunkMTT5NLXMcjs.d; exports.VITE_PREFIX = _chunkMTT5NLXMcjs.e; exports.formatErrorReport = _chunkMTT5NLXMcjs.f; exports.isBase64 = F; exports.isEmail = D; exports.isHexColor = N; exports.isIPv4 = T; exports.isJSON = C; exports.isObjectId = $; exports.isUUID = E; exports.isUrl = b; exports.matchesRegex = S; exports.parseDotEnv = _chunkGHUSMNQTcjs.a; exports.reportErrors = _chunkMTT5NLXMcjs.g; exports.s = m; exports.safeEnv = B; exports.toJSON = A; exports.toLowerCase = v; exports.toUpperCase = k; exports.trim = V;
package/dist/index.d.cts CHANGED
@@ -1,5 +1,5 @@
1
- import { F as FieldDefinition, E as EnvError, S as Schema, a as SafeEnvOptions, I as InferSchema } from './types-Bx-JEkTH.cjs';
2
- export { B as BUILD, b as BaseType, D as DEV, c as SERVE, V as VITE_DEV_FLAG, d as VITE_PREFIX } from './types-Bx-JEkTH.cjs';
1
+ import { F as FieldDefinition, E as EnvError, S as Schema, a as SafeEnvOptions, I as InferSchema } from './types-DaBhCHE2.cjs';
2
+ export { B as BUILD, b as BaseType, D as DEV, c as FieldMetadata, d as SERVE, V as VITE_DEV_FLAG, e as VITE_PREFIX, f as ValidationContext } from './types-DaBhCHE2.cjs';
3
3
 
4
4
  declare const s: {
5
5
  string: (d?: string) => FieldDefinition<string>;
@@ -9,9 +9,23 @@ declare const s: {
9
9
  array: (d?: string[], sep?: string) => FieldDefinition<string[]>;
10
10
  };
11
11
 
12
+ declare const isUrl: (v: string) => boolean;
13
+ declare const isEmail: (v: string) => boolean;
14
+ declare const matchesRegex: (pattern: RegExp) => (v: string) => boolean;
15
+ declare const isIPv4: (v: string) => boolean;
16
+ declare const isUUID: (v: string) => boolean;
17
+ declare const isBase64: (v: string) => boolean;
18
+ declare const isJSON: (v: string) => boolean;
19
+ declare const isHexColor: (v: string) => boolean;
20
+ declare const isObjectId: (v: string) => boolean;
21
+ declare const trim: (v: string) => string;
22
+ declare const toLowerCase: (v: string) => string;
23
+ declare const toUpperCase: (v: string) => string;
24
+ declare const toJSON: (v: string) => any;
25
+
12
26
  /***
13
27
  * 将 .env 内容字符串解析为对象
14
- * 兼容带引号的值、多层引号内部的注释标识符以及行尾注释
28
+ * 兼容带引号的值、转义字符(\"、\')、以及行尾注释
15
29
  */
16
30
  declare function parseDotEnv(content: string): Record<string, string>;
17
31
 
@@ -23,4 +37,4 @@ declare function safeEnv<T extends Schema>(schema: T, options?: SafeEnvOptions &
23
37
  useCache?: boolean;
24
38
  }): Readonly<InferSchema<T>>;
25
39
 
26
- export { EnvError, FieldDefinition, InferSchema, SafeEnvOptions, Schema, formatErrorReport, parseDotEnv, reportErrors, s, safeEnv };
40
+ export { EnvError, FieldDefinition, InferSchema, SafeEnvOptions, Schema, formatErrorReport, isBase64, isEmail, isHexColor, isIPv4, isJSON, isObjectId, isUUID, isUrl, matchesRegex, parseDotEnv, reportErrors, s, safeEnv, toJSON, toLowerCase, toUpperCase, trim };
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { F as FieldDefinition, E as EnvError, S as Schema, a as SafeEnvOptions, I as InferSchema } from './types-Bx-JEkTH.js';
2
- export { B as BUILD, b as BaseType, D as DEV, c as SERVE, V as VITE_DEV_FLAG, d as VITE_PREFIX } from './types-Bx-JEkTH.js';
1
+ import { F as FieldDefinition, E as EnvError, S as Schema, a as SafeEnvOptions, I as InferSchema } from './types-DaBhCHE2.js';
2
+ export { B as BUILD, b as BaseType, D as DEV, c as FieldMetadata, d as SERVE, V as VITE_DEV_FLAG, e as VITE_PREFIX, f as ValidationContext } from './types-DaBhCHE2.js';
3
3
 
4
4
  declare const s: {
5
5
  string: (d?: string) => FieldDefinition<string>;
@@ -9,9 +9,23 @@ declare const s: {
9
9
  array: (d?: string[], sep?: string) => FieldDefinition<string[]>;
10
10
  };
11
11
 
12
+ declare const isUrl: (v: string) => boolean;
13
+ declare const isEmail: (v: string) => boolean;
14
+ declare const matchesRegex: (pattern: RegExp) => (v: string) => boolean;
15
+ declare const isIPv4: (v: string) => boolean;
16
+ declare const isUUID: (v: string) => boolean;
17
+ declare const isBase64: (v: string) => boolean;
18
+ declare const isJSON: (v: string) => boolean;
19
+ declare const isHexColor: (v: string) => boolean;
20
+ declare const isObjectId: (v: string) => boolean;
21
+ declare const trim: (v: string) => string;
22
+ declare const toLowerCase: (v: string) => string;
23
+ declare const toUpperCase: (v: string) => string;
24
+ declare const toJSON: (v: string) => any;
25
+
12
26
  /***
13
27
  * 将 .env 内容字符串解析为对象
14
- * 兼容带引号的值、多层引号内部的注释标识符以及行尾注释
28
+ * 兼容带引号的值、转义字符(\"、\')、以及行尾注释
15
29
  */
16
30
  declare function parseDotEnv(content: string): Record<string, string>;
17
31
 
@@ -23,4 +37,4 @@ declare function safeEnv<T extends Schema>(schema: T, options?: SafeEnvOptions &
23
37
  useCache?: boolean;
24
38
  }): Readonly<InferSchema<T>>;
25
39
 
26
- export { EnvError, FieldDefinition, InferSchema, SafeEnvOptions, Schema, formatErrorReport, parseDotEnv, reportErrors, s, safeEnv };
40
+ export { EnvError, FieldDefinition, InferSchema, SafeEnvOptions, Schema, formatErrorReport, isBase64, isEmail, isHexColor, isIPv4, isJSON, isObjectId, isUUID, isUrl, matchesRegex, parseDotEnv, reportErrors, s, safeEnv, toJSON, toLowerCase, toUpperCase, trim };
package/dist/index.js CHANGED
@@ -1 +1 @@
1
- import{a as u,b as d}from"./chunk-5KPNMU6R.js";import{a as c,b as y,c as T,d as D,e as g,f as l,g as m,h as f}from"./chunk-I6JAZ77H.js";function a(r,e,n,o=[]){return{type:r,default:e,required:e===void 0,parse:n,metadata:o.length?{options:o}:{},from(t){return this.sourceKey=t,this},validate(t,i="Custom validation failed"){return this.metadata={...this.metadata,validate:{fn:t,message:i}},this},min(t){return this.metadata={...this.metadata,min:t},this},max(t){return this.metadata={...this.metadata,max:t},this},transform(t){let i=this.parse;return this.parse=s=>t(i(s)),this},secret(){return this.metadata={...this.metadata,isSecret:!0},this},url(){return this.validate(t=>{try{return new URL(String(t)),!0}catch{return!1}},"Invalid URL format")},email(){return this.validate(t=>/^[\w-\.]+@([\w-]+\.)+[\w-]{2,4}$/.test(String(t)),"Invalid email format")},regex(t,i="Value does not match pattern"){return this.validate(s=>t.test(String(s)),i)},description(t){return this.metadata={...this.metadata,description:t},this}}}var h={string:r=>a("string",r,e=>String(e)),number:r=>a("number",r,e=>{let n=Number(e);if(isNaN(n))throw new Error(`Invalid number: ${e}`);return n}),boolean:r=>a("boolean",r,e=>{if(typeof e=="boolean")return e;if(e===void 0||e==="")return!1;let n=String(e).toLowerCase().trim();if(["true","1","yes","on"].includes(n))return!0;if(["false","0","no","off"].includes(n))return!1;throw new Error(`Invalid boolean: ${e}`)}),enum:(r,e)=>a("enum",e,n=>{if(!r.includes(n))throw new Error(`Value "${n}" is not one of: ${r.join(", ")}`);return n},r),array:(r,e=",")=>a("array",r,n=>Array.isArray(n)?n:typeof n!="string"?[]:n.split(e).map(o=>o.trim()).filter(Boolean))};function E(r,e={}){let n=typeof process<"u"&&(process.release?.name==="node"||!!process.versions?.node);return f(r,{...e,envLoader:n?d:void 0})}export{T as BUILD,c as DEV,y as SERVE,D as VITE_DEV_FLAG,g as VITE_PREFIX,l as formatErrorReport,u as parseDotEnv,m as reportErrors,h as s,E as safeEnv};
1
+ import{a as d,b as l}from"./chunk-Z4SFK232.js";import{a as c,b as h,c as g,d as x,e as w,f as u,g as p,h as f}from"./chunk-3IILV4AU.js";var a=class{type;default;required;sourceKey;metadata;parse;constructor(e,n,r,i=[]){this.type=e,this.default=n,this.required=n===void 0,this.parse=r,this.metadata=i.length?{options:i}:{}}from(e){return this.sourceKey=e,this}optional(){return this.required=!1,this}requiredIf(e){return this.required=e,this}validate(e,n="Custom validation failed"){let r=this.parse;return this.parse=(i,o)=>{let s=r(i,o);if(!e(s,o))throw new Error(n);return s},this}min(e){let n=this.parse;return this.parse=(r,i)=>{let o=n(r,i);if(typeof o=="number"&&o<e)throw new Error(`Below min ${e}`);return o},this.metadata={...this.metadata,min:e},this}max(e){let n=this.parse;return this.parse=(r,i)=>{let o=n(r,i);if(typeof o=="number"&&o>e)throw new Error(`Above max ${e}`);return o},this.metadata={...this.metadata,max:e},this}transform(e){let n=this.parse;return this.parse=(r,i)=>e(n(r,i),i),this}secret(){return this.metadata={...this.metadata,isSecret:!0},this}description(e){return this.metadata={...this.metadata,description:e},this}},m={string:t=>new a("string",t,e=>String(e)),number:t=>new a("number",t,e=>{let n=Number(e);if(isNaN(n))throw new Error(`Invalid number value: "${e}"`);return n}),boolean:t=>new a("boolean",t,e=>{if(typeof e=="boolean")return e;if(e==="")return!1;let n=String(e).toLowerCase().trim();if(["true","1","yes","on"].includes(n))return!0;if(["false","0","no","off"].includes(n))return!1;throw new Error(`Invalid boolean value: "${e}"`)}),enum:(t,e)=>new a("enum",e,n=>{if(!t.includes(n))throw new Error(`Value "${n}" is not one of: ${t.join(", ")}`);return n},t),array:(t,e=",")=>new a("array",t,n=>{if(!e)throw new Error("[safe-env] array separator cannot be empty");return Array.isArray(n)?n:typeof n!="string"?[]:n.split(e).map(r=>r.trim()).filter(Boolean)})};var b=t=>{try{return new URL(String(t)),!0}catch{return!1}},D=t=>/^[\w-\.]+@([\w-]+\.)+[\w-]{2,4}$/.test(String(t)),S=t=>e=>t.test(String(e)),T=t=>/^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/.test(String(t)),E=t=>/^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i.test(String(t)),F=t=>/^(?:[A-Za-z0-9+\/]{4})*(?:[A-Za-z0-9+\/]{2}==|[A-Za-z0-9+\/]{3}=)?$/.test(String(t)),C=t=>{try{return JSON.parse(String(t)),!0}catch{return!1}},N=t=>/^#?([a-fA-F0-9]{6}|[a-fA-F0-9]{3})$/.test(String(t)),$=t=>/^[0-9a-fA-F]{24}$/.test(String(t)),V=t=>String(t).trim(),v=t=>String(t).toLowerCase(),k=t=>String(t).toUpperCase(),A=t=>{try{return JSON.parse(String(t))}catch{throw new Error("Invalid JSON string")}};function B(t,e={}){let n=typeof process<"u"&&(process.release?.name==="node"||!!process.versions?.node);return f(t,{...e,envLoader:n?l:void 0})}export{g as BUILD,c as DEV,h as SERVE,x as VITE_DEV_FLAG,w as VITE_PREFIX,u as formatErrorReport,F as isBase64,D as isEmail,N as isHexColor,T as isIPv4,C as isJSON,$ as isObjectId,E as isUUID,b as isUrl,S as matchesRegex,d as parseDotEnv,p as reportErrors,m as s,B as safeEnv,A as toJSON,v as toLowerCase,k as toUpperCase,V as trim};
@@ -4,22 +4,32 @@ declare const BUILD = "build";
4
4
  declare const VITE_DEV_FLAG = "VITE_DEV_SERVER";
5
5
  declare const VITE_PREFIX = "VITE_";
6
6
  type BaseType = "string" | "number" | "boolean" | "enum" | "array";
7
+ interface ValidationContext {
8
+ source: Record<string, any>;
9
+ parsed: Record<string, any>;
10
+ }
11
+ interface FieldMetadata {
12
+ isSecret?: boolean;
13
+ description?: string;
14
+ min?: number;
15
+ max?: number;
16
+ options?: string[];
17
+ }
7
18
  interface FieldDefinition<T = any, D extends string = string> {
8
19
  type: BaseType;
9
20
  default?: T;
10
- required: boolean;
21
+ required: boolean | ((ctx: ValidationContext) => boolean);
11
22
  sourceKey?: string;
12
- metadata?: any;
13
- parse: (val: any) => T;
23
+ metadata?: FieldMetadata;
24
+ parse: (val: any, ctx: ValidationContext) => T;
14
25
  from: (key: string) => FieldDefinition<T, D>;
15
- validate: (fn: (val: T) => boolean, message?: string) => FieldDefinition<T, D>;
26
+ optional: () => FieldDefinition<T | undefined, D>;
27
+ requiredIf: (fn: (ctx: ValidationContext) => boolean) => FieldDefinition<T, D>;
28
+ validate: (fn: (val: T, ctx: ValidationContext) => boolean, message?: string) => FieldDefinition<T, D>;
16
29
  min: (val: number) => FieldDefinition<T, D>;
17
30
  max: (val: number) => FieldDefinition<T, D>;
18
- transform: <U>(fn: (val: T) => U) => FieldDefinition<U, D>;
31
+ transform: <U>(fn: (val: T, ctx: ValidationContext) => U) => FieldDefinition<U, D>;
19
32
  secret: () => FieldDefinition<T, D>;
20
- url: () => FieldDefinition<T, D>;
21
- email: () => FieldDefinition<T, D>;
22
- regex: (pattern: RegExp, message?: string) => FieldDefinition<T, D>;
23
33
  description: <NewD extends string>(text: NewD) => FieldDefinition<T, NewD>;
24
34
  }
25
35
  type Schema = Record<string, FieldDefinition<any, any>>;
@@ -44,4 +54,4 @@ interface SafeEnvOptions {
44
54
  /** @internal */ devMode?: boolean;
45
55
  }
46
56
 
47
- export { BUILD as B, DEV as D, type EnvError as E, type FieldDefinition as F, type InferSchema as I, type Schema as S, VITE_DEV_FLAG as V, type SafeEnvOptions as a, type BaseType as b, SERVE as c, VITE_PREFIX as d };
57
+ export { BUILD as B, DEV as D, type EnvError as E, type FieldDefinition as F, type InferSchema as I, type Schema as S, VITE_DEV_FLAG as V, type SafeEnvOptions as a, type BaseType as b, type FieldMetadata as c, SERVE as d, VITE_PREFIX as e, type ValidationContext as f };
@@ -4,22 +4,32 @@ declare const BUILD = "build";
4
4
  declare const VITE_DEV_FLAG = "VITE_DEV_SERVER";
5
5
  declare const VITE_PREFIX = "VITE_";
6
6
  type BaseType = "string" | "number" | "boolean" | "enum" | "array";
7
+ interface ValidationContext {
8
+ source: Record<string, any>;
9
+ parsed: Record<string, any>;
10
+ }
11
+ interface FieldMetadata {
12
+ isSecret?: boolean;
13
+ description?: string;
14
+ min?: number;
15
+ max?: number;
16
+ options?: string[];
17
+ }
7
18
  interface FieldDefinition<T = any, D extends string = string> {
8
19
  type: BaseType;
9
20
  default?: T;
10
- required: boolean;
21
+ required: boolean | ((ctx: ValidationContext) => boolean);
11
22
  sourceKey?: string;
12
- metadata?: any;
13
- parse: (val: any) => T;
23
+ metadata?: FieldMetadata;
24
+ parse: (val: any, ctx: ValidationContext) => T;
14
25
  from: (key: string) => FieldDefinition<T, D>;
15
- validate: (fn: (val: T) => boolean, message?: string) => FieldDefinition<T, D>;
26
+ optional: () => FieldDefinition<T | undefined, D>;
27
+ requiredIf: (fn: (ctx: ValidationContext) => boolean) => FieldDefinition<T, D>;
28
+ validate: (fn: (val: T, ctx: ValidationContext) => boolean, message?: string) => FieldDefinition<T, D>;
16
29
  min: (val: number) => FieldDefinition<T, D>;
17
30
  max: (val: number) => FieldDefinition<T, D>;
18
- transform: <U>(fn: (val: T) => U) => FieldDefinition<U, D>;
31
+ transform: <U>(fn: (val: T, ctx: ValidationContext) => U) => FieldDefinition<U, D>;
19
32
  secret: () => FieldDefinition<T, D>;
20
- url: () => FieldDefinition<T, D>;
21
- email: () => FieldDefinition<T, D>;
22
- regex: (pattern: RegExp, message?: string) => FieldDefinition<T, D>;
23
33
  description: <NewD extends string>(text: NewD) => FieldDefinition<T, NewD>;
24
34
  }
25
35
  type Schema = Record<string, FieldDefinition<any, any>>;
@@ -44,4 +54,4 @@ interface SafeEnvOptions {
44
54
  /** @internal */ devMode?: boolean;
45
55
  }
46
56
 
47
- export { BUILD as B, DEV as D, type EnvError as E, type FieldDefinition as F, type InferSchema as I, type Schema as S, VITE_DEV_FLAG as V, type SafeEnvOptions as a, type BaseType as b, SERVE as c, VITE_PREFIX as d };
57
+ export { BUILD as B, DEV as D, type EnvError as E, type FieldDefinition as F, type InferSchema as I, type Schema as S, VITE_DEV_FLAG as V, type SafeEnvOptions as a, type BaseType as b, type FieldMetadata as c, SERVE as d, VITE_PREFIX as e, type ValidationContext as f };
package/dist/vite.cjs CHANGED
@@ -1,2 +1,2 @@
1
- "use strict";Object.defineProperty(exports, "__esModule", {value: true});var _chunkDEDBPEC7cjs = require('./chunk-DEDBPEC7.cjs');var _vite = require('vite');function g(v,f={}){let n=null,o=!1;return{name:"vite-plugin-safe-env",configResolved(e){o=e.command===_chunkDEDBPEC7cjs.b,process.env[_chunkDEDBPEC7cjs.d]=o?"true":"";let{envDir:t=e.root,prefix:r=e.envPrefix||_chunkDEDBPEC7cjs.e}=f,p=_vite.loadEnv.call(void 0, e.mode,t,r);try{_chunkDEDBPEC7cjs.h.call(void 0, v,{source:p,prefix:Array.isArray(r)?r[0]:r,loadProcessEnv:!1,throwOnError:!0}),n=null}catch(s){n=s,o?e.logger.error(s.message):(console.error(s.message),console.error(`\x1B[31m[safe-env] Fatal: Environment validation failed during build. Exiting...\x1B[0m
2
- `),process.exit(1))}},transform(e,t){if(o&&n&&(e.includes("import.meta.env")||e.includes("safeEnv"))){let r=new Error(n.plainMessage||n.message);throw r.stack="",r}return null}}}exports.viteSafeEnv = g;
1
+ "use strict";Object.defineProperty(exports, "__esModule", {value: true});var _chunkMTT5NLXMcjs = require('./chunk-MTT5NLXM.cjs');var _vite = require('vite');function g(v,c={}){let n=null,s=!1;return{name:"vite-plugin-safe-env",configResolved(e){s=e.command===_chunkMTT5NLXMcjs.b,process.env[_chunkMTT5NLXMcjs.d]=s?"true":"";let{envDir:o=e.root,prefix:r=e.envPrefix||_chunkMTT5NLXMcjs.e}=c,t=_vite.loadEnv.call(void 0, e.mode,o,r);try{_chunkMTT5NLXMcjs.h.call(void 0, v,{source:t,prefix:Array.isArray(r)?r[0]:r,loadProcessEnv:!1,throwOnError:!0}),n=null}catch(l){n=l,s?e.logger.error(l.message):(console.error(l.message),console.error(`\x1B[31m[safe-env] Fatal: Environment validation failed during build. Exiting...\x1B[0m
2
+ `),process.exit(1))}},transform(e,o){if(!s||!n||o.includes("node_modules")||o.endsWith(".d.ts"))return null;let r=e.replace(/\/\/[^\n]*/g,"").replace(/\/\*[\s\S]*?\*\//g,"");if(r.includes("import.meta.env")||r.includes("safeEnv")){let t=new Error(n.plainMessage||n.message);throw t.stack="",t}return null}}}exports.viteSafeEnv = g;
package/dist/vite.d.cts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { Plugin } from 'vite';
2
- import { S as Schema } from './types-Bx-JEkTH.cjs';
2
+ import { S as Schema } from './types-DaBhCHE2.cjs';
3
3
 
4
4
  /**
5
5
  * Vite 插件:在构建或开发启动时校验环境变量
package/dist/vite.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { Plugin } from 'vite';
2
- import { S as Schema } from './types-Bx-JEkTH.js';
2
+ import { S as Schema } from './types-DaBhCHE2.js';
3
3
 
4
4
  /**
5
5
  * Vite 插件:在构建或开发启动时校验环境变量
package/dist/vite.js CHANGED
@@ -1,2 +1,2 @@
1
- import{b as a,d as i,e as l,h as m}from"./chunk-I6JAZ77H.js";import{loadEnv as u}from"vite";function g(v,f={}){let n=null,o=!1;return{name:"vite-plugin-safe-env",configResolved(e){o=e.command===a,process.env[i]=o?"true":"";let{envDir:t=e.root,prefix:r=e.envPrefix||l}=f,p=u(e.mode,t,r);try{m(v,{source:p,prefix:Array.isArray(r)?r[0]:r,loadProcessEnv:!1,throwOnError:!0}),n=null}catch(s){n=s,o?e.logger.error(s.message):(console.error(s.message),console.error(`\x1B[31m[safe-env] Fatal: Environment validation failed during build. Exiting...\x1B[0m
2
- `),process.exit(1))}},transform(e,t){if(o&&n&&(e.includes("import.meta.env")||e.includes("safeEnv"))){let r=new Error(n.plainMessage||n.message);throw r.stack="",r}return null}}}export{g as viteSafeEnv};
1
+ import{b as a,d as i,e as m,h as u}from"./chunk-3IILV4AU.js";import{loadEnv as f}from"vite";function g(v,c={}){let n=null,s=!1;return{name:"vite-plugin-safe-env",configResolved(e){s=e.command===a,process.env[i]=s?"true":"";let{envDir:o=e.root,prefix:r=e.envPrefix||m}=c,t=f(e.mode,o,r);try{u(v,{source:t,prefix:Array.isArray(r)?r[0]:r,loadProcessEnv:!1,throwOnError:!0}),n=null}catch(l){n=l,s?e.logger.error(l.message):(console.error(l.message),console.error(`\x1B[31m[safe-env] Fatal: Environment validation failed during build. Exiting...\x1B[0m
2
+ `),process.exit(1))}},transform(e,o){if(!s||!n||o.includes("node_modules")||o.endsWith(".d.ts"))return null;let r=e.replace(/\/\/[^\n]*/g,"").replace(/\/\*[\s\S]*?\*\//g,"");if(r.includes("import.meta.env")||r.includes("safeEnv")){let t=new Error(n.plainMessage||n.message);throw t.stack="",t}return null}}}export{g as viteSafeEnv};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zh-moody/safe-env",
3
- "version": "0.4.1",
3
+ "version": "0.4.3",
4
4
  "description": "Type-safe environment variables for Node.js and Browser with schema validation.",
5
5
  "author": "Moody",
6
6
  "license": "MIT",
@@ -1 +0,0 @@
1
- import u from"fs";import a from"path";function m(r){let n={},i=r.split(/\r?\n/);for(let d of i){let e=d.trim();if(!e||e.startsWith("#"))continue;let o=e.indexOf("=");if(o===-1)continue;let s=e.slice(0,o).trim(),t=e.slice(o+1).trim();if(!t){n[s]="";continue}let c=t[0];if(c==='"'||c==="'"){let l=t.indexOf(c,1);if(l!==-1){n[s]=t.slice(1,l);continue}}let f=t.indexOf("#");f!==-1&&(t=t.slice(0,f).trim()),n[s]=t}return n}function h(r=".env",n){try{let i=a.resolve(n||process.cwd(),r);if(u.existsSync(i))return m(u.readFileSync(i,"utf-8"))}catch{}return{}}export{m as a,h as b};
@@ -1,9 +0,0 @@
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 F="development",P= exports.b ="serve",j= exports.c ="build",V= exports.d ="VITE_DEV_SERVER",R= exports.e ="VITE_";var k=typeof window<"u"&&typeof window.document<"u";function b(t,i=!0){if(k)return`SafeEnv Validation Failed: ${t.map(s=>`${s.key} (${s.error})`).join(", ")}`;let f={r:"\x1B[31m",g:"\x1B[32m",y:"\x1B[33m",b:"\x1B[1m",res:"\x1B[0m",d:"\x1B[2m",cy:"\x1B[36m"},r=(s,p)=>{let n=String(s),v=()=>n.length+(n.match(/[^\x00-\xff]/g)||[]).length;for(;v()>p;)n=n.slice(0,-1);return n+" ".repeat(p-v())},a=Math.max(80,(typeof process<"u"?process.stdout.columns:80)||80)-10,y=Math.floor(a*.3),E=Math.floor(a*.5),e=i?f:{r:"",g:"",y:"",b:"",res:"",d:"",cy:""},d=`
2
- ${e.r}${e.b}\u274C SafeEnv Validation Failed${e.res}
3
- `;return d+=` ${e.b}${r("Key",y)} \u2502 ${r("Error",E)} \u2502 Value${e.res}
4
- `,d+=e.d+"\u2500".repeat(a+5)+e.res+`
5
- `,t.forEach(s=>{let p=s.value===void 0?"undefined":s.isSecret?"********":`"${s.value}"`,n=s.value===void 0?e.d:s.isSecret?e.y:e.cy;d+=` ${e.y}${r(s.key,y)}${e.res} \u2502 ${e.r}${r(s.error,E)}${e.res} \u2502 ${n}${p}${e.res}
6
- `}),d+=e.d+"\u2500".repeat(a+5)+e.res+`
7
- `,d+=` ${e.g}\u{1F4A1} Tip: Check your .env files or schema definitions.${e.res}
8
- `,d}function O(t){if(k){console.group("%c \u274C SafeEnv Validation Failed ","background: #fee2e2; color: #b91c1c; font-weight: bold; padding: 4px; border-radius: 2px;");let i=t.reduce((f,r)=>(f[r.key]={"Error Message":r.error,"Current Value":r.value===void 0?"undefined":r.isSecret?"********":r.value},f),{});console.table(i),console.log("%c \u{1F4A1} Tip: Check your .env files or schema definitions. ","color: #059669; font-style: italic;"),console.groupEnd()}else console.error(b(t,!0))}var u={},$=new WeakMap;function I(t){let i=b(t,!1);return new Proxy({},{get(f,r){if(r==="__isSafeEnvError")return!0;if(r==="toJSON")return()=>({error:"SafeEnv Validation Failed"});throw new Error(`[safe-env] Cannot access "${String(r)}" because validation failed:
9
- ${i}`)},ownKeys(){return[]},getOwnPropertyDescriptor(){}})}var h="[safe-env] Cannot modify read-only environment variables.";function _(t){if($.has(t))return $.get(t);let i=new Proxy(t,{get(f,r){if(r==="__isSafeEnv")return!0;let a=Reflect.get(f,r);return a!==null&&typeof a=="object"&&!Object.isFrozen(a)?_(a):a},set(){throw new Error(h)},deleteProperty(){throw new Error(h)},defineProperty(){throw new Error(h)},setPrototypeOf(){throw new Error(h)}});return $.set(t,i),i}function q(t,i={}){let{loadProcessEnv:f=!0,prefix:r=R,cwd:a,useCache:y=!0,refreshCache:E=!1,envLoader:e}=i;if(E)for(let o in u)delete u[o];let d=typeof window<"u",s=typeof process<"u"&&(!!process.env.VITE||!!process.env[V]),p=d||s||"source"in i,n={};if("source"in i)n=i.source||{};else if(y&&!E&&Object.keys(u).length>0)n=u;else if(typeof process<"u"&&!d)try{let o=i.mode||process.env.NODE_ENV||F,c={};if(e)for(let g of[".env",`.env.${o}`,".env.local",`.env.${o}.local`])c={...c,...e(g,a)};n={...c,...f?process.env:{}}}catch (e2){n={}}y&&!E&&Object.keys(u).length>0?Object.keys(n).length===0?n=u:Object.assign(u,n):y&&Object.keys(n).length>0&&Object.assign(u,n);let v={},m=[];for(let o in t){let c=t[o],g=r&&!o.startsWith(r)?r+o:o,S=c.sourceKey||(n[g]!==void 0?g:n[o]!==void 0?o:g),x=n[S];try{if(x===void 0||x===""&&c.default!==void 0){if(c.required&&x===void 0)throw new Error("Missing required field");v[o]=c.default}else{let l=c.parse(x);if(l!==void 0&&c.metadata){let{min:w,max:D,validate:T}=c.metadata;if(typeof l=="number"){if(w!==void 0&&l<w)throw new Error(`Below min ${w}`);if(D!==void 0&&l>D)throw new Error(`Above max ${D}`)}if(T&&!T.fn(l))throw new Error(T.message)}v[o]=l}}catch(l){m.push({key:S,error:l.message,value:x,isSecret:_optionalChain([c, 'access', _2 => _2.metadata, 'optionalAccess', _3 => _3.isSecret])})}}if(m.length>0){if(i.throwOnError){let o=new Error(b(m,!0));throw o.plainMessage=b(m,!1),o}return O(m),typeof process<"u"&&process.exit&&!p&&process.exit(1),I(m)}return _(v)}exports.a = F; exports.b = P; exports.c = j; exports.d = V; exports.e = R; exports.f = b; exports.g = O; exports.h = q;
@@ -1,9 +0,0 @@
1
- var F="development",P="serve",j="build",V="VITE_DEV_SERVER",R="VITE_";var k=typeof window<"u"&&typeof window.document<"u";function b(t,i=!0){if(k)return`SafeEnv Validation Failed: ${t.map(s=>`${s.key} (${s.error})`).join(", ")}`;let f={r:"\x1B[31m",g:"\x1B[32m",y:"\x1B[33m",b:"\x1B[1m",res:"\x1B[0m",d:"\x1B[2m",cy:"\x1B[36m"},r=(s,p)=>{let n=String(s),v=()=>n.length+(n.match(/[^\x00-\xff]/g)||[]).length;for(;v()>p;)n=n.slice(0,-1);return n+" ".repeat(p-v())},a=Math.max(80,(typeof process<"u"?process.stdout.columns:80)||80)-10,y=Math.floor(a*.3),E=Math.floor(a*.5),e=i?f:{r:"",g:"",y:"",b:"",res:"",d:"",cy:""},d=`
2
- ${e.r}${e.b}\u274C SafeEnv Validation Failed${e.res}
3
- `;return d+=` ${e.b}${r("Key",y)} \u2502 ${r("Error",E)} \u2502 Value${e.res}
4
- `,d+=e.d+"\u2500".repeat(a+5)+e.res+`
5
- `,t.forEach(s=>{let p=s.value===void 0?"undefined":s.isSecret?"********":`"${s.value}"`,n=s.value===void 0?e.d:s.isSecret?e.y:e.cy;d+=` ${e.y}${r(s.key,y)}${e.res} \u2502 ${e.r}${r(s.error,E)}${e.res} \u2502 ${n}${p}${e.res}
6
- `}),d+=e.d+"\u2500".repeat(a+5)+e.res+`
7
- `,d+=` ${e.g}\u{1F4A1} Tip: Check your .env files or schema definitions.${e.res}
8
- `,d}function O(t){if(k){console.group("%c \u274C SafeEnv Validation Failed ","background: #fee2e2; color: #b91c1c; font-weight: bold; padding: 4px; border-radius: 2px;");let i=t.reduce((f,r)=>(f[r.key]={"Error Message":r.error,"Current Value":r.value===void 0?"undefined":r.isSecret?"********":r.value},f),{});console.table(i),console.log("%c \u{1F4A1} Tip: Check your .env files or schema definitions. ","color: #059669; font-style: italic;"),console.groupEnd()}else console.error(b(t,!0))}var u={},$=new WeakMap;function I(t){let i=b(t,!1);return new Proxy({},{get(f,r){if(r==="__isSafeEnvError")return!0;if(r==="toJSON")return()=>({error:"SafeEnv Validation Failed"});throw new Error(`[safe-env] Cannot access "${String(r)}" because validation failed:
9
- ${i}`)},ownKeys(){return[]},getOwnPropertyDescriptor(){}})}var h="[safe-env] Cannot modify read-only environment variables.";function _(t){if($.has(t))return $.get(t);let i=new Proxy(t,{get(f,r){if(r==="__isSafeEnv")return!0;let a=Reflect.get(f,r);return a!==null&&typeof a=="object"&&!Object.isFrozen(a)?_(a):a},set(){throw new Error(h)},deleteProperty(){throw new Error(h)},defineProperty(){throw new Error(h)},setPrototypeOf(){throw new Error(h)}});return $.set(t,i),i}function q(t,i={}){let{loadProcessEnv:f=!0,prefix:r=R,cwd:a,useCache:y=!0,refreshCache:E=!1,envLoader:e}=i;if(E)for(let o in u)delete u[o];let d=typeof window<"u",s=typeof process<"u"&&(!!process.env.VITE||!!process.env[V]),p=d||s||"source"in i,n={};if("source"in i)n=i.source||{};else if(y&&!E&&Object.keys(u).length>0)n=u;else if(typeof process<"u"&&!d)try{let o=i.mode||process.env.NODE_ENV||F,c={};if(e)for(let g of[".env",`.env.${o}`,".env.local",`.env.${o}.local`])c={...c,...e(g,a)};n={...c,...f?process.env:{}}}catch{n={}}y&&!E&&Object.keys(u).length>0?Object.keys(n).length===0?n=u:Object.assign(u,n):y&&Object.keys(n).length>0&&Object.assign(u,n);let v={},m=[];for(let o in t){let c=t[o],g=r&&!o.startsWith(r)?r+o:o,S=c.sourceKey||(n[g]!==void 0?g:n[o]!==void 0?o:g),x=n[S];try{if(x===void 0||x===""&&c.default!==void 0){if(c.required&&x===void 0)throw new Error("Missing required field");v[o]=c.default}else{let l=c.parse(x);if(l!==void 0&&c.metadata){let{min:w,max:D,validate:T}=c.metadata;if(typeof l=="number"){if(w!==void 0&&l<w)throw new Error(`Below min ${w}`);if(D!==void 0&&l>D)throw new Error(`Above max ${D}`)}if(T&&!T.fn(l))throw new Error(T.message)}v[o]=l}}catch(l){m.push({key:S,error:l.message,value:x,isSecret:c.metadata?.isSecret})}}if(m.length>0){if(i.throwOnError){let o=new Error(b(m,!0));throw o.plainMessage=b(m,!1),o}return O(m),typeof process<"u"&&process.exit&&!p&&process.exit(1),I(m)}return _(v)}export{F as a,P as b,j as c,V as d,R as e,b as f,O as g,q as h};
@@ -1 +0,0 @@
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 m(r){let n={},i=r.split(/\r?\n/);for(let d of i){let e=d.trim();if(!e||e.startsWith("#"))continue;let o=e.indexOf("=");if(o===-1)continue;let s=e.slice(0,o).trim(),t=e.slice(o+1).trim();if(!t){n[s]="";continue}let c=t[0];if(c==='"'||c==="'"){let l=t.indexOf(c,1);if(l!==-1){n[s]=t.slice(1,l);continue}}let f=t.indexOf("#");f!==-1&&(t=t.slice(0,f).trim()),n[s]=t}return n}function h(r=".env",n){try{let i=_path2.default.resolve(n||process.cwd(),r);if(_fs2.default.existsSync(i))return m(_fs2.default.readFileSync(i,"utf-8"))}catch (e2){}return{}}exports.a = m; exports.b = h;