env-drift-check 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/LICENSE +21 -0
- package/README.md +114 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +75 -0
- package/dist/config/loadConfig.d.ts +2 -0
- package/dist/config/loadConfig.js +24 -0
- package/dist/engine/driftChecker.d.ts +2 -0
- package/dist/engine/driftChecker.js +42 -0
- package/dist/engine/envParser.d.ts +1 -0
- package/dist/engine/envParser.js +15 -0
- package/dist/engine/fixer.d.ts +2 -0
- package/dist/engine/fixer.js +19 -0
- package/dist/engine/interactive.d.ts +2 -0
- package/dist/engine/interactive.js +42 -0
- package/dist/engine/validator.d.ts +2 -0
- package/dist/engine/validator.js +59 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.js +22 -0
- package/dist/reporter/consoleReporter.d.ts +2 -0
- package/dist/reporter/consoleReporter.js +30 -0
- package/dist/types.d.ts +29 -0
- package/dist/types.js +2 -0
- package/package.json +53 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 EnvWise Contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
# env-drift-check
|
|
2
|
+
|
|
3
|
+
[](https://www.npmjs.com/package/env-drift-check)
|
|
4
|
+
[](https://opensource.org/licenses/MIT)
|
|
5
|
+
|
|
6
|
+
> **Stop copy-pasting `.env` files.** Onboard developers in seconds with interactive prompts and smart validation.
|
|
7
|
+
|
|
8
|
+
**env-drift-check** is a powerful CLI tool that ensures your environment variables are always in sync with your code. Unlike other tools that just fail when keys are missing, we help you **fix them interactively**.
|
|
9
|
+
|
|
10
|
+
## Why use this?
|
|
11
|
+
|
|
12
|
+
| Feature | Other Tools | **env-drift-check** |
|
|
13
|
+
| :--- | :--- | :--- |
|
|
14
|
+
| **Missing Keys** | Crash & Exit | 🛠**Interactive Setup Wizard** |
|
|
15
|
+
| **Validation** | Basic Existence Check | **Rich Types** (Email, URL, Regex) |
|
|
16
|
+
| **Onboarding** | Manual (Read docs → Copy → Paste) | **Automated** (Run command → Fill prompts) |
|
|
17
|
+
|
|
18
|
+
## Features
|
|
19
|
+
|
|
20
|
+
- **Interactive Mode**: Automatically prompts users to fill in missing variables.
|
|
21
|
+
- **Smart Validation**: enforce types like `email`, `url`, `number`, `boolean`, and `regex`.
|
|
22
|
+
- **Drift Detection**: Compares your `.env` against `.env.example`.
|
|
23
|
+
- **Zero Config**: Works out of the box, or add `envwise.config.json` for superpowers.
|
|
24
|
+
|
|
25
|
+
## Installation
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
npm install -g env-drift-check
|
|
29
|
+
# OR
|
|
30
|
+
npx env-drift-check
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## 🛠Usage
|
|
34
|
+
|
|
35
|
+
### 1. Basic Check
|
|
36
|
+
Check if your `.env` is missing any keys defined in `.env.example`:
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
npx env-drift-check
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
### 2. Interactive Setup (Recommended)
|
|
43
|
+
Automatically prompt the user to fill in missing keys:
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
npx env-drift-check --interactive
|
|
47
|
+
# OR
|
|
48
|
+
npx env-drift-check -i
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
### 3. CI/CD Mode (Strict)
|
|
52
|
+
Fail the build if drift is detected (great for pipelines):
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
npx env-drift-check --strict
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
## Configuration
|
|
59
|
+
|
|
60
|
+
Create a `envwise.config.json` to define validation rules.
|
|
61
|
+
|
|
62
|
+
```json
|
|
63
|
+
{
|
|
64
|
+
"baseEnv": ".env.example",
|
|
65
|
+
"rules": {
|
|
66
|
+
"PORT": {
|
|
67
|
+
"type": "number",
|
|
68
|
+
"min": 3000,
|
|
69
|
+
"max": 9000
|
|
70
|
+
},
|
|
71
|
+
"DATABASE_URL": {
|
|
72
|
+
"type": "url",
|
|
73
|
+
"description": "Connection string for MongoDB"
|
|
74
|
+
},
|
|
75
|
+
"ADMIN_EMAIL": {
|
|
76
|
+
"type": "email",
|
|
77
|
+
"required": true
|
|
78
|
+
},
|
|
79
|
+
"FEATURE_FLAG": {
|
|
80
|
+
"type": "boolean",
|
|
81
|
+
"mustBeFalseIn": "production"
|
|
82
|
+
},
|
|
83
|
+
"API_KEY": {
|
|
84
|
+
"type": "regex",
|
|
85
|
+
"regex": "^[A-Z0-9]{32}$",
|
|
86
|
+
"description": "32-character alphanumeric key"
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
### Validation Types
|
|
93
|
+
|
|
94
|
+
| Type | Options | Description |
|
|
95
|
+
| :--- | :--- | :--- |
|
|
96
|
+
| `string` | `min`, `max` | String length validation |
|
|
97
|
+
| `number` | `min`, `max` | Numeric range validation |
|
|
98
|
+
| `boolean` | `mustBeFalseIn` | True/False check |
|
|
99
|
+
| `enum` | `values` (array) | Must be one of the allowlist |
|
|
100
|
+
| `email` | - | Valid email format |
|
|
101
|
+
| `url` | - | Valid URL format |
|
|
102
|
+
| `regex` | `regex` (string) | Custom pattern matching |
|
|
103
|
+
|
|
104
|
+
## Contributing
|
|
105
|
+
|
|
106
|
+
1. Fork the repository
|
|
107
|
+
2. Create your feature branch (`git checkout -b feature/amazing-feature`)
|
|
108
|
+
3. Commit your changes (`git commit -m 'Add some amazing feature'`)
|
|
109
|
+
4. Push to the branch (`git push origin feature/amazing-feature`)
|
|
110
|
+
5. Open a Pull Request
|
|
111
|
+
|
|
112
|
+
## License
|
|
113
|
+
|
|
114
|
+
This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
|
package/dist/cli.d.ts
ADDED
package/dist/cli.js
ADDED
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
4
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
5
|
+
};
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
const fs_1 = __importDefault(require("fs"));
|
|
8
|
+
const path_1 = __importDefault(require("path"));
|
|
9
|
+
const envParser_1 = require("./engine/envParser");
|
|
10
|
+
const driftChecker_1 = require("./engine/driftChecker");
|
|
11
|
+
const consoleReporter_1 = require("./reporter/consoleReporter");
|
|
12
|
+
const interactive_1 = require("./engine/interactive");
|
|
13
|
+
const loadConfig_1 = require("./config/loadConfig");
|
|
14
|
+
const args = process.argv.slice(2);
|
|
15
|
+
const strict = args.includes("--strict");
|
|
16
|
+
const interactive = args.includes("--interactive") || args.includes("-i");
|
|
17
|
+
const checkAll = args.includes("--all");
|
|
18
|
+
const positionalArgs = args.filter(a => !a.startsWith("-"));
|
|
19
|
+
const config = (0, loadConfig_1.loadConfig)();
|
|
20
|
+
const basePath = path_1.default.resolve(config.baseEnv || ".env.example");
|
|
21
|
+
if (!fs_1.default.existsSync(basePath)) {
|
|
22
|
+
console.error(`Reference file missing: ${basePath}`);
|
|
23
|
+
process.exit(1);
|
|
24
|
+
}
|
|
25
|
+
const baseEnv = (0, envParser_1.parseEnv)(basePath);
|
|
26
|
+
async function runForFile(targetFile) {
|
|
27
|
+
const targetPath = path_1.default.resolve(targetFile);
|
|
28
|
+
if (!fs_1.default.existsSync(targetPath)) {
|
|
29
|
+
console.error(`File missing: ${targetFile}`);
|
|
30
|
+
return false;
|
|
31
|
+
}
|
|
32
|
+
console.log(`\n Checking ${path_1.default.basename(targetPath)} against ${path_1.default.basename(basePath)}...`);
|
|
33
|
+
const targetEnv = (0, envParser_1.parseEnv)(targetPath);
|
|
34
|
+
let result = (0, driftChecker_1.checkDrift)(baseEnv, targetEnv, config);
|
|
35
|
+
if (result.missing.length > 0 && interactive) {
|
|
36
|
+
const newValues = await (0, interactive_1.interactiveSetup)(result.missing, baseEnv, config);
|
|
37
|
+
// Merge new values into targetEnv
|
|
38
|
+
const updatedEnv = { ...targetEnv, ...newValues };
|
|
39
|
+
// Write back to file
|
|
40
|
+
const newContent = Object.entries(updatedEnv)
|
|
41
|
+
.map(([k, v]) => `${k}=${v}`)
|
|
42
|
+
.join("\n");
|
|
43
|
+
fs_1.default.writeFileSync(targetPath, newContent);
|
|
44
|
+
console.log(`\n ✅ Updated ${path_1.default.basename(targetPath)} with new values.`);
|
|
45
|
+
// Re-check drift
|
|
46
|
+
result = (0, driftChecker_1.checkDrift)(baseEnv, updatedEnv, config);
|
|
47
|
+
}
|
|
48
|
+
(0, consoleReporter_1.report)(result);
|
|
49
|
+
const hasIssues = result.missing.length || result.mismatches.length || result.errors.length;
|
|
50
|
+
return !hasIssues;
|
|
51
|
+
}
|
|
52
|
+
async function main() {
|
|
53
|
+
let allFiles = [];
|
|
54
|
+
if (checkAll) {
|
|
55
|
+
allFiles = fs_1.default.readdirSync(process.cwd())
|
|
56
|
+
.filter(f => f.startsWith(".env") && f !== path_1.default.basename(basePath));
|
|
57
|
+
}
|
|
58
|
+
else {
|
|
59
|
+
allFiles = [positionalArgs[0] || ".env"];
|
|
60
|
+
}
|
|
61
|
+
let overallSuccess = true;
|
|
62
|
+
for (const file of allFiles) {
|
|
63
|
+
const success = await runForFile(file);
|
|
64
|
+
if (!success)
|
|
65
|
+
overallSuccess = false;
|
|
66
|
+
}
|
|
67
|
+
if (strict && !overallSuccess) {
|
|
68
|
+
console.error("\n Strict mode failed for one or more files");
|
|
69
|
+
process.exit(1);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
main().catch(err => {
|
|
73
|
+
console.error(err);
|
|
74
|
+
process.exit(1);
|
|
75
|
+
});
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.loadConfig = loadConfig;
|
|
7
|
+
const fs_1 = __importDefault(require("fs"));
|
|
8
|
+
const path_1 = __importDefault(require("path"));
|
|
9
|
+
const DEFAULT_CONFIG = {
|
|
10
|
+
baseEnv: ".env.example",
|
|
11
|
+
rules: {}
|
|
12
|
+
};
|
|
13
|
+
function loadConfig() {
|
|
14
|
+
const configPath = path_1.default.resolve("envwise.config.json");
|
|
15
|
+
if (!fs_1.default.existsSync(configPath)) {
|
|
16
|
+
return DEFAULT_CONFIG;
|
|
17
|
+
}
|
|
18
|
+
const raw = fs_1.default.readFileSync(configPath, "utf-8");
|
|
19
|
+
const userConfig = JSON.parse(raw);
|
|
20
|
+
return {
|
|
21
|
+
...DEFAULT_CONFIG,
|
|
22
|
+
...userConfig
|
|
23
|
+
};
|
|
24
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.checkDrift = checkDrift;
|
|
4
|
+
const validator_1 = require("./validator");
|
|
5
|
+
function checkDrift(base, target, config) {
|
|
6
|
+
const result = {
|
|
7
|
+
missing: [],
|
|
8
|
+
extra: [],
|
|
9
|
+
errors: [],
|
|
10
|
+
warnings: [],
|
|
11
|
+
mismatches: []
|
|
12
|
+
};
|
|
13
|
+
// Missing & extra keys
|
|
14
|
+
for (const key of Object.keys(base)) {
|
|
15
|
+
if (!(key in target))
|
|
16
|
+
result.missing.push(key);
|
|
17
|
+
}
|
|
18
|
+
for (const key of Object.keys(target)) {
|
|
19
|
+
if (!(key in base))
|
|
20
|
+
result.extra.push(key);
|
|
21
|
+
}
|
|
22
|
+
// Value mismatch detection
|
|
23
|
+
for (const key of Object.keys(base)) {
|
|
24
|
+
if (key in target && base[key] !== target[key]) {
|
|
25
|
+
result.mismatches.push({
|
|
26
|
+
key,
|
|
27
|
+
expected: base[key],
|
|
28
|
+
actual: target[key]
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
// Rule-based validation
|
|
33
|
+
const rules = config.rules || {};
|
|
34
|
+
for (const [key, rule] of Object.entries(rules)) {
|
|
35
|
+
if (target[key]) {
|
|
36
|
+
const err = (0, validator_1.validateValue)(key, target[key], rule, target["NODE_ENV"]);
|
|
37
|
+
if (err)
|
|
38
|
+
result.errors.push({ key, message: err });
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
return result;
|
|
42
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function parseEnv(path: string): Record<string, string>;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.parseEnv = parseEnv;
|
|
7
|
+
const fs_1 = __importDefault(require("fs"));
|
|
8
|
+
const dotenv_1 = __importDefault(require("dotenv"));
|
|
9
|
+
function parseEnv(path) {
|
|
10
|
+
if (!fs_1.default.existsSync(path)) {
|
|
11
|
+
throw new Error(`Env file not found: ${path}`);
|
|
12
|
+
}
|
|
13
|
+
const content = fs_1.default.readFileSync(path);
|
|
14
|
+
return dotenv_1.default.parse(content);
|
|
15
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.applyFixes = applyFixes;
|
|
4
|
+
function applyFixes(base, target, result) {
|
|
5
|
+
const fixedEnv = { ...target };
|
|
6
|
+
// Add missing keys
|
|
7
|
+
for (const key of result.missing) {
|
|
8
|
+
if (base[key]) {
|
|
9
|
+
fixedEnv[key] = base[key];
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
// Fix mismatched values (base → target)
|
|
13
|
+
for (const mismatch of result.mismatches) {
|
|
14
|
+
if (mismatch.expected) {
|
|
15
|
+
fixedEnv[mismatch.key] = mismatch.expected;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
return fixedEnv;
|
|
19
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.interactiveSetup = interactiveSetup;
|
|
7
|
+
const prompts_1 = __importDefault(require("prompts"));
|
|
8
|
+
const validator_1 = require("./validator");
|
|
9
|
+
async function interactiveSetup(missingKeys, baseEnv, config) {
|
|
10
|
+
const newValues = {};
|
|
11
|
+
console.log("\n🛠Interactive Setup: Let's fill in the missing variables.\n");
|
|
12
|
+
for (const key of missingKeys) {
|
|
13
|
+
const rule = config.rules?.[key] || { type: "string" };
|
|
14
|
+
const initial = baseEnv[key] || "";
|
|
15
|
+
// Determine prompt type
|
|
16
|
+
let promptType = "text";
|
|
17
|
+
if (rule.type === "boolean")
|
|
18
|
+
promptType = "confirm";
|
|
19
|
+
if (rule.type === "number")
|
|
20
|
+
promptType = "number";
|
|
21
|
+
if (key.includes("PASSWORD") || key.includes("SECRET"))
|
|
22
|
+
promptType = "password";
|
|
23
|
+
const response = await (0, prompts_1.default)({
|
|
24
|
+
type: promptType,
|
|
25
|
+
name: "value",
|
|
26
|
+
message: `${key}${rule.description ? ` (${rule.description})` : ""}:`,
|
|
27
|
+
initial: promptType === "confirm" ? initial === "true" : initial,
|
|
28
|
+
validate: (val) => {
|
|
29
|
+
const strVal = String(val);
|
|
30
|
+
const error = (0, validator_1.validateValue)(key, strVal, rule, "local"); // Assuming 'local' context for now
|
|
31
|
+
return error || true;
|
|
32
|
+
}
|
|
33
|
+
});
|
|
34
|
+
// Handle user cancellation (Ctrl+C)
|
|
35
|
+
if (response.value === undefined) {
|
|
36
|
+
console.log("\nSetup cancelled.");
|
|
37
|
+
process.exit(1);
|
|
38
|
+
}
|
|
39
|
+
newValues[key] = String(response.value);
|
|
40
|
+
}
|
|
41
|
+
return newValues;
|
|
42
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.validateValue = validateValue;
|
|
4
|
+
function validateValue(key, value, rule, env) {
|
|
5
|
+
if (!value && rule.required !== false) {
|
|
6
|
+
return `${key} is required`;
|
|
7
|
+
}
|
|
8
|
+
// Number validation
|
|
9
|
+
if (rule.type === "number") {
|
|
10
|
+
const num = Number(value);
|
|
11
|
+
if (isNaN(num))
|
|
12
|
+
return `${key} must be a number`;
|
|
13
|
+
if (rule.min !== undefined && num < rule.min)
|
|
14
|
+
return `${key} must be at least ${rule.min}`;
|
|
15
|
+
if (rule.max !== undefined && num > rule.max)
|
|
16
|
+
return `${key} must be at most ${rule.max}`;
|
|
17
|
+
}
|
|
18
|
+
// String length validation
|
|
19
|
+
if (rule.type === "string") {
|
|
20
|
+
if (rule.min !== undefined && value.length < rule.min)
|
|
21
|
+
return `${key} must be at least ${rule.min} chars`;
|
|
22
|
+
if (rule.max !== undefined && value.length > rule.max)
|
|
23
|
+
return `${key} must be at most ${rule.max} chars`;
|
|
24
|
+
}
|
|
25
|
+
// Enum validation
|
|
26
|
+
if (rule.type === "enum" && rule.values && !rule.values.includes(value)) {
|
|
27
|
+
return `${key} must be one of: ${rule.values.join(", ")}`;
|
|
28
|
+
}
|
|
29
|
+
// Boolean validation
|
|
30
|
+
if (rule.type === "boolean") {
|
|
31
|
+
if (value !== "true" && value !== "false")
|
|
32
|
+
return `${key} must be true or false`;
|
|
33
|
+
if (rule.mustBeFalseIn === env && value === "true") {
|
|
34
|
+
return `${key} must be false in ${env}`;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
// Email validation
|
|
38
|
+
if (rule.type === "email") {
|
|
39
|
+
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
40
|
+
if (!emailRegex.test(value))
|
|
41
|
+
return `${key} must be a valid email`;
|
|
42
|
+
}
|
|
43
|
+
// URL validation
|
|
44
|
+
if (rule.type === "url") {
|
|
45
|
+
try {
|
|
46
|
+
new URL(value);
|
|
47
|
+
}
|
|
48
|
+
catch {
|
|
49
|
+
return `${key} must be a valid URL`;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
// Regex validation
|
|
53
|
+
if (rule.type === "regex" && rule.regex) {
|
|
54
|
+
const regex = new RegExp(rule.regex);
|
|
55
|
+
if (!regex.test(value))
|
|
56
|
+
return `${key} does not match required pattern`;
|
|
57
|
+
}
|
|
58
|
+
return null;
|
|
59
|
+
}
|
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
__exportStar(require("./engine/driftChecker"), exports);
|
|
18
|
+
__exportStar(require("./engine/envParser"), exports);
|
|
19
|
+
__exportStar(require("./engine/validator"), exports);
|
|
20
|
+
__exportStar(require("./engine/interactive"), exports);
|
|
21
|
+
__exportStar(require("./config/loadConfig"), exports);
|
|
22
|
+
__exportStar(require("./types"), exports);
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.report = report;
|
|
4
|
+
function report(result) {
|
|
5
|
+
if (result.missing.length) {
|
|
6
|
+
console.log("\n Missing Variables:");
|
|
7
|
+
result.missing.forEach(v => console.log(" -", v));
|
|
8
|
+
}
|
|
9
|
+
if (result.extra.length) {
|
|
10
|
+
console.log("\n Extra Variables:");
|
|
11
|
+
result.extra.forEach(v => console.log(" -", v));
|
|
12
|
+
}
|
|
13
|
+
if (result.errors.length) {
|
|
14
|
+
console.log("\n Errors:");
|
|
15
|
+
result.errors.forEach(e => console.log(" -", `${e.key}: ${e.message}`));
|
|
16
|
+
}
|
|
17
|
+
if (result.mismatches.length) {
|
|
18
|
+
console.log("\n Value Mismatches:");
|
|
19
|
+
result.mismatches.forEach(m => {
|
|
20
|
+
console.log(` - ${m.key}: expected="${m.expected}" actual="${m.actual}"`);
|
|
21
|
+
console.log(` -Run --fix to sync ${m.key}`);
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
if (!result.missing.length &&
|
|
25
|
+
!result.extra.length &&
|
|
26
|
+
!result.errors.length &&
|
|
27
|
+
!result.mismatches.length) {
|
|
28
|
+
console.log("No environment drift detected");
|
|
29
|
+
}
|
|
30
|
+
}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
export interface Rule {
|
|
2
|
+
type: "string" | "number" | "boolean" | "enum" | "email" | "url" | "regex";
|
|
3
|
+
values?: string[];
|
|
4
|
+
regex?: string;
|
|
5
|
+
min?: number;
|
|
6
|
+
max?: number;
|
|
7
|
+
description?: string;
|
|
8
|
+
mustBeFalseIn?: string;
|
|
9
|
+
required?: boolean;
|
|
10
|
+
}
|
|
11
|
+
export interface Config {
|
|
12
|
+
baseEnv?: string;
|
|
13
|
+
rules?: Record<string, Rule>;
|
|
14
|
+
}
|
|
15
|
+
export interface ValueMismatch {
|
|
16
|
+
key: string;
|
|
17
|
+
expected: string;
|
|
18
|
+
actual: string;
|
|
19
|
+
}
|
|
20
|
+
export interface DriftResult {
|
|
21
|
+
missing: string[];
|
|
22
|
+
extra: string[];
|
|
23
|
+
errors: {
|
|
24
|
+
key: string;
|
|
25
|
+
message: string;
|
|
26
|
+
}[];
|
|
27
|
+
warnings: string[];
|
|
28
|
+
mismatches: ValueMismatch[];
|
|
29
|
+
}
|
package/dist/types.js
ADDED
package/package.json
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "env-drift-check",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Interactive environment variable checker and setup wizard. Sync .env files with validation (Email, URL, Regex) and prompts.",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"env",
|
|
7
|
+
"dotenv",
|
|
8
|
+
"check-env",
|
|
9
|
+
"dotenv-safe",
|
|
10
|
+
"environment-variables",
|
|
11
|
+
"config",
|
|
12
|
+
"validation",
|
|
13
|
+
"interactive",
|
|
14
|
+
"setup",
|
|
15
|
+
"onboarding",
|
|
16
|
+
"drift",
|
|
17
|
+
"checker"
|
|
18
|
+
],
|
|
19
|
+
"author": "Shashidhar Naik",
|
|
20
|
+
"license": "MIT",
|
|
21
|
+
"bugs": {
|
|
22
|
+
"url": "https://github.com/shashi089/env-drift-check/issues"
|
|
23
|
+
},
|
|
24
|
+
"homepage": "https://github.com/shashi089/env-drift-check#readme",
|
|
25
|
+
"engines": {
|
|
26
|
+
"node": ">=16.0.0"
|
|
27
|
+
},
|
|
28
|
+
"main": "dist/cli.js",
|
|
29
|
+
"bin": {
|
|
30
|
+
"env-drift-check": "dist/cli.js"
|
|
31
|
+
},
|
|
32
|
+
"types": "dist/cli.d.ts",
|
|
33
|
+
"files": [
|
|
34
|
+
"dist",
|
|
35
|
+
"README.md",
|
|
36
|
+
"LICENSE"
|
|
37
|
+
],
|
|
38
|
+
"scripts": {
|
|
39
|
+
"build": "tsc",
|
|
40
|
+
"prepare": "npm run build",
|
|
41
|
+
"prepublishOnly": "npm run build"
|
|
42
|
+
},
|
|
43
|
+
"dependencies": {
|
|
44
|
+
"commander": "^11.0.0",
|
|
45
|
+
"dotenv": "^16.4.0",
|
|
46
|
+
"prompts": "^2.4.2"
|
|
47
|
+
},
|
|
48
|
+
"devDependencies": {
|
|
49
|
+
"@types/prompts": "^2.4.9",
|
|
50
|
+
"ts-node": "^10.9.2",
|
|
51
|
+
"typescript": "^5.3.3"
|
|
52
|
+
}
|
|
53
|
+
}
|