@friggframework/devtools 2.0.0--canary.548.c8ae0ca.0 → 2.0.0--canary.545.c40eca4.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 +128 -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 +21 -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
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
const {ValidationException} = require('../../domain/exceptions/DomainException');
|
|
2
|
+
const {IntegrationValidator} = require('../../domain/services/IntegrationValidator');
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* AddApiModuleToIntegrationUseCase
|
|
6
|
+
*
|
|
7
|
+
* Application layer use case for adding API modules to existing integrations
|
|
8
|
+
* Orchestrates the addition with validation and persistence
|
|
9
|
+
*/
|
|
10
|
+
class AddApiModuleToIntegrationUseCase {
|
|
11
|
+
constructor(integrationRepository, apiModuleRepository, unitOfWork, integrationValidator = null, integrationJsUpdater = null) {
|
|
12
|
+
this.integrationRepository = integrationRepository;
|
|
13
|
+
this.apiModuleRepository = apiModuleRepository;
|
|
14
|
+
this.unitOfWork = unitOfWork;
|
|
15
|
+
this.integrationValidator = integrationValidator ||
|
|
16
|
+
new IntegrationValidator(integrationRepository);
|
|
17
|
+
this.integrationJsUpdater = integrationJsUpdater;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Execute the use case
|
|
22
|
+
* @param {object} request - Request data
|
|
23
|
+
* @param {string} request.integrationName - Name of the integration
|
|
24
|
+
* @param {string} request.moduleName - Name of the API module to add
|
|
25
|
+
* @param {string} request.moduleVersion - Version of the API module
|
|
26
|
+
* @param {string} request.source - Source of the module (npm, local, git)
|
|
27
|
+
* @returns {Promise<{success: boolean, integration: object}>}
|
|
28
|
+
*/
|
|
29
|
+
async execute(request) {
|
|
30
|
+
try {
|
|
31
|
+
// 1. Load the integration
|
|
32
|
+
const integration = await this.integrationRepository.findByName(request.integrationName);
|
|
33
|
+
if (!integration) {
|
|
34
|
+
throw new ValidationException(`Integration '${request.integrationName}' not found`);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// 2. Verify API module exists
|
|
38
|
+
const apiModuleExists = await this.apiModuleRepository.exists(request.moduleName);
|
|
39
|
+
if (!apiModuleExists) {
|
|
40
|
+
throw new ValidationException(`API module '${request.moduleName}' not found. Create it first with 'frigg create api-module ${request.moduleName}'`);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// 3. Validate API module addition
|
|
44
|
+
const validation = this.integrationValidator.validateApiModuleAddition(
|
|
45
|
+
integration,
|
|
46
|
+
request.moduleName,
|
|
47
|
+
request.moduleVersion || '1.0.0'
|
|
48
|
+
);
|
|
49
|
+
|
|
50
|
+
if (!validation.isValid) {
|
|
51
|
+
throw new ValidationException(validation.errors);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// 4. Add the API module to the integration
|
|
55
|
+
integration.addApiModule(
|
|
56
|
+
request.moduleName,
|
|
57
|
+
request.moduleVersion || '1.0.0',
|
|
58
|
+
request.source || 'local'
|
|
59
|
+
);
|
|
60
|
+
|
|
61
|
+
// 5. Save the updated integration
|
|
62
|
+
await this.integrationRepository.save(integration);
|
|
63
|
+
|
|
64
|
+
// 6. Update Integration.js file to add module import and Definition entry
|
|
65
|
+
if (this.integrationJsUpdater) {
|
|
66
|
+
const integrationJsExists = await this.integrationJsUpdater.exists(request.integrationName);
|
|
67
|
+
if (integrationJsExists) {
|
|
68
|
+
await this.integrationJsUpdater.addModuleToIntegration(
|
|
69
|
+
request.integrationName,
|
|
70
|
+
request.moduleName,
|
|
71
|
+
request.source || 'local'
|
|
72
|
+
);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// 7. Commit transaction
|
|
77
|
+
await this.unitOfWork.commit();
|
|
78
|
+
|
|
79
|
+
return {
|
|
80
|
+
success: true,
|
|
81
|
+
integration: integration.toObject(),
|
|
82
|
+
message: `API module '${request.moduleName}' added to integration '${request.integrationName}'`
|
|
83
|
+
};
|
|
84
|
+
} catch (error) {
|
|
85
|
+
// Rollback all file operations on error
|
|
86
|
+
await this.unitOfWork.rollback();
|
|
87
|
+
|
|
88
|
+
throw error;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
module.exports = {AddApiModuleToIntegrationUseCase};
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
const {ApiModule} = require('../../domain/entities/ApiModule');
|
|
2
|
+
const {ValidationException} = require('../../domain/exceptions/DomainException');
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* CreateApiModuleUseCase
|
|
6
|
+
*
|
|
7
|
+
* Application layer use case for creating new API modules
|
|
8
|
+
* Orchestrates API module creation with validation and persistence
|
|
9
|
+
*/
|
|
10
|
+
class CreateApiModuleUseCase {
|
|
11
|
+
constructor(apiModuleRepository, unitOfWork, appDefinitionRepository = null) {
|
|
12
|
+
this.apiModuleRepository = apiModuleRepository;
|
|
13
|
+
this.unitOfWork = unitOfWork;
|
|
14
|
+
this.appDefinitionRepository = appDefinitionRepository;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Execute the use case
|
|
19
|
+
* @param {object} request - Request data
|
|
20
|
+
* @param {string} request.name - API module name (kebab-case)
|
|
21
|
+
* @param {string} request.displayName - Human-readable name
|
|
22
|
+
* @param {string} request.description - Description
|
|
23
|
+
* @param {string} request.baseUrl - API base URL
|
|
24
|
+
* @param {string} request.authType - Authentication type
|
|
25
|
+
* @param {array} request.scopes - OAuth scopes
|
|
26
|
+
* @param {array} request.credentials - Required credentials
|
|
27
|
+
* @param {object} request.entities - Entity configurations
|
|
28
|
+
* @param {object} request.endpoints - API endpoints
|
|
29
|
+
* @returns {Promise<{success: boolean, apiModule: object}>}
|
|
30
|
+
*/
|
|
31
|
+
async execute(request) {
|
|
32
|
+
try {
|
|
33
|
+
// 1. Create domain entity
|
|
34
|
+
const apiModule = ApiModule.create({
|
|
35
|
+
name: request.name,
|
|
36
|
+
displayName: request.displayName,
|
|
37
|
+
description: request.description,
|
|
38
|
+
apiConfig: {
|
|
39
|
+
baseUrl: request.baseUrl || '',
|
|
40
|
+
authType: request.authType || 'oauth2',
|
|
41
|
+
version: request.apiVersion || 'v1'
|
|
42
|
+
},
|
|
43
|
+
entities: request.entities || {},
|
|
44
|
+
scopes: request.scopes || [],
|
|
45
|
+
credentials: request.credentials || [],
|
|
46
|
+
endpoints: request.endpoints || {}
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
// 2. Validate business rules
|
|
50
|
+
const validation = apiModule.validate();
|
|
51
|
+
if (!validation.isValid) {
|
|
52
|
+
throw new ValidationException(validation.errors);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// 3. Check for existing API module (uniqueness)
|
|
56
|
+
const exists = await this.apiModuleRepository.exists(apiModule.name);
|
|
57
|
+
if (exists) {
|
|
58
|
+
throw new ValidationException(`API module '${apiModule.name}' already exists`);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// 4. Save through repository (writes files atomically)
|
|
62
|
+
await this.apiModuleRepository.save(apiModule);
|
|
63
|
+
|
|
64
|
+
// 5. Register in AppDefinition (if repository is available)
|
|
65
|
+
if (this.appDefinitionRepository) {
|
|
66
|
+
try {
|
|
67
|
+
const appDef = await this.appDefinitionRepository.load();
|
|
68
|
+
if (appDef) {
|
|
69
|
+
appDef.registerApiModule(apiModule.name, apiModule.version.value, 'local');
|
|
70
|
+
await this.appDefinitionRepository.save(appDef);
|
|
71
|
+
}
|
|
72
|
+
} catch (error) {
|
|
73
|
+
console.warn('Could not register API module in app definition:', error.message);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// 6. Commit transaction (cleanup backups)
|
|
78
|
+
await this.unitOfWork.commit();
|
|
79
|
+
|
|
80
|
+
return {
|
|
81
|
+
success: true,
|
|
82
|
+
apiModule: apiModule.toObject()
|
|
83
|
+
};
|
|
84
|
+
} catch (error) {
|
|
85
|
+
// Rollback all file operations on error
|
|
86
|
+
await this.unitOfWork.rollback();
|
|
87
|
+
|
|
88
|
+
throw error;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
module.exports = {CreateApiModuleUseCase};
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
const {Integration} = require('../../domain/entities/Integration');
|
|
2
|
+
const {ValidationException} = require('../../domain/exceptions/DomainException');
|
|
3
|
+
const {IntegrationValidator} = require('../../domain/services/IntegrationValidator');
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* CreateIntegrationUseCase
|
|
7
|
+
* Application layer use case for creating new integrations
|
|
8
|
+
* Uses IntegrationValidator domain service for comprehensive validation
|
|
9
|
+
* Automatically registers integration in AppDefinition
|
|
10
|
+
*/
|
|
11
|
+
class CreateIntegrationUseCase {
|
|
12
|
+
constructor(integrationRepository, unitOfWork, integrationValidator = null, appDefinitionRepository = null, backendJsUpdater = null) {
|
|
13
|
+
this.integrationRepository = integrationRepository;
|
|
14
|
+
this.unitOfWork = unitOfWork;
|
|
15
|
+
this.appDefinitionRepository = appDefinitionRepository;
|
|
16
|
+
this.backendJsUpdater = backendJsUpdater;
|
|
17
|
+
// Allow validator injection for testing, or create default
|
|
18
|
+
this.integrationValidator = integrationValidator ||
|
|
19
|
+
new IntegrationValidator(integrationRepository);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Execute the use case
|
|
24
|
+
* @param {object} request - Request data
|
|
25
|
+
* @param {string} request.name - Integration name (kebab-case)
|
|
26
|
+
* @param {string} request.displayName - Human-readable name
|
|
27
|
+
* @param {string} request.description - Description
|
|
28
|
+
* @param {string} request.type - Integration type (api, webhook, sync, etc.)
|
|
29
|
+
* @param {string} request.category - Category
|
|
30
|
+
* @param {array} request.tags - Tags
|
|
31
|
+
* @param {object} request.entities - Entity configuration
|
|
32
|
+
* @param {object} request.capabilities - Capabilities
|
|
33
|
+
* @param {object} request.requirements - Requirements
|
|
34
|
+
* @returns {Promise<{success: boolean, integration: object}>}
|
|
35
|
+
*/
|
|
36
|
+
async execute(request) {
|
|
37
|
+
try {
|
|
38
|
+
// 1. Create domain entity (validates name format via value object)
|
|
39
|
+
const integration = Integration.create({
|
|
40
|
+
name: request.name,
|
|
41
|
+
displayName: request.displayName,
|
|
42
|
+
description: request.description,
|
|
43
|
+
type: request.type || 'custom',
|
|
44
|
+
category: request.category,
|
|
45
|
+
tags: request.tags || [],
|
|
46
|
+
entities: request.entities || {},
|
|
47
|
+
capabilities: request.capabilities || {},
|
|
48
|
+
requirements: request.requirements || {},
|
|
49
|
+
options: request.options || {}
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
// 2. Validate through domain service (entity rules + domain rules + uniqueness)
|
|
53
|
+
const validation = await this.integrationValidator.validate(integration);
|
|
54
|
+
if (!validation.isValid) {
|
|
55
|
+
throw new ValidationException(validation.errors);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// 3. Save through repository (validates schema, writes files atomically)
|
|
59
|
+
await this.integrationRepository.save(integration);
|
|
60
|
+
|
|
61
|
+
// 4. Register in AppDefinition (if repository is available)
|
|
62
|
+
if (this.appDefinitionRepository) {
|
|
63
|
+
try {
|
|
64
|
+
const appDef = await this.appDefinitionRepository.load();
|
|
65
|
+
if (appDef) {
|
|
66
|
+
appDef.registerIntegration(integration.name.value);
|
|
67
|
+
await this.appDefinitionRepository.save(appDef);
|
|
68
|
+
}
|
|
69
|
+
} catch (error) {
|
|
70
|
+
// Log but don't fail - app definition might not exist yet
|
|
71
|
+
console.warn('Could not register integration in app definition:', error.message);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// 5. Register in backend.js (if updater is available)
|
|
76
|
+
if (this.backendJsUpdater) {
|
|
77
|
+
try {
|
|
78
|
+
if (await this.backendJsUpdater.exists()) {
|
|
79
|
+
await this.backendJsUpdater.registerIntegration(integration.name.value);
|
|
80
|
+
}
|
|
81
|
+
} catch (error) {
|
|
82
|
+
// Log but don't fail - backend.js might not exist or have different structure
|
|
83
|
+
console.warn('Could not register integration in backend.js:', error.message);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// 6. Commit transaction (cleanup backups)
|
|
88
|
+
await this.unitOfWork.commit();
|
|
89
|
+
|
|
90
|
+
return {
|
|
91
|
+
success: true,
|
|
92
|
+
integration: integration.toObject()
|
|
93
|
+
};
|
|
94
|
+
} catch (error) {
|
|
95
|
+
// Rollback all file operations on error
|
|
96
|
+
await this.unitOfWork.rollback();
|
|
97
|
+
|
|
98
|
+
throw error;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
module.exports = {CreateIntegrationUseCase};
|
|
@@ -1,7 +1,15 @@
|
|
|
1
1
|
const { spawnSync } = require('child_process');
|
|
2
2
|
const path = require('path');
|
|
3
|
+
const fs = require('fs');
|
|
3
4
|
|
|
4
5
|
async function buildCommand(options) {
|
|
6
|
+
// Check if the app uses a non-AWS provider
|
|
7
|
+
const providerResult = loadProviderIfConfigured();
|
|
8
|
+
if (providerResult) {
|
|
9
|
+
return buildWithProvider(providerResult, options);
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
// Default: AWS build via serverless framework
|
|
5
13
|
console.log('Building the serverless application...');
|
|
6
14
|
|
|
7
15
|
// Suppress AWS SDK warning message about maintenance mode
|
|
@@ -10,13 +18,13 @@ async function buildCommand(options) {
|
|
|
10
18
|
// Skip AWS discovery for local builds (unless --production flag is set)
|
|
11
19
|
if (!options.production) {
|
|
12
20
|
process.env.FRIGG_SKIP_AWS_DISCOVERY = 'true';
|
|
13
|
-
console.log('
|
|
21
|
+
console.log('Building in local mode (use --production flag for production builds with AWS discovery)');
|
|
14
22
|
} else {
|
|
15
|
-
console.log('
|
|
23
|
+
console.log('Building in production mode with AWS discovery enabled');
|
|
16
24
|
}
|
|
17
25
|
|
|
18
26
|
// AWS discovery is now handled directly in serverless-template.js
|
|
19
|
-
console.log('
|
|
27
|
+
console.log('Packaging serverless application...');
|
|
20
28
|
const backendPath = path.resolve(process.cwd());
|
|
21
29
|
const infrastructurePath = 'infrastructure.js';
|
|
22
30
|
const command = 'osls'; // OSS-Serverless (drop-in replacement for serverless v3)
|
|
@@ -51,16 +59,120 @@ async function buildCommand(options) {
|
|
|
51
59
|
console.error(`Serverless build failed with code ${result.status}`);
|
|
52
60
|
process.exit(1);
|
|
53
61
|
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Check if the appDefinition specifies a non-AWS provider and resolve it.
|
|
66
|
+
*/
|
|
67
|
+
function loadProviderIfConfigured() {
|
|
68
|
+
try {
|
|
69
|
+
const { loadProviderForCli } = require('../utils/provider-helper');
|
|
70
|
+
const result = loadProviderForCli();
|
|
71
|
+
if (result && result.provider) {
|
|
72
|
+
return result;
|
|
73
|
+
}
|
|
74
|
+
} catch {
|
|
75
|
+
// Provider helper not available or appDefinition not found — fall through
|
|
76
|
+
}
|
|
77
|
+
return null;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Build using a provider plugin.
|
|
82
|
+
* Provider build steps:
|
|
83
|
+
* 1. generateConfig() — generate platform-specific config (netlify.toml, etc.)
|
|
84
|
+
* 2. getFunctionEntryPoints() — copy function files to the project
|
|
85
|
+
*/
|
|
86
|
+
async function buildWithProvider({ provider, appDefinition, providerName }, options) {
|
|
87
|
+
console.log(`Building for ${providerName} provider...`);
|
|
88
|
+
const projectDir = path.resolve(process.cwd());
|
|
89
|
+
|
|
90
|
+
// 1. Validate
|
|
91
|
+
if (typeof provider.validate === 'function') {
|
|
92
|
+
const validation = provider.validate(appDefinition);
|
|
93
|
+
if (validation.errors?.length > 0) {
|
|
94
|
+
console.error(`\nValidation errors for ${providerName}:`);
|
|
95
|
+
for (const error of validation.errors) {
|
|
96
|
+
console.error(` - ${error}`);
|
|
97
|
+
}
|
|
98
|
+
process.exit(1);
|
|
99
|
+
}
|
|
100
|
+
if (validation.warnings?.length > 0) {
|
|
101
|
+
for (const warning of validation.warnings) {
|
|
102
|
+
console.warn(` Warning: ${warning}`);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// 2. Generate platform config (e.g., netlify.toml)
|
|
108
|
+
if (typeof provider.generateConfig === 'function') {
|
|
109
|
+
console.log(`Generating ${providerName} configuration...`);
|
|
110
|
+
const config = provider.generateConfig(appDefinition);
|
|
111
|
+
|
|
112
|
+
const configFileNames = {
|
|
113
|
+
netlify: 'netlify.toml',
|
|
114
|
+
};
|
|
115
|
+
const configFileName = configFileNames[providerName] || `${providerName}.config`;
|
|
116
|
+
const configPath = path.join(projectDir, configFileName);
|
|
117
|
+
|
|
118
|
+
fs.writeFileSync(configPath, config, 'utf-8');
|
|
119
|
+
console.log(` Written ${configFileName}`);
|
|
120
|
+
}
|
|
54
121
|
|
|
55
|
-
//
|
|
56
|
-
|
|
57
|
-
|
|
122
|
+
// 3. Copy function entry points
|
|
123
|
+
if (typeof provider.getFunctionEntryPoints === 'function') {
|
|
124
|
+
console.log('Generating function entry points...');
|
|
125
|
+
const entryPoints = provider.getFunctionEntryPoints(appDefinition);
|
|
126
|
+
const functionsDir = path.join(projectDir, 'netlify', 'functions');
|
|
127
|
+
|
|
128
|
+
fs.mkdirSync(functionsDir, { recursive: true });
|
|
129
|
+
|
|
130
|
+
for (const [filename, content] of Object.entries(entryPoints)) {
|
|
131
|
+
const filePath = path.join(functionsDir, filename);
|
|
132
|
+
fs.writeFileSync(filePath, content, 'utf-8');
|
|
133
|
+
if (options.verbose) {
|
|
134
|
+
console.log(` Written ${path.relative(projectDir, filePath)}`);
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
console.log(` Generated ${Object.keys(entryPoints).length} function entry points`);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// 4. Copy lib entry points (re-export shims for runtime dependencies)
|
|
142
|
+
if (typeof provider.getLibEntryPoints === 'function') {
|
|
143
|
+
const libEntryPoints = provider.getLibEntryPoints(appDefinition);
|
|
144
|
+
const libDir = path.join(projectDir, 'netlify', 'lib');
|
|
145
|
+
|
|
146
|
+
fs.mkdirSync(libDir, { recursive: true });
|
|
147
|
+
|
|
148
|
+
for (const [filename, content] of Object.entries(libEntryPoints)) {
|
|
149
|
+
const filePath = path.join(libDir, filename);
|
|
150
|
+
fs.writeFileSync(filePath, content, 'utf-8');
|
|
151
|
+
if (options.verbose) {
|
|
152
|
+
console.log(` Written ${path.relative(projectDir, filePath)}`);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
console.log(` Generated ${Object.keys(libEntryPoints).length} lib entry points`);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
// 5. Generate env template (informational)
|
|
160
|
+
if (typeof provider.generateEnvTemplate === 'function') {
|
|
161
|
+
const envTemplate = provider.generateEnvTemplate(appDefinition);
|
|
162
|
+
const missingEnvVars = Object.entries(envTemplate)
|
|
163
|
+
.filter(([key]) => !process.env[key])
|
|
164
|
+
.map(([key, desc]) => `${key}: ${desc}`);
|
|
165
|
+
|
|
166
|
+
if (missingEnvVars.length > 0) {
|
|
167
|
+
console.log(`\n Required environment variables not set locally:`);
|
|
168
|
+
for (const entry of missingEnvVars) {
|
|
169
|
+
console.log(` - ${entry}`);
|
|
170
|
+
}
|
|
171
|
+
console.log(` Configure these in your ${providerName} dashboard.`);
|
|
172
|
+
}
|
|
173
|
+
}
|
|
58
174
|
|
|
59
|
-
|
|
60
|
-
// if (code !== 0) {
|
|
61
|
-
// console.log(`Child process exited with code ${code}`);
|
|
62
|
-
// }
|
|
63
|
-
// });
|
|
175
|
+
console.log(`\nBuild complete for ${providerName}.`);
|
|
64
176
|
}
|
|
65
177
|
|
|
66
178
|
module.exports = { buildCommand };
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
const path = require('path');
|
|
2
|
+
const {findNearestBackendPackageJson} = require('./utils/backend-path');
|
|
3
|
+
|
|
4
|
+
// Infrastructure
|
|
5
|
+
const {FileSystemAdapter} = require('./infrastructure/adapters/FileSystemAdapter');
|
|
6
|
+
const {SchemaValidator} = require('./infrastructure/adapters/SchemaValidator');
|
|
7
|
+
const {BackendJsUpdater} = require('./infrastructure/adapters/BackendJsUpdater');
|
|
8
|
+
const {IntegrationJsUpdater} = require('./infrastructure/adapters/IntegrationJsUpdater');
|
|
9
|
+
const {FileSystemIntegrationRepository} = require('./infrastructure/repositories/FileSystemIntegrationRepository');
|
|
10
|
+
const {FileSystemAppDefinitionRepository} = require('./infrastructure/repositories/FileSystemAppDefinitionRepository');
|
|
11
|
+
const {FileSystemApiModuleRepository} = require('./infrastructure/repositories/FileSystemApiModuleRepository');
|
|
12
|
+
const {UnitOfWork} = require('./infrastructure/UnitOfWork');
|
|
13
|
+
|
|
14
|
+
// Domain Services
|
|
15
|
+
const {IntegrationValidator} = require('./domain/services/IntegrationValidator');
|
|
16
|
+
|
|
17
|
+
// Application
|
|
18
|
+
const {CreateIntegrationUseCase} = require('./application/use-cases/CreateIntegrationUseCase');
|
|
19
|
+
const {CreateApiModuleUseCase} = require('./application/use-cases/CreateApiModuleUseCase');
|
|
20
|
+
const {AddApiModuleToIntegrationUseCase} = require('./application/use-cases/AddApiModuleToIntegrationUseCase');
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Dependency Injection Container
|
|
24
|
+
* Manages object creation and dependency wiring
|
|
25
|
+
*/
|
|
26
|
+
class Container {
|
|
27
|
+
constructor(startDir = process.cwd()) {
|
|
28
|
+
// Find backend directory
|
|
29
|
+
this.backendPath = findNearestBackendPackageJson(startDir);
|
|
30
|
+
if (!this.backendPath) {
|
|
31
|
+
throw new Error('Could not find backend directory. Make sure you are in a Frigg project.');
|
|
32
|
+
}
|
|
33
|
+
this.projectRoot = path.dirname(this.backendPath); // For backwards compatibility
|
|
34
|
+
this.instances = new Map();
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Get or create singleton instance
|
|
39
|
+
*/
|
|
40
|
+
get(serviceName) {
|
|
41
|
+
if (this.instances.has(serviceName)) {
|
|
42
|
+
return this.instances.get(serviceName);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const instance = this._create(serviceName);
|
|
46
|
+
this.instances.set(serviceName, instance);
|
|
47
|
+
return instance;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Create service instance with dependencies
|
|
52
|
+
*/
|
|
53
|
+
_create(serviceName) {
|
|
54
|
+
switch (serviceName) {
|
|
55
|
+
// Infrastructure - Adapters
|
|
56
|
+
case 'FileSystemAdapter':
|
|
57
|
+
return new FileSystemAdapter();
|
|
58
|
+
|
|
59
|
+
case 'SchemaValidator':
|
|
60
|
+
// Point to schemas package in monorepo
|
|
61
|
+
// Schema validator should always use the schemas from the frigg monorepo,
|
|
62
|
+
// not relative to the user's project
|
|
63
|
+
const schemasPath = path.join(__dirname, '../../schemas/schemas');
|
|
64
|
+
return new SchemaValidator(schemasPath);
|
|
65
|
+
|
|
66
|
+
case 'BackendJsUpdater':
|
|
67
|
+
return new BackendJsUpdater(
|
|
68
|
+
this.get('FileSystemAdapter'),
|
|
69
|
+
this.backendPath
|
|
70
|
+
);
|
|
71
|
+
|
|
72
|
+
case 'IntegrationJsUpdater':
|
|
73
|
+
return new IntegrationJsUpdater(
|
|
74
|
+
this.get('FileSystemAdapter'),
|
|
75
|
+
this.backendPath
|
|
76
|
+
);
|
|
77
|
+
|
|
78
|
+
// Infrastructure - Repositories
|
|
79
|
+
case 'IntegrationRepository':
|
|
80
|
+
return new FileSystemIntegrationRepository(
|
|
81
|
+
this.get('FileSystemAdapter'),
|
|
82
|
+
this.backendPath,
|
|
83
|
+
this.get('SchemaValidator')
|
|
84
|
+
);
|
|
85
|
+
|
|
86
|
+
case 'AppDefinitionRepository':
|
|
87
|
+
return new FileSystemAppDefinitionRepository(
|
|
88
|
+
this.get('FileSystemAdapter'),
|
|
89
|
+
this.backendPath,
|
|
90
|
+
this.get('SchemaValidator')
|
|
91
|
+
);
|
|
92
|
+
|
|
93
|
+
case 'ApiModuleRepository':
|
|
94
|
+
return new FileSystemApiModuleRepository(
|
|
95
|
+
this.get('FileSystemAdapter'),
|
|
96
|
+
this.backendPath,
|
|
97
|
+
this.get('SchemaValidator')
|
|
98
|
+
);
|
|
99
|
+
|
|
100
|
+
// Infrastructure - Unit of Work
|
|
101
|
+
case 'UnitOfWork':
|
|
102
|
+
return new UnitOfWork(
|
|
103
|
+
this.get('FileSystemAdapter')
|
|
104
|
+
);
|
|
105
|
+
|
|
106
|
+
// Domain Services
|
|
107
|
+
case 'IntegrationValidator':
|
|
108
|
+
return new IntegrationValidator(
|
|
109
|
+
this.get('IntegrationRepository')
|
|
110
|
+
);
|
|
111
|
+
|
|
112
|
+
// Application - Use Cases
|
|
113
|
+
case 'CreateIntegrationUseCase':
|
|
114
|
+
return new CreateIntegrationUseCase(
|
|
115
|
+
this.get('IntegrationRepository'),
|
|
116
|
+
this.get('UnitOfWork'),
|
|
117
|
+
this.get('IntegrationValidator'),
|
|
118
|
+
this.get('AppDefinitionRepository'),
|
|
119
|
+
this.get('BackendJsUpdater')
|
|
120
|
+
);
|
|
121
|
+
|
|
122
|
+
case 'CreateApiModuleUseCase':
|
|
123
|
+
return new CreateApiModuleUseCase(
|
|
124
|
+
this.get('ApiModuleRepository'),
|
|
125
|
+
this.get('UnitOfWork'),
|
|
126
|
+
this.get('AppDefinitionRepository')
|
|
127
|
+
);
|
|
128
|
+
|
|
129
|
+
case 'AddApiModuleToIntegrationUseCase':
|
|
130
|
+
return new AddApiModuleToIntegrationUseCase(
|
|
131
|
+
this.get('IntegrationRepository'),
|
|
132
|
+
this.get('ApiModuleRepository'),
|
|
133
|
+
this.get('UnitOfWork'),
|
|
134
|
+
this.get('IntegrationValidator'),
|
|
135
|
+
this.get('IntegrationJsUpdater')
|
|
136
|
+
);
|
|
137
|
+
|
|
138
|
+
default:
|
|
139
|
+
throw new Error(`Unknown service: ${serviceName}`);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Clear all instances (useful for testing)
|
|
145
|
+
*/
|
|
146
|
+
clear() {
|
|
147
|
+
this.instances.clear();
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* Set project root directory
|
|
152
|
+
*/
|
|
153
|
+
setProjectRoot(projectRoot) {
|
|
154
|
+
this.projectRoot = projectRoot;
|
|
155
|
+
this.clear(); // Clear cached instances
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
// Export singleton container
|
|
160
|
+
let containerInstance = null;
|
|
161
|
+
|
|
162
|
+
module.exports = {
|
|
163
|
+
Container,
|
|
164
|
+
getContainer: (projectRoot) => {
|
|
165
|
+
if (!containerInstance) {
|
|
166
|
+
containerInstance = new Container(projectRoot);
|
|
167
|
+
} else if (projectRoot) {
|
|
168
|
+
containerInstance.setProjectRoot(projectRoot);
|
|
169
|
+
}
|
|
170
|
+
return containerInstance;
|
|
171
|
+
}
|
|
172
|
+
};
|