safelaunch 1.0.9 → 1.0.11

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,5 +1,4 @@
1
1
  # safelaunch
2
-
3
2
  > Catch what breaks production before it breaks.
4
3
 
5
4
  safelaunch is a comprehensive environment validator for JavaScript projects. Run it before every push to catch everything that will break production.
@@ -24,7 +23,7 @@ safelaunch validate
24
23
 
25
24
  ## What safelaunch checks
26
25
 
27
- safelaunch validate runs 7 checks:
26
+ safelaunch validate runs 11 checks:
28
27
 
29
28
  1. Missing required environment variables
30
29
  2. Empty required environment variables
@@ -33,12 +32,16 @@ safelaunch validate runs 7 checks:
33
32
  5. Dependencies not installed
34
33
  6. Dependency drift (in package.json but not installed)
35
34
  7. Variables in .env.example but missing from .env
35
+ 8. VITE_ prefix warning (Vite projects — variables without VITE_ won't be exposed to the client)
36
+ 9. REACT_APP_ prefix warning (CRA projects — variables without REACT_APP_ won't be exposed to the client)
37
+ 10. NEXT_PUBLIC_ prefix awareness (Next.js — flags variables intended for the client)
38
+ 11. .env file priority conflicts (Next.js — warns when .env.local overrides .env silently)
36
39
 
37
40
  ## Example output
38
41
 
39
42
  Running safelaunch...
40
43
 
41
- project type: Node.js
44
+ project type: Next.js
42
45
 
43
46
  ⚠️ RUNTIME MISMATCH
44
47
 
@@ -49,6 +52,10 @@ project type: Node.js
49
52
 
50
53
  express in package.json but not installed
51
54
 
55
+ ⚠️ ENV FILE PRIORITY (1 found)
56
+
57
+ DATABASE_URL in both .env and .env.local (.env.local takes priority)
58
+
52
59
  ❌ EMPTY VARIABLES (1 found)
53
60
 
54
61
  DATABASE_URL required but empty in .env
@@ -68,9 +75,9 @@ Your environment is not ready for production.
68
75
  safelaunch automatically detects your project type.
69
76
 
70
77
  - Node.js scans process.env
71
- - Next.js scans process.env
72
- - Vite scans import.meta.env
73
- - React CRA scans process.env REACT_APP_ variables
78
+ - Next.js scans process.env, checks NEXT_PUBLIC_ and .env priority
79
+ - Vite scans import.meta.env, checks VITE_ prefix
80
+ - React CRA scans process.env, checks REACT_APP_ prefix
74
81
 
75
82
  ## CI Integration
76
83
 
@@ -78,7 +85,6 @@ Add this to your GitHub Actions workflow:
78
85
 
79
86
  - name: Install safelaunch
80
87
  run: npm install -g safelaunch
81
-
82
88
  - name: Validate environment
83
89
  run: safelaunch validate
84
90
 
@@ -87,5 +93,6 @@ Blocks deployments automatically if any check fails.
87
93
  ## Built by Orches
88
94
 
89
95
  Deployment Reliability Infrastructure.
96
+
90
97
  GitHub: https://github.com/karthicedric7-cloud/safelaunch
91
98
  VS Code: https://marketplace.visualstudio.com/items?itemName=Orches.deploycheck-vscode
@@ -0,0 +1,15 @@
1
+ {
2
+ // Use IntelliSense to learn about possible attributes.
3
+ // Hover to view descriptions of existing attributes.
4
+ // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
5
+ "version": "0.2.0",
6
+ "configurations": [
7
+ {
8
+ "type": "chrome",
9
+ "request": "launch",
10
+ "name": "Launch Chrome against localhost",
11
+ "url": "http://localhost:8080",
12
+ "webRoot": "${workspaceFolder}"
13
+ }
14
+ ]
15
+ }
@@ -0,0 +1,64 @@
1
+ # deploycheck
2
+
3
+ > Catch what breaks production before it breaks.
4
+
5
+ Real time environment validation inside VS Code. deploycheck runs 7 checks on your environment before you push to production.
6
+
7
+ Works with Node.js, Next.js, Vite, and Create React App. Automatically detects your project type.
8
+
9
+ ## Commands
10
+
11
+ Open the Command Palette with Ctrl+Shift+P and run:
12
+
13
+ **deploycheck: Generate env.manifest.json**
14
+ Scans your entire project for environment variables and automatically generates env.manifest.json.
15
+
16
+ Works with:
17
+ - process.env for Node.js and Next.js projects
18
+ - import.meta.env for Vite projects
19
+ - REACT_APP_ variables for Create React App
20
+
21
+ **deploycheck: Validate Environment**
22
+ Runs 7 checks on your environment and tells you exactly what will break before you push.
23
+
24
+ ## What deploycheck checks
25
+
26
+ 1. Missing required environment variables
27
+ 2. Empty required environment variables
28
+ 3. Runtime version mismatch (Node version)
29
+ 4. Duplicate variables in .env
30
+ 5. Dependencies not installed
31
+ 6. Dependency drift (in package.json but not installed)
32
+ 7. Variables in .env.example but missing from .env
33
+
34
+ ## Supported project types
35
+
36
+ deploycheck automatically detects your project type.
37
+
38
+ - Node.js scans process.env
39
+ - Next.js scans process.env
40
+ - Vite scans import.meta.env
41
+ - React CRA scans REACT_APP_ variables
42
+
43
+ ## How it works
44
+
45
+ Step 1: Open your project in VS Code
46
+ Step 2: Press Ctrl+Shift+P
47
+ Step 3: Type deploycheck
48
+ Step 4: Run Generate to create your manifest
49
+ Step 5: Run Validate before every push
50
+
51
+ ## CLI Version
52
+
53
+ For terminal and CI use install the CLI:
54
+
55
+ npm install -g safelaunch
56
+
57
+ safelaunch init
58
+ safelaunch validate
59
+
60
+ ## Built by Orches
61
+
62
+ JavaScript Reliability Infrastructure.
63
+ GitHub: https://github.com/karthicedric7-cloud/safelaunch
64
+ npm: https://www.npmjs.com/package/safelaunch
@@ -0,0 +1,54 @@
1
+ const fs = require('fs');
2
+ const path = require('path');
3
+
4
+ function scanFiles(dir, extensions, found = new Set()) {
5
+ const items = fs.readdirSync(dir);
6
+ for (const item of items) {
7
+ if (item === 'node_modules' || item === '.git') continue;
8
+ const full = path.join(dir, item);
9
+ const stat = fs.statSync(full);
10
+ if (stat.isDirectory()) {
11
+ scanFiles(full, extensions, found);
12
+ } else if (extensions.some(ext => item.endsWith(ext))) {
13
+ const content = fs.readFileSync(full, 'utf8');
14
+ const matches = content.matchAll(/process\.env\.([A-Z_][A-Z0-9_]*)/g);
15
+ for (const match of matches) {
16
+ found.add(match[1]);
17
+ }
18
+ }
19
+ }
20
+ return found;
21
+ }
22
+
23
+ function init() {
24
+ console.log('\nscanning project for environment variables...\n');
25
+ const cwd = process.cwd();
26
+ const extensions = ['.js', '.ts', '.jsx', '.tsx'];
27
+ const found = scanFiles(cwd, extensions);
28
+ if (found.size === 0) {
29
+ console.log('no process.env references found.\n');
30
+ return;
31
+ }
32
+ const variables = {};
33
+ for (const key of [...found].sort()) {
34
+ variables[key] = { required: true, description: '' };
35
+ }
36
+ const manifest = {
37
+ version: '1',
38
+ runtime: { node: process.version.replace('v', '').split('.')[0] },
39
+ variables
40
+ };
41
+ const manifestPath = path.join(cwd, 'env.manifest.json');
42
+ if (fs.existsSync(manifestPath)) {
43
+ console.log('env.manifest.json already exists. delete it first.\n');
44
+ return;
45
+ }
46
+ fs.writeFileSync(manifestPath, JSON.stringify(manifest, null, 2));
47
+ console.log('found ' + found.size + ' environment variables:\n');
48
+ for (const key of [...found].sort()) {
49
+ console.log(' ' + key);
50
+ }
51
+ console.log('\ncreated env.manifest.json\n');
52
+ }
53
+
54
+ init();
@@ -0,0 +1,35 @@
1
+ {
2
+ "name": "deploycheck-vscode",
3
+ "displayName": "deploycheck",
4
+ "description": "Catch what breaks production before it breaks. Real time environment validation inside VS Code.",
5
+ "version": "0.0.10",
6
+ "publisher": "Orches",
7
+ "engines": {
8
+ "vscode": "^1.74.0"
9
+ },
10
+ "categories": [
11
+ "Linters"
12
+ ],
13
+ "activationEvents": [
14
+ "*"
15
+ ],
16
+ "main": "./src/extension.js",
17
+ "contributes": {
18
+ "commands": [
19
+ {
20
+ "command": "deploycheck.init",
21
+ "title": "deploycheck: Generate env.manifest.json"
22
+ },
23
+ {
24
+ "command": "deploycheck.validate",
25
+ "title": "deploycheck: Validate Environment"
26
+ }
27
+ ]
28
+ },
29
+ "scripts": {
30
+ "test": "echo \"Error: no test specified\" && exit 1"
31
+ },
32
+ "devDependencies": {
33
+ "@types/vscode": "^1.74.0"
34
+ }
35
+ }
@@ -0,0 +1,199 @@
1
+ const vscode = require('vscode');
2
+ const fs = require('fs');
3
+ const path = require('path');
4
+
5
+ function detectProjectType(cwd) {
6
+ if (fs.existsSync(path.join(cwd, 'vite.config.js')) ||
7
+ fs.existsSync(path.join(cwd, 'vite.config.ts'))) {
8
+ return 'vite';
9
+ }
10
+ if (fs.existsSync(path.join(cwd, 'next.config.js')) ||
11
+ fs.existsSync(path.join(cwd, 'next.config.ts'))) {
12
+ return 'next';
13
+ }
14
+ const packagePath = path.join(cwd, 'package.json');
15
+ if (fs.existsSync(packagePath)) {
16
+ const pkg = JSON.parse(fs.readFileSync(packagePath, 'utf8'));
17
+ const deps = Object.assign({}, pkg.dependencies, pkg.devDependencies);
18
+ if (deps['react-scripts']) return 'cra';
19
+ }
20
+ return 'node';
21
+ }
22
+
23
+ function scanFiles(dir, extensions, pattern, found = new Set()) {
24
+ const items = fs.readdirSync(dir);
25
+ for (const item of items) {
26
+ if (item === 'node_modules' || item === '.git') continue;
27
+ const full = path.join(dir, item);
28
+ const stat = fs.statSync(full);
29
+ if (stat.isDirectory()) {
30
+ scanFiles(full, extensions, pattern, found);
31
+ } else if (extensions.some(ext => item.endsWith(ext))) {
32
+ const content = fs.readFileSync(full, 'utf8');
33
+ const matches = content.matchAll(pattern);
34
+ for (const match of matches) found.add(match[1]);
35
+ }
36
+ }
37
+ return found;
38
+ }
39
+
40
+ function activate(context) {
41
+ const initCommand = vscode.commands.registerCommand('deploycheck.init', function () {
42
+ const workspaceFolders = vscode.workspace.workspaceFolders;
43
+ if (!workspaceFolders) {
44
+ vscode.window.showErrorMessage('deploycheck: No workspace folder open.');
45
+ return;
46
+ }
47
+ const cwd = workspaceFolders[0].uri.fsPath;
48
+ const manifestPath = path.join(cwd, 'env.manifest.json');
49
+
50
+ if (fs.existsSync(manifestPath)) {
51
+ vscode.window.showWarningMessage('deploycheck: env.manifest.json already exists. Delete it first to regenerate.');
52
+ return;
53
+ }
54
+
55
+ const projectType = detectProjectType(cwd);
56
+ const extensions = ['.js', '.ts', '.jsx', '.tsx', '.vue', '.svelte'];
57
+ const typeLabels = { vite: 'Vite', next: 'Next.js', cra: 'Create React App', node: 'Node.js' };
58
+
59
+ let pattern;
60
+ if (projectType === 'vite') {
61
+ pattern = /import\.meta\.env\.([A-Z_][A-Z0-9_]*)/g;
62
+ } else if (projectType === 'cra') {
63
+ pattern = /process\.env\.(REACT_APP_[A-Z0-9_]*)/g;
64
+ } else {
65
+ pattern = /process\.env\.([A-Z_][A-Z0-9_]*)/g;
66
+ }
67
+
68
+ const found = scanFiles(cwd, extensions, pattern);
69
+
70
+ if (found.size === 0) {
71
+ vscode.window.showInformationMessage('deploycheck: No environment variables found in your ' + typeLabels[projectType] + ' project.');
72
+ return;
73
+ }
74
+
75
+ const variables = {};
76
+ for (const key of [...found].sort()) {
77
+ variables[key] = { required: true, description: '' };
78
+ }
79
+
80
+ const manifest = {
81
+ version: '1',
82
+ projectType: projectType,
83
+ runtime: { node: process.version.replace('v', '').split('.')[0] },
84
+ variables
85
+ };
86
+
87
+ fs.writeFileSync(manifestPath, JSON.stringify(manifest, null, 2));
88
+ vscode.window.showInformationMessage('deploycheck: Found ' + found.size + ' variables in ' + typeLabels[projectType] + ' project. Created env.manifest.json');
89
+
90
+ vscode.workspace.openTextDocument(manifestPath).then(doc => {
91
+ vscode.window.showTextDocument(doc);
92
+ });
93
+ });
94
+
95
+ const validateCommand = vscode.commands.registerCommand('deploycheck.validate', function () {
96
+ const workspaceFolders = vscode.workspace.workspaceFolders;
97
+ if (!workspaceFolders) {
98
+ vscode.window.showErrorMessage('deploycheck: No workspace folder open.');
99
+ return;
100
+ }
101
+ const cwd = workspaceFolders[0].uri.fsPath;
102
+ const manifestPath = path.join(cwd, 'env.manifest.json');
103
+
104
+ if (!fs.existsSync(manifestPath)) {
105
+ vscode.window.showWarningMessage('deploycheck: No env.manifest.json found. Run deploycheck.init first.');
106
+ return;
107
+ }
108
+
109
+ const envPath = path.join(cwd, '.env');
110
+ if (!fs.existsSync(envPath)) {
111
+ vscode.window.showErrorMessage('deploycheck: No .env file found.');
112
+ return;
113
+ }
114
+
115
+ const envVars = {};
116
+ const duplicates = [];
117
+ const seen = new Set();
118
+ const lines = fs.readFileSync(envPath, 'utf8').split('\n');
119
+ for (const line of lines) {
120
+ const match = line.match(/^([^=]+)=(.*)$/);
121
+ if (match) {
122
+ const key = match[1].trim();
123
+ if (seen.has(key)) duplicates.push(key);
124
+ seen.add(key);
125
+ envVars[key] = match[2].trim();
126
+ }
127
+ }
128
+
129
+ const manifest = JSON.parse(fs.readFileSync(manifestPath, 'utf8'));
130
+ const projectType = manifest.projectType || detectProjectType(cwd);
131
+ const typeLabels = { vite: 'Vite', next: 'Next.js', cra: 'Create React App', node: 'Node.js' };
132
+
133
+ const missing = [];
134
+ const empty = [];
135
+
136
+ for (const [key, val] of Object.entries(manifest.variables || {})) {
137
+ if (val.required && key in envVars && envVars[key] === '') {
138
+ empty.push(key);
139
+ } else if (val.required && !(key in envVars)) {
140
+ missing.push(key);
141
+ }
142
+ }
143
+
144
+ const examplePath = path.join(cwd, '.env.example');
145
+ const exampleMissing = [];
146
+ if (fs.existsSync(examplePath)) {
147
+ const exLines = fs.readFileSync(examplePath, 'utf8').split('\n');
148
+ for (const line of exLines) {
149
+ const match = line.match(/^([^=]+)=(.*)$/);
150
+ if (match && !envVars[match[1].trim()]) exampleMissing.push(match[1].trim());
151
+ }
152
+ }
153
+
154
+ const nodeRequired = manifest.runtime && manifest.runtime.node ? String(manifest.runtime.node) : null;
155
+ const nodeActual = process.version.replace('v', '').split('.')[0];
156
+ const runtimeMismatch = nodeRequired && nodeRequired !== nodeActual;
157
+
158
+ const modulesPath = path.join(cwd, 'node_modules');
159
+ const packagePath = path.join(cwd, 'package.json');
160
+ let driftMissing = [];
161
+ let notInstalled = false;
162
+ if (fs.existsSync(packagePath)) {
163
+ if (!fs.existsSync(modulesPath)) {
164
+ notInstalled = true;
165
+ } else {
166
+ const pkg = JSON.parse(fs.readFileSync(packagePath, 'utf8'));
167
+ const deps = Object.keys(pkg.dependencies || {});
168
+ driftMissing = deps.filter(dep => !fs.existsSync(path.join(modulesPath, dep)));
169
+ }
170
+ }
171
+
172
+ const problems = [];
173
+ if (runtimeMismatch) problems.push('Runtime mismatch: need Node ' + nodeRequired + ' got ' + nodeActual);
174
+ if (duplicates.length > 0) problems.push('Duplicates: ' + duplicates.join(', '));
175
+ if (notInstalled) problems.push('node_modules missing. Run npm install.');
176
+ if (driftMissing.length > 0) problems.push('Drift: ' + driftMissing.join(', '));
177
+ if (exampleMissing.length > 0) problems.push('Missing from .env.example: ' + exampleMissing.join(', '));
178
+ if (empty.length > 0) problems.push('Empty: ' + empty.join(', '));
179
+ if (missing.length > 0) problems.push('Missing: ' + missing.join(', '));
180
+
181
+ const label = typeLabels[projectType] || 'Node.js';
182
+
183
+ if (problems.length === 0) {
184
+ vscode.window.showInformationMessage('deploycheck (' + label + '): ✅ Environment is ready for production.');
185
+ } else {
186
+ vscode.window.showErrorMessage('deploycheck (' + label + '): ❌ ' + problems[0]);
187
+ if (problems.length > 1) {
188
+ vscode.window.showWarningMessage('deploycheck: ' + (problems.length - 1) + ' more issue(s): ' + problems.slice(1).join(' | '));
189
+ }
190
+ }
191
+ });
192
+
193
+ context.subscriptions.push(initCommand);
194
+ context.subscriptions.push(validateCommand);
195
+ }
196
+
197
+ function deactivate() {}
198
+
199
+ module.exports = { activate, deactivate };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "safelaunch",
3
- "version": "1.0.9",
3
+ "version": "1.0.11",
4
4
  "description": "Backend Reliability Infrastructure - catch what breaks production before it breaks",
5
5
  "main": "index.js",
6
6
  "bin": {
package/src/init.js CHANGED
@@ -54,4 +54,54 @@ async function init() {
54
54
  const projectType = detectProjectType(cwd);
55
55
  const extensions = ['.js', '.ts', '.jsx', '.tsx', '.vue', '.svelte'];
56
56
  let pattern;
57
- let typeLabel;
57
+ let typeLabel;
58
+
59
+ if (projectType === 'vite') {
60
+ pattern = /import\.meta\.env\.([A-Z_][A-Z0-9_]*)/g;
61
+ typeLabel = 'Vite';
62
+ } else if (projectType === 'cra') {
63
+ pattern = /process\.env\.(REACT_APP_[A-Z0-9_]*)/g;
64
+ typeLabel = 'Create React App';
65
+ } else if (projectType === 'next') {
66
+ pattern = /process\.env\.([A-Z_][A-Z0-9_]*)/g;
67
+ typeLabel = 'Next.js';
68
+ } else {
69
+ pattern = /process\.env\.([A-Z_][A-Z0-9_]*)/g;
70
+ typeLabel = 'Node.js';
71
+ }
72
+
73
+ console.log('detected project type: ' + typeLabel + '\n');
74
+ const found = scanFiles(cwd, extensions, pattern);
75
+
76
+ if (found.size === 0) {
77
+ console.log('no environment variables found in your project.\n');
78
+ await track('safelaunch_init_run', { project_type: projectType, vars_found: 0 });
79
+ await shutdown();
80
+ return;
81
+ }
82
+
83
+ const variables = {};
84
+ for (const key of [...found].sort()) {
85
+ variables[key] = { required: true, description: '' };
86
+ }
87
+
88
+ const manifest = {
89
+ version: '1',
90
+ projectType: projectType,
91
+ runtime: { node: process.version.replace('v', '').split('.')[0] },
92
+ variables
93
+ };
94
+
95
+ fs.writeFileSync(manifestPath, JSON.stringify(manifest, null, 2));
96
+
97
+ console.log('found ' + found.size + ' environment variables:\n');
98
+ for (const key of [...found].sort()) {
99
+ console.log(' ' + key);
100
+ }
101
+ console.log('\ncreated env.manifest.json\n');
102
+
103
+ await track('safelaunch_init_run', { project_type: projectType, vars_found: found.size });
104
+ await shutdown();
105
+ }
106
+
107
+ init();
package/src/telemetry.js CHANGED
@@ -1,6 +1,6 @@
1
1
  const { PostHog } = require('posthog-node');
2
2
 
3
- const client = new PostHog('phx_T3nHhNj4dVFDHRHbDaqoXTmqeic8kM6fa3nyjXpTanqxcVD', {
3
+ const client = new PostHog('phc_WFm3O5H7wkZK2Ne3kyy5QgUXJR8y86SQbszSwk3BUn0', {
4
4
  host: 'https://us.i.posthog.com'
5
5
  });
6
6
 
package/src/validate.js CHANGED
@@ -96,6 +96,49 @@ function checkDependencyDrift() {
96
96
  return { notInstalled: false, missing };
97
97
  }
98
98
 
99
+ function checkPrefixes(envVars, projectType) {
100
+ const warnings = [];
101
+ for (const key of Object.keys(envVars)) {
102
+ if (key === 'NODE_ENV') continue;
103
+ if (projectType === 'vite' && !key.startsWith('VITE_')) {
104
+ warnings.push(key + ' missing VITE_ prefix (won\'t be exposed to client)');
105
+ }
106
+ if (projectType === 'cra' && !key.startsWith('REACT_APP_')) {
107
+ warnings.push(key + ' missing REACT_APP_ prefix (won\'t be exposed to client)');
108
+ }
109
+ if (projectType === 'next' && key.startsWith('NEXT_PUBLIC_') === false) {
110
+ // server-side only — no warning needed
111
+ }
112
+ }
113
+ return warnings;
114
+ }
115
+
116
+ function checkEnvPriority(cwd, projectType) {
117
+ if (projectType !== 'next') return [];
118
+ const warnings = [];
119
+ const fileVars = {};
120
+ const envFiles = ['.env', '.env.local', '.env.development', '.env.production'];
121
+ for (const file of envFiles) {
122
+ const filePath = path.join(cwd, file);
123
+ if (!fs.existsSync(filePath)) continue;
124
+ const vars = {};
125
+ const lines = fs.readFileSync(filePath, 'utf8').split('\n');
126
+ for (const line of lines) {
127
+ const match = line.match(/^([^=]+)=(.*)$/);
128
+ if (match) vars[match[1].trim()] = match[2].trim();
129
+ }
130
+ fileVars[file] = vars;
131
+ }
132
+ if (fileVars['.env'] && fileVars['.env.local']) {
133
+ for (const key of Object.keys(fileVars['.env'])) {
134
+ if (fileVars['.env.local'][key]) {
135
+ warnings.push(key + ' in both .env and .env.local (.env.local takes priority)');
136
+ }
137
+ }
138
+ }
139
+ return warnings;
140
+ }
141
+
99
142
  async function validate() {
100
143
  console.log('\nRunning safelaunch...\n');
101
144
 
@@ -124,6 +167,8 @@ async function validate() {
124
167
  const runtimeMismatch = checkRuntime(manifest);
125
168
  const exampleMissing = checkEnvExample(envVars);
126
169
  const drift = checkDependencyDrift();
170
+ const prefixWarnings = checkPrefixes(envVars, projectType);
171
+ const priorityWarnings = checkEnvPriority(cwd, projectType);
127
172
 
128
173
  if (runtimeMismatch) {
129
174
  console.log('⚠️ RUNTIME MISMATCH\n');
@@ -161,6 +206,22 @@ async function validate() {
161
206
  console.log('');
162
207
  }
163
208
 
209
+ if (prefixWarnings.length > 0) {
210
+ console.log('⚠️ PREFIX WARNINGS (' + prefixWarnings.length + ' found)\n');
211
+ for (const w of prefixWarnings) {
212
+ console.log(' ' + w);
213
+ }
214
+ console.log('');
215
+ }
216
+
217
+ if (priorityWarnings.length > 0) {
218
+ console.log('⚠️ ENV FILE PRIORITY (' + priorityWarnings.length + ' found)\n');
219
+ for (const w of priorityWarnings) {
220
+ console.log(' ' + w);
221
+ }
222
+ console.log('');
223
+ }
224
+
164
225
  if (empty.length > 0) {
165
226
  console.log('❌ EMPTY VARIABLES (' + empty.length + ' found)\n');
166
227
  for (const key of empty) {
@@ -187,6 +248,7 @@ async function validate() {
187
248
 
188
249
  const hasFailed = runtimeMismatch || missing.length > 0 || empty.length > 0 ||
189
250
  duplicates.length > 0 || exampleMissing.length > 0 ||
251
+ prefixWarnings.length > 0 || priorityWarnings.length > 0 ||
190
252
  (drift && (drift.notInstalled || drift.missing.length > 0));
191
253
 
192
254
  if (hasFailed) {
@@ -198,7 +260,9 @@ async function validate() {
198
260
  empty: empty.length,
199
261
  duplicates: duplicates.length,
200
262
  runtime_mismatch: !!runtimeMismatch,
201
- dependency_drift: !!(drift && drift.missing.length > 0)
263
+ dependency_drift: !!(drift && drift.missing.length > 0),
264
+ prefix_warnings: prefixWarnings.length,
265
+ priority_warnings: priorityWarnings.length
202
266
  });
203
267
  await shutdown();
204
268
  process.exit(1);
@@ -213,9 +277,4 @@ async function validate() {
213
277
  }
214
278
  }
215
279
 
216
- validate();
217
- ```
218
-
219
- Save with **Cmd + S**. Now we need to publish this to npm. Go to Terminal and run:
220
- ```
221
- cd ~/safelaunch/safelaunch
280
+ validate();