zenvx 0.1.0 → 0.1.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.md CHANGED
@@ -26,6 +26,14 @@ Standard Zod is great, but environment variables are always strings. This leads
26
26
 
27
27
  ---
28
28
 
29
+ ## Features
30
+
31
+ - ✅ Type-safe environment variables
32
+ - ✅ Smart coercion for numbers, booleans, and ports
33
+ - ✅ Strict validation for strings, URLs, emails, JSON, enums
34
+ - ✅ Beautiful, human-readable error reporting
35
+ - ✅ Seamless TypeScript integration
36
+
29
37
  ## Installation
30
38
 
31
39
  ```bash
@@ -42,7 +50,7 @@ yarn add zenvx zod
42
50
 
43
51
  Create a file (e.g., src/env.ts) and export your configuration:
44
52
 
45
- ```javascript
53
+ ```ts
46
54
  import { defineEnv, tx } from "zenvx";
47
55
 
48
56
  export const env = defineEnv({
@@ -61,7 +69,7 @@ export const env = defineEnv({
61
69
 
62
70
  Now use it anywhere in your app:
63
71
 
64
- ```javascript
72
+ ```ts
65
73
  import { env } from "./env";
66
74
 
67
75
  console.log(`Server running on port ${env.PORT}`);
@@ -95,13 +103,12 @@ DATABASE_URL: Must be a valid URL
95
103
  | `tx.email()` | Valid email address. | `"admin@app.com"` | `"admin@..."` |
96
104
  | `tx.json()` | Parses a JSON string into an Object. | `{"foo":"bar"}` | `{foo: "bar"}` |
97
105
  | `tx.enum([...])` | Strict allow-list. | `"PROD"` | `"PROD"` |
98
- | `tx.ip()` | Validates IPv4 format. | `"192.168.1.1"` | `"192..."` |
99
106
 
100
107
  ## Customizing Error Messages
101
108
 
102
109
  Every tx validator accepts an optional custom error message.
103
110
 
104
- ```javascript
111
+ ```ts
105
112
  export const env = defineEnv({
106
113
  API_KEY: tx.string("Please provide a REAL API Key, not just numbers!"),
107
114
  PORT: tx.port("Port is invalid or out of range"),
@@ -114,7 +121,7 @@ Custom .env Path
114
121
 
115
122
  By default, zenvx looks for .env in your project root. You can change this:
116
123
 
117
- ```javascript
124
+ ```ts
118
125
  export const env = defineEnv(
119
126
  {
120
127
  PORT: tx.port(),
@@ -129,7 +136,7 @@ export const env = defineEnv(
129
136
 
130
137
  You can mix tx helpers with standard Zod schemas if you need specific logic.
131
138
 
132
- ```javascript
139
+ ```ts
133
140
  import { defineEnv, tx } from "zenvx";
134
141
  import { z } from "zod";
135
142
 
package/dist/index.cjs CHANGED
@@ -95,71 +95,95 @@ var tx = {
95
95
  * Rejects purely numeric strings (e.g. "12345").
96
96
  * Good for API Keys.
97
97
  */
98
- string: (message) => import_zod2.z.string().refine((val) => !/^\d+$/.test(val), {
99
- error: message || "Value should be text, but looks like a number."
100
- }),
98
+ string: (message) => {
99
+ return import_zod2.z.string().refine((val) => !/^\d+$/.test(val), {
100
+ error: message || "Value should be text, but looks like a number."
101
+ });
102
+ },
101
103
  /**
102
104
  * SMART NUMBER:
103
105
  * Automatically converts "3000" -> 3000.
104
106
  * Fails if the value is not a valid number (e.g. "abc").
105
107
  */
106
- number: (message) => import_zod2.z.coerce.number({ error: message || "Must be a number" }),
108
+ number: (message) => {
109
+ return import_zod2.z.coerce.number({ error: message || "Must be a number" });
110
+ },
107
111
  /**
108
112
  * PORT VALIDATOR:
109
113
  * Coerces to number and ensures it is between 1 and 65535.
110
114
  */
111
- port: (message) => import_zod2.z.coerce.number().min(1).max(65535).catch((ctx) => {
112
- ctx.error;
113
- return -1;
114
- }).refine((val) => val >= 1 && val <= 65535, {
115
- error: message || "Must be a valid port (1-65535)."
116
- }),
115
+ port: (message) => {
116
+ return import_zod2.z.coerce.number({
117
+ error: message || "Must be a valid port (1-65535)"
118
+ }).min(1, { error: message || "Port must be >= 1" }).max(65535, { error: message || "Port must be <= 65535" });
119
+ },
117
120
  /**
118
121
  * SMART BOOLEAN:
119
122
  * Handles "true", "TRUE", "1" -> true
120
123
  * Handles "false", "FALSE", "0" -> false
121
124
  * Throws error on anything else.
122
125
  */
123
- bool: (message) => import_zod2.z.string().transform((val, ctx) => {
124
- const v = val.toLowerCase();
125
- if (v === "true" || v === "1") return true;
126
- if (v === "false" || v === "0") return false;
127
- ctx.addIssue({
128
- code: import_zod2.z.ZodIssueCode.custom,
129
- error: message || "Must be a boolean (true/false or 1/0)."
126
+ bool: (message) => {
127
+ return import_zod2.z.union([import_zod2.z.string(), import_zod2.z.boolean()]).transform((val, ctx) => {
128
+ if (typeof val === "boolean") return val;
129
+ const v = val.toLowerCase();
130
+ if (v === "true" || v === "1") return true;
131
+ if (v === "false" || v === "0") return false;
132
+ ctx.addIssue({
133
+ code: import_zod2.z.ZodIssueCode.custom,
134
+ message: message || 'Must be a boolean ("true"/"false"/"1"/"0")'
135
+ });
136
+ return import_zod2.z.NEVER;
130
137
  });
131
- return import_zod2.z.NEVER;
132
- }),
138
+ },
133
139
  /**
134
140
  * URL:
135
141
  * Strict URL checking.
136
142
  */
137
- url: (message) => import_zod2.z.url({ error: message || "Must be a valid URL (e.g. https://...)" }),
143
+ url: (message) => {
144
+ return import_zod2.z.url({
145
+ error: message || "Must be a valid URL (e.g. https://...)"
146
+ });
147
+ },
138
148
  /**
139
149
  * EMAIL:
140
150
  * Strict Email checking.
141
151
  */
142
- email: (message) => import_zod2.z.email({ error: message || "Must be a valid email address." }),
143
- positiveNumber: (message) => import_zod2.z.coerce.number().refine((val) => val > 0, { error: message || "Must be > 0" }),
144
- nonEmptyString: (message) => import_zod2.z.string().min(1, { error: message || "Cannot be empty" }),
145
- semver: (message) => import_zod2.z.string().refine((val) => /^\d+\.\d+\.\d+(-[0-9A-Za-z-.]+)?$/.test(val), {
146
- error: message || "Must be valid semver"
147
- }),
148
- path: (message) => import_zod2.z.string().min(1, { error: message || "Must be a valid path" }),
149
- enum: (values, message) => import_zod2.z.string().refine((val) => values.includes(val), {
150
- error: message || `Must be one of: ${values.join(", ")}`
151
- }),
152
- json: (message) => import_zod2.z.string().transform((val, ctx) => {
153
- try {
154
- return JSON.parse(val);
155
- } catch {
156
- ctx.addIssue({
157
- code: import_zod2.z.ZodIssueCode.custom,
158
- message: message || "Must be valid JSON"
159
- });
160
- return import_zod2.z.NEVER;
161
- }
162
- })
152
+ email: (message) => {
153
+ return import_zod2.z.email({ error: message || "Must be a valid email address." });
154
+ },
155
+ positiveNumber: (message) => {
156
+ return import_zod2.z.coerce.number().gt(0, { message: message || "Must be > 0" });
157
+ },
158
+ nonEmptyString: (message) => {
159
+ return import_zod2.z.string().trim().min(1, { message: message || "Cannot be empty" });
160
+ },
161
+ semver: (message) => {
162
+ return import_zod2.z.string().refine((val) => /^\d+\.\d+\.\d+(-[0-9A-Za-z-.]+)?$/.test(val), {
163
+ error: message || "Must be valid semver"
164
+ });
165
+ },
166
+ path: (message) => {
167
+ return import_zod2.z.string().min(1, { error: message || "Must be a valid path" });
168
+ },
169
+ enum: (values, message) => {
170
+ return import_zod2.z.string().refine((val) => values.includes(val), {
171
+ error: message || `Must be one of: ${values.join(", ")}`
172
+ });
173
+ },
174
+ json: (message) => {
175
+ return import_zod2.z.string().transform((val, ctx) => {
176
+ try {
177
+ return JSON.parse(val);
178
+ } catch {
179
+ ctx.addIssue({
180
+ code: import_zod2.z.ZodIssueCode.custom,
181
+ message: message || "Must be valid JSON"
182
+ });
183
+ return import_zod2.z.NEVER;
184
+ }
185
+ });
186
+ }
163
187
  };
164
188
  function defineEnv(shape, options) {
165
189
  const fileEnv = loadDotEnv(options?.path);
package/dist/index.d.cts CHANGED
@@ -20,14 +20,14 @@ declare const tx: {
20
20
  * PORT VALIDATOR:
21
21
  * Coerces to number and ensures it is between 1 and 65535.
22
22
  */
23
- port: (message?: string) => z.ZodCatch<z.ZodCoercedNumber<unknown>>;
23
+ port: (message?: string) => z.ZodCoercedNumber<unknown>;
24
24
  /**
25
25
  * SMART BOOLEAN:
26
26
  * Handles "true", "TRUE", "1" -> true
27
27
  * Handles "false", "FALSE", "0" -> false
28
28
  * Throws error on anything else.
29
29
  */
30
- bool: (message?: string) => z.ZodPipe<z.ZodString, z.ZodTransform<boolean, string>>;
30
+ bool: (message?: string) => z.ZodPipe<z.ZodUnion<readonly [z.ZodString, z.ZodBoolean]>, z.ZodTransform<boolean, string | boolean>>;
31
31
  /**
32
32
  * URL:
33
33
  * Strict URL checking.
package/dist/index.d.ts CHANGED
@@ -20,14 +20,14 @@ declare const tx: {
20
20
  * PORT VALIDATOR:
21
21
  * Coerces to number and ensures it is between 1 and 65535.
22
22
  */
23
- port: (message?: string) => z.ZodCatch<z.ZodCoercedNumber<unknown>>;
23
+ port: (message?: string) => z.ZodCoercedNumber<unknown>;
24
24
  /**
25
25
  * SMART BOOLEAN:
26
26
  * Handles "true", "TRUE", "1" -> true
27
27
  * Handles "false", "FALSE", "0" -> false
28
28
  * Throws error on anything else.
29
29
  */
30
- bool: (message?: string) => z.ZodPipe<z.ZodString, z.ZodTransform<boolean, string>>;
30
+ bool: (message?: string) => z.ZodPipe<z.ZodUnion<readonly [z.ZodString, z.ZodBoolean]>, z.ZodTransform<boolean, string | boolean>>;
31
31
  /**
32
32
  * URL:
33
33
  * Strict URL checking.
package/dist/index.js CHANGED
@@ -60,71 +60,95 @@ var tx = {
60
60
  * Rejects purely numeric strings (e.g. "12345").
61
61
  * Good for API Keys.
62
62
  */
63
- string: (message) => z2.string().refine((val) => !/^\d+$/.test(val), {
64
- error: message || "Value should be text, but looks like a number."
65
- }),
63
+ string: (message) => {
64
+ return z2.string().refine((val) => !/^\d+$/.test(val), {
65
+ error: message || "Value should be text, but looks like a number."
66
+ });
67
+ },
66
68
  /**
67
69
  * SMART NUMBER:
68
70
  * Automatically converts "3000" -> 3000.
69
71
  * Fails if the value is not a valid number (e.g. "abc").
70
72
  */
71
- number: (message) => z2.coerce.number({ error: message || "Must be a number" }),
73
+ number: (message) => {
74
+ return z2.coerce.number({ error: message || "Must be a number" });
75
+ },
72
76
  /**
73
77
  * PORT VALIDATOR:
74
78
  * Coerces to number and ensures it is between 1 and 65535.
75
79
  */
76
- port: (message) => z2.coerce.number().min(1).max(65535).catch((ctx) => {
77
- ctx.error;
78
- return -1;
79
- }).refine((val) => val >= 1 && val <= 65535, {
80
- error: message || "Must be a valid port (1-65535)."
81
- }),
80
+ port: (message) => {
81
+ return z2.coerce.number({
82
+ error: message || "Must be a valid port (1-65535)"
83
+ }).min(1, { error: message || "Port must be >= 1" }).max(65535, { error: message || "Port must be <= 65535" });
84
+ },
82
85
  /**
83
86
  * SMART BOOLEAN:
84
87
  * Handles "true", "TRUE", "1" -> true
85
88
  * Handles "false", "FALSE", "0" -> false
86
89
  * Throws error on anything else.
87
90
  */
88
- bool: (message) => z2.string().transform((val, ctx) => {
89
- const v = val.toLowerCase();
90
- if (v === "true" || v === "1") return true;
91
- if (v === "false" || v === "0") return false;
92
- ctx.addIssue({
93
- code: z2.ZodIssueCode.custom,
94
- error: message || "Must be a boolean (true/false or 1/0)."
91
+ bool: (message) => {
92
+ return z2.union([z2.string(), z2.boolean()]).transform((val, ctx) => {
93
+ if (typeof val === "boolean") return val;
94
+ const v = val.toLowerCase();
95
+ if (v === "true" || v === "1") return true;
96
+ if (v === "false" || v === "0") return false;
97
+ ctx.addIssue({
98
+ code: z2.ZodIssueCode.custom,
99
+ message: message || 'Must be a boolean ("true"/"false"/"1"/"0")'
100
+ });
101
+ return z2.NEVER;
95
102
  });
96
- return z2.NEVER;
97
- }),
103
+ },
98
104
  /**
99
105
  * URL:
100
106
  * Strict URL checking.
101
107
  */
102
- url: (message) => z2.url({ error: message || "Must be a valid URL (e.g. https://...)" }),
108
+ url: (message) => {
109
+ return z2.url({
110
+ error: message || "Must be a valid URL (e.g. https://...)"
111
+ });
112
+ },
103
113
  /**
104
114
  * EMAIL:
105
115
  * Strict Email checking.
106
116
  */
107
- email: (message) => z2.email({ error: message || "Must be a valid email address." }),
108
- positiveNumber: (message) => z2.coerce.number().refine((val) => val > 0, { error: message || "Must be > 0" }),
109
- nonEmptyString: (message) => z2.string().min(1, { error: message || "Cannot be empty" }),
110
- semver: (message) => z2.string().refine((val) => /^\d+\.\d+\.\d+(-[0-9A-Za-z-.]+)?$/.test(val), {
111
- error: message || "Must be valid semver"
112
- }),
113
- path: (message) => z2.string().min(1, { error: message || "Must be a valid path" }),
114
- enum: (values, message) => z2.string().refine((val) => values.includes(val), {
115
- error: message || `Must be one of: ${values.join(", ")}`
116
- }),
117
- json: (message) => z2.string().transform((val, ctx) => {
118
- try {
119
- return JSON.parse(val);
120
- } catch {
121
- ctx.addIssue({
122
- code: z2.ZodIssueCode.custom,
123
- message: message || "Must be valid JSON"
124
- });
125
- return z2.NEVER;
126
- }
127
- })
117
+ email: (message) => {
118
+ return z2.email({ error: message || "Must be a valid email address." });
119
+ },
120
+ positiveNumber: (message) => {
121
+ return z2.coerce.number().gt(0, { message: message || "Must be > 0" });
122
+ },
123
+ nonEmptyString: (message) => {
124
+ return z2.string().trim().min(1, { message: message || "Cannot be empty" });
125
+ },
126
+ semver: (message) => {
127
+ return z2.string().refine((val) => /^\d+\.\d+\.\d+(-[0-9A-Za-z-.]+)?$/.test(val), {
128
+ error: message || "Must be valid semver"
129
+ });
130
+ },
131
+ path: (message) => {
132
+ return z2.string().min(1, { error: message || "Must be a valid path" });
133
+ },
134
+ enum: (values, message) => {
135
+ return z2.string().refine((val) => values.includes(val), {
136
+ error: message || `Must be one of: ${values.join(", ")}`
137
+ });
138
+ },
139
+ json: (message) => {
140
+ return z2.string().transform((val, ctx) => {
141
+ try {
142
+ return JSON.parse(val);
143
+ } catch {
144
+ ctx.addIssue({
145
+ code: z2.ZodIssueCode.custom,
146
+ message: message || "Must be valid JSON"
147
+ });
148
+ return z2.NEVER;
149
+ }
150
+ });
151
+ }
128
152
  };
129
153
  function defineEnv(shape, options) {
130
154
  const fileEnv = loadDotEnv(options?.path);
package/package.json CHANGED
@@ -1,10 +1,26 @@
1
1
  {
2
2
  "name": "zenvx",
3
- "version": "0.1.0",
3
+ "version": "0.1.3",
4
4
  "type": "module",
5
5
  "main": "./dist/index.cjs",
6
6
  "module": "./dist/index.js",
7
7
  "types": "./dist/index.d.ts",
8
+ "keywords": [
9
+ "env",
10
+ "environment-variables",
11
+ "configuration",
12
+ "config",
13
+ "dotenv",
14
+ "zod",
15
+ "typescript",
16
+ "validation",
17
+ "schema",
18
+ "type-safe",
19
+ "strict-env",
20
+ "parser",
21
+ "process.env"
22
+ ],
23
+ "license": "MIT",
8
24
  "author": {
9
25
  "name": "Sean Wilfred T. Custodio",
10
26
  "email": "custodiocsean@gmail.com"