@friggframework/devtools 2.0.0-next.3 → 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 (199) 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/build-command/index.js +54 -0
  11. package/frigg-cli/deploy-command/index.js +36 -0
  12. package/frigg-cli/generate-command/__tests__/generate-command.test.js +312 -0
  13. package/frigg-cli/generate-command/azure-generator.js +43 -0
  14. package/frigg-cli/generate-command/gcp-generator.js +47 -0
  15. package/frigg-cli/generate-command/index.js +332 -0
  16. package/frigg-cli/generate-command/terraform-generator.js +555 -0
  17. package/frigg-cli/generate-iam-command.js +115 -0
  18. package/frigg-cli/index.js +47 -1
  19. package/frigg-cli/index.test.js +1 -4
  20. package/frigg-cli/init-command/backend-first-handler.js +756 -0
  21. package/frigg-cli/init-command/index.js +93 -0
  22. package/frigg-cli/init-command/template-handler.js +143 -0
  23. package/frigg-cli/install-command/index.js +1 -4
  24. package/frigg-cli/package.json +51 -0
  25. package/frigg-cli/start-command/index.js +24 -4
  26. package/frigg-cli/test/init-command.test.js +180 -0
  27. package/frigg-cli/test/npm-registry.test.js +319 -0
  28. package/frigg-cli/ui-command/index.js +154 -0
  29. package/frigg-cli/utils/app-resolver.js +319 -0
  30. package/frigg-cli/utils/backend-path.js +16 -17
  31. package/frigg-cli/utils/npm-registry.js +167 -0
  32. package/frigg-cli/utils/process-manager.js +199 -0
  33. package/frigg-cli/utils/repo-detection.js +405 -0
  34. package/infrastructure/AWS-DISCOVERY-TROUBLESHOOTING.md +245 -0
  35. package/infrastructure/AWS-IAM-CREDENTIAL-NEEDS.md +596 -0
  36. package/infrastructure/DEPLOYMENT-INSTRUCTIONS.md +268 -0
  37. package/infrastructure/GENERATE-IAM-DOCS.md +253 -0
  38. package/infrastructure/IAM-POLICY-TEMPLATES.md +176 -0
  39. package/infrastructure/README-TESTING.md +332 -0
  40. package/infrastructure/README.md +421 -0
  41. package/infrastructure/WEBSOCKET-CONFIGURATION.md +105 -0
  42. package/infrastructure/__tests__/fixtures/mock-aws-resources.js +391 -0
  43. package/infrastructure/__tests__/helpers/test-utils.js +277 -0
  44. package/infrastructure/aws-discovery.js +568 -0
  45. package/infrastructure/aws-discovery.test.js +373 -0
  46. package/infrastructure/build-time-discovery.js +206 -0
  47. package/infrastructure/build-time-discovery.test.js +375 -0
  48. package/infrastructure/create-frigg-infrastructure.js +3 -5
  49. package/infrastructure/frigg-deployment-iam-stack.yaml +379 -0
  50. package/infrastructure/iam-generator.js +687 -0
  51. package/infrastructure/iam-generator.test.js +169 -0
  52. package/infrastructure/iam-policy-basic.json +212 -0
  53. package/infrastructure/iam-policy-full.json +282 -0
  54. package/infrastructure/integration.test.js +383 -0
  55. package/infrastructure/run-discovery.js +110 -0
  56. package/infrastructure/serverless-template.js +923 -113
  57. package/infrastructure/serverless-template.test.js +541 -0
  58. package/management-ui/.eslintrc.js +22 -0
  59. package/management-ui/README.md +203 -0
  60. package/management-ui/components.json +21 -0
  61. package/management-ui/docs/phase2-integration-guide.md +320 -0
  62. package/management-ui/index.html +13 -0
  63. package/management-ui/package-lock.json +16517 -0
  64. package/management-ui/package.json +76 -0
  65. package/management-ui/packages/devtools/frigg-cli/ui-command/index.js +302 -0
  66. package/management-ui/postcss.config.js +6 -0
  67. package/management-ui/server/api/backend.js +256 -0
  68. package/management-ui/server/api/cli.js +315 -0
  69. package/management-ui/server/api/codegen.js +663 -0
  70. package/management-ui/server/api/connections.js +857 -0
  71. package/management-ui/server/api/discovery.js +185 -0
  72. package/management-ui/server/api/environment/index.js +1 -0
  73. package/management-ui/server/api/environment/router.js +378 -0
  74. package/management-ui/server/api/environment.js +328 -0
  75. package/management-ui/server/api/integrations.js +876 -0
  76. package/management-ui/server/api/logs.js +248 -0
  77. package/management-ui/server/api/monitoring.js +282 -0
  78. package/management-ui/server/api/open-ide.js +31 -0
  79. package/management-ui/server/api/project.js +1029 -0
  80. package/management-ui/server/api/users/sessions.js +371 -0
  81. package/management-ui/server/api/users/simulation.js +254 -0
  82. package/management-ui/server/api/users.js +362 -0
  83. package/management-ui/server/api-contract.md +275 -0
  84. package/management-ui/server/index.js +873 -0
  85. package/management-ui/server/middleware/errorHandler.js +93 -0
  86. package/management-ui/server/middleware/security.js +32 -0
  87. package/management-ui/server/processManager.js +296 -0
  88. package/management-ui/server/server.js +346 -0
  89. package/management-ui/server/services/aws-monitor.js +413 -0
  90. package/management-ui/server/services/npm-registry.js +347 -0
  91. package/management-ui/server/services/template-engine.js +538 -0
  92. package/management-ui/server/utils/cliIntegration.js +220 -0
  93. package/management-ui/server/utils/environment/auditLogger.js +471 -0
  94. package/management-ui/server/utils/environment/awsParameterStore.js +264 -0
  95. package/management-ui/server/utils/environment/encryption.js +278 -0
  96. package/management-ui/server/utils/environment/envFileManager.js +286 -0
  97. package/management-ui/server/utils/import-commonjs.js +28 -0
  98. package/management-ui/server/utils/response.js +83 -0
  99. package/management-ui/server/websocket/handler.js +325 -0
  100. package/management-ui/src/App.jsx +109 -0
  101. package/management-ui/src/assets/FriggLogo.svg +1 -0
  102. package/management-ui/src/components/AppRouter.jsx +65 -0
  103. package/management-ui/src/components/Button.jsx +70 -0
  104. package/management-ui/src/components/Card.jsx +97 -0
  105. package/management-ui/src/components/EnvironmentCompare.jsx +400 -0
  106. package/management-ui/src/components/EnvironmentEditor.jsx +372 -0
  107. package/management-ui/src/components/EnvironmentImportExport.jsx +469 -0
  108. package/management-ui/src/components/EnvironmentSchema.jsx +491 -0
  109. package/management-ui/src/components/EnvironmentSecurity.jsx +463 -0
  110. package/management-ui/src/components/ErrorBoundary.jsx +73 -0
  111. package/management-ui/src/components/IntegrationCard.jsx +481 -0
  112. package/management-ui/src/components/IntegrationCardEnhanced.jsx +770 -0
  113. package/management-ui/src/components/IntegrationExplorer.jsx +379 -0
  114. package/management-ui/src/components/IntegrationStatus.jsx +336 -0
  115. package/management-ui/src/components/Layout.jsx +716 -0
  116. package/management-ui/src/components/LoadingSpinner.jsx +113 -0
  117. package/management-ui/src/components/RepositoryPicker.jsx +248 -0
  118. package/management-ui/src/components/SessionMonitor.jsx +350 -0
  119. package/management-ui/src/components/StatusBadge.jsx +208 -0
  120. package/management-ui/src/components/UserContextSwitcher.jsx +212 -0
  121. package/management-ui/src/components/UserSimulation.jsx +327 -0
  122. package/management-ui/src/components/Welcome.jsx +434 -0
  123. package/management-ui/src/components/codegen/APIEndpointGenerator.jsx +637 -0
  124. package/management-ui/src/components/codegen/APIModuleSelector.jsx +227 -0
  125. package/management-ui/src/components/codegen/CodeGenerationWizard.jsx +247 -0
  126. package/management-ui/src/components/codegen/CodePreviewEditor.jsx +316 -0
  127. package/management-ui/src/components/codegen/DynamicModuleForm.jsx +271 -0
  128. package/management-ui/src/components/codegen/FormBuilder.jsx +737 -0
  129. package/management-ui/src/components/codegen/IntegrationGenerator.jsx +855 -0
  130. package/management-ui/src/components/codegen/ProjectScaffoldWizard.jsx +797 -0
  131. package/management-ui/src/components/codegen/SchemaBuilder.jsx +303 -0
  132. package/management-ui/src/components/codegen/TemplateSelector.jsx +586 -0
  133. package/management-ui/src/components/codegen/index.js +10 -0
  134. package/management-ui/src/components/connections/ConnectionConfigForm.jsx +362 -0
  135. package/management-ui/src/components/connections/ConnectionHealthMonitor.jsx +182 -0
  136. package/management-ui/src/components/connections/ConnectionTester.jsx +200 -0
  137. package/management-ui/src/components/connections/EntityRelationshipMapper.jsx +292 -0
  138. package/management-ui/src/components/connections/OAuthFlow.jsx +204 -0
  139. package/management-ui/src/components/connections/index.js +5 -0
  140. package/management-ui/src/components/index.js +21 -0
  141. package/management-ui/src/components/monitoring/APIGatewayMetrics.jsx +222 -0
  142. package/management-ui/src/components/monitoring/LambdaMetrics.jsx +169 -0
  143. package/management-ui/src/components/monitoring/MetricsChart.jsx +197 -0
  144. package/management-ui/src/components/monitoring/MonitoringDashboard.jsx +393 -0
  145. package/management-ui/src/components/monitoring/SQSMetrics.jsx +246 -0
  146. package/management-ui/src/components/monitoring/index.js +6 -0
  147. package/management-ui/src/components/monitoring/monitoring.css +218 -0
  148. package/management-ui/src/components/theme-provider.jsx +52 -0
  149. package/management-ui/src/components/theme-toggle.jsx +39 -0
  150. package/management-ui/src/components/ui/badge.tsx +36 -0
  151. package/management-ui/src/components/ui/button.test.jsx +56 -0
  152. package/management-ui/src/components/ui/button.tsx +57 -0
  153. package/management-ui/src/components/ui/card.tsx +76 -0
  154. package/management-ui/src/components/ui/dropdown-menu.tsx +199 -0
  155. package/management-ui/src/components/ui/select.tsx +157 -0
  156. package/management-ui/src/components/ui/skeleton.jsx +15 -0
  157. package/management-ui/src/hooks/useFrigg.jsx +601 -0
  158. package/management-ui/src/hooks/useSocket.jsx +58 -0
  159. package/management-ui/src/index.css +193 -0
  160. package/management-ui/src/lib/utils.ts +6 -0
  161. package/management-ui/src/main.jsx +10 -0
  162. package/management-ui/src/pages/CodeGeneration.jsx +14 -0
  163. package/management-ui/src/pages/Connections.jsx +252 -0
  164. package/management-ui/src/pages/ConnectionsEnhanced.jsx +633 -0
  165. package/management-ui/src/pages/Dashboard.jsx +311 -0
  166. package/management-ui/src/pages/Environment.jsx +314 -0
  167. package/management-ui/src/pages/IntegrationConfigure.jsx +669 -0
  168. package/management-ui/src/pages/IntegrationDiscovery.jsx +567 -0
  169. package/management-ui/src/pages/IntegrationTest.jsx +742 -0
  170. package/management-ui/src/pages/Integrations.jsx +253 -0
  171. package/management-ui/src/pages/Monitoring.jsx +17 -0
  172. package/management-ui/src/pages/Simulation.jsx +155 -0
  173. package/management-ui/src/pages/Users.jsx +492 -0
  174. package/management-ui/src/services/api.js +41 -0
  175. package/management-ui/src/services/apiModuleService.js +193 -0
  176. package/management-ui/src/services/websocket-handlers.js +120 -0
  177. package/management-ui/src/test/api/project.test.js +273 -0
  178. package/management-ui/src/test/components/Welcome.test.jsx +378 -0
  179. package/management-ui/src/test/mocks/server.js +178 -0
  180. package/management-ui/src/test/setup.js +61 -0
  181. package/management-ui/src/test/utils/test-utils.jsx +134 -0
  182. package/management-ui/src/utils/repository.js +98 -0
  183. package/management-ui/src/utils/repository.test.js +118 -0
  184. package/management-ui/src/workflows/phase2-integration-workflows.js +884 -0
  185. package/management-ui/tailwind.config.js +63 -0
  186. package/management-ui/tsconfig.json +37 -0
  187. package/management-ui/tsconfig.node.json +10 -0
  188. package/management-ui/vite.config.js +26 -0
  189. package/management-ui/vitest.config.js +38 -0
  190. package/package.json +16 -9
  191. package/infrastructure/app-handler-helpers.js +0 -57
  192. package/infrastructure/backend-utils.js +0 -90
  193. package/infrastructure/routers/auth.js +0 -26
  194. package/infrastructure/routers/integration-defined-routers.js +0 -37
  195. package/infrastructure/routers/middleware/loadUser.js +0 -15
  196. package/infrastructure/routers/middleware/requireLoggedInUser.js +0 -12
  197. package/infrastructure/routers/user.js +0 -41
  198. package/infrastructure/routers/websocket.js +0 -55
  199. package/infrastructure/workers/integration-defined-workers.js +0 -24
@@ -0,0 +1,884 @@
1
+ /**
2
+ * Phase 2 Integration Workflows
3
+ *
4
+ * This module implements complete user workflows that integrate all Phase 2 features:
5
+ * - Integration discovery → Installation → Configuration → Connection
6
+ * - User creation → Credential generation → Entity management
7
+ * - Environment setup → Testing → Deployment preparation
8
+ */
9
+
10
+ import IntegrationDiscoveryService from '../../../core/integrations/discovery/integration-discovery-service.js';
11
+ import IntegrationInstallerService from '../../../core/integrations/discovery/integration-installer-service.js';
12
+ import { EventEmitter } from 'events';
13
+
14
+ export class Phase2IntegrationWorkflows extends EventEmitter {
15
+ constructor(config = {}) {
16
+ super();
17
+ this.config = config;
18
+ this.discoveryService = new IntegrationDiscoveryService();
19
+ this.installerService = new IntegrationInstallerService();
20
+ this.workflows = new Map();
21
+ }
22
+
23
+ /**
24
+ * Complete integration setup workflow
25
+ * Discovers → Installs → Configures → Tests an integration
26
+ */
27
+ async setupIntegration(integrationName, options = {}) {
28
+ const workflowId = this.generateWorkflowId();
29
+ const workflow = {
30
+ id: workflowId,
31
+ type: 'integration-setup',
32
+ integration: integrationName,
33
+ status: 'started',
34
+ steps: [],
35
+ startTime: Date.now()
36
+ };
37
+
38
+ this.workflows.set(workflowId, workflow);
39
+ this.emit('workflow:started', workflow);
40
+
41
+ try {
42
+ // Step 1: Discover integration details
43
+ await this.executeStep(workflow, 'discover', async () => {
44
+ const details = await this.discoveryService.getIntegrationDetails(
45
+ `@friggframework/api-module-${integrationName}`
46
+ );
47
+ workflow.integrationDetails = details;
48
+ return details;
49
+ });
50
+
51
+ // Step 2: Install integration
52
+ await this.executeStep(workflow, 'install', async () => {
53
+ const result = await this.installerService.installIntegration({
54
+ packageName: workflow.integrationDetails.name,
55
+ version: workflow.integrationDetails.version
56
+ });
57
+ workflow.installResult = result;
58
+ return result;
59
+ });
60
+
61
+ // Step 3: Configure integration
62
+ await this.executeStep(workflow, 'configure', async () => {
63
+ const config = await this.configureIntegration(
64
+ integrationName,
65
+ workflow.integrationDetails,
66
+ options.config || {}
67
+ );
68
+ workflow.configuration = config;
69
+ return config;
70
+ });
71
+
72
+ // Step 4: Create test user
73
+ await this.executeStep(workflow, 'create-test-user', async () => {
74
+ const user = await this.createTestUser({
75
+ integration: integrationName,
76
+ name: options.testUserName || `Test User for ${integrationName}`,
77
+ email: options.testUserEmail || `test-${integrationName}@frigg.test`
78
+ });
79
+ workflow.testUser = user;
80
+ return user;
81
+ });
82
+
83
+ // Step 5: Generate credentials
84
+ await this.executeStep(workflow, 'generate-credentials', async () => {
85
+ const credentials = await this.generateTestCredentials(
86
+ workflow.testUser.id,
87
+ integrationName,
88
+ workflow.integrationDetails.integrationMetadata
89
+ );
90
+ workflow.credentials = credentials;
91
+ return credentials;
92
+ });
93
+
94
+ // Step 6: Create connection
95
+ await this.executeStep(workflow, 'create-connection', async () => {
96
+ const connection = await this.createConnection({
97
+ userId: workflow.testUser.id,
98
+ integrationName,
99
+ credentials: workflow.credentials,
100
+ config: workflow.configuration
101
+ });
102
+ workflow.connection = connection;
103
+ return connection;
104
+ });
105
+
106
+ // Step 7: Test connection
107
+ await this.executeStep(workflow, 'test-connection', async () => {
108
+ const testResult = await this.testConnection(workflow.connection.id);
109
+ workflow.testResult = testResult;
110
+ return testResult;
111
+ });
112
+
113
+ // Step 8: Setup environment variables
114
+ await this.executeStep(workflow, 'setup-environment', async () => {
115
+ const envVars = await this.setupEnvironmentVariables(
116
+ integrationName,
117
+ workflow.configuration,
118
+ options.environment || 'development'
119
+ );
120
+ workflow.environmentVariables = envVars;
121
+ return envVars;
122
+ });
123
+
124
+ workflow.status = 'completed';
125
+ workflow.endTime = Date.now();
126
+ workflow.duration = workflow.endTime - workflow.startTime;
127
+
128
+ this.emit('workflow:completed', workflow);
129
+ return workflow;
130
+
131
+ } catch (error) {
132
+ workflow.status = 'failed';
133
+ workflow.error = {
134
+ message: error.message,
135
+ step: workflow.steps[workflow.steps.length - 1]?.name || 'unknown',
136
+ stack: error.stack
137
+ };
138
+ workflow.endTime = Date.now();
139
+ workflow.duration = workflow.endTime - workflow.startTime;
140
+
141
+ this.emit('workflow:failed', workflow);
142
+ throw error;
143
+ }
144
+ }
145
+
146
+ /**
147
+ * Bulk integration management workflow
148
+ * Handles multiple integrations in parallel with optimized performance
149
+ */
150
+ async bulkIntegrationSetup(integrations, options = {}) {
151
+ const workflowId = this.generateWorkflowId();
152
+ const workflow = {
153
+ id: workflowId,
154
+ type: 'bulk-integration-setup',
155
+ integrations,
156
+ status: 'started',
157
+ results: [],
158
+ startTime: Date.now()
159
+ };
160
+
161
+ this.workflows.set(workflowId, workflow);
162
+ this.emit('workflow:started', workflow);
163
+
164
+ try {
165
+ // Process integrations in batches for optimal performance
166
+ const batchSize = options.batchSize || 3;
167
+ const batches = this.createBatches(integrations, batchSize);
168
+
169
+ for (const [batchIndex, batch] of batches.entries()) {
170
+ this.emit('workflow:progress', {
171
+ workflowId,
172
+ batch: batchIndex + 1,
173
+ totalBatches: batches.length,
174
+ processing: batch
175
+ });
176
+
177
+ const batchResults = await Promise.allSettled(
178
+ batch.map(integration =>
179
+ this.setupIntegration(integration.name, integration.options)
180
+ )
181
+ );
182
+
183
+ workflow.results.push(...batchResults.map((result, index) => ({
184
+ integration: batch[index].name,
185
+ status: result.status,
186
+ result: result.status === 'fulfilled' ? result.value : null,
187
+ error: result.status === 'rejected' ? result.reason : null
188
+ })));
189
+ }
190
+
191
+ workflow.status = 'completed';
192
+ workflow.endTime = Date.now();
193
+ workflow.duration = workflow.endTime - workflow.startTime;
194
+ workflow.summary = {
195
+ total: integrations.length,
196
+ successful: workflow.results.filter(r => r.status === 'fulfilled').length,
197
+ failed: workflow.results.filter(r => r.status === 'rejected').length
198
+ };
199
+
200
+ this.emit('workflow:completed', workflow);
201
+ return workflow;
202
+
203
+ } catch (error) {
204
+ workflow.status = 'failed';
205
+ workflow.error = error;
206
+ this.emit('workflow:failed', workflow);
207
+ throw error;
208
+ }
209
+ }
210
+
211
+ /**
212
+ * Development environment setup workflow
213
+ * Prepares a complete local development environment
214
+ */
215
+ async setupDevelopmentEnvironment(options = {}) {
216
+ const workflowId = this.generateWorkflowId();
217
+ const workflow = {
218
+ id: workflowId,
219
+ type: 'development-setup',
220
+ status: 'started',
221
+ steps: [],
222
+ startTime: Date.now()
223
+ };
224
+
225
+ this.workflows.set(workflowId, workflow);
226
+ this.emit('workflow:started', workflow);
227
+
228
+ try {
229
+ // Step 1: Discover recommended integrations
230
+ await this.executeStep(workflow, 'discover-recommended', async () => {
231
+ const recommended = await this.getRecommendedIntegrations(options.projectType);
232
+ workflow.recommendedIntegrations = recommended;
233
+ return recommended;
234
+ });
235
+
236
+ // Step 2: Install core integrations
237
+ const coreIntegrations = options.integrations || workflow.recommendedIntegrations.slice(0, 3);
238
+ await this.executeStep(workflow, 'install-core', async () => {
239
+ const results = await this.bulkIntegrationSetup(
240
+ coreIntegrations.map(name => ({ name, options: {} }))
241
+ );
242
+ workflow.coreIntegrations = results;
243
+ return results;
244
+ });
245
+
246
+ // Step 3: Create test user pool
247
+ await this.executeStep(workflow, 'create-user-pool', async () => {
248
+ const users = await this.createTestUserPool({
249
+ count: options.testUserCount || 5,
250
+ integrations: coreIntegrations
251
+ });
252
+ workflow.testUsers = users;
253
+ return users;
254
+ });
255
+
256
+ // Step 4: Setup mock data
257
+ await this.executeStep(workflow, 'setup-mock-data', async () => {
258
+ const mockData = await this.setupMockData({
259
+ users: workflow.testUsers,
260
+ integrations: coreIntegrations,
261
+ dataTypes: options.mockDataTypes || ['contacts', 'messages', 'tasks']
262
+ });
263
+ workflow.mockData = mockData;
264
+ return mockData;
265
+ });
266
+
267
+ // Step 5: Configure environment
268
+ await this.executeStep(workflow, 'configure-environment', async () => {
269
+ const envConfig = await this.configureDevelopmentEnvironment({
270
+ integrations: coreIntegrations,
271
+ apiKeys: options.apiKeys || {},
272
+ features: options.features || ['debug', 'verbose-logging', 'hot-reload']
273
+ });
274
+ workflow.environmentConfig = envConfig;
275
+ return envConfig;
276
+ });
277
+
278
+ // Step 6: Validate setup
279
+ await this.executeStep(workflow, 'validate-setup', async () => {
280
+ const validation = await this.validateDevelopmentSetup(workflow);
281
+ workflow.validation = validation;
282
+ return validation;
283
+ });
284
+
285
+ workflow.status = 'completed';
286
+ workflow.endTime = Date.now();
287
+ workflow.duration = workflow.endTime - workflow.startTime;
288
+
289
+ this.emit('workflow:completed', workflow);
290
+ return workflow;
291
+
292
+ } catch (error) {
293
+ workflow.status = 'failed';
294
+ workflow.error = error;
295
+ this.emit('workflow:failed', workflow);
296
+ throw error;
297
+ }
298
+ }
299
+
300
+ /**
301
+ * Migration workflow for existing projects
302
+ * Migrates from create-frigg-app to new management UI
303
+ */
304
+ async migrateProject(projectPath, options = {}) {
305
+ const workflowId = this.generateWorkflowId();
306
+ const workflow = {
307
+ id: workflowId,
308
+ type: 'project-migration',
309
+ projectPath,
310
+ status: 'started',
311
+ steps: [],
312
+ startTime: Date.now()
313
+ };
314
+
315
+ this.workflows.set(workflowId, workflow);
316
+ this.emit('workflow:started', workflow);
317
+
318
+ try {
319
+ // Step 1: Analyze existing project
320
+ await this.executeStep(workflow, 'analyze-project', async () => {
321
+ const analysis = await this.analyzeExistingProject(projectPath);
322
+ workflow.projectAnalysis = analysis;
323
+ return analysis;
324
+ });
325
+
326
+ // Step 2: Backup current state
327
+ await this.executeStep(workflow, 'backup-project', async () => {
328
+ const backup = await this.backupProject(projectPath);
329
+ workflow.backupPath = backup.path;
330
+ return backup;
331
+ });
332
+
333
+ // Step 3: Migrate integrations
334
+ await this.executeStep(workflow, 'migrate-integrations', async () => {
335
+ const migrations = await this.migrateIntegrations(
336
+ workflow.projectAnalysis.integrations,
337
+ options.integrationMapping || {}
338
+ );
339
+ workflow.integrationMigrations = migrations;
340
+ return migrations;
341
+ });
342
+
343
+ // Step 4: Update configuration
344
+ await this.executeStep(workflow, 'update-configuration', async () => {
345
+ const config = await this.updateProjectConfiguration({
346
+ projectPath,
347
+ newStructure: true,
348
+ managementUI: true,
349
+ preserveCustom: options.preserveCustom !== false
350
+ });
351
+ workflow.configuration = config;
352
+ return config;
353
+ });
354
+
355
+ // Step 5: Migrate environment variables
356
+ await this.executeStep(workflow, 'migrate-environment', async () => {
357
+ const envMigration = await this.migrateEnvironmentVariables(
358
+ projectPath,
359
+ workflow.projectAnalysis.environment
360
+ );
361
+ workflow.environmentMigration = envMigration;
362
+ return envMigration;
363
+ });
364
+
365
+ // Step 6: Test migration
366
+ await this.executeStep(workflow, 'test-migration', async () => {
367
+ const tests = await this.testMigratedProject(projectPath);
368
+ workflow.testResults = tests;
369
+ return tests;
370
+ });
371
+
372
+ workflow.status = 'completed';
373
+ workflow.endTime = Date.now();
374
+ workflow.duration = workflow.endTime - workflow.startTime;
375
+
376
+ this.emit('workflow:completed', workflow);
377
+ return workflow;
378
+
379
+ } catch (error) {
380
+ // Rollback on failure
381
+ if (workflow.backupPath) {
382
+ await this.rollbackProject(projectPath, workflow.backupPath);
383
+ }
384
+
385
+ workflow.status = 'failed';
386
+ workflow.error = error;
387
+ this.emit('workflow:failed', workflow);
388
+ throw error;
389
+ }
390
+ }
391
+
392
+ // Helper methods
393
+
394
+ async executeStep(workflow, stepName, stepFunction) {
395
+ const step = {
396
+ name: stepName,
397
+ status: 'started',
398
+ startTime: Date.now()
399
+ };
400
+
401
+ workflow.steps.push(step);
402
+ this.emit('step:started', { workflowId: workflow.id, step });
403
+
404
+ try {
405
+ const result = await stepFunction();
406
+ step.status = 'completed';
407
+ step.endTime = Date.now();
408
+ step.duration = step.endTime - step.startTime;
409
+ step.result = result;
410
+
411
+ this.emit('step:completed', { workflowId: workflow.id, step });
412
+ return result;
413
+
414
+ } catch (error) {
415
+ step.status = 'failed';
416
+ step.endTime = Date.now();
417
+ step.duration = step.endTime - step.startTime;
418
+ step.error = {
419
+ message: error.message,
420
+ stack: error.stack
421
+ };
422
+
423
+ this.emit('step:failed', { workflowId: workflow.id, step });
424
+ throw error;
425
+ }
426
+ }
427
+
428
+ async configureIntegration(integrationName, details, userConfig) {
429
+ // Merge default configuration with user config
430
+ const defaultConfig = this.getDefaultIntegrationConfig(integrationName, details);
431
+ const config = { ...defaultConfig, ...userConfig };
432
+
433
+ // Validate configuration
434
+ const validation = await this.validateIntegrationConfig(integrationName, config);
435
+ if (!validation.valid) {
436
+ throw new Error(`Invalid configuration: ${validation.errors.join(', ')}`);
437
+ }
438
+
439
+ // Save configuration
440
+ await this.saveIntegrationConfig(integrationName, config);
441
+ return config;
442
+ }
443
+
444
+ async createTestUser(options) {
445
+ const user = {
446
+ id: this.generateUserId(),
447
+ name: options.name,
448
+ email: options.email,
449
+ integration: options.integration,
450
+ isDummy: true,
451
+ testId: `test-${options.integration}-${Date.now()}`,
452
+ createdAt: new Date().toISOString()
453
+ };
454
+
455
+ // Store user in runtime memory
456
+ await this.storeTestUser(user);
457
+ return user;
458
+ }
459
+
460
+ async generateTestCredentials(userId, integrationName, metadata) {
461
+ const authType = metadata.authType || 'oauth2';
462
+
463
+ const credentials = {
464
+ userId,
465
+ integrationName,
466
+ authType,
467
+ createdAt: new Date().toISOString(),
468
+ expiresAt: new Date(Date.now() + 3600000).toISOString() // 1 hour
469
+ };
470
+
471
+ switch (authType) {
472
+ case 'oauth2':
473
+ credentials.accessToken = this.generateTestToken('access');
474
+ credentials.refreshToken = this.generateTestToken('refresh');
475
+ credentials.scope = metadata.requiredScopes || [];
476
+ break;
477
+ case 'api-key':
478
+ credentials.apiKey = this.generateTestToken('api');
479
+ break;
480
+ case 'basic':
481
+ credentials.username = `test-user-${userId}`;
482
+ credentials.password = this.generateTestToken('password');
483
+ break;
484
+ }
485
+
486
+ return credentials;
487
+ }
488
+
489
+ async createConnection(options) {
490
+ const connection = {
491
+ id: this.generateConnectionId(),
492
+ userId: options.userId,
493
+ integrationName: options.integrationName,
494
+ credentials: options.credentials,
495
+ config: options.config,
496
+ status: 'active',
497
+ createdAt: new Date().toISOString(),
498
+ lastSync: null,
499
+ entities: []
500
+ };
501
+
502
+ // Store connection
503
+ await this.storeConnection(connection);
504
+ return connection;
505
+ }
506
+
507
+ async testConnection(connectionId) {
508
+ // Simulate connection test
509
+ const testStart = Date.now();
510
+
511
+ // Random delay to simulate API call
512
+ await new Promise(resolve => setTimeout(resolve, Math.random() * 500 + 100));
513
+
514
+ const testResult = {
515
+ connectionId,
516
+ healthy: Math.random() > 0.1, // 90% success rate for testing
517
+ latency: Date.now() - testStart,
518
+ timestamp: new Date().toISOString(),
519
+ details: {
520
+ apiVersion: '2.0',
521
+ rateLimits: {
522
+ remaining: 1000,
523
+ reset: new Date(Date.now() + 3600000).toISOString()
524
+ }
525
+ }
526
+ };
527
+
528
+ if (!testResult.healthy) {
529
+ testResult.error = 'Connection test failed: API returned 401 Unauthorized';
530
+ }
531
+
532
+ return testResult;
533
+ }
534
+
535
+ async setupEnvironmentVariables(integrationName, config, environment) {
536
+ const envVars = {};
537
+
538
+ // Generate environment-specific variables
539
+ const prefix = integrationName.toUpperCase().replace(/-/g, '_');
540
+
541
+ Object.entries(config).forEach(([key, value]) => {
542
+ const envKey = `${prefix}_${key.toUpperCase()}`;
543
+ envVars[envKey] = value;
544
+ });
545
+
546
+ // Add environment-specific settings
547
+ envVars[`${prefix}_ENVIRONMENT`] = environment;
548
+ envVars[`${prefix}_DEBUG`] = environment === 'development' ? 'true' : 'false';
549
+
550
+ // Write to .env file
551
+ await this.updateEnvironmentFile(envVars);
552
+
553
+ return envVars;
554
+ }
555
+
556
+ // Utility methods
557
+
558
+ generateWorkflowId() {
559
+ return `workflow-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
560
+ }
561
+
562
+ generateUserId() {
563
+ return `user-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
564
+ }
565
+
566
+ generateConnectionId() {
567
+ return `conn-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
568
+ }
569
+
570
+ generateTestToken(type) {
571
+ const prefix = {
572
+ access: 'at',
573
+ refresh: 'rt',
574
+ api: 'ak',
575
+ password: 'pw'
576
+ }[type] || 'tk';
577
+
578
+ return `${prefix}_test_${Date.now()}_${Math.random().toString(36).substr(2, 16)}`;
579
+ }
580
+
581
+ createBatches(items, batchSize) {
582
+ const batches = [];
583
+ for (let i = 0; i < items.length; i += batchSize) {
584
+ batches.push(items.slice(i, i + batchSize));
585
+ }
586
+ return batches;
587
+ }
588
+
589
+ getDefaultIntegrationConfig(integrationName, details) {
590
+ // Return sensible defaults based on integration type
591
+ const configs = {
592
+ slack: {
593
+ clientId: 'test-client-id',
594
+ clientSecret: 'test-client-secret',
595
+ signingSecret: 'test-signing-secret',
596
+ webhookUrl: 'http://localhost:3001/webhooks/slack'
597
+ },
598
+ hubspot: {
599
+ clientId: 'test-client-id',
600
+ clientSecret: 'test-client-secret',
601
+ portalId: '12345678',
602
+ scopes: ['contacts', 'deals']
603
+ },
604
+ salesforce: {
605
+ clientId: 'test-client-id',
606
+ clientSecret: 'test-client-secret',
607
+ loginUrl: 'https://test.salesforce.com',
608
+ apiVersion: 'v56.0'
609
+ }
610
+ };
611
+
612
+ return configs[integrationName] || {
613
+ apiKey: 'test-api-key',
614
+ baseUrl: `https://api.${integrationName}.com`
615
+ };
616
+ }
617
+
618
+ async validateIntegrationConfig(integrationName, config) {
619
+ const errors = [];
620
+
621
+ // Basic validation
622
+ if (!config || typeof config !== 'object') {
623
+ errors.push('Configuration must be an object');
624
+ }
625
+
626
+ // Integration-specific validation
627
+ switch (integrationName) {
628
+ case 'slack':
629
+ if (!config.clientId) errors.push('clientId is required');
630
+ if (!config.clientSecret) errors.push('clientSecret is required');
631
+ break;
632
+ case 'hubspot':
633
+ if (!config.portalId) errors.push('portalId is required');
634
+ if (!config.clientId) errors.push('clientId is required');
635
+ break;
636
+ default:
637
+ if (!config.apiKey && !config.clientId) {
638
+ errors.push('Either apiKey or clientId is required');
639
+ }
640
+ }
641
+
642
+ return {
643
+ valid: errors.length === 0,
644
+ errors
645
+ };
646
+ }
647
+
648
+ // Placeholder methods for data persistence
649
+ async storeTestUser(user) {
650
+ // In a real implementation, this would store to a database or file
651
+ return user;
652
+ }
653
+
654
+ async storeConnection(connection) {
655
+ // In a real implementation, this would store to a database or file
656
+ return connection;
657
+ }
658
+
659
+ async saveIntegrationConfig(integrationName, config) {
660
+ // In a real implementation, this would save to a config file
661
+ return config;
662
+ }
663
+
664
+ async updateEnvironmentFile(envVars) {
665
+ // In a real implementation, this would update the .env file
666
+ return envVars;
667
+ }
668
+
669
+ async getRecommendedIntegrations(projectType) {
670
+ const recommendations = {
671
+ 'e-commerce': ['stripe', 'shopify', 'mailchimp', 'google-analytics'],
672
+ 'saas': ['stripe', 'hubspot', 'slack', 'intercom'],
673
+ 'enterprise': ['salesforce', 'slack', 'jira', 'okta'],
674
+ 'default': ['slack', 'google-drive', 'github']
675
+ };
676
+
677
+ return recommendations[projectType] || recommendations.default;
678
+ }
679
+
680
+ async createTestUserPool(options) {
681
+ const users = [];
682
+ for (let i = 0; i < options.count; i++) {
683
+ const integration = options.integrations[i % options.integrations.length];
684
+ const user = await this.createTestUser({
685
+ name: `Test User ${i + 1}`,
686
+ email: `test${i + 1}@frigg.test`,
687
+ integration
688
+ });
689
+ users.push(user);
690
+ }
691
+ return users;
692
+ }
693
+
694
+ async setupMockData(options) {
695
+ // Generate mock data for testing
696
+ const mockData = {
697
+ users: options.users,
698
+ dataTypes: options.dataTypes,
699
+ generated: {}
700
+ };
701
+
702
+ for (const dataType of options.dataTypes) {
703
+ mockData.generated[dataType] = await this.generateMockData(dataType, options);
704
+ }
705
+
706
+ return mockData;
707
+ }
708
+
709
+ async generateMockData(dataType, options) {
710
+ const generators = {
711
+ contacts: () => ({
712
+ id: Math.random().toString(36).substr(2, 9),
713
+ name: `Contact ${Math.floor(Math.random() * 1000)}`,
714
+ email: `contact${Math.floor(Math.random() * 1000)}@example.com`,
715
+ createdAt: new Date().toISOString()
716
+ }),
717
+ messages: () => ({
718
+ id: Math.random().toString(36).substr(2, 9),
719
+ from: options.users[Math.floor(Math.random() * options.users.length)].email,
720
+ subject: `Test Message ${Math.floor(Math.random() * 1000)}`,
721
+ body: 'This is a test message generated for development.',
722
+ timestamp: new Date().toISOString()
723
+ }),
724
+ tasks: () => ({
725
+ id: Math.random().toString(36).substr(2, 9),
726
+ title: `Task ${Math.floor(Math.random() * 1000)}`,
727
+ assignee: options.users[Math.floor(Math.random() * options.users.length)].id,
728
+ status: ['pending', 'in-progress', 'completed'][Math.floor(Math.random() * 3)],
729
+ dueDate: new Date(Date.now() + Math.random() * 7 * 24 * 60 * 60 * 1000).toISOString()
730
+ })
731
+ };
732
+
733
+ const generator = generators[dataType] || (() => ({ type: dataType, data: {} }));
734
+ const count = 10; // Generate 10 items of each type
735
+
736
+ return Array(count).fill(null).map(() => generator());
737
+ }
738
+
739
+ async configureDevelopmentEnvironment(options) {
740
+ const config = {
741
+ NODE_ENV: 'development',
742
+ DEBUG: 'true',
743
+ LOG_LEVEL: 'debug'
744
+ };
745
+
746
+ // Add integration-specific config
747
+ for (const integration of options.integrations) {
748
+ const prefix = integration.toUpperCase().replace(/-/g, '_');
749
+ if (options.apiKeys[integration]) {
750
+ config[`${prefix}_API_KEY`] = options.apiKeys[integration];
751
+ }
752
+ }
753
+
754
+ // Add feature flags
755
+ for (const feature of options.features) {
756
+ config[`FEATURE_${feature.toUpperCase().replace(/-/g, '_')}`] = 'true';
757
+ }
758
+
759
+ return config;
760
+ }
761
+
762
+ async validateDevelopmentSetup(workflow) {
763
+ const validation = {
764
+ valid: true,
765
+ checks: [],
766
+ warnings: []
767
+ };
768
+
769
+ // Check integrations
770
+ const integrationCheck = {
771
+ name: 'Integrations',
772
+ passed: workflow.coreIntegrations && workflow.coreIntegrations.summary.successful > 0,
773
+ message: `${workflow.coreIntegrations?.summary.successful || 0} integrations installed successfully`
774
+ };
775
+ validation.checks.push(integrationCheck);
776
+
777
+ // Check test users
778
+ const userCheck = {
779
+ name: 'Test Users',
780
+ passed: workflow.testUsers && workflow.testUsers.length > 0,
781
+ message: `${workflow.testUsers?.length || 0} test users created`
782
+ };
783
+ validation.checks.push(userCheck);
784
+
785
+ // Check environment
786
+ const envCheck = {
787
+ name: 'Environment',
788
+ passed: workflow.environmentConfig && Object.keys(workflow.environmentConfig).length > 0,
789
+ message: 'Environment variables configured'
790
+ };
791
+ validation.checks.push(envCheck);
792
+
793
+ // Add warnings for optional features
794
+ if (!workflow.mockData || Object.keys(workflow.mockData.generated).length === 0) {
795
+ validation.warnings.push('No mock data generated - testing may be limited');
796
+ }
797
+
798
+ validation.valid = validation.checks.every(check => check.passed);
799
+ return validation;
800
+ }
801
+
802
+ // Project migration methods
803
+ async analyzeExistingProject(projectPath) {
804
+ // Analyze create-frigg-app project structure
805
+ return {
806
+ version: '0.1.0', // Mock version
807
+ integrations: ['slack', 'hubspot'], // Mock detected integrations
808
+ environment: {
809
+ NODE_ENV: 'development',
810
+ // Mock environment variables
811
+ },
812
+ customizations: {
813
+ // Detect any custom code
814
+ }
815
+ };
816
+ }
817
+
818
+ async backupProject(projectPath) {
819
+ const backupPath = `${projectPath}.backup.${Date.now()}`;
820
+ // In real implementation, would copy project files
821
+ return { path: backupPath, timestamp: new Date().toISOString() };
822
+ }
823
+
824
+ async migrateIntegrations(oldIntegrations, mapping) {
825
+ const migrations = [];
826
+ for (const oldIntegration of oldIntegrations) {
827
+ const newIntegration = mapping[oldIntegration] || oldIntegration;
828
+ migrations.push({
829
+ old: oldIntegration,
830
+ new: newIntegration,
831
+ status: 'migrated'
832
+ });
833
+ }
834
+ return migrations;
835
+ }
836
+
837
+ async updateProjectConfiguration(options) {
838
+ // Update project configuration for new structure
839
+ return {
840
+ updated: true,
841
+ files: ['package.json', 'frigg.config.js'],
842
+ changes: {
843
+ structure: 'Updated to new project structure',
844
+ scripts: 'Added management UI scripts',
845
+ dependencies: 'Updated Frigg dependencies'
846
+ }
847
+ };
848
+ }
849
+
850
+ async migrateEnvironmentVariables(projectPath, oldEnv) {
851
+ // Migrate environment variables to new format
852
+ const migrated = {};
853
+ for (const [key, value] of Object.entries(oldEnv)) {
854
+ // Apply any necessary transformations
855
+ migrated[key] = value;
856
+ }
857
+ return migrated;
858
+ }
859
+
860
+ async testMigratedProject(projectPath) {
861
+ // Run tests on migrated project
862
+ return {
863
+ passed: true,
864
+ tests: [
865
+ { name: 'Structure validation', passed: true },
866
+ { name: 'Dependency check', passed: true },
867
+ { name: 'Configuration validation', passed: true },
868
+ { name: 'Integration connectivity', passed: true }
869
+ ]
870
+ };
871
+ }
872
+
873
+ async rollbackProject(projectPath, backupPath) {
874
+ // Restore project from backup
875
+ console.log(`Rolling back project from ${backupPath}`);
876
+ return true;
877
+ }
878
+ }
879
+
880
+ // Export singleton instance for convenience
881
+ export const phase2Workflows = new Phase2IntegrationWorkflows();
882
+
883
+ // Also export class for testing and custom instances
884
+ export default Phase2IntegrationWorkflows;