@friggframework/devtools 2.0.0--canary.425.2d58c19.0 → 2.0.0--canary.427.c8ff14b.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.
@@ -54,7 +54,7 @@ describe('Generate Command', () => {
54
54
  // Mock app definition
55
55
  jest.doMock(mockAppDefinitionPath, () => ({
56
56
  vpc: { enable: true },
57
- encryption: { fieldLevelEncryptionMethod: 'kms' },
57
+ encryption: { useDefaultKMSForFieldLevelEncryption: true },
58
58
  ssm: { enable: true },
59
59
  websockets: { enable: false }
60
60
  }), { virtual: true });
@@ -228,7 +228,7 @@ async function generateCommand(options = {}) {
228
228
  function analyzeAppFeatures(appDefinition) {
229
229
  const features = {
230
230
  vpc: appDefinition.vpc?.enable === true,
231
- kms: appDefinition.encryption?.fieldLevelEncryptionMethod === 'kms',
231
+ kms: appDefinition.encryption?.useDefaultKMSForFieldLevelEncryption === true,
232
232
  ssm: appDefinition.ssm?.enable === true,
233
233
  websockets: appDefinition.websockets?.enable === true,
234
234
  // Add more feature detection as needed
@@ -670,7 +670,7 @@ To integrate Frigg into your production application:
670
670
  const appDefinition = {
671
671
  integrations: [], // Will be populated based on selected integrations
672
672
  user: { password: true },
673
- encryption: { fieldLevelEncryptionMethod: 'kms' },
673
+ encryption: { useDefaultKMSForFieldLevelEncryption: true },
674
674
  vpc: { enable: true },
675
675
  security: {
676
676
  cors: {
@@ -9,7 +9,7 @@ AWS Discovery automatically finds your default AWS resources (VPC, subnets, secu
9
9
  AWS Discovery runs automatically during `frigg build` and `frigg deploy` when your AppDefinition includes:
10
10
 
11
11
  - `vpc.enable: true` - VPC support
12
- - `encryption.fieldLevelEncryptionMethod: 'kms'` - KMS encryption
12
+ - `encryption.useDefaultKMSForFieldLevelEncryption: true` - KMS encryption
13
13
  - `ssm.enable: true` - SSM Parameter Store
14
14
 
15
15
  ## Fail-Fast Behavior
@@ -222,7 +222,7 @@ If you're stuck, try this recovery process:
222
222
  // backend/index.js - temporarily disable problematic features
223
223
  const appDefinition = {
224
224
  vpc: { enable: false },
225
- encryption: { fieldLevelEncryptionMethod: 'aes' },
225
+ encryption: { useDefaultKMSForFieldLevelEncryption: false },
226
226
  ssm: { enable: false }
227
227
  };
228
228
  ```
@@ -354,7 +354,7 @@ Additional permissions needed when your app definition includes `vpc: { enable:
354
354
 
355
355
  ### KMS Support
356
356
 
357
- Additional permissions needed when your app definition includes `encryption: { fieldLevelEncryptionMethod: 'kms' }`:
357
+ Additional permissions needed when your app definition includes `encryption: { useDefaultKMSForFieldLevelEncryption: true }`:
358
358
 
359
359
  ```json
360
360
  {
@@ -58,7 +58,7 @@ The command analyzes your `backend/index.js` AppDefinition and generates IAM pol
58
58
  - Route table and security group management
59
59
  - Elastic IP allocation
60
60
 
61
- #### KMS Encryption (`encryption.fieldLevelEncryptionMethod: 'kms'`)
61
+ #### KMS Encryption (`encryption.useDefaultKMSForFieldLevelEncryption: true`)
62
62
 
63
63
  - KMS key usage for Lambda and S3
64
64
  - Data encryption and decryption permissions
@@ -85,7 +85,7 @@ const appDefinition = {
85
85
  enable: true,
86
86
  },
87
87
  encryption: {
88
- fieldLevelEncryptionMethod: 'kms',
88
+ useDefaultKMSForFieldLevelEncryption: true,
89
89
  },
90
90
  ssm: {
91
91
  enable: false,
@@ -155,8 +155,7 @@ const appDefinition = {
155
155
 
156
156
  // KMS encryption
157
157
  encryption: {
158
- fieldLevelEncryptionMethod: 'kms',
159
- createResourceIfNoneFound: true
158
+ useDefaultKMSForFieldLevelEncryption: true
160
159
  },
161
160
 
162
161
  // SSM Parameter Store
@@ -218,7 +217,7 @@ const serverlessConfig = await composeServerlessDefinition(appDefinition);
218
217
  const appDefinition = {
219
218
  name: 'secure-app',
220
219
  vpc: { enable: true },
221
- encryption: { fieldLevelEncryptionMethod: 'kms' },
220
+ encryption: { useDefaultKMSForFieldLevelEncryption: true },
222
221
  ssm: { enable: true },
223
222
  integrations: [{ Definition: { name: 'salesforce' } }],
224
223
  };
@@ -151,7 +151,7 @@ const mockAppDefinitions = {
151
151
 
152
152
  kmsOnly: {
153
153
  name: 'kms-test-app',
154
- encryption: { fieldLevelEncryptionMethod: 'kms' },
154
+ encryption: { useDefaultKMSForFieldLevelEncryption: true },
155
155
  integrations: []
156
156
  },
157
157
 
@@ -164,7 +164,7 @@ const mockAppDefinitions = {
164
164
  allFeatures: {
165
165
  name: 'full-feature-app',
166
166
  vpc: { enable: true },
167
- encryption: { fieldLevelEncryptionMethod: 'kms' },
167
+ encryption: { useDefaultKMSForFieldLevelEncryption: true },
168
168
  ssm: { enable: true },
169
169
  integrations: [{
170
170
  Definition: {
@@ -281,7 +281,7 @@ const mockEnvironmentVariables = {
281
281
  AWS_DISCOVERY_SUBNET_ID_1: mockSubnets[0].SubnetId,
282
282
  AWS_DISCOVERY_SUBNET_ID_2: mockSubnets[1].SubnetId,
283
283
  AWS_DISCOVERY_ROUTE_TABLE_ID: mockRouteTables[0].RouteTableId,
284
- AWS_DISCOVERY_KMS_KEY_ID:mockKmsKeyMetadata.Arn
284
+ AWS_DISCOVERY_KMS_KEY_ID: mockKmsKeyMetadata.Arn
285
285
  };
286
286
 
287
287
  // Fallback environment variables for error scenarios
@@ -291,7 +291,7 @@ const mockFallbackEnvironmentVariables = {
291
291
  AWS_DISCOVERY_SUBNET_ID_1: 'subnet-fallback-1',
292
292
  AWS_DISCOVERY_SUBNET_ID_2: 'subnet-fallback-2',
293
293
  AWS_DISCOVERY_ROUTE_TABLE_ID: 'rtb-fallback',
294
- AWS_DISCOVERY_KMS_KEY_ID:'arn:aws:kms:*:*:key/*'
294
+ AWS_DISCOVERY_KMS_KEY_ID: 'arn:aws:kms:*:*:key/*'
295
295
  };
296
296
 
297
297
  // Mock AWS SDK responses
@@ -115,7 +115,7 @@ function createMockAppDefinition(features = {}, integrations = []) {
115
115
  }
116
116
 
117
117
  if (features.kms) {
118
- appDefinition.encryption = { fieldLevelEncryptionMethod: 'kms' };
118
+ appDefinition.encryption = { useDefaultKMSForFieldLevelEncryption: true };
119
119
  }
120
120
 
121
121
  if (features.ssm) {
@@ -453,16 +453,18 @@ class AWSDiscovery {
453
453
 
454
454
  /**
455
455
  * Find the default KMS key for the account
456
- * @returns {Promise<string|null>} KMS key ARN or null if no key found
456
+ * @returns {Promise<string>} KMS key ARN or wildcard pattern as fallback
457
457
  */
458
458
  async findDefaultKmsKey() {
459
459
  try {
460
+ // First try to find a key with alias/aws/lambda
460
461
  const command = new ListKeysCommand({});
461
462
  const response = await this.kmsClient.send(command);
462
463
 
463
464
  if (!response.Keys || response.Keys.length === 0) {
464
- console.log('No KMS keys found in account');
465
- return null;
465
+ // Return AWS managed key ARN pattern as fallback
466
+ const accountId = await this.getAccountId();
467
+ return `arn:aws:kms:${this.region}:${accountId}:key/*`;
466
468
  }
467
469
 
468
470
  // Look for customer managed keys first
@@ -474,21 +476,21 @@ class AWSDiscovery {
474
476
  if (keyDetails.KeyMetadata &&
475
477
  keyDetails.KeyMetadata.KeyManager === 'CUSTOMER' &&
476
478
  keyDetails.KeyMetadata.KeyState === 'Enabled') {
477
- console.log(`Found customer managed KMS key: ${keyDetails.KeyMetadata.Arn}`);
478
479
  return keyDetails.KeyMetadata.Arn;
479
480
  }
480
481
  } catch (error) {
481
482
  // Continue to next key if we can't describe this one
482
- console.warn(`Could not describe key ${key.KeyId}:`, error.message);
483
483
  continue;
484
484
  }
485
485
  }
486
486
 
487
- console.log('No customer managed KMS keys found');
488
- return null;
487
+ // Fallback to wildcard pattern for AWS managed keys
488
+ const accountId = await this.getAccountId();
489
+ return `arn:aws:kms:${this.region}:${accountId}:key/*`;
489
490
  } catch (error) {
490
491
  console.error('Error finding default KMS key:', error);
491
- return null;
492
+ // Return wildcard pattern as ultimate fallback
493
+ return '*';
492
494
  }
493
495
  }
494
496
 
@@ -501,7 +503,7 @@ class AWSDiscovery {
501
503
  * @returns {string} return.privateSubnetId2 - Second private subnet ID
502
504
  * @returns {string} return.publicSubnetId - Public subnet ID for NAT Gateway
503
505
  * @returns {string} return.privateRouteTableId - Private route table ID
504
- * @returns {string|null} return.defaultKmsKeyId - Default KMS key ARN or null if not found
506
+ * @returns {string} return.defaultKmsKeyId - Default KMS key ARN
505
507
  * @throws {Error} If resource discovery fails
506
508
  */
507
509
  async discoverResources() {
@@ -524,11 +526,7 @@ class AWSDiscovery {
524
526
  console.log(`Found route table: ${routeTable.RouteTableId}`);
525
527
 
526
528
  const kmsKeyArn = await this.findDefaultKmsKey();
527
- if (kmsKeyArn) {
528
- console.log(`Found KMS key: ${kmsKeyArn}`);
529
- } else {
530
- console.log('No KMS key found');
531
- }
529
+ console.log(`Found KMS key: ${kmsKeyArn}`);
532
530
 
533
531
  // Try to find existing NAT Gateway
534
532
  const existingNatGateway = await this.findExistingNatGateway(vpc.VpcId);
@@ -137,8 +137,8 @@ class BuildTimeDiscovery {
137
137
  console.log('Running pre-build AWS discovery hook...');
138
138
 
139
139
  // Only run discovery if VPC, KMS, or SSM features are enabled
140
- const needsDiscovery = appDefinition.vpc?.enable ||
141
- appDefinition.encryption?.fieldLevelEncryptionMethod === 'kms' ||
140
+ const needsDiscovery = appDefinition.vpc?.enable ||
141
+ appDefinition.encryption?.useDefaultKMSForFieldLevelEncryption ||
142
142
  appDefinition.ssm?.enable;
143
143
 
144
144
  if (!needsDiscovery) {
@@ -159,7 +159,7 @@ class BuildTimeDiscovery {
159
159
  AWS_DISCOVERY_SUBNET_ID_2: resources.privateSubnetId2,
160
160
  AWS_DISCOVERY_PUBLIC_SUBNET_ID: resources.publicSubnetId,
161
161
  AWS_DISCOVERY_ROUTE_TABLE_ID: resources.privateRouteTableId,
162
- AWS_DISCOVERY_KMS_KEY_ID: resources.defaultKmsKeyId // Keep consistent naming convention (even though it's an ARN)
162
+ AWS_DISCOVERY_KMS_KEY_ID: resources.defaultKmsKeyId
163
163
  };
164
164
 
165
165
  // Set environment variables for serverless to use
@@ -250,7 +250,7 @@ describe('BuildTimeDiscovery', () => {
250
250
 
251
251
  it('should run discovery when KMS is enabled', async () => {
252
252
  const appDefinition = {
253
- encryption: { fieldLevelEncryptionMethod: 'kms' },
253
+ encryption: { useDefaultKMSForFieldLevelEncryption: true },
254
254
  integrations: []
255
255
  };
256
256
 
@@ -35,7 +35,7 @@ function generateIAMCloudFormation(appDefinition, options = {}) {
35
35
  vpc: appDefinition.vpc?.enable === true,
36
36
  kms:
37
37
  appDefinition.encryption
38
- ?.fieldLevelEncryptionMethod === 'kms',
38
+ ?.useDefaultKMSForFieldLevelEncryption === true,
39
39
  ssm: appDefinition.ssm?.enable === true,
40
40
  websockets: appDefinition.websockets?.enable === true,
41
41
  };
@@ -605,19 +605,6 @@ function generateIAMCloudFormation(appDefinition, options = {}) {
605
605
  },
606
606
  },
607
607
  },
608
- {
609
- Sid: 'FriggKMSManagement',
610
- Effect: 'Allow',
611
- Action: [
612
- 'kms:CreateKey',
613
- 'kms:PutKeyPolicy',
614
- 'kms:EnableKeyRotation',
615
- 'kms:TagResource',
616
- 'kms:UntagResource',
617
- 'kms:ListResourceTags',
618
- ],
619
- Resource: '*',
620
- },
621
608
  ],
622
609
  },
623
610
  },
@@ -737,7 +724,8 @@ function getFeatureSummary(appDefinition) {
737
724
  core: true, // Always enabled
738
725
  vpc: appDefinition.vpc?.enable === true,
739
726
  kms:
740
- appDefinition.encryption?.fieldLevelEncryptionMethod === 'kms',
727
+ appDefinition.encryption?.useDefaultKMSForFieldLevelEncryption ===
728
+ true,
741
729
  ssm: appDefinition.ssm?.enable === true,
742
730
  websockets: appDefinition.websockets?.enable === true,
743
731
  };
@@ -7,7 +7,7 @@ describe('IAM Generator', () => {
7
7
  name: 'test-app',
8
8
  integrations: ['Integration1', 'Integration2'],
9
9
  vpc: { enable: true },
10
- encryption: { fieldLevelEncryptionMethod: 'kms' },
10
+ encryption: { useDefaultKMSForFieldLevelEncryption: true },
11
11
  ssm: { enable: true },
12
12
  websockets: { enable: true }
13
13
  };
@@ -46,7 +46,7 @@ describe('IAM Generator', () => {
46
46
  name: 'test-app',
47
47
  integrations: [],
48
48
  vpc: { enable: false },
49
- encryption: { fieldLevelEncryptionMethod: 'aes' },
49
+ encryption: { useDefaultKMSForFieldLevelEncryption: false },
50
50
  ssm: { enable: false },
51
51
  websockets: { enable: false }
52
52
  };
@@ -77,7 +77,7 @@ describe('IAM Generator', () => {
77
77
  const appDefinition = {
78
78
  name: 'test-app',
79
79
  integrations: [],
80
- encryption: { fieldLevelEncryptionMethod: 'kms' }
80
+ encryption: { useDefaultKMSForFieldLevelEncryption: true }
81
81
  };
82
82
 
83
83
  const yaml = generateIAMCloudFormation(appDefinition);
@@ -106,7 +106,7 @@ describe('IAM Generator', () => {
106
106
  name: 'test-app',
107
107
  integrations: [],
108
108
  vpc: { enable: true },
109
- encryption: { fieldLevelEncryptionMethod: 'aes' },
109
+ encryption: { useDefaultKMSForFieldLevelEncryption: false },
110
110
  ssm: { enable: true }
111
111
  };
112
112
 
@@ -49,7 +49,7 @@ describe('VPC/KMS/SSM Integration Tests', () => {
49
49
  const appDefinition = {
50
50
  name: 'test-frigg-app',
51
51
  vpc: { enable: true },
52
- encryption: { fieldLevelEncryptionMethod: 'kms' },
52
+ encryption: { useDefaultKMSForFieldLevelEncryption: true },
53
53
  ssm: { enable: true },
54
54
  integrations: [{
55
55
  Definition: {
@@ -67,7 +67,7 @@ describe('VPC/KMS/SSM Integration Tests', () => {
67
67
  process.env.AWS_DISCOVERY_SUBNET_ID_1 = discoveredResources.privateSubnetId1;
68
68
  process.env.AWS_DISCOVERY_SUBNET_ID_2 = discoveredResources.privateSubnetId2;
69
69
  process.env.AWS_DISCOVERY_ROUTE_TABLE_ID = discoveredResources.privateRouteTableId;
70
- process.env.AWS_DISCOVERY_KMS_KEY_ID =discoveredResources.defaultKmsKeyId;
70
+ process.env.AWS_DISCOVERY_KMS_KEY_ID = discoveredResources.defaultKmsKeyId;
71
71
 
72
72
  // Generate serverless configuration
73
73
  const serverlessConfig = composeServerlessDefinition(appDefinition);
@@ -173,11 +173,11 @@ describe('VPC/KMS/SSM Integration Tests', () => {
173
173
  it('should generate config with only KMS enabled', async () => {
174
174
  const appDefinition = {
175
175
  name: 'kms-only-app',
176
- encryption: { fieldLevelEncryptionMethod: 'kms' },
176
+ encryption: { useDefaultKMSForFieldLevelEncryption: true },
177
177
  integrations: []
178
178
  };
179
179
 
180
- process.env.AWS_DISCOVERY_KMS_KEY_ID =mockAWSResources.defaultKmsKeyId;
180
+ process.env.AWS_DISCOVERY_KMS_KEY_ID = mockAWSResources.defaultKmsKeyId;
181
181
 
182
182
  const serverlessConfig = composeServerlessDefinition(appDefinition);
183
183
 
@@ -231,7 +231,7 @@ describe('VPC/KMS/SSM Integration Tests', () => {
231
231
  expect(mockBuildTimeDiscovery.preBuildHook).toHaveBeenCalledWith(
232
232
  expect.objectContaining({
233
233
  vpc: { enable: true },
234
- encryption: { fieldLevelEncryptionMethod: 'kms' }
234
+ encryption: { useDefaultKMSForFieldLevelEncryption: true }
235
235
  }),
236
236
  'us-east-1'
237
237
  );
@@ -302,7 +302,7 @@ describe('VPC/KMS/SSM Integration Tests', () => {
302
302
  process.env.AWS_DISCOVERY_SECURITY_GROUP_ID = mockAWSResources.defaultSecurityGroupId;
303
303
  process.env.AWS_DISCOVERY_SUBNET_ID_1 = mockAWSResources.privateSubnetId1;
304
304
  process.env.AWS_DISCOVERY_SUBNET_ID_2 = mockAWSResources.privateSubnetId2;
305
- process.env.AWS_DISCOVERY_KMS_KEY_ID =mockAWSResources.defaultKmsKeyId;
305
+ process.env.AWS_DISCOVERY_KMS_KEY_ID = mockAWSResources.defaultKmsKeyId;
306
306
 
307
307
  // In a real deployment, serverless framework would resolve these environment variables
308
308
  // For testing, we can verify the placeholders are correctly formatted
@@ -353,7 +353,7 @@ describe('VPC/KMS/SSM Integration Tests', () => {
353
353
 
354
354
  const appDefinition = {
355
355
  vpc: { enable: true },
356
- encryption: { fieldLevelEncryptionMethod: 'kms' },
356
+ encryption: { useDefaultKMSForFieldLevelEncryption: true },
357
357
  integrations: []
358
358
  };
359
359
 
@@ -41,7 +41,7 @@ async function runDiscovery() {
41
41
 
42
42
  // Check if discovery is needed
43
43
  const needsDiscovery = appDefinition.vpc?.enable ||
44
- appDefinition.encryption?.fieldLevelEncryptionMethod === 'kms' ||
44
+ appDefinition.encryption?.useDefaultKMSForFieldLevelEncryption ||
45
45
  appDefinition.ssm?.enable;
46
46
 
47
47
  if (!needsDiscovery) {
@@ -51,7 +51,7 @@ async function runDiscovery() {
51
51
 
52
52
  console.log('📋 App requires AWS discovery for:');
53
53
  if (appDefinition.vpc?.enable) console.log(' ✅ VPC support');
54
- if (appDefinition.encryption?.fieldLevelEncryptionMethod === 'kms') console.log(' ✅ KMS encryption');
54
+ if (appDefinition.encryption?.useDefaultKMSForFieldLevelEncryption) console.log(' ✅ KMS encryption');
55
55
  if (appDefinition.ssm?.enable) console.log(' ✅ SSM parameters');
56
56
 
57
57
  // Run discovery
@@ -82,7 +82,7 @@ async function runDiscovery() {
82
82
  } else {
83
83
  console.error('🚨 Discovery is required because your AppDefinition has these features enabled:');
84
84
  if (appDefinition.vpc?.enable) console.error(' ❌ VPC support (vpc.enable: true)');
85
- if (appDefinition.encryption?.fieldLevelEncryptionMethod === 'kms') console.error(' ❌ KMS encryption (encryption.fieldLevelEncryptionMethod: \'kms\')');
85
+ if (appDefinition.encryption?.useDefaultKMSForFieldLevelEncryption) console.error(' ❌ KMS encryption (encryption.useDefaultKMSForFieldLevelEncryption: true)');
86
86
  if (appDefinition.ssm?.enable) console.error(' ❌ SSM parameters (ssm.enable: true)');
87
87
  console.error('');
88
88
  console.error('💡 To fix this issue:');
@@ -95,7 +95,7 @@ async function runDiscovery() {
95
95
 
96
96
  console.error('🔧 Or disable features in backend/index.js:');
97
97
  console.error(' vpc: { enable: false }');
98
- console.error(' encryption: { fieldLevelEncryptionMethod: \'aes\' }');
98
+ console.error(' encryption: { useDefaultKMSForFieldLevelEncryption: false }');
99
99
  console.error(' ssm: { enable: false }');
100
100
 
101
101
  process.exit(1);
@@ -10,7 +10,8 @@ const { AWSDiscovery } = require('./aws-discovery');
10
10
  const shouldRunDiscovery = (AppDefinition) => {
11
11
  return (
12
12
  AppDefinition.vpc?.enable === true ||
13
- AppDefinition.encryption?.fieldLevelEncryptionMethod === 'kms' ||
13
+ AppDefinition.encryption?.useDefaultKMSForFieldLevelEncryption ===
14
+ true ||
14
15
  AppDefinition.ssm?.enable === true
15
16
  );
16
17
  };
@@ -492,7 +493,10 @@ const createVPCInfrastructure = (AppDefinition) => {
492
493
  };
493
494
 
494
495
  // KMS Interface Endpoint (paid, but useful if using KMS)
495
- if (AppDefinition.encryption?.fieldLevelEncryptionMethod === 'kms') {
496
+ if (
497
+ AppDefinition.encryption?.useDefaultKMSForFieldLevelEncryption ===
498
+ true
499
+ ) {
496
500
  vpcResources.FriggKMSVPCEndpoint = {
497
501
  Type: 'AWS::EC2::VPCEndpoint',
498
502
  Properties: {
@@ -887,7 +891,9 @@ const composeServerlessDefinition = async (AppDefinition) => {
887
891
  };
888
892
 
889
893
  // KMS Configuration based on App Definition
890
- if (AppDefinition.encryption?.fieldLevelEncryptionMethod === 'kms') {
894
+ if (
895
+ AppDefinition.encryption?.useDefaultKMSForFieldLevelEncryption === true
896
+ ) {
891
897
  // Check if a KMS key was discovered
892
898
  if (discoveredResources.defaultKmsKeyId) {
893
899
  // Use the existing discovered KMS key
@@ -901,112 +907,55 @@ const composeServerlessDefinition = async (AppDefinition) => {
901
907
  Resource: [discoveredResources.defaultKmsKeyId],
902
908
  });
903
909
 
904
- // KMS_KEY_ARN will be set later from custom.kmsGrants for consistency
910
+ definition.provider.environment.KMS_KEY_ARN =
911
+ discoveredResources.defaultKmsKeyId;
905
912
  } else {
906
- // No existing key found - check if we should create one or error
907
- if (AppDefinition.encryption?.createResourceIfNoneFound === true) {
908
- // Create a new KMS key
909
- console.log('No existing KMS key found, creating a new one...');
913
+ // No existing key found, provision a dedicated KMS key
914
+ console.log('No existing KMS key found, creating a new one...');
910
915
 
911
- definition.resources.Resources.FriggKMSKey = {
912
- Type: 'AWS::KMS::Key',
913
- Properties: {
914
- EnableKeyRotation: true,
915
- Description: 'Frigg KMS key for field-level encryption',
916
- KeyPolicy: {
917
- Version: '2012-10-17',
918
- Statement: [
919
- {
920
- Sid: 'AllowRootAccountAdmin',
921
- Effect: 'Allow',
922
- Principal: {
923
- AWS: {
924
- 'Fn::Sub':
925
- 'arn:aws:iam::${AWS::AccountId}:root',
926
- },
927
- },
928
- Action: 'kms:*',
929
- Resource: '*',
930
- },
931
- {
932
- Sid: 'AllowLambdaService',
933
- Effect: 'Allow',
934
- Principal: {
935
- Service: 'lambda.amazonaws.com',
936
- },
937
- Action: [
938
- 'kms:GenerateDataKey',
939
- 'kms:Decrypt',
940
- 'kms:DescribeKey',
941
- ],
942
- Resource: '*',
943
- Condition: {
944
- StringEquals: {
945
- 'kms:ViaService': `lambda.${
946
- process.env.AWS_REGION ||
947
- 'us-east-1'
948
- }.amazonaws.com`,
949
- },
916
+ definition.resources.Resources.FriggKMSKey = {
917
+ Type: 'AWS::KMS::Key',
918
+ Properties: {
919
+ EnableKeyRotation: true,
920
+ KeyPolicy: {
921
+ Version: '2012-10-17',
922
+ Statement: [
923
+ {
924
+ Sid: 'AllowRootAccountAdmin',
925
+ Effect: 'Allow',
926
+ Principal: {
927
+ AWS: {
928
+ 'Fn::Sub':
929
+ 'arn:aws:iam::${AWS::AccountId}:root',
950
930
  },
951
931
  },
952
- ],
953
- },
954
- Tags: [
955
- {
956
- Key: 'Name',
957
- Value: '${self:service}-${self:provider.stage}-frigg-kms-key',
958
- },
959
- {
960
- Key: 'Purpose',
961
- Value: 'Field-level encryption for Frigg application',
932
+ Action: 'kms:*',
933
+ Resource: '*',
962
934
  },
963
935
  ],
964
936
  },
965
- };
966
-
967
- definition.provider.iamRoleStatements.push({
968
- Effect: 'Allow',
969
- Action: ['kms:GenerateDataKey', 'kms:Decrypt'],
970
- Resource: [{ 'Fn::GetAtt': ['FriggKMSKey', 'Arn'] }],
971
- });
937
+ },
938
+ };
972
939
 
973
- definition.provider.environment.KMS_KEY_ARN = {
974
- 'Fn::GetAtt': ['FriggKMSKey', 'Arn'],
975
- };
940
+ definition.provider.iamRoleStatements.push({
941
+ Effect: 'Allow',
942
+ Action: ['kms:GenerateDataKey', 'kms:Decrypt'],
943
+ Resource: [{ 'Fn::GetAtt': ['FriggKMSKey', 'Arn'] }],
944
+ });
976
945
 
977
- // Configure KMS grants to reference the created key
978
- definition.custom.kmsGrants = {
979
- kmsKeyId: { 'Fn::GetAtt': ['FriggKMSKey', 'Arn'] }
980
- };
981
- } else {
982
- // No key found and createIfNoneFound is not enabled - error
983
- throw new Error(
984
- 'KMS field-level encryption is enabled but no KMS key was found. ' +
985
- 'Either provide an existing KMS key or set encryption.createResourceIfNoneFound to true to create a new key.'
986
- );
987
- }
946
+ definition.provider.environment.KMS_KEY_ARN = {
947
+ 'Fn::GetAtt': ['FriggKMSKey', 'Arn'],
948
+ };
988
949
  }
989
950
 
990
951
  definition.plugins.push('serverless-kms-grants');
991
952
 
992
- // Configure KMS grants if not already set (when using existing key)
993
- if (!definition.custom.kmsGrants) {
994
- definition.custom.kmsGrants = {
995
- kmsKeyId:
996
- discoveredResources.defaultKmsKeyId ||
997
- '${env:AWS_DISCOVERY_KMS_KEY_ID}',
998
- };
999
- }
1000
-
1001
- // Always set KMS_KEY_ARN from custom.kmsGrants for consistency
1002
- // This translates AWS_DISCOVERY_KMS_KEY_ID to the runtime variable KMS_KEY_ARN
1003
- if (!definition.provider.environment.KMS_KEY_ARN) {
1004
- // Use the discovered value directly when available (from in-process discovery)
1005
- // Otherwise fall back to environment variable (from separate discovery process)
1006
- definition.provider.environment.KMS_KEY_ARN =
953
+ // Configure KMS grants with discovered default key or environment variable
954
+ definition.custom.kmsGrants = {
955
+ kmsKeyId:
1007
956
  discoveredResources.defaultKmsKeyId ||
1008
- '${env:AWS_DISCOVERY_KMS_KEY_ID}';
1009
- }
957
+ '${env:AWS_DISCOVERY_KMS_KEY_ID}',
958
+ };
1010
959
  }
1011
960
 
1012
961
  // VPC Configuration based on App Definition
@@ -1303,6 +1252,62 @@ const composeServerlessDefinition = async (AppDefinition) => {
1303
1252
  definition.custom[queueReference] = queueName;
1304
1253
  }
1305
1254
  }
1255
+
1256
+ // Discovery has already run successfully at this point if needed
1257
+ // The discoveredResources object contains all the necessary AWS resources
1258
+
1259
+ // Add websocket function if enabled
1260
+ if (AppDefinition.websockets?.enable === true) {
1261
+ definition.functions.defaultWebsocket = {
1262
+ handler:
1263
+ 'node_modules/@friggframework/core/handlers/routers/websocket.handler',
1264
+ events: [
1265
+ {
1266
+ websocket: {
1267
+ route: '$connect',
1268
+ },
1269
+ },
1270
+ {
1271
+ websocket: {
1272
+ route: '$default',
1273
+ },
1274
+ },
1275
+ {
1276
+ websocket: {
1277
+ route: '$disconnect',
1278
+ },
1279
+ },
1280
+ ],
1281
+ };
1282
+ }
1283
+
1284
+ // Discovery has already run successfully at this point if needed
1285
+ // The discoveredResources object contains all the necessary AWS resources
1286
+
1287
+ // Add websocket function if enabled
1288
+ if (AppDefinition.websockets?.enable === true) {
1289
+ definition.functions.defaultWebsocket = {
1290
+ handler:
1291
+ 'node_modules/@friggframework/core/handlers/routers/websocket.handler',
1292
+ events: [
1293
+ {
1294
+ websocket: {
1295
+ route: '$connect',
1296
+ },
1297
+ },
1298
+ {
1299
+ websocket: {
1300
+ route: '$default',
1301
+ },
1302
+ },
1303
+ {
1304
+ websocket: {
1305
+ route: '$disconnect',
1306
+ },
1307
+ },
1308
+ ],
1309
+ };
1310
+ }
1306
1311
  }
1307
1312
 
1308
1313
  // Discovery has already run successfully at this point if needed
@@ -1333,6 +1338,9 @@ const composeServerlessDefinition = async (AppDefinition) => {
1333
1338
  };
1334
1339
  }
1335
1340
 
1341
+ // Discovery has already run successfully at this point if needed
1342
+ // The discoveredResources object contains all the necessary AWS resources
1343
+
1336
1344
  // Modify handler paths to point to the correct node_modules location
1337
1345
  definition.functions = modifyHandlerPaths(definition.functions);
1338
1346
 
@@ -118,9 +118,16 @@ describe('composeServerlessDefinition', () => {
118
118
 
119
119
  const result = await composeServerlessDefinition(appDefinition);
120
120
 
121
- expect(result.provider.vpc).toBeDefined();
122
- expect(result.provider.vpc.securityGroupIds).toEqual(['sg-123456']);
123
- expect(result.provider.vpc.subnetIds).toEqual(['subnet-123456', 'subnet-789012']);
121
+ expect(result.provider.vpc).toBe('${self:custom.vpc.${self:provider.stage}}');
122
+ expect(result.custom.vpc).toEqual({
123
+ '${self:provider.stage}': {
124
+ securityGroupIds: ['${env:AWS_DISCOVERY_SECURITY_GROUP_ID}'],
125
+ subnetIds: [
126
+ '${env:AWS_DISCOVERY_SUBNET_ID_1}',
127
+ '${env:AWS_DISCOVERY_SUBNET_ID_2}'
128
+ ]
129
+ }
130
+ });
124
131
  });
125
132
 
126
133
  it('should add VPC endpoint for S3 when VPC is enabled', async () => {
@@ -131,9 +138,15 @@ describe('composeServerlessDefinition', () => {
131
138
 
132
139
  const result = await composeServerlessDefinition(appDefinition);
133
140
 
134
- expect(result.resources.Resources.VPCEndpointS3).toBeDefined();
135
- expect(result.resources.Resources.VPCEndpointS3.Type).toBe('AWS::EC2::VPCEndpoint');
136
- expect(result.resources.Resources.VPCEndpointS3.Properties.VpcId).toBe('vpc-123456');
141
+ expect(result.resources.Resources.VPCEndpointS3).toEqual({
142
+ Type: 'AWS::EC2::VPCEndpoint',
143
+ Properties: {
144
+ VpcId: '${env:AWS_DISCOVERY_VPC_ID}',
145
+ ServiceName: 'com.amazonaws.${self:provider.region}.s3',
146
+ VpcEndpointType: 'Gateway',
147
+ RouteTableIds: ['${env:AWS_DISCOVERY_ROUTE_TABLE_ID}']
148
+ }
149
+ });
137
150
  });
138
151
 
139
152
  it('should not add VPC configuration when vpc.enable is false', async () => {
@@ -145,6 +158,7 @@ describe('composeServerlessDefinition', () => {
145
158
  const result = await composeServerlessDefinition(appDefinition);
146
159
 
147
160
  expect(result.provider.vpc).toBeUndefined();
161
+ expect(result.custom.vpc).toBeUndefined();
148
162
  expect(result.resources.Resources.VPCEndpointS3).toBeUndefined();
149
163
  });
150
164
 
@@ -156,13 +170,14 @@ describe('composeServerlessDefinition', () => {
156
170
  const result = await composeServerlessDefinition(appDefinition);
157
171
 
158
172
  expect(result.provider.vpc).toBeUndefined();
173
+ expect(result.custom.vpc).toBeUndefined();
159
174
  });
160
175
  });
161
176
 
162
177
  describe('KMS Configuration', () => {
163
- it('should add KMS configuration when encryption is enabled and key is found', async () => {
178
+ it('should add KMS configuration when encryption is enabled', async () => {
164
179
  const appDefinition = {
165
- encryption: { fieldLevelEncryptionMethod: 'kms' },
180
+ encryption: { useDefaultKMSForFieldLevelEncryption: true },
166
181
  integrations: []
167
182
  };
168
183
 
@@ -178,188 +193,24 @@ describe('composeServerlessDefinition', () => {
178
193
  'kms:GenerateDataKey',
179
194
  'kms:Decrypt'
180
195
  ],
181
- Resource: ['arn:aws:kms:us-east-1:123456789012:key/12345678-1234-1234-1234-123456789012']
196
+ Resource: ['${self:custom.kmsGrants.kmsKeyId}']
182
197
  });
183
198
 
184
199
  // Check environment variable
185
- expect(result.provider.environment.KMS_KEY_ARN).toBe('arn:aws:kms:us-east-1:123456789012:key/12345678-1234-1234-1234-123456789012');
200
+ expect(result.provider.environment.KMS_KEY_ARN).toBe('${self:custom.kmsGrants.kmsKeyId}');
186
201
 
187
202
  // Check plugin
188
203
  expect(result.plugins).toContain('serverless-kms-grants');
189
204
 
190
205
  // Check custom configuration
191
206
  expect(result.custom.kmsGrants).toEqual({
192
- kmsKeyId: 'arn:aws:kms:us-east-1:123456789012:key/12345678-1234-1234-1234-123456789012'
193
- });
194
- });
195
-
196
- it('should create new KMS key when encryption is enabled, no key found, and createResourceIfNoneFound is true', async () => {
197
- // Mock AWS discovery to return no KMS key
198
- const { AWSDiscovery } = require('./aws-discovery');
199
- const mockDiscoverResources = jest.fn().mockResolvedValue({
200
- defaultVpcId: 'vpc-123456',
201
- defaultSecurityGroupId: 'sg-123456',
202
- privateSubnetId1: 'subnet-123456',
203
- privateSubnetId2: 'subnet-789012',
204
- publicSubnetId: 'subnet-public',
205
- defaultRouteTableId: 'rtb-123456',
206
- defaultKmsKeyId: null // No KMS key found
207
- });
208
- AWSDiscovery.mockImplementation(() => ({
209
- discoverResources: mockDiscoverResources
210
- }));
211
-
212
- const appDefinition = {
213
- encryption: {
214
- fieldLevelEncryptionMethod: 'kms',
215
- createResourceIfNoneFound: true
216
- },
217
- integrations: []
218
- };
219
-
220
- const result = await composeServerlessDefinition(appDefinition);
221
-
222
- // Check that KMS key resource was created
223
- expect(result.resources.Resources.FriggKMSKey).toEqual({
224
- Type: 'AWS::KMS::Key',
225
- Properties: {
226
- EnableKeyRotation: true,
227
- Description: 'Frigg KMS key for field-level encryption',
228
- KeyPolicy: {
229
- Version: '2012-10-17',
230
- Statement: [
231
- {
232
- Sid: 'AllowRootAccountAdmin',
233
- Effect: 'Allow',
234
- Principal: {
235
- AWS: {
236
- 'Fn::Sub': 'arn:aws:iam::${AWS::AccountId}:root'
237
- }
238
- },
239
- Action: 'kms:*',
240
- Resource: '*'
241
- },
242
- {
243
- Sid: 'AllowLambdaService',
244
- Effect: 'Allow',
245
- Principal: {
246
- Service: 'lambda.amazonaws.com'
247
- },
248
- Action: [
249
- 'kms:GenerateDataKey',
250
- 'kms:Decrypt',
251
- 'kms:DescribeKey'
252
- ],
253
- Resource: '*',
254
- Condition: {
255
- StringEquals: {
256
- 'kms:ViaService': 'lambda.us-east-1.amazonaws.com'
257
- }
258
- }
259
- }
260
- ]
261
- },
262
- Tags: [
263
- {
264
- Key: 'Name',
265
- Value: '${self:service}-${self:provider.stage}-frigg-kms-key'
266
- },
267
- {
268
- Key: 'Purpose',
269
- Value: 'Field-level encryption for Frigg application'
270
- }
271
- ]
272
- }
273
- });
274
-
275
- // Check IAM permissions for the new key
276
- const kmsPermission = result.provider.iamRoleStatements.find(
277
- statement => statement.Action.includes('kms:GenerateDataKey')
278
- );
279
- expect(kmsPermission).toEqual({
280
- Effect: 'Allow',
281
- Action: ['kms:GenerateDataKey', 'kms:Decrypt'],
282
- Resource: [{ 'Fn::GetAtt': ['FriggKMSKey', 'Arn'] }]
283
- });
284
-
285
- // Check environment variable
286
- expect(result.provider.environment.KMS_KEY_ARN).toEqual({
287
- 'Fn::GetAtt': ['FriggKMSKey', 'Arn']
288
- });
289
-
290
- // Check plugin
291
- expect(result.plugins).toContain('serverless-kms-grants');
292
-
293
- // Check custom configuration
294
- // When creating a new key, it should reference the CloudFormation resource
295
- expect(result.custom.kmsGrants).toEqual({
296
- kmsKeyId: { 'Fn::GetAtt': ['FriggKMSKey', 'Arn'] }
297
- });
298
- });
299
-
300
- it('should throw error when encryption is enabled, no key found, and createResourceIfNoneFound is false', async () => {
301
- // Mock AWS discovery to return no KMS key
302
- const { AWSDiscovery } = require('./aws-discovery');
303
- const mockDiscoverResources = jest.fn().mockResolvedValue({
304
- defaultVpcId: 'vpc-123456',
305
- defaultSecurityGroupId: 'sg-123456',
306
- privateSubnetId1: 'subnet-123456',
307
- privateSubnetId2: 'subnet-789012',
308
- publicSubnetId: 'subnet-public',
309
- defaultRouteTableId: 'rtb-123456',
310
- defaultKmsKeyId: null // No KMS key found
311
- });
312
- AWSDiscovery.mockImplementation(() => ({
313
- discoverResources: mockDiscoverResources
314
- }));
315
-
316
- const appDefinition = {
317
- encryption: {
318
- fieldLevelEncryptionMethod: 'kms',
319
- createResourceIfNoneFound: false
320
- },
321
- integrations: []
322
- };
323
-
324
- await expect(composeServerlessDefinition(appDefinition)).rejects.toThrow(
325
- 'KMS field-level encryption is enabled but no KMS key was found. ' +
326
- 'Either provide an existing KMS key or set encryption.createResourceIfNoneFound to true to create a new key.'
327
- );
328
- });
329
-
330
- it('should throw error when encryption is enabled, no key found, and createResourceIfNoneFound is not specified', async () => {
331
- // Mock AWS discovery to return no KMS key
332
- const { AWSDiscovery } = require('./aws-discovery');
333
- const mockDiscoverResources = jest.fn().mockResolvedValue({
334
- defaultVpcId: 'vpc-123456',
335
- defaultSecurityGroupId: 'sg-123456',
336
- privateSubnetId1: 'subnet-123456',
337
- privateSubnetId2: 'subnet-789012',
338
- publicSubnetId: 'subnet-public',
339
- defaultRouteTableId: 'rtb-123456',
340
- defaultKmsKeyId: null // No KMS key found
207
+ kmsKeyId: '${env:AWS_DISCOVERY_KMS_KEY_ID}'
341
208
  });
342
- AWSDiscovery.mockImplementation(() => ({
343
- discoverResources: mockDiscoverResources
344
- }));
345
-
346
- const appDefinition = {
347
- encryption: {
348
- fieldLevelEncryptionMethod: 'kms'
349
- // createResourceIfNoneFound not specified, defaults to false
350
- },
351
- integrations: []
352
- };
353
-
354
- await expect(composeServerlessDefinition(appDefinition)).rejects.toThrow(
355
- 'KMS field-level encryption is enabled but no KMS key was found. ' +
356
- 'Either provide an existing KMS key or set encryption.createResourceIfNoneFound to true to create a new key.'
357
- );
358
209
  });
359
210
 
360
211
  it('should not add KMS configuration when encryption is disabled', async () => {
361
212
  const appDefinition = {
362
- encryption: { fieldLevelEncryptionMethod: 'aes' },
213
+ encryption: { useDefaultKMSForFieldLevelEncryption: false },
363
214
  integrations: []
364
215
  };
365
216
 
@@ -464,9 +315,10 @@ describe('composeServerlessDefinition', () => {
464
315
  expect(result.functions.testIntegration).toEqual({
465
316
  handler: 'node_modules/@friggframework/core/handlers/routers/integration-defined-routers.handlers.testIntegration.handler',
466
317
  events: [{
467
- httpApi: {
318
+ http: {
468
319
  path: '/api/testIntegration-integration/{proxy+}',
469
- method: 'ANY'
320
+ method: 'ANY',
321
+ cors: true
470
322
  }
471
323
  }]
472
324
  });
@@ -537,7 +389,7 @@ describe('composeServerlessDefinition', () => {
537
389
  it('should combine VPC, KMS, and SSM configurations', async () => {
538
390
  const appDefinition = {
539
391
  vpc: { enable: true },
540
- encryption: { fieldLevelEncryptionMethod: 'kms' },
392
+ encryption: { useDefaultKMSForFieldLevelEncryption: true },
541
393
  ssm: { enable: true },
542
394
  integrations: [mockIntegration]
543
395
  };
@@ -546,7 +398,7 @@ describe('composeServerlessDefinition', () => {
546
398
 
547
399
  // VPC
548
400
  expect(result.provider.vpc).toBeDefined();
549
- // custom.vpc doesn't exist in the serverless template
401
+ expect(result.custom.vpc).toBeDefined();
550
402
  expect(result.resources.Resources.VPCEndpointS3).toBeDefined();
551
403
 
552
404
  // KMS
@@ -576,7 +428,7 @@ describe('composeServerlessDefinition', () => {
576
428
  it('should handle partial configuration combinations', async () => {
577
429
  const appDefinition = {
578
430
  vpc: { enable: true },
579
- encryption: { fieldLevelEncryptionMethod: 'kms' },
431
+ encryption: { useDefaultKMSForFieldLevelEncryption: true },
580
432
  integrations: []
581
433
  };
582
434
 
@@ -607,9 +459,9 @@ describe('composeServerlessDefinition', () => {
607
459
  expect(result.resources.Resources.ApiGatewayAlarm5xx).toBeDefined();
608
460
 
609
461
  // Check default functions
462
+ expect(result.functions.defaultWebsocket).toBeDefined();
610
463
  expect(result.functions.auth).toBeDefined();
611
464
  expect(result.functions.user).toBeDefined();
612
- expect(result.functions.health).toBeDefined();
613
465
 
614
466
  // Check default plugins
615
467
  expect(result.plugins).toContain('serverless-jetpack');
@@ -644,64 +496,11 @@ describe('composeServerlessDefinition', () => {
644
496
 
645
497
  const result = await composeServerlessDefinition(appDefinition);
646
498
 
647
- expect(result.provider.environment.STAGE).toBe('${opt:stage, "dev"}');
499
+ expect(result.provider.environment.STAGE).toBe('${opt:stage}');
648
500
  expect(result.provider.environment.AWS_NODEJS_CONNECTION_REUSE_ENABLED).toBe(1);
649
501
  });
650
502
  });
651
503
 
652
- describe('WebSocket Configuration', () => {
653
- it('should add websocket function when websockets.enable is true', async () => {
654
- const appDefinition = {
655
- websockets: { enable: true },
656
- integrations: []
657
- };
658
-
659
- const result = await composeServerlessDefinition(appDefinition);
660
-
661
- expect(result.functions.defaultWebsocket).toEqual({
662
- handler: 'node_modules/@friggframework/core/handlers/routers/websocket.handler',
663
- events: [
664
- {
665
- websocket: {
666
- route: '$connect',
667
- },
668
- },
669
- {
670
- websocket: {
671
- route: '$default',
672
- },
673
- },
674
- {
675
- websocket: {
676
- route: '$disconnect',
677
- },
678
- },
679
- ],
680
- });
681
- });
682
-
683
- it('should not add websocket function when websockets.enable is false', async () => {
684
- const appDefinition = {
685
- websockets: { enable: false },
686
- integrations: []
687
- };
688
-
689
- const result = await composeServerlessDefinition(appDefinition);
690
-
691
- expect(result.functions.defaultWebsocket).toBeUndefined();
692
- });
693
-
694
- it('should not add websocket function when websockets is not defined', async () => {
695
- const appDefinition = {
696
- integrations: []
697
- };
698
-
699
- const result = await composeServerlessDefinition(appDefinition);
700
-
701
- expect(result.functions.defaultWebsocket).toBeUndefined();
702
- });
703
- });
704
-
705
504
  describe('Edge Cases', () => {
706
505
  it('should handle empty app definition', async () => {
707
506
  const appDefinition = {};
@@ -716,9 +515,7 @@ describe('composeServerlessDefinition', () => {
716
515
  integrations: null
717
516
  };
718
517
 
719
- // Should not throw, just ignore invalid integrations
720
- const result = await composeServerlessDefinition(appDefinition);
721
- expect(result).toBeDefined();
518
+ await expect(composeServerlessDefinition(appDefinition)).rejects.toThrow();
722
519
  });
723
520
 
724
521
  it('should handle integration with missing Definition', async () => {
@@ -727,7 +524,7 @@ describe('composeServerlessDefinition', () => {
727
524
  integrations: [invalidIntegration]
728
525
  };
729
526
 
730
- await expect(composeServerlessDefinition(appDefinition)).rejects.toThrow('Invalid integration: missing Definition or name');
527
+ await expect(composeServerlessDefinition(appDefinition)).rejects.toThrow();
731
528
  });
732
529
 
733
530
  it('should handle integration with missing name', async () => {
@@ -738,7 +535,7 @@ describe('composeServerlessDefinition', () => {
738
535
  integrations: [invalidIntegration]
739
536
  };
740
537
 
741
- await expect(composeServerlessDefinition(appDefinition)).rejects.toThrow('Invalid integration: missing Definition or name');
538
+ await expect(composeServerlessDefinition(appDefinition)).rejects.toThrow();
742
539
  });
743
540
  });
744
541
  });
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.425.2d58c19.0",
4
+ "version": "2.0.0--canary.427.c8ff14b.0",
5
5
  "dependencies": {
6
6
  "@aws-sdk/client-ec2": "^3.835.0",
7
7
  "@aws-sdk/client-kms": "^3.835.0",
@@ -9,8 +9,8 @@
9
9
  "@babel/eslint-parser": "^7.18.9",
10
10
  "@babel/parser": "^7.25.3",
11
11
  "@babel/traverse": "^7.25.3",
12
- "@friggframework/schemas": "2.0.0--canary.425.2d58c19.0",
13
- "@friggframework/test": "2.0.0--canary.425.2d58c19.0",
12
+ "@friggframework/schemas": "2.0.0--canary.427.c8ff14b.0",
13
+ "@friggframework/test": "2.0.0--canary.427.c8ff14b.0",
14
14
  "@hapi/boom": "^10.0.1",
15
15
  "@inquirer/prompts": "^5.3.8",
16
16
  "axios": "^1.7.2",
@@ -32,9 +32,8 @@
32
32
  "serverless-http": "^2.7.0"
33
33
  },
34
34
  "devDependencies": {
35
- "@friggframework/eslint-config": "2.0.0--canary.425.2d58c19.0",
36
- "@friggframework/prettier-config": "2.0.0--canary.425.2d58c19.0",
37
- "jest": "^30.1.3",
35
+ "@friggframework/eslint-config": "2.0.0--canary.427.c8ff14b.0",
36
+ "@friggframework/prettier-config": "2.0.0--canary.427.c8ff14b.0",
38
37
  "prettier": "^2.7.1",
39
38
  "serverless": "3.39.0",
40
39
  "serverless-dotenv-plugin": "^6.0.0",
@@ -66,5 +65,5 @@
66
65
  "publishConfig": {
67
66
  "access": "public"
68
67
  },
69
- "gitHead": "2d58c196b068fafce914e0fc631d72a30b745f31"
68
+ "gitHead": "c8ff14b4f6c1a21bd6c254ad12f1c9963b0374db"
70
69
  }