@friggframework/devtools 2.0.0-next.45 → 2.0.0-next.46

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 (212) hide show
  1. package/infrastructure/ARCHITECTURE.md +487 -0
  2. package/infrastructure/HEALTH.md +468 -0
  3. package/infrastructure/README.md +51 -0
  4. package/infrastructure/__tests__/postgres-config.test.js +914 -0
  5. package/infrastructure/__tests__/template-generation.test.js +687 -0
  6. package/infrastructure/create-frigg-infrastructure.js +1 -1
  7. package/infrastructure/docs/POSTGRES-CONFIGURATION.md +630 -0
  8. package/infrastructure/{DEPLOYMENT-INSTRUCTIONS.md → docs/deployment-instructions.md} +3 -3
  9. package/infrastructure/{IAM-POLICY-TEMPLATES.md → docs/iam-policy-templates.md} +9 -10
  10. package/infrastructure/domains/database/aurora-builder.js +809 -0
  11. package/infrastructure/domains/database/aurora-builder.test.js +950 -0
  12. package/infrastructure/domains/database/aurora-discovery.js +87 -0
  13. package/infrastructure/domains/database/aurora-discovery.test.js +188 -0
  14. package/infrastructure/domains/database/aurora-resolver.js +210 -0
  15. package/infrastructure/domains/database/aurora-resolver.test.js +347 -0
  16. package/infrastructure/domains/database/migration-builder.js +633 -0
  17. package/infrastructure/domains/database/migration-builder.test.js +294 -0
  18. package/infrastructure/domains/database/migration-resolver.js +163 -0
  19. package/infrastructure/domains/database/migration-resolver.test.js +337 -0
  20. package/infrastructure/domains/health/application/ports/IPropertyReconciler.js +164 -0
  21. package/infrastructure/domains/health/application/ports/IResourceDetector.js +129 -0
  22. package/infrastructure/domains/health/application/ports/IResourceImporter.js +142 -0
  23. package/infrastructure/domains/health/application/ports/IStackRepository.js +131 -0
  24. package/infrastructure/domains/health/application/ports/index.js +26 -0
  25. package/infrastructure/domains/health/application/use-cases/__tests__/execute-resource-import-use-case.test.js +679 -0
  26. package/infrastructure/domains/health/application/use-cases/__tests__/mismatch-analyzer-method-name.test.js +167 -0
  27. package/infrastructure/domains/health/application/use-cases/__tests__/repair-via-import-use-case.test.js +1130 -0
  28. package/infrastructure/domains/health/application/use-cases/execute-resource-import-use-case.js +221 -0
  29. package/infrastructure/domains/health/application/use-cases/reconcile-properties-use-case.js +152 -0
  30. package/infrastructure/domains/health/application/use-cases/reconcile-properties-use-case.test.js +343 -0
  31. package/infrastructure/domains/health/application/use-cases/repair-via-import-use-case.js +535 -0
  32. package/infrastructure/domains/health/application/use-cases/repair-via-import-use-case.test.js +376 -0
  33. package/infrastructure/domains/health/application/use-cases/run-health-check-use-case.js +213 -0
  34. package/infrastructure/domains/health/application/use-cases/run-health-check-use-case.test.js +441 -0
  35. package/infrastructure/domains/health/docs/ACME-DEV-DRIFT-ANALYSIS.md +267 -0
  36. package/infrastructure/domains/health/docs/BUILD-VS-DEPLOYED-TEMPLATE-ANALYSIS.md +324 -0
  37. package/infrastructure/domains/health/docs/ORPHAN-DETECTION-ANALYSIS.md +386 -0
  38. package/infrastructure/domains/health/docs/SPEC-CLEANUP-COMMAND.md +1419 -0
  39. package/infrastructure/domains/health/docs/TDD-IMPLEMENTATION-SUMMARY.md +391 -0
  40. package/infrastructure/domains/health/docs/TEMPLATE-COMPARISON-IMPLEMENTATION.md +551 -0
  41. package/infrastructure/domains/health/domain/entities/issue.js +299 -0
  42. package/infrastructure/domains/health/domain/entities/issue.test.js +528 -0
  43. package/infrastructure/domains/health/domain/entities/property-mismatch.js +108 -0
  44. package/infrastructure/domains/health/domain/entities/property-mismatch.test.js +275 -0
  45. package/infrastructure/domains/health/domain/entities/resource.js +159 -0
  46. package/infrastructure/domains/health/domain/entities/resource.test.js +432 -0
  47. package/infrastructure/domains/health/domain/entities/stack-health-report.js +306 -0
  48. package/infrastructure/domains/health/domain/entities/stack-health-report.test.js +601 -0
  49. package/infrastructure/domains/health/domain/services/__tests__/health-score-percentage-based.test.js +380 -0
  50. package/infrastructure/domains/health/domain/services/__tests__/import-progress-monitor.test.js +971 -0
  51. package/infrastructure/domains/health/domain/services/__tests__/import-template-generator.test.js +1150 -0
  52. package/infrastructure/domains/health/domain/services/__tests__/logical-id-mapper.test.js +672 -0
  53. package/infrastructure/domains/health/domain/services/__tests__/template-parser.test.js +496 -0
  54. package/infrastructure/domains/health/domain/services/__tests__/update-progress-monitor.test.js +419 -0
  55. package/infrastructure/domains/health/domain/services/health-score-calculator.js +248 -0
  56. package/infrastructure/domains/health/domain/services/health-score-calculator.test.js +504 -0
  57. package/infrastructure/domains/health/domain/services/import-progress-monitor.js +195 -0
  58. package/infrastructure/domains/health/domain/services/import-template-generator.js +435 -0
  59. package/infrastructure/domains/health/domain/services/logical-id-mapper.js +345 -0
  60. package/infrastructure/domains/health/domain/services/mismatch-analyzer.js +234 -0
  61. package/infrastructure/domains/health/domain/services/mismatch-analyzer.test.js +431 -0
  62. package/infrastructure/domains/health/domain/services/property-mutability-config.js +382 -0
  63. package/infrastructure/domains/health/domain/services/template-parser.js +245 -0
  64. package/infrastructure/domains/health/domain/services/update-progress-monitor.js +192 -0
  65. package/infrastructure/domains/health/domain/value-objects/health-score.js +138 -0
  66. package/infrastructure/domains/health/domain/value-objects/health-score.test.js +267 -0
  67. package/infrastructure/domains/health/domain/value-objects/property-mutability.js +161 -0
  68. package/infrastructure/domains/health/domain/value-objects/property-mutability.test.js +198 -0
  69. package/infrastructure/domains/health/domain/value-objects/resource-state.js +167 -0
  70. package/infrastructure/domains/health/domain/value-objects/resource-state.test.js +196 -0
  71. package/infrastructure/domains/health/domain/value-objects/stack-identifier.js +192 -0
  72. package/infrastructure/domains/health/domain/value-objects/stack-identifier.test.js +262 -0
  73. package/infrastructure/domains/health/infrastructure/adapters/__tests__/orphan-detection-cfn-tagged.test.js +312 -0
  74. package/infrastructure/domains/health/infrastructure/adapters/__tests__/orphan-detection-multi-stack.test.js +367 -0
  75. package/infrastructure/domains/health/infrastructure/adapters/__tests__/orphan-detection-relationship-analysis.test.js +432 -0
  76. package/infrastructure/domains/health/infrastructure/adapters/aws-property-reconciler.js +784 -0
  77. package/infrastructure/domains/health/infrastructure/adapters/aws-property-reconciler.test.js +1133 -0
  78. package/infrastructure/domains/health/infrastructure/adapters/aws-resource-detector.js +565 -0
  79. package/infrastructure/domains/health/infrastructure/adapters/aws-resource-detector.test.js +554 -0
  80. package/infrastructure/domains/health/infrastructure/adapters/aws-resource-importer.js +318 -0
  81. package/infrastructure/domains/health/infrastructure/adapters/aws-resource-importer.test.js +398 -0
  82. package/infrastructure/domains/health/infrastructure/adapters/aws-stack-repository.js +777 -0
  83. package/infrastructure/domains/health/infrastructure/adapters/aws-stack-repository.test.js +580 -0
  84. package/infrastructure/domains/integration/integration-builder.js +397 -0
  85. package/infrastructure/domains/integration/integration-builder.test.js +593 -0
  86. package/infrastructure/domains/integration/integration-resolver.js +170 -0
  87. package/infrastructure/domains/integration/integration-resolver.test.js +369 -0
  88. package/infrastructure/domains/integration/websocket-builder.js +69 -0
  89. package/infrastructure/domains/integration/websocket-builder.test.js +195 -0
  90. package/infrastructure/domains/networking/vpc-builder.js +1829 -0
  91. package/infrastructure/domains/networking/vpc-builder.test.js +1262 -0
  92. package/infrastructure/domains/networking/vpc-discovery.js +177 -0
  93. package/infrastructure/domains/networking/vpc-discovery.test.js +350 -0
  94. package/infrastructure/domains/networking/vpc-resolver.js +324 -0
  95. package/infrastructure/domains/networking/vpc-resolver.test.js +501 -0
  96. package/infrastructure/domains/parameters/ssm-builder.js +79 -0
  97. package/infrastructure/domains/parameters/ssm-builder.test.js +189 -0
  98. package/infrastructure/domains/parameters/ssm-discovery.js +84 -0
  99. package/infrastructure/domains/parameters/ssm-discovery.test.js +210 -0
  100. package/infrastructure/{iam-generator.js → domains/security/iam-generator.js} +2 -2
  101. package/infrastructure/domains/security/kms-builder.js +366 -0
  102. package/infrastructure/domains/security/kms-builder.test.js +374 -0
  103. package/infrastructure/domains/security/kms-discovery.js +80 -0
  104. package/infrastructure/domains/security/kms-discovery.test.js +177 -0
  105. package/infrastructure/domains/security/kms-resolver.js +96 -0
  106. package/infrastructure/domains/security/kms-resolver.test.js +216 -0
  107. package/infrastructure/domains/shared/base-builder.js +112 -0
  108. package/infrastructure/domains/shared/base-resolver.js +186 -0
  109. package/infrastructure/domains/shared/base-resolver.test.js +305 -0
  110. package/infrastructure/domains/shared/builder-orchestrator.js +212 -0
  111. package/infrastructure/domains/shared/builder-orchestrator.test.js +213 -0
  112. package/infrastructure/domains/shared/cloudformation-discovery-v2.js +334 -0
  113. package/infrastructure/domains/shared/cloudformation-discovery.js +375 -0
  114. package/infrastructure/domains/shared/cloudformation-discovery.test.js +590 -0
  115. package/infrastructure/domains/shared/environment-builder.js +119 -0
  116. package/infrastructure/domains/shared/environment-builder.test.js +247 -0
  117. package/infrastructure/domains/shared/providers/aws-provider-adapter.js +544 -0
  118. package/infrastructure/domains/shared/providers/aws-provider-adapter.test.js +377 -0
  119. package/infrastructure/domains/shared/providers/azure-provider-adapter.stub.js +93 -0
  120. package/infrastructure/domains/shared/providers/cloud-provider-adapter.js +136 -0
  121. package/infrastructure/domains/shared/providers/gcp-provider-adapter.stub.js +82 -0
  122. package/infrastructure/domains/shared/providers/provider-factory.js +108 -0
  123. package/infrastructure/domains/shared/providers/provider-factory.test.js +170 -0
  124. package/infrastructure/domains/shared/resource-discovery.js +192 -0
  125. package/infrastructure/domains/shared/resource-discovery.test.js +552 -0
  126. package/infrastructure/domains/shared/types/app-definition.js +205 -0
  127. package/infrastructure/domains/shared/types/discovery-result.js +106 -0
  128. package/infrastructure/domains/shared/types/discovery-result.test.js +258 -0
  129. package/infrastructure/domains/shared/types/index.js +46 -0
  130. package/infrastructure/domains/shared/types/resource-ownership.js +108 -0
  131. package/infrastructure/domains/shared/types/resource-ownership.test.js +101 -0
  132. package/infrastructure/domains/shared/utilities/base-definition-factory.js +380 -0
  133. package/infrastructure/domains/shared/utilities/base-definition-factory.js.bak +338 -0
  134. package/infrastructure/domains/shared/utilities/base-definition-factory.test.js +248 -0
  135. package/infrastructure/domains/shared/utilities/handler-path-resolver.js +134 -0
  136. package/infrastructure/domains/shared/utilities/handler-path-resolver.test.js +268 -0
  137. package/infrastructure/domains/shared/utilities/prisma-layer-manager.js +55 -0
  138. package/infrastructure/domains/shared/utilities/prisma-layer-manager.test.js +138 -0
  139. package/infrastructure/{env-validator.js → domains/shared/validation/env-validator.js} +2 -1
  140. package/infrastructure/domains/shared/validation/env-validator.test.js +173 -0
  141. package/infrastructure/esbuild.config.js +53 -0
  142. package/infrastructure/infrastructure-composer.js +87 -0
  143. package/infrastructure/{serverless-template.test.js → infrastructure-composer.test.js} +115 -24
  144. package/infrastructure/scripts/build-prisma-layer.js +553 -0
  145. package/infrastructure/scripts/build-prisma-layer.test.js +102 -0
  146. package/infrastructure/{build-time-discovery.js → scripts/build-time-discovery.js} +80 -48
  147. package/infrastructure/{build-time-discovery.test.js → scripts/build-time-discovery.test.js} +5 -4
  148. package/layers/prisma/nodejs/package.json +8 -0
  149. package/management-ui/server/utils/cliIntegration.js +1 -1
  150. package/management-ui/server/utils/environment/awsParameterStore.js +29 -18
  151. package/package.json +11 -11
  152. package/frigg-cli/.eslintrc.js +0 -141
  153. package/frigg-cli/__tests__/unit/commands/build.test.js +0 -251
  154. package/frigg-cli/__tests__/unit/commands/db-setup.test.js +0 -548
  155. package/frigg-cli/__tests__/unit/commands/install.test.js +0 -400
  156. package/frigg-cli/__tests__/unit/commands/ui.test.js +0 -346
  157. package/frigg-cli/__tests__/unit/utils/database-validator.test.js +0 -366
  158. package/frigg-cli/__tests__/unit/utils/error-messages.test.js +0 -304
  159. package/frigg-cli/__tests__/unit/utils/prisma-runner.test.js +0 -486
  160. package/frigg-cli/__tests__/utils/mock-factory.js +0 -270
  161. package/frigg-cli/__tests__/utils/prisma-mock.js +0 -194
  162. package/frigg-cli/__tests__/utils/test-fixtures.js +0 -463
  163. package/frigg-cli/__tests__/utils/test-setup.js +0 -287
  164. package/frigg-cli/build-command/index.js +0 -65
  165. package/frigg-cli/db-setup-command/index.js +0 -193
  166. package/frigg-cli/deploy-command/index.js +0 -175
  167. package/frigg-cli/generate-command/__tests__/generate-command.test.js +0 -301
  168. package/frigg-cli/generate-command/azure-generator.js +0 -43
  169. package/frigg-cli/generate-command/gcp-generator.js +0 -47
  170. package/frigg-cli/generate-command/index.js +0 -332
  171. package/frigg-cli/generate-command/terraform-generator.js +0 -555
  172. package/frigg-cli/generate-iam-command.js +0 -118
  173. package/frigg-cli/index.js +0 -75
  174. package/frigg-cli/index.test.js +0 -158
  175. package/frigg-cli/init-command/backend-first-handler.js +0 -756
  176. package/frigg-cli/init-command/index.js +0 -93
  177. package/frigg-cli/init-command/template-handler.js +0 -143
  178. package/frigg-cli/install-command/backend-js.js +0 -33
  179. package/frigg-cli/install-command/commit-changes.js +0 -16
  180. package/frigg-cli/install-command/environment-variables.js +0 -127
  181. package/frigg-cli/install-command/environment-variables.test.js +0 -136
  182. package/frigg-cli/install-command/index.js +0 -54
  183. package/frigg-cli/install-command/install-package.js +0 -13
  184. package/frigg-cli/install-command/integration-file.js +0 -30
  185. package/frigg-cli/install-command/logger.js +0 -12
  186. package/frigg-cli/install-command/template.js +0 -90
  187. package/frigg-cli/install-command/validate-package.js +0 -75
  188. package/frigg-cli/jest.config.js +0 -124
  189. package/frigg-cli/package.json +0 -54
  190. package/frigg-cli/start-command/index.js +0 -149
  191. package/frigg-cli/start-command/start-command.test.js +0 -297
  192. package/frigg-cli/test/init-command.test.js +0 -180
  193. package/frigg-cli/test/npm-registry.test.js +0 -319
  194. package/frigg-cli/ui-command/index.js +0 -154
  195. package/frigg-cli/utils/app-resolver.js +0 -319
  196. package/frigg-cli/utils/backend-path.js +0 -25
  197. package/frigg-cli/utils/database-validator.js +0 -161
  198. package/frigg-cli/utils/error-messages.js +0 -257
  199. package/frigg-cli/utils/npm-registry.js +0 -167
  200. package/frigg-cli/utils/prisma-runner.js +0 -280
  201. package/frigg-cli/utils/process-manager.js +0 -199
  202. package/frigg-cli/utils/repo-detection.js +0 -405
  203. package/infrastructure/aws-discovery.js +0 -1176
  204. package/infrastructure/aws-discovery.test.js +0 -1220
  205. package/infrastructure/serverless-template.js +0 -2094
  206. /package/infrastructure/{WEBSOCKET-CONFIGURATION.md → docs/WEBSOCKET-CONFIGURATION.md} +0 -0
  207. /package/infrastructure/{GENERATE-IAM-DOCS.md → docs/generate-iam-command.md} +0 -0
  208. /package/infrastructure/{iam-generator.test.js → domains/security/iam-generator.test.js} +0 -0
  209. /package/infrastructure/{frigg-deployment-iam-stack.yaml → domains/security/templates/frigg-deployment-iam-stack.yaml} +0 -0
  210. /package/infrastructure/{iam-policy-basic.json → domains/security/templates/iam-policy-basic.json} +0 -0
  211. /package/infrastructure/{iam-policy-full.json → domains/security/templates/iam-policy-full.json} +0 -0
  212. /package/infrastructure/{run-discovery.js → scripts/run-discovery.js} +0 -0
@@ -0,0 +1,108 @@
1
+ /**
2
+ * Cloud Provider Factory
3
+ *
4
+ * Factory Pattern - Hexagonal Architecture
5
+ *
6
+ * Creates appropriate cloud provider adapter instances based on configuration.
7
+ * This enables runtime provider selection and makes it easy to add new providers.
8
+ */
9
+
10
+ const { CloudProviderAdapter } = require('./cloud-provider-adapter');
11
+ const { AWSProviderAdapter } = require('./aws-provider-adapter');
12
+
13
+ class CloudProviderFactory {
14
+ /**
15
+ * Create cloud provider adapter instance
16
+ *
17
+ * @param {string} providerName - Provider name ('aws', 'gcp', 'azure')
18
+ * @param {string} region - Provider region
19
+ * @param {Object} [credentials] - Optional credential configuration
20
+ * @returns {CloudProviderAdapter} Provider adapter instance
21
+ * @throws {Error} If provider is not supported
22
+ */
23
+ static create(providerName, region, credentials = {}) {
24
+ const normalizedProvider = (providerName || 'aws').toLowerCase();
25
+
26
+ switch (normalizedProvider) {
27
+ case 'aws':
28
+ return new AWSProviderAdapter(region, credentials);
29
+
30
+ case 'gcp':
31
+ case 'google':
32
+ throw new Error(
33
+ 'GCP provider not yet implemented. ' +
34
+ 'AWS is currently the only supported cloud provider. ' +
35
+ 'GCP support is planned for future releases.'
36
+ );
37
+
38
+ case 'azure':
39
+ case 'microsoft':
40
+ throw new Error(
41
+ 'Azure provider not yet implemented. ' +
42
+ 'AWS is currently the only supported cloud provider. ' +
43
+ 'Azure support is planned for future releases.'
44
+ );
45
+
46
+ default:
47
+ throw new Error(
48
+ `Unknown cloud provider: "${providerName}". ` +
49
+ `Supported providers: aws (gcp and azure coming soon)`
50
+ );
51
+ }
52
+ }
53
+
54
+ /**
55
+ * Get list of supported providers
56
+ *
57
+ * @returns {Array<Object>} List of provider metadata
58
+ */
59
+ static getSupportedProviders() {
60
+ return [
61
+ {
62
+ name: 'aws',
63
+ displayName: 'Amazon Web Services',
64
+ status: 'available',
65
+ description: 'AWS cloud provider with support for Lambda, VPC, RDS, KMS, etc.',
66
+ },
67
+ {
68
+ name: 'gcp',
69
+ displayName: 'Google Cloud Platform',
70
+ status: 'planned',
71
+ description: 'GCP cloud provider support coming soon',
72
+ },
73
+ {
74
+ name: 'azure',
75
+ displayName: 'Microsoft Azure',
76
+ status: 'planned',
77
+ description: 'Azure cloud provider support coming soon',
78
+ },
79
+ ];
80
+ }
81
+
82
+ /**
83
+ * Check if a provider is supported
84
+ *
85
+ * @param {string} providerName - Provider name to check
86
+ * @returns {boolean} True if provider is supported
87
+ */
88
+ static isSupported(providerName) {
89
+ const normalized = (providerName || '').toLowerCase();
90
+ return ['aws', 'gcp', 'google', 'azure', 'microsoft'].includes(normalized);
91
+ }
92
+
93
+ /**
94
+ * Check if a provider is available (implemented)
95
+ *
96
+ * @param {string} providerName - Provider name to check
97
+ * @returns {boolean} True if provider is implemented
98
+ */
99
+ static isAvailable(providerName) {
100
+ const normalized = (providerName || '').toLowerCase();
101
+ return normalized === 'aws';
102
+ }
103
+ }
104
+
105
+ module.exports = {
106
+ CloudProviderFactory,
107
+ };
108
+
@@ -0,0 +1,170 @@
1
+ /**
2
+ * Tests for CloudProviderFactory
3
+ *
4
+ * Verifies provider instantiation, error handling, and factory patterns
5
+ */
6
+
7
+ const { CloudProviderFactory } = require('./provider-factory');
8
+ const { AWSProviderAdapter } = require('./aws-provider-adapter');
9
+
10
+ describe('CloudProviderFactory', () => {
11
+ describe('create()', () => {
12
+ it('should create AWS provider when provider is "aws"', () => {
13
+ const provider = CloudProviderFactory.create('aws', 'us-east-1');
14
+
15
+ expect(provider).toBeInstanceOf(AWSProviderAdapter);
16
+ expect(provider.getName()).toBe('aws');
17
+ expect(provider.region).toBe('us-east-1');
18
+ });
19
+
20
+ it('should default to AWS provider when no provider specified', () => {
21
+ const provider = CloudProviderFactory.create(null, 'us-west-2');
22
+
23
+ expect(provider).toBeInstanceOf(AWSProviderAdapter);
24
+ expect(provider.region).toBe('us-west-2');
25
+ });
26
+
27
+ it('should be case-insensitive for provider names', () => {
28
+ const provider = CloudProviderFactory.create('AWS', 'eu-west-1');
29
+
30
+ expect(provider).toBeInstanceOf(AWSProviderAdapter);
31
+ });
32
+
33
+ it('should throw error for GCP (not yet implemented)', () => {
34
+ expect(() => {
35
+ CloudProviderFactory.create('gcp', 'us-central1');
36
+ }).toThrow('GCP provider not yet implemented');
37
+ });
38
+
39
+ it('should throw error for Azure (not yet implemented)', () => {
40
+ expect(() => {
41
+ CloudProviderFactory.create('azure', 'eastus');
42
+ }).toThrow('Azure provider not yet implemented');
43
+ });
44
+
45
+ it('should handle "google" alias for GCP', () => {
46
+ expect(() => {
47
+ CloudProviderFactory.create('google', 'us-central1');
48
+ }).toThrow('GCP provider not yet implemented');
49
+ });
50
+
51
+ it('should handle "microsoft" alias for Azure', () => {
52
+ expect(() => {
53
+ CloudProviderFactory.create('microsoft', 'eastus');
54
+ }).toThrow('Azure provider not yet implemented');
55
+ });
56
+
57
+ it('should throw error for unknown provider', () => {
58
+ expect(() => {
59
+ CloudProviderFactory.create('unknown-cloud', 'region-1');
60
+ }).toThrow('Unknown cloud provider: "unknown-cloud"');
61
+ });
62
+
63
+ it('should pass credentials to provider', () => {
64
+ const credentials = {
65
+ accessKeyId: 'test-key',
66
+ secretAccessKey: 'test-secret',
67
+ };
68
+
69
+ const provider = CloudProviderFactory.create('aws', 'us-east-1', credentials);
70
+
71
+ expect(provider.credentials).toEqual(credentials);
72
+ });
73
+ });
74
+
75
+ describe('getSupportedProviders()', () => {
76
+ it('should return list of supported providers', () => {
77
+ const providers = CloudProviderFactory.getSupportedProviders();
78
+
79
+ expect(Array.isArray(providers)).toBe(true);
80
+ expect(providers.length).toBeGreaterThanOrEqual(3);
81
+ });
82
+
83
+ it('should include AWS as available', () => {
84
+ const providers = CloudProviderFactory.getSupportedProviders();
85
+ const aws = providers.find(p => p.name === 'aws');
86
+
87
+ expect(aws).toBeDefined();
88
+ expect(aws.status).toBe('available');
89
+ expect(aws.displayName).toBe('Amazon Web Services');
90
+ });
91
+
92
+ it('should include GCP as planned', () => {
93
+ const providers = CloudProviderFactory.getSupportedProviders();
94
+ const gcp = providers.find(p => p.name === 'gcp');
95
+
96
+ expect(gcp).toBeDefined();
97
+ expect(gcp.status).toBe('planned');
98
+ expect(gcp.displayName).toBe('Google Cloud Platform');
99
+ });
100
+
101
+ it('should include Azure as planned', () => {
102
+ const providers = CloudProviderFactory.getSupportedProviders();
103
+ const azure = providers.find(p => p.name === 'azure');
104
+
105
+ expect(azure).toBeDefined();
106
+ expect(azure.status).toBe('planned');
107
+ expect(azure.displayName).toBe('Microsoft Azure');
108
+ });
109
+ });
110
+
111
+ describe('isSupported()', () => {
112
+ it('should return true for aws', () => {
113
+ expect(CloudProviderFactory.isSupported('aws')).toBe(true);
114
+ });
115
+
116
+ it('should return true for gcp', () => {
117
+ expect(CloudProviderFactory.isSupported('gcp')).toBe(true);
118
+ });
119
+
120
+ it('should return true for azure', () => {
121
+ expect(CloudProviderFactory.isSupported('azure')).toBe(true);
122
+ });
123
+
124
+ it('should return true for google (gcp alias)', () => {
125
+ expect(CloudProviderFactory.isSupported('google')).toBe(true);
126
+ });
127
+
128
+ it('should return true for microsoft (azure alias)', () => {
129
+ expect(CloudProviderFactory.isSupported('microsoft')).toBe(true);
130
+ });
131
+
132
+ it('should return false for unknown provider', () => {
133
+ expect(CloudProviderFactory.isSupported('unknown')).toBe(false);
134
+ });
135
+
136
+ it('should be case-insensitive', () => {
137
+ expect(CloudProviderFactory.isSupported('AWS')).toBe(true);
138
+ expect(CloudProviderFactory.isSupported('GCP')).toBe(true);
139
+ });
140
+
141
+ it('should handle null/undefined gracefully', () => {
142
+ expect(CloudProviderFactory.isSupported(null)).toBe(false);
143
+ expect(CloudProviderFactory.isSupported(undefined)).toBe(false);
144
+ });
145
+ });
146
+
147
+ describe('isAvailable()', () => {
148
+ it('should return true only for aws', () => {
149
+ expect(CloudProviderFactory.isAvailable('aws')).toBe(true);
150
+ });
151
+
152
+ it('should return false for gcp (not yet implemented)', () => {
153
+ expect(CloudProviderFactory.isAvailable('gcp')).toBe(false);
154
+ });
155
+
156
+ it('should return false for azure (not yet implemented)', () => {
157
+ expect(CloudProviderFactory.isAvailable('azure')).toBe(false);
158
+ });
159
+
160
+ it('should return false for unknown provider', () => {
161
+ expect(CloudProviderFactory.isAvailable('unknown')).toBe(false);
162
+ });
163
+
164
+ it('should be case-insensitive', () => {
165
+ expect(CloudProviderFactory.isAvailable('AWS')).toBe(true);
166
+ expect(CloudProviderFactory.isAvailable('GCP')).toBe(false);
167
+ });
168
+ });
169
+ });
170
+
@@ -0,0 +1,192 @@
1
+ /**
2
+ * Resource Discovery Service
3
+ *
4
+ * Domain Service - Hexagonal Architecture
5
+ *
6
+ * Orchestrates discovery of cloud resources (VPC, databases, encryption keys, etc.)
7
+ * using the cloud provider abstraction layer and domain-specific discovery services.
8
+ *
9
+ * This service is cloud-agnostic and delegates to provider-specific implementations.
10
+ */
11
+
12
+ const { CloudProviderFactory } = require('./providers/provider-factory');
13
+ const { CloudFormationDiscovery } = require('./cloudformation-discovery');
14
+ const { VpcDiscovery } = require('../networking/vpc-discovery');
15
+ const { KmsDiscovery } = require('../security/kms-discovery');
16
+ const { AuroraDiscovery } = require('../database/aurora-discovery');
17
+ const { SsmDiscovery } = require('../parameters/ssm-discovery');
18
+
19
+ /**
20
+ * Determine if AWS discovery should run
21
+ *
22
+ * @param {Object} appDefinition - Application definition
23
+ * @returns {boolean} True if discovery is needed
24
+ */
25
+ function shouldRunDiscovery(appDefinition) {
26
+ console.log(
27
+ '⚙️ Checking FRIGG_SKIP_AWS_DISCOVERY:',
28
+ process.env.FRIGG_SKIP_AWS_DISCOVERY
29
+ );
30
+
31
+ if (process.env.FRIGG_SKIP_AWS_DISCOVERY === 'true') {
32
+ console.log(
33
+ '⚙️ Skipping AWS discovery because FRIGG_SKIP_AWS_DISCOVERY is set.'
34
+ );
35
+ return false;
36
+ }
37
+
38
+ return (
39
+ appDefinition.vpc?.enable === true ||
40
+ appDefinition.encryption?.fieldLevelEncryptionMethod === 'kms' ||
41
+ appDefinition.ssm?.enable === true ||
42
+ appDefinition.database?.postgres?.enable === true
43
+ );
44
+ }
45
+
46
+ /**
47
+ * Gather discovered cloud resources
48
+ *
49
+ * Uses cloud provider abstraction to discover resources in a provider-agnostic way.
50
+ *
51
+ * @param {Object} appDefinition - Application definition
52
+ * @returns {Promise<Object>} Discovered cloud resources
53
+ */
54
+ async function gatherDiscoveredResources(appDefinition) {
55
+ if (!shouldRunDiscovery(appDefinition)) {
56
+ console.log('⚙️ Skipping cloud resource discovery (not required for this configuration)');
57
+ return {};
58
+ }
59
+
60
+ console.log('🔍 Running cloud resource discovery...');
61
+
62
+ try {
63
+ // Get cloud provider (defaults to AWS, can be overridden via env var)
64
+ const providerName = process.env.CLOUD_PROVIDER || appDefinition.provider || 'aws';
65
+ const region = process.env.AWS_REGION || 'us-east-1';
66
+
67
+ console.log(` Provider: ${providerName}`);
68
+ console.log(` Region: ${region}`);
69
+
70
+ // Create provider adapter
71
+ const provider = CloudProviderFactory.create(providerName, region);
72
+
73
+ // Build discovery configuration
74
+ const stage = process.env.SLS_STAGE || 'dev';
75
+ const stackName = `${appDefinition.name || 'create-frigg-app'}-${stage}`;
76
+ const serviceName = appDefinition.name || 'create-frigg-app';
77
+
78
+ // Try CloudFormation-first discovery
79
+ const cfDiscovery = new CloudFormationDiscovery(provider, { serviceName, stage });
80
+ const stackResources = await cfDiscovery.discoverFromStack(stackName);
81
+
82
+ // Validate CF discovery results - only use if contains useful data
83
+ const hasVpcData = stackResources?.defaultVpcId;
84
+ const hasKmsData = stackResources?.defaultKmsKeyId;
85
+ const hasAuroraData = stackResources?.auroraClusterId;
86
+ const hasSomeUsefulData = hasVpcData || hasKmsData || hasAuroraData;
87
+
88
+ // Check if we're in isolated mode (each stage gets its own VPC/Aurora)
89
+ const isIsolatedMode = appDefinition.managementMode === 'managed' &&
90
+ appDefinition.vpcIsolation === 'isolated';
91
+
92
+ if (stackResources && hasSomeUsefulData) {
93
+ console.log(' ✓ Discovered resources from existing CloudFormation stack');
94
+ console.log('✅ Cloud resource discovery completed successfully!');
95
+ return stackResources;
96
+ }
97
+
98
+ // In isolated mode, NEVER fall back to AWS discovery for VPC/Aurora
99
+ // These resources must be isolated per stage, so we either:
100
+ // 1. Use resources from THIS stage's CloudFormation stack (handled above)
101
+ // 2. Return empty to CREATE fresh isolated resources for this stage
102
+ if (isIsolatedMode) {
103
+ console.log(' ℹ Isolated mode: No CloudFormation stack or no VPC/Aurora in stack');
104
+ console.log(' ℹ Will create fresh isolated VPC/Aurora for this stage');
105
+ console.log(' ℹ Checking for shared KMS key...');
106
+
107
+ // KMS keys CAN be shared across stages (encryption keys are safe to reuse)
108
+ const kmsDiscovery = new KmsDiscovery(provider);
109
+ const kmsConfig = {
110
+ serviceName: appDefinition.name || 'create-frigg-app',
111
+ stage,
112
+ keyAlias: `alias/${appDefinition.name || 'create-frigg-app'}-${stage}-frigg-kms`,
113
+ };
114
+ const kmsResult = await kmsDiscovery.discover(kmsConfig);
115
+
116
+ if (kmsResult?.defaultKmsKeyId) {
117
+ console.log(' ✓ Found shared KMS key (can be reused across stages)');
118
+ console.log('✅ Cloud resource discovery completed - will create isolated VPC/Aurora!');
119
+ return kmsResult;
120
+ }
121
+
122
+ console.log(' ℹ No existing KMS key found - will create new one');
123
+ console.log('✅ Cloud resource discovery completed - will create fresh isolated resources!');
124
+ return {};
125
+ }
126
+
127
+ // Fallback to AWS API discovery (fresh deployment, stack not found, or stack has no useful data)
128
+ if (stackResources && !hasSomeUsefulData) {
129
+ console.log(' ℹ Stack found but contains no usable resources - running AWS API discovery...');
130
+ } else {
131
+ console.log(' ℹ No stack found - running AWS API discovery...');
132
+ }
133
+
134
+ // Create domain discovery services with provider
135
+ const vpcDiscovery = new VpcDiscovery(provider);
136
+ const kmsDiscovery = new KmsDiscovery(provider);
137
+ const auroraDiscovery = new AuroraDiscovery(provider);
138
+ const ssmDiscovery = new SsmDiscovery(provider);
139
+
140
+ const config = {
141
+ serviceName: appDefinition.name || 'create-frigg-app',
142
+ stage,
143
+ vpcId: appDefinition.vpc?.vpcId,
144
+ databaseId: appDefinition.database?.postgres?.clusterId ||
145
+ appDefinition.database?.postgres?.instanceId,
146
+ keyAlias: appDefinition.encryption?.keyAlias,
147
+ includeSecrets: true,
148
+ };
149
+
150
+ // Run discoveries in parallel for better performance
151
+ const [vpcResources, kmsResources, dbResources, ssmResources] =
152
+ await Promise.all([
153
+ appDefinition.vpc?.enable ? vpcDiscovery.discover(config) : Promise.resolve({}),
154
+ appDefinition.encryption?.fieldLevelEncryptionMethod === 'kms'
155
+ ? kmsDiscovery.discover(config)
156
+ : Promise.resolve({}),
157
+ appDefinition.database?.postgres?.enable
158
+ ? auroraDiscovery.discover(config)
159
+ : Promise.resolve({}),
160
+ appDefinition.ssm?.enable
161
+ ? ssmDiscovery.discover(config)
162
+ : Promise.resolve({}),
163
+ ]);
164
+
165
+ // Aggregate results
166
+ const discoveredResources = {
167
+ ...vpcResources,
168
+ ...kmsResources,
169
+ ...dbResources,
170
+ ...ssmResources,
171
+ };
172
+
173
+ console.log('✅ Cloud resource discovery completed successfully!');
174
+
175
+ return discoveredResources;
176
+ } catch (error) {
177
+ console.error('❌ Cloud resource discovery failed:', error.message);
178
+ console.error('Stack:', error.stack);
179
+
180
+ // Don't fail the build - return empty resources and let validation handle it
181
+ console.warn(
182
+ '⚠️ Continuing with empty discovered resources. This may cause deployment issues if resources are required.'
183
+ );
184
+ return {};
185
+ }
186
+ }
187
+
188
+ module.exports = {
189
+ shouldRunDiscovery,
190
+ gatherDiscoveredResources,
191
+ };
192
+