@friggframework/devtools 2.0.0-next.36 → 2.0.0-next.38
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.
- package/frigg-cli/generate-command/__tests__/generate-command.test.js +1 -1
- package/frigg-cli/generate-command/index.js +1 -1
- package/frigg-cli/init-command/backend-first-handler.js +1 -1
- package/infrastructure/AWS-DISCOVERY-TROUBLESHOOTING.md +2 -2
- package/infrastructure/AWS-IAM-CREDENTIAL-NEEDS.md +1 -1
- package/infrastructure/GENERATE-IAM-DOCS.md +2 -2
- package/infrastructure/README.md +116 -105
- package/infrastructure/__tests__/fixtures/mock-aws-resources.js +4 -4
- package/infrastructure/__tests__/helpers/test-utils.js +1 -1
- package/infrastructure/aws-discovery.js +14 -12
- package/infrastructure/build-time-discovery.js +3 -3
- package/infrastructure/build-time-discovery.test.js +1 -1
- package/infrastructure/iam-generator.js +15 -3
- package/infrastructure/iam-generator.test.js +4 -4
- package/infrastructure/integration.test.js +7 -7
- package/infrastructure/run-discovery.js +4 -4
- package/infrastructure/serverless-template.js +102 -103
- package/infrastructure/serverless-template.test.js +241 -38
- package/package.json +7 -6
|
@@ -118,16 +118,9 @@ describe('composeServerlessDefinition', () => {
|
|
|
118
118
|
|
|
119
119
|
const result = await composeServerlessDefinition(appDefinition);
|
|
120
120
|
|
|
121
|
-
expect(result.provider.vpc).
|
|
122
|
-
expect(result.
|
|
123
|
-
|
|
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
|
-
});
|
|
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']);
|
|
131
124
|
});
|
|
132
125
|
|
|
133
126
|
it('should add VPC endpoint for S3 when VPC is enabled', async () => {
|
|
@@ -138,15 +131,9 @@ describe('composeServerlessDefinition', () => {
|
|
|
138
131
|
|
|
139
132
|
const result = await composeServerlessDefinition(appDefinition);
|
|
140
133
|
|
|
141
|
-
expect(result.resources.Resources.VPCEndpointS3).
|
|
142
|
-
|
|
143
|
-
|
|
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
|
-
});
|
|
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');
|
|
150
137
|
});
|
|
151
138
|
|
|
152
139
|
it('should not add VPC configuration when vpc.enable is false', async () => {
|
|
@@ -158,7 +145,6 @@ describe('composeServerlessDefinition', () => {
|
|
|
158
145
|
const result = await composeServerlessDefinition(appDefinition);
|
|
159
146
|
|
|
160
147
|
expect(result.provider.vpc).toBeUndefined();
|
|
161
|
-
expect(result.custom.vpc).toBeUndefined();
|
|
162
148
|
expect(result.resources.Resources.VPCEndpointS3).toBeUndefined();
|
|
163
149
|
});
|
|
164
150
|
|
|
@@ -170,14 +156,13 @@ describe('composeServerlessDefinition', () => {
|
|
|
170
156
|
const result = await composeServerlessDefinition(appDefinition);
|
|
171
157
|
|
|
172
158
|
expect(result.provider.vpc).toBeUndefined();
|
|
173
|
-
expect(result.custom.vpc).toBeUndefined();
|
|
174
159
|
});
|
|
175
160
|
});
|
|
176
161
|
|
|
177
162
|
describe('KMS Configuration', () => {
|
|
178
|
-
it('should add KMS configuration when encryption is enabled', async () => {
|
|
163
|
+
it('should add KMS configuration when encryption is enabled and key is found', async () => {
|
|
179
164
|
const appDefinition = {
|
|
180
|
-
encryption: {
|
|
165
|
+
encryption: { fieldLevelEncryptionMethod: 'kms' },
|
|
181
166
|
integrations: []
|
|
182
167
|
};
|
|
183
168
|
|
|
@@ -193,24 +178,188 @@ describe('composeServerlessDefinition', () => {
|
|
|
193
178
|
'kms:GenerateDataKey',
|
|
194
179
|
'kms:Decrypt'
|
|
195
180
|
],
|
|
196
|
-
Resource: ['
|
|
181
|
+
Resource: ['arn:aws:kms:us-east-1:123456789012:key/12345678-1234-1234-1234-123456789012']
|
|
197
182
|
});
|
|
198
183
|
|
|
199
184
|
// Check environment variable
|
|
200
|
-
expect(result.provider.environment.KMS_KEY_ARN).toBe('
|
|
185
|
+
expect(result.provider.environment.KMS_KEY_ARN).toBe('arn:aws:kms:us-east-1:123456789012:key/12345678-1234-1234-1234-123456789012');
|
|
201
186
|
|
|
202
187
|
// Check plugin
|
|
203
188
|
expect(result.plugins).toContain('serverless-kms-grants');
|
|
204
189
|
|
|
205
190
|
// Check custom configuration
|
|
206
191
|
expect(result.custom.kmsGrants).toEqual({
|
|
207
|
-
kmsKeyId: '
|
|
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
|
|
208
341
|
});
|
|
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
|
+
);
|
|
209
358
|
});
|
|
210
359
|
|
|
211
360
|
it('should not add KMS configuration when encryption is disabled', async () => {
|
|
212
361
|
const appDefinition = {
|
|
213
|
-
encryption: {
|
|
362
|
+
encryption: { fieldLevelEncryptionMethod: 'aes' },
|
|
214
363
|
integrations: []
|
|
215
364
|
};
|
|
216
365
|
|
|
@@ -315,10 +464,9 @@ describe('composeServerlessDefinition', () => {
|
|
|
315
464
|
expect(result.functions.testIntegration).toEqual({
|
|
316
465
|
handler: 'node_modules/@friggframework/core/handlers/routers/integration-defined-routers.handlers.testIntegration.handler',
|
|
317
466
|
events: [{
|
|
318
|
-
|
|
467
|
+
httpApi: {
|
|
319
468
|
path: '/api/testIntegration-integration/{proxy+}',
|
|
320
|
-
method: 'ANY'
|
|
321
|
-
cors: true
|
|
469
|
+
method: 'ANY'
|
|
322
470
|
}
|
|
323
471
|
}]
|
|
324
472
|
});
|
|
@@ -389,7 +537,7 @@ describe('composeServerlessDefinition', () => {
|
|
|
389
537
|
it('should combine VPC, KMS, and SSM configurations', async () => {
|
|
390
538
|
const appDefinition = {
|
|
391
539
|
vpc: { enable: true },
|
|
392
|
-
encryption: {
|
|
540
|
+
encryption: { fieldLevelEncryptionMethod: 'kms' },
|
|
393
541
|
ssm: { enable: true },
|
|
394
542
|
integrations: [mockIntegration]
|
|
395
543
|
};
|
|
@@ -398,7 +546,7 @@ describe('composeServerlessDefinition', () => {
|
|
|
398
546
|
|
|
399
547
|
// VPC
|
|
400
548
|
expect(result.provider.vpc).toBeDefined();
|
|
401
|
-
|
|
549
|
+
// custom.vpc doesn't exist in the serverless template
|
|
402
550
|
expect(result.resources.Resources.VPCEndpointS3).toBeDefined();
|
|
403
551
|
|
|
404
552
|
// KMS
|
|
@@ -428,7 +576,7 @@ describe('composeServerlessDefinition', () => {
|
|
|
428
576
|
it('should handle partial configuration combinations', async () => {
|
|
429
577
|
const appDefinition = {
|
|
430
578
|
vpc: { enable: true },
|
|
431
|
-
encryption: {
|
|
579
|
+
encryption: { fieldLevelEncryptionMethod: 'kms' },
|
|
432
580
|
integrations: []
|
|
433
581
|
};
|
|
434
582
|
|
|
@@ -459,9 +607,9 @@ describe('composeServerlessDefinition', () => {
|
|
|
459
607
|
expect(result.resources.Resources.ApiGatewayAlarm5xx).toBeDefined();
|
|
460
608
|
|
|
461
609
|
// Check default functions
|
|
462
|
-
expect(result.functions.defaultWebsocket).toBeDefined();
|
|
463
610
|
expect(result.functions.auth).toBeDefined();
|
|
464
611
|
expect(result.functions.user).toBeDefined();
|
|
612
|
+
expect(result.functions.health).toBeDefined();
|
|
465
613
|
|
|
466
614
|
// Check default plugins
|
|
467
615
|
expect(result.plugins).toContain('serverless-jetpack');
|
|
@@ -496,11 +644,64 @@ describe('composeServerlessDefinition', () => {
|
|
|
496
644
|
|
|
497
645
|
const result = await composeServerlessDefinition(appDefinition);
|
|
498
646
|
|
|
499
|
-
expect(result.provider.environment.STAGE).toBe('${opt:stage}');
|
|
647
|
+
expect(result.provider.environment.STAGE).toBe('${opt:stage, "dev"}');
|
|
500
648
|
expect(result.provider.environment.AWS_NODEJS_CONNECTION_REUSE_ENABLED).toBe(1);
|
|
501
649
|
});
|
|
502
650
|
});
|
|
503
651
|
|
|
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
|
+
|
|
504
705
|
describe('Edge Cases', () => {
|
|
505
706
|
it('should handle empty app definition', async () => {
|
|
506
707
|
const appDefinition = {};
|
|
@@ -515,7 +716,9 @@ describe('composeServerlessDefinition', () => {
|
|
|
515
716
|
integrations: null
|
|
516
717
|
};
|
|
517
718
|
|
|
518
|
-
|
|
719
|
+
// Should not throw, just ignore invalid integrations
|
|
720
|
+
const result = await composeServerlessDefinition(appDefinition);
|
|
721
|
+
expect(result).toBeDefined();
|
|
519
722
|
});
|
|
520
723
|
|
|
521
724
|
it('should handle integration with missing Definition', async () => {
|
|
@@ -524,7 +727,7 @@ describe('composeServerlessDefinition', () => {
|
|
|
524
727
|
integrations: [invalidIntegration]
|
|
525
728
|
};
|
|
526
729
|
|
|
527
|
-
await expect(composeServerlessDefinition(appDefinition)).rejects.toThrow();
|
|
730
|
+
await expect(composeServerlessDefinition(appDefinition)).rejects.toThrow('Invalid integration: missing Definition or name');
|
|
528
731
|
});
|
|
529
732
|
|
|
530
733
|
it('should handle integration with missing name', async () => {
|
|
@@ -535,7 +738,7 @@ describe('composeServerlessDefinition', () => {
|
|
|
535
738
|
integrations: [invalidIntegration]
|
|
536
739
|
};
|
|
537
740
|
|
|
538
|
-
await expect(composeServerlessDefinition(appDefinition)).rejects.toThrow();
|
|
741
|
+
await expect(composeServerlessDefinition(appDefinition)).rejects.toThrow('Invalid integration: missing Definition or name');
|
|
539
742
|
});
|
|
540
743
|
});
|
|
541
744
|
});
|
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-next.
|
|
4
|
+
"version": "2.0.0-next.38",
|
|
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-next.
|
|
13
|
-
"@friggframework/test": "2.0.0-next.
|
|
12
|
+
"@friggframework/schemas": "2.0.0-next.38",
|
|
13
|
+
"@friggframework/test": "2.0.0-next.38",
|
|
14
14
|
"@hapi/boom": "^10.0.1",
|
|
15
15
|
"@inquirer/prompts": "^5.3.8",
|
|
16
16
|
"axios": "^1.7.2",
|
|
@@ -32,8 +32,9 @@
|
|
|
32
32
|
"serverless-http": "^2.7.0"
|
|
33
33
|
},
|
|
34
34
|
"devDependencies": {
|
|
35
|
-
"@friggframework/eslint-config": "2.0.0-next.
|
|
36
|
-
"@friggframework/prettier-config": "2.0.0-next.
|
|
35
|
+
"@friggframework/eslint-config": "2.0.0-next.38",
|
|
36
|
+
"@friggframework/prettier-config": "2.0.0-next.38",
|
|
37
|
+
"jest": "^30.1.3",
|
|
37
38
|
"prettier": "^2.7.1",
|
|
38
39
|
"serverless": "3.39.0",
|
|
39
40
|
"serverless-dotenv-plugin": "^6.0.0",
|
|
@@ -65,5 +66,5 @@
|
|
|
65
66
|
"publishConfig": {
|
|
66
67
|
"access": "public"
|
|
67
68
|
},
|
|
68
|
-
"gitHead": "
|
|
69
|
+
"gitHead": "a9c9c28fd9abdc8c96a38b8ab0fbb3cbf7d89960"
|
|
69
70
|
}
|