@friggframework/devtools 2.0.0--canary.549.70ef06a.0 → 2.0.0--canary.545.ccb5010.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 (127) hide show
  1. package/frigg-cli/README.md +1 -1
  2. package/frigg-cli/__tests__/application/use-cases/AddApiModuleToIntegrationUseCase.test.js +326 -0
  3. package/frigg-cli/__tests__/application/use-cases/CreateApiModuleUseCase.test.js +337 -0
  4. package/frigg-cli/__tests__/domain/entities/ApiModule.test.js +373 -0
  5. package/frigg-cli/__tests__/domain/entities/AppDefinition.test.js +313 -0
  6. package/frigg-cli/__tests__/domain/services/IntegrationValidator.test.js +269 -0
  7. package/frigg-cli/__tests__/domain/value-objects/IntegrationName.test.js +82 -0
  8. package/frigg-cli/__tests__/infrastructure/adapters/IntegrationJsUpdater.test.js +408 -0
  9. package/frigg-cli/__tests__/infrastructure/repositories/FileSystemApiModuleRepository.test.js +583 -0
  10. package/frigg-cli/__tests__/infrastructure/repositories/FileSystemAppDefinitionRepository.test.js +314 -0
  11. package/frigg-cli/__tests__/infrastructure/repositories/FileSystemIntegrationRepository.test.js +383 -0
  12. package/frigg-cli/__tests__/unit/commands/build.test.js +1 -1
  13. package/frigg-cli/__tests__/unit/commands/doctor.test.js +0 -2
  14. package/frigg-cli/__tests__/unit/commands/init.test.js +406 -0
  15. package/frigg-cli/__tests__/unit/commands/install.test.js +23 -19
  16. package/frigg-cli/__tests__/unit/commands/provider-dispatch.test.js +383 -0
  17. package/frigg-cli/__tests__/unit/commands/repair.test.js +275 -0
  18. package/frigg-cli/__tests__/unit/dependencies.test.js +2 -2
  19. package/frigg-cli/__tests__/unit/start-command/application/RunPreflightChecksUseCase.test.js +411 -0
  20. package/frigg-cli/__tests__/unit/start-command/infrastructure/DatabaseAdapter.test.js +405 -0
  21. package/frigg-cli/__tests__/unit/start-command/infrastructure/DockerAdapter.test.js +496 -0
  22. package/frigg-cli/__tests__/unit/start-command/presentation/InteractivePromptAdapter.test.js +474 -0
  23. package/frigg-cli/__tests__/unit/utils/output.test.js +196 -0
  24. package/frigg-cli/application/use-cases/AddApiModuleToIntegrationUseCase.js +93 -0
  25. package/frigg-cli/application/use-cases/CreateApiModuleUseCase.js +93 -0
  26. package/frigg-cli/application/use-cases/CreateIntegrationUseCase.js +103 -0
  27. package/frigg-cli/build-command/index.js +123 -11
  28. package/frigg-cli/container.js +172 -0
  29. package/frigg-cli/deploy-command/index.js +83 -1
  30. package/frigg-cli/docs/OUTPUT_MIGRATION_GUIDE.md +286 -0
  31. package/frigg-cli/doctor-command/index.js +37 -16
  32. package/frigg-cli/domain/entities/ApiModule.js +272 -0
  33. package/frigg-cli/domain/entities/AppDefinition.js +227 -0
  34. package/frigg-cli/domain/entities/Integration.js +198 -0
  35. package/frigg-cli/domain/exceptions/DomainException.js +24 -0
  36. package/frigg-cli/domain/ports/IApiModuleRepository.js +53 -0
  37. package/frigg-cli/domain/ports/IAppDefinitionRepository.js +43 -0
  38. package/frigg-cli/domain/ports/IIntegrationRepository.js +61 -0
  39. package/frigg-cli/domain/services/IntegrationValidator.js +185 -0
  40. package/frigg-cli/domain/value-objects/IntegrationId.js +42 -0
  41. package/frigg-cli/domain/value-objects/IntegrationName.js +60 -0
  42. package/frigg-cli/domain/value-objects/SemanticVersion.js +70 -0
  43. package/frigg-cli/generate-iam-command.js +21 -1
  44. package/frigg-cli/index.js +21 -6
  45. package/frigg-cli/index.test.js +7 -2
  46. package/frigg-cli/infrastructure/UnitOfWork.js +46 -0
  47. package/frigg-cli/infrastructure/adapters/BackendJsUpdater.js +197 -0
  48. package/frigg-cli/infrastructure/adapters/FileSystemAdapter.js +224 -0
  49. package/frigg-cli/infrastructure/adapters/IntegrationJsUpdater.js +249 -0
  50. package/frigg-cli/infrastructure/adapters/SchemaValidator.js +92 -0
  51. package/frigg-cli/infrastructure/repositories/FileSystemApiModuleRepository.js +373 -0
  52. package/frigg-cli/infrastructure/repositories/FileSystemAppDefinitionRepository.js +116 -0
  53. package/frigg-cli/infrastructure/repositories/FileSystemIntegrationRepository.js +277 -0
  54. package/frigg-cli/init-command/backend-first-handler.js +124 -42
  55. package/frigg-cli/init-command/index.js +2 -1
  56. package/frigg-cli/init-command/template-handler.js +13 -3
  57. package/frigg-cli/install-command/backend-js.js +3 -3
  58. package/frigg-cli/install-command/environment-variables.js +16 -19
  59. package/frigg-cli/install-command/environment-variables.test.js +12 -13
  60. package/frigg-cli/install-command/index.js +14 -9
  61. package/frigg-cli/install-command/integration-file.js +3 -3
  62. package/frigg-cli/install-command/validate-package.js +5 -9
  63. package/frigg-cli/jest.config.js +4 -1
  64. package/frigg-cli/package-lock.json +16226 -0
  65. package/frigg-cli/repair-command/index.js +121 -128
  66. package/frigg-cli/start-command/application/RunPreflightChecksUseCase.js +376 -0
  67. package/frigg-cli/start-command/index.js +324 -2
  68. package/frigg-cli/start-command/infrastructure/DatabaseAdapter.js +591 -0
  69. package/frigg-cli/start-command/infrastructure/DockerAdapter.js +306 -0
  70. package/frigg-cli/start-command/presentation/InteractivePromptAdapter.js +329 -0
  71. package/frigg-cli/templates/backend/.env.example +62 -0
  72. package/frigg-cli/templates/backend/.eslintrc.json +12 -0
  73. package/frigg-cli/templates/backend/.prettierrc +6 -0
  74. package/frigg-cli/templates/backend/docker-compose.yml +22 -0
  75. package/frigg-cli/templates/backend/index.js +96 -0
  76. package/frigg-cli/templates/backend/infrastructure.js +12 -0
  77. package/frigg-cli/templates/backend/jest.config.js +17 -0
  78. package/frigg-cli/templates/backend/package.json +50 -0
  79. package/frigg-cli/templates/backend/src/api-modules/.gitkeep +10 -0
  80. package/frigg-cli/templates/backend/src/base/.gitkeep +7 -0
  81. package/frigg-cli/templates/backend/src/integrations/.gitkeep +10 -0
  82. package/frigg-cli/templates/backend/src/integrations/ExampleIntegration.js +65 -0
  83. package/frigg-cli/templates/backend/src/utils/.gitkeep +7 -0
  84. package/frigg-cli/templates/backend/test/setup.js +30 -0
  85. package/frigg-cli/templates/backend/ui-extensions/.gitkeep +0 -0
  86. package/frigg-cli/templates/backend/ui-extensions/README.md +77 -0
  87. package/frigg-cli/ui-command/index.js +58 -36
  88. package/frigg-cli/utils/__tests__/provider-helper.test.js +55 -0
  89. package/frigg-cli/utils/__tests__/repo-detection.test.js +436 -0
  90. package/frigg-cli/utils/output.js +382 -0
  91. package/frigg-cli/utils/provider-helper.js +75 -0
  92. package/frigg-cli/utils/repo-detection.js +85 -37
  93. package/frigg-cli/validate-command/__tests__/adapters/validate-command.test.js +205 -0
  94. package/frigg-cli/validate-command/__tests__/application/validate-app-use-case.test.js +104 -0
  95. package/frigg-cli/validate-command/__tests__/domain/fix-suggestion.test.js +153 -0
  96. package/frigg-cli/validate-command/__tests__/domain/validation-error.test.js +162 -0
  97. package/frigg-cli/validate-command/__tests__/domain/validation-result.test.js +152 -0
  98. package/frigg-cli/validate-command/__tests__/infrastructure/api-module-validator.test.js +332 -0
  99. package/frigg-cli/validate-command/__tests__/infrastructure/app-definition-validator.test.js +191 -0
  100. package/frigg-cli/validate-command/__tests__/infrastructure/integration-class-validator.test.js +146 -0
  101. package/frigg-cli/validate-command/__tests__/infrastructure/template-validation.test.js +155 -0
  102. package/frigg-cli/validate-command/adapters/cli/validate-command.js +199 -0
  103. package/frigg-cli/validate-command/application/use-cases/validate-app-use-case.js +35 -0
  104. package/frigg-cli/validate-command/domain/entities/validation-result.js +74 -0
  105. package/frigg-cli/validate-command/domain/value-objects/fix-suggestion.js +74 -0
  106. package/frigg-cli/validate-command/domain/value-objects/validation-error.js +68 -0
  107. package/frigg-cli/validate-command/infrastructure/validators/api-module-validator.js +181 -0
  108. package/frigg-cli/validate-command/infrastructure/validators/app-definition-validator.js +145 -0
  109. package/frigg-cli/validate-command/infrastructure/validators/integration-class-validator.js +113 -0
  110. package/infrastructure/create-frigg-infrastructure.js +93 -0
  111. package/infrastructure/docs/iam-policy-templates.md +1 -1
  112. package/infrastructure/domains/admin-scripts/admin-script-builder.js +200 -0
  113. package/infrastructure/domains/admin-scripts/admin-script-builder.test.js +499 -0
  114. package/infrastructure/domains/admin-scripts/index.js +5 -0
  115. package/infrastructure/domains/networking/vpc-builder.test.js +2 -4
  116. package/infrastructure/domains/networking/vpc-resolver.test.js +1 -1
  117. package/infrastructure/domains/shared/resource-discovery.js +5 -5
  118. package/infrastructure/domains/shared/types/app-definition.js +35 -0
  119. package/infrastructure/domains/shared/types/discovery-result.test.js +1 -1
  120. package/infrastructure/domains/shared/utilities/base-definition-factory.js +10 -1
  121. package/infrastructure/domains/shared/utilities/base-definition-factory.test.js +2 -2
  122. package/infrastructure/infrastructure-composer.js +2 -0
  123. package/infrastructure/infrastructure-composer.test.js +2 -2
  124. package/infrastructure/jest.config.js +16 -0
  125. package/management-ui/README.md +245 -109
  126. package/package.json +8 -7
  127. package/frigg-cli/install-command/logger.js +0 -12
@@ -1,7 +1,7 @@
1
1
  const fs = require('fs-extra');
2
2
  const path = require('path');
3
3
  const chalk = require('chalk');
4
- const { select, confirm, multiselect } = require('@inquirer/prompts');
4
+ const { select, confirm, checkbox } = require('@inquirer/prompts');
5
5
  const { execSync } = require('child_process');
6
6
  const spawn = require('cross-spawn');
7
7
  const npmRegistry = require('../utils/npm-registry');
@@ -35,15 +35,15 @@ class BackendFirstHandler {
35
35
  await this.createProject(deploymentMode, config);
36
36
 
37
37
  console.log(chalk.green('\n✅ Frigg application created successfully!'));
38
-
39
- // If user needs custom API module, prompt to create it
40
- if (config.needsCustomApiModule) {
38
+
39
+ // If user needs custom API module, prompt to create it (skip in --yes mode)
40
+ if (config.needsCustomApiModule && !this.options.yes) {
41
41
  console.log(chalk.cyan('\n🔧 Now let\'s create your custom API module...'));
42
42
  const createModule = await confirm({
43
43
  message: 'Would you like to create your custom API module now?',
44
44
  default: true
45
45
  });
46
-
46
+
47
47
  if (createModule) {
48
48
  console.log(chalk.gray('\n Run this command after setup:'));
49
49
  console.log(chalk.cyan(` cd ${path.relative(process.cwd(), this.targetPath)}`));
@@ -62,6 +62,11 @@ class BackendFirstHandler {
62
62
  return this.options.mode;
63
63
  }
64
64
 
65
+ // If --yes flag is set, use default
66
+ if (this.options.yes) {
67
+ return 'standalone';
68
+ }
69
+
65
70
  const mode = await select({
66
71
  message: 'How will you deploy this Frigg application?',
67
72
  choices: [
@@ -88,6 +93,21 @@ class BackendFirstHandler {
88
93
  async getProjectConfiguration(deploymentMode) {
89
94
  const config = { deploymentMode };
90
95
 
96
+ // If --yes flag is set, use all defaults
97
+ if (this.options.yes) {
98
+ return {
99
+ deploymentMode,
100
+ appPurpose: 'exploring',
101
+ needsCustomApiModule: false,
102
+ includeIntegrations: false,
103
+ starterIntegrations: [],
104
+ includeDemoFrontend: false,
105
+ serverlessProvider: deploymentMode === 'standalone' ? 'aws' : undefined,
106
+ installDependencies: true,
107
+ initializeGit: true,
108
+ };
109
+ }
110
+
91
111
  // Ask about the purpose of this Frigg application
92
112
  config.appPurpose = await select({
93
113
  message: 'What are you building with Frigg?',
@@ -158,7 +178,7 @@ class BackendFirstHandler {
158
178
  }
159
179
  });
160
180
 
161
- config.starterIntegrations = await multiselect({
181
+ config.starterIntegrations = await checkbox({
162
182
  message: 'Select API modules to integrate (space to select, enter to confirm):',
163
183
  choices,
164
184
  instructions: '\n Press <space> to select, <a> to toggle all, <enter> to confirm\n',
@@ -266,63 +286,115 @@ class BackendFirstHandler {
266
286
 
267
287
  /**
268
288
  * Create standalone Frigg service
289
+ *
290
+ * Structure:
291
+ * my-app/
292
+ * ├── package.json # Root - delegates to backend with "cd backend &&"
293
+ * ├── backend/ # Actual Frigg app with its own package.json & node_modules
294
+ * │ ├── index.js
295
+ * │ ├── infrastructure.js
296
+ * │ ├── package.json
297
+ * │ └── src/
298
+ * └── ui-extensions/ # Platform-specific UI extensions
299
+ * └── README.md
269
300
  */
270
301
  async createStandaloneProject(config) {
271
- // Copy backend template
302
+ const backendPath = path.join(this.targetPath, 'backend');
303
+ const uiExtensionsPath = path.join(this.targetPath, 'ui-extensions');
304
+
305
+ // Copy backend template to backend/ subdirectory
272
306
  const backendTemplate = path.join(this.templatesDir, 'backend');
273
- await fs.copy(backendTemplate, this.targetPath);
307
+ await fs.copy(backendTemplate, backendPath);
274
308
 
275
- // Create package.json for standalone mode
276
- const packageJson = {
277
- name: this.appName,
309
+ // Read template package.json
310
+ const templatePackageJsonPath = path.join(backendPath, 'package.json');
311
+ let templatePackageJson = {};
312
+ if (await fs.pathExists(templatePackageJsonPath)) {
313
+ templatePackageJson = await fs.readJSON(templatePackageJsonPath);
314
+ }
315
+
316
+ // Create backend package.json with all dependencies
317
+ const backendPackageJson = {
318
+ name: `${this.appName}-backend`,
278
319
  version: '0.1.0',
279
320
  private: true,
321
+ prettier: templatePackageJson.prettier || '@friggframework/prettier-config',
280
322
  scripts: {
323
+ ...templatePackageJson.scripts,
281
324
  "backend-start": "node infrastructure.js start",
282
325
  "start": "npm run backend-start",
283
326
  "build": "node infrastructure.js package",
284
327
  "deploy": "node infrastructure.js deploy",
285
- "test": "jest"
286
328
  },
287
329
  dependencies: {
288
- "@friggframework/core": "^2.0.0"
330
+ ...templatePackageJson.dependencies,
331
+ "@friggframework/core": "2.0.0-next.58"
332
+ },
333
+ devDependencies: {
334
+ ...templatePackageJson.devDependencies
289
335
  }
290
336
  };
291
337
 
292
- // Add demo frontend if requested
293
- if (config.includeDemoFrontend) {
294
- packageJson.workspaces = ['backend', 'frontend'];
295
- packageJson.scripts['dev'] = 'concurrently "npm run backend-start" "npm run frontend:dev"';
296
- packageJson.scripts['frontend:dev'] = 'cd frontend && npm run dev';
297
-
298
- await this.createDemoFrontend(config);
299
- }
300
-
301
- // Add selected integrations as dependencies
338
+ // Add selected integrations as dependencies to backend
302
339
  if (config.starterIntegrations && config.starterIntegrations.length > 0) {
303
340
  for (const integration of config.starterIntegrations) {
304
- packageJson.dependencies[`@friggframework/api-module-${integration}`] = '^2.0.0';
341
+ backendPackageJson.dependencies[`@friggframework/api-module-${integration}`] = '^2.0.0';
305
342
  }
306
343
  }
307
344
 
345
+ await fs.writeJSON(templatePackageJsonPath, backendPackageJson, { spaces: 2 });
346
+
347
+ // Create root package.json that delegates to backend
348
+ const rootPackageJson = {
349
+ name: this.appName,
350
+ version: '0.1.0',
351
+ private: true,
352
+ scripts: {
353
+ "start": "cd backend && npm run frigg:start",
354
+ "docker:start": "cd backend && npm run docker:start",
355
+ "docker:stop": "cd backend && npm run docker:stop",
356
+ "test": "cd backend && npm test",
357
+ "build": "cd backend && npm run build",
358
+ "deploy": "cd backend && npm run deploy",
359
+ "lint": "cd backend && npm run lint",
360
+ "format": "cd backend && npm run format"
361
+ }
362
+ };
363
+
364
+ // Add demo frontend if requested
365
+ if (config.includeDemoFrontend) {
366
+ rootPackageJson.scripts['dev'] = 'concurrently "cd backend && npm run backend-start" "cd frontend && npm run dev"';
367
+ rootPackageJson.scripts['frontend:dev'] = 'cd frontend && npm run dev';
368
+ rootPackageJson.devDependencies = { concurrently: '^8.2.2' };
369
+
370
+ await this.createDemoFrontend(config);
371
+ }
372
+
308
373
  await fs.writeJSON(
309
374
  path.join(this.targetPath, 'package.json'),
310
- packageJson,
375
+ rootPackageJson,
311
376
  { spaces: 2 }
312
377
  );
313
378
 
379
+ // Create ui-extensions directory with README
380
+ await fs.ensureDir(uiExtensionsPath);
381
+ const uiExtensionsReadme = path.join(this.templatesDir, 'backend', 'ui-extensions', 'README.md');
382
+ if (await fs.pathExists(uiExtensionsReadme)) {
383
+ await fs.copy(uiExtensionsReadme, path.join(uiExtensionsPath, 'README.md'));
384
+ }
385
+
314
386
  // Update index.js with selected integrations
315
387
  if (config.starterIntegrations && config.starterIntegrations.length > 0) {
316
- await this.updateAppDefinition(config.starterIntegrations);
388
+ await this.updateAppDefinition(config.starterIntegrations, backendPath);
317
389
  }
318
390
 
319
391
  // Validate generated app definition against schema
320
- const appDefPath = path.join(this.targetPath, 'index.js');
392
+ const appDefPath = path.join(backendPath, 'index.js');
321
393
  await this.validateGeneratedAppDefinition(appDefPath);
322
394
 
323
395
  // Update serverless.yml based on provider
324
396
  if (config.serverlessProvider === 'aws') {
325
- await this.configureAWSServerless();
397
+ await this.configureAWSServerless(backendPath);
326
398
  }
327
399
  }
328
400
 
@@ -506,9 +578,9 @@ To integrate Frigg into your production application:
506
578
  /**
507
579
  * Configure AWS serverless
508
580
  */
509
- async configureAWSServerless() {
581
+ async configureAWSServerless(targetDir = this.targetPath) {
510
582
  // Update serverless.yml for AWS
511
- const serverlessPath = path.join(this.targetPath, 'serverless.yml');
583
+ const serverlessPath = path.join(targetDir, 'serverless.yml');
512
584
  if (await fs.pathExists(serverlessPath)) {
513
585
  // Keep existing AWS configuration
514
586
  console.log(chalk.gray('AWS Lambda configuration ready'));
@@ -533,23 +605,25 @@ To integrate Frigg into your production application:
533
605
  }
534
606
 
535
607
  /**
536
- * Install dependencies
608
+ * Install dependencies in the backend directory
537
609
  */
538
610
  async installDependencies(config) {
539
- console.log(chalk.blue('\n📦 Installing dependencies...'));
540
-
611
+ console.log(chalk.blue('\n📦 Installing dependencies in backend...'));
612
+
541
613
  const useYarn = this.isUsingYarn();
542
614
  const command = useYarn ? 'yarn' : 'npm';
543
615
  const args = useYarn ? [] : ['install'];
544
616
 
617
+ // Install in backend directory where package.json with dependencies lives
618
+ const backendPath = path.join(this.targetPath, 'backend');
545
619
  const proc = spawn.sync(command, args, {
546
- cwd: this.targetPath,
620
+ cwd: backendPath,
547
621
  stdio: 'inherit'
548
622
  });
549
623
 
550
624
  if (proc.status !== 0) {
551
625
  console.log(chalk.yellow('\n⚠️ Dependency installation failed'));
552
- console.log(chalk.gray(`You can install manually with: ${command} install`));
626
+ console.log(chalk.gray(`You can install manually with: cd backend && ${command} install`));
553
627
  }
554
628
  }
555
629
 
@@ -577,7 +651,7 @@ To integrate Frigg into your production application:
577
651
  * Select from default integrations when npm is unavailable
578
652
  */
579
653
  async selectDefaultIntegrations() {
580
- return await multiselect({
654
+ return await checkbox({
581
655
  message: 'Select starter integrations (space to select, enter to confirm):',
582
656
  choices: [
583
657
  { name: 'Salesforce - CRM integration', value: 'salesforce' },
@@ -596,8 +670,8 @@ To integrate Frigg into your production application:
596
670
  /**
597
671
  * Update index.js with selected integrations
598
672
  */
599
- async updateAppDefinition(integrations) {
600
- const appDefPath = path.join(this.targetPath, 'index.js');
673
+ async updateAppDefinition(integrations, targetDir = this.targetPath) {
674
+ const appDefPath = path.join(targetDir, 'index.js');
601
675
  if (await fs.pathExists(appDefPath)) {
602
676
  let content = await fs.readFile(appDefPath, 'utf8');
603
677
 
@@ -720,14 +794,18 @@ To integrate Frigg into your production application:
720
794
  console.log(chalk.cyan(` cd ${cdPath}\n`));
721
795
 
722
796
  if (deploymentMode === 'standalone') {
723
- console.log(`2. Start the development server:`);
797
+ console.log(`2. Install backend dependencies:`);
798
+ console.log(chalk.cyan(` cd backend && npm install\n`));
799
+
800
+ console.log(`3. Start the development server:`);
724
801
  console.log(chalk.cyan(` npm start\n`));
802
+ console.log(chalk.gray(` (or from backend: npm run frigg:start)\n`));
725
803
 
726
- console.log(`3. Open the Frigg UI for development:`);
804
+ console.log(`4. Open the Frigg UI for development:`);
727
805
  console.log(chalk.cyan(` frigg ui\n`));
728
806
 
729
807
  if (config.serverlessProvider === 'aws') {
730
- console.log(`4. Deploy to AWS Lambda:`);
808
+ console.log(`5. Deploy to AWS Lambda:`);
731
809
  console.log(chalk.cyan(` npm run deploy\n`));
732
810
  }
733
811
  } else {
@@ -747,8 +825,12 @@ To integrate Frigg into your production application:
747
825
  console.log(chalk.gray(' See frontend/README.md for integration guidance.'));
748
826
  }
749
827
 
828
+ console.log(chalk.gray('\n📁 Project Structure:'));
829
+ console.log(chalk.gray(' backend/ - Frigg app (run npm install here)'));
830
+ console.log(chalk.gray(' ui-extensions/ - Platform-specific UI extensions'));
831
+
750
832
  console.log(chalk.green('\n🎉 Happy integrating with Frigg!\n'));
751
- console.log(chalk.gray('Documentation: https://docs.frigg.dev'));
833
+ console.log(chalk.gray('Documentation: https://docs.friggframework.org'));
752
834
  console.log(chalk.gray('Support: https://github.com/friggframework/frigg/issues'));
753
835
  }
754
836
  }
@@ -68,7 +68,8 @@ async function initCommand(projectName, options) {
68
68
  force,
69
69
  verbose,
70
70
  mode: options.mode,
71
- frontend: options.frontend
71
+ frontend: options.frontend,
72
+ yes: options.yes
72
73
  });
73
74
 
74
75
  await handler.initialize();
@@ -75,6 +75,16 @@ class TemplateHandler {
75
75
  return !['node_modules', '.serverless', 'dist', 'build'].includes(basename);
76
76
  }
77
77
  });
78
+
79
+ // Rename .env.default to .env if it exists and .env doesn't already exist
80
+ const envDefaultPath = path.join(target, '.env.default');
81
+ const envPath = path.join(target, '.env');
82
+ if (fs.existsSync(envDefaultPath) && !fs.existsSync(envPath)) {
83
+ await fs.rename(envDefaultPath, envPath);
84
+ } else if (fs.existsSync(envDefaultPath)) {
85
+ // Remove the .env.default if .env already exists
86
+ await fs.remove(envDefaultPath);
87
+ }
78
88
  }
79
89
 
80
90
  /**
@@ -100,14 +110,14 @@ class TemplateHandler {
100
110
  async updateServerlessConfig() {
101
111
  const serverlessPath = path.join(this.targetPath, 'serverless.yml');
102
112
  let serverlessContent = await fs.readFile(serverlessPath, 'utf8');
103
-
113
+
104
114
  // Update service name based on directory name
105
115
  const projectName = path.basename(this.targetPath);
106
116
  serverlessContent = serverlessContent.replace(
107
- /^service: create-frigg-app$/m,
117
+ /^service: frigg-app$/m,
108
118
  `service: ${projectName}`
109
119
  );
110
-
120
+
111
121
  await fs.writeFile(serverlessPath, serverlessContent);
112
122
  }
113
123
 
@@ -1,12 +1,12 @@
1
1
  const fs = require('fs-extra');
2
2
  const path = require('path');
3
- const { logInfo } = require('./logger');
3
+ const output = require('../utils/output');
4
4
  const INTEGRATIONS_DIR = 'src/integrations';
5
5
  const BACKEND_JS = 'backend.js';
6
6
 
7
7
  function updateBackendJsFile(backendPath, apiModuleName) {
8
8
  const backendJsPath = path.join(path.dirname(backendPath), BACKEND_JS);
9
- logInfo(`Updating backend.js: ${backendJsPath}`);
9
+ output.debug(`Updating backend.js: ${backendJsPath}`);
10
10
  updateBackendJs(backendJsPath, apiModuleName);
11
11
  }
12
12
 
@@ -21,7 +21,7 @@ function updateBackendJs(backendJsPath, apiModuleName) {
21
21
  );
22
22
  fs.writeFileSync(backendJsPath, importStatement + updatedContent);
23
23
  } else {
24
- logInfo(
24
+ output.debug(
25
25
  `Import statement for ${apiModuleName}Integration already exists in backend.js`
26
26
  );
27
27
  }
@@ -1,9 +1,8 @@
1
1
  const fs = require('fs');
2
2
  const dotenv = require('dotenv');
3
3
  const { readFileSync, writeFileSync, existsSync } = require('fs');
4
- const { logInfo } = require('./logger');
4
+ const output = require('../utils/output');
5
5
  const { resolve } = require('node:path');
6
- const { confirm, input } = require('@inquirer/prompts');
7
6
  const { parse } = require('@babel/parser');
8
7
  const traverse = require('@babel/traverse').default;
9
8
 
@@ -43,15 +42,14 @@ const extractRawEnvVariables = (modulePath) => {
43
42
  return envVariables;
44
43
  };
45
44
  const handleEnvVariables = async (backendPath, modulePath) => {
46
- logInfo('Searching for missing environment variables...');
45
+ output.info('Searching for missing environment variables...');
47
46
  const Definition = { env: extractRawEnvVariables(modulePath) };
48
47
  if (Definition && Definition.env) {
49
- console.log('Here is Definition.env:', Definition.env);
48
+ output.debug('Definition.env:', JSON.stringify(Definition.env));
50
49
  const envVars = Object.values(Definition.env);
51
50
 
52
- console.log(
53
- 'Found the following environment variables in the API module:',
54
- envVars
51
+ output.info(
52
+ `Found environment variables in API module: ${envVars.join(', ')}`
55
53
  );
56
54
 
57
55
  const localEnvPath = resolve(backendPath, '../.env');
@@ -78,23 +76,19 @@ const handleEnvVariables = async (backendPath, modulePath) => {
78
76
  (envVar) => !localEnvVars[envVar] && !localDevConfig[envVar]
79
77
  );
80
78
 
81
- logInfo(`Missing environment variables: ${missingEnvVars.join(', ')}`);
82
-
83
79
  if (missingEnvVars.length > 0) {
84
- const addEnvVars = await confirm({
85
- message: `The following environment variables are required: ${missingEnvVars.join(
80
+ output.warn(`Missing environment variables: ${missingEnvVars.join(', ')}`);
81
+
82
+ const addEnvVars = await output.confirm(
83
+ `The following environment variables are required: ${missingEnvVars.join(
86
84
  ', '
87
- )}. Do you want to add them now?`,
88
- });
85
+ )}. Do you want to add them now?`
86
+ );
89
87
 
90
88
  if (addEnvVars) {
91
89
  const envValues = {};
92
90
  for (const envVar of missingEnvVars) {
93
- const value = await input({
94
- type: 'input',
95
- name: 'value',
96
- message: `Enter value for ${envVar}:`,
97
- });
91
+ const value = await output.input(`Enter value for ${envVar}:`);
98
92
  envValues[envVar] = value;
99
93
  }
100
94
 
@@ -117,9 +111,12 @@ const handleEnvVariables = async (backendPath, modulePath) => {
117
111
  JSON.stringify(updatedDevConfig, null, 2)
118
112
  );
119
113
  }
114
+ output.success('Environment variables added successfully');
120
115
  } else {
121
- logInfo("Edit whenever you're able, safe travels friend!");
116
+ output.info("Edit whenever you're able, safe travels friend!");
122
117
  }
118
+ } else {
119
+ output.success('All required environment variables are already configured');
123
120
  }
124
121
  }
125
122
  };
@@ -1,16 +1,14 @@
1
1
  const { handleEnvVariables } = require('./environment-variables');
2
- const { logInfo } = require('./logger');
3
- const inquirer = require('inquirer');
2
+ const output = require('../utils/output');
4
3
  const fs = require('fs');
5
4
  const dotenv = require('dotenv');
6
5
  const { resolve } = require('node:path');
7
6
  const { parse } = require('@babel/parser');
8
7
  const traverse = require('@babel/traverse');
9
8
 
10
- jest.mock('inquirer');
9
+ jest.mock('../utils/output');
11
10
  jest.mock('fs');
12
11
  jest.mock('dotenv');
13
- jest.mock('./logger');
14
12
  jest.mock('@babel/parser');
15
13
  jest.mock('@babel/traverse');
16
14
 
@@ -99,22 +97,23 @@ describe('handleEnvVariables', () => {
99
97
  return '';
100
98
  });
101
99
 
102
- inquirer.prompt
103
- .mockResolvedValueOnce({ addEnvVars: true })
104
- .mockResolvedValueOnce({ value: 'client_id_value' })
105
- .mockResolvedValueOnce({ value: 'client_secret_value' })
106
- .mockResolvedValueOnce({ value: 'redirect_uri_value' })
107
- .mockResolvedValueOnce({ value: 'scope_value' });
100
+ output.confirm.mockResolvedValueOnce(true);
101
+ output.input
102
+ .mockResolvedValueOnce('client_id_value')
103
+ .mockResolvedValueOnce('client_secret_value')
104
+ .mockResolvedValueOnce('redirect_uri_value')
105
+ .mockResolvedValueOnce('scope_value');
108
106
 
109
107
  await handleEnvVariables(backendPath, modulePath);
110
108
 
111
- expect(logInfo).toHaveBeenCalledWith(
109
+ expect(output.info).toHaveBeenCalledWith(
112
110
  'Searching for missing environment variables...'
113
111
  );
114
- expect(logInfo).toHaveBeenCalledWith(
112
+ expect(output.warn).toHaveBeenCalledWith(
115
113
  'Missing environment variables: GOOGLE_CALENDAR_CLIENT_ID, GOOGLE_CALENDAR_CLIENT_SECRET, REDIRECT_URI, GOOGLE_CALENDAR_SCOPE'
116
114
  );
117
- expect(inquirer.prompt).toHaveBeenCalledTimes(5);
115
+ expect(output.confirm).toHaveBeenCalledTimes(1);
116
+ expect(output.input).toHaveBeenCalledTimes(4);
118
117
  expect(fs.appendFileSync).toHaveBeenCalledWith(
119
118
  localEnvPath,
120
119
  '\nGOOGLE_CALENDAR_CLIENT_ID=client_id_value\nGOOGLE_CALENDAR_CLIENT_SECRET=client_secret_value\nREDIRECT_URI=redirect_uri_value\nGOOGLE_CALENDAR_SCOPE=scope_value'
@@ -2,7 +2,7 @@ const { installPackage } = require('./install-package');
2
2
  const { createIntegrationFile } = require('./integration-file');
3
3
  const { resolve } = require('node:path');
4
4
  const { updateBackendJsFile } = require('./backend-js');
5
- const { logInfo, logError } = require('./logger');
5
+ const output = require('../utils/output');
6
6
  const { commitChanges } = require('./commit-changes');
7
7
  const { handleEnvVariables } = require('./environment-variables');
8
8
  const {
@@ -35,18 +35,23 @@ const installCommand = async (apiModuleName) => {
35
35
  const sanitizedLabel = label.replace(
36
36
  /[<>:"/\\|?*\x00-\x1F\s]/g,
37
37
  ''
38
- ); // Remove invalid characters and spaces console.log('Installing integration for:', sanitizedLabel);
39
- createIntegrationFile(backendPath, sanitizedLabel, ApiClass);
40
- updateBackendJsFile(backendPath, sanitizedLabel);
41
- commitChanges(backendPath, sanitizedLabel);
42
- logInfo(
43
- `Successfully installed ${packageName} and updated the project.`
44
- );
38
+ ); // Remove invalid characters and spaces
39
+
40
+ const spinner = output.spinner(`Installing integration for ${sanitizedLabel}...`);
41
+ try {
42
+ createIntegrationFile(backendPath, sanitizedLabel, ApiClass);
43
+ updateBackendJsFile(backendPath, sanitizedLabel);
44
+ commitChanges(backendPath, sanitizedLabel);
45
+ spinner.succeed(`Successfully installed ${packageName} and updated the project.`);
46
+ } catch (innerError) {
47
+ spinner.fail(`Failed to install ${packageName}`);
48
+ throw innerError;
49
+ }
45
50
 
46
51
  await handleEnvVariables(backendPath, modulePath);
47
52
  }
48
53
  } catch (error) {
49
- logError('An error occurred:', error);
54
+ output.error('An error occurred:', error);
50
55
  process.exit(1);
51
56
  }
52
57
  };
@@ -1,6 +1,6 @@
1
1
  const fs = require('fs-extra');
2
2
  const path = require('path');
3
- const { logInfo } = require('./logger');
3
+ const output = require('../utils/output');
4
4
  const { getIntegrationTemplate } = require('./template');
5
5
  const INTEGRATIONS_DIR = 'src/integrations';
6
6
 
@@ -9,14 +9,14 @@ function createIntegrationFile(backendPath, apiModuleName, ApiClass) {
9
9
  path.dirname(backendPath),
10
10
  INTEGRATIONS_DIR
11
11
  );
12
- logInfo(`Ensuring directory exists: ${integrationDir}`);
12
+ output.debug(`Ensuring directory exists: ${integrationDir}`);
13
13
  fs.ensureDirSync(integrationDir);
14
14
 
15
15
  const integrationFilePath = path.join(
16
16
  integrationDir,
17
17
  `${apiModuleName}Integration.js`
18
18
  );
19
- logInfo(`Writing integration file: ${integrationFilePath}`);
19
+ output.debug(`Writing integration file: ${integrationFilePath}`);
20
20
  const integrationTemplate = getIntegrationTemplate(
21
21
  apiModuleName,
22
22
  backendPath,
@@ -1,7 +1,6 @@
1
1
  const { execSync } = require('child_process');
2
2
  const axios = require('axios');
3
- const { logError } = require('./logger');
4
- const { checkbox } = require('@inquirer/prompts');
3
+ const output = require('../utils/output');
5
4
 
6
5
  async function searchPackages(apiModuleName) {
7
6
  const searchCommand = `npm search @friggframework/api-module-${apiModuleName} --json`;
@@ -31,7 +30,7 @@ const searchAndSelectPackage = async (apiModuleName) => {
31
30
  const searchResults = await searchPackages(apiModuleName || '');
32
31
 
33
32
  if (searchResults.length === 0) {
34
- logError(`No packages found matching ${apiModuleName}`);
33
+ output.error(`No packages found matching ${apiModuleName}`);
35
34
  process.exit(1);
36
35
  }
37
36
 
@@ -44,7 +43,7 @@ const searchAndSelectPackage = async (apiModuleName) => {
44
43
  const earlierVersions = searchResults
45
44
  .map((pkg) => `${pkg.name} (${pkg.version})`)
46
45
  .join(', ');
47
- logError(
46
+ output.error(
48
47
  `No packages found with version 1.0.0 or above for ${apiModuleName}. Found earlier versions: ${earlierVersions}`
49
48
  );
50
49
  process.exit(1);
@@ -58,11 +57,8 @@ const searchAndSelectPackage = async (apiModuleName) => {
58
57
  };
59
58
  });
60
59
 
61
- const selectedPackages = await checkbox({
62
- message: 'Select the packages to install:',
63
- choices,
64
- });
65
- console.log('Selected packages:', selectedPackages);
60
+ const selectedPackages = await output.checkbox('Select the packages to install:', choices);
61
+ output.info(`Selected packages: ${selectedPackages.join(', ')}`);
66
62
 
67
63
  return selectedPackages.map((choice) => choice.split(' ')[0]);
68
64
  };
@@ -1,5 +1,8 @@
1
+ const path = require('path');
2
+
1
3
  module.exports = {
2
4
  displayName: 'Frigg CLI Tests',
5
+ rootDir: __dirname,
3
6
  testMatch: [
4
7
  '<rootDir>/__tests__/**/*.test.js',
5
8
  '<rootDir>/__tests__/**/*.spec.js',
@@ -93,7 +96,7 @@ module.exports = {
93
96
  }
94
97
  },
95
98
  setupFilesAfterEnv: [
96
- '<rootDir>/__tests__/utils/test-setup.js'
99
+ path.join(__dirname, '__tests__', 'utils', 'test-setup.js')
97
100
  ],
98
101
  testTimeout: 10000,
99
102
  maxWorkers: '50%',