@friggframework/devtools 2.0.0--canary.461.ea64a60.0 → 2.0.0--canary.461.d5b9ddf.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.
@@ -65,7 +65,11 @@ class MigrationBuilder extends InfrastructureBuilder {
65
65
  DeletionPolicy: 'Retain', // Protect migration history during stack rollbacks/deletions
66
66
  UpdateReplacePolicy: 'Retain', // Protect during stack updates that require replacement
67
67
  Properties: {
68
- BucketName: '${self:service}-${self:provider.stage}-migration-status',
68
+ // Let CloudFormation auto-generate bucket name for global uniqueness
69
+ // Result: ${StackName}-friggmigrationstatusbucket-${randomHash}
70
+ // Example: quo-integrations-prod-friggmigrationstatusbucket-abc123xyz
71
+ // This ensures no conflicts across accounts/regions/stages
72
+ // BucketName: undefined (CloudFormation generates unique name)
69
73
  VersioningConfiguration: {
70
74
  Status: 'Enabled', // Enable versioning for audit trail
71
75
  },
@@ -372,10 +372,17 @@ class VpcBuilder extends InfrastructureBuilder {
372
372
  */
373
373
  async buildSubnets(appDefinition, discoveredResources, result) {
374
374
  const vpcManagement = appDefinition.vpc.management || 'discover';
375
- const defaultSubnetManagement = vpcManagement === 'create-new' ? 'create' : 'discover';
375
+ // Default subnet management depends on context:
376
+ // - use-existing mode with subnet IDs provided: use-existing
377
+ // - create-new mode: create
378
+ // - discover mode: create (for stage isolation)
379
+ let defaultSubnetManagement = 'create';
380
+ if (vpcManagement === 'use-existing' && appDefinition.vpc.subnets?.ids?.length >= 2) {
381
+ defaultSubnetManagement = 'use-existing';
382
+ }
376
383
  const subnetManagement = appDefinition.vpc.subnets?.management || defaultSubnetManagement;
377
384
 
378
- console.log(` Subnet Management Mode: ${subnetManagement}`);
385
+ console.log(` Subnet Management Mode: ${subnetManagement} (default: ${defaultSubnetManagement}, explicit: ${appDefinition.vpc.subnets?.management})`);
379
386
 
380
387
  switch (subnetManagement) {
381
388
  case 'create':
@@ -508,17 +515,19 @@ class VpcBuilder extends InfrastructureBuilder {
508
515
  return;
509
516
  }
510
517
 
511
- // Use discovered subnets
518
+ // User explicitly set subnets.management: 'discover', so use discovered subnets
519
+ // NOTE: This may cause route table conflicts if multiple stages share subnets
520
+ // Default behavior is now to create stage-specific subnets (subnets.management: 'create')
512
521
  if (discoveredResources.privateSubnetId1 && discoveredResources.privateSubnetId2) {
513
522
  result.vpcConfig.subnetIds = [
514
523
  discoveredResources.privateSubnetId1,
515
524
  discoveredResources.privateSubnetId2,
516
525
  ];
517
- console.log(' ✅ Discovered 2 private subnets');
526
+ console.log(' ✅ Using discovered subnets (backwards compatibility mode)');
518
527
  return;
519
528
  }
520
529
 
521
- // Fallback: create if self-heal enabled
530
+ // No subnets found - create if self-heal enabled
522
531
  if (appDefinition.vpc.selfHeal) {
523
532
  console.log(' ⚠️ No subnets found - self-heal will create them');
524
533
  this.createSubnets(appDefinition, discoveredResources, result, 'discover');
@@ -241,7 +241,7 @@ describe('VpcBuilder', () => {
241
241
  });
242
242
 
243
243
  describe('build() - discover mode', () => {
244
- it('should use discovered VPC resources', async () => {
244
+ it('should use discovered VPC but create stage-specific subnets by default', async () => {
245
245
  const appDefinition = {
246
246
  vpc: {
247
247
  enable: true,
@@ -251,6 +251,7 @@ describe('VpcBuilder', () => {
251
251
 
252
252
  const discoveredResources = {
253
253
  defaultVpcId: 'vpc-discovered',
254
+ // Even though subnets are discovered, we should create new ones for stage isolation
254
255
  privateSubnetId1: 'subnet-private1',
255
256
  privateSubnetId2: 'subnet-private2',
256
257
  defaultSecurityGroupId: 'sg-discovered',
@@ -258,13 +259,43 @@ describe('VpcBuilder', () => {
258
259
 
259
260
  const result = await vpcBuilder.build(appDefinition, discoveredResources);
260
261
 
261
- expect(result.vpcConfig.subnetIds).toEqual(['subnet-private1', 'subnet-private2']);
262
+ // NEW BEHAVIOR: Create stage-specific subnets for isolation (prevent route table conflicts)
263
+ expect(result.vpcConfig.subnetIds).toEqual([
264
+ { Ref: 'FriggPrivateSubnet1' },
265
+ { Ref: 'FriggPrivateSubnet2' },
266
+ ]);
267
+ expect(result.resources.FriggPrivateSubnet1).toBeDefined();
268
+ expect(result.resources.FriggPrivateSubnet2).toBeDefined();
262
269
  // In discover mode, we create FriggLambdaSecurityGroup in the discovered VPC
263
270
  expect(result.vpcConfig.securityGroupIds).toEqual([{ Ref: 'FriggLambdaSecurityGroup' }]);
264
271
  expect(result.resources.FriggLambdaSecurityGroup).toBeDefined();
265
272
  expect(result.resources.FriggLambdaSecurityGroup.Properties.VpcId).toBe('vpc-discovered');
266
273
  });
267
274
 
275
+ it('should allow sharing discovered subnets when explicitly configured', async () => {
276
+ const appDefinition = {
277
+ vpc: {
278
+ enable: true,
279
+ management: 'discover',
280
+ subnets: {
281
+ management: 'discover', // Explicitly opt-in to subnet sharing
282
+ },
283
+ },
284
+ };
285
+
286
+ const discoveredResources = {
287
+ defaultVpcId: 'vpc-discovered',
288
+ privateSubnetId1: 'subnet-shared-1',
289
+ privateSubnetId2: 'subnet-shared-2',
290
+ };
291
+
292
+ const result = await vpcBuilder.build(appDefinition, discoveredResources);
293
+
294
+ // OLD BEHAVIOR: When explicitly set to 'discover', reuse discovered subnets
295
+ expect(result.vpcConfig.subnetIds).toEqual(['subnet-shared-1', 'subnet-shared-2']);
296
+ expect(result.resources.FriggPrivateSubnet1).toBeUndefined();
297
+ });
298
+
268
299
  it('should create VPC endpoints in discover mode with selfHeal when none exist', async () => {
269
300
  const appDefinition = {
270
301
  vpc: {
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.ea64a60.0",
4
+ "version": "2.0.0--canary.461.d5b9ddf.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.ea64a60.0",
15
- "@friggframework/test": "2.0.0--canary.461.ea64a60.0",
14
+ "@friggframework/schemas": "2.0.0--canary.461.d5b9ddf.0",
15
+ "@friggframework/test": "2.0.0--canary.461.d5b9ddf.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.ea64a60.0",
38
- "@friggframework/prettier-config": "2.0.0--canary.461.ea64a60.0",
37
+ "@friggframework/eslint-config": "2.0.0--canary.461.d5b9ddf.0",
38
+ "@friggframework/prettier-config": "2.0.0--canary.461.d5b9ddf.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": "ea64a60d6ba9a69e06cd06dd4a3eace9b71f466b"
73
+ "gitHead": "d5b9ddfa8deb275e2275139ca229781776609fa4"
74
74
  }