@friggframework/devtools 2.0.0--canary.461.ec909cf.0 → 2.0.0--canary.461.9483dbe.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 (71) hide show
  1. package/frigg-cli/__tests__/unit/commands/build.test.js +6 -6
  2. package/frigg-cli/build-command/index.js +1 -1
  3. package/frigg-cli/deploy-command/index.js +6 -6
  4. package/frigg-cli/generate-command/index.js +2 -2
  5. package/frigg-cli/generate-iam-command.js +10 -10
  6. package/frigg-cli/start-command/index.js +1 -1
  7. package/frigg-cli/start-command/start-command.test.js +3 -3
  8. package/frigg-cli/utils/database-validator.js +14 -21
  9. package/infrastructure/REFACTOR.md +532 -0
  10. package/infrastructure/TRANSFORMATION-VISUAL.md +239 -0
  11. package/infrastructure/__tests__/postgres-config.test.js +1 -1
  12. package/infrastructure/create-frigg-infrastructure.js +1 -1
  13. package/infrastructure/{DEPLOYMENT-INSTRUCTIONS.md → docs/deployment-instructions.md} +3 -3
  14. package/infrastructure/{IAM-POLICY-TEMPLATES.md → docs/iam-policy-templates.md} +9 -10
  15. package/infrastructure/domains/database/aurora-discovery.js +81 -0
  16. package/infrastructure/domains/database/aurora-discovery.test.js +188 -0
  17. package/infrastructure/domains/integration/integration-builder.js +178 -0
  18. package/infrastructure/domains/integration/integration-builder.test.js +362 -0
  19. package/infrastructure/domains/integration/websocket-builder.js +69 -0
  20. package/infrastructure/domains/integration/websocket-builder.test.js +195 -0
  21. package/infrastructure/domains/networking/vpc-discovery.test.js +257 -0
  22. package/infrastructure/domains/parameters/ssm-builder.js +79 -0
  23. package/infrastructure/domains/parameters/ssm-builder.test.js +188 -0
  24. package/infrastructure/domains/parameters/ssm-discovery.js +84 -0
  25. package/infrastructure/domains/parameters/ssm-discovery.test.js +210 -0
  26. package/infrastructure/{iam-generator.js → domains/security/iam-generator.js} +2 -2
  27. package/infrastructure/domains/security/kms-builder.js +169 -0
  28. package/infrastructure/domains/security/kms-builder.test.js +354 -0
  29. package/infrastructure/domains/security/kms-discovery.js +80 -0
  30. package/infrastructure/domains/security/kms-discovery.test.js +176 -0
  31. package/infrastructure/domains/shared/base-builder.js +112 -0
  32. package/infrastructure/domains/shared/builder-orchestrator.js +212 -0
  33. package/infrastructure/domains/shared/builder-orchestrator.test.js +213 -0
  34. package/infrastructure/domains/shared/environment-builder.js +118 -0
  35. package/infrastructure/domains/shared/environment-builder.test.js +246 -0
  36. package/infrastructure/domains/shared/providers/aws-provider-adapter.test.js +366 -0
  37. package/infrastructure/domains/shared/providers/azure-provider-adapter.stub.js +93 -0
  38. package/infrastructure/domains/shared/providers/cloud-provider-adapter.js +136 -0
  39. package/infrastructure/domains/shared/providers/gcp-provider-adapter.stub.js +82 -0
  40. package/infrastructure/domains/shared/providers/provider-factory.js +108 -0
  41. package/infrastructure/domains/shared/providers/provider-factory.test.js +170 -0
  42. package/infrastructure/domains/shared/resource-discovery.js +132 -0
  43. package/infrastructure/domains/shared/resource-discovery.test.js +410 -0
  44. package/infrastructure/domains/shared/utilities/base-definition-factory.js +2 -3
  45. package/infrastructure/domains/shared/utilities/base-definition-factory.js.bak +338 -0
  46. package/infrastructure/domains/shared/utilities/base-definition-factory.test.js +248 -0
  47. package/infrastructure/domains/shared/utilities/handler-path-resolver.test.js +259 -0
  48. package/infrastructure/domains/shared/utilities/prisma-layer-manager.js +55 -0
  49. package/infrastructure/domains/shared/utilities/prisma-layer-manager.test.js +134 -0
  50. package/infrastructure/domains/shared/validation/env-validator.test.js +173 -0
  51. package/infrastructure/esbuild.config.js +53 -0
  52. package/infrastructure/infrastructure-composer.js +85 -0
  53. package/infrastructure/scripts/build-prisma-layer.js +60 -47
  54. package/infrastructure/{build-time-discovery.test.js → scripts/build-time-discovery.test.js} +5 -4
  55. package/layers/prisma/nodejs/package.json +8 -0
  56. package/management-ui/server/utils/environment/awsParameterStore.js +29 -18
  57. package/package.json +8 -8
  58. package/infrastructure/aws-discovery.js +0 -1704
  59. package/infrastructure/aws-discovery.test.js +0 -1666
  60. package/infrastructure/serverless-template.js +0 -2804
  61. package/infrastructure/serverless-template.test.js +0 -1897
  62. /package/infrastructure/{POSTGRES-CONFIGURATION.md → docs/POSTGRES-CONFIGURATION.md} +0 -0
  63. /package/infrastructure/{WEBSOCKET-CONFIGURATION.md → docs/WEBSOCKET-CONFIGURATION.md} +0 -0
  64. /package/infrastructure/{GENERATE-IAM-DOCS.md → docs/generate-iam-command.md} +0 -0
  65. /package/infrastructure/{iam-generator.test.js → domains/security/iam-generator.test.js} +0 -0
  66. /package/infrastructure/{frigg-deployment-iam-stack.yaml → domains/security/templates/frigg-deployment-iam-stack.yaml} +0 -0
  67. /package/infrastructure/{iam-policy-basic.json → domains/security/templates/iam-policy-basic.json} +0 -0
  68. /package/infrastructure/{iam-policy-full.json → domains/security/templates/iam-policy-full.json} +0 -0
  69. /package/infrastructure/{env-validator.js → domains/shared/validation/env-validator.js} +0 -0
  70. /package/infrastructure/{build-time-discovery.js → scripts/build-time-discovery.js} +0 -0
  71. /package/infrastructure/{run-discovery.js → scripts/run-discovery.js} +0 -0
@@ -0,0 +1,112 @@
1
+ /**
2
+ * Base Infrastructure Builder Interface
3
+ *
4
+ * Domain Layer - Hexagonal Architecture
5
+ *
6
+ * This abstract class defines the contract for all infrastructure builders.
7
+ * Each infrastructure domain (VPC, KMS, Database, etc.) implements this interface.
8
+ *
9
+ * Benefits of Hexagonal Architecture:
10
+ * - Domain logic separated from infrastructure concerns
11
+ * - Easy to test in isolation
12
+ * - Dependency injection for cross-cutting concerns
13
+ * - Clear boundaries between domains
14
+ * - Enables parallel execution where dependencies allow
15
+ */
16
+
17
+ class InfrastructureBuilder {
18
+ /**
19
+ * Build infrastructure resources
20
+ *
21
+ * @param {Object} appDefinition - Application definition from user
22
+ * @param {Object} discoveredResources - Resources discovered from AWS
23
+ * @returns {Object} CloudFormation resources to add to template
24
+ * @throws {Error} If validation fails or build encounters errors
25
+ */
26
+ async build(appDefinition, discoveredResources) {
27
+ throw new Error('InfrastructureBuilder.build() must be implemented by subclass');
28
+ }
29
+
30
+ /**
31
+ * Validate configuration before building
32
+ *
33
+ * @param {Object} config - Configuration to validate
34
+ * @returns {Object} Validation result { valid: boolean, errors: string[] }
35
+ */
36
+ validate(config) {
37
+ throw new Error('InfrastructureBuilder.validate() must be implemented by subclass');
38
+ }
39
+
40
+ /**
41
+ * Check if this builder should execute
42
+ *
43
+ * @param {Object} appDefinition - Application definition
44
+ * @returns {boolean} True if builder should execute
45
+ */
46
+ shouldExecute(appDefinition) {
47
+ return false;
48
+ }
49
+
50
+ /**
51
+ * Get dependencies (other builders that must execute first)
52
+ *
53
+ * @returns {Array<string>} Array of builder names this depends on
54
+ */
55
+ getDependencies() {
56
+ return [];
57
+ }
58
+
59
+ /**
60
+ * Get builder name for logging and dependency resolution
61
+ *
62
+ * @returns {string} Builder name
63
+ */
64
+ getName() {
65
+ return this.constructor.name;
66
+ }
67
+ }
68
+
69
+ /**
70
+ * Value Object for validation results
71
+ */
72
+ class ValidationResult {
73
+ constructor(valid = true, errors = [], warnings = []) {
74
+ this.valid = valid;
75
+ this.errors = errors;
76
+ this.warnings = warnings;
77
+ }
78
+
79
+ addError(error) {
80
+ this.errors.push(error);
81
+ this.valid = false;
82
+ }
83
+
84
+ addWarning(warning) {
85
+ this.warnings.push(warning);
86
+ }
87
+
88
+ hasErrors() {
89
+ return this.errors.length > 0;
90
+ }
91
+
92
+ hasWarnings() {
93
+ return this.warnings.length > 0;
94
+ }
95
+
96
+ toString() {
97
+ let result = `Valid: ${this.valid}\n`;
98
+ if (this.errors.length > 0) {
99
+ result += `Errors:\n - ${this.errors.join('\n - ')}\n`;
100
+ }
101
+ if (this.warnings.length > 0) {
102
+ result += `Warnings:\n - ${this.warnings.join('\n - ')}\n`;
103
+ }
104
+ return result;
105
+ }
106
+ }
107
+
108
+ module.exports = {
109
+ InfrastructureBuilder,
110
+ ValidationResult,
111
+ };
112
+
@@ -0,0 +1,212 @@
1
+ /**
2
+ * Infrastructure Builder Orchestrator
3
+ *
4
+ * Application Layer - Hexagonal Architecture
5
+ *
6
+ * Orchestrates the execution of all infrastructure builders with:
7
+ * - Dependency resolution
8
+ * - Parallel execution where possible
9
+ * - Error handling and validation
10
+ * - Progress reporting
11
+ */
12
+
13
+ const { gatherDiscoveredResources } = require('./resource-discovery');
14
+ const { getAppEnvironmentVars, buildEnvironment } = require('./environment-builder');
15
+
16
+ class BuilderOrchestrator {
17
+ constructor(builders = []) {
18
+ this.builders = new Map();
19
+ builders.forEach(builder => this.registerBuilder(builder));
20
+ }
21
+
22
+ /**
23
+ * Register a builder
24
+ */
25
+ registerBuilder(builder) {
26
+ this.builders.set(builder.getName(), builder);
27
+ }
28
+
29
+ /**
30
+ * Validate all applicable builders
31
+ */
32
+ async validateAll(appDefinition) {
33
+ console.log('\n🔍 Validating infrastructure configuration...');
34
+
35
+ const validationResults = [];
36
+ let hasErrors = false;
37
+
38
+ for (const [name, builder] of this.builders) {
39
+ if (builder.shouldExecute(appDefinition)) {
40
+ const result = builder.validate(appDefinition);
41
+ validationResults.push({ builder: name, result });
42
+
43
+ if (result.hasErrors()) {
44
+ hasErrors = true;
45
+ console.log(`❌ ${name} validation failed:`);
46
+ result.errors.forEach(error => console.log(` - ${error}`));
47
+ }
48
+
49
+ if (result.hasWarnings()) {
50
+ console.log(`⚠️ ${name} warnings:`);
51
+ result.warnings.forEach(warning => console.log(` - ${warning}`));
52
+ }
53
+ }
54
+ }
55
+
56
+ if (hasErrors) {
57
+ throw new Error('Infrastructure validation failed. See errors above.');
58
+ }
59
+
60
+ console.log('✅ All infrastructure validation passed\n');
61
+ return validationResults;
62
+ }
63
+
64
+ /**
65
+ * Resolve builder execution order based on dependencies
66
+ */
67
+ resolveBuildOrder(appDefinition) {
68
+ const executionOrder = [];
69
+ const visited = new Set();
70
+ const visiting = new Set();
71
+
72
+ const visit = (builderName) => {
73
+ if (visited.has(builderName)) return;
74
+ if (visiting.has(builderName)) {
75
+ throw new Error(`Circular dependency detected: ${builderName}`);
76
+ }
77
+
78
+ const builder = this.builders.get(builderName);
79
+ if (!builder || !builder.shouldExecute(appDefinition)) {
80
+ return;
81
+ }
82
+
83
+ visiting.add(builderName);
84
+
85
+ // Visit dependencies first
86
+ const dependencies = builder.getDependencies() || [];
87
+ dependencies.forEach(dep => visit(dep));
88
+
89
+ visiting.delete(builderName);
90
+ visited.add(builderName);
91
+ executionOrder.push(builderName);
92
+ };
93
+
94
+ // Visit all builders
95
+ for (const [builderName] of this.builders) {
96
+ visit(builderName);
97
+ }
98
+
99
+ return executionOrder;
100
+ }
101
+
102
+ /**
103
+ * Build all infrastructure
104
+ */
105
+ async buildAll(appDefinition) {
106
+ console.log('\n🏗️ Building infrastructure...');
107
+
108
+ // Step 1: Validate configuration
109
+ await this.validateAll(appDefinition);
110
+
111
+ // Step 2: Discover AWS resources
112
+ const discoveredResources = await gatherDiscoveredResources(appDefinition);
113
+
114
+ // Step 3: Resolve build order
115
+ const buildOrder = this.resolveBuildOrder(appDefinition);
116
+ console.log(`📋 Build order: ${buildOrder.join(' → ')}\n`);
117
+
118
+ // Step 4: Execute builders in order
119
+ const buildResults = {};
120
+
121
+ for (const builderName of buildOrder) {
122
+ const builder = this.builders.get(builderName);
123
+ try {
124
+ const result = await builder.build(appDefinition, discoveredResources);
125
+ buildResults[builderName] = result;
126
+ } catch (error) {
127
+ console.error(`❌ ${builderName} build failed:`, error.message);
128
+ throw error;
129
+ }
130
+ }
131
+
132
+ // Step 5: Merge results
133
+ return this.mergeResults(buildResults, appDefinition, discoveredResources);
134
+ }
135
+
136
+ /**
137
+ * Merge builder results into cohesive definition
138
+ */
139
+ mergeResults(buildResults, appDefinition, discoveredResources) {
140
+ console.log('\n🔗 Merging infrastructure results...');
141
+
142
+ const merged = {
143
+ resources: {},
144
+ iamStatements: [],
145
+ environment: {},
146
+ functions: {},
147
+ layers: {},
148
+ plugins: [],
149
+ custom: {},
150
+ vpcConfig: null,
151
+ };
152
+
153
+ // Merge results from each builder
154
+ for (const [builderName, result] of Object.entries(buildResults)) {
155
+ // Merge resources
156
+ if (result.resources) {
157
+ Object.assign(merged.resources, result.resources);
158
+ }
159
+
160
+ // Merge IAM statements
161
+ if (result.iamStatements) {
162
+ merged.iamStatements.push(...result.iamStatements);
163
+ }
164
+
165
+ // Merge environment variables
166
+ if (result.environment) {
167
+ Object.assign(merged.environment, result.environment);
168
+ }
169
+
170
+ // Merge functions
171
+ if (result.functions) {
172
+ Object.assign(merged.functions, result.functions);
173
+ }
174
+
175
+ // Merge layers
176
+ if (result.layers) {
177
+ Object.assign(merged.layers, result.layers);
178
+ }
179
+
180
+ // Merge plugins
181
+ if (result.plugins) {
182
+ merged.plugins.push(...result.plugins);
183
+ }
184
+
185
+ // Merge custom configuration
186
+ if (result.pluginConfig) {
187
+ Object.assign(merged.custom, result.pluginConfig);
188
+ }
189
+ if (result.custom) {
190
+ Object.assign(merged.custom, result.custom);
191
+ }
192
+
193
+ // Capture VPC config (from VpcBuilder)
194
+ if (result.vpcConfig) {
195
+ merged.vpcConfig = result.vpcConfig;
196
+ }
197
+
198
+ console.log(` ✓ Merged ${builderName} results`);
199
+ }
200
+
201
+ console.log('✅ Infrastructure build completed successfully\n');
202
+
203
+ return {
204
+ merged,
205
+ discoveredResources,
206
+ appEnvironmentVars: getAppEnvironmentVars(appDefinition),
207
+ };
208
+ }
209
+ }
210
+
211
+ module.exports = { BuilderOrchestrator };
212
+
@@ -0,0 +1,213 @@
1
+ /**
2
+ * Tests for BuilderOrchestrator
3
+ *
4
+ * Tests orchestration, dependency resolution, and parallel execution
5
+ */
6
+
7
+ const { BuilderOrchestrator } = require('./builder-orchestrator');
8
+ const { InfrastructureBuilder, ValidationResult } = require('./base-builder');
9
+
10
+ // Mock builders for testing
11
+ class MockBuilderA extends InfrastructureBuilder {
12
+ getName() { return 'MockBuilderA'; }
13
+ shouldExecute(appDef) { return appDef.featureA === true; }
14
+ validate(appDef) { return new ValidationResult(); }
15
+ async build(appDef, discovered) {
16
+ return {
17
+ resources: { ResourceA: { Type: 'Test' } },
18
+ iamStatements: [{ Effect: 'Allow', Action: 'test:A' }],
19
+ };
20
+ }
21
+ }
22
+
23
+ class MockBuilderB extends InfrastructureBuilder {
24
+ getName() { return 'MockBuilderB'; }
25
+ shouldExecute(appDef) { return appDef.featureB === true; }
26
+ validate(appDef) { return new ValidationResult(); }
27
+ getDependencies() { return ['MockBuilderA']; } // Depends on A
28
+ async build(appDef, discovered) {
29
+ return {
30
+ resources: { ResourceB: { Type: 'Test' } },
31
+ environment: { VAR_B: 'value-b' },
32
+ };
33
+ }
34
+ }
35
+
36
+ class MockBuilderC extends InfrastructureBuilder {
37
+ getName() { return 'MockBuilderC'; }
38
+ shouldExecute() { return false; } // Never executes
39
+ validate() { return new ValidationResult(); }
40
+ async build() {
41
+ throw new Error('Should not be called');
42
+ }
43
+ }
44
+
45
+ jest.mock('./resource-discovery', () => ({
46
+ gatherDiscoveredResources: jest.fn().mockResolvedValue({ discovered: true }),
47
+ }));
48
+
49
+ jest.mock('./environment-builder', () => ({
50
+ getAppEnvironmentVars: jest.fn().mockReturnValue({ ENV_VAR: 'value' }),
51
+ buildEnvironment: jest.fn().mockReturnValue({ ENV_VAR: 'value' }),
52
+ }));
53
+
54
+ describe('BuilderOrchestrator', () => {
55
+ let orchestrator;
56
+
57
+ beforeEach(() => {
58
+ jest.clearAllMocks();
59
+ });
60
+
61
+ describe('registerBuilder()', () => {
62
+ it('should register builders', () => {
63
+ orchestrator = new BuilderOrchestrator();
64
+ const builder = new MockBuilderA();
65
+
66
+ orchestrator.registerBuilder(builder);
67
+
68
+ expect(orchestrator.builders.has('MockBuilderA')).toBe(true);
69
+ });
70
+
71
+ it('should register multiple builders via constructor', () => {
72
+ orchestrator = new BuilderOrchestrator([
73
+ new MockBuilderA(),
74
+ new MockBuilderB(),
75
+ ]);
76
+
77
+ expect(orchestrator.builders.size).toBe(2);
78
+ });
79
+ });
80
+
81
+ describe('validateAll()', () => {
82
+ it('should validate all applicable builders', async () => {
83
+ orchestrator = new BuilderOrchestrator([
84
+ new MockBuilderA(),
85
+ new MockBuilderB(),
86
+ ]);
87
+
88
+ const appDef = { featureA: true, featureB: true };
89
+
90
+ await expect(orchestrator.validateAll(appDef)).resolves.toBeDefined();
91
+ });
92
+
93
+ it('should skip builders that shouldNotExecute', async () => {
94
+ orchestrator = new BuilderOrchestrator([
95
+ new MockBuilderA(),
96
+ new MockBuilderC(), // Should not execute
97
+ ]);
98
+
99
+ const appDef = { featureA: true };
100
+
101
+ const results = await orchestrator.validateAll(appDef);
102
+
103
+ // Should only validate MockBuilderA
104
+ expect(results).toHaveLength(1);
105
+ expect(results[0].builder).toBe('MockBuilderA');
106
+ });
107
+
108
+ it('should throw error if validation fails', async () => {
109
+ class FailingBuilder extends InfrastructureBuilder {
110
+ getName() { return 'FailingBuilder'; }
111
+ shouldExecute() { return true; }
112
+ validate() {
113
+ const result = new ValidationResult();
114
+ result.addError('Test error');
115
+ return result;
116
+ }
117
+ }
118
+
119
+ orchestrator = new BuilderOrchestrator([new FailingBuilder()]);
120
+
121
+ await expect(orchestrator.validateAll({})).rejects.toThrow('Infrastructure validation failed');
122
+ });
123
+ });
124
+
125
+ describe('resolveBuildOrder()', () => {
126
+ it('should resolve dependencies correctly', () => {
127
+ orchestrator = new BuilderOrchestrator([
128
+ new MockBuilderB(), // Depends on A
129
+ new MockBuilderA(), // No dependencies
130
+ ]);
131
+
132
+ const appDef = { featureA: true, featureB: true };
133
+ const order = orchestrator.resolveBuildOrder(appDef);
134
+
135
+ // A should come before B
136
+ expect(order).toEqual(['MockBuilderA', 'MockBuilderB']);
137
+ });
138
+
139
+ it('should handle builders with no dependencies', () => {
140
+ orchestrator = new BuilderOrchestrator([
141
+ new MockBuilderA(),
142
+ ]);
143
+
144
+ const appDef = { featureA: true };
145
+ const order = orchestrator.resolveBuildOrder(appDef);
146
+
147
+ expect(order).toEqual(['MockBuilderA']);
148
+ });
149
+
150
+ it('should skip builders that should not execute', () => {
151
+ orchestrator = new BuilderOrchestrator([
152
+ new MockBuilderA(),
153
+ new MockBuilderC(), // Should not execute
154
+ ]);
155
+
156
+ const appDef = { featureA: true };
157
+ const order = orchestrator.resolveBuildOrder(appDef);
158
+
159
+ expect(order).toEqual(['MockBuilderA']);
160
+ });
161
+ });
162
+
163
+ describe('buildAll()', () => {
164
+ it('should build all infrastructure and merge results', async () => {
165
+ orchestrator = new BuilderOrchestrator([
166
+ new MockBuilderA(),
167
+ new MockBuilderB(),
168
+ ]);
169
+
170
+ const appDef = { featureA: true, featureB: true };
171
+
172
+ const result = await orchestrator.buildAll(appDef);
173
+
174
+ expect(result.merged).toBeDefined();
175
+ expect(result.merged.resources).toMatchObject({
176
+ ResourceA: { Type: 'Test' },
177
+ ResourceB: { Type: 'Test' },
178
+ });
179
+ expect(result.merged.iamStatements).toHaveLength(1);
180
+ expect(result.merged.environment).toMatchObject({ VAR_B: 'value-b' });
181
+ });
182
+
183
+ it('should skip builders that should not execute', async () => {
184
+ orchestrator = new BuilderOrchestrator([
185
+ new MockBuilderA(),
186
+ new MockBuilderC(), // Should not execute
187
+ ]);
188
+
189
+ const appDef = { featureA: true };
190
+
191
+ const result = await orchestrator.buildAll(appDef);
192
+
193
+ // Should only have results from A
194
+ expect(Object.keys(result.merged.resources)).toEqual(['ResourceA']);
195
+ });
196
+
197
+ it('should throw error if builder fails', async () => {
198
+ class FailingBuilder extends InfrastructureBuilder {
199
+ getName() { return 'FailingBuilder'; }
200
+ shouldExecute() { return true; }
201
+ validate() { return new ValidationResult(); }
202
+ async build() {
203
+ throw new Error('Build failed');
204
+ }
205
+ }
206
+
207
+ orchestrator = new BuilderOrchestrator([new FailingBuilder()]);
208
+
209
+ await expect(orchestrator.buildAll({})).rejects.toThrow('Build failed');
210
+ });
211
+ });
212
+ });
213
+
@@ -0,0 +1,118 @@
1
+ /**
2
+ * Environment Builder Service
3
+ *
4
+ * Domain Service - Hexagonal Architecture
5
+ *
6
+ * Builds Lambda environment variable configuration from:
7
+ * 1. AppDefinition environment flags
8
+ * 2. Discovered AWS resources (VPC IDs, KMS keys, etc.)
9
+ * 3. Generated resource references
10
+ */
11
+
12
+ /**
13
+ * Get environment variables from AppDefinition
14
+ *
15
+ * Extracts environment variable definitions where value is true,
16
+ * and creates Serverless variable references.
17
+ *
18
+ * @param {Object} appDefinition - Application definition
19
+ * @returns {Object} Environment variable mappings
20
+ */
21
+ function getAppEnvironmentVars(appDefinition) {
22
+ const envVars = {};
23
+ const reservedVars = new Set([
24
+ '_HANDLER',
25
+ '_X_AMZN_TRACE_ID',
26
+ 'AWS_DEFAULT_REGION',
27
+ 'AWS_EXECUTION_ENV',
28
+ 'AWS_REGION',
29
+ 'AWS_LAMBDA_FUNCTION_NAME',
30
+ 'AWS_LAMBDA_FUNCTION_MEMORY_SIZE',
31
+ 'AWS_LAMBDA_FUNCTION_VERSION',
32
+ 'AWS_LAMBDA_INITIALIZATION_TYPE',
33
+ 'AWS_LAMBDA_LOG_GROUP_NAME',
34
+ 'AWS_LAMBDA_LOG_STREAM_NAME',
35
+ 'AWS_ACCESS_KEY',
36
+ 'AWS_ACCESS_KEY_ID',
37
+ 'AWS_SECRET_ACCESS_KEY',
38
+ 'AWS_SESSION_TOKEN',
39
+ ]);
40
+
41
+ if (!appDefinition.environment) {
42
+ return envVars;
43
+ }
44
+
45
+ console.log('📋 Loading environment variables from appDefinition...');
46
+ const envKeys = [];
47
+ const skippedKeys = [];
48
+
49
+ for (const [key, value] of Object.entries(appDefinition.environment)) {
50
+ if (value !== true) continue;
51
+ if (reservedVars.has(key)) {
52
+ skippedKeys.push(key);
53
+ continue;
54
+ }
55
+ envVars[key] = `\${env:${key}, ''}`;
56
+ envKeys.push(key);
57
+ }
58
+
59
+ if (envKeys.length > 0) {
60
+ console.log(
61
+ ` Found ${envKeys.length} environment variables: ${envKeys.join(
62
+ ', '
63
+ )}`
64
+ );
65
+ }
66
+ if (skippedKeys.length > 0) {
67
+ console.log(
68
+ ` ⚠️ Skipped ${skippedKeys.length
69
+ } reserved AWS Lambda variables: ${skippedKeys.join(', ')}`
70
+ );
71
+ }
72
+
73
+ return envVars;
74
+ }
75
+
76
+ /**
77
+ * Build complete environment configuration for Lambda functions
78
+ *
79
+ * Combines app environment vars with discovered AWS resource references
80
+ *
81
+ * @param {Object} appEnvironmentVars - Environment vars from AppDefinition
82
+ * @param {Object} discoveredResources - Discovered AWS resources
83
+ * @returns {Object} Complete environment configuration
84
+ */
85
+ function buildEnvironment(appEnvironmentVars, discoveredResources) {
86
+ const environment = {
87
+ ...appEnvironmentVars,
88
+ FRIGG_STACK: '${self:service}',
89
+ FRIGG_STAGE: '${self:provider.stage}',
90
+ FRIGG_REGION: '${self:provider.region}',
91
+ };
92
+
93
+ // Add KMS key if discovered or created
94
+ if (discoveredResources.kmsKeyId) {
95
+ environment.KMS_KEY_ARN = discoveredResources.kmsKeyId;
96
+ } else if (discoveredResources.kmsKeyArn) {
97
+ environment.KMS_KEY_ARN = discoveredResources.kmsKeyArn;
98
+ }
99
+
100
+ // Add database connection info if discovered
101
+ if (discoveredResources.auroraClusterEndpoint) {
102
+ environment.DATABASE_HOST = discoveredResources.auroraClusterEndpoint;
103
+ environment.DATABASE_PORT = String(discoveredResources.auroraPort || 5432);
104
+ }
105
+
106
+ // Add secrets manager secret ARN if discovered
107
+ if (discoveredResources.databaseSecretArn) {
108
+ environment.DATABASE_SECRET_ARN = discoveredResources.databaseSecretArn;
109
+ }
110
+
111
+ return environment;
112
+ }
113
+
114
+ module.exports = {
115
+ getAppEnvironmentVars,
116
+ buildEnvironment,
117
+ };
118
+