genbox 1.0.47 → 1.0.48
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/create.js +3 -8
- package/dist/commands/db-sync.js +1 -1
- package/dist/commands/init.js +1 -1
- package/dist/commands/migrate.js +18 -215
- package/dist/commands/profiles.js +5 -11
- package/dist/commands/push.js +19 -24
- package/dist/commands/rebuild.js +3 -8
- package/dist/commands/validate.js +13 -13
- package/dist/config-explainer.js +1 -1
- package/dist/config-loader.js +74 -150
- package/dist/profile-resolver.js +15 -23
- package/dist/schema-v4.js +35 -35
- package/dist/strict-mode.js +57 -126
- package/package.json +1 -1
- package/dist/migration.js +0 -335
- package/dist/schema-v3.js +0 -48
package/dist/config-loader.js
CHANGED
|
@@ -4,10 +4,7 @@
|
|
|
4
4
|
*
|
|
5
5
|
* Loads genbox.yaml from the CURRENT directory only.
|
|
6
6
|
* No parent directory lookup - each project is self-contained.
|
|
7
|
-
*
|
|
8
|
-
* Additional sources (merged with project config):
|
|
9
|
-
* - User profiles (~/.genbox/profiles.yaml)
|
|
10
|
-
* - User defaults (~/.genbox/config.json)
|
|
7
|
+
* Only supports v4 configuration format.
|
|
11
8
|
*/
|
|
12
9
|
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
13
10
|
if (k2 === undefined) k2 = k;
|
|
@@ -44,7 +41,6 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
44
41
|
})();
|
|
45
42
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
46
43
|
exports.configLoader = exports.ConfigLoader = void 0;
|
|
47
|
-
exports.isV4 = isV4;
|
|
48
44
|
exports.getProfileConnection = getProfileConnection;
|
|
49
45
|
exports.getAppDependencies = getAppDependencies;
|
|
50
46
|
exports.getInfrastructure = getInfrastructure;
|
|
@@ -52,33 +48,20 @@ const fs = __importStar(require("fs"));
|
|
|
52
48
|
const path = __importStar(require("path"));
|
|
53
49
|
const yaml = __importStar(require("js-yaml"));
|
|
54
50
|
const os = __importStar(require("os"));
|
|
55
|
-
|
|
56
|
-
// Helper to check if config is v4
|
|
57
|
-
function isV4(config) {
|
|
58
|
-
return (0, schema_v4_1.getConfigVersion)(config) === 4;
|
|
59
|
-
}
|
|
60
|
-
// Helper to get profile connection (handles both v3 and v4)
|
|
51
|
+
// Helper to get profile connection
|
|
61
52
|
function getProfileConnection(profile) {
|
|
62
|
-
|
|
63
|
-
return profile.default_connection || profile.connect_to;
|
|
53
|
+
return profile.default_connection;
|
|
64
54
|
}
|
|
65
|
-
// Helper to get app dependencies
|
|
55
|
+
// Helper to get app dependencies
|
|
66
56
|
function getAppDependencies(app) {
|
|
67
|
-
// v4 uses connects_to, v3 uses requires
|
|
68
57
|
if (app.connects_to) {
|
|
69
58
|
return Object.keys(app.connects_to);
|
|
70
59
|
}
|
|
71
|
-
if (app.requires) {
|
|
72
|
-
return Object.keys(app.requires);
|
|
73
|
-
}
|
|
74
60
|
return [];
|
|
75
61
|
}
|
|
76
|
-
// Helper to get infrastructure/provides
|
|
62
|
+
// Helper to get infrastructure/provides
|
|
77
63
|
function getInfrastructure(config) {
|
|
78
|
-
|
|
79
|
-
return config.provides;
|
|
80
|
-
}
|
|
81
|
-
return config.infrastructure;
|
|
64
|
+
return config.provides;
|
|
82
65
|
}
|
|
83
66
|
const CONFIG_FILENAME = 'genbox.yaml';
|
|
84
67
|
const ENV_FILENAME = '.env.genbox';
|
|
@@ -102,16 +85,18 @@ class ConfigLoader {
|
|
|
102
85
|
// Merge configurations
|
|
103
86
|
const mergedConfig = this.mergeConfigs(sources, userConfig);
|
|
104
87
|
// Validate
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
88
|
+
if (mergedConfig) {
|
|
89
|
+
const validation = this.validate(mergedConfig);
|
|
90
|
+
if (!validation.valid) {
|
|
91
|
+
warnings.push(...validation.errors.map(e => e.message));
|
|
92
|
+
}
|
|
93
|
+
warnings.push(...validation.warnings.map(w => w.message));
|
|
108
94
|
}
|
|
109
|
-
warnings.push(...validation.warnings.map(w => w.message));
|
|
110
95
|
// Root is current directory
|
|
111
96
|
const root = projectConfig?.path ? path.dirname(projectConfig.path) : cwd;
|
|
112
97
|
return {
|
|
113
98
|
found: sources.length > 0,
|
|
114
|
-
config:
|
|
99
|
+
config: mergedConfig,
|
|
115
100
|
sources,
|
|
116
101
|
warnings,
|
|
117
102
|
isWorkspace: false,
|
|
@@ -129,6 +114,12 @@ class ConfigLoader {
|
|
|
129
114
|
try {
|
|
130
115
|
const content = fs.readFileSync(configPath, 'utf8');
|
|
131
116
|
const config = yaml.load(content);
|
|
117
|
+
// Validate it's v4
|
|
118
|
+
if (config.version !== 4) {
|
|
119
|
+
console.warn(`Warning: Config version ${config.version} is not supported. Only v4 is supported.`);
|
|
120
|
+
console.warn('Run "genbox init" to create a new v4 configuration.');
|
|
121
|
+
return null;
|
|
122
|
+
}
|
|
132
123
|
return {
|
|
133
124
|
type: 'project',
|
|
134
125
|
path: configPath,
|
|
@@ -173,43 +164,27 @@ class ConfigLoader {
|
|
|
173
164
|
}
|
|
174
165
|
}
|
|
175
166
|
/**
|
|
176
|
-
* Merge configurations from all sources
|
|
177
|
-
* Supports both v3 and v4 config formats
|
|
167
|
+
* Merge configurations from all sources (v4 only)
|
|
178
168
|
*/
|
|
179
169
|
mergeConfigs(sources, userConfig) {
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
for (const source of sources) {
|
|
183
|
-
// Version can be number (v4) or string (v3)
|
|
184
|
-
if (source.config.version === 4) {
|
|
185
|
-
isVersion4 = true;
|
|
186
|
-
break;
|
|
187
|
-
}
|
|
170
|
+
if (sources.length === 0) {
|
|
171
|
+
return null;
|
|
188
172
|
}
|
|
189
|
-
// Start with
|
|
190
|
-
const merged =
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
: {
|
|
205
|
-
version: '3.0',
|
|
206
|
-
project: {
|
|
207
|
-
name: 'unnamed',
|
|
208
|
-
structure: 'single-app',
|
|
209
|
-
},
|
|
210
|
-
apps: {},
|
|
211
|
-
};
|
|
212
|
-
// Merge sources in order (workspace first, then project)
|
|
173
|
+
// Start with v4 default structure
|
|
174
|
+
const merged = {
|
|
175
|
+
version: 4,
|
|
176
|
+
project: {
|
|
177
|
+
name: 'unnamed',
|
|
178
|
+
structure: 'single-app',
|
|
179
|
+
},
|
|
180
|
+
apps: {},
|
|
181
|
+
strict: {
|
|
182
|
+
enabled: true,
|
|
183
|
+
allow_detect: true,
|
|
184
|
+
warnings_as_errors: false,
|
|
185
|
+
},
|
|
186
|
+
};
|
|
187
|
+
// Merge sources in order
|
|
213
188
|
for (const source of sources) {
|
|
214
189
|
this.deepMerge(merged, source.config);
|
|
215
190
|
}
|
|
@@ -232,10 +207,6 @@ class ConfigLoader {
|
|
|
232
207
|
}
|
|
233
208
|
/**
|
|
234
209
|
* Deep merge objects
|
|
235
|
-
*
|
|
236
|
-
* Special handling:
|
|
237
|
-
* - 'version' field is never overwritten (prevents v4 -> v1 downgrades)
|
|
238
|
-
* - Empty objects don't overwrite non-empty objects
|
|
239
210
|
*/
|
|
240
211
|
deepMerge(target, source) {
|
|
241
212
|
for (const key of Object.keys(source)) {
|
|
@@ -243,7 +214,7 @@ class ConfigLoader {
|
|
|
243
214
|
const targetValue = target[key];
|
|
244
215
|
if (sourceValue === undefined)
|
|
245
216
|
continue;
|
|
246
|
-
// Never overwrite version field
|
|
217
|
+
// Never overwrite version field
|
|
247
218
|
if (key === 'version' && targetValue !== undefined)
|
|
248
219
|
continue;
|
|
249
220
|
// Don't overwrite non-empty objects with empty objects
|
|
@@ -255,7 +226,6 @@ class ConfigLoader {
|
|
|
255
226
|
targetValue !== null &&
|
|
256
227
|
!Array.isArray(targetValue) &&
|
|
257
228
|
Object.keys(targetValue).length > 0) {
|
|
258
|
-
// Skip - don't let empty {} overwrite populated object
|
|
259
229
|
continue;
|
|
260
230
|
}
|
|
261
231
|
if (typeof sourceValue === 'object' &&
|
|
@@ -272,12 +242,19 @@ class ConfigLoader {
|
|
|
272
242
|
}
|
|
273
243
|
}
|
|
274
244
|
/**
|
|
275
|
-
* Validate configuration (
|
|
245
|
+
* Validate configuration (v4 only)
|
|
276
246
|
*/
|
|
277
247
|
validate(config) {
|
|
278
248
|
const errors = [];
|
|
279
249
|
const warnings = [];
|
|
280
|
-
|
|
250
|
+
// Check version
|
|
251
|
+
if (config.version !== 4) {
|
|
252
|
+
errors.push({
|
|
253
|
+
path: 'version',
|
|
254
|
+
message: `Config version ${config.version} is not supported. Only v4 is supported.`,
|
|
255
|
+
severity: 'error',
|
|
256
|
+
});
|
|
257
|
+
}
|
|
281
258
|
// Required fields
|
|
282
259
|
if (!config.project?.name) {
|
|
283
260
|
errors.push({
|
|
@@ -293,8 +270,7 @@ class ConfigLoader {
|
|
|
293
270
|
severity: 'warning',
|
|
294
271
|
});
|
|
295
272
|
}
|
|
296
|
-
|
|
297
|
-
const infra = getInfrastructure(config);
|
|
273
|
+
const infra = config.provides;
|
|
298
274
|
// Validate apps
|
|
299
275
|
for (const [name, app] of Object.entries(config.apps || {})) {
|
|
300
276
|
if (!app.path) {
|
|
@@ -311,16 +287,15 @@ class ConfigLoader {
|
|
|
311
287
|
severity: 'warning',
|
|
312
288
|
});
|
|
313
289
|
}
|
|
314
|
-
// Validate dependencies
|
|
290
|
+
// Validate dependencies
|
|
315
291
|
const deps = getAppDependencies(app);
|
|
316
|
-
const depField = configIsV4 ? 'connects_to' : 'requires';
|
|
317
292
|
for (const dep of deps) {
|
|
318
293
|
const isAppDep = config.apps?.[dep];
|
|
319
294
|
const isInfraDep = infra?.[dep];
|
|
320
295
|
if (!isAppDep && !isInfraDep) {
|
|
321
296
|
warnings.push({
|
|
322
|
-
path: `apps.${name}.${
|
|
323
|
-
message: `App '${name}'
|
|
297
|
+
path: `apps.${name}.connects_to.${dep}`,
|
|
298
|
+
message: `App '${name}' connects to '${dep}' which is not defined`,
|
|
324
299
|
severity: 'warning',
|
|
325
300
|
});
|
|
326
301
|
}
|
|
@@ -328,7 +303,6 @@ class ConfigLoader {
|
|
|
328
303
|
}
|
|
329
304
|
// Validate profiles
|
|
330
305
|
for (const [name, profile] of Object.entries(config.profiles || {})) {
|
|
331
|
-
// Check apps exist
|
|
332
306
|
for (const appName of profile.apps || []) {
|
|
333
307
|
if (!config.apps?.[appName]) {
|
|
334
308
|
errors.push({
|
|
@@ -338,7 +312,6 @@ class ConfigLoader {
|
|
|
338
312
|
});
|
|
339
313
|
}
|
|
340
314
|
}
|
|
341
|
-
// Check extends
|
|
342
315
|
if (profile.extends && !config.profiles?.[profile.extends]) {
|
|
343
316
|
errors.push({
|
|
344
317
|
path: `profiles.${name}.extends`,
|
|
@@ -346,12 +319,10 @@ class ConfigLoader {
|
|
|
346
319
|
severity: 'error',
|
|
347
320
|
});
|
|
348
321
|
}
|
|
349
|
-
|
|
350
|
-
const connection = getProfileConnection(profile);
|
|
322
|
+
const connection = profile.default_connection;
|
|
351
323
|
if (connection && !config.environments?.[connection]) {
|
|
352
|
-
const connField = configIsV4 ? 'default_connection' : 'connect_to';
|
|
353
324
|
warnings.push({
|
|
354
|
-
path: `profiles.${name}
|
|
325
|
+
path: `profiles.${name}.default_connection`,
|
|
355
326
|
message: `Profile '${name}' connects to undefined environment '${connection}'`,
|
|
356
327
|
severity: 'warning',
|
|
357
328
|
});
|
|
@@ -359,37 +330,20 @@ class ConfigLoader {
|
|
|
359
330
|
}
|
|
360
331
|
// Validate environments
|
|
361
332
|
for (const [name, env] of Object.entries(config.environments || {})) {
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
}
|
|
374
|
-
};
|
|
375
|
-
// v4 uses urls object, v3 uses separate mongodb/redis/rabbitmq objects
|
|
376
|
-
if (configIsV4) {
|
|
377
|
-
const v4Env = env;
|
|
378
|
-
if (v4Env.urls) {
|
|
379
|
-
for (const [key, url] of Object.entries(v4Env.urls)) {
|
|
380
|
-
checkValue(url, `environments.${name}.urls.${key}`);
|
|
333
|
+
if (env.urls) {
|
|
334
|
+
for (const [key, url] of Object.entries(env.urls)) {
|
|
335
|
+
if (typeof url === 'string' && url.includes('${')) {
|
|
336
|
+
const match = url.match(/\$\{([^}]+)\}/);
|
|
337
|
+
if (match) {
|
|
338
|
+
warnings.push({
|
|
339
|
+
path: `environments.${name}.urls.${key}`,
|
|
340
|
+
message: `Environment '${name}' has variable reference '${match[1]}' - ensure it's defined in .env.genbox`,
|
|
341
|
+
severity: 'warning',
|
|
342
|
+
});
|
|
343
|
+
}
|
|
381
344
|
}
|
|
382
345
|
}
|
|
383
346
|
}
|
|
384
|
-
else {
|
|
385
|
-
const v3Env = env;
|
|
386
|
-
if (v3Env.mongodb?.url)
|
|
387
|
-
checkValue(v3Env.mongodb.url, `environments.${name}.mongodb.url`);
|
|
388
|
-
if (v3Env.redis?.url)
|
|
389
|
-
checkValue(v3Env.redis.url, `environments.${name}.redis.url`);
|
|
390
|
-
if (v3Env.rabbitmq?.url)
|
|
391
|
-
checkValue(v3Env.rabbitmq.url, `environments.${name}.rabbitmq.url`);
|
|
392
|
-
}
|
|
393
347
|
}
|
|
394
348
|
return {
|
|
395
349
|
valid: errors.length === 0,
|
|
@@ -398,12 +352,11 @@ class ConfigLoader {
|
|
|
398
352
|
};
|
|
399
353
|
}
|
|
400
354
|
/**
|
|
401
|
-
* Get a specific profile, resolving extends
|
|
355
|
+
* Get a specific profile, resolving extends
|
|
402
356
|
*/
|
|
403
357
|
getProfile(config, profileName) {
|
|
404
358
|
const profile = config.profiles?.[profileName];
|
|
405
359
|
if (!profile) {
|
|
406
|
-
// Check user profiles
|
|
407
360
|
const userProfiles = this.loadUserProfiles();
|
|
408
361
|
if (userProfiles?.profiles?.[profileName]) {
|
|
409
362
|
return this.resolveProfile(config, userProfiles.profiles[profileName], userProfiles);
|
|
@@ -413,13 +366,12 @@ class ConfigLoader {
|
|
|
413
366
|
return this.resolveProfile(config, profile);
|
|
414
367
|
}
|
|
415
368
|
/**
|
|
416
|
-
* Resolve profile inheritance (extends)
|
|
369
|
+
* Resolve profile inheritance (extends)
|
|
417
370
|
*/
|
|
418
371
|
resolveProfile(config, profile, userProfiles) {
|
|
419
372
|
if (!profile.extends) {
|
|
420
373
|
return profile;
|
|
421
374
|
}
|
|
422
|
-
// Find parent profile
|
|
423
375
|
let parent = config.profiles?.[profile.extends];
|
|
424
376
|
if (!parent && userProfiles) {
|
|
425
377
|
parent = userProfiles.profiles?.[profile.extends];
|
|
@@ -428,47 +380,26 @@ class ConfigLoader {
|
|
|
428
380
|
console.warn(`Warning: Profile extends '${profile.extends}' which doesn't exist`);
|
|
429
381
|
return profile;
|
|
430
382
|
}
|
|
431
|
-
// Resolve parent first (recursive)
|
|
432
383
|
const resolvedParent = this.resolveProfile(config, parent, userProfiles);
|
|
433
|
-
|
|
434
|
-
const merged = {
|
|
384
|
+
return {
|
|
435
385
|
...resolvedParent,
|
|
436
386
|
...profile,
|
|
437
|
-
// Arrays are replaced, not merged
|
|
438
387
|
apps: profile.apps || resolvedParent.apps,
|
|
439
|
-
// Objects are merged
|
|
440
388
|
env: {
|
|
441
389
|
...resolvedParent.env,
|
|
442
390
|
...profile.env,
|
|
443
391
|
},
|
|
392
|
+
connections: {
|
|
393
|
+
...resolvedParent.connections,
|
|
394
|
+
...profile.connections,
|
|
395
|
+
},
|
|
444
396
|
};
|
|
445
|
-
// Handle v4 connections override
|
|
446
|
-
if (isV4(config)) {
|
|
447
|
-
const v4Profile = profile;
|
|
448
|
-
const v4Parent = resolvedParent;
|
|
449
|
-
merged.connections = {
|
|
450
|
-
...v4Parent.connections,
|
|
451
|
-
...v4Profile.connections,
|
|
452
|
-
};
|
|
453
|
-
}
|
|
454
|
-
else {
|
|
455
|
-
// v3 infrastructure merge
|
|
456
|
-
const v3Profile = profile;
|
|
457
|
-
const v3Parent = resolvedParent;
|
|
458
|
-
merged.infrastructure = {
|
|
459
|
-
...v3Parent.infrastructure,
|
|
460
|
-
...v3Profile.infrastructure,
|
|
461
|
-
};
|
|
462
|
-
}
|
|
463
|
-
return merged;
|
|
464
397
|
}
|
|
465
398
|
/**
|
|
466
|
-
* List all available profiles
|
|
399
|
+
* List all available profiles
|
|
467
400
|
*/
|
|
468
401
|
listProfiles(config) {
|
|
469
402
|
const profiles = [];
|
|
470
|
-
// Project profiles only (user/global profiles removed - they don't make sense
|
|
471
|
-
// because profiles reference project-specific app names)
|
|
472
403
|
for (const [name, profile] of Object.entries(config.profiles || {})) {
|
|
473
404
|
const resolved = this.getProfile(config, name);
|
|
474
405
|
profiles.push({
|
|
@@ -477,7 +408,7 @@ class ConfigLoader {
|
|
|
477
408
|
source: 'project',
|
|
478
409
|
apps: resolved?.apps || [],
|
|
479
410
|
size: resolved?.size,
|
|
480
|
-
connection: resolved
|
|
411
|
+
connection: resolved?.default_connection,
|
|
481
412
|
});
|
|
482
413
|
}
|
|
483
414
|
return profiles;
|
|
@@ -486,11 +417,9 @@ class ConfigLoader {
|
|
|
486
417
|
* Save user profile
|
|
487
418
|
*/
|
|
488
419
|
saveUserProfile(name, profile) {
|
|
489
|
-
// Ensure directory exists
|
|
490
420
|
if (!fs.existsSync(USER_CONFIG_DIR)) {
|
|
491
421
|
fs.mkdirSync(USER_CONFIG_DIR, { recursive: true });
|
|
492
422
|
}
|
|
493
|
-
// Load existing profiles
|
|
494
423
|
let profiles = { profiles: {} };
|
|
495
424
|
if (fs.existsSync(USER_PROFILES_FILE)) {
|
|
496
425
|
try {
|
|
@@ -499,9 +428,7 @@ class ConfigLoader {
|
|
|
499
428
|
}
|
|
500
429
|
catch { }
|
|
501
430
|
}
|
|
502
|
-
// Add/update profile
|
|
503
431
|
profiles.profiles[name] = profile;
|
|
504
|
-
// Save
|
|
505
432
|
const content = yaml.dump(profiles, { lineWidth: 120 });
|
|
506
433
|
fs.writeFileSync(USER_PROFILES_FILE, content);
|
|
507
434
|
}
|
|
@@ -510,10 +437,9 @@ class ConfigLoader {
|
|
|
510
437
|
*/
|
|
511
438
|
loadEnvVars(root) {
|
|
512
439
|
const vars = {};
|
|
513
|
-
// Load from multiple locations
|
|
514
440
|
const envPaths = [
|
|
515
441
|
path.join(root, ENV_FILENAME),
|
|
516
|
-
path.join(path.dirname(root), ENV_FILENAME),
|
|
442
|
+
path.join(path.dirname(root), ENV_FILENAME),
|
|
517
443
|
];
|
|
518
444
|
for (const envPath of envPaths) {
|
|
519
445
|
if (fs.existsSync(envPath)) {
|
|
@@ -532,13 +458,11 @@ class ConfigLoader {
|
|
|
532
458
|
const lines = content.split('\n');
|
|
533
459
|
for (const line of lines) {
|
|
534
460
|
const trimmed = line.trim();
|
|
535
|
-
// Skip comments and empty lines
|
|
536
461
|
if (!trimmed || trimmed.startsWith('#'))
|
|
537
462
|
continue;
|
|
538
463
|
const match = trimmed.match(/^([A-Za-z_][A-Za-z0-9_]*)\s*=\s*(.*)$/);
|
|
539
464
|
if (match) {
|
|
540
465
|
let [, key, value] = match;
|
|
541
|
-
// Remove quotes
|
|
542
466
|
if ((value.startsWith('"') && value.endsWith('"')) ||
|
|
543
467
|
(value.startsWith("'") && value.endsWith("'"))) {
|
|
544
468
|
value = value.slice(1, -1);
|
package/dist/profile-resolver.js
CHANGED
|
@@ -60,7 +60,6 @@ class ProfileResolver {
|
|
|
60
60
|
*/
|
|
61
61
|
async resolve(config, options) {
|
|
62
62
|
const warnings = [];
|
|
63
|
-
const isV4Config = (0, config_loader_1.isV4)(config);
|
|
64
63
|
// Step 1: Get base profile (if specified)
|
|
65
64
|
let profile = {};
|
|
66
65
|
if (options.profile) {
|
|
@@ -193,12 +192,13 @@ class ProfileResolver {
|
|
|
193
192
|
const dependencies = {};
|
|
194
193
|
const infrastructure = (0, config_loader_1.getInfrastructure)(config);
|
|
195
194
|
// Process each dependency (v3: requires, v4: connects_to)
|
|
196
|
-
const appDeps =
|
|
195
|
+
const appDeps = true
|
|
197
196
|
? Object.entries(appConfig.connects_to || {})
|
|
198
|
-
: Object.entries(appConfig.
|
|
197
|
+
: Object.entries(appConfig.connects_to || {});
|
|
199
198
|
for (const [depName, requirement] of appDeps) {
|
|
200
|
-
//
|
|
201
|
-
|
|
199
|
+
// Skip if not required
|
|
200
|
+
const reqObj = requirement;
|
|
201
|
+
if (!reqObj.required)
|
|
202
202
|
continue;
|
|
203
203
|
// Check if dependency is already in selected apps
|
|
204
204
|
const isLocalApp = selectedApps.includes(depName);
|
|
@@ -218,12 +218,12 @@ class ProfileResolver {
|
|
|
218
218
|
url,
|
|
219
219
|
};
|
|
220
220
|
}
|
|
221
|
-
else if (
|
|
221
|
+
else if (reqObj.required && !options.yes) {
|
|
222
222
|
// Prompt user for resolution
|
|
223
223
|
const resolution = await this.promptDependencyResolution(config, appName, depName, isInfra ? 'infrastructure' : 'app');
|
|
224
224
|
dependencies[depName] = resolution;
|
|
225
225
|
}
|
|
226
|
-
else if (
|
|
226
|
+
else if (!reqObj.required) {
|
|
227
227
|
// Skip optional dependencies if not explicitly included
|
|
228
228
|
continue;
|
|
229
229
|
}
|
|
@@ -250,13 +250,13 @@ class ProfileResolver {
|
|
|
250
250
|
type,
|
|
251
251
|
framework,
|
|
252
252
|
port,
|
|
253
|
-
services: appConfig.services,
|
|
254
253
|
commands: appConfig.commands,
|
|
255
254
|
env: appConfig.env,
|
|
256
255
|
runner,
|
|
257
256
|
docker,
|
|
258
257
|
healthcheck,
|
|
259
258
|
dependsOn,
|
|
259
|
+
connections: [], // Will be populated later if needed
|
|
260
260
|
dependencies,
|
|
261
261
|
};
|
|
262
262
|
}
|
|
@@ -314,16 +314,8 @@ class ProfileResolver {
|
|
|
314
314
|
* Get URL for a dependency from environment config
|
|
315
315
|
*/
|
|
316
316
|
getUrlForDependency(depName, envConfig) {
|
|
317
|
-
// Check
|
|
318
|
-
|
|
319
|
-
// Check common fields: url, gateway, api, or first string value
|
|
320
|
-
const apiConfig = envConfig.api;
|
|
321
|
-
return apiConfig.url || apiConfig.gateway || apiConfig.api ||
|
|
322
|
-
Object.values(apiConfig).find(v => typeof v === 'string' && v.startsWith('http'));
|
|
323
|
-
}
|
|
324
|
-
// Check infrastructure
|
|
325
|
-
const infraConfig = envConfig[depName];
|
|
326
|
-
return infraConfig?.url;
|
|
317
|
+
// Check the urls map in the environment config
|
|
318
|
+
return envConfig.urls?.[depName];
|
|
327
319
|
}
|
|
328
320
|
/**
|
|
329
321
|
* Resolve database mode
|
|
@@ -348,7 +340,7 @@ class ProfileResolver {
|
|
|
348
340
|
if (profileConnection) {
|
|
349
341
|
const envConfig = config.environments?.[profileConnection];
|
|
350
342
|
// v4 uses urls.mongodb, v3 uses mongodb.url
|
|
351
|
-
const mongoUrl =
|
|
343
|
+
const mongoUrl = true
|
|
352
344
|
? envConfig?.urls?.mongodb
|
|
353
345
|
: envConfig?.mongodb?.url;
|
|
354
346
|
return {
|
|
@@ -409,7 +401,7 @@ class ProfileResolver {
|
|
|
409
401
|
const source = answer.replace('remote-', '');
|
|
410
402
|
const envConfig = config.environments?.[source];
|
|
411
403
|
// v4 uses urls.mongodb, v3 uses mongodb.url
|
|
412
|
-
const mongoUrl =
|
|
404
|
+
const mongoUrl = true
|
|
413
405
|
? envConfig?.urls?.mongodb
|
|
414
406
|
: envConfig?.mongodb?.url;
|
|
415
407
|
return {
|
|
@@ -427,9 +419,9 @@ class ProfileResolver {
|
|
|
427
419
|
const localApps = apps.length;
|
|
428
420
|
const localInfra = infrastructure.filter(i => i.mode === 'local').length;
|
|
429
421
|
const total = localApps + localInfra;
|
|
430
|
-
// Check for heavy
|
|
431
|
-
const
|
|
432
|
-
if (total > 8 ||
|
|
422
|
+
// Check for heavy apps (many dependencies)
|
|
423
|
+
const hasHeavyApp = apps.some(a => Object.keys(a.dependencies).length > 3);
|
|
424
|
+
if (total > 8 || hasHeavyApp)
|
|
433
425
|
return 'xl';
|
|
434
426
|
if (total > 5)
|
|
435
427
|
return 'large';
|
package/dist/schema-v4.js
CHANGED
|
@@ -2,53 +2,53 @@
|
|
|
2
2
|
/**
|
|
3
3
|
* Genbox Configuration Schema v4
|
|
4
4
|
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
* - `provides` section for infrastructure definitions
|
|
8
|
-
* - `$detect` markers for opt-in auto-detection
|
|
9
|
-
* - Strict mode by default (no implicit inference)
|
|
10
|
-
* - Better separation of concerns
|
|
5
|
+
* This is the ONLY supported schema version.
|
|
6
|
+
* All legacy v2/v3 support has been removed.
|
|
11
7
|
*/
|
|
12
8
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
13
|
-
exports.
|
|
14
|
-
exports.
|
|
9
|
+
exports.DETECTABLE_APP_FIELDS = exports.DETECT_MARKER = void 0;
|
|
10
|
+
exports.isDetectMarker = isDetectMarker;
|
|
11
|
+
exports.isValidConfig = isValidConfig;
|
|
15
12
|
exports.getConfigVersion = getConfigVersion;
|
|
16
13
|
// ============================================
|
|
17
|
-
//
|
|
14
|
+
// $detect Marker Support
|
|
18
15
|
// ============================================
|
|
19
16
|
/**
|
|
20
|
-
*
|
|
17
|
+
* Special marker indicating a value should be auto-detected.
|
|
21
18
|
*/
|
|
22
|
-
|
|
19
|
+
exports.DETECT_MARKER = '$detect';
|
|
20
|
+
/**
|
|
21
|
+
* List of fields that support $detect markers in AppConfig
|
|
22
|
+
*/
|
|
23
|
+
exports.DETECTABLE_APP_FIELDS = [
|
|
24
|
+
'type',
|
|
25
|
+
'framework',
|
|
26
|
+
'port',
|
|
27
|
+
'commands.install',
|
|
28
|
+
'commands.dev',
|
|
29
|
+
'commands.build',
|
|
30
|
+
'commands.start',
|
|
31
|
+
];
|
|
32
|
+
/**
|
|
33
|
+
* Check if a value is a $detect marker
|
|
34
|
+
*/
|
|
35
|
+
function isDetectMarker(value) {
|
|
36
|
+
return value === exports.DETECT_MARKER;
|
|
37
|
+
}
|
|
38
|
+
// ============================================
|
|
39
|
+
// Helper Functions
|
|
40
|
+
// ============================================
|
|
41
|
+
/**
|
|
42
|
+
* Check if a config is valid v4
|
|
43
|
+
*/
|
|
44
|
+
function isValidConfig(config) {
|
|
23
45
|
return config.version === 4;
|
|
24
46
|
}
|
|
25
47
|
/**
|
|
26
|
-
* Get the version of a config
|
|
48
|
+
* Get the version of a config (only v4 is valid now)
|
|
27
49
|
*/
|
|
28
50
|
function getConfigVersion(config) {
|
|
29
51
|
if (config.version === 4)
|
|
30
52
|
return 4;
|
|
31
|
-
|
|
32
|
-
return 3;
|
|
33
|
-
return 'unknown';
|
|
53
|
+
return 'invalid';
|
|
34
54
|
}
|
|
35
|
-
/**
|
|
36
|
-
* List of deprecated fields from v3
|
|
37
|
-
*/
|
|
38
|
-
exports.DEPRECATED_V3_FIELDS = [
|
|
39
|
-
{
|
|
40
|
-
path: 'apps.*.requires',
|
|
41
|
-
replacement: 'apps.*.connects_to',
|
|
42
|
-
message: 'Use connects_to with explicit connection modes',
|
|
43
|
-
},
|
|
44
|
-
{
|
|
45
|
-
path: 'infrastructure',
|
|
46
|
-
replacement: 'provides',
|
|
47
|
-
message: 'Renamed to provides for clarity',
|
|
48
|
-
},
|
|
49
|
-
{
|
|
50
|
-
path: 'profiles.*.connect_to',
|
|
51
|
-
replacement: 'profiles.*.default_connection',
|
|
52
|
-
message: 'Use default_connection or per-app connections override',
|
|
53
|
-
},
|
|
54
|
-
];
|