@friggframework/devtools 2.0.0-next.29 → 2.0.0-next.30

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 (164) hide show
  1. package/frigg-cli/.eslintrc.js +141 -0
  2. package/frigg-cli/__tests__/jest.config.js +102 -0
  3. package/frigg-cli/__tests__/unit/commands/build.test.js +483 -0
  4. package/frigg-cli/__tests__/unit/commands/install.test.js +418 -0
  5. package/frigg-cli/__tests__/unit/commands/ui.test.js +592 -0
  6. package/frigg-cli/__tests__/utils/command-tester.js +170 -0
  7. package/frigg-cli/__tests__/utils/mock-factory.js +270 -0
  8. package/frigg-cli/__tests__/utils/test-fixtures.js +463 -0
  9. package/frigg-cli/__tests__/utils/test-setup.js +286 -0
  10. package/frigg-cli/generate-command/__tests__/generate-command.test.js +312 -0
  11. package/frigg-cli/generate-command/azure-generator.js +43 -0
  12. package/frigg-cli/generate-command/gcp-generator.js +47 -0
  13. package/frigg-cli/generate-command/index.js +332 -0
  14. package/frigg-cli/generate-command/terraform-generator.js +555 -0
  15. package/frigg-cli/index.js +19 -1
  16. package/frigg-cli/init-command/backend-first-handler.js +756 -0
  17. package/frigg-cli/init-command/index.js +93 -0
  18. package/frigg-cli/init-command/template-handler.js +143 -0
  19. package/frigg-cli/package.json +51 -0
  20. package/frigg-cli/test/init-command.test.js +180 -0
  21. package/frigg-cli/test/npm-registry.test.js +319 -0
  22. package/frigg-cli/ui-command/index.js +154 -0
  23. package/frigg-cli/utils/app-resolver.js +319 -0
  24. package/frigg-cli/utils/backend-path.js +25 -0
  25. package/frigg-cli/utils/npm-registry.js +167 -0
  26. package/frigg-cli/utils/process-manager.js +199 -0
  27. package/frigg-cli/utils/repo-detection.js +405 -0
  28. package/infrastructure/serverless-template.js +177 -292
  29. package/management-ui/.eslintrc.js +22 -0
  30. package/management-ui/README.md +203 -0
  31. package/management-ui/components.json +21 -0
  32. package/management-ui/docs/phase2-integration-guide.md +320 -0
  33. package/management-ui/{dist/index.html → index.html} +1 -2
  34. package/management-ui/package-lock.json +16517 -0
  35. package/management-ui/package.json +76 -0
  36. package/management-ui/packages/devtools/frigg-cli/ui-command/index.js +302 -0
  37. package/management-ui/postcss.config.js +6 -0
  38. package/management-ui/server/api/backend.js +256 -0
  39. package/management-ui/server/api/cli.js +315 -0
  40. package/management-ui/server/api/codegen.js +663 -0
  41. package/management-ui/server/api/connections.js +857 -0
  42. package/management-ui/server/api/discovery.js +185 -0
  43. package/management-ui/server/api/environment/index.js +1 -0
  44. package/management-ui/server/api/environment/router.js +378 -0
  45. package/management-ui/server/api/environment.js +328 -0
  46. package/management-ui/server/api/integrations.js +876 -0
  47. package/management-ui/server/api/logs.js +248 -0
  48. package/management-ui/server/api/monitoring.js +282 -0
  49. package/management-ui/server/api/open-ide.js +31 -0
  50. package/management-ui/server/api/project.js +1029 -0
  51. package/management-ui/server/api/users/sessions.js +371 -0
  52. package/management-ui/server/api/users/simulation.js +254 -0
  53. package/management-ui/server/api/users.js +362 -0
  54. package/management-ui/server/api-contract.md +275 -0
  55. package/management-ui/server/index.js +873 -0
  56. package/management-ui/server/middleware/errorHandler.js +93 -0
  57. package/management-ui/server/middleware/security.js +32 -0
  58. package/management-ui/server/processManager.js +296 -0
  59. package/management-ui/server/server.js +346 -0
  60. package/management-ui/server/services/aws-monitor.js +413 -0
  61. package/management-ui/server/services/npm-registry.js +347 -0
  62. package/management-ui/server/services/template-engine.js +538 -0
  63. package/management-ui/server/utils/cliIntegration.js +220 -0
  64. package/management-ui/server/utils/environment/auditLogger.js +471 -0
  65. package/management-ui/server/utils/environment/awsParameterStore.js +264 -0
  66. package/management-ui/server/utils/environment/encryption.js +278 -0
  67. package/management-ui/server/utils/environment/envFileManager.js +286 -0
  68. package/management-ui/server/utils/import-commonjs.js +28 -0
  69. package/management-ui/server/utils/response.js +83 -0
  70. package/management-ui/server/websocket/handler.js +325 -0
  71. package/management-ui/src/App.jsx +109 -0
  72. package/management-ui/src/components/AppRouter.jsx +65 -0
  73. package/management-ui/src/components/Button.jsx +70 -0
  74. package/management-ui/src/components/Card.jsx +97 -0
  75. package/management-ui/src/components/EnvironmentCompare.jsx +400 -0
  76. package/management-ui/src/components/EnvironmentEditor.jsx +372 -0
  77. package/management-ui/src/components/EnvironmentImportExport.jsx +469 -0
  78. package/management-ui/src/components/EnvironmentSchema.jsx +491 -0
  79. package/management-ui/src/components/EnvironmentSecurity.jsx +463 -0
  80. package/management-ui/src/components/ErrorBoundary.jsx +73 -0
  81. package/management-ui/src/components/IntegrationCard.jsx +481 -0
  82. package/management-ui/src/components/IntegrationCardEnhanced.jsx +770 -0
  83. package/management-ui/src/components/IntegrationExplorer.jsx +379 -0
  84. package/management-ui/src/components/IntegrationStatus.jsx +336 -0
  85. package/management-ui/src/components/Layout.jsx +716 -0
  86. package/management-ui/src/components/LoadingSpinner.jsx +113 -0
  87. package/management-ui/src/components/RepositoryPicker.jsx +248 -0
  88. package/management-ui/src/components/SessionMonitor.jsx +350 -0
  89. package/management-ui/src/components/StatusBadge.jsx +208 -0
  90. package/management-ui/src/components/UserContextSwitcher.jsx +212 -0
  91. package/management-ui/src/components/UserSimulation.jsx +327 -0
  92. package/management-ui/src/components/Welcome.jsx +434 -0
  93. package/management-ui/src/components/codegen/APIEndpointGenerator.jsx +637 -0
  94. package/management-ui/src/components/codegen/APIModuleSelector.jsx +227 -0
  95. package/management-ui/src/components/codegen/CodeGenerationWizard.jsx +247 -0
  96. package/management-ui/src/components/codegen/CodePreviewEditor.jsx +316 -0
  97. package/management-ui/src/components/codegen/DynamicModuleForm.jsx +271 -0
  98. package/management-ui/src/components/codegen/FormBuilder.jsx +737 -0
  99. package/management-ui/src/components/codegen/IntegrationGenerator.jsx +855 -0
  100. package/management-ui/src/components/codegen/ProjectScaffoldWizard.jsx +797 -0
  101. package/management-ui/src/components/codegen/SchemaBuilder.jsx +303 -0
  102. package/management-ui/src/components/codegen/TemplateSelector.jsx +586 -0
  103. package/management-ui/src/components/codegen/index.js +10 -0
  104. package/management-ui/src/components/connections/ConnectionConfigForm.jsx +362 -0
  105. package/management-ui/src/components/connections/ConnectionHealthMonitor.jsx +182 -0
  106. package/management-ui/src/components/connections/ConnectionTester.jsx +200 -0
  107. package/management-ui/src/components/connections/EntityRelationshipMapper.jsx +292 -0
  108. package/management-ui/src/components/connections/OAuthFlow.jsx +204 -0
  109. package/management-ui/src/components/connections/index.js +5 -0
  110. package/management-ui/src/components/index.js +21 -0
  111. package/management-ui/src/components/monitoring/APIGatewayMetrics.jsx +222 -0
  112. package/management-ui/src/components/monitoring/LambdaMetrics.jsx +169 -0
  113. package/management-ui/src/components/monitoring/MetricsChart.jsx +197 -0
  114. package/management-ui/src/components/monitoring/MonitoringDashboard.jsx +393 -0
  115. package/management-ui/src/components/monitoring/SQSMetrics.jsx +246 -0
  116. package/management-ui/src/components/monitoring/index.js +6 -0
  117. package/management-ui/src/components/monitoring/monitoring.css +218 -0
  118. package/management-ui/src/components/theme-provider.jsx +52 -0
  119. package/management-ui/src/components/theme-toggle.jsx +39 -0
  120. package/management-ui/src/components/ui/badge.tsx +36 -0
  121. package/management-ui/src/components/ui/button.test.jsx +56 -0
  122. package/management-ui/src/components/ui/button.tsx +57 -0
  123. package/management-ui/src/components/ui/card.tsx +76 -0
  124. package/management-ui/src/components/ui/dropdown-menu.tsx +199 -0
  125. package/management-ui/src/components/ui/select.tsx +157 -0
  126. package/management-ui/src/components/ui/skeleton.jsx +15 -0
  127. package/management-ui/src/hooks/useFrigg.jsx +601 -0
  128. package/management-ui/src/hooks/useSocket.jsx +58 -0
  129. package/management-ui/src/index.css +193 -0
  130. package/management-ui/src/lib/utils.ts +6 -0
  131. package/management-ui/src/main.jsx +10 -0
  132. package/management-ui/src/pages/CodeGeneration.jsx +14 -0
  133. package/management-ui/src/pages/Connections.jsx +252 -0
  134. package/management-ui/src/pages/ConnectionsEnhanced.jsx +633 -0
  135. package/management-ui/src/pages/Dashboard.jsx +311 -0
  136. package/management-ui/src/pages/Environment.jsx +314 -0
  137. package/management-ui/src/pages/IntegrationConfigure.jsx +669 -0
  138. package/management-ui/src/pages/IntegrationDiscovery.jsx +567 -0
  139. package/management-ui/src/pages/IntegrationTest.jsx +742 -0
  140. package/management-ui/src/pages/Integrations.jsx +253 -0
  141. package/management-ui/src/pages/Monitoring.jsx +17 -0
  142. package/management-ui/src/pages/Simulation.jsx +155 -0
  143. package/management-ui/src/pages/Users.jsx +492 -0
  144. package/management-ui/src/services/api.js +41 -0
  145. package/management-ui/src/services/apiModuleService.js +193 -0
  146. package/management-ui/src/services/websocket-handlers.js +120 -0
  147. package/management-ui/src/test/api/project.test.js +273 -0
  148. package/management-ui/src/test/components/Welcome.test.jsx +378 -0
  149. package/management-ui/src/test/mocks/server.js +178 -0
  150. package/management-ui/src/test/setup.js +61 -0
  151. package/management-ui/src/test/utils/test-utils.jsx +134 -0
  152. package/management-ui/src/utils/repository.js +98 -0
  153. package/management-ui/src/utils/repository.test.js +118 -0
  154. package/management-ui/src/workflows/phase2-integration-workflows.js +884 -0
  155. package/management-ui/tailwind.config.js +63 -0
  156. package/management-ui/tsconfig.json +37 -0
  157. package/management-ui/tsconfig.node.json +10 -0
  158. package/management-ui/vite.config.js +26 -0
  159. package/management-ui/vitest.config.js +38 -0
  160. package/package.json +5 -5
  161. package/management-ui/dist/assets/index-BA21WgFa.js +0 -1221
  162. package/management-ui/dist/assets/index-CbM64Oba.js +0 -1221
  163. package/management-ui/dist/assets/index-CkvseXTC.css +0 -1
  164. /package/management-ui/{dist/assets/FriggLogo-B7Xx8ZW1.svg → src/assets/FriggLogo.svg} +0 -0
@@ -0,0 +1,93 @@
1
+ /**
2
+ * Copyright (c) 2024 Frigg Integration Framework
3
+ *
4
+ * This source code is licensed under the MIT license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ */
7
+
8
+ 'use strict';
9
+
10
+ const path = require('path');
11
+ const chalk = require('chalk');
12
+ const validateProjectName = require('validate-npm-package-name');
13
+ const semver = require('semver');
14
+ const BackendFirstHandler = require('./backend-first-handler');
15
+
16
+ function checkAppName(appName) {
17
+ const validationResult = validateProjectName(appName);
18
+ if (!validationResult.validForNewPackages) {
19
+ console.error(
20
+ chalk.red(
21
+ `Cannot create a project named ${chalk.green(
22
+ `"${appName}"`
23
+ )} because of npm naming restrictions:\n`
24
+ )
25
+ );
26
+ [
27
+ ...(validationResult.errors || []),
28
+ ...(validationResult.warnings || []),
29
+ ].forEach(error => {
30
+ console.error(chalk.red(` * ${error}`));
31
+ });
32
+ console.error(chalk.red('\nPlease choose a different project name.'));
33
+ process.exit(1);
34
+ }
35
+ }
36
+
37
+ function checkNodeVersion() {
38
+ const unsupportedNodeVersion = !semver.satisfies(
39
+ semver.coerce(process.version),
40
+ '>=14'
41
+ );
42
+
43
+ if (unsupportedNodeVersion) {
44
+ console.log(
45
+ chalk.yellow(
46
+ `You are using Node ${process.version} so the project will be bootstrapped with an old unsupported version of tools.\n\n` +
47
+ `Please update to Node 14 or higher for a better, fully supported experience.\n`
48
+ )
49
+ );
50
+ }
51
+ }
52
+
53
+ async function initCommand(projectName, options) {
54
+ const verbose = options.verbose || false;
55
+ const force = options.force || false;
56
+
57
+ checkNodeVersion();
58
+
59
+ const root = path.resolve(projectName);
60
+ const appName = path.basename(root);
61
+
62
+ checkAppName(appName);
63
+
64
+ // Use backend-first handler by default
65
+ if (!options.template && !options.legacyFrontend) {
66
+ try {
67
+ const handler = new BackendFirstHandler(root, {
68
+ force,
69
+ verbose,
70
+ mode: options.mode,
71
+ frontend: options.frontend
72
+ });
73
+
74
+ await handler.initialize();
75
+ return;
76
+ } catch (error) {
77
+ console.log();
78
+ console.log(chalk.red('Aborting installation.'));
79
+ console.log(chalk.red('Error:'), error.message);
80
+ console.log();
81
+ process.exit(1);
82
+ }
83
+ }
84
+
85
+ // If we get here, show an error for legacy options
86
+ console.log();
87
+ console.log(chalk.red('Legacy template system is no longer supported.'));
88
+ console.log(chalk.yellow('Please use the new backend-first approach.'));
89
+ console.log();
90
+ process.exit(1);
91
+ }
92
+
93
+ module.exports = { initCommand };
@@ -0,0 +1,143 @@
1
+ const fs = require('fs-extra');
2
+ const path = require('path');
3
+ const { execSync } = require('child_process');
4
+
5
+ /**
6
+ * Handles template initialization for the frigg init command
7
+ */
8
+ class TemplateHandler {
9
+ constructor(targetPath, options = {}) {
10
+ this.targetPath = targetPath;
11
+ this.options = options;
12
+ this.templatesDir = path.join(__dirname, '..', 'templates');
13
+ }
14
+
15
+ /**
16
+ * Initialize a new Frigg backend project from template
17
+ */
18
+ async initializeBackendTemplate() {
19
+ const templatePath = path.join(this.templatesDir, 'backend');
20
+
21
+ if (!fs.existsSync(templatePath)) {
22
+ throw new Error('Backend template not found. Please ensure the CLI is properly installed.');
23
+ }
24
+
25
+ // Create target directory if it doesn't exist
26
+ await fs.ensureDir(this.targetPath);
27
+
28
+ // Check if directory is empty
29
+ const files = await fs.readdir(this.targetPath);
30
+ if (files.length > 0 && !this.options.force) {
31
+ throw new Error('Target directory is not empty. Use --force to override.');
32
+ }
33
+
34
+ console.log('🚀 Initializing new Frigg backend project...');
35
+
36
+ // Copy template files
37
+ await this.copyTemplateFiles(templatePath, this.targetPath);
38
+
39
+ // Update package.json with project name
40
+ await this.updatePackageJson();
41
+
42
+ // Update serverless.yml with project name
43
+ await this.updateServerlessConfig();
44
+
45
+ // Initialize git if requested
46
+ if (this.options.git !== false) {
47
+ await this.initializeGit();
48
+ }
49
+
50
+ // Install dependencies if requested
51
+ if (this.options.install !== false) {
52
+ await this.installDependencies();
53
+ }
54
+
55
+ console.log('✅ Frigg backend project initialized successfully!');
56
+ console.log('\nNext steps:');
57
+ console.log(` cd ${path.basename(this.targetPath)}`);
58
+ if (this.options.install === false) {
59
+ console.log(' npm install');
60
+ }
61
+ console.log(' npm run docker:start');
62
+ console.log(' npm run backend-start');
63
+ console.log('\nFor more information, check the README.md file.');
64
+ }
65
+
66
+ /**
67
+ * Copy template files to target directory
68
+ */
69
+ async copyTemplateFiles(source, target) {
70
+ await fs.copy(source, target, {
71
+ overwrite: this.options.force || false,
72
+ filter: (src) => {
73
+ // Skip node_modules and other build artifacts
74
+ const basename = path.basename(src);
75
+ return !['node_modules', '.serverless', 'dist', 'build'].includes(basename);
76
+ }
77
+ });
78
+ }
79
+
80
+ /**
81
+ * Update package.json with project-specific details
82
+ */
83
+ async updatePackageJson() {
84
+ const packageJsonPath = path.join(this.targetPath, 'package.json');
85
+ const packageJson = await fs.readJson(packageJsonPath);
86
+
87
+ // Update package name based on directory name
88
+ const projectName = path.basename(this.targetPath);
89
+ packageJson.name = projectName;
90
+
91
+ // Remove private flag for new projects
92
+ delete packageJson.private;
93
+
94
+ await fs.writeJson(packageJsonPath, packageJson, { spaces: 2 });
95
+ }
96
+
97
+ /**
98
+ * Update serverless.yml with project-specific details
99
+ */
100
+ async updateServerlessConfig() {
101
+ const serverlessPath = path.join(this.targetPath, 'serverless.yml');
102
+ let serverlessContent = await fs.readFile(serverlessPath, 'utf8');
103
+
104
+ // Update service name based on directory name
105
+ const projectName = path.basename(this.targetPath);
106
+ serverlessContent = serverlessContent.replace(
107
+ /^service: create-frigg-app$/m,
108
+ `service: ${projectName}`
109
+ );
110
+
111
+ await fs.writeFile(serverlessPath, serverlessContent);
112
+ }
113
+
114
+ /**
115
+ * Initialize git repository
116
+ */
117
+ async initializeGit() {
118
+ try {
119
+ console.log('📦 Initializing git repository...');
120
+ execSync('git init', { cwd: this.targetPath, stdio: 'ignore' });
121
+ execSync('git add .', { cwd: this.targetPath, stdio: 'ignore' });
122
+ execSync('git commit -m "Initial commit from Frigg CLI"', {
123
+ cwd: this.targetPath,
124
+ stdio: 'ignore'
125
+ });
126
+ } catch (error) {
127
+ console.warn('⚠️ Git initialization failed. You can initialize git manually later.');
128
+ }
129
+ }
130
+
131
+ /**
132
+ * Install npm dependencies
133
+ */
134
+ async installDependencies() {
135
+ console.log('📦 Installing dependencies...');
136
+ execSync('npm install', {
137
+ cwd: this.targetPath,
138
+ stdio: 'inherit'
139
+ });
140
+ }
141
+ }
142
+
143
+ module.exports = TemplateHandler;
@@ -0,0 +1,51 @@
1
+ {
2
+ "name": "@friggframework/frigg-cli",
3
+ "version": "2.0.0-next.0",
4
+ "description": "Frigg Framework CLI tool",
5
+ "main": "index.js",
6
+ "bin": {
7
+ "frigg": "./index.js"
8
+ },
9
+ "scripts": {
10
+ "test": "jest"
11
+ },
12
+ "dependencies": {
13
+ "@babel/parser": "^7.25.3",
14
+ "@babel/traverse": "^7.25.3",
15
+ "@friggframework/schemas": "workspace:*",
16
+ "@inquirer/prompts": "^5.3.8",
17
+ "axios": "^1.7.2",
18
+ "chalk": "^4.1.2",
19
+ "commander": "^12.1.0",
20
+ "cross-spawn": "^7.0.3",
21
+ "fs-extra": "^11.2.0",
22
+ "js-yaml": "^4.1.0",
23
+ "lodash": "4.17.21",
24
+ "node-cache": "^5.1.2",
25
+ "open": "^8.4.2",
26
+ "semver": "^7.6.0",
27
+ "validate-npm-package-name": "^5.0.0"
28
+ },
29
+ "devDependencies": {
30
+ "jest": "^29.7.0"
31
+ },
32
+ "keywords": [
33
+ "frigg",
34
+ "cli",
35
+ "integration",
36
+ "framework"
37
+ ],
38
+ "author": "Frigg Framework Team",
39
+ "license": "MIT",
40
+ "repository": {
41
+ "type": "git",
42
+ "url": "git+https://github.com/friggframework/frigg.git"
43
+ },
44
+ "bugs": {
45
+ "url": "https://github.com/friggframework/frigg/issues"
46
+ },
47
+ "homepage": "https://github.com/friggframework/frigg#readme",
48
+ "engines": {
49
+ "node": ">=18"
50
+ }
51
+ }
@@ -0,0 +1,180 @@
1
+ const path = require('path');
2
+ const fs = require('fs-extra');
3
+ const { initCommand } = require('../init-command');
4
+ const BackendFirstHandler = require('../init-command/backend-first-handler');
5
+
6
+ // Mock dependencies
7
+ jest.mock('fs-extra');
8
+ jest.mock('chalk', () => ({
9
+ blue: jest.fn(text => text),
10
+ green: jest.fn(text => text),
11
+ red: jest.fn(text => text),
12
+ yellow: jest.fn(text => text),
13
+ gray: jest.fn(text => text),
14
+ cyan: jest.fn(text => text),
15
+ bold: jest.fn(text => text)
16
+ }));
17
+ jest.mock('@inquirer/prompts');
18
+ jest.mock('child_process');
19
+ jest.mock('cross-spawn');
20
+ jest.mock('../init-command/backend-first-handler');
21
+ jest.mock('validate-npm-package-name');
22
+
23
+ describe('Init Command', () => {
24
+ const mockProjectPath = '/test/project/path';
25
+ const mockProjectName = 'test-project';
26
+ let mockExit;
27
+ let mockConsoleLog;
28
+ const validateProjectName = require('validate-npm-package-name');
29
+
30
+ beforeEach(() => {
31
+ jest.clearAllMocks();
32
+
33
+ // Mock process.exit
34
+ mockExit = jest.spyOn(process, 'exit').mockImplementation(() => {
35
+ throw new Error('process.exit');
36
+ });
37
+
38
+ // Mock console.log and console.error
39
+ mockConsoleLog = jest.spyOn(console, 'log').mockImplementation();
40
+ jest.spyOn(console, 'error').mockImplementation();
41
+
42
+ // Reset fs mocks
43
+ const fs = require('fs-extra');
44
+ fs.ensureDirSync.mockImplementation(() => {});
45
+ fs.pathExists.mockResolvedValue(false);
46
+ fs.readdir.mockResolvedValue([]);
47
+ fs.readdirSync.mockReturnValue([]);
48
+ fs.writeFileSync.mockImplementation(() => {});
49
+ fs.lstatSync.mockReturnValue({ isDirectory: () => false });
50
+
51
+ // Mock BackendFirstHandler
52
+ BackendFirstHandler.mockImplementation(() => ({
53
+ initialize: jest.fn().mockResolvedValue()
54
+ }));
55
+
56
+ // Mock validate-npm-package-name
57
+ validateProjectName.mockReturnValue({
58
+ validForNewPackages: true,
59
+ errors: [],
60
+ warnings: []
61
+ });
62
+ });
63
+
64
+ afterEach(() => {
65
+ mockExit.mockRestore();
66
+ mockConsoleLog.mockRestore();
67
+ });
68
+
69
+ describe('Validation', () => {
70
+ it('should validate project name', async () => {
71
+ validateProjectName.mockReturnValue({
72
+ validForNewPackages: false,
73
+ errors: ['Invalid name'],
74
+ warnings: []
75
+ });
76
+
77
+ await expect(initCommand('invalid-name', {})).rejects.toThrow('process.exit');
78
+ expect(mockExit).toHaveBeenCalledWith(1);
79
+ expect(console.error).toHaveBeenCalledWith(expect.stringContaining('npm naming restrictions'));
80
+ });
81
+
82
+ it('should accept valid project names', async () => {
83
+ validateProjectName.mockReturnValue({
84
+ validForNewPackages: true,
85
+ errors: [],
86
+ warnings: []
87
+ });
88
+
89
+ await initCommand('valid-name', {});
90
+ expect(BackendFirstHandler).toHaveBeenCalled();
91
+ });
92
+
93
+ it('should check Node.js version compatibility', async () => {
94
+ const originalVersion = process.version;
95
+ Object.defineProperty(process, 'version', {
96
+ value: 'v12.0.0',
97
+ configurable: true
98
+ });
99
+
100
+ const consoleSpy = jest.spyOn(console, 'log').mockImplementation();
101
+ await initCommand(mockProjectName, {});
102
+
103
+ expect(consoleSpy).toHaveBeenCalledWith(expect.stringContaining('Node 14 or higher'));
104
+
105
+ Object.defineProperty(process, 'version', {
106
+ value: originalVersion,
107
+ configurable: true
108
+ });
109
+ consoleSpy.mockRestore();
110
+ });
111
+ });
112
+
113
+ describe('Project Creation Modes', () => {
114
+ it('should use BackendFirstHandler by default', async () => {
115
+ await initCommand(mockProjectName, {});
116
+
117
+ expect(BackendFirstHandler).toHaveBeenCalledWith(
118
+ expect.stringContaining(mockProjectName),
119
+ expect.objectContaining({
120
+ force: false,
121
+ verbose: false
122
+ })
123
+ );
124
+ });
125
+
126
+ it('should pass options to BackendFirstHandler', async () => {
127
+ const options = {
128
+ force: true,
129
+ verbose: true,
130
+ mode: 'standalone',
131
+ frontend: false
132
+ };
133
+
134
+ await initCommand(mockProjectName, options);
135
+
136
+ expect(BackendFirstHandler).toHaveBeenCalledWith(
137
+ expect.any(String),
138
+ expect.objectContaining(options)
139
+ );
140
+ });
141
+
142
+ it('should handle initialization errors gracefully', async () => {
143
+ const mockError = new Error('Initialization failed');
144
+ BackendFirstHandler.mockImplementation(() => ({
145
+ initialize: jest.fn().mockRejectedValue(mockError)
146
+ }));
147
+
148
+ await expect(initCommand(mockProjectName, {})).rejects.toThrow('process.exit');
149
+
150
+ expect(mockConsoleLog).toHaveBeenCalledWith(expect.stringContaining('Aborting'));
151
+ expect(mockExit).toHaveBeenCalledWith(1);
152
+ });
153
+ });
154
+
155
+ describe('Directory Safety', () => {
156
+ it('should check if directory is safe to use', async () => {
157
+ BackendFirstHandler.mockImplementation(() => ({
158
+ initialize: jest.fn().mockRejectedValue(new Error('Directory not empty'))
159
+ }));
160
+
161
+ await expect(initCommand(mockProjectName, {})).rejects.toThrow('process.exit');
162
+
163
+ expect(mockExit).toHaveBeenCalledWith(1);
164
+ });
165
+
166
+ it('should allow safe files in directory', async () => {
167
+ await initCommand(mockProjectName, {});
168
+ expect(BackendFirstHandler).toHaveBeenCalled();
169
+ });
170
+
171
+ it('should force overwrite when --force flag is used', async () => {
172
+ await initCommand(mockProjectName, { force: true });
173
+ expect(BackendFirstHandler).toHaveBeenCalledWith(
174
+ expect.any(String),
175
+ expect.objectContaining({ force: true })
176
+ );
177
+ });
178
+ });
179
+
180
+ });