@friggframework/devtools 2.0.0--canary.545.e256e95.0 → 2.0.0--canary.553.dc5f898.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__/unit/commands/build.test.js +1 -1
- package/frigg-cli/__tests__/unit/commands/doctor.test.js +2 -0
- package/frigg-cli/__tests__/unit/commands/install.test.js +19 -23
- package/frigg-cli/__tests__/unit/dependencies.test.js +2 -2
- package/frigg-cli/build-command/index.js +11 -123
- package/frigg-cli/deploy-command/index.js +1 -83
- package/frigg-cli/doctor-command/index.js +16 -37
- package/frigg-cli/generate-iam-command.js +1 -21
- package/frigg-cli/index.js +6 -21
- package/frigg-cli/index.test.js +2 -7
- package/frigg-cli/init-command/backend-first-handler.js +42 -124
- package/frigg-cli/init-command/index.js +1 -2
- package/frigg-cli/init-command/template-handler.js +3 -13
- package/frigg-cli/install-command/backend-js.js +3 -3
- package/frigg-cli/install-command/environment-variables.js +19 -16
- package/frigg-cli/install-command/environment-variables.test.js +13 -12
- package/frigg-cli/install-command/index.js +9 -14
- package/frigg-cli/install-command/integration-file.js +3 -3
- package/frigg-cli/install-command/logger.js +12 -0
- package/frigg-cli/install-command/validate-package.js +9 -5
- package/frigg-cli/jest.config.js +1 -4
- package/frigg-cli/repair-command/index.js +128 -121
- package/frigg-cli/start-command/index.js +2 -324
- package/frigg-cli/ui-command/index.js +36 -58
- package/frigg-cli/utils/repo-detection.js +37 -85
- package/infrastructure/create-frigg-infrastructure.js +0 -93
- package/infrastructure/docs/iam-policy-templates.md +1 -1
- package/infrastructure/domains/integration/integration-builder.js +3 -2
- package/infrastructure/domains/integration/integration-builder.test.js +54 -2
- package/infrastructure/domains/networking/vpc-builder.test.js +4 -2
- 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 +0 -35
- package/infrastructure/domains/shared/types/discovery-result.test.js +1 -1
- package/infrastructure/domains/shared/utilities/base-definition-factory.js +1 -10
- package/infrastructure/domains/shared/utilities/base-definition-factory.test.js +2 -2
- package/infrastructure/infrastructure-composer.js +0 -2
- package/infrastructure/infrastructure-composer.test.js +6 -5
- package/management-ui/README.md +109 -245
- package/package.json +7 -8
- package/frigg-cli/__tests__/application/use-cases/AddApiModuleToIntegrationUseCase.test.js +0 -326
- package/frigg-cli/__tests__/application/use-cases/CreateApiModuleUseCase.test.js +0 -337
- package/frigg-cli/__tests__/domain/entities/ApiModule.test.js +0 -373
- package/frigg-cli/__tests__/domain/entities/AppDefinition.test.js +0 -313
- package/frigg-cli/__tests__/domain/services/IntegrationValidator.test.js +0 -269
- package/frigg-cli/__tests__/domain/value-objects/IntegrationName.test.js +0 -82
- package/frigg-cli/__tests__/infrastructure/adapters/IntegrationJsUpdater.test.js +0 -408
- package/frigg-cli/__tests__/infrastructure/repositories/FileSystemApiModuleRepository.test.js +0 -583
- package/frigg-cli/__tests__/infrastructure/repositories/FileSystemAppDefinitionRepository.test.js +0 -314
- package/frigg-cli/__tests__/infrastructure/repositories/FileSystemIntegrationRepository.test.js +0 -383
- package/frigg-cli/__tests__/unit/commands/init.test.js +0 -406
- package/frigg-cli/__tests__/unit/commands/provider-dispatch.test.js +0 -383
- package/frigg-cli/__tests__/unit/commands/repair.test.js +0 -275
- package/frigg-cli/__tests__/unit/start-command/application/RunPreflightChecksUseCase.test.js +0 -411
- package/frigg-cli/__tests__/unit/start-command/infrastructure/DatabaseAdapter.test.js +0 -405
- package/frigg-cli/__tests__/unit/start-command/infrastructure/DockerAdapter.test.js +0 -496
- package/frigg-cli/__tests__/unit/start-command/presentation/InteractivePromptAdapter.test.js +0 -474
- package/frigg-cli/__tests__/unit/utils/output.test.js +0 -196
- package/frigg-cli/application/use-cases/AddApiModuleToIntegrationUseCase.js +0 -93
- package/frigg-cli/application/use-cases/CreateApiModuleUseCase.js +0 -93
- package/frigg-cli/application/use-cases/CreateIntegrationUseCase.js +0 -103
- package/frigg-cli/container.js +0 -172
- package/frigg-cli/docs/OUTPUT_MIGRATION_GUIDE.md +0 -286
- package/frigg-cli/domain/entities/ApiModule.js +0 -272
- package/frigg-cli/domain/entities/AppDefinition.js +0 -227
- package/frigg-cli/domain/entities/Integration.js +0 -198
- package/frigg-cli/domain/exceptions/DomainException.js +0 -24
- package/frigg-cli/domain/ports/IApiModuleRepository.js +0 -53
- package/frigg-cli/domain/ports/IAppDefinitionRepository.js +0 -43
- package/frigg-cli/domain/ports/IIntegrationRepository.js +0 -61
- package/frigg-cli/domain/services/IntegrationValidator.js +0 -185
- package/frigg-cli/domain/value-objects/IntegrationId.js +0 -42
- package/frigg-cli/domain/value-objects/IntegrationName.js +0 -60
- package/frigg-cli/domain/value-objects/SemanticVersion.js +0 -70
- package/frigg-cli/infrastructure/UnitOfWork.js +0 -46
- package/frigg-cli/infrastructure/adapters/BackendJsUpdater.js +0 -197
- package/frigg-cli/infrastructure/adapters/FileSystemAdapter.js +0 -224
- package/frigg-cli/infrastructure/adapters/IntegrationJsUpdater.js +0 -249
- package/frigg-cli/infrastructure/adapters/SchemaValidator.js +0 -92
- package/frigg-cli/infrastructure/repositories/FileSystemApiModuleRepository.js +0 -373
- package/frigg-cli/infrastructure/repositories/FileSystemAppDefinitionRepository.js +0 -116
- package/frigg-cli/infrastructure/repositories/FileSystemIntegrationRepository.js +0 -277
- package/frigg-cli/package-lock.json +0 -16226
- package/frigg-cli/start-command/application/RunPreflightChecksUseCase.js +0 -376
- package/frigg-cli/start-command/infrastructure/DatabaseAdapter.js +0 -591
- package/frigg-cli/start-command/infrastructure/DockerAdapter.js +0 -306
- package/frigg-cli/start-command/presentation/InteractivePromptAdapter.js +0 -329
- package/frigg-cli/templates/backend/.env.example +0 -62
- package/frigg-cli/templates/backend/.eslintrc.json +0 -12
- package/frigg-cli/templates/backend/.prettierrc +0 -6
- package/frigg-cli/templates/backend/docker-compose.yml +0 -22
- package/frigg-cli/templates/backend/index.js +0 -96
- package/frigg-cli/templates/backend/infrastructure.js +0 -12
- package/frigg-cli/templates/backend/jest.config.js +0 -17
- package/frigg-cli/templates/backend/package.json +0 -50
- package/frigg-cli/templates/backend/src/api-modules/.gitkeep +0 -10
- package/frigg-cli/templates/backend/src/base/.gitkeep +0 -7
- package/frigg-cli/templates/backend/src/integrations/.gitkeep +0 -10
- package/frigg-cli/templates/backend/src/integrations/ExampleIntegration.js +0 -65
- package/frigg-cli/templates/backend/src/utils/.gitkeep +0 -7
- package/frigg-cli/templates/backend/test/setup.js +0 -30
- package/frigg-cli/templates/backend/ui-extensions/.gitkeep +0 -0
- package/frigg-cli/templates/backend/ui-extensions/README.md +0 -77
- package/frigg-cli/utils/__tests__/provider-helper.test.js +0 -55
- package/frigg-cli/utils/__tests__/repo-detection.test.js +0 -436
- package/frigg-cli/utils/output.js +0 -382
- package/frigg-cli/utils/provider-helper.js +0 -75
- package/frigg-cli/validate-command/__tests__/adapters/validate-command.test.js +0 -205
- package/frigg-cli/validate-command/__tests__/application/validate-app-use-case.test.js +0 -104
- package/frigg-cli/validate-command/__tests__/domain/fix-suggestion.test.js +0 -153
- package/frigg-cli/validate-command/__tests__/domain/validation-error.test.js +0 -162
- package/frigg-cli/validate-command/__tests__/domain/validation-result.test.js +0 -152
- package/frigg-cli/validate-command/__tests__/infrastructure/api-module-validator.test.js +0 -332
- package/frigg-cli/validate-command/__tests__/infrastructure/app-definition-validator.test.js +0 -191
- package/frigg-cli/validate-command/__tests__/infrastructure/integration-class-validator.test.js +0 -146
- package/frigg-cli/validate-command/__tests__/infrastructure/template-validation.test.js +0 -155
- package/frigg-cli/validate-command/adapters/cli/validate-command.js +0 -199
- package/frigg-cli/validate-command/application/use-cases/validate-app-use-case.js +0 -35
- package/frigg-cli/validate-command/domain/entities/validation-result.js +0 -74
- package/frigg-cli/validate-command/domain/value-objects/fix-suggestion.js +0 -74
- package/frigg-cli/validate-command/domain/value-objects/validation-error.js +0 -68
- package/frigg-cli/validate-command/infrastructure/validators/api-module-validator.js +0 -181
- package/frigg-cli/validate-command/infrastructure/validators/app-definition-validator.js +0 -145
- package/frigg-cli/validate-command/infrastructure/validators/integration-class-validator.js +0 -113
- package/infrastructure/domains/admin-scripts/admin-script-builder.js +0 -200
- package/infrastructure/domains/admin-scripts/admin-script-builder.test.js +0 -499
- package/infrastructure/domains/admin-scripts/index.js +0 -5
- package/infrastructure/jest.config.js +0 -16
|
@@ -1,185 +0,0 @@
|
|
|
1
|
-
const {DomainException, ValidationException} = require('../exceptions/DomainException');
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* IntegrationValidator Domain Service
|
|
5
|
-
*
|
|
6
|
-
* Centralizes validation logic that involves multiple entities or external checks
|
|
7
|
-
* Complements the entity's self-validation by handling cross-cutting concerns
|
|
8
|
-
*/
|
|
9
|
-
class IntegrationValidator {
|
|
10
|
-
constructor(integrationRepository) {
|
|
11
|
-
this.integrationRepository = integrationRepository;
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
/**
|
|
15
|
-
* Validate that integration name is unique
|
|
16
|
-
* @param {IntegrationName} name - Integration name to check
|
|
17
|
-
* @returns {Promise<{isValid: boolean, errors: string[]}>}
|
|
18
|
-
*/
|
|
19
|
-
async validateUniqueness(name) {
|
|
20
|
-
const exists = await this.integrationRepository.exists(name);
|
|
21
|
-
|
|
22
|
-
if (exists) {
|
|
23
|
-
return {
|
|
24
|
-
isValid: false,
|
|
25
|
-
errors: [`Integration with name '${name.value}' already exists`]
|
|
26
|
-
};
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
return {
|
|
30
|
-
isValid: true,
|
|
31
|
-
errors: []
|
|
32
|
-
};
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
/**
|
|
36
|
-
* Validate integration against business rules
|
|
37
|
-
* Combines entity validation with domain-level rules
|
|
38
|
-
*
|
|
39
|
-
* @param {Integration} integration - Integration entity to validate
|
|
40
|
-
* @returns {Promise<{isValid: boolean, errors: string[]}>}
|
|
41
|
-
*/
|
|
42
|
-
async validate(integration) {
|
|
43
|
-
const errors = [];
|
|
44
|
-
|
|
45
|
-
// 1. Entity self-validation
|
|
46
|
-
const entityValidation = integration.validate();
|
|
47
|
-
if (!entityValidation.isValid) {
|
|
48
|
-
errors.push(...entityValidation.errors);
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
// 2. Uniqueness check
|
|
52
|
-
const uniquenessValidation = await this.validateUniqueness(integration.name);
|
|
53
|
-
if (!uniquenessValidation.isValid) {
|
|
54
|
-
errors.push(...uniquenessValidation.errors);
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
// 3. Additional domain rules
|
|
58
|
-
const domainRules = this.validateDomainRules(integration);
|
|
59
|
-
if (!domainRules.isValid) {
|
|
60
|
-
errors.push(...domainRules.errors);
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
return {
|
|
64
|
-
isValid: errors.length === 0,
|
|
65
|
-
errors
|
|
66
|
-
};
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
/**
|
|
70
|
-
* Validate domain-specific business rules
|
|
71
|
-
* These are rules that apply across the domain, not just to one entity
|
|
72
|
-
*
|
|
73
|
-
* @param {Integration} integration
|
|
74
|
-
* @returns {{isValid: boolean, errors: string[]}}
|
|
75
|
-
*/
|
|
76
|
-
validateDomainRules(integration) {
|
|
77
|
-
const errors = [];
|
|
78
|
-
|
|
79
|
-
// Rule: Webhook integrations must have webhook capability
|
|
80
|
-
if (integration.type === 'webhook' && !integration.capabilities.webhooks) {
|
|
81
|
-
errors.push('Webhook integrations must have webhooks capability enabled');
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
// Rule: Sync integrations should have bidirectional capability
|
|
85
|
-
if (integration.type === 'sync' && integration.capabilities.sync && !integration.capabilities.sync.bidirectional) {
|
|
86
|
-
// This is a warning, not an error - sync can be unidirectional
|
|
87
|
-
// But we'll log it for the developer's awareness
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
// Rule: OAuth2 integrations must have auth capability
|
|
91
|
-
if (integration.capabilities.auth && integration.capabilities.auth.includes('oauth2')) {
|
|
92
|
-
// This is good - OAuth2 should be in auth array
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
// Rule: Integrations with realtime capability should have websocket requirements
|
|
96
|
-
if (integration.capabilities.realtime) {
|
|
97
|
-
if (!integration.requirements || !integration.requirements.websocket) {
|
|
98
|
-
// Warn but don't fail - they might add it later
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
// Rule: Integration should have at least one entity or be marked as entityless
|
|
103
|
-
if (Object.keys(integration.entities).length === 0) {
|
|
104
|
-
// This is unusual but not invalid - might be a transform-only integration
|
|
105
|
-
// We don't add an error, just note it
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
return {
|
|
109
|
-
isValid: errors.length === 0,
|
|
110
|
-
errors
|
|
111
|
-
};
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
/**
|
|
115
|
-
* Validate integration configuration before update
|
|
116
|
-
* Ensures updates don't violate domain rules
|
|
117
|
-
*
|
|
118
|
-
* @param {Integration} existingIntegration
|
|
119
|
-
* @param {Integration} updatedIntegration
|
|
120
|
-
* @returns {{isValid: boolean, errors: string[]}}
|
|
121
|
-
*/
|
|
122
|
-
validateUpdate(existingIntegration, updatedIntegration) {
|
|
123
|
-
const errors = [];
|
|
124
|
-
|
|
125
|
-
// Rule: Cannot change integration name
|
|
126
|
-
if (!existingIntegration.name.equals(updatedIntegration.name)) {
|
|
127
|
-
errors.push('Integration name cannot be changed after creation');
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
// Rule: Version must be incremented, not decremented
|
|
131
|
-
if (existingIntegration.version.isGreaterThan(updatedIntegration.version)) {
|
|
132
|
-
errors.push('Cannot downgrade integration version');
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
// Rule: Cannot remove entities that have existing data
|
|
136
|
-
// (This would require checking with a data repository in real implementation)
|
|
137
|
-
const removedEntities = Object.keys(existingIntegration.entities)
|
|
138
|
-
.filter(key => !updatedIntegration.entities[key]);
|
|
139
|
-
|
|
140
|
-
if (removedEntities.length > 0) {
|
|
141
|
-
errors.push(`Cannot remove entities with potential existing data: ${removedEntities.join(', ')}`);
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
return {
|
|
145
|
-
isValid: errors.length === 0,
|
|
146
|
-
errors
|
|
147
|
-
};
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
/**
|
|
151
|
-
* Validate API module addition
|
|
152
|
-
* Ensures API module can be safely added to integration
|
|
153
|
-
*
|
|
154
|
-
* @param {Integration} integration
|
|
155
|
-
* @param {string} moduleName
|
|
156
|
-
* @param {string} moduleVersion
|
|
157
|
-
* @returns {{isValid: boolean, errors: string[]}}
|
|
158
|
-
*/
|
|
159
|
-
validateApiModuleAddition(integration, moduleName, moduleVersion) {
|
|
160
|
-
const errors = [];
|
|
161
|
-
|
|
162
|
-
// Check if module already exists
|
|
163
|
-
if (integration.hasApiModule(moduleName)) {
|
|
164
|
-
errors.push(`API module '${moduleName}' is already added to this integration`);
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
// Validate module name format
|
|
168
|
-
if (!moduleName || moduleName.trim().length === 0) {
|
|
169
|
-
errors.push('API module name is required');
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
// Validate version format (should be semantic version)
|
|
173
|
-
const versionPattern = /^\d+\.\d+\.\d+(-[a-zA-Z0-9.-]+)?$/;
|
|
174
|
-
if (!versionPattern.test(moduleVersion)) {
|
|
175
|
-
errors.push(`Invalid API module version format: ${moduleVersion}. Must be semantic version (e.g., 1.0.0)`);
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
return {
|
|
179
|
-
isValid: errors.length === 0,
|
|
180
|
-
errors
|
|
181
|
-
};
|
|
182
|
-
}
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
module.exports = {IntegrationValidator};
|
|
@@ -1,42 +0,0 @@
|
|
|
1
|
-
const {DomainException} = require('../exceptions/DomainException');
|
|
2
|
-
const crypto = require('crypto');
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* IntegrationId Value Object
|
|
6
|
-
* Unique identifier for integrations
|
|
7
|
-
*/
|
|
8
|
-
class IntegrationId {
|
|
9
|
-
constructor(value) {
|
|
10
|
-
if (value) {
|
|
11
|
-
// Use provided ID
|
|
12
|
-
if (typeof value !== 'string' || value.length === 0) {
|
|
13
|
-
throw new DomainException('Integration ID must be a non-empty string');
|
|
14
|
-
}
|
|
15
|
-
this._value = value;
|
|
16
|
-
} else {
|
|
17
|
-
// Generate new ID
|
|
18
|
-
this._value = crypto.randomUUID();
|
|
19
|
-
}
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
get value() {
|
|
23
|
-
return this._value;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
equals(other) {
|
|
27
|
-
if (!(other instanceof IntegrationId)) {
|
|
28
|
-
return false;
|
|
29
|
-
}
|
|
30
|
-
return this._value === other._value;
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
toString() {
|
|
34
|
-
return this._value;
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
static generate() {
|
|
38
|
-
return new IntegrationId();
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
module.exports = {IntegrationId};
|
|
@@ -1,60 +0,0 @@
|
|
|
1
|
-
const {DomainException} = require('../exceptions/DomainException');
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* IntegrationName Value Object
|
|
5
|
-
* Ensures integration names follow kebab-case format
|
|
6
|
-
*/
|
|
7
|
-
class IntegrationName {
|
|
8
|
-
constructor(value) {
|
|
9
|
-
if (!value || typeof value !== 'string') {
|
|
10
|
-
throw new DomainException('Integration name must be a non-empty string');
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
this._value = value;
|
|
14
|
-
this._validate();
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
_validate() {
|
|
18
|
-
const rules = [
|
|
19
|
-
{
|
|
20
|
-
test: () => /^[a-z0-9][a-z0-9-]*[a-z0-9]$/.test(this._value),
|
|
21
|
-
message: 'Name must be kebab-case (lowercase letters, numbers, and hyphens only)'
|
|
22
|
-
},
|
|
23
|
-
{
|
|
24
|
-
test: () => this._value.length >= 2 && this._value.length <= 100,
|
|
25
|
-
message: 'Name must be between 2 and 100 characters'
|
|
26
|
-
},
|
|
27
|
-
{
|
|
28
|
-
test: () => !this._value.startsWith('-') && !this._value.endsWith('-'),
|
|
29
|
-
message: 'Name cannot start or end with a hyphen'
|
|
30
|
-
},
|
|
31
|
-
{
|
|
32
|
-
test: () => !this._value.includes('--'),
|
|
33
|
-
message: 'Name cannot contain consecutive hyphens'
|
|
34
|
-
}
|
|
35
|
-
];
|
|
36
|
-
|
|
37
|
-
for (const rule of rules) {
|
|
38
|
-
if (!rule.test()) {
|
|
39
|
-
throw new DomainException(rule.message);
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
get value() {
|
|
45
|
-
return this._value;
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
equals(other) {
|
|
49
|
-
if (!(other instanceof IntegrationName)) {
|
|
50
|
-
return false;
|
|
51
|
-
}
|
|
52
|
-
return this._value === other._value;
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
toString() {
|
|
56
|
-
return this._value;
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
module.exports = {IntegrationName};
|
|
@@ -1,70 +0,0 @@
|
|
|
1
|
-
const {DomainException} = require('../exceptions/DomainException');
|
|
2
|
-
const semver = require('semver');
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* SemanticVersion Value Object
|
|
6
|
-
* Ensures versions follow semantic versioning
|
|
7
|
-
*/
|
|
8
|
-
class SemanticVersion {
|
|
9
|
-
constructor(value) {
|
|
10
|
-
if (!value || typeof value !== 'string') {
|
|
11
|
-
throw new DomainException('Version must be a non-empty string');
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
if (!semver.valid(value)) {
|
|
15
|
-
throw new DomainException(
|
|
16
|
-
`Invalid semantic version: ${value}. Must follow format X.Y.Z (e.g., 1.0.0)`
|
|
17
|
-
);
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
this._value = value;
|
|
21
|
-
this._parsed = semver.parse(value);
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
get value() {
|
|
25
|
-
return this._value;
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
get major() {
|
|
29
|
-
return this._parsed.major;
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
get minor() {
|
|
33
|
-
return this._parsed.minor;
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
get patch() {
|
|
37
|
-
return this._parsed.patch;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
get prerelease() {
|
|
41
|
-
return this._parsed.prerelease;
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
equals(other) {
|
|
45
|
-
if (!(other instanceof SemanticVersion)) {
|
|
46
|
-
return false;
|
|
47
|
-
}
|
|
48
|
-
return this._value === other._value;
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
isGreaterThan(other) {
|
|
52
|
-
if (!(other instanceof SemanticVersion)) {
|
|
53
|
-
throw new DomainException('Can only compare with another SemanticVersion');
|
|
54
|
-
}
|
|
55
|
-
return semver.gt(this._value, other._value);
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
isLessThan(other) {
|
|
59
|
-
if (!(other instanceof SemanticVersion)) {
|
|
60
|
-
throw new DomainException('Can only compare with another SemanticVersion');
|
|
61
|
-
}
|
|
62
|
-
return semver.lt(this._value, other._value);
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
toString() {
|
|
66
|
-
return this._value;
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
module.exports = {SemanticVersion};
|
|
@@ -1,46 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* UnitOfWork
|
|
3
|
-
* Coordinates transactions across repositories
|
|
4
|
-
*/
|
|
5
|
-
class UnitOfWork {
|
|
6
|
-
constructor(fileSystemAdapter) {
|
|
7
|
-
this.fileSystemAdapter = fileSystemAdapter;
|
|
8
|
-
this.repositories = new Map();
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
/**
|
|
12
|
-
* Register a repository
|
|
13
|
-
*/
|
|
14
|
-
registerRepository(name, repository) {
|
|
15
|
-
this.repositories.set(name, repository);
|
|
16
|
-
return this;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
/**
|
|
20
|
-
* Commit all tracked operations
|
|
21
|
-
*/
|
|
22
|
-
async commit() {
|
|
23
|
-
try {
|
|
24
|
-
await this.fileSystemAdapter.commit();
|
|
25
|
-
return {success: true};
|
|
26
|
-
} catch (error) {
|
|
27
|
-
throw new Error(`Failed to commit transaction: ${error.message}`);
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
/**
|
|
32
|
-
* Rollback all tracked operations
|
|
33
|
-
*/
|
|
34
|
-
async rollback() {
|
|
35
|
-
return await this.fileSystemAdapter.rollback();
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
/**
|
|
39
|
-
* Clear tracked operations without commit/rollback
|
|
40
|
-
*/
|
|
41
|
-
clear() {
|
|
42
|
-
this.fileSystemAdapter.clear();
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
module.exports = {UnitOfWork};
|
|
@@ -1,197 +0,0 @@
|
|
|
1
|
-
const path = require('path');
|
|
2
|
-
const fs = require('fs-extra');
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* BackendJsUpdater
|
|
6
|
-
*
|
|
7
|
-
* Infrastructure service for updating backend.js file with new integrations
|
|
8
|
-
* Uses AST manipulation to safely add integration imports and registrations
|
|
9
|
-
*/
|
|
10
|
-
class BackendJsUpdater {
|
|
11
|
-
constructor(fileSystemAdapter, backendPath) {
|
|
12
|
-
this.fileSystemAdapter = fileSystemAdapter;
|
|
13
|
-
this.backendPath = backendPath;
|
|
14
|
-
this.indexJsPath = path.join(backendPath, 'index.js');
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
/**
|
|
18
|
-
* Register an integration in backend/index.js
|
|
19
|
-
* @param {string} integrationName - kebab-case integration name
|
|
20
|
-
* @returns {Promise<void>}
|
|
21
|
-
*/
|
|
22
|
-
async registerIntegration(integrationName) {
|
|
23
|
-
if (!await this.fileSystemAdapter.exists(this.indexJsPath)) {
|
|
24
|
-
throw new Error('backend/index.js not found');
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
const className = this._toClassName(integrationName);
|
|
28
|
-
const importPath = `./src/integrations/${className}Integration`;
|
|
29
|
-
|
|
30
|
-
await this.fileSystemAdapter.updateFile(this.indexJsPath, (content) => {
|
|
31
|
-
return this._addIntegration(content, className, integrationName, importPath);
|
|
32
|
-
});
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
/**
|
|
36
|
-
* Remove an integration from backend/index.js
|
|
37
|
-
* @param {string} integrationName
|
|
38
|
-
* @returns {Promise<void>}
|
|
39
|
-
*/
|
|
40
|
-
async unregisterIntegration(integrationName) {
|
|
41
|
-
if (!await this.fileSystemAdapter.exists(this.indexJsPath)) {
|
|
42
|
-
throw new Error('backend/index.js not found');
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
const className = this._toClassName(integrationName);
|
|
46
|
-
|
|
47
|
-
await this.fileSystemAdapter.updateFile(this.indexJsPath, (content) => {
|
|
48
|
-
return this._removeIntegration(content, className, integrationName);
|
|
49
|
-
});
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
/**
|
|
53
|
-
* Add integration to backend.js content
|
|
54
|
-
* Simple string manipulation approach (can be replaced with AST parsing if needed)
|
|
55
|
-
*
|
|
56
|
-
* @param {string} content - Current backend.js content
|
|
57
|
-
* @param {string} className - Integration class name
|
|
58
|
-
* @param {string} integrationName - kebab-case name
|
|
59
|
-
* @param {string} importPath - relative import path
|
|
60
|
-
* @returns {string} - Updated content
|
|
61
|
-
*/
|
|
62
|
-
_addIntegration(content, className, integrationName, importPath) {
|
|
63
|
-
// Check if integration is already registered
|
|
64
|
-
if (content.includes(`const ${className}Integration`)) {
|
|
65
|
-
console.warn(`Integration ${integrationName} is already registered in backend.js`);
|
|
66
|
-
return content;
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
let updated = content;
|
|
70
|
-
|
|
71
|
-
// 1. Add import statement after other integration imports
|
|
72
|
-
const importRegex = /(const \w+Integration = require\('\.\/src\/integrations\/[^']+'\);)/g;
|
|
73
|
-
const importMatches = [...content.matchAll(importRegex)];
|
|
74
|
-
|
|
75
|
-
if (importMatches.length > 0) {
|
|
76
|
-
// Add after last integration import
|
|
77
|
-
const lastImport = importMatches[importMatches.length - 1];
|
|
78
|
-
const insertIndex = lastImport.index + lastImport[0].length;
|
|
79
|
-
const importStatement = `\nconst ${className}Integration = require('${importPath}');`;
|
|
80
|
-
updated = updated.slice(0, insertIndex) + importStatement + updated.slice(insertIndex);
|
|
81
|
-
} else {
|
|
82
|
-
// No existing imports - add at the top after requires
|
|
83
|
-
const requiresRegex = /const .+ = require\([^)]+\);/g;
|
|
84
|
-
const requireMatches = [...content.matchAll(requiresRegex)];
|
|
85
|
-
if (requireMatches.length > 0) {
|
|
86
|
-
const lastRequire = requireMatches[requireMatches.length - 1];
|
|
87
|
-
const insertIndex = lastRequire.index + lastRequire[0].length;
|
|
88
|
-
const importStatement = `\n\n// Integrations\nconst ${className}Integration = require('${importPath}');`;
|
|
89
|
-
updated = updated.slice(0, insertIndex) + importStatement + updated.slice(insertIndex);
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
// 2. Add to integrations array
|
|
94
|
-
// Look for patterns:
|
|
95
|
-
// - const integrations = [...]
|
|
96
|
-
// - integrations: [...] (inside appDefinition object)
|
|
97
|
-
|
|
98
|
-
// Try standalone array first
|
|
99
|
-
const standaloneArrayRegex = /const integrations = \[([\s\S]*?)\];/;
|
|
100
|
-
let match = updated.match(standaloneArrayRegex);
|
|
101
|
-
|
|
102
|
-
if (match) {
|
|
103
|
-
const currentArray = match[1];
|
|
104
|
-
const newEntry = `\n ${className}Integration,`;
|
|
105
|
-
|
|
106
|
-
// Check if it's an empty array
|
|
107
|
-
if (currentArray.trim() === '') {
|
|
108
|
-
updated = updated.replace(standaloneArrayRegex, `const integrations = [${newEntry}\n];`);
|
|
109
|
-
} else {
|
|
110
|
-
// Add to existing array
|
|
111
|
-
const insertAt = match.index + match[0].length - 2; // Before ];
|
|
112
|
-
updated = updated.slice(0, insertAt) + ',' + newEntry + updated.slice(insertAt);
|
|
113
|
-
}
|
|
114
|
-
} else {
|
|
115
|
-
// Try appDefinition pattern
|
|
116
|
-
const appDefArrayRegex = /integrations:\s*\[([\s\S]*?)\]/;
|
|
117
|
-
match = updated.match(appDefArrayRegex);
|
|
118
|
-
|
|
119
|
-
if (match) {
|
|
120
|
-
const currentArray = match[1];
|
|
121
|
-
const newEntry = `\n ${className}Integration,`;
|
|
122
|
-
|
|
123
|
-
// Check if array is empty or has only comments
|
|
124
|
-
const hasOnlyComments = currentArray.trim() === '' ||
|
|
125
|
-
currentArray.trim().split('\n').every(line => line.trim().startsWith('//'));
|
|
126
|
-
|
|
127
|
-
if (hasOnlyComments) {
|
|
128
|
-
// Replace entire array content
|
|
129
|
-
updated = updated.replace(appDefArrayRegex, `integrations: [${newEntry}\n ]`);
|
|
130
|
-
} else {
|
|
131
|
-
// Add to existing array - find the last entry and add comma if needed
|
|
132
|
-
const lines = currentArray.split('\n');
|
|
133
|
-
const lastNonEmptyLine = lines.reverse().find(line => line.trim() && !line.trim().startsWith('//'));
|
|
134
|
-
const needsComma = lastNonEmptyLine && !lastNonEmptyLine.trim().endsWith(',');
|
|
135
|
-
const comma = needsComma ? ',' : '';
|
|
136
|
-
|
|
137
|
-
const insertAt = match.index + match[0].length - 1; // Before ]
|
|
138
|
-
updated = updated.slice(0, insertAt) + comma + newEntry + '\n ' + updated.slice(insertAt);
|
|
139
|
-
}
|
|
140
|
-
} else {
|
|
141
|
-
// No integrations array found - this is a problem
|
|
142
|
-
console.warn('Could not find integrations array in backend/index.js');
|
|
143
|
-
}
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
return updated;
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
/**
|
|
150
|
-
* Remove integration from backend.js content
|
|
151
|
-
*
|
|
152
|
-
* @param {string} content
|
|
153
|
-
* @param {string} className
|
|
154
|
-
* @param {string} integrationName
|
|
155
|
-
* @returns {string}
|
|
156
|
-
*/
|
|
157
|
-
_removeIntegration(content, className, integrationName) {
|
|
158
|
-
let updated = content;
|
|
159
|
-
|
|
160
|
-
// 1. Remove import statement
|
|
161
|
-
const importRegex = new RegExp(`\\nconst ${className}Integration = require\\([^)]+\\);`, 'g');
|
|
162
|
-
updated = updated.replace(importRegex, '');
|
|
163
|
-
|
|
164
|
-
// 2. Remove from integrations array
|
|
165
|
-
const arrayEntryRegex = new RegExp(`,?\\s*${className}Integration,?`, 'g');
|
|
166
|
-
updated = updated.replace(arrayEntryRegex, '');
|
|
167
|
-
|
|
168
|
-
// Clean up extra commas
|
|
169
|
-
updated = updated.replace(/,\s*,/g, ',');
|
|
170
|
-
updated = updated.replace(/\[\s*,/g, '[');
|
|
171
|
-
updated = updated.replace(/,\s*\]/g, ']');
|
|
172
|
-
|
|
173
|
-
return updated;
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
/**
|
|
177
|
-
* Convert kebab-case to ClassName
|
|
178
|
-
* @param {string} kebabCase
|
|
179
|
-
* @returns {string}
|
|
180
|
-
*/
|
|
181
|
-
_toClassName(kebabCase) {
|
|
182
|
-
return kebabCase
|
|
183
|
-
.split('-')
|
|
184
|
-
.map(word => word.charAt(0).toUpperCase() + word.slice(1))
|
|
185
|
-
.join('');
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
/**
|
|
189
|
-
* Check if backend/index.js exists
|
|
190
|
-
* @returns {Promise<boolean>}
|
|
191
|
-
*/
|
|
192
|
-
async exists() {
|
|
193
|
-
return await this.fileSystemAdapter.exists(this.indexJsPath);
|
|
194
|
-
}
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
module.exports = {BackendJsUpdater};
|