safelaunch 1.0.7 → 1.0.8

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.
Files changed (3) hide show
  1. package/README.md +23 -30
  2. package/package.json +1 -1
  3. package/src/validate.js +91 -28
package/README.md CHANGED
@@ -4,7 +4,7 @@
4
4
 
5
5
  safelaunch is a comprehensive environment validator for JavaScript projects. Run it before every push to catch everything that will break production.
6
6
 
7
- Works with Node.js, Next.js, Vite, and Create React App projects. Automatically detects your project type and runs the right checks.
7
+ Automatically detects your project type and runs the right checks.
8
8
 
9
9
  ## Install
10
10
 
@@ -16,7 +16,9 @@ npm install -g safelaunch
16
16
 
17
17
  safelaunch init
18
18
 
19
- Scans your entire codebase, finds every environment variable your app uses, and generates env.manifest.json automatically. Works with process.env and import.meta.env.
19
+ Scans your entire codebase and generates env.manifest.json automatically.
20
+
21
+ Works with process.env and import.meta.env.
20
22
 
21
23
  **Step 2: Validate before every push**
22
24
 
@@ -24,7 +26,7 @@ safelaunch validate
24
26
 
25
27
  ## What safelaunch checks
26
28
 
27
- safelaunch validate runs 7 checks:
29
+ safelaunch validate runs 11 checks:
28
30
 
29
31
  1. Missing required environment variables
30
32
  2. Empty required environment variables
@@ -33,59 +35,50 @@ safelaunch validate runs 7 checks:
33
35
  5. Dependencies not installed
34
36
  6. Dependency drift (in package.json but not installed)
35
37
  7. Variables in .env.example but missing from .env
38
+ 8. Missing VITE_ prefix for Vite projects
39
+ 9. Missing NEXT_PUBLIC_ prefix for Next.js projects
40
+ 10. Missing REACT_APP_ prefix for Create React App
41
+ 11. .env file priority order for Next.js projects
36
42
 
37
- ## Example output
38
-
39
- Running safelaunch...
43
+ ## Supported project types
40
44
 
41
- project type: Node.js
45
+ safelaunch automatically detects your project type.
42
46
 
43
- ⚠️ RUNTIME MISMATCH
47
+ - Node.js scans process.env
48
+ - Next.js scans process.env, checks NEXT_PUBLIC_ prefixes
49
+ - Vite scans import.meta.env, checks VITE_ prefixes
50
+ - React CRA scans REACT_APP_ variables
44
51
 
45
- Node required: 18
46
- Node actual: 20
52
+ ## Example output
47
53
 
48
- ⚠️ DEPENDENCY DRIFT (1 found)
54
+ Running safelaunch...
49
55
 
50
- express in package.json but not installed
56
+ project type: Vite
51
57
 
52
- EMPTY VARIABLES (1 found)
58
+ ⚠️ FRAMEWORK PREFIX WARNINGS (1 found)
53
59
 
54
- DATABASE_URL required but empty in .env
60
+ API_URL missing VITE_ prefix - will be undefined in browser
55
61
 
56
62
  ❌ MISSING VARIABLES (1 found)
57
63
 
58
- REDIS_URL required but missing from .env
64
+ VITE_STRIPE_KEY required but missing from .env
59
65
 
60
66
  ✅ PASSING (1)
61
67
 
62
- API_KEY present
68
+ VITE_API_URL present
63
69
 
64
70
  Your environment is not ready for production.
65
71
 
66
- ## Supported project types
67
-
68
- safelaunch automatically detects your project type.
69
-
70
- - 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
74
-
75
72
  ## CI Integration
76
73
 
77
- Add this to your GitHub Actions workflow:
78
-
79
74
  - name: Install safelaunch
80
75
  run: npm install -g safelaunch
81
76
 
82
77
  - name: Validate environment
83
78
  run: safelaunch validate
84
79
 
85
- Blocks deployments automatically if any check fails.
86
-
87
80
  ## Built by Orches
88
81
 
89
- Deployment Reliability Infrastructure.
82
+ JavaScript Reliability Infrastructure.
90
83
  GitHub: https://github.com/karthicedric7-cloud/safelaunch
91
84
  VS Code: https://marketplace.visualstudio.com/items?itemName=Orches.deploycheck-vscode
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "safelaunch",
3
- "version": "1.0.7",
3
+ "version": "1.0.8",
4
4
  "description": "Backend Reliability Infrastructure - catch what breaks production before it breaks",
5
5
  "main": "index.js",
6
6
  "bin": {
package/src/validate.js CHANGED
@@ -28,30 +28,42 @@ function readManifest() {
28
28
  return JSON.parse(fs.readFileSync(manifestPath, 'utf8'));
29
29
  }
30
30
 
31
- function readEnv() {
32
- const envPath = path.join(process.cwd(), '.env');
33
- if (!fs.existsSync(envPath)) {
34
- console.log('\nNo .env file found.\n');
35
- process.exit(1);
36
- }
31
+ function readEnv(cwd) {
32
+ const envFiles = ['.env.local', '.env.development.local', '.env.production.local', '.env.development', '.env.production', '.env'];
37
33
  const envVars = {};
38
34
  const duplicates = [];
39
35
  const seen = new Set();
40
- const lines = fs.readFileSync(envPath, 'utf8').split('\n');
41
- for (const line of lines) {
42
- const match = line.match(/^([^=]+)=(.*)$/);
43
- if (match) {
44
- const key = match[1].trim();
45
- if (seen.has(key)) duplicates.push(key);
46
- seen.add(key);
47
- envVars[key] = match[2].trim();
36
+ const loadedFiles = [];
37
+
38
+ for (const file of envFiles) {
39
+ const envPath = path.join(cwd, file);
40
+ if (!fs.existsSync(envPath)) continue;
41
+ loadedFiles.push(file);
42
+ const lines = fs.readFileSync(envPath, 'utf8').split('\n');
43
+ for (const line of lines) {
44
+ const match = line.match(/^([^=]+)=(.*)$/);
45
+ if (match) {
46
+ const key = match[1].trim();
47
+ if (seen.has(key)) {
48
+ duplicates.push(key + ' (in ' + file + ')');
49
+ } else {
50
+ seen.add(key);
51
+ envVars[key] = match[2].trim();
52
+ }
53
+ }
48
54
  }
49
55
  }
50
- return { envVars, duplicates };
56
+
57
+ if (loadedFiles.length === 0) {
58
+ console.log('\nNo .env file found.\n');
59
+ process.exit(1);
60
+ }
61
+
62
+ return { envVars, duplicates, loadedFiles };
51
63
  }
52
64
 
53
- function readEnvExample() {
54
- const envExamplePath = path.join(process.cwd(), '.env.example');
65
+ function readEnvExample(cwd) {
66
+ const envExamplePath = path.join(cwd, '.env.example');
55
67
  if (!fs.existsSync(envExamplePath)) return null;
56
68
  const envVars = {};
57
69
  const lines = fs.readFileSync(envExamplePath, 'utf8').split('\n');
@@ -70,8 +82,8 @@ function checkRuntime(manifest) {
70
82
  return null;
71
83
  }
72
84
 
73
- function checkEnvExample(envVars) {
74
- const example = readEnvExample();
85
+ function checkEnvExample(envVars, cwd) {
86
+ const example = readEnvExample(cwd);
75
87
  if (!example) return [];
76
88
  const missing = [];
77
89
  for (const key of Object.keys(example)) {
@@ -80,9 +92,9 @@ function checkEnvExample(envVars) {
80
92
  return missing;
81
93
  }
82
94
 
83
- function checkDependencyDrift() {
84
- const packagePath = path.join(process.cwd(), 'package.json');
85
- const modulesPath = path.join(process.cwd(), 'node_modules');
95
+ function checkDependencyDrift(cwd) {
96
+ const packagePath = path.join(cwd, 'package.json');
97
+ const modulesPath = path.join(cwd, 'node_modules');
86
98
  if (!fs.existsSync(packagePath)) return null;
87
99
  if (!fs.existsSync(modulesPath)) return { notInstalled: true, missing: [] };
88
100
  const pkg = JSON.parse(fs.readFileSync(packagePath, 'utf8'));
@@ -95,17 +107,57 @@ function checkDependencyDrift() {
95
107
  return { notInstalled: false, missing };
96
108
  }
97
109
 
110
+ function checkFrameworkPrefixes(envVars, manifest, projectType) {
111
+ const warnings = [];
112
+ const variables = Object.keys(manifest.variables || {});
113
+
114
+ if (projectType === 'vite') {
115
+ for (const key of variables) {
116
+ if (!key.startsWith('VITE_')) {
117
+ warnings.push(key + ' missing VITE_ prefix - will be undefined in browser');
118
+ }
119
+ }
120
+ }
121
+
122
+ if (projectType === 'next') {
123
+ for (const key of variables) {
124
+ if (!key.startsWith('NEXT_PUBLIC_') && !key.startsWith('NEXT_') ) {
125
+ warnings.push(key + ' consider NEXT_PUBLIC_ prefix to expose to browser');
126
+ }
127
+ }
128
+ }
129
+
130
+ if (projectType === 'cra') {
131
+ for (const key of variables) {
132
+ if (!key.startsWith('REACT_APP_')) {
133
+ warnings.push(key + ' missing REACT_APP_ prefix - will be undefined in browser');
134
+ }
135
+ }
136
+ }
137
+
138
+ return warnings;
139
+ }
140
+
98
141
  function validate() {
99
142
  console.log('\nRunning safelaunch...\n');
100
143
 
101
144
  const cwd = process.cwd();
102
145
  const manifest = readManifest();
103
- const { envVars, duplicates } = readEnv();
104
-
105
146
  const projectType = manifest.projectType || detectProjectType(cwd);
147
+
106
148
  const typeLabels = { vite: 'Vite', next: 'Next.js', cra: 'Create React App', node: 'Node.js' };
107
149
  console.log('project type: ' + (typeLabels[projectType] || 'Node.js') + '\n');
108
150
 
151
+ const { envVars, duplicates, loadedFiles } = readEnv(cwd);
152
+
153
+ if (loadedFiles.length > 1) {
154
+ console.log('env files loaded (priority order):\n');
155
+ for (const file of loadedFiles) {
156
+ console.log(' ' + file);
157
+ }
158
+ console.log('');
159
+ }
160
+
109
161
  const missing = [];
110
162
  const empty = [];
111
163
  const passing = [];
@@ -121,8 +173,9 @@ function validate() {
121
173
  }
122
174
 
123
175
  const runtimeMismatch = checkRuntime(manifest);
124
- const exampleMissing = checkEnvExample(envVars);
125
- const drift = checkDependencyDrift();
176
+ const exampleMissing = checkEnvExample(envVars, cwd);
177
+ const drift = checkDependencyDrift(cwd);
178
+ const prefixWarnings = checkFrameworkPrefixes(envVars, manifest, projectType);
126
179
 
127
180
  if (runtimeMismatch) {
128
181
  console.log('⚠️ RUNTIME MISMATCH\n');
@@ -134,7 +187,7 @@ function validate() {
134
187
  if (duplicates.length > 0) {
135
188
  console.log('⚠️ DUPLICATE VARIABLES (' + duplicates.length + ' found)\n');
136
189
  for (const key of duplicates) {
137
- console.log(' ' + key + ' defined more than once in .env');
190
+ console.log(' ' + key);
138
191
  }
139
192
  console.log('');
140
193
  }
@@ -152,6 +205,14 @@ function validate() {
152
205
  }
153
206
  }
154
207
 
208
+ if (prefixWarnings.length > 0) {
209
+ console.log('⚠️ FRAMEWORK PREFIX WARNINGS (' + prefixWarnings.length + ' found)\n');
210
+ for (const warning of prefixWarnings) {
211
+ console.log(' ' + warning);
212
+ }
213
+ console.log('');
214
+ }
215
+
155
216
  if (exampleMissing.length > 0) {
156
217
  console.log('⚠️ MISSING FROM .env.example (' + exampleMissing.length + ' found)\n');
157
218
  for (const key of exampleMissing) {
@@ -184,7 +245,9 @@ function validate() {
184
245
  console.log('');
185
246
  }
186
247
 
187
- const hasFailed = runtimeMismatch || missing.length > 0 || empty.length > 0 || duplicates.length > 0 || exampleMissing.length > 0 || (drift && (drift.notInstalled || drift.missing.length > 0));
248
+ const hasFailed = runtimeMismatch || missing.length > 0 || empty.length > 0 ||
249
+ duplicates.length > 0 || exampleMissing.length > 0 || prefixWarnings.length > 0 ||
250
+ (drift && (drift.notInstalled || drift.missing.length > 0));
188
251
 
189
252
  if (hasFailed) {
190
253
  console.log('Your environment is not ready for production.\n');