env-typed-checker 0.1.1 โ†’ 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,34 +1,34 @@
1
- # env-typed-checker ๐Ÿฉบ
1
+ # env-typed-checker
2
2
 
3
- A tiny, developer-friendly library to **validate and parse environment variables** using a simple schema.
3
+ Validate and parse environment variables using a tiny schema โ€” with both a **TypeScript/Node API** and a **CLI**.
4
4
 
5
- env-typed-checker prevents your application from starting with:
5
+ It helps your app fail fast when configuration is wrong:
6
6
 
7
- - โŒ missing environment variables
8
- - โŒ wrong types (e.g. PORT="abc")
9
- - โŒ invalid URLs or JSON
10
- - โŒ silent configuration mistakes
7
+ - โŒ Missing required variables
8
+ - โŒ Wrong types (e.g. `PORT="abc"`)
9
+ - โŒ Invalid URLs or JSON
10
+ - โŒ Silent mistakes that only crash later
11
11
 
12
12
  ---
13
13
 
14
14
  ## โœจ Features
15
15
 
16
- - Simple schema syntax
17
- - Automatic `.env` loading
18
- - Type parsing (number, boolean, json, url)
19
- - Optional variables with `?`
20
- - Friendly aggregated error messages
21
- - TypeScript support out of the box
22
- - Zero dependencies except `dotenv`
16
+ - Simple schema syntax (`"number"`, `"boolean?"`, `"url"`, `"json"`, `"string"`)
17
+ - Loads `.env` automatically via `dotenv` (optional)
18
+ - Aggregated, friendly error output (shows all issues at once)
19
+ - TypeScript types inferred from the schema
20
+ - CLI for quick checks (great for CI)
21
+ - Minimal dependencies (runtime: `dotenv` only)
23
22
 
24
23
  ---
25
24
 
26
- ## ๐Ÿ“ฆ Installation
25
+ ## ๐Ÿ“ฆ Install
27
26
 
28
27
  ```bash
29
28
  npm install env-typed-checker
30
29
  ```
31
- ### ๐Ÿš€ Basic Usage
30
+
31
+ ## ๐Ÿš€ Quick Start (Code)
32
32
 
33
33
  ```ts
34
34
  import { envDoctor } from "env-typed-checker";
@@ -39,10 +39,15 @@ export const config = envDoctor({
39
39
  DEBUG: "boolean?"
40
40
  });
41
41
  ```
42
- ### Result
43
- * **PORT** โ†’ `number` (e.g., `"3000"` becomes `3000`)
44
- * **DB_URL** โ†’ `string` (validated as a proper URL string)
45
- * **DEBUG** โ†’ `boolean | undefined` (optional field; parses `"true"`, `"1"`, etc.)
42
+
43
+ ### What you get
44
+
45
+ * PORT โ†’ number
46
+
47
+ * DB_URL โ†’ string (validated as URL)
48
+
49
+ * DEBUG โ†’ boolean | undefined (optional)
50
+
46
51
 
47
52
  ### ๐Ÿงฉ Supported Types
48
53
  | Type | Description |
@@ -53,26 +58,55 @@ export const config = envDoctor({
53
58
  | **json** | Validates and parses a valid JSON string |
54
59
  | **url** | Validates for a properly formatted URL |
55
60
 
61
+
56
62
  ### Optional Values
57
63
  Add ? to make a variable optional:
58
64
  ```ts
59
- { DEBUG: "boolean?" }
65
+ envDoctor({ DEBUG: "boolean?" });
60
66
  ```
61
67
  If missing โ†’ value will be undefined.
62
68
 
69
+
70
+ ### โš™๏ธ Options
71
+
72
+ ```ts
73
+ envDoctor(schema, {
74
+ loadDotEnv: true, // default: true (loads .env)
75
+ env: process.env // default: process.env (override for tests)
76
+ });
77
+ ```
78
+
79
+ ### ๐Ÿงช Testing with custom env
80
+
81
+ ```ts
82
+ import { envDoctor } from "env-typed-checker";
83
+
84
+ const cfg = envDoctor(
85
+ { PORT: "number" },
86
+ { loadDotEnv: false, env: { PORT: "3000" } }
87
+ );
88
+
89
+ console.log(cfg.PORT); // 3000
90
+ ```
91
+
63
92
  ### โŒ Error Example
64
- Given this .env:
65
- ```.env
93
+
94
+ Given a `.env` like:
95
+
96
+ ```env
66
97
  PORT=abc
67
98
  DB_URL=not-a-url
68
- Code:
69
99
  ```
100
+
70
101
  ```ts
102
+ import { envDoctor } from "env-typed-checker";
103
+
71
104
  envDoctor({
72
105
  PORT: "number",
73
106
  DB_URL: "url"
74
107
  });
75
108
  ```
109
+
76
110
  ### Output:
77
111
 
78
112
  ```ts
@@ -82,24 +116,56 @@ ENV validation failed
82
116
  ```
83
117
  All errors are shown together so you can fix them in one go.
84
118
 
85
- ### โš™๏ธ Options
86
- ```ts
87
- envDoctor(schema, {
88
- loadDotEnv: true, // auto load .env (default)
89
- env: process.env // custom env source (useful for tests)
90
- });
119
+ # ๐Ÿ–ฅ๏ธ CLI
120
+
121
+ Validate your environment without writing code โ€” perfect for CI pipelines.
122
+
123
+ ## 1) Create a schema file
124
+
125
+ `env.schema.json`
126
+ ```json
127
+ {
128
+ "PORT": "number",
129
+ "DB_URL": "url",
130
+ "DEBUG": "boolean?"
131
+ }
91
132
  ```
92
- ### ๐Ÿงช Example with Custom Env (Testing)
93
133
 
94
- ```ts
95
- const cfg = envDoctor(
96
- { PORT: "number" },
97
- { loadDotEnv: false, env: { PORT: "3000" } }
98
- );
134
+ ## 2) Run the check
99
135
 
100
- console.log(cfg.PORT); // 3000 (number)
136
+ ```bash
137
+ npx env-typed-checker check --schema env.schema.json
101
138
  ```
102
- ### ๐Ÿ›  Development
139
+
140
+ ### Options
141
+
142
+ ```bash
143
+ # Custom env file
144
+ npx env-typed-checker check --schema env.schema.json --env-file .env.production
145
+
146
+ # Skip dotenv (use process.env only)
147
+ npx env-typed-checker check --schema env.schema.json --no-dotenv
148
+ ```
149
+
150
+ ## Exit codes
151
+
152
+ * `0` = OK
153
+
154
+ * `1` = validation failed
155
+
156
+ * `2` = CLI usage / unexpected error
157
+
158
+ ## โœ… CI Example (GitHub Actions)
159
+
160
+ Add this to your workflow to fail the build if env is invalid:
161
+
162
+ ```yml
163
+ - name: Validate env
164
+ run: npx env-typed-checker check --schema env.schema.json
165
+ ```
166
+ (If you use a different env file in CI, pass --env-file.)
167
+
168
+ ## ๐Ÿ›  Development
103
169
  Clone the repo and install:
104
170
  ```bash
105
171
  npm install
@@ -111,43 +177,36 @@ npm run test # run tests
111
177
  npm run typecheck # TypeScript check
112
178
  npm run dev # watch build
113
179
  ```
180
+
114
181
  ### ๐Ÿค Contributing
115
- Contributions are welcome!
182
+ PRs are welcome!
183
+
184
+ * Add new validators (e.g. `enum`, `regex`, `email`)
185
+
186
+ * Improve CLI output formatting
187
+
188
+ * Add .env.example generator
189
+
190
+ * Improve docs & examples
116
191
 
117
- * Improve error messages
118
- * Add more boolean variants
119
- * Enhance URL validation
120
- * Add JSON schema validation
121
- * Write better docs & examples
122
192
  * Please read CONTRIBUTING.md before opening a PR.
123
193
 
124
194
  ### ๐Ÿ“Œ Roadmap
125
- #### v1 (current)
126
- * Schema validation
127
- * Type parsing
128
- * Optional values
129
- * Friendly errors
130
-
131
- #### v2 (planned)
132
- * CLI support
133
- * .env.example generator
134
- * Strict unknown variable check
135
- * Framework integrations
195
+ * `.env.example` generator
136
196
 
137
- # ๐Ÿ“ License
138
- MIT
197
+ * Strict mode: warn on unknown variables
139
198
 
199
+ * More schema features: enums, defaults, min/max
140
200
 
141
- ---
201
+ * Framework helpers (Next.js / Express / etc.)
142
202
 
143
- ```yml
144
- If you want, I can help you add:
145
203
 
146
- - badges (npm version, CI, coverage)
147
- - a small logo
148
- - example project section
204
+ # ๐Ÿ“ License
205
+ MIT
149
206
 
150
- Just tell me ๐Ÿ‘
151
- ```
152
207
 
208
+ ---
153
209
 
210
+ ```yml
211
+ ::contentReference[oaicite:0]{index=0}
212
+ ```
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env node
2
+
3
+ const { runCli } = require("../dist/cli.js");
4
+
5
+ const code = runCli(process.argv.slice(2), console);
6
+ process.exitCode = code;
@@ -0,0 +1,126 @@
1
+ // src/index.ts
2
+ import * as dotenv from "dotenv";
3
+
4
+ // src/error.ts
5
+ var EnvDoctorError = class extends Error {
6
+ constructor(issues) {
7
+ const header = "ENV validation failed";
8
+ const lines = issues.map((i) => `- ${i.key}: ${i.message}`);
9
+ super([header, ...lines].join("\n"));
10
+ this.name = "EnvDoctorError";
11
+ this.issues = issues;
12
+ }
13
+ };
14
+
15
+ // src/parsers.ts
16
+ function parseByType(type, raw) {
17
+ switch (type) {
18
+ case "string":
19
+ return raw;
20
+ case "number": {
21
+ const n = Number(raw.trim());
22
+ if (!Number.isFinite(n)) {
23
+ throw new Error(`expected number, got "${raw}"`);
24
+ }
25
+ return n;
26
+ }
27
+ case "boolean": {
28
+ const v = raw.trim().toLowerCase();
29
+ if (["true", "1", "yes", "y", "on"].includes(v)) return true;
30
+ if (["false", "0", "no", "n", "off"].includes(v)) return false;
31
+ throw new Error(
32
+ `expected boolean (true/false/1/0/yes/no/on/off), got "${raw}"`
33
+ );
34
+ }
35
+ case "json": {
36
+ try {
37
+ return JSON.parse(raw);
38
+ } catch {
39
+ throw new Error(`expected json, got "${raw}"`);
40
+ }
41
+ }
42
+ case "url": {
43
+ try {
44
+ new URL(raw);
45
+ return raw;
46
+ } catch {
47
+ throw new Error(`expected url, got "${raw}"`);
48
+ }
49
+ }
50
+ /* v8 ignore next -- @preserve */
51
+ default: {
52
+ const _never = type;
53
+ return _never;
54
+ }
55
+ }
56
+ }
57
+ function splitOptional(schemaValue) {
58
+ const optional = schemaValue.endsWith("?");
59
+ const base = optional ? schemaValue.slice(0, -1) : schemaValue;
60
+ const allowed = ["string", "number", "boolean", "json", "url"];
61
+ if (!allowed.includes(base)) {
62
+ throw new Error(
63
+ `Unsupported type "${schemaValue}". Supported: string, number, boolean, json, url (optional with ?)`
64
+ );
65
+ }
66
+ return { baseType: base, optional };
67
+ }
68
+
69
+ // src/validator.ts
70
+ function validateAndParse(schema, env) {
71
+ const issues = [];
72
+ const out = {};
73
+ for (const [key, schemaValue] of Object.entries(schema)) {
74
+ let baseType;
75
+ let optional;
76
+ try {
77
+ ({ baseType, optional } = splitOptional(schemaValue));
78
+ } catch (e) {
79
+ issues.push({
80
+ key,
81
+ kind: "invalid",
82
+ message: String(e)
83
+ });
84
+ continue;
85
+ }
86
+ const raw = env[key];
87
+ if (raw === void 0 || raw === "") {
88
+ if (optional) {
89
+ out[key] = void 0;
90
+ } else {
91
+ issues.push({
92
+ key,
93
+ kind: "missing",
94
+ message: "missing required environment variable"
95
+ });
96
+ }
97
+ continue;
98
+ }
99
+ try {
100
+ out[key] = parseByType(baseType, raw);
101
+ } catch (e) {
102
+ issues.push({
103
+ key,
104
+ kind: "invalid",
105
+ message: String(e)
106
+ });
107
+ }
108
+ }
109
+ if (issues.length > 0) {
110
+ throw new EnvDoctorError(issues);
111
+ }
112
+ return out;
113
+ }
114
+
115
+ // src/index.ts
116
+ function envDoctor(schema, options = {}) {
117
+ const { loadDotEnv = true, env = process.env } = options;
118
+ if (loadDotEnv) dotenv.config();
119
+ return validateAndParse(schema, env);
120
+ }
121
+
122
+ export {
123
+ EnvDoctorError,
124
+ envDoctor
125
+ };
126
+ //# sourceMappingURL=chunk-U6GH2TRS.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.ts","../src/error.ts","../src/parsers.ts","../src/validator.ts"],"sourcesContent":["import * as dotenv from \"dotenv\";\nimport { validateAndParse } from \"./validator\";\nimport type { EnvDoctorOptions, EnvDoctorResult, EnvDoctorSchema } from \"./types\";\n\nexport type { EnvDoctorOptions, EnvDoctorSchema } from \"./types\";\nexport { EnvDoctorError } from \"./error\";\n\nexport function envDoctor<TSchema extends EnvDoctorSchema>(\n schema: TSchema,\n options: EnvDoctorOptions = {}\n): EnvDoctorResult<TSchema> {\n const { loadDotEnv = true, env = process.env } = options;\n\n if (loadDotEnv) dotenv.config();\n\n return validateAndParse(schema, env);\n}\n","export type EnvDoctorIssue =\n | { key: string; kind: \"missing\"; message: string }\n | { key: string; kind: \"invalid\"; message: string };\n\nexport class EnvDoctorError extends Error {\n public readonly issues: EnvDoctorIssue[];\n\n constructor(issues: EnvDoctorIssue[]) {\n const header = \"ENV validation failed\";\n const lines = issues.map((i) => `- ${i.key}: ${i.message}`);\n super([header, ...lines].join(\"\\n\"));\n this.name = \"EnvDoctorError\";\n this.issues = issues;\n }\n}\n","import type { EnvBaseType } from \"./types\";\n\nexport function parseByType(type: EnvBaseType, raw: string): unknown {\n switch (type) {\n case \"string\":\n return raw;\n\n case \"number\": {\n // Trim to handle \" 3000 \"\n const n = Number(raw.trim());\n if (!Number.isFinite(n)) {\n throw new Error(`expected number, got \"${raw}\"`);\n }\n return n;\n }\n\n case \"boolean\": {\n const v = raw.trim().toLowerCase();\n if ([\"true\", \"1\", \"yes\", \"y\", \"on\"].includes(v)) return true;\n if ([\"false\", \"0\", \"no\", \"n\", \"off\"].includes(v)) return false;\n throw new Error(\n `expected boolean (true/false/1/0/yes/no/on/off), got \"${raw}\"`,\n );\n }\n\n case \"json\": {\n try {\n return JSON.parse(raw);\n } catch {\n throw new Error(`expected json, got \"${raw}\"`);\n }\n }\n\n case \"url\": {\n try {\n // If it doesn't parse as a URL, this throws.\n // We return the original string (common for configs).\n new URL(raw);\n return raw;\n } catch {\n throw new Error(`expected url, got \"${raw}\"`);\n }\n }\n\n /* v8 ignore next -- @preserve */\n default: {\n // Exhaustive check (unreachable at runtime)\n const _never: never = type;\n return _never;\n }\n }\n}\n\nexport function splitOptional(schemaValue: string): {\n baseType: EnvBaseType;\n optional: boolean;\n} {\n const optional = schemaValue.endsWith(\"?\");\n const base = optional ? schemaValue.slice(0, -1) : schemaValue;\n\n // runtime safety\n const allowed = [\"string\", \"number\", \"boolean\", \"json\", \"url\"] as const;\n if (!allowed.includes(base as any)) {\n throw new Error(\n `Unsupported type \"${schemaValue}\". Supported: string, number, boolean, json, url (optional with ?)`,\n );\n }\n\n return { baseType: base as EnvBaseType, optional };\n}\n","import { EnvDoctorError, type EnvDoctorIssue } from \"./error\";\nimport { parseByType, splitOptional } from \"./parsers\";\nimport type { EnvDoctorResult, EnvDoctorSchema } from \"./types\";\n\nexport function validateAndParse<TSchema extends EnvDoctorSchema>(\n schema: TSchema,\n env: Record<string, string | undefined>,\n): EnvDoctorResult<TSchema> {\n const issues: EnvDoctorIssue[] = [];\n const out: Record<string, unknown> = {};\n\n for (const [key, schemaValue] of Object.entries(schema)) {\n let baseType: any;\n let optional: boolean;\n\n try {\n ({ baseType, optional } = splitOptional(schemaValue));\n } catch (e) {\n issues.push({\n key,\n kind: \"invalid\",\n message: String(e),\n });\n continue;\n }\n\n const raw = env[key];\n\n if (raw === undefined || raw === \"\") {\n if (optional) {\n out[key] = undefined;\n } else {\n issues.push({\n key,\n kind: \"missing\",\n message: \"missing required environment variable\",\n });\n }\n continue;\n }\n\n try {\n out[key] = parseByType(baseType, raw);\n } catch (e) {\n issues.push({\n key,\n kind: \"invalid\",\n message: String(e),\n });\n }\n }\n\n if (issues.length > 0) {\n throw new EnvDoctorError(issues);\n }\n\n return out as EnvDoctorResult<TSchema>;\n}\n"],"mappings":"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","names":[]}
package/dist/cli.d.mts ADDED
@@ -0,0 +1,7 @@
1
+ type Io = {
2
+ log: (msg: string) => void;
3
+ error: (msg: string) => void;
4
+ };
5
+ declare function runCli(argv: string[], io?: Io): number;
6
+
7
+ export { runCli };
package/dist/cli.d.ts ADDED
@@ -0,0 +1,7 @@
1
+ type Io = {
2
+ log: (msg: string) => void;
3
+ error: (msg: string) => void;
4
+ };
5
+ declare function runCli(argv: string[], io?: Io): number;
6
+
7
+ export { runCli };
package/dist/cli.js ADDED
@@ -0,0 +1,247 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
28
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
+
30
+ // src/cli.ts
31
+ var cli_exports = {};
32
+ __export(cli_exports, {
33
+ runCli: () => runCli
34
+ });
35
+ module.exports = __toCommonJS(cli_exports);
36
+ var import_node_fs = __toESM(require("fs"));
37
+ var import_node_path = __toESM(require("path"));
38
+ var dotenv2 = __toESM(require("dotenv"));
39
+
40
+ // src/index.ts
41
+ var dotenv = __toESM(require("dotenv"));
42
+
43
+ // src/error.ts
44
+ var EnvDoctorError = class extends Error {
45
+ constructor(issues) {
46
+ const header = "ENV validation failed";
47
+ const lines = issues.map((i) => `- ${i.key}: ${i.message}`);
48
+ super([header, ...lines].join("\n"));
49
+ this.name = "EnvDoctorError";
50
+ this.issues = issues;
51
+ }
52
+ };
53
+
54
+ // src/parsers.ts
55
+ function parseByType(type, raw) {
56
+ switch (type) {
57
+ case "string":
58
+ return raw;
59
+ case "number": {
60
+ const n = Number(raw.trim());
61
+ if (!Number.isFinite(n)) {
62
+ throw new Error(`expected number, got "${raw}"`);
63
+ }
64
+ return n;
65
+ }
66
+ case "boolean": {
67
+ const v = raw.trim().toLowerCase();
68
+ if (["true", "1", "yes", "y", "on"].includes(v)) return true;
69
+ if (["false", "0", "no", "n", "off"].includes(v)) return false;
70
+ throw new Error(
71
+ `expected boolean (true/false/1/0/yes/no/on/off), got "${raw}"`
72
+ );
73
+ }
74
+ case "json": {
75
+ try {
76
+ return JSON.parse(raw);
77
+ } catch {
78
+ throw new Error(`expected json, got "${raw}"`);
79
+ }
80
+ }
81
+ case "url": {
82
+ try {
83
+ new URL(raw);
84
+ return raw;
85
+ } catch {
86
+ throw new Error(`expected url, got "${raw}"`);
87
+ }
88
+ }
89
+ /* v8 ignore next -- @preserve */
90
+ default: {
91
+ const _never = type;
92
+ return _never;
93
+ }
94
+ }
95
+ }
96
+ function splitOptional(schemaValue) {
97
+ const optional = schemaValue.endsWith("?");
98
+ const base = optional ? schemaValue.slice(0, -1) : schemaValue;
99
+ const allowed = ["string", "number", "boolean", "json", "url"];
100
+ if (!allowed.includes(base)) {
101
+ throw new Error(
102
+ `Unsupported type "${schemaValue}". Supported: string, number, boolean, json, url (optional with ?)`
103
+ );
104
+ }
105
+ return { baseType: base, optional };
106
+ }
107
+
108
+ // src/validator.ts
109
+ function validateAndParse(schema, env) {
110
+ const issues = [];
111
+ const out = {};
112
+ for (const [key, schemaValue] of Object.entries(schema)) {
113
+ let baseType;
114
+ let optional;
115
+ try {
116
+ ({ baseType, optional } = splitOptional(schemaValue));
117
+ } catch (e) {
118
+ issues.push({
119
+ key,
120
+ kind: "invalid",
121
+ message: String(e)
122
+ });
123
+ continue;
124
+ }
125
+ const raw = env[key];
126
+ if (raw === void 0 || raw === "") {
127
+ if (optional) {
128
+ out[key] = void 0;
129
+ } else {
130
+ issues.push({
131
+ key,
132
+ kind: "missing",
133
+ message: "missing required environment variable"
134
+ });
135
+ }
136
+ continue;
137
+ }
138
+ try {
139
+ out[key] = parseByType(baseType, raw);
140
+ } catch (e) {
141
+ issues.push({
142
+ key,
143
+ kind: "invalid",
144
+ message: String(e)
145
+ });
146
+ }
147
+ }
148
+ if (issues.length > 0) {
149
+ throw new EnvDoctorError(issues);
150
+ }
151
+ return out;
152
+ }
153
+
154
+ // src/index.ts
155
+ function envDoctor(schema, options = {}) {
156
+ const { loadDotEnv = true, env = process.env } = options;
157
+ if (loadDotEnv) dotenv.config();
158
+ return validateAndParse(schema, env);
159
+ }
160
+
161
+ // src/cli.ts
162
+ function parseArgs(argv) {
163
+ const out = { useDotenv: true };
164
+ const [cmd, ...rest] = argv;
165
+ out.cmd = cmd;
166
+ for (let i = 0; i < rest.length; i++) {
167
+ const a = rest[i];
168
+ if (a === "--schema") {
169
+ out.schemaPath = rest[++i];
170
+ } else if (a.startsWith("--schema=")) {
171
+ out.schemaPath = a.split("=", 2)[1];
172
+ } else if (a === "--env-file") {
173
+ out.envFile = rest[++i];
174
+ } else if (a.startsWith("--env-file=")) {
175
+ out.envFile = a.split("=", 2)[1];
176
+ } else if (a === "--no-dotenv") {
177
+ out.useDotenv = false;
178
+ }
179
+ }
180
+ return out;
181
+ }
182
+ function loadSchema(schemaPath) {
183
+ const abs = import_node_path.default.resolve(process.cwd(), schemaPath);
184
+ const raw = import_node_fs.default.readFileSync(abs, "utf8");
185
+ const json = JSON.parse(raw);
186
+ if (!json || typeof json !== "object" || Array.isArray(json)) {
187
+ throw new Error("Schema must be a JSON object of key -> type.");
188
+ }
189
+ for (const [k, v] of Object.entries(json)) {
190
+ if (typeof k !== "string" || typeof v !== "string") {
191
+ throw new Error("Schema must be a JSON object of string -> string.");
192
+ }
193
+ }
194
+ return json;
195
+ }
196
+ function runCli(argv, io = console) {
197
+ const { cmd, schemaPath, envFile, useDotenv } = parseArgs(argv);
198
+ if (!cmd || cmd === "help" || cmd === "--help" || cmd === "-h") {
199
+ io.log(
200
+ [
201
+ "env-typed-checker",
202
+ "",
203
+ "Usage:",
204
+ " env-typed-checker check --schema <file> [--env-file <file>] [--no-dotenv]",
205
+ "",
206
+ "Options:",
207
+ " --schema <file> Path to schema JSON (required)",
208
+ " --env-file <file> Env file path (default: .env)",
209
+ " --no-dotenv Do not load env file; use process.env only",
210
+ "",
211
+ "Exit codes:",
212
+ " 0 = OK, 1 = validation failed, 2 = CLI error"
213
+ ].join("\n")
214
+ );
215
+ return 0;
216
+ }
217
+ if (cmd !== "check") {
218
+ io.error(`Unknown command: ${cmd}`);
219
+ return 2;
220
+ }
221
+ if (!schemaPath) {
222
+ io.error("Missing required option: --schema <file>");
223
+ return 2;
224
+ }
225
+ try {
226
+ if (useDotenv) {
227
+ const p = envFile ?? ".env";
228
+ dotenv2.config({ path: import_node_path.default.resolve(process.cwd(), p) });
229
+ }
230
+ const schema = loadSchema(schemaPath);
231
+ envDoctor(schema, { loadDotEnv: false, env: process.env });
232
+ io.log("\u2705 Environment is valid.");
233
+ return 0;
234
+ } catch (e) {
235
+ if (e instanceof EnvDoctorError) {
236
+ io.error(e.message);
237
+ return 1;
238
+ }
239
+ io.error(String(e));
240
+ return 2;
241
+ }
242
+ }
243
+ // Annotate the CommonJS export names for ESM import in node:
244
+ 0 && (module.exports = {
245
+ runCli
246
+ });
247
+ //# sourceMappingURL=cli.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/cli.ts","../src/index.ts","../src/error.ts","../src/parsers.ts","../src/validator.ts"],"sourcesContent":["import fs from \"node:fs\";\nimport path from \"node:path\";\nimport * as dotenv from \"dotenv\";\nimport { envDoctor, EnvDoctorError } from \"./index\";\nimport type { EnvDoctorSchema } from \"./types\";\n\ntype Io = {\n log: (msg: string) => void;\n error: (msg: string) => void;\n};\n\nfunction parseArgs(argv: string[]) {\n const out: {\n cmd?: string;\n schemaPath?: string;\n envFile?: string;\n useDotenv: boolean;\n } = { useDotenv: true };\n\n const [cmd, ...rest] = argv;\n out.cmd = cmd;\n\n for (let i = 0; i < rest.length; i++) {\n const a = rest[i];\n\n if (a === \"--schema\") {\n out.schemaPath = rest[++i];\n } else if (a.startsWith(\"--schema=\")) {\n out.schemaPath = a.split(\"=\", 2)[1];\n } else if (a === \"--env-file\") {\n out.envFile = rest[++i];\n } else if (a.startsWith(\"--env-file=\")) {\n out.envFile = a.split(\"=\", 2)[1];\n } else if (a === \"--no-dotenv\") {\n out.useDotenv = false;\n }\n }\n\n return out;\n}\n\nfunction loadSchema(schemaPath: string): EnvDoctorSchema {\n const abs = path.resolve(process.cwd(), schemaPath);\n const raw = fs.readFileSync(abs, \"utf8\");\n const json = JSON.parse(raw);\n\n if (!json || typeof json !== \"object\" || Array.isArray(json)) {\n throw new Error(\"Schema must be a JSON object of key -> type.\");\n }\n\n // Ensure values are strings\n for (const [k, v] of Object.entries(json)) {\n if (typeof k !== \"string\" || typeof v !== \"string\") {\n throw new Error(\"Schema must be a JSON object of string -> string.\");\n }\n }\n\n return json as EnvDoctorSchema;\n}\n\nexport function runCli(argv: string[], io: Io = console): number {\n const { cmd, schemaPath, envFile, useDotenv } = parseArgs(argv);\n\n if (!cmd || cmd === \"help\" || cmd === \"--help\" || cmd === \"-h\") {\n io.log(\n [\n \"env-typed-checker\",\n \"\",\n \"Usage:\",\n \" env-typed-checker check --schema <file> [--env-file <file>] [--no-dotenv]\",\n \"\",\n \"Options:\",\n \" --schema <file> Path to schema JSON (required)\",\n \" --env-file <file> Env file path (default: .env)\",\n \" --no-dotenv Do not load env file; use process.env only\",\n \"\",\n \"Exit codes:\",\n \" 0 = OK, 1 = validation failed, 2 = CLI error\",\n ].join(\"\\n\"),\n );\n return 0;\n }\n\n if (cmd !== \"check\") {\n io.error(`Unknown command: ${cmd}`);\n return 2;\n }\n\n if (!schemaPath) {\n io.error(\"Missing required option: --schema <file>\");\n return 2;\n }\n\n try {\n if (useDotenv) {\n const p = envFile ?? \".env\";\n dotenv.config({ path: path.resolve(process.cwd(), p) });\n }\n\n const schema = loadSchema(schemaPath);\n\n // envDoctor will validate and throw EnvDoctorError if invalid\n envDoctor(schema, { loadDotEnv: false, env: process.env });\n\n io.log(\"โœ… Environment is valid.\");\n return 0;\n } catch (e) {\n if (e instanceof EnvDoctorError) {\n io.error(e.message);\n return 1;\n }\n io.error(String(e));\n return 2;\n }\n}\n","import * as dotenv from \"dotenv\";\nimport { validateAndParse } from \"./validator\";\nimport type { EnvDoctorOptions, EnvDoctorResult, EnvDoctorSchema } from \"./types\";\n\nexport type { EnvDoctorOptions, EnvDoctorSchema } from \"./types\";\nexport { EnvDoctorError } from \"./error\";\n\nexport function envDoctor<TSchema extends EnvDoctorSchema>(\n schema: TSchema,\n options: EnvDoctorOptions = {}\n): EnvDoctorResult<TSchema> {\n const { loadDotEnv = true, env = process.env } = options;\n\n if (loadDotEnv) dotenv.config();\n\n return validateAndParse(schema, env);\n}\n","export type EnvDoctorIssue =\n | { key: string; kind: \"missing\"; message: string }\n | { key: string; kind: \"invalid\"; message: string };\n\nexport class EnvDoctorError extends Error {\n public readonly issues: EnvDoctorIssue[];\n\n constructor(issues: EnvDoctorIssue[]) {\n const header = \"ENV validation failed\";\n const lines = issues.map((i) => `- ${i.key}: ${i.message}`);\n super([header, ...lines].join(\"\\n\"));\n this.name = \"EnvDoctorError\";\n this.issues = issues;\n }\n}\n","import type { EnvBaseType } from \"./types\";\n\nexport function parseByType(type: EnvBaseType, raw: string): unknown {\n switch (type) {\n case \"string\":\n return raw;\n\n case \"number\": {\n // Trim to handle \" 3000 \"\n const n = Number(raw.trim());\n if (!Number.isFinite(n)) {\n throw new Error(`expected number, got \"${raw}\"`);\n }\n return n;\n }\n\n case \"boolean\": {\n const v = raw.trim().toLowerCase();\n if ([\"true\", \"1\", \"yes\", \"y\", \"on\"].includes(v)) return true;\n if ([\"false\", \"0\", \"no\", \"n\", \"off\"].includes(v)) return false;\n throw new Error(\n `expected boolean (true/false/1/0/yes/no/on/off), got \"${raw}\"`,\n );\n }\n\n case \"json\": {\n try {\n return JSON.parse(raw);\n } catch {\n throw new Error(`expected json, got \"${raw}\"`);\n }\n }\n\n case \"url\": {\n try {\n // If it doesn't parse as a URL, this throws.\n // We return the original string (common for configs).\n new URL(raw);\n return raw;\n } catch {\n throw new Error(`expected url, got \"${raw}\"`);\n }\n }\n\n /* v8 ignore next -- @preserve */\n default: {\n // Exhaustive check (unreachable at runtime)\n const _never: never = type;\n return _never;\n }\n }\n}\n\nexport function splitOptional(schemaValue: string): {\n baseType: EnvBaseType;\n optional: boolean;\n} {\n const optional = schemaValue.endsWith(\"?\");\n const base = optional ? schemaValue.slice(0, -1) : schemaValue;\n\n // runtime safety\n const allowed = [\"string\", \"number\", \"boolean\", \"json\", \"url\"] as const;\n if (!allowed.includes(base as any)) {\n throw new Error(\n `Unsupported type \"${schemaValue}\". Supported: string, number, boolean, json, url (optional with ?)`,\n );\n }\n\n return { baseType: base as EnvBaseType, optional };\n}\n","import { EnvDoctorError, type EnvDoctorIssue } from \"./error\";\nimport { parseByType, splitOptional } from \"./parsers\";\nimport type { EnvDoctorResult, EnvDoctorSchema } from \"./types\";\n\nexport function validateAndParse<TSchema extends EnvDoctorSchema>(\n schema: TSchema,\n env: Record<string, string | undefined>,\n): EnvDoctorResult<TSchema> {\n const issues: EnvDoctorIssue[] = [];\n const out: Record<string, unknown> = {};\n\n for (const [key, schemaValue] of Object.entries(schema)) {\n let baseType: any;\n let optional: boolean;\n\n try {\n ({ baseType, optional } = splitOptional(schemaValue));\n } catch (e) {\n issues.push({\n key,\n kind: \"invalid\",\n message: String(e),\n });\n continue;\n }\n\n const raw = env[key];\n\n if (raw === undefined || raw === \"\") {\n if (optional) {\n out[key] = undefined;\n } else {\n issues.push({\n key,\n kind: \"missing\",\n message: \"missing required environment variable\",\n });\n }\n continue;\n }\n\n try {\n out[key] = parseByType(baseType, raw);\n } catch (e) {\n issues.push({\n key,\n kind: \"invalid\",\n message: String(e),\n });\n }\n }\n\n if (issues.length > 0) {\n throw new EnvDoctorError(issues);\n }\n\n return out as EnvDoctorResult<TSchema>;\n}\n"],"mappings":"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","names":["dotenv","path","fs"]}
package/dist/cli.mjs ADDED
@@ -0,0 +1,94 @@
1
+ import {
2
+ EnvDoctorError,
3
+ envDoctor
4
+ } from "./chunk-U6GH2TRS.mjs";
5
+
6
+ // src/cli.ts
7
+ import fs from "fs";
8
+ import path from "path";
9
+ import * as dotenv from "dotenv";
10
+ function parseArgs(argv) {
11
+ const out = { useDotenv: true };
12
+ const [cmd, ...rest] = argv;
13
+ out.cmd = cmd;
14
+ for (let i = 0; i < rest.length; i++) {
15
+ const a = rest[i];
16
+ if (a === "--schema") {
17
+ out.schemaPath = rest[++i];
18
+ } else if (a.startsWith("--schema=")) {
19
+ out.schemaPath = a.split("=", 2)[1];
20
+ } else if (a === "--env-file") {
21
+ out.envFile = rest[++i];
22
+ } else if (a.startsWith("--env-file=")) {
23
+ out.envFile = a.split("=", 2)[1];
24
+ } else if (a === "--no-dotenv") {
25
+ out.useDotenv = false;
26
+ }
27
+ }
28
+ return out;
29
+ }
30
+ function loadSchema(schemaPath) {
31
+ const abs = path.resolve(process.cwd(), schemaPath);
32
+ const raw = fs.readFileSync(abs, "utf8");
33
+ const json = JSON.parse(raw);
34
+ if (!json || typeof json !== "object" || Array.isArray(json)) {
35
+ throw new Error("Schema must be a JSON object of key -> type.");
36
+ }
37
+ for (const [k, v] of Object.entries(json)) {
38
+ if (typeof k !== "string" || typeof v !== "string") {
39
+ throw new Error("Schema must be a JSON object of string -> string.");
40
+ }
41
+ }
42
+ return json;
43
+ }
44
+ function runCli(argv, io = console) {
45
+ const { cmd, schemaPath, envFile, useDotenv } = parseArgs(argv);
46
+ if (!cmd || cmd === "help" || cmd === "--help" || cmd === "-h") {
47
+ io.log(
48
+ [
49
+ "env-typed-checker",
50
+ "",
51
+ "Usage:",
52
+ " env-typed-checker check --schema <file> [--env-file <file>] [--no-dotenv]",
53
+ "",
54
+ "Options:",
55
+ " --schema <file> Path to schema JSON (required)",
56
+ " --env-file <file> Env file path (default: .env)",
57
+ " --no-dotenv Do not load env file; use process.env only",
58
+ "",
59
+ "Exit codes:",
60
+ " 0 = OK, 1 = validation failed, 2 = CLI error"
61
+ ].join("\n")
62
+ );
63
+ return 0;
64
+ }
65
+ if (cmd !== "check") {
66
+ io.error(`Unknown command: ${cmd}`);
67
+ return 2;
68
+ }
69
+ if (!schemaPath) {
70
+ io.error("Missing required option: --schema <file>");
71
+ return 2;
72
+ }
73
+ try {
74
+ if (useDotenv) {
75
+ const p = envFile ?? ".env";
76
+ dotenv.config({ path: path.resolve(process.cwd(), p) });
77
+ }
78
+ const schema = loadSchema(schemaPath);
79
+ envDoctor(schema, { loadDotEnv: false, env: process.env });
80
+ io.log("\u2705 Environment is valid.");
81
+ return 0;
82
+ } catch (e) {
83
+ if (e instanceof EnvDoctorError) {
84
+ io.error(e.message);
85
+ return 1;
86
+ }
87
+ io.error(String(e));
88
+ return 2;
89
+ }
90
+ }
91
+ export {
92
+ runCli
93
+ };
94
+ //# sourceMappingURL=cli.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/cli.ts"],"sourcesContent":["import fs from \"node:fs\";\nimport path from \"node:path\";\nimport * as dotenv from \"dotenv\";\nimport { envDoctor, EnvDoctorError } from \"./index\";\nimport type { EnvDoctorSchema } from \"./types\";\n\ntype Io = {\n log: (msg: string) => void;\n error: (msg: string) => void;\n};\n\nfunction parseArgs(argv: string[]) {\n const out: {\n cmd?: string;\n schemaPath?: string;\n envFile?: string;\n useDotenv: boolean;\n } = { useDotenv: true };\n\n const [cmd, ...rest] = argv;\n out.cmd = cmd;\n\n for (let i = 0; i < rest.length; i++) {\n const a = rest[i];\n\n if (a === \"--schema\") {\n out.schemaPath = rest[++i];\n } else if (a.startsWith(\"--schema=\")) {\n out.schemaPath = a.split(\"=\", 2)[1];\n } else if (a === \"--env-file\") {\n out.envFile = rest[++i];\n } else if (a.startsWith(\"--env-file=\")) {\n out.envFile = a.split(\"=\", 2)[1];\n } else if (a === \"--no-dotenv\") {\n out.useDotenv = false;\n }\n }\n\n return out;\n}\n\nfunction loadSchema(schemaPath: string): EnvDoctorSchema {\n const abs = path.resolve(process.cwd(), schemaPath);\n const raw = fs.readFileSync(abs, \"utf8\");\n const json = JSON.parse(raw);\n\n if (!json || typeof json !== \"object\" || Array.isArray(json)) {\n throw new Error(\"Schema must be a JSON object of key -> type.\");\n }\n\n // Ensure values are strings\n for (const [k, v] of Object.entries(json)) {\n if (typeof k !== \"string\" || typeof v !== \"string\") {\n throw new Error(\"Schema must be a JSON object of string -> string.\");\n }\n }\n\n return json as EnvDoctorSchema;\n}\n\nexport function runCli(argv: string[], io: Io = console): number {\n const { cmd, schemaPath, envFile, useDotenv } = parseArgs(argv);\n\n if (!cmd || cmd === \"help\" || cmd === \"--help\" || cmd === \"-h\") {\n io.log(\n [\n \"env-typed-checker\",\n \"\",\n \"Usage:\",\n \" env-typed-checker check --schema <file> [--env-file <file>] [--no-dotenv]\",\n \"\",\n \"Options:\",\n \" --schema <file> Path to schema JSON (required)\",\n \" --env-file <file> Env file path (default: .env)\",\n \" --no-dotenv Do not load env file; use process.env only\",\n \"\",\n \"Exit codes:\",\n \" 0 = OK, 1 = validation failed, 2 = CLI error\",\n ].join(\"\\n\"),\n );\n return 0;\n }\n\n if (cmd !== \"check\") {\n io.error(`Unknown command: ${cmd}`);\n return 2;\n }\n\n if (!schemaPath) {\n io.error(\"Missing required option: --schema <file>\");\n return 2;\n }\n\n try {\n if (useDotenv) {\n const p = envFile ?? \".env\";\n dotenv.config({ path: path.resolve(process.cwd(), p) });\n }\n\n const schema = loadSchema(schemaPath);\n\n // envDoctor will validate and throw EnvDoctorError if invalid\n envDoctor(schema, { loadDotEnv: false, env: process.env });\n\n io.log(\"โœ… Environment is valid.\");\n return 0;\n } catch (e) {\n if (e instanceof EnvDoctorError) {\n io.error(e.message);\n return 1;\n }\n io.error(String(e));\n return 2;\n }\n}\n"],"mappings":";;;;;;AAAA,OAAO,QAAQ;AACf,OAAO,UAAU;AACjB,YAAY,YAAY;AASxB,SAAS,UAAU,MAAgB;AACjC,QAAM,MAKF,EAAE,WAAW,KAAK;AAEtB,QAAM,CAAC,KAAK,GAAG,IAAI,IAAI;AACvB,MAAI,MAAM;AAEV,WAAS,IAAI,GAAG,IAAI,KAAK,QAAQ,KAAK;AACpC,UAAM,IAAI,KAAK,CAAC;AAEhB,QAAI,MAAM,YAAY;AACpB,UAAI,aAAa,KAAK,EAAE,CAAC;AAAA,IAC3B,WAAW,EAAE,WAAW,WAAW,GAAG;AACpC,UAAI,aAAa,EAAE,MAAM,KAAK,CAAC,EAAE,CAAC;AAAA,IACpC,WAAW,MAAM,cAAc;AAC7B,UAAI,UAAU,KAAK,EAAE,CAAC;AAAA,IACxB,WAAW,EAAE,WAAW,aAAa,GAAG;AACtC,UAAI,UAAU,EAAE,MAAM,KAAK,CAAC,EAAE,CAAC;AAAA,IACjC,WAAW,MAAM,eAAe;AAC9B,UAAI,YAAY;AAAA,IAClB;AAAA,EACF;AAEA,SAAO;AACT;AAEA,SAAS,WAAW,YAAqC;AACvD,QAAM,MAAM,KAAK,QAAQ,QAAQ,IAAI,GAAG,UAAU;AAClD,QAAM,MAAM,GAAG,aAAa,KAAK,MAAM;AACvC,QAAM,OAAO,KAAK,MAAM,GAAG;AAE3B,MAAI,CAAC,QAAQ,OAAO,SAAS,YAAY,MAAM,QAAQ,IAAI,GAAG;AAC5D,UAAM,IAAI,MAAM,8CAA8C;AAAA,EAChE;AAGA,aAAW,CAAC,GAAG,CAAC,KAAK,OAAO,QAAQ,IAAI,GAAG;AACzC,QAAI,OAAO,MAAM,YAAY,OAAO,MAAM,UAAU;AAClD,YAAM,IAAI,MAAM,mDAAmD;AAAA,IACrE;AAAA,EACF;AAEA,SAAO;AACT;AAEO,SAAS,OAAO,MAAgB,KAAS,SAAiB;AAC/D,QAAM,EAAE,KAAK,YAAY,SAAS,UAAU,IAAI,UAAU,IAAI;AAE9D,MAAI,CAAC,OAAO,QAAQ,UAAU,QAAQ,YAAY,QAAQ,MAAM;AAC9D,OAAG;AAAA,MACD;AAAA,QACE;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MACF,EAAE,KAAK,IAAI;AAAA,IACb;AACA,WAAO;AAAA,EACT;AAEA,MAAI,QAAQ,SAAS;AACnB,OAAG,MAAM,oBAAoB,GAAG,EAAE;AAClC,WAAO;AAAA,EACT;AAEA,MAAI,CAAC,YAAY;AACf,OAAG,MAAM,0CAA0C;AACnD,WAAO;AAAA,EACT;AAEA,MAAI;AACF,QAAI,WAAW;AACb,YAAM,IAAI,WAAW;AACrB,MAAO,cAAO,EAAE,MAAM,KAAK,QAAQ,QAAQ,IAAI,GAAG,CAAC,EAAE,CAAC;AAAA,IACxD;AAEA,UAAM,SAAS,WAAW,UAAU;AAGpC,cAAU,QAAQ,EAAE,YAAY,OAAO,KAAK,QAAQ,IAAI,CAAC;AAEzD,OAAG,IAAI,8BAAyB;AAChC,WAAO;AAAA,EACT,SAAS,GAAG;AACV,QAAI,aAAa,gBAAgB;AAC/B,SAAG,MAAM,EAAE,OAAO;AAClB,aAAO;AAAA,IACT;AACA,OAAG,MAAM,OAAO,CAAC,CAAC;AAClB,WAAO;AAAA,EACT;AACF;","names":[]}
package/dist/index.mjs CHANGED
@@ -1,123 +1,7 @@
1
- // src/index.ts
2
- import * as dotenv from "dotenv";
3
-
4
- // src/error.ts
5
- var EnvDoctorError = class extends Error {
6
- constructor(issues) {
7
- const header = "ENV validation failed";
8
- const lines = issues.map((i) => `- ${i.key}: ${i.message}`);
9
- super([header, ...lines].join("\n"));
10
- this.name = "EnvDoctorError";
11
- this.issues = issues;
12
- }
13
- };
14
-
15
- // src/parsers.ts
16
- function parseByType(type, raw) {
17
- switch (type) {
18
- case "string":
19
- return raw;
20
- case "number": {
21
- const n = Number(raw.trim());
22
- if (!Number.isFinite(n)) {
23
- throw new Error(`expected number, got "${raw}"`);
24
- }
25
- return n;
26
- }
27
- case "boolean": {
28
- const v = raw.trim().toLowerCase();
29
- if (["true", "1", "yes", "y", "on"].includes(v)) return true;
30
- if (["false", "0", "no", "n", "off"].includes(v)) return false;
31
- throw new Error(
32
- `expected boolean (true/false/1/0/yes/no/on/off), got "${raw}"`
33
- );
34
- }
35
- case "json": {
36
- try {
37
- return JSON.parse(raw);
38
- } catch {
39
- throw new Error(`expected json, got "${raw}"`);
40
- }
41
- }
42
- case "url": {
43
- try {
44
- new URL(raw);
45
- return raw;
46
- } catch {
47
- throw new Error(`expected url, got "${raw}"`);
48
- }
49
- }
50
- /* v8 ignore next -- @preserve */
51
- default: {
52
- const _never = type;
53
- return _never;
54
- }
55
- }
56
- }
57
- function splitOptional(schemaValue) {
58
- const optional = schemaValue.endsWith("?");
59
- const base = optional ? schemaValue.slice(0, -1) : schemaValue;
60
- const allowed = ["string", "number", "boolean", "json", "url"];
61
- if (!allowed.includes(base)) {
62
- throw new Error(
63
- `Unsupported type "${schemaValue}". Supported: string, number, boolean, json, url (optional with ?)`
64
- );
65
- }
66
- return { baseType: base, optional };
67
- }
68
-
69
- // src/validator.ts
70
- function validateAndParse(schema, env) {
71
- const issues = [];
72
- const out = {};
73
- for (const [key, schemaValue] of Object.entries(schema)) {
74
- let baseType;
75
- let optional;
76
- try {
77
- ({ baseType, optional } = splitOptional(schemaValue));
78
- } catch (e) {
79
- issues.push({
80
- key,
81
- kind: "invalid",
82
- message: String(e)
83
- });
84
- continue;
85
- }
86
- const raw = env[key];
87
- if (raw === void 0 || raw === "") {
88
- if (optional) {
89
- out[key] = void 0;
90
- } else {
91
- issues.push({
92
- key,
93
- kind: "missing",
94
- message: "missing required environment variable"
95
- });
96
- }
97
- continue;
98
- }
99
- try {
100
- out[key] = parseByType(baseType, raw);
101
- } catch (e) {
102
- issues.push({
103
- key,
104
- kind: "invalid",
105
- message: String(e)
106
- });
107
- }
108
- }
109
- if (issues.length > 0) {
110
- throw new EnvDoctorError(issues);
111
- }
112
- return out;
113
- }
114
-
115
- // src/index.ts
116
- function envDoctor(schema, options = {}) {
117
- const { loadDotEnv = true, env = process.env } = options;
118
- if (loadDotEnv) dotenv.config();
119
- return validateAndParse(schema, env);
120
- }
1
+ import {
2
+ EnvDoctorError,
3
+ envDoctor
4
+ } from "./chunk-U6GH2TRS.mjs";
121
5
  export {
122
6
  EnvDoctorError,
123
7
  envDoctor
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/error.ts","../src/parsers.ts","../src/validator.ts"],"sourcesContent":["import * as dotenv from \"dotenv\";\nimport { validateAndParse } from \"./validator\";\nimport type { EnvDoctorOptions, EnvDoctorResult, EnvDoctorSchema } from \"./types\";\n\nexport type { EnvDoctorOptions, EnvDoctorSchema } from \"./types\";\nexport { EnvDoctorError } from \"./error\";\n\nexport function envDoctor<TSchema extends EnvDoctorSchema>(\n schema: TSchema,\n options: EnvDoctorOptions = {}\n): EnvDoctorResult<TSchema> {\n const { loadDotEnv = true, env = process.env } = options;\n\n if (loadDotEnv) dotenv.config();\n\n return validateAndParse(schema, env);\n}\n","export type EnvDoctorIssue =\n | { key: string; kind: \"missing\"; message: string }\n | { key: string; kind: \"invalid\"; message: string };\n\nexport class EnvDoctorError extends Error {\n public readonly issues: EnvDoctorIssue[];\n\n constructor(issues: EnvDoctorIssue[]) {\n const header = \"ENV validation failed\";\n const lines = issues.map((i) => `- ${i.key}: ${i.message}`);\n super([header, ...lines].join(\"\\n\"));\n this.name = \"EnvDoctorError\";\n this.issues = issues;\n }\n}\n","import type { EnvBaseType } from \"./types\";\n\nexport function parseByType(type: EnvBaseType, raw: string): unknown {\n switch (type) {\n case \"string\":\n return raw;\n\n case \"number\": {\n // Trim to handle \" 3000 \"\n const n = Number(raw.trim());\n if (!Number.isFinite(n)) {\n throw new Error(`expected number, got \"${raw}\"`);\n }\n return n;\n }\n\n case \"boolean\": {\n const v = raw.trim().toLowerCase();\n if ([\"true\", \"1\", \"yes\", \"y\", \"on\"].includes(v)) return true;\n if ([\"false\", \"0\", \"no\", \"n\", \"off\"].includes(v)) return false;\n throw new Error(\n `expected boolean (true/false/1/0/yes/no/on/off), got \"${raw}\"`,\n );\n }\n\n case \"json\": {\n try {\n return JSON.parse(raw);\n } catch {\n throw new Error(`expected json, got \"${raw}\"`);\n }\n }\n\n case \"url\": {\n try {\n // If it doesn't parse as a URL, this throws.\n // We return the original string (common for configs).\n new URL(raw);\n return raw;\n } catch {\n throw new Error(`expected url, got \"${raw}\"`);\n }\n }\n\n /* v8 ignore next -- @preserve */\n default: {\n // Exhaustive check (unreachable at runtime)\n const _never: never = type;\n return _never;\n }\n }\n}\n\nexport function splitOptional(schemaValue: string): {\n baseType: EnvBaseType;\n optional: boolean;\n} {\n const optional = schemaValue.endsWith(\"?\");\n const base = optional ? schemaValue.slice(0, -1) : schemaValue;\n\n // runtime safety\n const allowed = [\"string\", \"number\", \"boolean\", \"json\", \"url\"] as const;\n if (!allowed.includes(base as any)) {\n throw new Error(\n `Unsupported type \"${schemaValue}\". Supported: string, number, boolean, json, url (optional with ?)`,\n );\n }\n\n return { baseType: base as EnvBaseType, optional };\n}\n","import { EnvDoctorError, type EnvDoctorIssue } from \"./error\";\nimport { parseByType, splitOptional } from \"./parsers\";\nimport type { EnvDoctorResult, EnvDoctorSchema } from \"./types\";\n\nexport function validateAndParse<TSchema extends EnvDoctorSchema>(\n schema: TSchema,\n env: Record<string, string | undefined>,\n): EnvDoctorResult<TSchema> {\n const issues: EnvDoctorIssue[] = [];\n const out: Record<string, unknown> = {};\n\n for (const [key, schemaValue] of Object.entries(schema)) {\n let baseType: any;\n let optional: boolean;\n\n try {\n ({ baseType, optional } = splitOptional(schemaValue));\n } catch (e) {\n issues.push({\n key,\n kind: \"invalid\",\n message: String(e),\n });\n continue;\n }\n\n const raw = env[key];\n\n if (raw === undefined || raw === \"\") {\n if (optional) {\n out[key] = undefined;\n } else {\n issues.push({\n key,\n kind: \"missing\",\n message: \"missing required environment variable\",\n });\n }\n continue;\n }\n\n try {\n out[key] = parseByType(baseType, raw);\n } catch (e) {\n issues.push({\n key,\n kind: \"invalid\",\n message: String(e),\n });\n }\n }\n\n if (issues.length > 0) {\n throw new EnvDoctorError(issues);\n }\n\n return out as EnvDoctorResult<TSchema>;\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "env-typed-checker",
3
- "version": "0.1.1",
3
+ "version": "0.2.0",
4
4
  "description": "Validate and parse environment variables with a tiny, type-safe schema.",
5
5
  "license": "MIT",
6
6
  "type": "commonjs",
@@ -8,6 +8,9 @@
8
8
  "main": "dist/index.js",
9
9
  "module": "dist/index.mjs",
10
10
  "types": "dist/index.d.ts",
11
+ "bin": {
12
+ "env-typed-checker": "./bin/env-typed-checker.cjs"
13
+ },
11
14
  "exports": {
12
15
  ".": {
13
16
  "types": "./dist/index.d.ts",
@@ -17,6 +20,7 @@
17
20
  },
18
21
  "files": [
19
22
  "dist",
23
+ "bin",
20
24
  "README.md",
21
25
  "LICENSE"
22
26
  ],