dotenv-gad 0.1.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 ADDED
@@ -0,0 +1,202 @@
1
+ # dotenv-gad
2
+
3
+ [![npm version](https://badge.fury.io/js/dotenv-gad.svg)](https://badge.fury.io/js/dotenv-guard)
4
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
5
+
6
+ **dotenv-gad** is an environment variable validation tool that brings type safety and schema validation to your Node.js and JavaScript applications. It extends `dotenv` with features like:
7
+
8
+ - Type-safe environment variables
9
+ - Schema validation
10
+ - Automatic documentation generation
11
+ - TypeScript support
12
+ - CLI tooling
13
+ - Secret management
14
+
15
+ ## Installation
16
+
17
+ ```bash
18
+ npm install dotenv-gad
19
+ # or
20
+ yarn add dotenv-gad
21
+ # or
22
+ pnpm add dotenv-gad
23
+ ```
24
+
25
+ ## Quick Start
26
+
27
+ 1. Create a schema file (`env.schema.ts`):
28
+
29
+ ```typescript
30
+ import { defineSchema } from "dotenv-gad";
31
+
32
+ export default defineSchema({
33
+ PORT: {
34
+ type: "number",
35
+ default: 3000,
36
+ docs: "Port to run the server on",
37
+ },
38
+ DATABASE_URL: {
39
+ type: "string",
40
+ required: true,
41
+ sensitive: true,
42
+ },
43
+ });
44
+ ```
45
+
46
+ 2. Validate your environment:
47
+
48
+ ```typescript
49
+ import { loadEnv } from "dotenv-gad";
50
+ import schema from "./env.schema";
51
+
52
+ const env = loadEnv(schema);
53
+ console.log(`Server running on port ${env.PORT}`);
54
+ ```
55
+
56
+ ## CLI Commands
57
+
58
+ | Command | Description |
59
+ | ------- | ---------------------------------- |
60
+ | `check` | Validate .env against schema |
61
+ | `sync` | Generate/update .env.example |
62
+ | `types` | Generate env.d.ts TypeScript types |
63
+ | `init` | Create starter schema |
64
+
65
+ ```bash
66
+ npx dotenv-gad check
67
+ npx dotenv-gad types
68
+ ```
69
+
70
+ ## Features
71
+
72
+ ### Core Validation
73
+
74
+ - Type checking (string, number, boolean, array, object)
75
+ - Required/optional fields
76
+ - Default values
77
+ - Custom validation functions
78
+ - Environment-specific rules
79
+
80
+ ### Advanced Types
81
+
82
+ ```typescript
83
+ {
84
+ API_URL: { type: 'url' },
85
+ EMAIL: { type: 'email' },
86
+ CONFIG: { type: 'json' },
87
+ TAGS: {
88
+ type: 'array',
89
+ items: { type: 'string' }
90
+ }
91
+ }
92
+ ```
93
+
94
+ ### CLI Features
95
+
96
+ - Color-coded output
97
+ - Interactive fixes
98
+ - Strict mode
99
+ - Custom schema paths
100
+ - CI/CD friendly (comming soon!)
101
+
102
+ ### Secret Management
103
+
104
+ ```typescript
105
+ {
106
+ API_KEY: {
107
+ type: 'string',
108
+ sensitive: true, // Excluded from .env.example
109
+ validate: (val) => val.startsWith('sk_')
110
+ }
111
+ }
112
+ ```
113
+
114
+ ## Framework Integrations
115
+
116
+ ### Express.js
117
+
118
+ ```typescript
119
+ import express from "express";
120
+ import { loadEnv } from "dotenv-gad";
121
+ import schema from "./env.schema";
122
+
123
+ const env = loadEnv(schema);
124
+ const app = express();
125
+
126
+ app.listen(env.PORT, () => {
127
+ console.log(`Server running on port ${env.PORT}`);
128
+ });
129
+ ```
130
+
131
+ ### Next.js
132
+
133
+ Create `next.config.js`:
134
+
135
+ ```javascript
136
+ const { loadEnv } = require("dotenv-gad");
137
+ const schema = require("./env.schema");
138
+
139
+ const env = loadEnv(schema);
140
+
141
+ module.exports = {
142
+ env: {
143
+ API_URL: env.API_URL,
144
+ },
145
+ };
146
+ ```
147
+
148
+ ## Validation Reports
149
+
150
+ Example error output:
151
+
152
+ ```
153
+ Environment validation failed:
154
+ - DATABASE_URL: Missing required environment variable
155
+ - PORT: Must be a number (received: "abc")
156
+ - API_KEY: Must start with 'sk_' (received: "invalid")
157
+ ```
158
+
159
+ ## more usages
160
+
161
+ ### Environment-Specific Rules
162
+
163
+ ```typescript
164
+ {
165
+ DEBUG: {
166
+ type: 'boolean',
167
+ env: {
168
+ development: { default: true },
169
+ production: { default: false }
170
+ }
171
+ }
172
+ }
173
+ ```
174
+
175
+ ### Custom Validators
176
+
177
+ ```typescript
178
+ {
179
+ PASSWORD: {
180
+ type: 'string',
181
+ validate: (val) => val.length >= 8,
182
+ error: 'Password must be at least 8 characters'
183
+ }
184
+ }
185
+ ```
186
+
187
+ ### Transformations
188
+
189
+ ```typescript
190
+ {
191
+ FEATURES: {
192
+ type: 'array',
193
+ transform: (val) => val.split(',')
194
+ }
195
+ }
196
+ ```
197
+
198
+ ## 📜 License
199
+
200
+ MIT © [Kasim Lyee]
201
+
202
+ Contributions are welcome!!
@@ -0,0 +1,41 @@
1
+ import { Command } from "commander";
2
+ import chalk from "chalk";
3
+ import ora from "ora";
4
+ import { validateEnv } from "../../index.js";
5
+ import { AggregateError } from "../../errors.js";
6
+ import { loadSchema } from "./utils.js";
7
+ export default function (program) {
8
+ return new Command("check")
9
+ .description("Validate .env against schema")
10
+ .option("--strict", "Fail on extra env vars not in schema")
11
+ .option("--fix", "Attempt to fix errors interactively")
12
+ .action(async (option, command) => {
13
+ const rootOpts = command.parent.opts();
14
+ const spinner = ora("Validatng environment.......").start();
15
+ try {
16
+ const schema = await loadSchema(rootOpts.schema);
17
+ const env = validateEnv(schema, {
18
+ strict: option.strict,
19
+ });
20
+ spinner.succeed(chalk.green("Environment validation passed!"));
21
+ console.log(chalk.dim(`Found ${Object.keys(env).length} valid variables`));
22
+ }
23
+ catch (error) {
24
+ spinner.stop();
25
+ if (error instanceof AggregateError) {
26
+ console.error(chalk.red("\nEnvironment validation failed:"));
27
+ error.errors.forEach((e) => {
28
+ console.log(` ${chalk.yellow(e.key)}: ${e.message}`);
29
+ if (e.rule?.docs) {
30
+ console.log(chalk.dim(` ${e.rule.docs}`));
31
+ }
32
+ });
33
+ process.exit(1);
34
+ }
35
+ else {
36
+ console.error(chalk.red("Unexpected error:"), error);
37
+ process.exit(2);
38
+ }
39
+ }
40
+ });
41
+ }
@@ -0,0 +1,61 @@
1
+ import { Command } from "commander";
2
+ import chalk from "chalk";
3
+ import ora from "ora";
4
+ import { writeFileSync, existsSync } from "fs";
5
+ import inquirer from "inquirer";
6
+ import { dirname } from "path";
7
+ import { fileURLToPath } from "url";
8
+ const __dirname = dirname(fileURLToPath(import.meta.url));
9
+ export default function (program) {
10
+ return new Command("init")
11
+ .description("Initialize new schema file")
12
+ .option("--force", "Overwrite existing files")
13
+ .action(async (options, command) => {
14
+ const rootOpts = command.parent.opts();
15
+ const schemaPath = rootOpts.schema;
16
+ if (existsSync(schemaPath)) {
17
+ if (!options.force) {
18
+ const { overwrite } = await inquirer.prompt({
19
+ type: "confirm",
20
+ name: "overwrite",
21
+ message: "Schema file already exists. Overwrite?",
22
+ default: false,
23
+ });
24
+ if (!overwrite)
25
+ process.exit(0);
26
+ }
27
+ }
28
+ const spinner = ora("Creating new schema...").start();
29
+ try {
30
+ const template = `import { defineSchema } from 'dotenv-gad';
31
+
32
+ export default defineSchema({
33
+ // Add your environment variables here
34
+ PORT: {
35
+ type: 'number',
36
+ default: 3000,
37
+ docs: 'Port to run the server on'
38
+ },
39
+ NODE_ENV: {
40
+ type: 'string',
41
+ enum: ['development', 'production', 'test'],
42
+ default: 'development'
43
+ },
44
+ DB_URL: {
45
+ type: 'string',
46
+ required: true,
47
+ docs: 'Database connection URL'
48
+ }
49
+ });
50
+ `;
51
+ writeFileSync(schemaPath, template);
52
+ spinner.succeed(chalk.green(`Created ${schemaPath} successfully!`));
53
+ console.log(chalk.dim("\nEdit this file to define your environment schema"));
54
+ }
55
+ catch (error) {
56
+ spinner.fail(chalk.red("Failed to create schema file"));
57
+ console.error(error);
58
+ process.exit(1);
59
+ }
60
+ });
61
+ }
@@ -0,0 +1,37 @@
1
+ import { Command } from "commander";
2
+ import chalk from "chalk";
3
+ import ora from "ora";
4
+ import { writeFileSync } from "fs";
5
+ import { loadSchema } from "./utils.js";
6
+ export default function (program) {
7
+ return new Command("sync")
8
+ .description("Genearte/update .env.example file")
9
+ .option("--output <file>", "Output file path", ".env.example")
10
+ .action(async (options, command) => {
11
+ const rootOpts = command.parent.opts();
12
+ const spinner = ora("Generating .env.example.......").start();
13
+ try {
14
+ const schema = await loadSchema(rootOpts.schema);
15
+ let exampleContent = "# Auto-generated by dotenv-gad\n\n";
16
+ Object.entries(schema).forEach(([key, rule]) => {
17
+ if (rule.sensitive)
18
+ return;
19
+ exampleContent += `# ${rule.docs || "No description available"}\n`;
20
+ exampleContent += `# Type: ${rule.type}\n`;
21
+ if (rule.default !== undefined) {
22
+ exampleContent += `# Default: ${JSON.stringify(rule.default)}\n`;
23
+ }
24
+ exampleContent += `${key}=${rule.default
25
+ ? JSON.stringify(rule.default)
26
+ : ""}\n\n`;
27
+ });
28
+ writeFileSync(options.output, exampleContent.trim());
29
+ spinner.succeed(chalk.green(`Generated ${options.output} successfully!`));
30
+ }
31
+ catch (error) {
32
+ spinner.fail(chalk.red(`Failed to generate .env.example`));
33
+ console.error(error);
34
+ process.exit(1);
35
+ }
36
+ });
37
+ }
@@ -0,0 +1,40 @@
1
+ import { Command } from "commander";
2
+ import chalk from "chalk";
3
+ import ora from "ora";
4
+ import { writeFileSync } from "fs";
5
+ import { loadSchema } from "./utils.js";
6
+ export default function (program) {
7
+ return new Command("types")
8
+ .description("Generate Typescript types")
9
+ .option("--output <file>", "Output file path", "env.d.ts")
10
+ .action(async (options, command) => {
11
+ const rootOpts = command.parent.opts();
12
+ const spinner = ora("Generating type definitions.......").start();
13
+ try {
14
+ const schema = await loadSchema(rootOpts.schema);
15
+ let typeContent = "// Auto-generated by dotenv-gad\n\ndeclare namespace NodeJS{\n interface ProcessEnv{\n";
16
+ Object.entries(schema).forEach(([key, rule]) => {
17
+ let type;
18
+ switch (rule.type) {
19
+ case "number":
20
+ type = "number";
21
+ break;
22
+ case "boolean":
23
+ type = "boolean";
24
+ break;
25
+ default:
26
+ type = "string";
27
+ }
28
+ typeContent += ` ${key}${rule.required ? "" : "?"}:${type};\n`;
29
+ });
30
+ typeContent += " }\n}\n";
31
+ writeFileSync(options.output, typeContent);
32
+ spinner.succeed(chalk.green(`Generated ${options.output} successfully!`));
33
+ }
34
+ catch (error) {
35
+ spinner.fail(chalk.red("Failed to generate type definitions"));
36
+ console.error(error);
37
+ process.exit(1);
38
+ }
39
+ });
40
+ }
@@ -0,0 +1,40 @@
1
+ import { readFileSync, writeFileSync, unlinkSync } from "fs";
2
+ import { dirname, join } from "path";
3
+ import { fileURLToPath } from "url";
4
+ import { transformSync } from "esbuild";
5
+ const __dirname = dirname(fileURLToPath(import.meta.url));
6
+ export async function loadSchema(schemaPath) {
7
+ try {
8
+ if (schemaPath.endsWith(".ts")) {
9
+ const code = readFileSync(schemaPath, "utf-8");
10
+ const result = transformSync(code, {
11
+ format: "esm",
12
+ loader: "ts",
13
+ target: "esnext",
14
+ });
15
+ // Create temporary file
16
+ const tempFile = join(__dirname, "../../temp-schema.mjs");
17
+ writeFileSync(tempFile, result.code);
18
+ try {
19
+ // Import with query string to bust cache
20
+ const module = await import(`${tempFile}?t=${Date.now()}`);
21
+ return module.default;
22
+ }
23
+ finally {
24
+ // Clean up temp file
25
+ unlinkSync(tempFile);
26
+ }
27
+ }
28
+ else if (schemaPath.endsWith(".js")) {
29
+ const module = await import(schemaPath);
30
+ return module.default;
31
+ }
32
+ else if (schemaPath.endsWith(".json")) {
33
+ return JSON.parse(readFileSync(schemaPath, "utf-8"));
34
+ }
35
+ throw new Error("Unsupported schema format. Use .ts, .js or .json");
36
+ }
37
+ catch (error) {
38
+ throw new Error(`Failed to load schema: ${error.message}`);
39
+ }
40
+ }
@@ -0,0 +1,34 @@
1
+ #!/usr/bin/env node
2
+ import { Command } from "commander";
3
+ import chalk from "chalk";
4
+ import figlet from "figlet";
5
+ import { readFileSync } from "fs";
6
+ import { dirname, join } from "path";
7
+ import { fileURLToPath } from "url";
8
+ import checkCommand from "./commands/check.js";
9
+ import syncCommand from "./commands/sync.js";
10
+ import typesCommand from "./commands/types.js";
11
+ import initCommand from "./commands/init.js";
12
+ const __dirname = dirname(fileURLToPath(import.meta.url));
13
+ const pkg = JSON.parse(readFileSync(join(__dirname, "../../package.json"), "utf-8"));
14
+ export function createCLI() {
15
+ const program = new Command();
16
+ program
17
+ .version(pkg.version)
18
+ .description(chalk.green(figlet.textSync("dotenv-gad", {
19
+ font: "Standard",
20
+ horizontalLayout: "fitted",
21
+ })))
22
+ .option("--debug", "Enable debug output")
23
+ .option("--env <file>", "Specify env file path", ".env")
24
+ .option("--schema <file>", "Specify schema file path", "env.schema.ts");
25
+ const commands = [checkCommand, syncCommand, typesCommand, initCommand];
26
+ commands.forEach((command) => {
27
+ const cmd = command(program);
28
+ program.addCommand(cmd);
29
+ });
30
+ return program;
31
+ }
32
+ const program = createCLI();
33
+ program.parse(process.argv);
34
+ export default program;
package/dist/errors.js ADDED
@@ -0,0 +1,22 @@
1
+ export class AggregateError extends Error {
2
+ errors;
3
+ constructor(errors, message) {
4
+ super(message);
5
+ this.errors = errors;
6
+ this.name = "AggregateError";
7
+ Object.setPrototypeOf(this, AggregateError.prototype);
8
+ }
9
+ toString() {
10
+ const errorList = this.errors
11
+ .map((e) => {
12
+ let msg = ` - ${e.key}:${e.message}`;
13
+ if (e.value !== undefined)
14
+ msg += ` (reaceived: ${JSON.stringify(e.value)})`;
15
+ if (e.rule?.docs)
16
+ msg += `\n (${e.rule.docs})`;
17
+ return msg;
18
+ })
19
+ .join("\n");
20
+ return `${this.message}:\n${errorList}`;
21
+ }
22
+ }
package/dist/index.js ADDED
@@ -0,0 +1,11 @@
1
+ import { EnvValidator } from "./validator.js";
2
+ import { defineSchema } from "./schema.js";
3
+ import { AggregateError } from "./errors.js";
4
+ import { loadEnv, createEnvProxy } from "./utils.js";
5
+ import dotenv from "dotenv";
6
+ export { defineSchema, AggregateError, EnvValidator, loadEnv, createEnvProxy };
7
+ export function validateEnv(schema, options) {
8
+ const env = dotenv.config().parsed || {};
9
+ const validator = new EnvValidator(schema, options);
10
+ return validator.validate(env);
11
+ }
package/dist/schema.js ADDED
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Define a schema for a set of environment variables.
3
+ *
4
+ * @param schema A record where each key is the name of an environment variable
5
+ * and the value is a `SchemaRule` object that defines the rules for that
6
+ * variable.
7
+ */
8
+ export function defineSchema(schema) {
9
+ return schema;
10
+ }
package/dist/utils.js ADDED
@@ -0,0 +1,36 @@
1
+ import dotenv from "dotenv";
2
+ import { EnvValidator } from "./validator.js";
3
+ /**
4
+ * Load the environment variables from a .env file, validate them against the schema
5
+ * and return an object with the validated values.
6
+ *
7
+ * @param schema The schema definition for the environment variables.
8
+ * @param options Options for the validation process.
9
+ *
10
+ * @returns A validated object with the environment variables.
11
+ */
12
+ export function loadEnv(schema, options) {
13
+ const env = dotenv.config().parsed || {};
14
+ const validator = new EnvValidator(schema, options);
15
+ return validator.validate(env);
16
+ }
17
+ /**
18
+ * Create a proxy around the validated environment variables. The proxy will
19
+ * throw an error if you try to access a variable that is not validated.
20
+ *
21
+ * @param validatedEnv The validated environment variables.
22
+ *
23
+ * @returns A proxy object that throws an error if you access an
24
+ * unvalidated variable.
25
+ */
26
+ export function createEnvProxy(validatedEnv) {
27
+ return new Proxy(validatedEnv, {
28
+ get(target, prop) {
29
+ const value = target[prop];
30
+ if (value === undefined) {
31
+ throw new Error(`Environment variable ${String(prop)} is not validated`);
32
+ }
33
+ return value;
34
+ },
35
+ });
36
+ }
@@ -0,0 +1,180 @@
1
+ import { AggregateError } from "./errors.js";
2
+ export class EnvValidator {
3
+ schema;
4
+ options;
5
+ errors = [];
6
+ constructor(schema, options) {
7
+ this.schema = schema;
8
+ this.options = options;
9
+ }
10
+ validate(env) {
11
+ this.errors = [];
12
+ const result = {};
13
+ for (const [key, rule] of Object.entries(this.schema)) {
14
+ try {
15
+ result[key] = this.validateKey(key, rule, env[key]);
16
+ }
17
+ catch (error) {
18
+ if (error instanceof Error) {
19
+ this.errors.push({
20
+ key,
21
+ message: error.message,
22
+ value: env[key],
23
+ rule,
24
+ });
25
+ }
26
+ }
27
+ }
28
+ if (this.errors.length > 0) {
29
+ const keys = this.errors.map((e) => e.key).join(", ");
30
+ throw new AggregateError(this.errors, `Environment validation failed: ${keys}`);
31
+ }
32
+ if (this.options?.strict) {
33
+ const extraVars = Object.keys(env).filter((k) => !(k in this.schema));
34
+ if (extraVars.length > 0) {
35
+ throw new Error(`Unexpected environment variables: ${extraVars.join(", ")}`);
36
+ }
37
+ }
38
+ return result;
39
+ }
40
+ validateKey(key, rule, value) {
41
+ const effectiveRule = this.getEffectiveRule(key, rule);
42
+ if (value === undefined) {
43
+ if (effectiveRule.required)
44
+ throw new Error(`Missing required environment variable`);
45
+ return effectiveRule.default;
46
+ }
47
+ if (effectiveRule.transform) {
48
+ value = effectiveRule.transform(value);
49
+ }
50
+ switch (effectiveRule.type) {
51
+ case "string":
52
+ value = String(value).trim();
53
+ if (effectiveRule.minLength && value.length < effectiveRule.minLength) {
54
+ throw new Error(`Environment variable ${key} must be at least ${effectiveRule.minLength} characters`);
55
+ }
56
+ if (effectiveRule.maxLength && value.length > effectiveRule.maxLength) {
57
+ throw new Error(`Environment variable ${key} must be at most ${effectiveRule.maxLength} characters`);
58
+ }
59
+ break;
60
+ case "number":
61
+ value = Number(value);
62
+ if (isNaN(value)) {
63
+ throw new Error(`Environment variable ${key} must be a number`);
64
+ }
65
+ if (effectiveRule.min !== undefined && value < effectiveRule.min) {
66
+ throw new Error(`Environment variable ${key} must be at least ${effectiveRule.min}`);
67
+ }
68
+ if (effectiveRule.max !== undefined && value > effectiveRule.max) {
69
+ throw new Error(`Environment variable ${key} must be at most ${effectiveRule.max}`);
70
+ }
71
+ break;
72
+ case "boolean":
73
+ if (typeof value === "string") {
74
+ value = value.toLowerCase();
75
+ if (value === "true") {
76
+ value = true;
77
+ }
78
+ else if (value === "false") {
79
+ value = false;
80
+ }
81
+ }
82
+ if (typeof value !== "boolean") {
83
+ throw new Error(`Environment variable ${key} must be a boolean (true/false)`);
84
+ }
85
+ value = Boolean(value);
86
+ break;
87
+ case "url":
88
+ try {
89
+ new URL(String(value));
90
+ }
91
+ catch {
92
+ throw new Error("Must be a valid URL");
93
+ }
94
+ break;
95
+ case "email":
96
+ if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(String(value))) {
97
+ throw new Error("Must be a valid email");
98
+ }
99
+ break;
100
+ case "ip":
101
+ if (!/^(?:[0-9]{1,3}\.){3}[0-9]{1,3}$/.test(String(value))) {
102
+ throw new Error("Must be a valid IP address");
103
+ }
104
+ break;
105
+ case "port":
106
+ const port = Number(value);
107
+ if (isNaN(port))
108
+ throw new Error("Must be a number");
109
+ if (port < 1 || port > 65535) {
110
+ throw new Error("Must be between 1 and 65535");
111
+ }
112
+ break;
113
+ case "json":
114
+ try {
115
+ value = JSON.parse(value);
116
+ }
117
+ catch {
118
+ throw new Error("Must be valid JSON");
119
+ }
120
+ break;
121
+ case "array":
122
+ if (!Array.isArray(value)) {
123
+ try {
124
+ value = JSON.parse(value);
125
+ }
126
+ catch {
127
+ throw new Error("Must be a valid array or JSON array string");
128
+ }
129
+ }
130
+ if (effectiveRule.items) {
131
+ value = value.map((item, i) => {
132
+ try {
133
+ return this.validateKey(`${key}[${i}]`, effectiveRule.items, item);
134
+ }
135
+ catch (error) {
136
+ throw new Error(`Array item '${i}':${error.message}`);
137
+ }
138
+ });
139
+ }
140
+ break;
141
+ case "object":
142
+ if (typeof value === "string") {
143
+ try {
144
+ value = JSON.parse(value);
145
+ }
146
+ catch {
147
+ throw new Error("Must be a valid object or JSON string");
148
+ }
149
+ }
150
+ if (effectiveRule.properties) {
151
+ const obj = {};
152
+ for (const [prop, propRule] of Object.entries(effectiveRule.properties)) {
153
+ try {
154
+ obj[prop] = this.validateKey(`${key}.${prop}`, propRule, value[prop]);
155
+ }
156
+ catch (error) {
157
+ throw new Error(`Property '${prop}':${error.message}`);
158
+ }
159
+ }
160
+ value = obj;
161
+ }
162
+ break;
163
+ }
164
+ if (effectiveRule.enum && !effectiveRule.enum.includes(value)) {
165
+ throw new Error(`Environment variable ${key} must be one of ${effectiveRule.enum.join(", ")}`);
166
+ }
167
+ if (effectiveRule.regex && !effectiveRule.regex.test(value)) {
168
+ throw new Error(`Environment variable ${key} must match ${effectiveRule.regex}`);
169
+ }
170
+ if (effectiveRule.validate && !effectiveRule.validate(value)) {
171
+ throw new Error(effectiveRule.error || "Custom validation failed");
172
+ }
173
+ return value;
174
+ }
175
+ getEffectiveRule(key, rule) {
176
+ const envName = process.env.NODE_ENV || "development";
177
+ const envRule = rule.env?.[envName] || {};
178
+ return { ...rule, ...envRule };
179
+ }
180
+ }
package/package.json ADDED
@@ -0,0 +1,68 @@
1
+ {
2
+ "name": "dotenv-gad",
3
+ "version": "0.1.0",
4
+ "main": "dist/index.js",
5
+ "types": "dist/index.d.ts",
6
+ "type": "module",
7
+ "bin": {
8
+ "dotenv-guard": "./dist/cli/index.js"
9
+ },
10
+ "scripts": {
11
+ "build": "tsc && chmod +x ./dist/cli/index.js",
12
+ "test": "NODE_OPTIONS=--experimental-vm-modules jest",
13
+ "dev": "tsc --watch",
14
+ "prepublishOnly": "npm run build && npm test"
15
+ },
16
+ "jest": {
17
+ "extensionsToTreatAsEsm": [
18
+ ".ts"
19
+ ],
20
+ "transform": {}
21
+ },
22
+ "keywords": [
23
+ "dotenv",
24
+ "environment",
25
+ "validation",
26
+ "typescript",
27
+ "schema",
28
+ "configuration"
29
+ ],
30
+ "exports": {
31
+ ".": {
32
+ "import": "./dist/index.js",
33
+ "types": "./dist/index.d.ts"
34
+ },
35
+ "./package.json": "./package.json"
36
+ },
37
+ "files": [
38
+ "dist/**/*",
39
+ "README.md",
40
+ "LICENSE"
41
+ ],
42
+ "author": "Kasim Lyee",
43
+ "license": "MIT",
44
+ "description": "Environment variable validation and type safety for Node.js and modern JavaScript applications",
45
+ "repository": {
46
+ "type": "git",
47
+ "url": "https://github.com/kasimlyee/dotenv-gad.git"
48
+ },
49
+ "bugs": {
50
+ "url": "https://github.com/kasimlyee/dotenv-gad/issues"
51
+ },
52
+ "homepage": "https://github.com/kasimlyee/dotenv-gad#readme",
53
+ "devDependencies": {
54
+ "@types/chalk": "^0.4.31",
55
+ "@types/commander": "^2.12.0",
56
+ "@types/dotenv": "^6.1.1",
57
+ "@types/figlet": "^1.7.0",
58
+ "@types/inquirer": "^9.0.8",
59
+ "@types/jest": "^30.0.0",
60
+ "@types/node": "^24.0.3",
61
+ "@types/ora": "^3.1.0",
62
+ "jest-environment-jsdom": "^30.0.2",
63
+ "ts-jest": "^29.4.0"
64
+ },
65
+ "dependencies": {
66
+ "esbuild": "^0.25.5"
67
+ }
68
+ }