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

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.
@@ -3,9 +3,14 @@ const fs = require('fs');
3
3
  const { AWSDiscovery } = require('./aws-discovery');
4
4
 
5
5
  const shouldRunDiscovery = (AppDefinition) => {
6
- console.log('⚙️ Checking FRIGG_SKIP_AWS_DISCOVERY:', process.env.FRIGG_SKIP_AWS_DISCOVERY);
6
+ console.log(
7
+ '⚙️ Checking FRIGG_SKIP_AWS_DISCOVERY:',
8
+ process.env.FRIGG_SKIP_AWS_DISCOVERY
9
+ );
7
10
  if (process.env.FRIGG_SKIP_AWS_DISCOVERY === 'true') {
8
- console.log('⚙️ Skipping AWS discovery because FRIGG_SKIP_AWS_DISCOVERY is set.');
11
+ console.log(
12
+ '⚙️ Skipping AWS discovery because FRIGG_SKIP_AWS_DISCOVERY is set.'
13
+ );
9
14
  return false;
10
15
  }
11
16
 
@@ -55,11 +60,16 @@ const getAppEnvironmentVars = (AppDefinition) => {
55
60
  }
56
61
 
57
62
  if (envKeys.length > 0) {
58
- console.log(` Found ${envKeys.length} environment variables: ${envKeys.join(', ')}`);
63
+ console.log(
64
+ ` Found ${envKeys.length} environment variables: ${envKeys.join(
65
+ ', '
66
+ )}`
67
+ );
59
68
  }
60
69
  if (skippedKeys.length > 0) {
61
70
  console.log(
62
- ` ⚠️ Skipped ${skippedKeys.length} reserved AWS Lambda variables: ${skippedKeys.join(', ')}`
71
+ ` ⚠️ Skipped ${skippedKeys.length
72
+ } reserved AWS Lambda variables: ${skippedKeys.join(', ')}`
63
73
  );
64
74
  }
65
75
 
@@ -75,7 +85,9 @@ const findNodeModulesPath = () => {
75
85
  const potentialPath = path.join(currentDir, 'node_modules');
76
86
  if (fs.existsSync(potentialPath)) {
77
87
  nodeModulesPath = potentialPath;
78
- console.log(`Found node_modules at: ${nodeModulesPath} (method 1)`);
88
+ console.log(
89
+ `Found node_modules at: ${nodeModulesPath} (method 1)`
90
+ );
79
91
  break;
80
92
  }
81
93
  const parentDir = path.dirname(currentDir);
@@ -86,10 +98,14 @@ const findNodeModulesPath = () => {
86
98
  if (!nodeModulesPath) {
87
99
  try {
88
100
  const { execSync } = require('node:child_process');
89
- const npmRoot = execSync('npm root', { encoding: 'utf8' }).trim();
101
+ const npmRoot = execSync('npm root', {
102
+ encoding: 'utf8',
103
+ }).trim();
90
104
  if (fs.existsSync(npmRoot)) {
91
105
  nodeModulesPath = npmRoot;
92
- console.log(`Found node_modules at: ${nodeModulesPath} (method 2)`);
106
+ console.log(
107
+ `Found node_modules at: ${nodeModulesPath} (method 2)`
108
+ );
93
109
  }
94
110
  } catch (npmError) {
95
111
  console.error('Error executing npm root:', npmError);
@@ -101,10 +117,15 @@ const findNodeModulesPath = () => {
101
117
  for (let i = 0; i < 5; i++) {
102
118
  const packageJsonPath = path.join(currentDir, 'package.json');
103
119
  if (fs.existsSync(packageJsonPath)) {
104
- const potentialNodeModules = path.join(currentDir, 'node_modules');
120
+ const potentialNodeModules = path.join(
121
+ currentDir,
122
+ 'node_modules'
123
+ );
105
124
  if (fs.existsSync(potentialNodeModules)) {
106
125
  nodeModulesPath = potentialNodeModules;
107
- console.log(`Found node_modules at: ${nodeModulesPath} (method 3)`);
126
+ console.log(
127
+ `Found node_modules at: ${nodeModulesPath} (method 3)`
128
+ );
108
129
  break;
109
130
  }
110
131
  }
@@ -118,7 +139,9 @@ const findNodeModulesPath = () => {
118
139
  return nodeModulesPath;
119
140
  }
120
141
 
121
- console.warn('Could not find node_modules path, falling back to default');
142
+ console.warn(
143
+ 'Could not find node_modules path, falling back to default'
144
+ );
122
145
  return path.resolve(process.cwd(), '../node_modules');
123
146
  } catch (error) {
124
147
  console.error('Error finding node_modules path:', error);
@@ -143,8 +166,13 @@ const modifyHandlerPaths = (functions) => {
143
166
  const functionDef = modifiedFunctions[functionName];
144
167
  if (functionDef?.handler?.includes('node_modules/')) {
145
168
  const relativePath = path.relative(process.cwd(), nodeModulesPath);
146
- functionDef.handler = functionDef.handler.replace('node_modules/', `${relativePath}/`);
147
- console.log(`Updated handler for ${functionName}: ${functionDef.handler}`);
169
+ functionDef.handler = functionDef.handler.replace(
170
+ 'node_modules/',
171
+ `${relativePath}/`
172
+ );
173
+ console.log(
174
+ `Updated handler for ${functionName}: ${functionDef.handler}`
175
+ );
148
176
  }
149
177
  }
150
178
 
@@ -160,7 +188,10 @@ const createVPCInfrastructure = (AppDefinition) => {
160
188
  EnableDnsHostnames: true,
161
189
  EnableDnsSupport: true,
162
190
  Tags: [
163
- { Key: 'Name', Value: '${self:service}-${self:provider.stage}-vpc' },
191
+ {
192
+ Key: 'Name',
193
+ Value: '${self:service}-${self:provider.stage}-vpc',
194
+ },
164
195
  { Key: 'ManagedBy', Value: 'Frigg' },
165
196
  { Key: 'Service', Value: '${self:service}' },
166
197
  { Key: 'Stage', Value: '${self:provider.stage}' },
@@ -171,7 +202,10 @@ const createVPCInfrastructure = (AppDefinition) => {
171
202
  Type: 'AWS::EC2::InternetGateway',
172
203
  Properties: {
173
204
  Tags: [
174
- { Key: 'Name', Value: '${self:service}-${self:provider.stage}-igw' },
205
+ {
206
+ Key: 'Name',
207
+ Value: '${self:service}-${self:provider.stage}-igw',
208
+ },
175
209
  { Key: 'ManagedBy', Value: 'Frigg' },
176
210
  { Key: 'Service', Value: '${self:service}' },
177
211
  { Key: 'Stage', Value: '${self:provider.stage}' },
@@ -193,7 +227,10 @@ const createVPCInfrastructure = (AppDefinition) => {
193
227
  AvailabilityZone: { 'Fn::Select': [0, { 'Fn::GetAZs': '' }] },
194
228
  MapPublicIpOnLaunch: true,
195
229
  Tags: [
196
- { Key: 'Name', Value: '${self:service}-${self:provider.stage}-public-subnet' },
230
+ {
231
+ Key: 'Name',
232
+ Value: '${self:service}-${self:provider.stage}-public-subnet',
233
+ },
197
234
  { Key: 'ManagedBy', Value: 'Frigg' },
198
235
  { Key: 'Service', Value: '${self:service}' },
199
236
  { Key: 'Stage', Value: '${self:provider.stage}' },
@@ -208,7 +245,10 @@ const createVPCInfrastructure = (AppDefinition) => {
208
245
  CidrBlock: '10.0.2.0/24',
209
246
  AvailabilityZone: { 'Fn::Select': [0, { 'Fn::GetAZs': '' }] },
210
247
  Tags: [
211
- { Key: 'Name', Value: '${self:service}-${self:provider.stage}-private-subnet-1' },
248
+ {
249
+ Key: 'Name',
250
+ Value: '${self:service}-${self:provider.stage}-private-subnet-1',
251
+ },
212
252
  { Key: 'ManagedBy', Value: 'Frigg' },
213
253
  { Key: 'Service', Value: '${self:service}' },
214
254
  { Key: 'Stage', Value: '${self:provider.stage}' },
@@ -223,7 +263,10 @@ const createVPCInfrastructure = (AppDefinition) => {
223
263
  CidrBlock: '10.0.3.0/24',
224
264
  AvailabilityZone: { 'Fn::Select': [1, { 'Fn::GetAZs': '' }] },
225
265
  Tags: [
226
- { Key: 'Name', Value: '${self:service}-${self:provider.stage}-private-subnet-2' },
266
+ {
267
+ Key: 'Name',
268
+ Value: '${self:service}-${self:provider.stage}-private-subnet-2',
269
+ },
227
270
  { Key: 'ManagedBy', Value: 'Frigg' },
228
271
  { Key: 'Service', Value: '${self:service}' },
229
272
  { Key: 'Stage', Value: '${self:provider.stage}' },
@@ -236,7 +279,10 @@ const createVPCInfrastructure = (AppDefinition) => {
236
279
  Properties: {
237
280
  Domain: 'vpc',
238
281
  Tags: [
239
- { Key: 'Name', Value: '${self:service}-${self:provider.stage}-nat-eip' },
282
+ {
283
+ Key: 'Name',
284
+ Value: '${self:service}-${self:provider.stage}-nat-eip',
285
+ },
240
286
  { Key: 'ManagedBy', Value: 'Frigg' },
241
287
  { Key: 'Service', Value: '${self:service}' },
242
288
  { Key: 'Stage', Value: '${self:provider.stage}' },
@@ -247,10 +293,15 @@ const createVPCInfrastructure = (AppDefinition) => {
247
293
  FriggNATGateway: {
248
294
  Type: 'AWS::EC2::NatGateway',
249
295
  Properties: {
250
- AllocationId: { 'Fn::GetAtt': ['FriggNATGatewayEIP', 'AllocationId'] },
296
+ AllocationId: {
297
+ 'Fn::GetAtt': ['FriggNATGatewayEIP', 'AllocationId'],
298
+ },
251
299
  SubnetId: { Ref: 'FriggPublicSubnet' },
252
300
  Tags: [
253
- { Key: 'Name', Value: '${self:service}-${self:provider.stage}-nat-gateway' },
301
+ {
302
+ Key: 'Name',
303
+ Value: '${self:service}-${self:provider.stage}-nat-gateway',
304
+ },
254
305
  { Key: 'ManagedBy', Value: 'Frigg' },
255
306
  { Key: 'Service', Value: '${self:service}' },
256
307
  { Key: 'Stage', Value: '${self:provider.stage}' },
@@ -262,7 +313,10 @@ const createVPCInfrastructure = (AppDefinition) => {
262
313
  Properties: {
263
314
  VpcId: { Ref: 'FriggVPC' },
264
315
  Tags: [
265
- { Key: 'Name', Value: '${self:service}-${self:provider.stage}-public-rt' },
316
+ {
317
+ Key: 'Name',
318
+ Value: '${self:service}-${self:provider.stage}-public-rt',
319
+ },
266
320
  { Key: 'ManagedBy', Value: 'Frigg' },
267
321
  { Key: 'Service', Value: '${self:service}' },
268
322
  { Key: 'Stage', Value: '${self:provider.stage}' },
@@ -291,7 +345,10 @@ const createVPCInfrastructure = (AppDefinition) => {
291
345
  Properties: {
292
346
  VpcId: { Ref: 'FriggVPC' },
293
347
  Tags: [
294
- { Key: 'Name', Value: '${self:service}-${self:provider.stage}-private-rt' },
348
+ {
349
+ Key: 'Name',
350
+ Value: '${self:service}-${self:provider.stage}-private-rt',
351
+ },
295
352
  { Key: 'ManagedBy', Value: 'Frigg' },
296
353
  { Key: 'Service', Value: '${self:service}' },
297
354
  { Key: 'Stage', Value: '${self:provider.stage}' },
@@ -327,14 +384,47 @@ const createVPCInfrastructure = (AppDefinition) => {
327
384
  GroupDescription: 'Security group for Frigg Lambda functions',
328
385
  VpcId: { Ref: 'FriggVPC' },
329
386
  SecurityGroupEgress: [
330
- { IpProtocol: 'tcp', FromPort: 443, ToPort: 443, CidrIp: '0.0.0.0/0', Description: 'HTTPS outbound' },
331
- { IpProtocol: 'tcp', FromPort: 80, ToPort: 80, CidrIp: '0.0.0.0/0', Description: 'HTTP outbound' },
332
- { IpProtocol: 'tcp', FromPort: 53, ToPort: 53, CidrIp: '0.0.0.0/0', Description: 'DNS TCP' },
333
- { IpProtocol: 'udp', FromPort: 53, ToPort: 53, CidrIp: '0.0.0.0/0', Description: 'DNS UDP' },
334
- { IpProtocol: 'tcp', FromPort: 27017, ToPort: 27017, CidrIp: '0.0.0.0/0', Description: 'MongoDB outbound' },
387
+ {
388
+ IpProtocol: 'tcp',
389
+ FromPort: 443,
390
+ ToPort: 443,
391
+ CidrIp: '0.0.0.0/0',
392
+ Description: 'HTTPS outbound',
393
+ },
394
+ {
395
+ IpProtocol: 'tcp',
396
+ FromPort: 80,
397
+ ToPort: 80,
398
+ CidrIp: '0.0.0.0/0',
399
+ Description: 'HTTP outbound',
400
+ },
401
+ {
402
+ IpProtocol: 'tcp',
403
+ FromPort: 53,
404
+ ToPort: 53,
405
+ CidrIp: '0.0.0.0/0',
406
+ Description: 'DNS TCP',
407
+ },
408
+ {
409
+ IpProtocol: 'udp',
410
+ FromPort: 53,
411
+ ToPort: 53,
412
+ CidrIp: '0.0.0.0/0',
413
+ Description: 'DNS UDP',
414
+ },
415
+ {
416
+ IpProtocol: 'tcp',
417
+ FromPort: 27017,
418
+ ToPort: 27017,
419
+ CidrIp: '0.0.0.0/0',
420
+ Description: 'MongoDB outbound',
421
+ },
335
422
  ],
336
423
  Tags: [
337
- { Key: 'Name', Value: '${self:service}-${self:provider.stage}-lambda-sg' },
424
+ {
425
+ Key: 'Name',
426
+ Value: '${self:service}-${self:provider.stage}-lambda-sg',
427
+ },
338
428
  { Key: 'ManagedBy', Value: 'Frigg' },
339
429
  { Key: 'Service', Value: '${self:service}' },
340
430
  { Key: 'Stage', Value: '${self:provider.stage}' },
@@ -371,8 +461,13 @@ const createVPCInfrastructure = (AppDefinition) => {
371
461
  VpcId: { Ref: 'FriggVPC' },
372
462
  ServiceName: 'com.amazonaws.${self:provider.region}.kms',
373
463
  VpcEndpointType: 'Interface',
374
- SubnetIds: [{ Ref: 'FriggPrivateSubnet1' }, { Ref: 'FriggPrivateSubnet2' }],
375
- SecurityGroupIds: [{ Ref: 'FriggVPCEndpointSecurityGroup' }],
464
+ SubnetIds: [
465
+ { Ref: 'FriggPrivateSubnet1' },
466
+ { Ref: 'FriggPrivateSubnet2' },
467
+ ],
468
+ SecurityGroupIds: [
469
+ { Ref: 'FriggVPCEndpointSecurityGroup' },
470
+ ],
376
471
  PrivateDnsEnabled: true,
377
472
  },
378
473
  };
@@ -382,9 +477,13 @@ const createVPCInfrastructure = (AppDefinition) => {
382
477
  Type: 'AWS::EC2::VPCEndpoint',
383
478
  Properties: {
384
479
  VpcId: { Ref: 'FriggVPC' },
385
- ServiceName: 'com.amazonaws.${self:provider.region}.secretsmanager',
480
+ ServiceName:
481
+ 'com.amazonaws.${self:provider.region}.secretsmanager',
386
482
  VpcEndpointType: 'Interface',
387
- SubnetIds: [{ Ref: 'FriggPrivateSubnet1' }, { Ref: 'FriggPrivateSubnet2' }],
483
+ SubnetIds: [
484
+ { Ref: 'FriggPrivateSubnet1' },
485
+ { Ref: 'FriggPrivateSubnet2' },
486
+ ],
388
487
  SecurityGroupIds: [{ Ref: 'FriggVPCEndpointSecurityGroup' }],
389
488
  PrivateDnsEnabled: true,
390
489
  },
@@ -393,14 +492,17 @@ const createVPCInfrastructure = (AppDefinition) => {
393
492
  vpcResources.FriggVPCEndpointSecurityGroup = {
394
493
  Type: 'AWS::EC2::SecurityGroup',
395
494
  Properties: {
396
- GroupDescription: 'Security group for Frigg VPC Endpoints - allows HTTPS from Lambda functions',
495
+ GroupDescription:
496
+ 'Security group for Frigg VPC Endpoints - allows HTTPS from Lambda functions',
397
497
  VpcId: { Ref: 'FriggVPC' },
398
498
  SecurityGroupIngress: [
399
499
  {
400
500
  IpProtocol: 'tcp',
401
501
  FromPort: 443,
402
502
  ToPort: 443,
403
- SourceSecurityGroupId: { Ref: 'FriggLambdaSecurityGroup' },
503
+ SourceSecurityGroupId: {
504
+ Ref: 'FriggLambdaSecurityGroup',
505
+ },
404
506
  Description: 'HTTPS from Lambda security group',
405
507
  },
406
508
  {
@@ -412,12 +514,18 @@ const createVPCInfrastructure = (AppDefinition) => {
412
514
  },
413
515
  ],
414
516
  Tags: [
415
- { Key: 'Name', Value: '${self:service}-${self:provider.stage}-vpc-endpoint-sg' },
517
+ {
518
+ Key: 'Name',
519
+ Value: '${self:service}-${self:provider.stage}-vpc-endpoint-sg',
520
+ },
416
521
  { Key: 'ManagedBy', Value: 'Frigg' },
417
522
  { Key: 'Service', Value: '${self:service}' },
418
523
  { Key: 'Stage', Value: '${self:provider.stage}' },
419
524
  { Key: 'Type', Value: 'VPCEndpoint' },
420
- { Key: 'Purpose', Value: 'Allow Lambda functions to access VPC endpoints' },
525
+ {
526
+ Key: 'Purpose',
527
+ Value: 'Allow Lambda functions to access VPC endpoints',
528
+ },
421
529
  ],
422
530
  },
423
531
  };
@@ -447,13 +555,18 @@ const gatherDiscoveredResources = async (AppDefinition) => {
447
555
  if (discoveredResources.defaultVpcId) {
448
556
  console.log(` VPC: ${discoveredResources.defaultVpcId}`);
449
557
  }
450
- if (discoveredResources.privateSubnetId1 && discoveredResources.privateSubnetId2) {
558
+ if (
559
+ discoveredResources.privateSubnetId1 &&
560
+ discoveredResources.privateSubnetId2
561
+ ) {
451
562
  console.log(
452
563
  ` Subnets: ${discoveredResources.privateSubnetId1}, ${discoveredResources.privateSubnetId2}`
453
564
  );
454
565
  }
455
566
  if (discoveredResources.defaultSecurityGroupId) {
456
- console.log(` Security Group: ${discoveredResources.defaultSecurityGroupId}`);
567
+ console.log(
568
+ ` Security Group: ${discoveredResources.defaultSecurityGroupId}`
569
+ );
457
570
  }
458
571
  if (discoveredResources.defaultKmsKeyId) {
459
572
  console.log(` KMS Key: ${discoveredResources.defaultKmsKeyId}`);
@@ -492,7 +605,11 @@ const buildEnvironment = (appEnvironmentVars, discoveredResources) => {
492
605
  return environment;
493
606
  };
494
607
 
495
- const createBaseDefinition = (AppDefinition, appEnvironmentVars, discoveredResources) => {
608
+ const createBaseDefinition = (
609
+ AppDefinition,
610
+ appEnvironmentVars,
611
+ discoveredResources
612
+ ) => {
496
613
  const region = process.env.AWS_REGION || 'us-east-1';
497
614
 
498
615
  return {
@@ -500,7 +617,11 @@ const createBaseDefinition = (AppDefinition, appEnvironmentVars, discoveredResou
500
617
  service: AppDefinition.name || 'create-frigg-app',
501
618
  package: {
502
619
  individually: true,
503
- exclude: ['!**/node_modules/aws-sdk/**', '!**/node_modules/@aws-sdk/**', '!package.json'],
620
+ exclude: [
621
+ '!**/node_modules/aws-sdk/**',
622
+ '!**/node_modules/@aws-sdk/**',
623
+ '!package.json',
624
+ ],
504
625
  },
505
626
  useDotenv: true,
506
627
  provider: {
@@ -509,7 +630,10 @@ const createBaseDefinition = (AppDefinition, appEnvironmentVars, discoveredResou
509
630
  timeout: 30,
510
631
  region,
511
632
  stage: '${opt:stage}',
512
- environment: buildEnvironment(appEnvironmentVars, discoveredResources),
633
+ environment: buildEnvironment(
634
+ appEnvironmentVars,
635
+ discoveredResources
636
+ ),
513
637
  iamRoleStatements: [
514
638
  {
515
639
  Effect: 'Allow',
@@ -518,13 +642,20 @@ const createBaseDefinition = (AppDefinition, appEnvironmentVars, discoveredResou
518
642
  },
519
643
  {
520
644
  Effect: 'Allow',
521
- Action: ['sqs:SendMessage', 'sqs:SendMessageBatch', 'sqs:GetQueueUrl', 'sqs:GetQueueAttributes'],
645
+ Action: [
646
+ 'sqs:SendMessage',
647
+ 'sqs:SendMessageBatch',
648
+ 'sqs:GetQueueUrl',
649
+ 'sqs:GetQueueAttributes',
650
+ ],
522
651
  Resource: [
523
652
  { 'Fn::GetAtt': ['InternalErrorQueue', 'Arn'] },
524
653
  {
525
654
  'Fn::Join': [
526
655
  ':',
527
- ['arn:aws:sqs:${self:provider.region}:*:${self:service}--${self:provider.stage}-*Queue'],
656
+ [
657
+ 'arn:aws:sqs:${self:provider.region}:*:${self:service}--${self:provider.stage}-*Queue',
658
+ ],
528
659
  ],
529
660
  },
530
661
  ],
@@ -543,7 +674,8 @@ const createBaseDefinition = (AppDefinition, appEnvironmentVars, discoveredResou
543
674
  },
544
675
  },
545
676
  plugins: [
546
- 'serverless-jetpack',
677
+ // Temporarily disabled Jetpack - it ignores package.patterns in dependency mode
678
+ // 'serverless-jetpack',
547
679
  'serverless-dotenv-plugin',
548
680
  'serverless-offline-sqs',
549
681
  'serverless-offline',
@@ -564,25 +696,36 @@ const createBaseDefinition = (AppDefinition, appEnvironmentVars, discoveredResou
564
696
  secretAccessKey: 'root',
565
697
  skipCacheInvalidation: false,
566
698
  },
567
- jetpack: {
568
- base: '..',
569
- },
699
+ // Jetpack config removed - testing with standard Serverless packaging
700
+ // jetpack: {
701
+ // base: '..',
702
+ // },
570
703
  },
571
704
  functions: {
572
705
  auth: {
573
- handler: 'node_modules/@friggframework/core/handlers/routers/auth.handler',
706
+ handler:
707
+ 'node_modules/@friggframework/core/handlers/routers/auth.handler',
574
708
  events: [
575
709
  { httpApi: { path: '/api/integrations', method: 'ANY' } },
576
- { httpApi: { path: '/api/integrations/{proxy+}', method: 'ANY' } },
710
+ {
711
+ httpApi: {
712
+ path: '/api/integrations/{proxy+}',
713
+ method: 'ANY',
714
+ },
715
+ },
577
716
  { httpApi: { path: '/api/authorize', method: 'ANY' } },
578
717
  ],
579
718
  },
580
719
  user: {
581
- handler: 'node_modules/@friggframework/core/handlers/routers/user.handler',
582
- events: [{ httpApi: { path: '/user/{proxy+}', method: 'ANY' } }],
720
+ handler:
721
+ 'node_modules/@friggframework/core/handlers/routers/user.handler',
722
+ events: [
723
+ { httpApi: { path: '/user/{proxy+}', method: 'ANY' } },
724
+ ],
583
725
  },
584
726
  health: {
585
- handler: 'node_modules/@friggframework/core/handlers/routers/health.handler',
727
+ handler:
728
+ 'node_modules/@friggframework/core/handlers/routers/health.handler',
586
729
  events: [
587
730
  { httpApi: { path: '/health', method: 'GET' } },
588
731
  { httpApi: { path: '/health/{proxy+}', method: 'GET' } },
@@ -594,7 +737,8 @@ const createBaseDefinition = (AppDefinition, appEnvironmentVars, discoveredResou
594
737
  InternalErrorQueue: {
595
738
  Type: 'AWS::SQS::Queue',
596
739
  Properties: {
597
- QueueName: '${self:service}-internal-error-queue-${self:provider.stage}',
740
+ QueueName:
741
+ '${self:service}-internal-error-queue-${self:provider.stage}',
598
742
  MessageRetentionPeriod: 300,
599
743
  },
600
744
  },
@@ -604,7 +748,9 @@ const createBaseDefinition = (AppDefinition, appEnvironmentVars, discoveredResou
604
748
  Subscription: [
605
749
  {
606
750
  Protocol: 'sqs',
607
- Endpoint: { 'Fn::GetAtt': ['InternalErrorQueue', 'Arn'] },
751
+ Endpoint: {
752
+ 'Fn::GetAtt': ['InternalErrorQueue', 'Arn'],
753
+ },
608
754
  },
609
755
  ],
610
756
  },
@@ -620,10 +766,22 @@ const createBaseDefinition = (AppDefinition, appEnvironmentVars, discoveredResou
620
766
  Sid: 'Allow Dead Letter SNS to publish to SQS',
621
767
  Effect: 'Allow',
622
768
  Principal: { Service: 'sns.amazonaws.com' },
623
- Resource: { 'Fn::GetAtt': ['InternalErrorQueue', 'Arn'] },
624
- Action: ['SQS:SendMessage', 'SQS:SendMessageBatch'],
769
+ Resource: {
770
+ 'Fn::GetAtt': [
771
+ 'InternalErrorQueue',
772
+ 'Arn',
773
+ ],
774
+ },
775
+ Action: [
776
+ 'SQS:SendMessage',
777
+ 'SQS:SendMessageBatch',
778
+ ],
625
779
  Condition: {
626
- ArnEquals: { 'aws:SourceArn': { Ref: 'InternalErrorBridgeTopic' } },
780
+ ArnEquals: {
781
+ 'aws:SourceArn': {
782
+ Ref: 'InternalErrorBridgeTopic',
783
+ },
784
+ },
627
785
  },
628
786
  },
629
787
  ],
@@ -653,24 +811,33 @@ const createBaseDefinition = (AppDefinition, appEnvironmentVars, discoveredResou
653
811
  };
654
812
  };
655
813
 
656
- const applyKmsConfiguration = (definition, AppDefinition, discoveredResources) => {
814
+ const applyKmsConfiguration = (
815
+ definition,
816
+ AppDefinition,
817
+ discoveredResources
818
+ ) => {
657
819
  if (AppDefinition.encryption?.fieldLevelEncryptionMethod !== 'kms') {
658
820
  return;
659
821
  }
660
822
 
661
823
  // Skip KMS configuration for local development when AWS discovery is disabled
662
824
  if (process.env.FRIGG_SKIP_AWS_DISCOVERY === 'true') {
663
- console.log('⚙️ Skipping KMS configuration for local development (FRIGG_SKIP_AWS_DISCOVERY is set)');
825
+ console.log(
826
+ '⚙️ Skipping KMS configuration for local development (FRIGG_SKIP_AWS_DISCOVERY is set)'
827
+ );
664
828
  return;
665
829
  }
666
830
 
667
831
  if (discoveredResources.defaultKmsKeyId) {
668
- console.log(`Using existing KMS key: ${discoveredResources.defaultKmsKeyId}`);
832
+ console.log(
833
+ `Using existing KMS key: ${discoveredResources.defaultKmsKeyId}`
834
+ );
669
835
  definition.resources.Resources.FriggKMSKeyAlias = {
670
836
  Type: 'AWS::KMS::Alias',
671
837
  DeletionPolicy: 'Retain',
672
838
  Properties: {
673
- AliasName: 'alias/${self:service}-${self:provider.stage}-frigg-kms',
839
+ AliasName:
840
+ 'alias/${self:service}-${self:provider.stage}-frigg-kms',
674
841
  TargetKeyId: discoveredResources.defaultKmsKeyId,
675
842
  },
676
843
  };
@@ -684,7 +851,7 @@ const applyKmsConfiguration = (definition, AppDefinition, discoveredResources) =
684
851
  if (AppDefinition.encryption?.createResourceIfNoneFound !== true) {
685
852
  throw new Error(
686
853
  'KMS field-level encryption is enabled but no KMS key was found. ' +
687
- 'Either provide an existing KMS key or set encryption.createResourceIfNoneFound to true to create a new key.'
854
+ 'Either provide an existing KMS key or set encryption.createResourceIfNoneFound to true to create a new key.'
688
855
  );
689
856
  }
690
857
 
@@ -703,7 +870,10 @@ const applyKmsConfiguration = (definition, AppDefinition, discoveredResources) =
703
870
  Sid: 'AllowRootAccountAdmin',
704
871
  Effect: 'Allow',
705
872
  Principal: {
706
- AWS: { 'Fn::Sub': 'arn:aws:iam::${AWS::AccountId}:root' },
873
+ AWS: {
874
+ 'Fn::Sub':
875
+ 'arn:aws:iam::${AWS::AccountId}:root',
876
+ },
707
877
  },
708
878
  Action: 'kms:*',
709
879
  Resource: '*',
@@ -712,20 +882,31 @@ const applyKmsConfiguration = (definition, AppDefinition, discoveredResources) =
712
882
  Sid: 'AllowLambdaService',
713
883
  Effect: 'Allow',
714
884
  Principal: { Service: 'lambda.amazonaws.com' },
715
- Action: ['kms:GenerateDataKey', 'kms:Decrypt', 'kms:DescribeKey'],
885
+ Action: [
886
+ 'kms:GenerateDataKey',
887
+ 'kms:Decrypt',
888
+ 'kms:DescribeKey',
889
+ ],
716
890
  Resource: '*',
717
891
  Condition: {
718
892
  StringEquals: {
719
- 'kms:ViaService': `lambda.${process.env.AWS_REGION || 'us-east-1'}.amazonaws.com`,
893
+ 'kms:ViaService': `lambda.${process.env.AWS_REGION || 'us-east-1'
894
+ }.amazonaws.com`,
720
895
  },
721
896
  },
722
897
  },
723
898
  ],
724
899
  },
725
900
  Tags: [
726
- { Key: 'Name', Value: '${self:service}-${self:provider.stage}-frigg-kms-key' },
901
+ {
902
+ Key: 'Name',
903
+ Value: '${self:service}-${self:provider.stage}-frigg-kms-key',
904
+ },
727
905
  { Key: 'ManagedBy', Value: 'Frigg' },
728
- { Key: 'Purpose', Value: 'Field-level encryption for Frigg application' },
906
+ {
907
+ Key: 'Purpose',
908
+ Value: 'Field-level encryption for Frigg application',
909
+ },
729
910
  ],
730
911
  },
731
912
  };
@@ -734,7 +915,8 @@ const applyKmsConfiguration = (definition, AppDefinition, discoveredResources) =
734
915
  Type: 'AWS::KMS::Alias',
735
916
  DeletionPolicy: 'Retain',
736
917
  Properties: {
737
- AliasName: 'alias/${self:service}-${self:provider.stage}-frigg-kms',
918
+ AliasName:
919
+ 'alias/${self:service}-${self:provider.stage}-frigg-kms',
738
920
  TargetKeyId: { 'Fn::GetAtt': ['FriggKMSKey', 'Arn'] },
739
921
  },
740
922
  };
@@ -745,7 +927,9 @@ const applyKmsConfiguration = (definition, AppDefinition, discoveredResources) =
745
927
  Resource: [{ 'Fn::GetAtt': ['FriggKMSKey', 'Arn'] }],
746
928
  });
747
929
 
748
- definition.provider.environment.KMS_KEY_ARN = { 'Fn::GetAtt': ['FriggKMSKey', 'Arn'] };
930
+ definition.provider.environment.KMS_KEY_ARN = {
931
+ 'Fn::GetAtt': ['FriggKMSKey', 'Arn'],
932
+ };
749
933
  definition.custom.kmsGrants = {
750
934
  kmsKeyId: { 'Fn::GetAtt': ['FriggKMSKey', 'Arn'] },
751
935
  };
@@ -754,13 +938,16 @@ const applyKmsConfiguration = (definition, AppDefinition, discoveredResources) =
754
938
  definition.plugins.push('serverless-kms-grants');
755
939
  if (!definition.custom.kmsGrants) {
756
940
  definition.custom.kmsGrants = {
757
- kmsKeyId: discoveredResources.defaultKmsKeyId || '${env:AWS_DISCOVERY_KMS_KEY_ID}',
941
+ kmsKeyId:
942
+ discoveredResources.defaultKmsKeyId ||
943
+ '${env:AWS_DISCOVERY_KMS_KEY_ID}',
758
944
  };
759
945
  }
760
946
 
761
947
  if (!definition.provider.environment.KMS_KEY_ARN) {
762
948
  definition.provider.environment.KMS_KEY_ARN =
763
- discoveredResources.defaultKmsKeyId || '${env:AWS_DISCOVERY_KMS_KEY_ID}';
949
+ discoveredResources.defaultKmsKeyId ||
950
+ '${env:AWS_DISCOVERY_KMS_KEY_ID}';
764
951
  }
765
952
  };
766
953
 
@@ -777,7 +964,9 @@ const healVpcConfiguration = (discoveredResources, AppDefinition) => {
777
964
  return healingReport;
778
965
  }
779
966
 
780
- console.log('🔧 Self-healing mode enabled - checking for VPC misconfigurations...');
967
+ console.log(
968
+ '🔧 Self-healing mode enabled - checking for VPC misconfigurations...'
969
+ );
781
970
 
782
971
  if (discoveredResources.natGatewayInPrivateSubnet) {
783
972
  healingReport.warnings.push(
@@ -787,7 +976,9 @@ const healVpcConfiguration = (discoveredResources, AppDefinition) => {
787
976
  'NAT Gateway should be recreated in a public subnet for proper internet connectivity'
788
977
  );
789
978
  discoveredResources.needsNewNatGateway = true;
790
- healingReport.healed.push('Marked NAT Gateway for recreation in public subnet');
979
+ healingReport.healed.push(
980
+ 'Marked NAT Gateway for recreation in public subnet'
981
+ );
791
982
  }
792
983
 
793
984
  if (discoveredResources.elasticIpAlreadyAssociated) {
@@ -796,10 +987,14 @@ const healVpcConfiguration = (discoveredResources, AppDefinition) => {
796
987
  );
797
988
 
798
989
  if (discoveredResources.existingNatGatewayId) {
799
- healingReport.healed.push('Will reuse existing NAT Gateway instead of creating a new one');
990
+ healingReport.healed.push(
991
+ 'Will reuse existing NAT Gateway instead of creating a new one'
992
+ );
800
993
  discoveredResources.reuseExistingNatGateway = true;
801
994
  } else {
802
- healingReport.healed.push('Will allocate a new Elastic IP for NAT Gateway');
995
+ healingReport.healed.push(
996
+ 'Will allocate a new Elastic IP for NAT Gateway'
997
+ );
803
998
  discoveredResources.allocateNewElasticIp = true;
804
999
  }
805
1000
  }
@@ -823,19 +1018,25 @@ const healVpcConfiguration = (discoveredResources, AppDefinition) => {
823
1018
  healingReport.warnings.push(
824
1019
  'Subnet configuration mismatch detected - Lambda functions require private subnets'
825
1020
  );
826
- healingReport.healed.push('Will create proper route table configuration for subnet isolation');
1021
+ healingReport.healed.push(
1022
+ 'Will create proper route table configuration for subnet isolation'
1023
+ );
827
1024
  }
828
1025
 
829
1026
  if (discoveredResources.orphanedElasticIps?.length > 0) {
830
1027
  healingReport.warnings.push(
831
1028
  `Found ${discoveredResources.orphanedElasticIps.length} orphaned Elastic IPs`
832
1029
  );
833
- healingReport.recommendations.push('Consider releasing orphaned Elastic IPs to avoid charges');
1030
+ healingReport.recommendations.push(
1031
+ 'Consider releasing orphaned Elastic IPs to avoid charges'
1032
+ );
834
1033
  }
835
1034
 
836
1035
  if (healingReport.criticalActions.length > 0) {
837
1036
  console.log('🚨 CRITICAL ACTIONS:');
838
- healingReport.criticalActions.forEach((action) => console.log(` - ${action}`));
1037
+ healingReport.criticalActions.forEach((action) =>
1038
+ console.log(` - ${action}`)
1039
+ );
839
1040
  }
840
1041
 
841
1042
  if (healingReport.healed.length > 0) {
@@ -845,12 +1046,16 @@ const healVpcConfiguration = (discoveredResources, AppDefinition) => {
845
1046
 
846
1047
  if (healingReport.warnings.length > 0) {
847
1048
  console.log('⚠️ Issues detected:');
848
- healingReport.warnings.forEach((warning) => console.log(` - ${warning}`));
1049
+ healingReport.warnings.forEach((warning) =>
1050
+ console.log(` - ${warning}`)
1051
+ );
849
1052
  }
850
1053
 
851
1054
  if (healingReport.recommendations.length > 0) {
852
1055
  console.log('💡 Recommendations:');
853
- healingReport.recommendations.forEach((rec) => console.log(` - ${rec}`));
1056
+ healingReport.recommendations.forEach((rec) =>
1057
+ console.log(` - ${rec}`)
1058
+ );
854
1059
  }
855
1060
 
856
1061
  return healingReport;
@@ -863,7 +1068,9 @@ const configureVpc = (definition, AppDefinition, discoveredResources) => {
863
1068
 
864
1069
  // Skip VPC configuration for local development when AWS discovery is disabled
865
1070
  if (process.env.FRIGG_SKIP_AWS_DISCOVERY === 'true') {
866
- console.log('⚙️ Skipping VPC configuration for local development (FRIGG_SKIP_AWS_DISCOVERY is set)');
1071
+ console.log(
1072
+ '⚙️ Skipping VPC configuration for local development (FRIGG_SKIP_AWS_DISCOVERY is set)'
1073
+ );
867
1074
  return;
868
1075
  }
869
1076
 
@@ -880,9 +1087,16 @@ const configureVpc = (definition, AppDefinition, discoveredResources) => {
880
1087
  });
881
1088
 
882
1089
  if (Object.keys(discoveredResources).length > 0) {
883
- const healingReport = healVpcConfiguration(discoveredResources, AppDefinition);
1090
+ const healingReport = healVpcConfiguration(
1091
+ discoveredResources,
1092
+ AppDefinition
1093
+ );
884
1094
  if (healingReport.errors.length > 0 && !AppDefinition.vpc?.selfHeal) {
885
- throw new Error(`VPC configuration errors detected: ${healingReport.errors.join(', ')}`);
1095
+ throw new Error(
1096
+ `VPC configuration errors detected: ${healingReport.errors.join(
1097
+ ', '
1098
+ )}`
1099
+ );
886
1100
  }
887
1101
  }
888
1102
 
@@ -899,15 +1113,21 @@ const configureVpc = (definition, AppDefinition, discoveredResources) => {
899
1113
  const vpcResources = createVPCInfrastructure(AppDefinition);
900
1114
  Object.assign(definition.resources.Resources, vpcResources);
901
1115
  vpcId = { Ref: 'FriggVPC' };
902
- vpcConfig.securityGroupIds = AppDefinition.vpc.securityGroupIds || [{ Ref: 'FriggLambdaSecurityGroup' }];
1116
+ vpcConfig.securityGroupIds = AppDefinition.vpc.securityGroupIds || [
1117
+ { Ref: 'FriggLambdaSecurityGroup' },
1118
+ ];
903
1119
  } else if (vpcManagement === 'use-existing') {
904
1120
  if (!AppDefinition.vpc.vpcId) {
905
- throw new Error('VPC management is set to "use-existing" but no vpcId was provided');
1121
+ throw new Error(
1122
+ 'VPC management is set to "use-existing" but no vpcId was provided'
1123
+ );
906
1124
  }
907
1125
  vpcId = AppDefinition.vpc.vpcId;
908
1126
  vpcConfig.securityGroupIds =
909
1127
  AppDefinition.vpc.securityGroupIds ||
910
- (discoveredResources.defaultSecurityGroupId ? [discoveredResources.defaultSecurityGroupId] : []);
1128
+ (discoveredResources.defaultSecurityGroupId
1129
+ ? [discoveredResources.defaultSecurityGroupId]
1130
+ : []);
911
1131
  } else {
912
1132
  if (!discoveredResources.defaultVpcId) {
913
1133
  throw new Error(
@@ -917,11 +1137,15 @@ const configureVpc = (definition, AppDefinition, discoveredResources) => {
917
1137
  vpcId = discoveredResources.defaultVpcId;
918
1138
  vpcConfig.securityGroupIds =
919
1139
  AppDefinition.vpc.securityGroupIds ||
920
- (discoveredResources.defaultSecurityGroupId ? [discoveredResources.defaultSecurityGroupId] : []);
1140
+ (discoveredResources.defaultSecurityGroupId
1141
+ ? [discoveredResources.defaultSecurityGroupId]
1142
+ : []);
921
1143
  }
922
1144
 
923
- const defaultSubnetManagement = vpcManagement === 'create-new' ? 'create' : 'discover';
924
- let subnetManagement = AppDefinition.vpc.subnets?.management || defaultSubnetManagement;
1145
+ const defaultSubnetManagement =
1146
+ vpcManagement === 'create-new' ? 'create' : 'discover';
1147
+ let subnetManagement =
1148
+ AppDefinition.vpc.subnets?.management || defaultSubnetManagement;
925
1149
  console.log(`Subnet Management Mode: ${subnetManagement}`);
926
1150
 
927
1151
  const effectiveVpcId = vpcId || discoveredResources.defaultVpcId;
@@ -931,7 +1155,10 @@ const configureVpc = (definition, AppDefinition, discoveredResources) => {
931
1155
 
932
1156
  if (subnetManagement === 'create') {
933
1157
  console.log('Creating new subnets...');
934
- const subnetVpcId = vpcManagement === 'create-new' ? { Ref: 'FriggVPC' } : effectiveVpcId;
1158
+ const subnetVpcId =
1159
+ vpcManagement === 'create-new'
1160
+ ? { Ref: 'FriggVPC' }
1161
+ : effectiveVpcId;
935
1162
  let subnet1Cidr;
936
1163
  let subnet2Cidr;
937
1164
  let publicSubnetCidr;
@@ -954,7 +1181,10 @@ const configureVpc = (definition, AppDefinition, discoveredResources) => {
954
1181
  CidrBlock: subnet1Cidr,
955
1182
  AvailabilityZone: { 'Fn::Select': [0, { 'Fn::GetAZs': '' }] },
956
1183
  Tags: [
957
- { Key: 'Name', Value: '${self:service}-${self:provider.stage}-private-1' },
1184
+ {
1185
+ Key: 'Name',
1186
+ Value: '${self:service}-${self:provider.stage}-private-1',
1187
+ },
958
1188
  { Key: 'Type', Value: 'Private' },
959
1189
  { Key: 'ManagedBy', Value: 'Frigg' },
960
1190
  ],
@@ -968,7 +1198,10 @@ const configureVpc = (definition, AppDefinition, discoveredResources) => {
968
1198
  CidrBlock: subnet2Cidr,
969
1199
  AvailabilityZone: { 'Fn::Select': [1, { 'Fn::GetAZs': '' }] },
970
1200
  Tags: [
971
- { Key: 'Name', Value: '${self:service}-${self:provider.stage}-private-2' },
1201
+ {
1202
+ Key: 'Name',
1203
+ Value: '${self:service}-${self:provider.stage}-private-2',
1204
+ },
972
1205
  { Key: 'Type', Value: 'Private' },
973
1206
  { Key: 'ManagedBy', Value: 'Frigg' },
974
1207
  ],
@@ -983,23 +1216,38 @@ const configureVpc = (definition, AppDefinition, discoveredResources) => {
983
1216
  MapPublicIpOnLaunch: true,
984
1217
  AvailabilityZone: { 'Fn::Select': [0, { 'Fn::GetAZs': '' }] },
985
1218
  Tags: [
986
- { Key: 'Name', Value: '${self:service}-${self:provider.stage}-public' },
1219
+ {
1220
+ Key: 'Name',
1221
+ Value: '${self:service}-${self:provider.stage}-public',
1222
+ },
987
1223
  { Key: 'Type', Value: 'Public' },
988
1224
  { Key: 'ManagedBy', Value: 'Frigg' },
989
1225
  ],
990
1226
  },
991
1227
  };
992
1228
 
993
- vpcConfig.subnetIds = [{ Ref: 'FriggPrivateSubnet1' }, { Ref: 'FriggPrivateSubnet2' }];
1229
+ vpcConfig.subnetIds = [
1230
+ { Ref: 'FriggPrivateSubnet1' },
1231
+ { Ref: 'FriggPrivateSubnet2' },
1232
+ ];
994
1233
 
995
- if (!AppDefinition.vpc.natGateway || AppDefinition.vpc.natGateway.management === 'discover') {
996
- if (vpcManagement === 'create-new' || !discoveredResources.internetGatewayId) {
1234
+ if (
1235
+ !AppDefinition.vpc.natGateway ||
1236
+ AppDefinition.vpc.natGateway.management === 'discover'
1237
+ ) {
1238
+ if (
1239
+ vpcManagement === 'create-new' ||
1240
+ !discoveredResources.internetGatewayId
1241
+ ) {
997
1242
  if (!definition.resources.Resources.FriggInternetGateway) {
998
1243
  definition.resources.Resources.FriggInternetGateway = {
999
1244
  Type: 'AWS::EC2::InternetGateway',
1000
1245
  Properties: {
1001
1246
  Tags: [
1002
- { Key: 'Name', Value: '${self:service}-${self:provider.stage}-igw' },
1247
+ {
1248
+ Key: 'Name',
1249
+ Value: '${self:service}-${self:provider.stage}-igw',
1250
+ },
1003
1251
  { Key: 'ManagedBy', Value: 'Frigg' },
1004
1252
  ],
1005
1253
  },
@@ -1020,7 +1268,10 @@ const configureVpc = (definition, AppDefinition, discoveredResources) => {
1020
1268
  Properties: {
1021
1269
  VpcId: subnetVpcId,
1022
1270
  Tags: [
1023
- { Key: 'Name', Value: '${self:service}-${self:provider.stage}-public-rt' },
1271
+ {
1272
+ Key: 'Name',
1273
+ Value: '${self:service}-${self:provider.stage}-public-rt',
1274
+ },
1024
1275
  { Key: 'ManagedBy', Value: 'Frigg' },
1025
1276
  ],
1026
1277
  },
@@ -1028,15 +1279,21 @@ const configureVpc = (definition, AppDefinition, discoveredResources) => {
1028
1279
 
1029
1280
  definition.resources.Resources.FriggPublicRoute = {
1030
1281
  Type: 'AWS::EC2::Route',
1031
- DependsOn: vpcManagement === 'create-new' ? 'FriggIGWAttachment' : undefined,
1282
+ DependsOn:
1283
+ vpcManagement === 'create-new'
1284
+ ? 'FriggIGWAttachment'
1285
+ : undefined,
1032
1286
  Properties: {
1033
1287
  RouteTableId: { Ref: 'FriggPublicRouteTable' },
1034
1288
  DestinationCidrBlock: '0.0.0.0/0',
1035
- GatewayId: discoveredResources.internetGatewayId || { Ref: 'FriggInternetGateway' },
1289
+ GatewayId: discoveredResources.internetGatewayId || {
1290
+ Ref: 'FriggInternetGateway',
1291
+ },
1036
1292
  },
1037
1293
  };
1038
1294
 
1039
- definition.resources.Resources.FriggPublicSubnetRouteTableAssociation = {
1295
+ definition.resources.Resources.FriggPublicSubnetRouteTableAssociation =
1296
+ {
1040
1297
  Type: 'AWS::EC2::SubnetRouteTableAssociation',
1041
1298
  Properties: {
1042
1299
  SubnetId: { Ref: 'FriggPublicSubnet' },
@@ -1049,13 +1306,17 @@ const configureVpc = (definition, AppDefinition, discoveredResources) => {
1049
1306
  Properties: {
1050
1307
  VpcId: subnetVpcId,
1051
1308
  Tags: [
1052
- { Key: 'Name', Value: '${self:service}-${self:provider.stage}-lambda-rt' },
1309
+ {
1310
+ Key: 'Name',
1311
+ Value: '${self:service}-${self:provider.stage}-lambda-rt',
1312
+ },
1053
1313
  { Key: 'ManagedBy', Value: 'Frigg' },
1054
1314
  ],
1055
1315
  },
1056
1316
  };
1057
1317
 
1058
- definition.resources.Resources.FriggPrivateSubnet1RouteTableAssociation = {
1318
+ definition.resources.Resources.FriggPrivateSubnet1RouteTableAssociation =
1319
+ {
1059
1320
  Type: 'AWS::EC2::SubnetRouteTableAssociation',
1060
1321
  Properties: {
1061
1322
  SubnetId: { Ref: 'FriggPrivateSubnet1' },
@@ -1063,7 +1324,8 @@ const configureVpc = (definition, AppDefinition, discoveredResources) => {
1063
1324
  },
1064
1325
  };
1065
1326
 
1066
- definition.resources.Resources.FriggPrivateSubnet2RouteTableAssociation = {
1327
+ definition.resources.Resources.FriggPrivateSubnet2RouteTableAssociation =
1328
+ {
1067
1329
  Type: 'AWS::EC2::SubnetRouteTableAssociation',
1068
1330
  Properties: {
1069
1331
  SubnetId: { Ref: 'FriggPrivateSubnet2' },
@@ -1072,7 +1334,10 @@ const configureVpc = (definition, AppDefinition, discoveredResources) => {
1072
1334
  };
1073
1335
  }
1074
1336
  } else if (subnetManagement === 'use-existing') {
1075
- if (!AppDefinition.vpc.subnets?.ids || AppDefinition.vpc.subnets.ids.length < 2) {
1337
+ if (
1338
+ !AppDefinition.vpc.subnets?.ids ||
1339
+ AppDefinition.vpc.subnets.ids.length < 2
1340
+ ) {
1076
1341
  throw new Error(
1077
1342
  'Subnet management is "use-existing" but less than 2 subnet IDs provided. Provide at least 2 subnet IDs in vpc.subnets.ids.'
1078
1343
  );
@@ -1082,13 +1347,19 @@ const configureVpc = (definition, AppDefinition, discoveredResources) => {
1082
1347
  vpcConfig.subnetIds =
1083
1348
  AppDefinition.vpc.subnets?.ids?.length > 0
1084
1349
  ? AppDefinition.vpc.subnets.ids
1085
- : discoveredResources.privateSubnetId1 && discoveredResources.privateSubnetId2
1086
- ? [discoveredResources.privateSubnetId1, discoveredResources.privateSubnetId2]
1087
- : [];
1350
+ : discoveredResources.privateSubnetId1 &&
1351
+ discoveredResources.privateSubnetId2
1352
+ ? [
1353
+ discoveredResources.privateSubnetId1,
1354
+ discoveredResources.privateSubnetId2,
1355
+ ]
1356
+ : [];
1088
1357
 
1089
1358
  if (vpcConfig.subnetIds.length < 2) {
1090
1359
  if (AppDefinition.vpc.selfHeal) {
1091
- console.log('No subnets found but self-heal enabled - creating minimal subnet setup');
1360
+ console.log(
1361
+ 'No subnets found but self-heal enabled - creating minimal subnet setup'
1362
+ );
1092
1363
  subnetManagement = 'create';
1093
1364
  discoveredResources.createSubnets = true;
1094
1365
  } else {
@@ -1100,19 +1371,22 @@ const configureVpc = (definition, AppDefinition, discoveredResources) => {
1100
1371
  }
1101
1372
 
1102
1373
  if (subnetManagement === 'create' && discoveredResources.createSubnets) {
1103
- definition.resources.Resources.FriggLambdaRouteTable =
1104
- definition.resources.Resources.FriggLambdaRouteTable || {
1105
- Type: 'AWS::EC2::RouteTable',
1106
- Properties: {
1107
- VpcId: effectiveVpcId,
1108
- Tags: [
1109
- { Key: 'Name', Value: '${self:service}-${self:provider.stage}-lambda-rt' },
1110
- { Key: 'ManagedBy', Value: 'Frigg' },
1111
- { Key: 'Environment', Value: '${self:provider.stage}' },
1112
- { Key: 'Service', Value: '${self:service}' },
1113
- ],
1114
- },
1115
- };
1374
+ definition.resources.Resources.FriggLambdaRouteTable = definition
1375
+ .resources.Resources.FriggLambdaRouteTable || {
1376
+ Type: 'AWS::EC2::RouteTable',
1377
+ Properties: {
1378
+ VpcId: effectiveVpcId,
1379
+ Tags: [
1380
+ {
1381
+ Key: 'Name',
1382
+ Value: '${self:service}-${self:provider.stage}-lambda-rt',
1383
+ },
1384
+ { Key: 'ManagedBy', Value: 'Frigg' },
1385
+ { Key: 'Environment', Value: '${self:provider.stage}' },
1386
+ { Key: 'Service', Value: '${self:service}' },
1387
+ ],
1388
+ },
1389
+ };
1116
1390
  }
1117
1391
 
1118
1392
  if (
@@ -1121,7 +1395,8 @@ const configureVpc = (definition, AppDefinition, discoveredResources) => {
1121
1395
  ) {
1122
1396
  definition.provider.vpc = vpcConfig;
1123
1397
 
1124
- const natGatewayManagement = AppDefinition.vpc.natGateway?.management || 'discover';
1398
+ const natGatewayManagement =
1399
+ AppDefinition.vpc.natGateway?.management || 'discover';
1125
1400
  let needsNewNatGateway =
1126
1401
  natGatewayManagement === 'createAndManage' ||
1127
1402
  discoveredResources.needsNewNatGateway === true;
@@ -1132,7 +1407,9 @@ const configureVpc = (definition, AppDefinition, discoveredResources) => {
1132
1407
  let useExistingEip = false;
1133
1408
 
1134
1409
  if (needsNewNatGateway) {
1135
- console.log('Create mode: Creating dedicated EIP, public subnet, and NAT Gateway...');
1410
+ console.log(
1411
+ 'Create mode: Creating dedicated EIP, public subnet, and NAT Gateway...'
1412
+ );
1136
1413
 
1137
1414
  if (
1138
1415
  discoveredResources.existingNatGatewayId &&
@@ -1140,12 +1417,18 @@ const configureVpc = (definition, AppDefinition, discoveredResources) => {
1140
1417
  ) {
1141
1418
  console.log('Found existing Frigg-managed NAT Gateway and EIP');
1142
1419
  if (!discoveredResources.natGatewayInPrivateSubnet) {
1143
- console.log('✅ Existing NAT Gateway is in PUBLIC subnet, will reuse it');
1420
+ console.log(
1421
+ '✅ Existing NAT Gateway is in PUBLIC subnet, will reuse it'
1422
+ );
1144
1423
  reuseExistingNatGateway = true;
1145
1424
  } else {
1146
- console.log('❌ NAT Gateway is in PRIVATE subnet - MUST create new one in PUBLIC subnet');
1425
+ console.log(
1426
+ '❌ NAT Gateway is in PRIVATE subnet - MUST create new one in PUBLIC subnet'
1427
+ );
1147
1428
  if (AppDefinition.vpc.selfHeal) {
1148
- console.log('Self-heal enabled: Creating new NAT Gateway in PUBLIC subnet');
1429
+ console.log(
1430
+ 'Self-heal enabled: Creating new NAT Gateway in PUBLIC subnet'
1431
+ );
1149
1432
  reuseExistingNatGateway = false;
1150
1433
  useExistingEip = false;
1151
1434
  discoveredResources.needsCleanup = true;
@@ -1159,12 +1442,16 @@ const configureVpc = (definition, AppDefinition, discoveredResources) => {
1159
1442
  discoveredResources.existingElasticIpAllocationId &&
1160
1443
  !discoveredResources.existingNatGatewayId
1161
1444
  ) {
1162
- console.log('Found orphaned EIP, will reuse it for new NAT Gateway in PUBLIC subnet');
1445
+ console.log(
1446
+ 'Found orphaned EIP, will reuse it for new NAT Gateway in PUBLIC subnet'
1447
+ );
1163
1448
  useExistingEip = true;
1164
1449
  }
1165
1450
 
1166
1451
  if (reuseExistingNatGateway) {
1167
- console.log('Reusing existing NAT Gateway - skipping resource creation');
1452
+ console.log(
1453
+ 'Reusing existing NAT Gateway - skipping resource creation'
1454
+ );
1168
1455
  } else {
1169
1456
  if (!useExistingEip) {
1170
1457
  definition.resources.Resources.FriggNATGatewayEIP = {
@@ -1174,10 +1461,16 @@ const configureVpc = (definition, AppDefinition, discoveredResources) => {
1174
1461
  Properties: {
1175
1462
  Domain: 'vpc',
1176
1463
  Tags: [
1177
- { Key: 'Name', Value: '${self:service}-${self:provider.stage}-nat-eip' },
1464
+ {
1465
+ Key: 'Name',
1466
+ Value: '${self:service}-${self:provider.stage}-nat-eip',
1467
+ },
1178
1468
  { Key: 'ManagedBy', Value: 'Frigg' },
1179
1469
  { Key: 'Service', Value: '${self:service}' },
1180
- { Key: 'Stage', Value: '${self:provider.stage}' },
1470
+ {
1471
+ Key: 'Stage',
1472
+ Value: '${self:provider.stage}',
1473
+ },
1181
1474
  ],
1182
1475
  },
1183
1476
  };
@@ -1185,25 +1478,34 @@ const configureVpc = (definition, AppDefinition, discoveredResources) => {
1185
1478
 
1186
1479
  if (!discoveredResources.publicSubnetId) {
1187
1480
  if (discoveredResources.internetGatewayId) {
1188
- console.log('Reusing existing Internet Gateway for NAT Gateway');
1481
+ console.log(
1482
+ 'Reusing existing Internet Gateway for NAT Gateway'
1483
+ );
1189
1484
  } else {
1190
1485
  definition.resources.Resources.FriggInternetGateway =
1191
- definition.resources.Resources.FriggInternetGateway || {
1486
+ definition.resources.Resources
1487
+ .FriggInternetGateway || {
1192
1488
  Type: 'AWS::EC2::InternetGateway',
1193
1489
  Properties: {
1194
1490
  Tags: [
1195
- { Key: 'Name', Value: '${self:service}-${self:provider.stage}-igw' },
1491
+ {
1492
+ Key: 'Name',
1493
+ Value: '${self:service}-${self:provider.stage}-igw',
1494
+ },
1196
1495
  { Key: 'ManagedBy', Value: 'Frigg' },
1197
1496
  ],
1198
1497
  },
1199
1498
  };
1200
1499
 
1201
1500
  definition.resources.Resources.FriggIGWAttachment =
1202
- definition.resources.Resources.FriggIGWAttachment || {
1501
+ definition.resources.Resources
1502
+ .FriggIGWAttachment || {
1203
1503
  Type: 'AWS::EC2::VPCGatewayAttachment',
1204
1504
  Properties: {
1205
1505
  VpcId: discoveredResources.defaultVpcId,
1206
- InternetGatewayId: { Ref: 'FriggInternetGateway' },
1506
+ InternetGatewayId: {
1507
+ Ref: 'FriggInternetGateway',
1508
+ },
1207
1509
  },
1208
1510
  };
1209
1511
  }
@@ -1213,11 +1515,17 @@ const configureVpc = (definition, AppDefinition, discoveredResources) => {
1213
1515
  Properties: {
1214
1516
  VpcId: discoveredResources.defaultVpcId,
1215
1517
  CidrBlock:
1216
- AppDefinition.vpc.natGateway?.publicSubnetCidr || '172.31.250.0/24',
1217
- AvailabilityZone: { 'Fn::Select': [0, { 'Fn::GetAZs': '' }] },
1518
+ AppDefinition.vpc.natGateway
1519
+ ?.publicSubnetCidr || '172.31.250.0/24',
1520
+ AvailabilityZone: {
1521
+ 'Fn::Select': [0, { 'Fn::GetAZs': '' }],
1522
+ },
1218
1523
  MapPublicIpOnLaunch: true,
1219
1524
  Tags: [
1220
- { Key: 'Name', Value: '${self:service}-${self:provider.stage}-public-subnet' },
1525
+ {
1526
+ Key: 'Name',
1527
+ Value: '${self:service}-${self:provider.stage}-public-subnet',
1528
+ },
1221
1529
  { Key: 'Type', Value: 'Public' },
1222
1530
  ],
1223
1531
  },
@@ -1228,22 +1536,31 @@ const configureVpc = (definition, AppDefinition, discoveredResources) => {
1228
1536
  Properties: {
1229
1537
  VpcId: discoveredResources.defaultVpcId,
1230
1538
  Tags: [
1231
- { Key: 'Name', Value: '${self:service}-${self:provider.stage}-public-rt' },
1539
+ {
1540
+ Key: 'Name',
1541
+ Value: '${self:service}-${self:provider.stage}-public-rt',
1542
+ },
1232
1543
  ],
1233
1544
  },
1234
1545
  };
1235
1546
 
1236
1547
  definition.resources.Resources.FriggPublicRoute = {
1237
1548
  Type: 'AWS::EC2::Route',
1238
- DependsOn: discoveredResources.internetGatewayId ? [] : 'FriggIGWAttachment',
1549
+ DependsOn: discoveredResources.internetGatewayId
1550
+ ? []
1551
+ : 'FriggIGWAttachment',
1239
1552
  Properties: {
1240
1553
  RouteTableId: { Ref: 'FriggPublicRouteTable' },
1241
1554
  DestinationCidrBlock: '0.0.0.0/0',
1242
- GatewayId: discoveredResources.internetGatewayId || { Ref: 'FriggInternetGateway' },
1555
+ GatewayId:
1556
+ discoveredResources.internetGatewayId || {
1557
+ Ref: 'FriggInternetGateway',
1558
+ },
1243
1559
  },
1244
1560
  };
1245
1561
 
1246
- definition.resources.Resources.FriggPublicSubnetRouteTableAssociation = {
1562
+ definition.resources.Resources.FriggPublicSubnetRouteTableAssociation =
1563
+ {
1247
1564
  Type: 'AWS::EC2::SubnetRouteTableAssociation',
1248
1565
  Properties: {
1249
1566
  SubnetId: { Ref: 'FriggPublicSubnet' },
@@ -1259,11 +1576,20 @@ const configureVpc = (definition, AppDefinition, discoveredResources) => {
1259
1576
  Properties: {
1260
1577
  AllocationId: useExistingEip
1261
1578
  ? discoveredResources.existingElasticIpAllocationId
1262
- : { 'Fn::GetAtt': ['FriggNATGatewayEIP', 'AllocationId'] },
1263
- SubnetId:
1264
- discoveredResources.publicSubnetId || { Ref: 'FriggPublicSubnet' },
1579
+ : {
1580
+ 'Fn::GetAtt': [
1581
+ 'FriggNATGatewayEIP',
1582
+ 'AllocationId',
1583
+ ],
1584
+ },
1585
+ SubnetId: discoveredResources.publicSubnetId || {
1586
+ Ref: 'FriggPublicSubnet',
1587
+ },
1265
1588
  Tags: [
1266
- { Key: 'Name', Value: '${self:service}-${self:provider.stage}-nat-gateway' },
1589
+ {
1590
+ Key: 'Name',
1591
+ Value: '${self:service}-${self:provider.stage}-nat-gateway',
1592
+ },
1267
1593
  { Key: 'ManagedBy', Value: 'Frigg' },
1268
1594
  { Key: 'Service', Value: '${self:service}' },
1269
1595
  { Key: 'Stage', Value: '${self:provider.stage}' },
@@ -1275,9 +1601,15 @@ const configureVpc = (definition, AppDefinition, discoveredResources) => {
1275
1601
  natGatewayManagement === 'discover' ||
1276
1602
  natGatewayManagement === 'useExisting'
1277
1603
  ) {
1278
- if (natGatewayManagement === 'useExisting' && AppDefinition.vpc.natGateway?.id) {
1279
- console.log(`Using explicitly provided NAT Gateway: ${AppDefinition.vpc.natGateway.id}`);
1280
- discoveredResources.existingNatGatewayId = AppDefinition.vpc.natGateway.id;
1604
+ if (
1605
+ natGatewayManagement === 'useExisting' &&
1606
+ AppDefinition.vpc.natGateway?.id
1607
+ ) {
1608
+ console.log(
1609
+ `Using explicitly provided NAT Gateway: ${AppDefinition.vpc.natGateway.id}`
1610
+ );
1611
+ discoveredResources.existingNatGatewayId =
1612
+ AppDefinition.vpc.natGateway.id;
1281
1613
  }
1282
1614
 
1283
1615
  if (discoveredResources.existingNatGatewayId) {
@@ -1287,14 +1619,20 @@ const configureVpc = (definition, AppDefinition, discoveredResources) => {
1287
1619
  );
1288
1620
 
1289
1621
  if (discoveredResources.natGatewayInPrivateSubnet) {
1290
- console.log('❌ CRITICAL: NAT Gateway is in PRIVATE subnet - Internet connectivity will NOT work!');
1622
+ console.log(
1623
+ '❌ CRITICAL: NAT Gateway is in PRIVATE subnet - Internet connectivity will NOT work!'
1624
+ );
1291
1625
 
1292
1626
  if (AppDefinition.vpc.selfHeal === true) {
1293
- console.log('Self-heal enabled: Will create new NAT Gateway in PUBLIC subnet');
1627
+ console.log(
1628
+ 'Self-heal enabled: Will create new NAT Gateway in PUBLIC subnet'
1629
+ );
1294
1630
  needsNewNatGateway = true;
1295
1631
  discoveredResources.existingNatGatewayId = null;
1296
1632
  if (!discoveredResources.publicSubnetId) {
1297
- console.log('No public subnet found - will create one for NAT Gateway');
1633
+ console.log(
1634
+ 'No public subnet found - will create one for NAT Gateway'
1635
+ );
1298
1636
  discoveredResources.createPublicSubnet = true;
1299
1637
  }
1300
1638
  } else {
@@ -1303,52 +1641,73 @@ const configureVpc = (definition, AppDefinition, discoveredResources) => {
1303
1641
  );
1304
1642
  }
1305
1643
  } else {
1306
- console.log(`Using discovered NAT Gateway for routing: ${discoveredResources.existingNatGatewayId}`);
1644
+ console.log(
1645
+ `Using discovered NAT Gateway for routing: ${discoveredResources.existingNatGatewayId}`
1646
+ );
1307
1647
  }
1308
- } else if (!needsNewNatGateway && AppDefinition.vpc.natGateway?.id) {
1309
- console.log(`Using explicitly provided NAT Gateway: ${AppDefinition.vpc.natGateway.id}`);
1310
- discoveredResources.existingNatGatewayId = AppDefinition.vpc.natGateway.id;
1648
+ } else if (
1649
+ !needsNewNatGateway &&
1650
+ AppDefinition.vpc.natGateway?.id
1651
+ ) {
1652
+ console.log(
1653
+ `Using explicitly provided NAT Gateway: ${AppDefinition.vpc.natGateway.id}`
1654
+ );
1655
+ discoveredResources.existingNatGatewayId =
1656
+ AppDefinition.vpc.natGateway.id;
1311
1657
  }
1312
1658
  }
1313
1659
 
1314
- definition.resources.Resources.FriggLambdaRouteTable =
1315
- definition.resources.Resources.FriggLambdaRouteTable || {
1316
- Type: 'AWS::EC2::RouteTable',
1317
- Properties: {
1318
- VpcId: discoveredResources.defaultVpcId || vpcId,
1319
- Tags: [
1320
- { Key: 'Name', Value: '${self:service}-${self:provider.stage}-lambda-rt' },
1321
- { Key: 'ManagedBy', Value: 'Frigg' },
1322
- { Key: 'Environment', Value: '${self:provider.stage}' },
1323
- { Key: 'Service', Value: '${self:service}' },
1324
- ],
1325
- },
1326
- };
1660
+ definition.resources.Resources.FriggLambdaRouteTable = definition
1661
+ .resources.Resources.FriggLambdaRouteTable || {
1662
+ Type: 'AWS::EC2::RouteTable',
1663
+ Properties: {
1664
+ VpcId: discoveredResources.defaultVpcId || vpcId,
1665
+ Tags: [
1666
+ {
1667
+ Key: 'Name',
1668
+ Value: '${self:service}-${self:provider.stage}-lambda-rt',
1669
+ },
1670
+ { Key: 'ManagedBy', Value: 'Frigg' },
1671
+ { Key: 'Environment', Value: '${self:provider.stage}' },
1672
+ { Key: 'Service', Value: '${self:service}' },
1673
+ ],
1674
+ },
1675
+ };
1327
1676
 
1328
1677
  const routeTableId = { Ref: 'FriggLambdaRouteTable' };
1329
1678
  let natGatewayIdForRoute;
1330
1679
 
1331
1680
  if (reuseExistingNatGateway) {
1332
1681
  natGatewayIdForRoute = discoveredResources.existingNatGatewayId;
1333
- console.log(`Using discovered NAT Gateway for routing: ${natGatewayIdForRoute}`);
1682
+ console.log(
1683
+ `Using discovered NAT Gateway for routing: ${natGatewayIdForRoute}`
1684
+ );
1334
1685
  } else if (needsNewNatGateway && !reuseExistingNatGateway) {
1335
1686
  natGatewayIdForRoute = { Ref: 'FriggNATGateway' };
1336
1687
  console.log('Using newly created NAT Gateway for routing');
1337
1688
  } else if (discoveredResources.existingNatGatewayId) {
1338
1689
  natGatewayIdForRoute = discoveredResources.existingNatGatewayId;
1339
- console.log(`Using discovered NAT Gateway for routing: ${natGatewayIdForRoute}`);
1690
+ console.log(
1691
+ `Using discovered NAT Gateway for routing: ${natGatewayIdForRoute}`
1692
+ );
1340
1693
  } else if (AppDefinition.vpc.natGateway?.id) {
1341
1694
  natGatewayIdForRoute = AppDefinition.vpc.natGateway.id;
1342
- console.log(`Using explicitly provided NAT Gateway for routing: ${natGatewayIdForRoute}`);
1695
+ console.log(
1696
+ `Using explicitly provided NAT Gateway for routing: ${natGatewayIdForRoute}`
1697
+ );
1343
1698
  } else if (AppDefinition.vpc.selfHeal === true) {
1344
1699
  natGatewayIdForRoute = null;
1345
- console.log('No NAT Gateway available - skipping NAT route creation');
1700
+ console.log(
1701
+ 'No NAT Gateway available - skipping NAT route creation'
1702
+ );
1346
1703
  } else {
1347
1704
  throw new Error('No existing NAT Gateway found in discovery mode');
1348
1705
  }
1349
1706
 
1350
1707
  if (natGatewayIdForRoute) {
1351
- console.log(`Configuring NAT route: 0.0.0.0/0 → ${natGatewayIdForRoute}`);
1708
+ console.log(
1709
+ `Configuring NAT route: 0.0.0.0/0 → ${natGatewayIdForRoute}`
1710
+ );
1352
1711
  definition.resources.Resources.FriggNATRoute = {
1353
1712
  Type: 'AWS::EC2::Route',
1354
1713
  DependsOn: 'FriggLambdaRouteTable',
@@ -1359,7 +1718,9 @@ const configureVpc = (definition, AppDefinition, discoveredResources) => {
1359
1718
  },
1360
1719
  };
1361
1720
  } else {
1362
- console.warn('⚠️ No NAT Gateway configured - Lambda functions will not have internet access');
1721
+ console.warn(
1722
+ '⚠️ No NAT Gateway configured - Lambda functions will not have internet access'
1723
+ );
1363
1724
  }
1364
1725
 
1365
1726
  if (typeof vpcConfig.subnetIds[0] === 'string') {
@@ -1384,25 +1745,37 @@ const configureVpc = (definition, AppDefinition, discoveredResources) => {
1384
1745
  };
1385
1746
  }
1386
1747
 
1387
- if (typeof vpcConfig.subnetIds[0] === 'object' && vpcConfig.subnetIds[0].Ref) {
1748
+ if (
1749
+ typeof vpcConfig.subnetIds[0] === 'object' &&
1750
+ vpcConfig.subnetIds[0].Ref
1751
+ ) {
1388
1752
  definition.resources.Resources.FriggNewSubnet1RouteAssociation = {
1389
1753
  Type: 'AWS::EC2::SubnetRouteTableAssociation',
1390
1754
  Properties: {
1391
1755
  SubnetId: vpcConfig.subnetIds[0],
1392
1756
  RouteTableId: routeTableId,
1393
1757
  },
1394
- DependsOn: ['FriggLambdaRouteTable', vpcConfig.subnetIds[0].Ref],
1758
+ DependsOn: [
1759
+ 'FriggLambdaRouteTable',
1760
+ vpcConfig.subnetIds[0].Ref,
1761
+ ],
1395
1762
  };
1396
1763
  }
1397
1764
 
1398
- if (typeof vpcConfig.subnetIds[1] === 'object' && vpcConfig.subnetIds[1].Ref) {
1765
+ if (
1766
+ typeof vpcConfig.subnetIds[1] === 'object' &&
1767
+ vpcConfig.subnetIds[1].Ref
1768
+ ) {
1399
1769
  definition.resources.Resources.FriggNewSubnet2RouteAssociation = {
1400
1770
  Type: 'AWS::EC2::SubnetRouteTableAssociation',
1401
1771
  Properties: {
1402
1772
  SubnetId: vpcConfig.subnetIds[1],
1403
1773
  RouteTableId: routeTableId,
1404
1774
  },
1405
- DependsOn: ['FriggLambdaRouteTable', vpcConfig.subnetIds[1].Ref],
1775
+ DependsOn: [
1776
+ 'FriggLambdaRouteTable',
1777
+ vpcConfig.subnetIds[1].Ref,
1778
+ ],
1406
1779
  };
1407
1780
  }
1408
1781
 
@@ -1421,7 +1794,8 @@ const configureVpc = (definition, AppDefinition, discoveredResources) => {
1421
1794
  Type: 'AWS::EC2::VPCEndpoint',
1422
1795
  Properties: {
1423
1796
  VpcId: discoveredResources.defaultVpcId,
1424
- ServiceName: 'com.amazonaws.${self:provider.region}.dynamodb',
1797
+ ServiceName:
1798
+ 'com.amazonaws.${self:provider.region}.dynamodb',
1425
1799
  VpcEndpointType: 'Gateway',
1426
1800
  RouteTableIds: [routeTableId],
1427
1801
  },
@@ -1438,7 +1812,10 @@ const configureVpc = (definition, AppDefinition, discoveredResources) => {
1438
1812
  if (!definition.resources.Resources.VPCEndpointSecurityGroup) {
1439
1813
  const vpcEndpointIngressRules = [];
1440
1814
 
1441
- if (vpcConfig.securityGroupIds && vpcConfig.securityGroupIds.length > 0) {
1815
+ if (
1816
+ vpcConfig.securityGroupIds &&
1817
+ vpcConfig.securityGroupIds.length > 0
1818
+ ) {
1442
1819
  for (const sg of vpcConfig.securityGroupIds) {
1443
1820
  if (typeof sg === 'string') {
1444
1821
  vpcEndpointIngressRules.push({
@@ -1486,13 +1863,20 @@ const configureVpc = (definition, AppDefinition, discoveredResources) => {
1486
1863
  definition.resources.Resources.VPCEndpointSecurityGroup = {
1487
1864
  Type: 'AWS::EC2::SecurityGroup',
1488
1865
  Properties: {
1489
- GroupDescription: 'Security group for VPC endpoints - allows HTTPS from Lambda functions',
1866
+ GroupDescription:
1867
+ 'Security group for VPC endpoints - allows HTTPS from Lambda functions',
1490
1868
  VpcId: discoveredResources.defaultVpcId,
1491
1869
  SecurityGroupIngress: vpcEndpointIngressRules,
1492
1870
  Tags: [
1493
- { Key: 'Name', Value: '${self:service}-${self:provider.stage}-vpc-endpoints-sg' },
1871
+ {
1872
+ Key: 'Name',
1873
+ Value: '${self:service}-${self:provider.stage}-vpc-endpoints-sg',
1874
+ },
1494
1875
  { Key: 'ManagedBy', Value: 'Frigg' },
1495
- { Key: 'Purpose', Value: 'Allow Lambda functions to access VPC endpoints' },
1876
+ {
1877
+ Key: 'Purpose',
1878
+ Value: 'Allow Lambda functions to access VPC endpoints',
1879
+ },
1496
1880
  ],
1497
1881
  },
1498
1882
  };
@@ -1515,7 +1899,8 @@ const configureVpc = (definition, AppDefinition, discoveredResources) => {
1515
1899
  Type: 'AWS::EC2::VPCEndpoint',
1516
1900
  Properties: {
1517
1901
  VpcId: discoveredResources.defaultVpcId,
1518
- ServiceName: 'com.amazonaws.${self:provider.region}.secretsmanager',
1902
+ ServiceName:
1903
+ 'com.amazonaws.${self:provider.region}.secretsmanager',
1519
1904
  VpcEndpointType: 'Interface',
1520
1905
  SubnetIds: vpcConfig.subnetIds,
1521
1906
  SecurityGroupIds: [{ Ref: 'VPCEndpointSecurityGroup' }],
@@ -1538,19 +1923,31 @@ const configureSsm = (definition, AppDefinition) => {
1538
1923
 
1539
1924
  definition.provider.iamRoleStatements.push({
1540
1925
  Effect: 'Allow',
1541
- Action: ['ssm:GetParameter', 'ssm:GetParameters', 'ssm:GetParametersByPath'],
1542
- Resource: ['arn:aws:ssm:${self:provider.region}:*:parameter/${self:service}/${self:provider.stage}/*'],
1926
+ Action: [
1927
+ 'ssm:GetParameter',
1928
+ 'ssm:GetParameters',
1929
+ 'ssm:GetParametersByPath',
1930
+ ],
1931
+ Resource: [
1932
+ 'arn:aws:ssm:${self:provider.region}:*:parameter/${self:service}/${self:provider.stage}/*',
1933
+ ],
1543
1934
  });
1544
1935
 
1545
- definition.provider.environment.SSM_PARAMETER_PREFIX = '/${self:service}/${self:provider.stage}';
1936
+ definition.provider.environment.SSM_PARAMETER_PREFIX =
1937
+ '/${self:service}/${self:provider.stage}';
1546
1938
  };
1547
1939
 
1548
1940
  const attachIntegrations = (definition, AppDefinition) => {
1549
- if (!Array.isArray(AppDefinition.integrations) || AppDefinition.integrations.length === 0) {
1941
+ if (
1942
+ !Array.isArray(AppDefinition.integrations) ||
1943
+ AppDefinition.integrations.length === 0
1944
+ ) {
1550
1945
  return;
1551
1946
  }
1552
1947
 
1553
- console.log(`Processing ${AppDefinition.integrations.length} integrations...`);
1948
+ console.log(
1949
+ `Processing ${AppDefinition.integrations.length} integrations...`
1950
+ );
1554
1951
 
1555
1952
  for (const integration of AppDefinition.integrations) {
1556
1953
  if (!integration?.Definition?.name) {
@@ -1558,7 +1955,8 @@ const attachIntegrations = (definition, AppDefinition) => {
1558
1955
  }
1559
1956
 
1560
1957
  const integrationName = integration.Definition.name;
1561
- const queueReference = `${integrationName.charAt(0).toUpperCase() + integrationName.slice(1)}Queue`;
1958
+ const queueReference = `${integrationName.charAt(0).toUpperCase() + integrationName.slice(1)
1959
+ }Queue`;
1562
1960
  const queueName = `\${self:service}--\${self:provider.stage}-${queueReference}`;
1563
1961
 
1564
1962
  definition.functions[integrationName] = {
@@ -1581,7 +1979,9 @@ const attachIntegrations = (definition, AppDefinition) => {
1581
1979
  VisibilityTimeout: 1800,
1582
1980
  RedrivePolicy: {
1583
1981
  maxReceiveCount: 1,
1584
- deadLetterTargetArn: { 'Fn::GetAtt': ['InternalErrorQueue', 'Arn'] },
1982
+ deadLetterTargetArn: {
1983
+ 'Fn::GetAtt': ['InternalErrorQueue', 'Arn'],
1984
+ },
1585
1985
  },
1586
1986
  },
1587
1987
  };
@@ -1603,10 +2003,36 @@ const attachIntegrations = (definition, AppDefinition) => {
1603
2003
 
1604
2004
  definition.provider.environment = {
1605
2005
  ...definition.provider.environment,
1606
- [`${integrationName.toUpperCase()}_QUEUE_URL`]: { Ref: queueReference },
2006
+ [`${integrationName.toUpperCase()}_QUEUE_URL`]: {
2007
+ Ref: queueReference,
2008
+ },
1607
2009
  };
1608
2010
 
1609
2011
  definition.custom[queueReference] = queueName;
2012
+
2013
+ // Add webhook handler if enabled
2014
+ const webhookConfig = integration.Definition.webhooks;
2015
+ if (webhookConfig && (webhookConfig === true || webhookConfig.enabled === true)) {
2016
+ const webhookFunctionName = `${integrationName}Webhook`;
2017
+
2018
+ definition.functions[webhookFunctionName] = {
2019
+ handler: `node_modules/@friggframework/core/handlers/routers/integration-webhook-routers.handlers.${integrationName}Webhook.handler`,
2020
+ events: [
2021
+ {
2022
+ httpApi: {
2023
+ path: `/api/${integrationName}-integration/webhooks`,
2024
+ method: 'POST',
2025
+ },
2026
+ },
2027
+ {
2028
+ httpApi: {
2029
+ path: `/api/${integrationName}-integration/webhooks/{integrationId}`,
2030
+ method: 'POST',
2031
+ },
2032
+ },
2033
+ ],
2034
+ };
2035
+ }
1610
2036
  }
1611
2037
  };
1612
2038
 
@@ -1616,7 +2042,8 @@ const configureWebsockets = (definition, AppDefinition) => {
1616
2042
  }
1617
2043
 
1618
2044
  definition.functions.defaultWebsocket = {
1619
- handler: 'node_modules/@friggframework/core/handlers/routers/websocket.handler',
2045
+ handler:
2046
+ 'node_modules/@friggframework/core/handlers/routers/websocket.handler',
1620
2047
  events: [
1621
2048
  { websocket: { route: '$connect' } },
1622
2049
  { websocket: { route: '$default' } },
@@ -1630,11 +2057,32 @@ const composeServerlessDefinition = async (AppDefinition) => {
1630
2057
 
1631
2058
  const discoveredResources = await gatherDiscoveredResources(AppDefinition);
1632
2059
  const appEnvironmentVars = getAppEnvironmentVars(AppDefinition);
1633
- const definition = createBaseDefinition(AppDefinition, appEnvironmentVars, discoveredResources);
2060
+ const definition = createBaseDefinition(
2061
+ AppDefinition,
2062
+ appEnvironmentVars,
2063
+ discoveredResources
2064
+ );
2065
+
2066
+ // Check if we're in local build mode (AWS discovery was skipped)
2067
+ const isLocalBuild = !shouldRunDiscovery(AppDefinition);
2068
+
2069
+ if (isLocalBuild) {
2070
+ console.log(
2071
+ '🏠 Local build mode detected - skipping AWS-dependent configurations'
2072
+ );
2073
+ }
2074
+
2075
+ // Apply configurations (skip AWS-dependent ones in local build mode)
2076
+ if (!isLocalBuild) {
2077
+ applyKmsConfiguration(definition, AppDefinition, discoveredResources);
2078
+ configureVpc(definition, AppDefinition, discoveredResources);
2079
+ configureSsm(definition, AppDefinition);
2080
+ } else {
2081
+ console.log(
2082
+ ' ⏭️ Skipping: KMS, VPC, PostgreSQL, SSM configurations'
2083
+ );
2084
+ }
1634
2085
 
1635
- applyKmsConfiguration(definition, AppDefinition, discoveredResources);
1636
- configureVpc(definition, AppDefinition, discoveredResources);
1637
- configureSsm(definition, AppDefinition);
1638
2086
  attachIntegrations(definition, AppDefinition);
1639
2087
  configureWebsockets(definition, AppDefinition);
1640
2088