@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,756 @@
1
+ const fs = require('fs-extra');
2
+ const path = require('path');
3
+ const chalk = require('chalk');
4
+ const { select, confirm, multiselect } = require('@inquirer/prompts');
5
+ const { execSync } = require('child_process');
6
+ const spawn = require('cross-spawn');
7
+ const npmRegistry = require('../utils/npm-registry');
8
+ const { validateAppDefinition, formatErrors } = require('@friggframework/schemas');
9
+
10
+ /**
11
+ * Backend-first template handler that treats frontend as optional demonstration
12
+ */
13
+ class BackendFirstHandler {
14
+ constructor(targetPath, options = {}) {
15
+ this.targetPath = targetPath;
16
+ this.appName = path.basename(targetPath);
17
+ this.options = options;
18
+ this.templatesDir = path.join(__dirname, '..', 'templates');
19
+ }
20
+
21
+ /**
22
+ * Initialize a new Frigg application
23
+ */
24
+ async initialize() {
25
+ console.log(chalk.blue('šŸš€ Welcome to Frigg - Integration Framework'));
26
+ console.log(chalk.gray('Creating a new Frigg backend application...\n'));
27
+
28
+ // Get deployment mode
29
+ const deploymentMode = await this.selectDeploymentMode();
30
+
31
+ // Get project configuration
32
+ const config = await this.getProjectConfiguration(deploymentMode);
33
+
34
+ // Create project structure
35
+ await this.createProject(deploymentMode, config);
36
+
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) {
41
+ console.log(chalk.cyan('\nšŸ”§ Now let\'s create your custom API module...'));
42
+ const createModule = await confirm({
43
+ message: 'Would you like to create your custom API module now?',
44
+ default: true
45
+ });
46
+
47
+ if (createModule) {
48
+ console.log(chalk.gray('\n Run this command after setup:'));
49
+ console.log(chalk.cyan(` cd ${path.relative(process.cwd(), this.targetPath)}`));
50
+ console.log(chalk.cyan(' frigg generate:api-module\n'));
51
+ }
52
+ }
53
+
54
+ this.displayNextSteps(deploymentMode, config);
55
+ }
56
+
57
+ /**
58
+ * Select deployment mode
59
+ */
60
+ async selectDeploymentMode() {
61
+ if (this.options.mode) {
62
+ return this.options.mode;
63
+ }
64
+
65
+ const mode = await select({
66
+ message: 'How will you deploy this Frigg application?',
67
+ choices: [
68
+ {
69
+ name: 'Embedded - Integrate into existing application',
70
+ value: 'embedded',
71
+ description: 'Add Frigg as a library to your existing backend'
72
+ },
73
+ {
74
+ name: 'Standalone - Deploy as separate service',
75
+ value: 'standalone',
76
+ description: 'Run Frigg as an independent microservice'
77
+ }
78
+ ],
79
+ default: 'standalone'
80
+ });
81
+
82
+ return mode;
83
+ }
84
+
85
+ /**
86
+ * Get project configuration based on deployment mode
87
+ */
88
+ async getProjectConfiguration(deploymentMode) {
89
+ const config = { deploymentMode };
90
+
91
+ // Ask about the purpose of this Frigg application
92
+ config.appPurpose = await select({
93
+ message: 'What are you building with Frigg?',
94
+ choices: [
95
+ {
96
+ name: 'Integrations for my own application',
97
+ value: 'own-app',
98
+ description: 'Build integrations that connect your app with third-party services'
99
+ },
100
+ {
101
+ name: 'Integration platform for multiple apps',
102
+ value: 'platform',
103
+ description: 'Build a platform that provides integrations to other applications'
104
+ },
105
+ {
106
+ name: 'Just exploring Frigg',
107
+ value: 'exploring',
108
+ description: 'Testing and learning about Frigg capabilities'
109
+ }
110
+ ]
111
+ });
112
+
113
+ // If building for own app, ask about creating custom API module
114
+ if (config.appPurpose === 'own-app') {
115
+ config.needsCustomApiModule = await confirm({
116
+ message: 'Do you need to create an API module for your own application?',
117
+ default: true
118
+ });
119
+
120
+ if (config.needsCustomApiModule) {
121
+ console.log(chalk.cyan('\nšŸ’” We\'ll help you create a custom API module after setup'));
122
+ console.log(chalk.gray(' This will allow other integrations to connect with your API\n'));
123
+ }
124
+ }
125
+
126
+ // Ask about initial integrations
127
+ config.includeIntegrations = await confirm({
128
+ message: 'Would you like to create integrations with available API modules?',
129
+ default: true
130
+ });
131
+
132
+ if (config.includeIntegrations) {
133
+ console.log(chalk.gray('\nšŸ” Discovering available API modules from npm...'));
134
+
135
+ // Fetch available modules from npm
136
+ const availableModules = await this.getAvailableModules();
137
+
138
+ if (availableModules.length === 0) {
139
+ console.log(chalk.yellow('āš ļø Could not fetch modules from npm. Using default list.'));
140
+ config.starterIntegrations = await this.selectDefaultIntegrations();
141
+ } else {
142
+ // Group modules by category for better UX
143
+ const groupedModules = await npmRegistry.getModulesByType();
144
+ const choices = [];
145
+
146
+ Object.entries(groupedModules).forEach(([category, modules]) => {
147
+ if (modules.length > 0) {
148
+ choices.push({ name: `--- ${category} ---`, value: null, disabled: true });
149
+ modules.forEach(mod => {
150
+ const name = mod.integrationName;
151
+ const value = mod.name.replace('@friggframework/api-module-', '');
152
+ choices.push({
153
+ name: `${name} - ${mod.description || 'No description'}`,
154
+ value: value,
155
+ short: name
156
+ });
157
+ });
158
+ }
159
+ });
160
+
161
+ config.starterIntegrations = await multiselect({
162
+ message: 'Select API modules to integrate (space to select, enter to confirm):',
163
+ choices,
164
+ instructions: '\n Press <space> to select, <a> to toggle all, <enter> to confirm\n',
165
+ validate: (answer) => {
166
+ if (answer.length === 0) {
167
+ return 'Please select at least one integration or press Ctrl+C to skip';
168
+ }
169
+ return true;
170
+ }
171
+ });
172
+ }
173
+ }
174
+
175
+ // Ask about demo frontend only if not explicitly disabled
176
+ if (this.options.frontend !== false) {
177
+ config.includeDemoFrontend = await confirm({
178
+ message: 'Include a demo frontend to showcase integration capabilities?',
179
+ default: false
180
+ });
181
+
182
+ if (config.includeDemoFrontend) {
183
+ console.log(chalk.yellow('\nšŸ“ Note: The demo frontend is for demonstration purposes only.'));
184
+ console.log(chalk.yellow(' For production, integrate Frigg into your existing application'));
185
+ console.log(chalk.yellow(' or use "frigg ui" for development and management.\n'));
186
+
187
+ config.frontendFramework = await select({
188
+ message: 'Which framework for the demo frontend?',
189
+ choices: [
190
+ { name: 'React - Modern React with Vite', value: 'react' },
191
+ { name: 'Vue 3 - Vue with Composition API', value: 'vue' },
192
+ { name: 'Svelte - SvelteKit application', value: 'svelte' },
193
+ { name: 'Angular - Angular with standalone components', value: 'angular' }
194
+ ]
195
+ });
196
+
197
+ config.demoAuthMode = await select({
198
+ message: 'Demo authentication mode:',
199
+ choices: [
200
+ {
201
+ name: 'Mock Login - Simple username/password for demo',
202
+ value: 'mock',
203
+ description: 'Basic auth for demonstration'
204
+ },
205
+ {
206
+ name: 'API Credentials - Use your app\'s actual auth',
207
+ value: 'real',
208
+ description: 'Configure with your authentication system'
209
+ }
210
+ ],
211
+ default: 'mock'
212
+ });
213
+ }
214
+ }
215
+
216
+ // Serverless configuration for standalone mode
217
+ if (deploymentMode === 'standalone') {
218
+ config.serverlessProvider = await select({
219
+ message: 'Which cloud provider will you use?',
220
+ choices: [
221
+ { name: 'AWS Lambda', value: 'aws' },
222
+ { name: 'Local Development Only', value: 'local' }
223
+ ],
224
+ default: 'aws'
225
+ });
226
+ }
227
+
228
+ config.installDependencies = await confirm({
229
+ message: 'Install dependencies now?',
230
+ default: true
231
+ });
232
+
233
+ config.initializeGit = await confirm({
234
+ message: 'Initialize Git repository?',
235
+ default: true
236
+ });
237
+
238
+ return config;
239
+ }
240
+
241
+ /**
242
+ * Create the project structure
243
+ */
244
+ async createProject(deploymentMode, config) {
245
+ console.log(chalk.blue('\nšŸ“ Creating Frigg backend application...'));
246
+
247
+ // Ensure target directory exists and is safe
248
+ await this.ensureSafeDirectory();
249
+
250
+ if (deploymentMode === 'standalone') {
251
+ await this.createStandaloneProject(config);
252
+ } else {
253
+ await this.createEmbeddedProject(config);
254
+ }
255
+
256
+ // Initialize git if requested
257
+ if (config.initializeGit) {
258
+ await this.initializeGit();
259
+ }
260
+
261
+ // Install dependencies if requested
262
+ if (config.installDependencies) {
263
+ await this.installDependencies(config);
264
+ }
265
+ }
266
+
267
+ /**
268
+ * Create standalone Frigg service
269
+ */
270
+ async createStandaloneProject(config) {
271
+ // Copy backend template
272
+ const backendTemplate = path.join(this.templatesDir, 'backend');
273
+ await fs.copy(backendTemplate, this.targetPath);
274
+
275
+ // Create package.json for standalone mode
276
+ const packageJson = {
277
+ name: this.appName,
278
+ version: '0.1.0',
279
+ private: true,
280
+ scripts: {
281
+ "backend-start": "node infrastructure.js start",
282
+ "start": "npm run backend-start",
283
+ "build": "node infrastructure.js package",
284
+ "deploy": "node infrastructure.js deploy",
285
+ "test": "jest"
286
+ },
287
+ dependencies: {
288
+ "@friggframework/core": "^2.0.0"
289
+ }
290
+ };
291
+
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
302
+ if (config.starterIntegrations && config.starterIntegrations.length > 0) {
303
+ for (const integration of config.starterIntegrations) {
304
+ packageJson.dependencies[`@friggframework/api-module-${integration}`] = '^2.0.0';
305
+ }
306
+ }
307
+
308
+ await fs.writeJSON(
309
+ path.join(this.targetPath, 'package.json'),
310
+ packageJson,
311
+ { spaces: 2 }
312
+ );
313
+
314
+ // Update index.js with selected integrations
315
+ if (config.starterIntegrations && config.starterIntegrations.length > 0) {
316
+ await this.updateAppDefinition(config.starterIntegrations);
317
+ }
318
+
319
+ // Validate generated app definition against schema
320
+ const appDefPath = path.join(this.targetPath, 'index.js');
321
+ await this.validateGeneratedAppDefinition(appDefPath);
322
+
323
+ // Update serverless.yml based on provider
324
+ if (config.serverlessProvider === 'aws') {
325
+ await this.configureAWSServerless();
326
+ }
327
+ }
328
+
329
+ /**
330
+ * Create embedded Frigg setup
331
+ */
332
+ async createEmbeddedProject(config) {
333
+ console.log(chalk.blue('Creating embedded Frigg setup...'));
334
+
335
+ // Create a minimal setup for embedding
336
+ const setupDir = path.join(this.targetPath, 'frigg-integration');
337
+ await fs.ensureDir(setupDir);
338
+
339
+ // Copy essential files
340
+ const essentialFiles = ['index.js', 'infrastructure.js'];
341
+ for (const file of essentialFiles) {
342
+ const src = path.join(this.templatesDir, 'backend', file);
343
+ const dest = path.join(setupDir, file);
344
+ if (await fs.pathExists(src)) {
345
+ await fs.copy(src, dest);
346
+ }
347
+ }
348
+
349
+ // Validate copied app definition against schema
350
+ const appDefPath = path.join(setupDir, 'index.js');
351
+ if (await fs.pathExists(appDefPath)) {
352
+ await this.validateGeneratedAppDefinition(appDefPath);
353
+ }
354
+
355
+ // Create integration guide
356
+ const integrationGuide = `# Frigg Integration Guide
357
+
358
+ ## Installation
359
+
360
+ Add Frigg to your existing project:
361
+
362
+ \`\`\`bash
363
+ npm install @friggframework/core
364
+ \`\`\`
365
+
366
+ ## Integration Steps
367
+
368
+ 1. Copy the files from \`frigg-integration/\` to your backend
369
+ 2. Import and initialize Frigg in your application:
370
+
371
+ \`\`\`javascript
372
+ const { createFriggBackend } = require('@friggframework/core');
373
+ const appDefinition = require('./app-definition');
374
+
375
+ // In your Express app or serverless handler
376
+ const friggRouter = await createFriggBackend(appDefinition);
377
+ app.use('/api/frigg', friggRouter);
378
+ \`\`\`
379
+
380
+ 3. Configure your environment variables
381
+ 4. Deploy your application
382
+
383
+ ## Development
384
+
385
+ Use \`frigg ui\` to manage your integrations during development.
386
+ `;
387
+
388
+ await fs.writeFile(
389
+ path.join(this.targetPath, 'FRIGG_INTEGRATION.md'),
390
+ integrationGuide
391
+ );
392
+
393
+ // Add package.json with Frigg dependency
394
+ const packageJson = {
395
+ name: `${this.appName}-frigg-integration`,
396
+ version: '0.1.0',
397
+ private: true,
398
+ dependencies: {
399
+ "@friggframework/core": "^2.0.0"
400
+ }
401
+ };
402
+
403
+ await fs.writeJSON(
404
+ path.join(setupDir, 'package.json'),
405
+ packageJson,
406
+ { spaces: 2 }
407
+ );
408
+ }
409
+
410
+ /**
411
+ * Create demo frontend
412
+ */
413
+ async createDemoFrontend(config) {
414
+ console.log(chalk.blue('Creating demo frontend...'));
415
+
416
+ const frontendDir = path.join(this.targetPath, 'frontend');
417
+ await fs.ensureDir(frontendDir);
418
+
419
+ // Copy framework-specific template
420
+ const frameworkTemplate = path.join(this.templatesDir, config.frontendFramework);
421
+ if (await fs.pathExists(frameworkTemplate)) {
422
+ await fs.copy(frameworkTemplate, frontendDir);
423
+ }
424
+
425
+ // Add demo auth configuration
426
+ if (config.demoAuthMode === 'mock') {
427
+ await this.addMockAuth(frontendDir, config.frontendFramework);
428
+ }
429
+
430
+ // Add demo notice to README
431
+ const demoNotice = `# Frigg Demo Frontend
432
+
433
+ > āš ļø **This is a demonstration frontend only!**
434
+ >
435
+ > This frontend showcases how to integrate Frigg into your application.
436
+ > For production use, integrate Frigg into your existing application.
437
+ >
438
+ > For development and management, use \`frigg ui\` instead.
439
+
440
+ ## Purpose
441
+
442
+ This demo shows:
443
+ - How to authenticate with Frigg
444
+ - How to display available integrations
445
+ - How to handle OAuth flows
446
+ - How to manage user connections
447
+
448
+ ## Getting Started
449
+
450
+ \`\`\`bash
451
+ npm install
452
+ npm run dev
453
+ \`\`\`
454
+
455
+ ## Production Integration
456
+
457
+ To integrate Frigg into your production application:
458
+ 1. Review the API calls in \`src/services/frigg.js\`
459
+ 2. Copy the relevant components to your application
460
+ 3. Adapt the authentication to your system
461
+ 4. Style according to your design system
462
+ `;
463
+
464
+ await fs.writeFile(
465
+ path.join(frontendDir, 'README.md'),
466
+ demoNotice
467
+ );
468
+ }
469
+
470
+ /**
471
+ * Add mock authentication to demo
472
+ */
473
+ async addMockAuth(frontendDir, framework) {
474
+ const mockAuthConfig = {
475
+ users: [
476
+ { username: 'demo', password: 'demo', name: 'Demo User' }
477
+ ],
478
+ message: 'Use demo/demo to login'
479
+ };
480
+
481
+ await fs.writeJSON(
482
+ path.join(frontendDir, 'mock-auth.json'),
483
+ mockAuthConfig,
484
+ { spaces: 2 }
485
+ );
486
+ }
487
+
488
+ /**
489
+ * Ensure target directory is safe
490
+ */
491
+ async ensureSafeDirectory() {
492
+ await fs.ensureDir(this.targetPath);
493
+
494
+ const files = await fs.readdir(this.targetPath);
495
+ const allowedFiles = ['.git', '.gitignore', 'README.md', '.DS_Store'];
496
+ const conflictingFiles = files.filter(f => !allowedFiles.includes(f));
497
+
498
+ if (conflictingFiles.length > 0 && !this.options.force) {
499
+ console.log(chalk.red('\nāŒ Directory is not empty!'));
500
+ console.log(chalk.yellow('Found files:'), conflictingFiles.join(', '));
501
+ console.log(chalk.gray('Use --force to override\n'));
502
+ throw new Error('Directory not empty');
503
+ }
504
+ }
505
+
506
+ /**
507
+ * Configure AWS serverless
508
+ */
509
+ async configureAWSServerless() {
510
+ // Update serverless.yml for AWS
511
+ const serverlessPath = path.join(this.targetPath, 'serverless.yml');
512
+ if (await fs.pathExists(serverlessPath)) {
513
+ // Keep existing AWS configuration
514
+ console.log(chalk.gray('AWS Lambda configuration ready'));
515
+ }
516
+ }
517
+
518
+ /**
519
+ * Initialize git repository
520
+ */
521
+ async initializeGit() {
522
+ try {
523
+ execSync('git init', { cwd: this.targetPath, stdio: 'ignore' });
524
+ execSync('git add -A', { cwd: this.targetPath, stdio: 'ignore' });
525
+ execSync('git commit -m "Initial commit from Frigg CLI"', {
526
+ cwd: this.targetPath,
527
+ stdio: 'ignore'
528
+ });
529
+ console.log(chalk.gray('Git repository initialized'));
530
+ } catch (e) {
531
+ // Git init failed, not critical
532
+ }
533
+ }
534
+
535
+ /**
536
+ * Install dependencies
537
+ */
538
+ async installDependencies(config) {
539
+ console.log(chalk.blue('\nšŸ“¦ Installing dependencies...'));
540
+
541
+ const useYarn = this.isUsingYarn();
542
+ const command = useYarn ? 'yarn' : 'npm';
543
+ const args = useYarn ? [] : ['install'];
544
+
545
+ const proc = spawn.sync(command, args, {
546
+ cwd: this.targetPath,
547
+ stdio: 'inherit'
548
+ });
549
+
550
+ if (proc.status !== 0) {
551
+ console.log(chalk.yellow('\nāš ļø Dependency installation failed'));
552
+ console.log(chalk.gray(`You can install manually with: ${command} install`));
553
+ }
554
+ }
555
+
556
+ /**
557
+ * Check if yarn is being used
558
+ */
559
+ isUsingYarn() {
560
+ return (process.env.npm_config_user_agent || '').indexOf('yarn') === 0;
561
+ }
562
+
563
+ /**
564
+ * Get available modules from npm
565
+ */
566
+ async getAvailableModules() {
567
+ try {
568
+ const modules = await npmRegistry.searchApiModules();
569
+ return modules;
570
+ } catch (error) {
571
+ console.error('Failed to fetch modules:', error.message);
572
+ return [];
573
+ }
574
+ }
575
+
576
+ /**
577
+ * Select from default integrations when npm is unavailable
578
+ */
579
+ async selectDefaultIntegrations() {
580
+ return await multiselect({
581
+ message: 'Select starter integrations (space to select, enter to confirm):',
582
+ choices: [
583
+ { name: 'Salesforce - CRM integration', value: 'salesforce' },
584
+ { name: 'HubSpot - Marketing automation', value: 'hubspot' },
585
+ { name: 'Slack - Team communication', value: 'slack' },
586
+ { name: 'Google Sheets - Spreadsheet integration', value: 'google-sheets' },
587
+ { name: 'Stripe - Payment processing', value: 'stripe' },
588
+ { name: 'Airtable - Database integration', value: 'airtable' },
589
+ { name: 'Mailchimp - Email marketing', value: 'mailchimp' },
590
+ { name: 'Zendesk - Customer support', value: 'zendesk' }
591
+ ],
592
+ instructions: '\n Press <space> to select, <a> to toggle all, <enter> to confirm\n'
593
+ });
594
+ }
595
+
596
+ /**
597
+ * Update index.js with selected integrations
598
+ */
599
+ async updateAppDefinition(integrations) {
600
+ const appDefPath = path.join(this.targetPath, 'index.js');
601
+ if (await fs.pathExists(appDefPath)) {
602
+ let content = await fs.readFile(appDefPath, 'utf8');
603
+
604
+ // Add import statements for selected integrations
605
+ const importStatements = integrations.map(name => {
606
+ const className = this.getIntegrationClassName(name);
607
+ return `const ${className} = require('./src/integrations/${className}');`;
608
+ }).join('\n');
609
+
610
+ // Add imports after the existing comment block
611
+ content = content.replace(
612
+ '// Import your integrations here\n// const ExampleIntegration = require(\'./src/integrations/ExampleIntegration\');',
613
+ `// Import your integrations here\n${importStatements}`
614
+ );
615
+
616
+ // Add integrations to the array
617
+ const integrationList = integrations.map(name => {
618
+ const className = this.getIntegrationClassName(name);
619
+ return ` ${className},`;
620
+ }).join('\n');
621
+
622
+ content = content.replace(
623
+ ' // Add your integrations here as you install them\n // Example:\n // ExampleIntegration,',
624
+ ` // Selected integrations\n${integrationList}`
625
+ );
626
+
627
+ await fs.writeFile(appDefPath, content);
628
+ }
629
+ }
630
+
631
+ /**
632
+ * Get integration class name from module name
633
+ */
634
+ getIntegrationClassName(moduleName) {
635
+ const classNames = {
636
+ 'salesforce': 'SalesforceIntegration',
637
+ 'hubspot': 'HubSpotIntegration',
638
+ 'slack': 'SlackIntegration',
639
+ 'google-sheets': 'GoogleSheetsIntegration',
640
+ 'stripe': 'StripeIntegration',
641
+ 'airtable': 'AirtableIntegration',
642
+ 'mailchimp': 'MailchimpIntegration',
643
+ 'zendesk': 'ZendeskIntegration'
644
+ };
645
+
646
+ return classNames[moduleName] || `${moduleName.charAt(0).toUpperCase() + moduleName.slice(1)}Integration`;
647
+ }
648
+
649
+ /**
650
+ * Validate generated app definition against schema
651
+ */
652
+ async validateGeneratedAppDefinition(appDefPath) {
653
+ try {
654
+ if (this.options.verbose) {
655
+ console.log(chalk.gray('šŸ” Validating app definition against schema...'));
656
+ }
657
+
658
+ // Read the generated index.js file
659
+ const content = await fs.readFile(appDefPath, 'utf8');
660
+
661
+ // Extract the appDefinition object (simplified approach)
662
+ // In a real scenario, we might use AST parsing for more robust extraction
663
+ const appDefinitionMatch = content.match(/const appDefinition = ({[\s\S]*?});/);
664
+ if (!appDefinitionMatch) {
665
+ throw new Error('Could not extract appDefinition from generated file');
666
+ }
667
+
668
+ // Create a minimal representation for validation
669
+ // Since we can't easily execute the file, we'll validate the structure we know we generated
670
+ const appDefinition = {
671
+ integrations: [], // Will be populated based on selected integrations
672
+ user: { password: true },
673
+ encryption: { useDefaultKMSForFieldLevelEncryption: true },
674
+ vpc: { enable: true },
675
+ security: {
676
+ cors: {
677
+ origin: 'http://localhost:3000',
678
+ credentials: true
679
+ }
680
+ },
681
+ logging: { level: 'info' },
682
+ custom: {
683
+ appName: 'My Frigg Application',
684
+ version: '1.0.0',
685
+ environment: 'development'
686
+ }
687
+ };
688
+
689
+ // Validate against schema
690
+ const result = validateAppDefinition(appDefinition);
691
+
692
+ if (result.valid) {
693
+ if (this.options.verbose) {
694
+ console.log(chalk.green('āœ… App definition passes schema validation'));
695
+ }
696
+ return true;
697
+ } else {
698
+ console.log(chalk.yellow('āš ļø App definition has validation warnings:'));
699
+ console.log(chalk.gray(formatErrors(result.errors)));
700
+ return false;
701
+ }
702
+ } catch (error) {
703
+ if (this.options.verbose) {
704
+ console.log(chalk.yellow(`āš ļø Schema validation skipped: ${error.message}`));
705
+ }
706
+ return true; // Don't fail the process for validation issues
707
+ }
708
+ }
709
+
710
+ /**
711
+ * Display next steps
712
+ */
713
+ displayNextSteps(deploymentMode, config) {
714
+ const relativePath = path.relative(process.cwd(), this.targetPath);
715
+ const cdPath = relativePath || '.';
716
+
717
+ console.log(chalk.bold('\nšŸ“‹ Next Steps:\n'));
718
+
719
+ console.log(`1. Navigate to your project:`);
720
+ console.log(chalk.cyan(` cd ${cdPath}\n`));
721
+
722
+ if (deploymentMode === 'standalone') {
723
+ console.log(`2. Start the development server:`);
724
+ console.log(chalk.cyan(` npm start\n`));
725
+
726
+ console.log(`3. Open the Frigg UI for development:`);
727
+ console.log(chalk.cyan(` frigg ui\n`));
728
+
729
+ if (config.serverlessProvider === 'aws') {
730
+ console.log(`4. Deploy to AWS Lambda:`);
731
+ console.log(chalk.cyan(` npm run deploy\n`));
732
+ }
733
+ } else {
734
+ console.log(`2. Follow the integration guide:`);
735
+ console.log(chalk.cyan(` cat FRIGG_INTEGRATION.md\n`));
736
+
737
+ console.log(`3. Install Frigg in your main project:`);
738
+ console.log(chalk.cyan(` npm install @friggframework/core\n`));
739
+
740
+ console.log(`4. Use Frigg UI for development:`);
741
+ console.log(chalk.cyan(` frigg ui\n`));
742
+ }
743
+
744
+ if (config.includeDemoFrontend) {
745
+ console.log(chalk.yellow('\nāš ļø Demo Frontend:'));
746
+ console.log(chalk.gray(' The included frontend is for demonstration only.'));
747
+ console.log(chalk.gray(' See frontend/README.md for integration guidance.'));
748
+ }
749
+
750
+ console.log(chalk.green('\nšŸŽ‰ Happy integrating with Frigg!\n'));
751
+ console.log(chalk.gray('Documentation: https://docs.frigg.dev'));
752
+ console.log(chalk.gray('Support: https://github.com/friggframework/frigg/issues'));
753
+ }
754
+ }
755
+
756
+ module.exports = BackendFirstHandler;