@friggframework/devtools 2.0.0--canary.522.923dfae.0 → 2.0.0--canary.540.c5ef83f.0

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 (119) hide show
  1. package/frigg-cli/README.md +1 -1
  2. package/frigg-cli/__tests__/unit/commands/doctor.test.js +2 -0
  3. package/frigg-cli/__tests__/unit/commands/install.test.js +19 -23
  4. package/frigg-cli/__tests__/unit/dependencies.test.js +2 -2
  5. package/frigg-cli/doctor-command/index.js +16 -17
  6. package/frigg-cli/index.js +6 -21
  7. package/frigg-cli/index.test.js +2 -7
  8. package/frigg-cli/init-command/backend-first-handler.js +42 -124
  9. package/frigg-cli/init-command/index.js +1 -2
  10. package/frigg-cli/init-command/template-handler.js +3 -13
  11. package/frigg-cli/install-command/backend-js.js +3 -3
  12. package/frigg-cli/install-command/environment-variables.js +19 -16
  13. package/frigg-cli/install-command/environment-variables.test.js +13 -12
  14. package/frigg-cli/install-command/index.js +9 -14
  15. package/frigg-cli/install-command/integration-file.js +3 -3
  16. package/frigg-cli/install-command/logger.js +12 -0
  17. package/frigg-cli/install-command/validate-package.js +9 -5
  18. package/frigg-cli/jest.config.js +1 -4
  19. package/frigg-cli/repair-command/index.js +128 -101
  20. package/frigg-cli/start-command/index.js +2 -246
  21. package/frigg-cli/ui-command/index.js +36 -58
  22. package/frigg-cli/utils/repo-detection.js +37 -85
  23. package/infrastructure/docs/iam-policy-templates.md +1 -1
  24. package/infrastructure/domains/networking/vpc-builder.test.js +4 -2
  25. package/infrastructure/domains/networking/vpc-resolver.test.js +1 -1
  26. package/infrastructure/domains/shared/cloudformation-discovery.test.js +7 -4
  27. package/infrastructure/domains/shared/resource-discovery.js +5 -5
  28. package/infrastructure/domains/shared/types/app-definition.js +0 -21
  29. package/infrastructure/domains/shared/types/discovery-result.test.js +1 -1
  30. package/infrastructure/domains/shared/utilities/base-definition-factory.js +1 -10
  31. package/infrastructure/domains/shared/utilities/base-definition-factory.test.js +2 -2
  32. package/infrastructure/infrastructure-composer.test.js +2 -2
  33. package/management-ui/README.md +109 -245
  34. package/package.json +7 -8
  35. package/frigg-cli/__tests__/application/use-cases/AddApiModuleToIntegrationUseCase.test.js +0 -326
  36. package/frigg-cli/__tests__/application/use-cases/CreateApiModuleUseCase.test.js +0 -337
  37. package/frigg-cli/__tests__/domain/entities/ApiModule.test.js +0 -373
  38. package/frigg-cli/__tests__/domain/entities/AppDefinition.test.js +0 -313
  39. package/frigg-cli/__tests__/domain/services/IntegrationValidator.test.js +0 -269
  40. package/frigg-cli/__tests__/domain/value-objects/IntegrationName.test.js +0 -82
  41. package/frigg-cli/__tests__/infrastructure/adapters/IntegrationJsUpdater.test.js +0 -408
  42. package/frigg-cli/__tests__/infrastructure/repositories/FileSystemApiModuleRepository.test.js +0 -583
  43. package/frigg-cli/__tests__/infrastructure/repositories/FileSystemAppDefinitionRepository.test.js +0 -314
  44. package/frigg-cli/__tests__/infrastructure/repositories/FileSystemIntegrationRepository.test.js +0 -383
  45. package/frigg-cli/__tests__/unit/commands/init.test.js +0 -406
  46. package/frigg-cli/__tests__/unit/commands/repair.test.js +0 -275
  47. package/frigg-cli/__tests__/unit/start-command/application/RunPreflightChecksUseCase.test.js +0 -411
  48. package/frigg-cli/__tests__/unit/start-command/infrastructure/DatabaseAdapter.test.js +0 -405
  49. package/frigg-cli/__tests__/unit/start-command/infrastructure/DockerAdapter.test.js +0 -496
  50. package/frigg-cli/__tests__/unit/start-command/presentation/InteractivePromptAdapter.test.js +0 -474
  51. package/frigg-cli/__tests__/unit/utils/output.test.js +0 -196
  52. package/frigg-cli/application/use-cases/AddApiModuleToIntegrationUseCase.js +0 -93
  53. package/frigg-cli/application/use-cases/CreateApiModuleUseCase.js +0 -93
  54. package/frigg-cli/application/use-cases/CreateIntegrationUseCase.js +0 -103
  55. package/frigg-cli/container.js +0 -172
  56. package/frigg-cli/docs/OUTPUT_MIGRATION_GUIDE.md +0 -286
  57. package/frigg-cli/domain/entities/ApiModule.js +0 -272
  58. package/frigg-cli/domain/entities/AppDefinition.js +0 -227
  59. package/frigg-cli/domain/entities/Integration.js +0 -198
  60. package/frigg-cli/domain/exceptions/DomainException.js +0 -24
  61. package/frigg-cli/domain/ports/IApiModuleRepository.js +0 -53
  62. package/frigg-cli/domain/ports/IAppDefinitionRepository.js +0 -43
  63. package/frigg-cli/domain/ports/IIntegrationRepository.js +0 -61
  64. package/frigg-cli/domain/services/IntegrationValidator.js +0 -185
  65. package/frigg-cli/domain/value-objects/IntegrationId.js +0 -42
  66. package/frigg-cli/domain/value-objects/IntegrationName.js +0 -60
  67. package/frigg-cli/domain/value-objects/SemanticVersion.js +0 -70
  68. package/frigg-cli/infrastructure/UnitOfWork.js +0 -46
  69. package/frigg-cli/infrastructure/adapters/BackendJsUpdater.js +0 -197
  70. package/frigg-cli/infrastructure/adapters/FileSystemAdapter.js +0 -224
  71. package/frigg-cli/infrastructure/adapters/IntegrationJsUpdater.js +0 -249
  72. package/frigg-cli/infrastructure/adapters/SchemaValidator.js +0 -92
  73. package/frigg-cli/infrastructure/repositories/FileSystemApiModuleRepository.js +0 -373
  74. package/frigg-cli/infrastructure/repositories/FileSystemAppDefinitionRepository.js +0 -116
  75. package/frigg-cli/infrastructure/repositories/FileSystemIntegrationRepository.js +0 -277
  76. package/frigg-cli/package-lock.json +0 -16226
  77. package/frigg-cli/start-command/application/RunPreflightChecksUseCase.js +0 -376
  78. package/frigg-cli/start-command/infrastructure/DatabaseAdapter.js +0 -591
  79. package/frigg-cli/start-command/infrastructure/DockerAdapter.js +0 -306
  80. package/frigg-cli/start-command/presentation/InteractivePromptAdapter.js +0 -329
  81. package/frigg-cli/templates/backend/.env.example +0 -62
  82. package/frigg-cli/templates/backend/.eslintrc.json +0 -12
  83. package/frigg-cli/templates/backend/.prettierrc +0 -6
  84. package/frigg-cli/templates/backend/docker-compose.yml +0 -22
  85. package/frigg-cli/templates/backend/index.js +0 -96
  86. package/frigg-cli/templates/backend/infrastructure.js +0 -12
  87. package/frigg-cli/templates/backend/jest.config.js +0 -17
  88. package/frigg-cli/templates/backend/package.json +0 -50
  89. package/frigg-cli/templates/backend/src/api-modules/.gitkeep +0 -10
  90. package/frigg-cli/templates/backend/src/base/.gitkeep +0 -7
  91. package/frigg-cli/templates/backend/src/integrations/.gitkeep +0 -10
  92. package/frigg-cli/templates/backend/src/integrations/ExampleIntegration.js +0 -65
  93. package/frigg-cli/templates/backend/src/utils/.gitkeep +0 -7
  94. package/frigg-cli/templates/backend/test/setup.js +0 -30
  95. package/frigg-cli/templates/backend/ui-extensions/.gitkeep +0 -0
  96. package/frigg-cli/templates/backend/ui-extensions/README.md +0 -77
  97. package/frigg-cli/utils/__tests__/repo-detection.test.js +0 -436
  98. package/frigg-cli/utils/output.js +0 -382
  99. package/frigg-cli/validate-command/__tests__/adapters/validate-command.test.js +0 -205
  100. package/frigg-cli/validate-command/__tests__/application/validate-app-use-case.test.js +0 -104
  101. package/frigg-cli/validate-command/__tests__/domain/fix-suggestion.test.js +0 -153
  102. package/frigg-cli/validate-command/__tests__/domain/validation-error.test.js +0 -162
  103. package/frigg-cli/validate-command/__tests__/domain/validation-result.test.js +0 -152
  104. package/frigg-cli/validate-command/__tests__/infrastructure/api-module-validator.test.js +0 -332
  105. package/frigg-cli/validate-command/__tests__/infrastructure/app-definition-validator.test.js +0 -191
  106. package/frigg-cli/validate-command/__tests__/infrastructure/integration-class-validator.test.js +0 -146
  107. package/frigg-cli/validate-command/__tests__/infrastructure/template-validation.test.js +0 -155
  108. package/frigg-cli/validate-command/adapters/cli/validate-command.js +0 -199
  109. package/frigg-cli/validate-command/application/use-cases/validate-app-use-case.js +0 -35
  110. package/frigg-cli/validate-command/domain/entities/validation-result.js +0 -74
  111. package/frigg-cli/validate-command/domain/value-objects/fix-suggestion.js +0 -74
  112. package/frigg-cli/validate-command/domain/value-objects/validation-error.js +0 -68
  113. package/frigg-cli/validate-command/infrastructure/validators/api-module-validator.js +0 -181
  114. package/frigg-cli/validate-command/infrastructure/validators/app-definition-validator.js +0 -128
  115. package/frigg-cli/validate-command/infrastructure/validators/integration-class-validator.js +0 -113
  116. package/infrastructure/domains/admin-scripts/admin-script-builder.js +0 -200
  117. package/infrastructure/domains/admin-scripts/admin-script-builder.test.js +0 -499
  118. package/infrastructure/domains/admin-scripts/index.js +0 -5
  119. package/infrastructure/jest.config.js +0 -16
@@ -1,6 +1,5 @@
1
1
  const { spawn } = require('node:child_process');
2
2
  const path = require('node:path');
3
- const fs = require('node:fs');
4
3
  const dotenv = require('dotenv');
5
4
  const chalk = require('chalk');
6
5
  const {
@@ -14,12 +13,6 @@ const {
14
13
  getPrismaClientNotGeneratedError
15
14
  } = require('../utils/error-messages');
16
15
 
17
- // Import new pre-flight infrastructure
18
- const { DockerAdapter } = require('./infrastructure/DockerAdapter');
19
- const { DatabaseAdapter } = require('./infrastructure/DatabaseAdapter');
20
- const { RunPreflightChecksUseCase } = require('./application/RunPreflightChecksUseCase');
21
- const { InteractivePromptAdapter } = require('./presentation/InteractivePromptAdapter');
22
-
23
16
  async function startCommand(options) {
24
17
  if (options.verbose) {
25
18
  console.log('Verbose mode enabled');
@@ -32,26 +25,7 @@ async function startCommand(options) {
32
25
  const envPath = path.join(process.cwd(), '.env');
33
26
  dotenv.config({ path: envPath });
34
27
 
35
- const projectPath = process.cwd();
36
-
37
- // Run interactive pre-flight checks if enabled (default: true)
38
- if (options.interactive !== false) {
39
- try {
40
- const preflightPassed = await runInteractivePreflightChecks(projectPath, options);
41
- if (!preflightPassed) {
42
- console.error(chalk.red('\n❌ Pre-flight checks failed'));
43
- console.error(chalk.gray('Fix the issues above before starting the application.\n'));
44
- process.exit(1);
45
- }
46
- } catch (error) {
47
- if (options.verbose) {
48
- console.error(chalk.yellow(`Pre-flight check error: ${error.message}`));
49
- }
50
- // Fall back to legacy checks if new system fails
51
- }
52
- }
53
-
54
- // Legacy pre-flight database checks (still run for Prisma validation)
28
+ // Pre-flight database checks
55
29
  try {
56
30
  await performDatabaseChecks(options.verbose);
57
31
  } catch (error) {
@@ -60,7 +34,7 @@ async function startCommand(options) {
60
34
  process.exit(1);
61
35
  }
62
36
 
63
- console.log(chalk.green('✓ Pre-flight checks passed\n'));
37
+ console.log(chalk.green('✓ Database checks passed\n'));
64
38
  console.log('Starting backend and optional frontend...');
65
39
 
66
40
  // Suppress AWS SDK warning message about maintenance mode
@@ -109,224 +83,6 @@ async function startCommand(options) {
109
83
  });
110
84
  }
111
85
 
112
- /**
113
- * Run interactive pre-flight checks with resolution prompts
114
- * @param {string} projectPath - Path to the Frigg project
115
- * @param {object} options - Command options
116
- * @returns {Promise<boolean>} True if all checks pass or were resolved
117
- */
118
- async function runInteractivePreflightChecks(projectPath, options) {
119
- // Create adapters and use case
120
- const dockerAdapter = new DockerAdapter();
121
- const databaseAdapter = new DatabaseAdapter();
122
- const preflightUseCase = new RunPreflightChecksUseCase({
123
- dockerAdapter,
124
- databaseAdapter
125
- });
126
-
127
- // Create prompt adapter based on mode (terminal or IPC)
128
- const promptMode = options.ipc ? 'ipc' : 'terminal';
129
- const promptAdapter = InteractivePromptAdapter.create({ mode: promptMode });
130
-
131
- if (options.verbose) {
132
- console.log(chalk.gray('Running pre-flight checks...'));
133
- }
134
-
135
- // Track which checks we've already processed to avoid infinite loops
136
- const processedChecks = new Set();
137
-
138
- // Keep running checks and resolving issues until all pass or no more can be resolved
139
- let maxIterations = 10; // Safety limit to prevent infinite loops
140
- while (maxIterations > 0) {
141
- maxIterations--;
142
-
143
- // Run checks
144
- const result = await preflightUseCase.execute({ projectPath });
145
-
146
- // If all passed, we're done
147
- if (result.allPassed) {
148
- return true;
149
- }
150
-
151
- // Get resolvable checks that we haven't already processed
152
- const resolvableChecks = preflightUseCase.getResolvableChecks(result)
153
- .filter(check => !processedChecks.has(check.name));
154
-
155
- // If no new resolvable checks, show failures and exit
156
- if (resolvableChecks.length === 0) {
157
- const failedChecks = result.checks.filter(c => c.status === 'failed');
158
- for (const check of failedChecks) {
159
- console.log(chalk.red(` ✗ ${check.name}: ${check.message}`));
160
- if (check.resolution?.instructions) {
161
- console.log(chalk.gray(` ${check.resolution.instructions}`));
162
- }
163
- }
164
- return false;
165
- }
166
-
167
- // Process the first resolvable check
168
- const check = resolvableChecks[0];
169
- processedChecks.add(check.name);
170
-
171
- // Display the failure
172
- console.log(chalk.yellow(`\n⚠️ ${check.message}`));
173
-
174
- // Only prompt if check is resolvable
175
- if (!check.canResolve) {
176
- if (check.resolution?.instructions) {
177
- console.log(chalk.gray(` ${check.resolution.instructions}`));
178
- }
179
- continue;
180
- }
181
-
182
- // Ask user if they want to resolve
183
- const response = await promptAdapter.promptForResolution(check);
184
-
185
- if (!response.shouldResolve) {
186
- console.log(chalk.gray(' Skipping resolution'));
187
- // User declined, show remaining failures and exit
188
- const failedChecks = result.checks.filter(c => c.status === 'failed');
189
- for (const failedCheck of failedChecks) {
190
- if (failedCheck.name !== check.name) {
191
- console.log(chalk.red(` ✗ ${failedCheck.name}: ${failedCheck.message}`));
192
- }
193
- }
194
- return false;
195
- }
196
-
197
- // Execute the resolution
198
- const resolved = await executeResolution(check, dockerAdapter, options);
199
-
200
- if (!resolved) {
201
- return false;
202
- }
203
-
204
- // Loop will continue and re-run checks
205
- }
206
-
207
- // If we exhausted iterations, something went wrong
208
- console.log(chalk.yellow(' ⚠️ Pre-flight check loop limit reached'));
209
- return false;
210
- }
211
-
212
- /**
213
- * Execute a resolution action
214
- * @param {object} check - The check that failed
215
- * @param {DockerAdapter} dockerAdapter - Docker adapter
216
- * @param {object} options - Command options
217
- * @returns {Promise<boolean>} True if resolution succeeded
218
- */
219
- async function executeResolution(check, dockerAdapter, options) {
220
- const { resolution } = check;
221
-
222
- switch (resolution.type) {
223
- case 'start_docker':
224
- console.log(chalk.blue(' Starting Docker Desktop...'));
225
- const dockerResult = await dockerAdapter.startDockerDesktop();
226
- if (!dockerResult.success) {
227
- console.error(chalk.red(` Failed to start Docker: ${dockerResult.error}`));
228
- return false;
229
- }
230
- // Wait for Docker to be ready
231
- console.log(chalk.gray(' Waiting for Docker to start...'));
232
- const isReady = await dockerAdapter.waitForDockerReady({
233
- maxAttempts: 60,
234
- intervalMs: 1000
235
- });
236
- if (!isReady) {
237
- console.error(chalk.red(' Docker did not start in time'));
238
- return false;
239
- }
240
- console.log(chalk.green(' ✓ Docker is now running'));
241
- return true;
242
-
243
- case 'start_docker_compose':
244
- console.log(chalk.blue(' Starting Docker Compose services...'));
245
- const composeResult = await dockerAdapter.startDockerCompose(resolution.composePath);
246
- if (!composeResult.success) {
247
- console.error(chalk.red(` Failed to start services: ${composeResult.error}`));
248
- return false;
249
- }
250
- console.log(chalk.green(' ✓ Docker Compose services started'));
251
- // Poll LocalStack health endpoint until services are ready
252
- console.log(chalk.gray(' Waiting for LocalStack to initialize...'));
253
- const localstackResult = await dockerAdapter.waitForLocalStack({
254
- maxAttempts: 30,
255
- intervalMs: 2000
256
- });
257
- if (localstackResult.ready) {
258
- console.log(chalk.green(' ✓ LocalStack services ready'));
259
- if (options.verbose && localstackResult.services) {
260
- console.log(chalk.gray(` Services: ${Object.keys(localstackResult.services).join(', ')}`));
261
- }
262
- } else {
263
- console.log(chalk.yellow(' ⚠️ LocalStack may not be fully ready'));
264
- if (localstackResult.error) {
265
- console.log(chalk.gray(` ${localstackResult.error}`));
266
- }
267
- // Continue anyway - the service might still work
268
- }
269
- return true;
270
-
271
- case 'create_env':
272
- // Try to copy .env.example to .env
273
- const projectPath = process.cwd();
274
- const envExamplePath = path.join(projectPath, '.env.example');
275
- const envPath = path.join(projectPath, '.env');
276
-
277
- if (fs.existsSync(envExamplePath)) {
278
- try {
279
- fs.copyFileSync(envExamplePath, envPath);
280
- console.log(chalk.green(' ✓ Created .env file from .env.example'));
281
- console.log(chalk.yellow(' ⚠️ Please edit .env to set your DATABASE_URL'));
282
-
283
- // Reload environment variables from the new .env file
284
- dotenv.config({ path: envPath, override: true });
285
-
286
- // Check if DATABASE_URL is now set
287
- if (process.env.DATABASE_URL && process.env.DATABASE_URL.trim() !== '') {
288
- console.log(chalk.green(' ✓ DATABASE_URL is now configured'));
289
- return true;
290
- } else {
291
- console.log(chalk.yellow(' ⚠️ DATABASE_URL is still not set in .env'));
292
- console.log(chalk.gray(' Please edit .env and set DATABASE_URL, then try again'));
293
- return false;
294
- }
295
- } catch (err) {
296
- console.error(chalk.red(` Failed to create .env: ${err.message}`));
297
- return false;
298
- }
299
- } else {
300
- console.log(chalk.yellow(' No .env.example file found'));
301
- console.log(chalk.gray(' Please create a .env file manually with DATABASE_URL'));
302
- return false;
303
- }
304
-
305
- case 'run_migrations':
306
- console.log(chalk.blue(' Running PostgreSQL migrations...'));
307
- const databaseAdapter = new DatabaseAdapter();
308
- const migrationProjectPath = process.cwd();
309
-
310
- // Use deploy mode (default) - non-interactive, applies existing migrations
311
- const migrateResult = await databaseAdapter.runMigrations(migrationProjectPath);
312
-
313
- if (!migrateResult.success) {
314
- console.error(chalk.red(` Failed to run migrations: ${migrateResult.error}`));
315
- console.log(chalk.gray(' Try running "frigg db:setup" manually'));
316
- return false;
317
- }
318
-
319
- console.log(chalk.green(' ✓ Database migrations applied successfully'));
320
- return true;
321
-
322
- default:
323
- if (options.verbose) {
324
- console.log(chalk.gray(` Unknown resolution type: ${resolution.type}`));
325
- }
326
- return false;
327
- }
328
- }
329
-
330
86
  /**
331
87
  * Performs pre-flight database validation checks
332
88
  * @param {boolean} verbose - Enable verbose output
@@ -2,19 +2,16 @@ const open = require('open');
2
2
  const chalk = require('chalk');
3
3
  const path = require('path');
4
4
  const ProcessManager = require('../utils/process-manager');
5
- const {
6
- getCurrentRepositoryInfo,
7
- discoverFriggRepositories,
5
+ const {
6
+ getCurrentRepositoryInfo,
7
+ discoverFriggRepositories,
8
8
  promptRepositorySelection,
9
- formatRepositoryInfo
9
+ formatRepositoryInfo
10
10
  } = require('../utils/repo-detection');
11
11
 
12
12
  async function uiCommand(options) {
13
- const { port = 3210, open: shouldOpen = true, repo: specifiedRepo, dev = false } = options;
14
-
15
- // Fix: --no-open should set open to false, not use the default true
16
- const shouldOpenBrowser = options.open !== false;
17
-
13
+ const { port = 3001, open: shouldOpen = true, repo: specifiedRepo, dev = false } = options;
14
+
18
15
  let targetRepo = null;
19
16
  let workingDirectory = process.cwd();
20
17
 
@@ -28,7 +25,7 @@ async function uiCommand(options) {
28
25
  // Check if we're already in a Frigg repository
29
26
  console.log(chalk.blue('Detecting Frigg repository...'));
30
27
  const currentRepo = await getCurrentRepositoryInfo();
31
-
28
+
32
29
  if (currentRepo) {
33
30
  console.log(chalk.green(`✓ Found Frigg repository: ${formatRepositoryInfo(currentRepo)}`));
34
31
  if (currentRepo.currentSubPath) {
@@ -40,40 +37,40 @@ async function uiCommand(options) {
40
37
  // Discover Frigg repositories
41
38
  console.log(chalk.yellow('Current directory is not a Frigg repository.'));
42
39
  console.log(chalk.blue('Searching for Frigg repositories...'));
43
-
40
+
44
41
  const discoveredRepos = await discoverFriggRepositories();
45
-
42
+
46
43
  if (discoveredRepos.length === 0) {
47
44
  console.log(chalk.red('No Frigg repositories found. Please create one first.'));
48
45
  process.exit(1);
49
46
  }
50
-
47
+
51
48
  // For UI command, we'll let the UI handle repository selection
52
49
  // Set a placeholder and pass the discovered repos via environment
53
- targetRepo = {
54
- name: 'Multiple Repositories Available',
50
+ targetRepo = {
51
+ name: 'Multiple Repositories Available',
55
52
  path: process.cwd(),
56
53
  isMultiRepo: true,
57
54
  availableRepos: discoveredRepos
58
55
  };
59
56
  workingDirectory = process.cwd();
60
-
57
+
61
58
  console.log(chalk.blue(`Found ${discoveredRepos.length} Frigg repositories. You'll be able to select one in the UI.`));
62
59
  }
63
60
  }
64
61
 
65
62
  console.log(chalk.blue('🚀 Starting Frigg Management UI...'));
66
-
63
+
67
64
  const processManager = new ProcessManager();
68
-
65
+
69
66
  try {
70
67
  const managementUiPath = path.join(__dirname, '../../management-ui');
71
-
68
+
72
69
  // Check if we're in development mode
73
70
  // For CLI usage, we prefer development mode unless explicitly set to production
74
71
  const fs = require('fs');
75
72
  const isDevelopment = dev || process.env.NODE_ENV !== 'production';
76
-
73
+
77
74
  if (isDevelopment) {
78
75
  const env = {
79
76
  ...process.env,
@@ -83,15 +80,15 @@ async function uiCommand(options) {
83
80
  REPOSITORY_INFO: JSON.stringify(targetRepo),
84
81
  AVAILABLE_REPOSITORIES: targetRepo.isMultiRepo ? JSON.stringify(targetRepo.availableRepos) : null
85
82
  };
86
-
87
- // Start backend server with nodemon for auto-restart
83
+
84
+ // Start backend server
88
85
  processManager.spawnProcess(
89
86
  'backend',
90
87
  'npm',
91
- ['run', 'server:dev'],
88
+ ['run', 'server'],
92
89
  { cwd: managementUiPath, env }
93
90
  );
94
-
91
+
95
92
  // Start frontend dev server
96
93
  processManager.spawnProcess(
97
94
  'frontend',
@@ -99,71 +96,52 @@ async function uiCommand(options) {
99
96
  ['run', 'dev'],
100
97
  { cwd: managementUiPath, env }
101
98
  );
102
-
103
- // Wait for backend to be ready by polling health endpoint
104
- const maxAttempts = 20;
105
- const delayMs = 250;
106
- let backendReady = false;
107
-
108
- for (let attempt = 0; attempt < maxAttempts; attempt++) {
109
- try {
110
- const response = await fetch(`http://localhost:${port}/api/health`);
111
- if (response.ok) {
112
- backendReady = true;
113
- break;
114
- }
115
- } catch (err) {
116
- // Backend not ready yet, wait and retry
117
- }
118
- await new Promise(resolve => setTimeout(resolve, delayMs));
119
- }
120
-
121
- if (!backendReady) {
122
- console.warn('⚠️ Backend health check timed out, but continuing anyway...');
123
- }
124
-
99
+
100
+ // Wait for servers to start
101
+ await new Promise(resolve => setTimeout(resolve, 2000));
102
+
125
103
  // Display clean status
126
104
  processManager.printStatus(
127
105
  'http://localhost:5173',
128
106
  `http://localhost:${port}`,
129
107
  targetRepo.name
130
108
  );
131
-
109
+
132
110
  // Open browser if requested
133
- if (shouldOpenBrowser) {
111
+ if (shouldOpen) {
134
112
  setTimeout(() => {
135
113
  open('http://localhost:5173');
136
114
  }, 1000);
137
115
  }
138
-
116
+
139
117
  } else {
140
118
  // Production mode - just start the backend server
141
119
  const { FriggManagementServer } = await import('../../management-ui/server/index.js');
142
-
143
- const server = new FriggManagementServer({
144
- port,
120
+
121
+ const server = new FriggManagementServer({
122
+ port,
145
123
  projectRoot: workingDirectory,
146
124
  repositoryInfo: targetRepo,
147
125
  availableRepositories: targetRepo.isMultiRepo ? targetRepo.availableRepos : null
148
126
  });
149
127
  await server.start();
150
-
128
+
151
129
  processManager.printStatus(
152
130
  `http://localhost:${port}`,
153
131
  `http://localhost:${port}/api`,
154
132
  targetRepo.name
155
133
  );
156
-
157
- if (shouldOpenBrowser) {
134
+
135
+ if (shouldOpen) {
158
136
  setTimeout(() => {
159
137
  open(`http://localhost:${port}`);
160
138
  }, 1000);
161
139
  }
162
140
  }
163
-
141
+
164
142
  // Keep the process running
165
143
  process.stdin.resume();
166
-
144
+
167
145
  } catch (error) {
168
146
  console.error(chalk.red('Failed to start Management UI:'), error.message);
169
147
  if (error.code === 'EADDRINUSE') {
@@ -37,70 +37,17 @@ async function isFriggRepository(directory) {
37
37
  friggDependencies: []
38
38
  };
39
39
 
40
- // Check for @friggframework/core v2.0+ specifically (REQUIRED for Frigg apps)
41
- // Check both root package.json and backend/package.json for workspace projects
40
+ // Check for @friggframework dependencies
42
41
  const allDeps = {
43
42
  ...packageJson.dependencies,
44
- ...packageJson.devDependencies
43
+ ...packageJson.devDependencies,
44
+ ...packageJson.peerDependencies
45
45
  };
46
46
 
47
- // Check root package.json for core dependency
48
- let coreVersion = allDeps['@friggframework/core'];
49
- let hasFriggCore = coreVersion && (
50
- coreVersion === 'next' ||
51
- coreVersion.includes('2.0.0') ||
52
- coreVersion.includes('next') ||
53
- coreVersion.startsWith('^2.') ||
54
- coreVersion.startsWith('~2.') ||
55
- coreVersion.startsWith('2.')
56
- );
57
-
58
- // If not in root, check backend/package.json for workspace projects
59
- if (!hasFriggCore) {
60
- const backendPackagePath = path.join(directory, 'backend', 'package.json');
61
- if (fs.existsSync(backendPackagePath)) {
62
- try {
63
- const backendPackageJson = await fs.readJson(backendPackagePath);
64
- const backendDeps = {
65
- ...backendPackageJson.dependencies,
66
- ...backendPackageJson.devDependencies
67
- };
68
- coreVersion = backendDeps['@friggframework/core'];
69
- hasFriggCore = coreVersion && (
70
- coreVersion === 'next' ||
71
- coreVersion.includes('2.0.0') ||
72
- coreVersion.includes('next') ||
73
- coreVersion.startsWith('^2.') ||
74
- coreVersion.startsWith('~2.') ||
75
- coreVersion.startsWith('2.')
76
- );
77
- } catch (error) {
78
- // Ignore errors reading backend package.json
79
- }
80
- }
81
- }
82
-
83
- if (hasFriggCore) {
84
- indicators.hasFriggDependencies = true;
85
- indicators.friggDependencies.push('@friggframework/core');
86
- }
87
-
88
- // Also track other Frigg dependencies for reference
89
47
  for (const dep in allDeps) {
90
- if (dep.startsWith('@friggframework/') && dep !== '@friggframework/core') {
91
- const version = allDeps[dep];
92
- const isV2Plus = version && (
93
- version === 'next' ||
94
- version.includes('2.0.0') ||
95
- version.includes('next') ||
96
- version.startsWith('^2.') ||
97
- version.startsWith('~2.') ||
98
- version.startsWith('2.')
99
- );
100
-
101
- if (isV2Plus) {
102
- indicators.friggDependencies.push(dep);
103
- }
48
+ if (dep.startsWith('@friggframework/')) {
49
+ indicators.hasFriggDependencies = true;
50
+ indicators.friggDependencies.push(dep);
104
51
  }
105
52
  }
106
53
 
@@ -189,33 +136,42 @@ async function isFriggRepository(directory) {
189
136
  }
190
137
  }
191
138
 
192
- // STRICT VALIDATION: A directory is considered a Frigg app repository ONLY if:
193
- // 1. Has @friggframework/core v2.0+ as a direct dependency (MANDATORY)
194
- // 2. Has actual Frigg app structure (index.js with Definition OR backend/serverless.yml)
195
- // 3. Is NOT a framework package itself (no @friggframework/* package name)
196
-
197
- // Check for Frigg app structure indicators
198
- const hasRootIndexJs = fs.existsSync(path.join(directory, 'index.js'));
199
- const hasBackendIndexJs = fs.existsSync(path.join(directory, 'backend', 'index.js'));
200
- const hasBackendServerless = fs.existsSync(path.join(directory, 'backend', 'serverless.yml'));
201
- const hasAppStructure = hasRootIndexJs || hasBackendIndexJs || hasBackendServerless;
139
+ // A directory is considered a Frigg repo if it has:
140
+ // 1. Frigg dependencies (MANDATORY - most reliable indicator) OR
141
+ // 2. Frigg-specific configuration files OR
142
+ // 3. Frigg-specific directories OR
143
+ // 4. Frigg-specific scripts in package.json OR
144
+ // 5. Serverless config with explicit Frigg references AND proper structure
145
+ //
146
+ // For Zapier apps, we require explicit Frigg indicators
147
+ const hasFriggIndicators = indicators.hasFriggDependencies ||
148
+ indicators.hasFriggConfig ||
149
+ indicators.hasFriggDirectories ||
150
+ indicators.hasFriggScripts ||
151
+ hasFriggServerlessIndicators;
152
+
153
+ // Determine if it's a Frigg repository
154
+ let isFriggRepo = false;
155
+
156
+ if (isZapierApp) {
157
+ // For Zapier apps, require explicit Frigg dependencies or config
158
+ isFriggRepo = indicators.hasFriggDependencies || indicators.hasFriggConfig;
159
+ } else {
160
+ // For non-Zapier apps, any Frigg indicator is sufficient
161
+ isFriggRepo = hasFriggIndicators;
162
+ }
202
163
 
203
- // Determine if it's a Frigg repository with STRICT criteria
204
- const isFriggRepo = indicators.hasFriggDependencies && hasAppStructure;
164
+ // Additional validation for edge cases
165
+ if (isZapierApp && !indicators.hasFriggDependencies && !indicators.hasFriggConfig) {
166
+ return { isFriggRepo: false, repoInfo: null };
167
+ }
205
168
 
206
169
  if (isFriggRepo) {
207
- // IMPORTANT: If backend/ has a Frigg app structure, use backend/ as the path
208
- // This handles workspace projects where the actual app is in backend/
209
- let actualPath = directory;
210
- if (hasBackendIndexJs || hasBackendServerless) {
211
- actualPath = path.join(directory, 'backend');
212
- }
213
-
214
170
  return {
215
171
  isFriggRepo: true,
216
172
  repoInfo: {
217
173
  name: packageJson.name || path.basename(directory),
218
- path: actualPath,
174
+ path: directory,
219
175
  version: packageJson.version,
220
176
  framework: detectFramework(directory, existingFrontendDirs),
221
177
  hasBackend: fs.existsSync(path.join(directory, 'backend')),
@@ -289,16 +245,12 @@ async function discoverFriggRepositories(options = {}) {
289
245
  searchPaths = [
290
246
  process.cwd(),
291
247
  path.join(os.homedir(), 'Documents'),
292
- path.join(os.homedir(), 'Documents', 'GitHub'), // Common GitHub Desktop location
293
248
  path.join(os.homedir(), 'Projects'),
294
249
  path.join(os.homedir(), 'Development'),
295
250
  path.join(os.homedir(), 'dev'),
296
- path.join(os.homedir(), 'Code'),
297
- path.join(os.homedir(), 'GitHub'), // Alternative GitHub location
298
- path.join(os.homedir(), 'repos'), // Common repos folder
299
- path.join(os.homedir(), 'src') // Common source folder
251
+ path.join(os.homedir(), 'Code')
300
252
  ],
301
- maxDepth = 4, // Increased to handle deeper nested projects (e.g., org/apps/project)
253
+ maxDepth = 3,
302
254
  excludePatterns = ['node_modules', '.git', 'dist', 'build', '.next', 'coverage']
303
255
  } = options;
304
256
 
@@ -160,7 +160,7 @@ Consider separate policies for different environments:
160
160
  ### Validation
161
161
  Test your policy by deploying a simple Frigg app:
162
162
  ```bash
163
- frigg init test-deployment
163
+ npx create-frigg-app test-deployment
164
164
  cd test-deployment
165
165
  frigg deploy
166
166
  ```
@@ -1500,9 +1500,10 @@ describe('VpcBuilder', () => {
1500
1500
  }
1501
1501
  };
1502
1502
 
1503
+ // Discovery results matching ACTUAL Frontify production stack
1503
1504
  const discoveredResources = {
1504
1505
  fromCloudFormationStack: true,
1505
- stackName: 'frigg-app-production',
1506
+ stackName: 'create-frigg-app-production',
1506
1507
  existingLogicalIds: [
1507
1508
  'FriggLambdaRouteTable',
1508
1509
  'FriggNATRoute', // OLD naming
@@ -1565,9 +1566,10 @@ describe('VpcBuilder', () => {
1565
1566
  });
1566
1567
 
1567
1568
  it('should convert OLD logical IDs to structured discovery stackManaged array', () => {
1569
+ // TDD test: Verify that VPCEndpointS3 in existingLogicalIds gets added to stackManaged
1568
1570
  const flatDiscovery = {
1569
1571
  fromCloudFormationStack: true,
1570
- stackName: 'frigg-app-production',
1572
+ stackName: 'create-frigg-app-production',
1571
1573
  existingLogicalIds: [
1572
1574
  'VPCEndpointS3', // OLD naming
1573
1575
  'VPCEndpointDynamoDB', // OLD naming
@@ -746,7 +746,7 @@ describe('VpcResourceResolver', () => {
746
746
  ],
747
747
  external: [],
748
748
  fromCloudFormation: true,
749
- stackName: 'frigg-app-production'
749
+ stackName: 'create-frigg-app-production'
750
750
  };
751
751
 
752
752
  const decisions = resolver.resolveAll(appDefinition, discovery);