genbox 1.0.47 → 1.0.49

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.
@@ -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
- const schema_v4_1 = require("./schema-v4");
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
- // v4 uses default_connection, v3 uses connect_to
63
- return profile.default_connection || profile.connect_to;
53
+ return profile.default_connection;
64
54
  }
65
- // Helper to get app dependencies (handles both v3 and v4)
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 (handles both v3 and v4)
62
+ // Helper to get infrastructure/provides
77
63
  function getInfrastructure(config) {
78
- if (isV4(config)) {
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
- const validation = this.validate(mergedConfig);
106
- if (!validation.valid) {
107
- warnings.push(...validation.errors.map(e => e.message));
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: sources.length > 0 ? mergedConfig : null,
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
- // Detect version from first source that has it
181
- let isVersion4 = false;
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 appropriate default structure
190
- const merged = isVersion4
191
- ? {
192
- version: 4,
193
- project: {
194
- name: 'unnamed',
195
- structure: 'single-app',
196
- },
197
- apps: {},
198
- strict: {
199
- enabled: true,
200
- allow_detect: true,
201
- warnings_as_errors: false,
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 - workspace config takes precedence
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 (supports both v3 and v4)
245
+ * Validate configuration (v4 only)
276
246
  */
277
247
  validate(config) {
278
248
  const errors = [];
279
249
  const warnings = [];
280
- const configIsV4 = isV4(config);
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
- // Get infrastructure (v4: provides, v3: infrastructure)
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 (v4: connects_to, v3: requires)
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}.${depField}.${dep}`,
323
- message: `App '${name}' ${configIsV4 ? 'connects to' : 'requires'} '${dep}' which is not defined`,
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
- // Check connection (v4: default_connection, v3: connect_to)
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}.${connField}`,
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
- // Check for unresolved variable references
363
- const checkValue = (value, path) => {
364
- if (typeof value === 'string' && value.includes('${')) {
365
- const match = value.match(/\$\{([^}]+)\}/);
366
- if (match) {
367
- warnings.push({
368
- path,
369
- message: `Environment '${name}' has variable reference '${match[1]}' - ensure it's defined in .env.genbox`,
370
- severity: 'warning',
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 (supports both v3 and v4)
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) - supports both v3 and v4
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
- // Merge parent into child - handle both v3 and v4
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 (supports both v3 and v4)
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 ? getProfileConnection(resolved) : undefined,
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), // Workspace level
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);