genbox 1.0.12 → 1.0.13
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/dist/commands/config.js +273 -0
- package/dist/commands/migrate.js +246 -0
- package/dist/commands/resolve.js +243 -0
- package/dist/commands/scan.js +354 -0
- package/dist/commands/validate.js +529 -0
- package/dist/config-explainer.js +379 -0
- package/dist/detected-config.js +145 -0
- package/dist/index.js +12 -1
- package/dist/migration.js +334 -0
- package/dist/profile-resolver.js +8 -3
- package/dist/schema-v3.js +36 -0
- package/dist/schema-v4.js +54 -0
- package/dist/strict-mode.js +288 -0
- package/package.json +1 -1
|
@@ -0,0 +1,288 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Strict Mode Enforcement
|
|
4
|
+
*
|
|
5
|
+
* In strict mode (default for v4):
|
|
6
|
+
* - All app types must be explicit (no inference from names)
|
|
7
|
+
* - All ports must be explicit or use $detect markers
|
|
8
|
+
* - All connections must be declared in connects_to
|
|
9
|
+
* - No implicit defaults are applied
|
|
10
|
+
*
|
|
11
|
+
* This ensures reproducible, predictable behavior across
|
|
12
|
+
* different environments and team members.
|
|
13
|
+
*/
|
|
14
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
+
exports.DEFAULT_STRICT_CONFIG = void 0;
|
|
16
|
+
exports.validateStrictMode = validateStrictMode;
|
|
17
|
+
exports.getStrictModeSettings = getStrictModeSettings;
|
|
18
|
+
exports.isStrictModeEnabled = isStrictModeEnabled;
|
|
19
|
+
exports.formatStrictViolations = formatStrictViolations;
|
|
20
|
+
exports.generateStrictFixes = generateStrictFixes;
|
|
21
|
+
const schema_v3_1 = require("./schema-v3");
|
|
22
|
+
// ============================================
|
|
23
|
+
// Default Strict Mode Settings
|
|
24
|
+
// ============================================
|
|
25
|
+
exports.DEFAULT_STRICT_CONFIG = {
|
|
26
|
+
enabled: true,
|
|
27
|
+
allow_detect: true,
|
|
28
|
+
warnings_as_errors: false,
|
|
29
|
+
};
|
|
30
|
+
// ============================================
|
|
31
|
+
// Strict Mode Validation
|
|
32
|
+
// ============================================
|
|
33
|
+
/**
|
|
34
|
+
* Validate a configuration against strict mode rules
|
|
35
|
+
*/
|
|
36
|
+
function validateStrictMode(config, strictConfig = {}) {
|
|
37
|
+
const settings = { ...exports.DEFAULT_STRICT_CONFIG, ...strictConfig };
|
|
38
|
+
const violations = [];
|
|
39
|
+
let explicitValues = 0;
|
|
40
|
+
let detectMarkers = 0;
|
|
41
|
+
let inferredValues = 0;
|
|
42
|
+
// Check each app
|
|
43
|
+
for (const [appName, appConfig] of Object.entries(config.apps || {})) {
|
|
44
|
+
const appPath = `apps.${appName}`;
|
|
45
|
+
// Check app type
|
|
46
|
+
if (!appConfig.type) {
|
|
47
|
+
violations.push({
|
|
48
|
+
path: `${appPath}.type`,
|
|
49
|
+
type: 'missing_type',
|
|
50
|
+
message: `App "${appName}" has no type specified`,
|
|
51
|
+
suggestion: `Add explicit "type: frontend | backend | worker | gateway"`,
|
|
52
|
+
severity: settings.enabled ? 'error' : 'warning',
|
|
53
|
+
});
|
|
54
|
+
inferredValues++;
|
|
55
|
+
}
|
|
56
|
+
else if ((0, schema_v3_1.isDetectMarker)(appConfig.type)) {
|
|
57
|
+
if (!settings.allow_detect) {
|
|
58
|
+
violations.push({
|
|
59
|
+
path: `${appPath}.type`,
|
|
60
|
+
type: 'implicit_value',
|
|
61
|
+
message: `$detect markers are not allowed in strict mode`,
|
|
62
|
+
suggestion: `Replace $detect with explicit type value`,
|
|
63
|
+
severity: 'error',
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
detectMarkers++;
|
|
67
|
+
}
|
|
68
|
+
else {
|
|
69
|
+
explicitValues++;
|
|
70
|
+
// Check for naming-based inference (when type matches name pattern)
|
|
71
|
+
if (isTypeInferredFromName(appName, appConfig.type)) {
|
|
72
|
+
violations.push({
|
|
73
|
+
path: `${appPath}.type`,
|
|
74
|
+
type: 'naming_inference',
|
|
75
|
+
message: `App type "${appConfig.type}" appears to match naming convention`,
|
|
76
|
+
suggestion: `This is fine, but ensure the type is intentional, not just matching the name`,
|
|
77
|
+
severity: 'warning',
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
// Check port for backend apps
|
|
82
|
+
if (appConfig.type === 'backend' || appConfig.type === 'gateway') {
|
|
83
|
+
if (!appConfig.port) {
|
|
84
|
+
violations.push({
|
|
85
|
+
path: `${appPath}.port`,
|
|
86
|
+
type: 'missing_port',
|
|
87
|
+
message: `Backend app "${appName}" has no port specified`,
|
|
88
|
+
suggestion: `Add explicit "port: <number>" or "port: $detect"`,
|
|
89
|
+
severity: settings.enabled ? 'error' : 'warning',
|
|
90
|
+
});
|
|
91
|
+
inferredValues++;
|
|
92
|
+
}
|
|
93
|
+
else if ((0, schema_v3_1.isDetectMarker)(appConfig.port)) {
|
|
94
|
+
if (!settings.allow_detect) {
|
|
95
|
+
violations.push({
|
|
96
|
+
path: `${appPath}.port`,
|
|
97
|
+
type: 'implicit_value',
|
|
98
|
+
message: `$detect markers are not allowed in strict mode`,
|
|
99
|
+
suggestion: `Replace $detect with explicit port number`,
|
|
100
|
+
severity: 'error',
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
detectMarkers++;
|
|
104
|
+
}
|
|
105
|
+
else {
|
|
106
|
+
explicitValues++;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
// Check connections (v4 style)
|
|
110
|
+
const v4App = appConfig;
|
|
111
|
+
if (v4App.connects_to) {
|
|
112
|
+
for (const [connName, connConfig] of Object.entries(v4App.connects_to)) {
|
|
113
|
+
if (!connConfig || typeof connConfig !== 'object') {
|
|
114
|
+
violations.push({
|
|
115
|
+
path: `${appPath}.connects_to.${connName}`,
|
|
116
|
+
type: 'undeclared_connection',
|
|
117
|
+
message: `Invalid connection configuration for "${connName}"`,
|
|
118
|
+
suggestion: `Specify connection mode: { mode: local | staging | production }`,
|
|
119
|
+
severity: 'error',
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
explicitValues++;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
// Check for legacy requires (v3 style)
|
|
126
|
+
const v3App = appConfig;
|
|
127
|
+
if (v3App.requires) {
|
|
128
|
+
for (const [depName, requirement] of Object.entries(v3App.requires)) {
|
|
129
|
+
violations.push({
|
|
130
|
+
path: `${appPath}.requires.${depName}`,
|
|
131
|
+
type: 'undeclared_connection',
|
|
132
|
+
message: `Legacy "requires" syntax used`,
|
|
133
|
+
suggestion: `Use "connects_to: { ${depName}: { mode: local } }" instead`,
|
|
134
|
+
severity: settings.enabled ? 'error' : 'warning',
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
// Check provides/infrastructure
|
|
140
|
+
const v4Config = config;
|
|
141
|
+
if (v4Config.provides) {
|
|
142
|
+
for (const [infraName, infraConfig] of Object.entries(v4Config.provides)) {
|
|
143
|
+
if (!infraConfig.type || !infraConfig.image || !infraConfig.port) {
|
|
144
|
+
violations.push({
|
|
145
|
+
path: `provides.${infraName}`,
|
|
146
|
+
type: 'implicit_value',
|
|
147
|
+
message: `Infrastructure "${infraName}" missing required fields`,
|
|
148
|
+
suggestion: `Specify type, image, and port explicitly`,
|
|
149
|
+
severity: 'error',
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
else {
|
|
153
|
+
explicitValues += 3; // type, image, port
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
// Calculate summary
|
|
158
|
+
const errors = violations.filter(v => v.severity === 'error').length;
|
|
159
|
+
const warnings = violations.filter(v => v.severity === 'warning').length;
|
|
160
|
+
// In strict mode with warnings_as_errors, warnings become errors
|
|
161
|
+
const finalErrors = settings.warnings_as_errors ? errors + warnings : errors;
|
|
162
|
+
return {
|
|
163
|
+
valid: finalErrors === 0,
|
|
164
|
+
violations,
|
|
165
|
+
summary: {
|
|
166
|
+
errors,
|
|
167
|
+
warnings,
|
|
168
|
+
explicit_values: explicitValues,
|
|
169
|
+
detect_markers: detectMarkers,
|
|
170
|
+
inferred_values: inferredValues,
|
|
171
|
+
},
|
|
172
|
+
};
|
|
173
|
+
}
|
|
174
|
+
/**
|
|
175
|
+
* Check if a type appears to be inferred from the app name
|
|
176
|
+
*/
|
|
177
|
+
function isTypeInferredFromName(appName, type) {
|
|
178
|
+
const nameLower = appName.toLowerCase();
|
|
179
|
+
if (type === 'frontend') {
|
|
180
|
+
return /web|frontend|ui|client|app/.test(nameLower);
|
|
181
|
+
}
|
|
182
|
+
if (type === 'backend') {
|
|
183
|
+
return /api|backend|server|service/.test(nameLower);
|
|
184
|
+
}
|
|
185
|
+
if (type === 'worker') {
|
|
186
|
+
return /worker|queue|job|processor/.test(nameLower);
|
|
187
|
+
}
|
|
188
|
+
if (type === 'gateway') {
|
|
189
|
+
return /gateway|proxy|router/.test(nameLower);
|
|
190
|
+
}
|
|
191
|
+
return false;
|
|
192
|
+
}
|
|
193
|
+
// ============================================
|
|
194
|
+
// Strict Mode Helpers
|
|
195
|
+
// ============================================
|
|
196
|
+
/**
|
|
197
|
+
* Get strict mode settings from config
|
|
198
|
+
*/
|
|
199
|
+
function getStrictModeSettings(config) {
|
|
200
|
+
const v4Config = config;
|
|
201
|
+
if (v4Config.version === 4 && v4Config.strict) {
|
|
202
|
+
return { ...exports.DEFAULT_STRICT_CONFIG, ...v4Config.strict };
|
|
203
|
+
}
|
|
204
|
+
// v3 configs default to lenient mode
|
|
205
|
+
if (config.version !== 4) {
|
|
206
|
+
return {
|
|
207
|
+
enabled: false,
|
|
208
|
+
allow_detect: true,
|
|
209
|
+
warnings_as_errors: false,
|
|
210
|
+
};
|
|
211
|
+
}
|
|
212
|
+
return exports.DEFAULT_STRICT_CONFIG;
|
|
213
|
+
}
|
|
214
|
+
/**
|
|
215
|
+
* Check if strict mode is enabled
|
|
216
|
+
*/
|
|
217
|
+
function isStrictModeEnabled(config) {
|
|
218
|
+
return getStrictModeSettings(config).enabled;
|
|
219
|
+
}
|
|
220
|
+
/**
|
|
221
|
+
* Format strict mode violations for display
|
|
222
|
+
*/
|
|
223
|
+
function formatStrictViolations(result) {
|
|
224
|
+
const lines = [];
|
|
225
|
+
if (result.valid) {
|
|
226
|
+
lines.push('✓ Strict mode validation passed');
|
|
227
|
+
}
|
|
228
|
+
else {
|
|
229
|
+
lines.push('✗ Strict mode validation failed');
|
|
230
|
+
}
|
|
231
|
+
lines.push('');
|
|
232
|
+
lines.push(`Summary:`);
|
|
233
|
+
lines.push(` Explicit values: ${result.summary.explicit_values}`);
|
|
234
|
+
lines.push(` $detect markers: ${result.summary.detect_markers}`);
|
|
235
|
+
lines.push(` Inferred values: ${result.summary.inferred_values}`);
|
|
236
|
+
lines.push(` Errors: ${result.summary.errors}`);
|
|
237
|
+
lines.push(` Warnings: ${result.summary.warnings}`);
|
|
238
|
+
if (result.violations.length > 0) {
|
|
239
|
+
lines.push('');
|
|
240
|
+
lines.push('Violations:');
|
|
241
|
+
for (const violation of result.violations) {
|
|
242
|
+
const icon = violation.severity === 'error' ? '✗' : '⚠';
|
|
243
|
+
lines.push(` ${icon} ${violation.path}`);
|
|
244
|
+
lines.push(` ${violation.message}`);
|
|
245
|
+
lines.push(` → ${violation.suggestion}`);
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
return lines.join('\n');
|
|
249
|
+
}
|
|
250
|
+
/**
|
|
251
|
+
* Generate fixes for strict mode violations
|
|
252
|
+
*/
|
|
253
|
+
function generateStrictFixes(config, result) {
|
|
254
|
+
const fixes = [];
|
|
255
|
+
for (const violation of result.violations) {
|
|
256
|
+
if (violation.type === 'missing_type') {
|
|
257
|
+
// Add $detect marker for missing types
|
|
258
|
+
fixes.push({
|
|
259
|
+
path: violation.path,
|
|
260
|
+
action: 'add',
|
|
261
|
+
value: '$detect',
|
|
262
|
+
description: `Add $detect marker to auto-detect type`,
|
|
263
|
+
});
|
|
264
|
+
}
|
|
265
|
+
if (violation.type === 'missing_port') {
|
|
266
|
+
fixes.push({
|
|
267
|
+
path: violation.path,
|
|
268
|
+
action: 'add',
|
|
269
|
+
value: '$detect',
|
|
270
|
+
description: `Add $detect marker to auto-detect port`,
|
|
271
|
+
});
|
|
272
|
+
}
|
|
273
|
+
if (violation.type === 'undeclared_connection' && violation.path.includes('requires')) {
|
|
274
|
+
// Convert requires to connects_to
|
|
275
|
+
const match = violation.path.match(/apps\.(\w+)\.requires\.(\w+)/);
|
|
276
|
+
if (match) {
|
|
277
|
+
const [, appName, depName] = match;
|
|
278
|
+
fixes.push({
|
|
279
|
+
path: `apps.${appName}.connects_to.${depName}`,
|
|
280
|
+
action: 'add',
|
|
281
|
+
value: { mode: 'local', required: true },
|
|
282
|
+
description: `Convert requires.${depName} to connects_to`,
|
|
283
|
+
});
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
return fixes;
|
|
288
|
+
}
|