@friggframework/devtools 2.0.0--canary.461.77c8d12.0 → 2.0.0--canary.461.39e4094.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.
@@ -122,18 +122,21 @@ class CloudFormationDiscovery {
122
122
  if (LogicalResourceId === 'FriggLambdaSecurityGroup' && ResourceType === 'AWS::EC2::SecurityGroup') {
123
123
  discovered.securityGroupId = PhysicalResourceId;
124
124
  console.log(` ✓ Found security group in stack: ${PhysicalResourceId}`);
125
- // Query security group to get VPC ID
126
- if (this.provider && !discovered.defaultVpcId) {
125
+
126
+ // Query security group to get VPC ID (required because SG resource doesn't include VPC ID)
127
+ if (this.provider && this.provider.getEC2Client && !discovered.defaultVpcId) {
127
128
  try {
128
- console.log(` Querying security group to get VPC ID...`);
129
+ console.log(` Querying EC2 to get VPC ID from security group...`);
129
130
  const { DescribeSecurityGroupsCommand } = require('@aws-sdk/client-ec2');
130
- const sgDetails = await this.provider.getEC2Client().send(
131
+ const ec2Client = this.provider.getEC2Client();
132
+ const sgDetails = await ec2Client.send(
131
133
  new DescribeSecurityGroupsCommand({
132
134
  GroupIds: [PhysicalResourceId]
133
135
  })
134
136
  );
137
+
135
138
  if (sgDetails.SecurityGroups && sgDetails.SecurityGroups.length > 0) {
136
- discovered.defaultVpcId = sgDetails.SecurityGroups[0].VpcId; // VpcBuilder expects 'defaultVpcId'
139
+ discovered.defaultVpcId = sgDetails.SecurityGroups[0].VpcId;
137
140
  console.log(` ✓ Extracted VPC ID from security group: ${discovered.defaultVpcId}`);
138
141
  } else {
139
142
  console.warn(` ⚠️ Security group query returned no results`);
@@ -193,6 +196,12 @@ class CloudFormationDiscovery {
193
196
  discovered.natGatewayId = PhysicalResourceId;
194
197
  }
195
198
 
199
+ // VPC - direct extraction (primary method)
200
+ if (LogicalResourceId === 'FriggVPC' && ResourceType === 'AWS::EC2::VPC') {
201
+ discovered.defaultVpcId = PhysicalResourceId;
202
+ console.log(` ✓ Found VPC in stack: ${PhysicalResourceId}`);
203
+ }
204
+
196
205
  // KMS Key (alternative to output)
197
206
  if (LogicalResourceId === 'FriggKMSKey' && ResourceType === 'AWS::KMS::Key') {
198
207
  // Note: For KMS, we prefer the ARN from outputs, but this is a fallback
@@ -253,6 +253,30 @@ describe('CloudFormationDiscovery', () => {
253
253
  });
254
254
  });
255
255
 
256
+ it('should extract VPC directly from stack resources', async () => {
257
+ const mockStack = {
258
+ StackName: 'test-stack',
259
+ Outputs: [],
260
+ };
261
+
262
+ const mockResources = [
263
+ {
264
+ LogicalResourceId: 'FriggVPC',
265
+ PhysicalResourceId: 'vpc-037ec55fe87aec1e7',
266
+ ResourceType: 'AWS::EC2::VPC',
267
+ },
268
+ ];
269
+
270
+ mockProvider.describeStack.mockResolvedValue(mockStack);
271
+ mockProvider.listStackResources.mockResolvedValue(mockResources);
272
+
273
+ const result = await cfDiscovery.discoverFromStack('test-stack');
274
+
275
+ expect(result).toEqual({
276
+ defaultVpcId: 'vpc-037ec55fe87aec1e7',
277
+ });
278
+ });
279
+
256
280
  it('should combine outputs and resources correctly', async () => {
257
281
  const mockStack = {
258
282
  StackName: 'test-stack',
@@ -85,19 +85,26 @@ async function gatherDiscoveredResources(appDefinition) {
85
85
  const hasAuroraData = stackResources?.auroraClusterId;
86
86
  const hasSomeUsefulData = hasVpcData || hasKmsData || hasAuroraData;
87
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
+
88
92
  if (stackResources && hasSomeUsefulData) {
89
93
  console.log(' ✓ Discovered resources from existing CloudFormation stack');
90
94
  console.log('✅ Cloud resource discovery completed successfully!');
91
95
  return stackResources;
92
96
  }
93
97
 
94
- // In isolated mode, ONLY use CloudFormation discovery for VPC/Aurora
95
- // But still discover KMS (encryption keys can be safely shared across stages)
96
- if (appDefinition.managementMode === 'managed' && appDefinition.vpcIsolation === 'isolated') {
97
- console.log(' ℹ Isolated mode: discovering KMS (shareable) but not VPC/Aurora (isolated)');
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...');
98
106
 
99
- // Still run KMS discovery - encryption keys are safe to share
100
- // Pass serviceName and stage to search for stage-specific alias
107
+ // KMS keys CAN be shared across stages (encryption keys are safe to reuse)
101
108
  const kmsDiscovery = new KmsDiscovery(provider);
102
109
  const kmsConfig = {
103
110
  serviceName: appDefinition.name || 'create-frigg-app',
@@ -108,12 +115,12 @@ async function gatherDiscoveredResources(appDefinition) {
108
115
 
109
116
  if (kmsResult?.defaultKmsKeyId) {
110
117
  console.log(' ✓ Found shared KMS key (can be reused across stages)');
111
- console.log('✅ Cloud resource discovery completed successfully!');
118
+ console.log('✅ Cloud resource discovery completed - will create isolated VPC/Aurora!');
112
119
  return kmsResult;
113
120
  }
114
121
 
115
122
  console.log(' ℹ No existing KMS key found - will create new one');
116
- console.log('✅ Cloud resource discovery completed successfully!');
123
+ console.log('✅ Cloud resource discovery completed - will create fresh isolated resources!');
117
124
  return {};
118
125
  }
119
126
 
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@friggframework/devtools",
3
3
  "prettier": "@friggframework/prettier-config",
4
- "version": "2.0.0--canary.461.77c8d12.0",
4
+ "version": "2.0.0--canary.461.39e4094.0",
5
5
  "dependencies": {
6
6
  "@aws-sdk/client-ec2": "^3.835.0",
7
7
  "@aws-sdk/client-kms": "^3.835.0",
@@ -11,8 +11,8 @@
11
11
  "@babel/eslint-parser": "^7.18.9",
12
12
  "@babel/parser": "^7.25.3",
13
13
  "@babel/traverse": "^7.25.3",
14
- "@friggframework/schemas": "2.0.0--canary.461.77c8d12.0",
15
- "@friggframework/test": "2.0.0--canary.461.77c8d12.0",
14
+ "@friggframework/schemas": "2.0.0--canary.461.39e4094.0",
15
+ "@friggframework/test": "2.0.0--canary.461.39e4094.0",
16
16
  "@hapi/boom": "^10.0.1",
17
17
  "@inquirer/prompts": "^5.3.8",
18
18
  "axios": "^1.7.2",
@@ -34,8 +34,8 @@
34
34
  "serverless-http": "^2.7.0"
35
35
  },
36
36
  "devDependencies": {
37
- "@friggframework/eslint-config": "2.0.0--canary.461.77c8d12.0",
38
- "@friggframework/prettier-config": "2.0.0--canary.461.77c8d12.0",
37
+ "@friggframework/eslint-config": "2.0.0--canary.461.39e4094.0",
38
+ "@friggframework/prettier-config": "2.0.0--canary.461.39e4094.0",
39
39
  "aws-sdk-client-mock": "^4.1.0",
40
40
  "aws-sdk-client-mock-jest": "^4.1.0",
41
41
  "jest": "^30.1.3",
@@ -70,5 +70,5 @@
70
70
  "publishConfig": {
71
71
  "access": "public"
72
72
  },
73
- "gitHead": "77c8d12f6a33bb2d66e19b43b6d96ee27d6f5e06"
73
+ "gitHead": "39e40947472c29e3847fa53dad03ab1354568e56"
74
74
  }