@friggframework/devtools 2.0.0--canary.474.6ec870b.0 → 2.0.0--canary.474.d64c550.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.
Files changed (20) hide show
  1. package/infrastructure/domains/health/application/ports/IPropertyReconciler.js +164 -0
  2. package/infrastructure/domains/health/application/ports/IResourceDetector.js +129 -0
  3. package/infrastructure/domains/health/application/ports/IResourceImporter.js +142 -0
  4. package/infrastructure/domains/health/application/ports/IStackRepository.js +131 -0
  5. package/infrastructure/domains/health/application/ports/index.js +26 -0
  6. package/infrastructure/domains/health/domain/entities/issue.js +250 -0
  7. package/infrastructure/domains/health/domain/entities/issue.test.js +417 -0
  8. package/infrastructure/domains/health/domain/entities/property-mismatch.js +7 -4
  9. package/infrastructure/domains/health/domain/entities/property-mismatch.test.js +28 -4
  10. package/infrastructure/domains/health/domain/entities/resource.js +159 -0
  11. package/infrastructure/domains/health/domain/entities/resource.test.js +432 -0
  12. package/infrastructure/domains/health/domain/entities/stack-health-report.js +306 -0
  13. package/infrastructure/domains/health/domain/entities/stack-health-report.test.js +601 -0
  14. package/infrastructure/domains/health/domain/services/health-score-calculator.js +165 -0
  15. package/infrastructure/domains/health/domain/services/health-score-calculator.test.js +400 -0
  16. package/infrastructure/domains/health/domain/services/mismatch-analyzer.js +234 -0
  17. package/infrastructure/domains/health/domain/services/mismatch-analyzer.test.js +431 -0
  18. package/infrastructure/domains/health/domain/value-objects/stack-identifier.js +13 -0
  19. package/infrastructure/domains/health/domain/value-objects/stack-identifier.test.js +29 -0
  20. package/package.json +6 -6
@@ -0,0 +1,164 @@
1
+ /**
2
+ * IPropertyReconciler Port Interface
3
+ *
4
+ * Defines operations for reconciling property mismatches between CloudFormation
5
+ * template definitions and actual cloud resource properties. This is used by
6
+ * the "frigg repair --reconcile" command to fix mutable property drift.
7
+ *
8
+ * This is a port in the hexagonal architecture that will be implemented
9
+ * by provider-specific adapters (e.g., AWSPropertyReconciler).
10
+ *
11
+ * Purpose: Abstract property update operations from the domain layer
12
+ */
13
+
14
+ class IPropertyReconciler {
15
+ /**
16
+ * Check if a property mismatch can be auto-fixed
17
+ *
18
+ * @param {PropertyMismatch} mismatch - Property mismatch to evaluate
19
+ * @returns {Promise<boolean>} True if mismatch can be automatically fixed
20
+ */
21
+ async canReconcile(mismatch) {
22
+ throw new Error(
23
+ 'IPropertyReconciler.canReconcile() must be implemented by adapter'
24
+ );
25
+ }
26
+
27
+ /**
28
+ * Reconcile a single property mismatch
29
+ *
30
+ * @param {Object} params
31
+ * @param {StackIdentifier} params.stackIdentifier - Stack identifier
32
+ * @param {string} params.logicalId - Logical resource ID
33
+ * @param {PropertyMismatch} params.mismatch - Property mismatch to fix
34
+ * @param {string} [params.mode='template'] - Reconciliation mode:
35
+ * - 'template': Update CloudFormation template to match actual (default)
36
+ * - 'resource': Update cloud resource to match template
37
+ * @returns {Promise<Object>} Reconciliation result
38
+ * @returns {Promise<Object>} Result with properties:
39
+ * - success: boolean
40
+ * - mode: string ('template' or 'resource')
41
+ * - propertyPath: string
42
+ * - oldValue: any
43
+ * - newValue: any
44
+ * - message: string
45
+ * @throws {Error} If reconciliation fails
46
+ */
47
+ async reconcileProperty({ stackIdentifier, logicalId, mismatch, mode = 'template' }) {
48
+ throw new Error(
49
+ 'IPropertyReconciler.reconcileProperty() must be implemented by adapter'
50
+ );
51
+ }
52
+
53
+ /**
54
+ * Reconcile multiple property mismatches for a resource
55
+ *
56
+ * @param {Object} params
57
+ * @param {StackIdentifier} params.stackIdentifier - Stack identifier
58
+ * @param {string} params.logicalId - Logical resource ID
59
+ * @param {PropertyMismatch[]} params.mismatches - Property mismatches to fix
60
+ * @param {string} [params.mode='template'] - Reconciliation mode
61
+ * @returns {Promise<Object>} Reconciliation result
62
+ * @returns {Promise<Object>} Result with properties:
63
+ * - reconciledCount: number
64
+ * - failedCount: number
65
+ * - results: Array<Object> (per-property results)
66
+ * - message: string
67
+ */
68
+ async reconcileMultipleProperties({
69
+ stackIdentifier,
70
+ logicalId,
71
+ mismatches,
72
+ mode = 'template',
73
+ }) {
74
+ throw new Error(
75
+ 'IPropertyReconciler.reconcileMultipleProperties() must be implemented by adapter'
76
+ );
77
+ }
78
+
79
+ /**
80
+ * Preview property reconciliation without applying changes
81
+ *
82
+ * @param {Object} params
83
+ * @param {StackIdentifier} params.stackIdentifier - Stack identifier
84
+ * @param {string} params.logicalId - Logical resource ID
85
+ * @param {PropertyMismatch} params.mismatch - Property mismatch to preview
86
+ * @param {string} [params.mode='template'] - Reconciliation mode
87
+ * @returns {Promise<Object>} Preview result
88
+ * @returns {Promise<Object>} Result with properties:
89
+ * - canReconcile: boolean
90
+ * - mode: string
91
+ * - propertyPath: string
92
+ * - currentValue: any
93
+ * - proposedValue: any
94
+ * - impact: string (description of impact)
95
+ * - warnings: Array<string>
96
+ */
97
+ async previewReconciliation({ stackIdentifier, logicalId, mismatch, mode = 'template' }) {
98
+ throw new Error(
99
+ 'IPropertyReconciler.previewReconciliation() must be implemented by adapter'
100
+ );
101
+ }
102
+
103
+ /**
104
+ * Update CloudFormation template property
105
+ *
106
+ * @param {Object} params
107
+ * @param {StackIdentifier} params.stackIdentifier - Stack identifier
108
+ * @param {string} params.logicalId - Logical resource ID
109
+ * @param {string} params.propertyPath - Property path (e.g., "Properties.Tags")
110
+ * @param {*} params.newValue - New property value
111
+ * @returns {Promise<Object>} Update result
112
+ * @returns {Promise<Object>} Result with properties:
113
+ * - success: boolean
114
+ * - changeSetId: string (CloudFormation change set ID)
115
+ * - message: string
116
+ * @throws {Error} If update fails
117
+ */
118
+ async updateTemplateProperty({ stackIdentifier, logicalId, propertyPath, newValue }) {
119
+ throw new Error(
120
+ 'IPropertyReconciler.updateTemplateProperty() must be implemented by adapter'
121
+ );
122
+ }
123
+
124
+ /**
125
+ * Update cloud resource property directly
126
+ *
127
+ * @param {Object} params
128
+ * @param {string} params.resourceType - CloudFormation resource type
129
+ * @param {string} params.physicalId - Physical resource ID
130
+ * @param {string} params.region - AWS region
131
+ * @param {string} params.propertyPath - Property path
132
+ * @param {*} params.newValue - New property value
133
+ * @returns {Promise<Object>} Update result
134
+ * @returns {Promise<Object>} Result with properties:
135
+ * - success: boolean
136
+ * - message: string
137
+ * - updatedAt: Date
138
+ * @throws {Error} If update fails or is not supported
139
+ */
140
+ async updateResourceProperty({ resourceType, physicalId, region, propertyPath, newValue }) {
141
+ throw new Error(
142
+ 'IPropertyReconciler.updateResourceProperty() must be implemented by adapter'
143
+ );
144
+ }
145
+
146
+ /**
147
+ * Get reconciliation strategy for a resource type
148
+ *
149
+ * @param {string} resourceType - CloudFormation resource type
150
+ * @returns {Promise<Object>} Strategy information
151
+ * @returns {Promise<Object>} Strategy with properties:
152
+ * - supportsTemplateUpdate: boolean
153
+ * - supportsResourceUpdate: boolean
154
+ * - recommendedMode: string ('template' or 'resource')
155
+ * - limitations: Array<string>
156
+ */
157
+ async getReconciliationStrategy(resourceType) {
158
+ throw new Error(
159
+ 'IPropertyReconciler.getReconciliationStrategy() must be implemented by adapter'
160
+ );
161
+ }
162
+ }
163
+
164
+ module.exports = IPropertyReconciler;
@@ -0,0 +1,129 @@
1
+ /**
2
+ * IResourceDetector Port Interface
3
+ *
4
+ * Defines operations for detecting cloud resources directly (outside of CloudFormation).
5
+ * This is used to find orphaned resources that exist in the cloud but are not managed
6
+ * by CloudFormation.
7
+ *
8
+ * This is a port in the hexagonal architecture that will be implemented
9
+ * by provider-specific adapters (e.g., AWSResourceDetector).
10
+ *
11
+ * Purpose: Abstract cloud resource discovery APIs from the domain layer
12
+ */
13
+
14
+ class IResourceDetector {
15
+ /**
16
+ * Detect all resources of a specific type in a region
17
+ *
18
+ * @param {Object} params
19
+ * @param {string} params.resourceType - CloudFormation resource type (e.g., AWS::EC2::VPC)
20
+ * @param {string} params.region - AWS region
21
+ * @param {Object} [params.filters={}] - Optional filters (e.g., tags, names)
22
+ * @returns {Promise<Array<Object>>} Array of detected resources
23
+ * @returns {Promise<Array<Object>>} Resources with properties:
24
+ * - physicalId: string (actual cloud resource ID)
25
+ * - resourceType: string (CloudFormation resource type)
26
+ * - properties: Object (resource properties from cloud API)
27
+ * - tags: Object (resource tags, if supported)
28
+ * - createdTime: Date (if available)
29
+ * @throws {Error} If resource type is not supported
30
+ */
31
+ async detectResources({ resourceType, region, filters = {} }) {
32
+ throw new Error(
33
+ 'IResourceDetector.detectResources() must be implemented by adapter'
34
+ );
35
+ }
36
+
37
+ /**
38
+ * Get details for a specific resource
39
+ *
40
+ * @param {Object} params
41
+ * @param {string} params.resourceType - CloudFormation resource type
42
+ * @param {string} params.physicalId - Physical resource ID
43
+ * @param {string} params.region - AWS region
44
+ * @returns {Promise<Object>} Resource details
45
+ * @returns {Promise<Object>} Resource with properties:
46
+ * - physicalId: string
47
+ * - resourceType: string
48
+ * - properties: Object (complete resource properties)
49
+ * - tags: Object
50
+ * - status: string (resource-specific status)
51
+ * - metadata: Object (additional resource metadata)
52
+ * @throws {Error} If resource does not exist
53
+ */
54
+ async getResourceDetails({ resourceType, physicalId, region }) {
55
+ throw new Error(
56
+ 'IResourceDetector.getResourceDetails() must be implemented by adapter'
57
+ );
58
+ }
59
+
60
+ /**
61
+ * Check if a resource exists
62
+ *
63
+ * @param {Object} params
64
+ * @param {string} params.resourceType - CloudFormation resource type
65
+ * @param {string} params.physicalId - Physical resource ID
66
+ * @param {string} params.region - AWS region
67
+ * @returns {Promise<boolean>} True if resource exists
68
+ */
69
+ async resourceExists({ resourceType, physicalId, region }) {
70
+ throw new Error(
71
+ 'IResourceDetector.resourceExists() must be implemented by adapter'
72
+ );
73
+ }
74
+
75
+ /**
76
+ * Get list of supported resource types
77
+ *
78
+ * @returns {Promise<Array<string>>} Array of supported CloudFormation resource types
79
+ */
80
+ async getSupportedResourceTypes() {
81
+ throw new Error(
82
+ 'IResourceDetector.getSupportedResourceTypes() must be implemented by adapter'
83
+ );
84
+ }
85
+
86
+ /**
87
+ * Detect resources by tags
88
+ *
89
+ * @param {Object} params
90
+ * @param {Object} params.tags - Tags to filter by (key-value pairs)
91
+ * @param {string} params.region - AWS region
92
+ * @param {string[]} [params.resourceTypes] - Optional: limit to specific resource types
93
+ * @returns {Promise<Array<Object>>} Array of resources matching tags
94
+ * @returns {Promise<Array<Object>>} Resources with properties:
95
+ * - physicalId: string
96
+ * - resourceType: string
97
+ * - properties: Object
98
+ * - tags: Object
99
+ */
100
+ async detectResourcesByTags({ tags, region, resourceTypes = [] }) {
101
+ throw new Error(
102
+ 'IResourceDetector.detectResourcesByTags() must be implemented by adapter'
103
+ );
104
+ }
105
+
106
+ /**
107
+ * Find orphaned resources (exist in cloud but not in any stack)
108
+ *
109
+ * @param {Object} params
110
+ * @param {string} params.region - AWS region
111
+ * @param {string[]} [params.resourceTypes] - Optional: limit to specific resource types
112
+ * @param {string[]} [params.excludePhysicalIds=[]] - Physical IDs to exclude from orphan check
113
+ * @returns {Promise<Array<Object>>} Array of orphaned resources
114
+ * @returns {Promise<Array<Object>>} Resources with properties:
115
+ * - physicalId: string
116
+ * - resourceType: string
117
+ * - properties: Object
118
+ * - tags: Object
119
+ * - isOrphaned: boolean (always true)
120
+ * - reason: string (explanation of why it's orphaned)
121
+ */
122
+ async findOrphanedResources({ region, resourceTypes = [], excludePhysicalIds = [] }) {
123
+ throw new Error(
124
+ 'IResourceDetector.findOrphanedResources() must be implemented by adapter'
125
+ );
126
+ }
127
+ }
128
+
129
+ module.exports = IResourceDetector;
@@ -0,0 +1,142 @@
1
+ /**
2
+ * IResourceImporter Port Interface
3
+ *
4
+ * Defines operations for importing existing cloud resources into CloudFormation stacks.
5
+ * This is used by the "frigg repair --import" command to fix orphaned resources.
6
+ *
7
+ * This is a port in the hexagonal architecture that will be implemented
8
+ * by provider-specific adapters (e.g., AWSResourceImporter).
9
+ *
10
+ * Purpose: Abstract CloudFormation resource import operations from the domain layer
11
+ */
12
+
13
+ class IResourceImporter {
14
+ /**
15
+ * Check if a resource type supports import
16
+ *
17
+ * @param {string} resourceType - CloudFormation resource type (e.g., AWS::EC2::VPC)
18
+ * @returns {Promise<boolean>} True if resource type can be imported
19
+ */
20
+ async supportsImport(resourceType) {
21
+ throw new Error(
22
+ 'IResourceImporter.supportsImport() must be implemented by adapter'
23
+ );
24
+ }
25
+
26
+ /**
27
+ * Get the identifier property for a resource type
28
+ * (e.g., "VpcId" for AWS::EC2::VPC, "BucketName" for AWS::S3::Bucket)
29
+ *
30
+ * @param {string} resourceType - CloudFormation resource type
31
+ * @returns {Promise<string>} Property name used as identifier for import
32
+ * @throws {Error} If resource type doesn't support import
33
+ */
34
+ async getIdentifierProperty(resourceType) {
35
+ throw new Error(
36
+ 'IResourceImporter.getIdentifierProperty() must be implemented by adapter'
37
+ );
38
+ }
39
+
40
+ /**
41
+ * Validate that a resource can be imported
42
+ *
43
+ * @param {Object} params
44
+ * @param {string} params.resourceType - CloudFormation resource type
45
+ * @param {string} params.physicalId - Physical resource ID
46
+ * @param {string} params.region - AWS region
47
+ * @returns {Promise<Object>} Validation result
48
+ * @returns {Promise<Object>} Result with properties:
49
+ * - canImport: boolean
50
+ * - reason: string (explanation if cannot import)
51
+ * - warnings: Array<string> (potential issues with import)
52
+ */
53
+ async validateImport({ resourceType, physicalId, region }) {
54
+ throw new Error(
55
+ 'IResourceImporter.validateImport() must be implemented by adapter'
56
+ );
57
+ }
58
+
59
+ /**
60
+ * Import a single resource into a stack
61
+ *
62
+ * @param {Object} params
63
+ * @param {StackIdentifier} params.stackIdentifier - Target stack
64
+ * @param {string} params.logicalId - Desired logical ID for the resource
65
+ * @param {string} params.resourceType - CloudFormation resource type
66
+ * @param {string} params.physicalId - Physical resource ID to import
67
+ * @param {Object} params.properties - Resource properties for template
68
+ * @returns {Promise<Object>} Import operation result
69
+ * @returns {Promise<Object>} Result with properties:
70
+ * - operationId: string (CloudFormation change set ID)
71
+ * - status: string (IN_PROGRESS, COMPLETE, FAILED)
72
+ * - message: string (status message)
73
+ * @throws {Error} If import fails validation or execution
74
+ */
75
+ async importResource({ stackIdentifier, logicalId, resourceType, physicalId, properties }) {
76
+ throw new Error(
77
+ 'IResourceImporter.importResource() must be implemented by adapter'
78
+ );
79
+ }
80
+
81
+ /**
82
+ * Import multiple resources into a stack in a single operation
83
+ *
84
+ * @param {Object} params
85
+ * @param {StackIdentifier} params.stackIdentifier - Target stack
86
+ * @param {Array<Object>} params.resources - Resources to import
87
+ * @param {string} params.resources[].logicalId - Desired logical ID
88
+ * @param {string} params.resources[].resourceType - CloudFormation resource type
89
+ * @param {string} params.resources[].physicalId - Physical resource ID
90
+ * @param {Object} params.resources[].properties - Resource properties
91
+ * @returns {Promise<Object>} Import operation result
92
+ * @returns {Promise<Object>} Result with properties:
93
+ * - operationId: string (CloudFormation change set ID)
94
+ * - status: string (IN_PROGRESS, COMPLETE, FAILED)
95
+ * - importedCount: number
96
+ * - failedCount: number
97
+ * - message: string
98
+ * - details: Array<Object> (per-resource status)
99
+ * @throws {Error} If import fails validation or execution
100
+ */
101
+ async importMultipleResources({ stackIdentifier, resources }) {
102
+ throw new Error(
103
+ 'IResourceImporter.importMultipleResources() must be implemented by adapter'
104
+ );
105
+ }
106
+
107
+ /**
108
+ * Get status of an import operation
109
+ *
110
+ * @param {string} operationId - CloudFormation change set ID
111
+ * @returns {Promise<Object>} Operation status
112
+ * @returns {Promise<Object>} Status with properties:
113
+ * - operationId: string
114
+ * - status: string (IN_PROGRESS, COMPLETE, FAILED)
115
+ * - progress: number (0-100)
116
+ * - message: string
117
+ * - completedTime: Date (if complete)
118
+ */
119
+ async getImportStatus(operationId) {
120
+ throw new Error(
121
+ 'IResourceImporter.getImportStatus() must be implemented by adapter'
122
+ );
123
+ }
124
+
125
+ /**
126
+ * Generate CloudFormation template snippet for an imported resource
127
+ *
128
+ * @param {Object} params
129
+ * @param {string} params.logicalId - Logical ID for the resource
130
+ * @param {string} params.resourceType - CloudFormation resource type
131
+ * @param {Object} params.properties - Current resource properties from cloud
132
+ * @returns {Promise<Object>} Template snippet for the resource
133
+ * @returns {Promise<Object>} Template snippet with CloudFormation resource definition
134
+ */
135
+ async generateTemplateSnippet({ logicalId, resourceType, properties }) {
136
+ throw new Error(
137
+ 'IResourceImporter.generateTemplateSnippet() must be implemented by adapter'
138
+ );
139
+ }
140
+ }
141
+
142
+ module.exports = IResourceImporter;
@@ -0,0 +1,131 @@
1
+ /**
2
+ * IStackRepository Port Interface
3
+ *
4
+ * Defines operations for accessing CloudFormation stack information.
5
+ * This is a port in the hexagonal architecture that will be implemented
6
+ * by provider-specific adapters (e.g., AWSStackRepository).
7
+ *
8
+ * Purpose: Abstract CloudFormation API interactions from the domain layer
9
+ */
10
+
11
+ class IStackRepository {
12
+ /**
13
+ * Get stack information by identifier
14
+ *
15
+ * @param {StackIdentifier} identifier - Stack identifier
16
+ * @returns {Promise<Object>} Stack information
17
+ * @returns {Promise<Object>} Stack information with properties:
18
+ * - stackName: string
19
+ * - region: string
20
+ * - accountId: string
21
+ * - stackId: string (ARN)
22
+ * - status: string (CREATE_COMPLETE, UPDATE_COMPLETE, etc.)
23
+ * - creationTime: Date
24
+ * - lastUpdatedTime: Date
25
+ * - parameters: Object (key-value pairs)
26
+ * - outputs: Object (key-value pairs)
27
+ * - tags: Object (key-value pairs)
28
+ * @throws {Error} If stack does not exist
29
+ */
30
+ async getStack(identifier) {
31
+ throw new Error('IStackRepository.getStack() must be implemented by adapter');
32
+ }
33
+
34
+ /**
35
+ * List all resources in a stack
36
+ *
37
+ * @param {StackIdentifier} identifier - Stack identifier
38
+ * @returns {Promise<Array>} Array of stack resources
39
+ * @returns {Promise<Array<Object>>} Array of resources with properties:
40
+ * - logicalId: string (CloudFormation logical ID)
41
+ * - physicalId: string (actual cloud resource ID)
42
+ * - resourceType: string (e.g., AWS::EC2::VPC)
43
+ * - status: string (CREATE_COMPLETE, UPDATE_COMPLETE, etc.)
44
+ * - lastUpdatedTime: Date
45
+ * - driftStatus: string (IN_SYNC, MODIFIED, DELETED, NOT_CHECKED)
46
+ * @throws {Error} If stack does not exist
47
+ */
48
+ async listResources(identifier) {
49
+ throw new Error(
50
+ 'IStackRepository.listResources() must be implemented by adapter'
51
+ );
52
+ }
53
+
54
+ /**
55
+ * Get resource details from stack
56
+ *
57
+ * @param {StackIdentifier} identifier - Stack identifier
58
+ * @param {string} logicalId - Logical resource ID
59
+ * @returns {Promise<Object>} Resource details
60
+ * @returns {Promise<Object>} Resource with properties:
61
+ * - logicalId: string
62
+ * - physicalId: string
63
+ * - resourceType: string
64
+ * - status: string
65
+ * - properties: Object (current resource properties)
66
+ * - metadata: Object (resource metadata from template)
67
+ * @throws {Error} If stack or resource does not exist
68
+ */
69
+ async getResource(identifier, logicalId) {
70
+ throw new Error('IStackRepository.getResource() must be implemented by adapter');
71
+ }
72
+
73
+ /**
74
+ * Get the CloudFormation template for a stack
75
+ *
76
+ * @param {StackIdentifier} identifier - Stack identifier
77
+ * @returns {Promise<Object>} CloudFormation template as object
78
+ * @throws {Error} If stack does not exist
79
+ */
80
+ async getTemplate(identifier) {
81
+ throw new Error('IStackRepository.getTemplate() must be implemented by adapter');
82
+ }
83
+
84
+ /**
85
+ * Check if a stack exists
86
+ *
87
+ * @param {StackIdentifier} identifier - Stack identifier
88
+ * @returns {Promise<boolean>} True if stack exists
89
+ */
90
+ async exists(identifier) {
91
+ throw new Error('IStackRepository.exists() must be implemented by adapter');
92
+ }
93
+
94
+ /**
95
+ * Detect drift for the entire stack
96
+ *
97
+ * @param {StackIdentifier} identifier - Stack identifier
98
+ * @returns {Promise<Object>} Drift detection result
99
+ * @returns {Promise<Object>} Result with properties:
100
+ * - stackDriftStatus: string (DRIFTED, IN_SYNC, UNKNOWN, NOT_CHECKED)
101
+ * - driftedResourceCount: number
102
+ * - detectionTime: Date
103
+ * @throws {Error} If stack does not exist
104
+ */
105
+ async detectStackDrift(identifier) {
106
+ throw new Error(
107
+ 'IStackRepository.detectStackDrift() must be implemented by adapter'
108
+ );
109
+ }
110
+
111
+ /**
112
+ * Get drift details for a specific resource
113
+ *
114
+ * @param {StackIdentifier} identifier - Stack identifier
115
+ * @param {string} logicalId - Logical resource ID
116
+ * @returns {Promise<Object>} Resource drift details
117
+ * @returns {Promise<Object>} Drift with properties:
118
+ * - driftStatus: string (IN_SYNC, MODIFIED, DELETED, NOT_CHECKED)
119
+ * - expectedProperties: Object (from template)
120
+ * - actualProperties: Object (from cloud)
121
+ * - propertyDifferences: Array<Object> (list of drifted properties)
122
+ * @throws {Error} If stack or resource does not exist
123
+ */
124
+ async getResourceDrift(identifier, logicalId) {
125
+ throw new Error(
126
+ 'IStackRepository.getResourceDrift() must be implemented by adapter'
127
+ );
128
+ }
129
+ }
130
+
131
+ module.exports = IStackRepository;
@@ -0,0 +1,26 @@
1
+ /**
2
+ * Health Domain - Application Layer Ports
3
+ *
4
+ * Port interfaces define how the application layer communicates with
5
+ * infrastructure adapters. These are part of the hexagonal architecture
6
+ * pattern, allowing provider-specific implementations (AWS, GCP, Azure, etc.)
7
+ * without changing the domain or application logic.
8
+ *
9
+ * Ports:
10
+ * - IStackRepository: CloudFormation stack operations
11
+ * - IResourceDetector: Cloud resource discovery (orphan detection)
12
+ * - IResourceImporter: Import resources into CloudFormation
13
+ * - IPropertyReconciler: Fix property drift (mutable properties)
14
+ */
15
+
16
+ const IStackRepository = require('./IStackRepository');
17
+ const IResourceDetector = require('./IResourceDetector');
18
+ const IResourceImporter = require('./IResourceImporter');
19
+ const IPropertyReconciler = require('./IPropertyReconciler');
20
+
21
+ module.exports = {
22
+ IStackRepository,
23
+ IResourceDetector,
24
+ IResourceImporter,
25
+ IPropertyReconciler,
26
+ };