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