@friggframework/devtools 2.0.0--canary.608.ba60ba6.0 → 2.0.0--canary.608.03436383054a.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.
@@ -198,11 +198,7 @@ class IntegrationBuilder extends InfrastructureBuilder {
198
198
 
199
199
  // App-level FIFO queue for dispatch:'queue' events — one per app,
200
200
  // independent of per-integration queue ownership.
201
- this.createUserActionQueue(
202
- result,
203
- functionPackageConfig,
204
- usePrismaLayer
205
- );
201
+ this.createUserActionQueue(result, functionPackageConfig, usePrismaLayer);
206
202
 
207
203
  for (const integration of appDefinition.integrations) {
208
204
  const integrationName = integration.Definition.name;
@@ -489,11 +485,7 @@ class IntegrationBuilder extends InfrastructureBuilder {
489
485
  * distinct integrations process in parallel. Always created, like the
490
486
  * InternalErrorQueue — idle cost is ~$0.
491
487
  */
492
- createUserActionQueue(
493
- result,
494
- functionPackageConfig,
495
- usePrismaLayer = true
496
- ) {
488
+ createUserActionQueue(result, functionPackageConfig, usePrismaLayer = true) {
497
489
  const queueName =
498
490
  '${self:service}-${self:provider.stage}-FriggUserActionQueue.fifo';
499
491
  const dlqName =
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * Tests for Integration Builder
3
- *
3
+ *
4
4
  * Tests integration-specific Lambda functions and SQS queues
5
5
  */
6
6
 
@@ -17,7 +17,9 @@ describe('IntegrationBuilder', () => {
17
17
  describe('shouldExecute()', () => {
18
18
  it('should return true when integrations array has items', () => {
19
19
  const appDefinition = {
20
- integrations: [{ Definition: { name: 'test' } }],
20
+ integrations: [
21
+ { Definition: { name: 'test' } },
22
+ ],
21
23
  };
22
24
 
23
25
  expect(integrationBuilder.shouldExecute(appDefinition)).toBe(true);
@@ -83,7 +85,9 @@ describe('IntegrationBuilder', () => {
83
85
 
84
86
  it('should error when integration is missing Definition', () => {
85
87
  const appDefinition = {
86
- integrations: [{ someOtherField: 'value' }],
88
+ integrations: [
89
+ { someOtherField: 'value' },
90
+ ],
87
91
  };
88
92
 
89
93
  const result = integrationBuilder.validate(appDefinition);
@@ -96,7 +100,9 @@ describe('IntegrationBuilder', () => {
96
100
 
97
101
  it('should error when integration Definition is missing name', () => {
98
102
  const appDefinition = {
99
- integrations: [{ Definition: {} }],
103
+ integrations: [
104
+ { Definition: {} },
105
+ ],
100
106
  };
101
107
 
102
108
  const result = integrationBuilder.validate(appDefinition);
@@ -126,7 +132,9 @@ describe('IntegrationBuilder', () => {
126
132
  describe('build()', () => {
127
133
  it('should create HTTP handler for integration', async () => {
128
134
  const appDefinition = {
129
- integrations: [{ Definition: { name: 'hubspot' } }],
135
+ integrations: [
136
+ { Definition: { name: 'hubspot' } },
137
+ ],
130
138
  };
131
139
 
132
140
  const result = await integrationBuilder.build(appDefinition, {});
@@ -139,7 +147,9 @@ describe('IntegrationBuilder', () => {
139
147
 
140
148
  it('should configure HTTP API event for integration', async () => {
141
149
  const appDefinition = {
142
- integrations: [{ Definition: { name: 'salesforce' } }],
150
+ integrations: [
151
+ { Definition: { name: 'salesforce' } },
152
+ ],
143
153
  };
144
154
 
145
155
  const result = await integrationBuilder.build(appDefinition, {});
@@ -156,7 +166,9 @@ describe('IntegrationBuilder', () => {
156
166
 
157
167
  it('should create SQS queue for integration', async () => {
158
168
  const appDefinition = {
159
- integrations: [{ Definition: { name: 'slack' } }],
169
+ integrations: [
170
+ { Definition: { name: 'slack' } },
171
+ ],
160
172
  };
161
173
 
162
174
  const result = await integrationBuilder.build(appDefinition, {});
@@ -167,39 +179,39 @@ describe('IntegrationBuilder', () => {
167
179
 
168
180
  it('should configure queue with correct retention and visibility timeout', async () => {
169
181
  const appDefinition = {
170
- integrations: [{ Definition: { name: 'test' } }],
182
+ integrations: [
183
+ { Definition: { name: 'test' } },
184
+ ],
171
185
  };
172
186
 
173
187
  const result = await integrationBuilder.build(appDefinition, {});
174
188
 
175
- expect(
176
- result.resources.TestQueue.Properties.MessageRetentionPeriod
177
- ).toBe(345600);
178
- expect(
179
- result.resources.TestQueue.Properties.VisibilityTimeout
180
- ).toBe(1800);
189
+ expect(result.resources.TestQueue.Properties.MessageRetentionPeriod).toBe(345600);
190
+ expect(result.resources.TestQueue.Properties.VisibilityTimeout).toBe(1800);
181
191
  });
182
192
 
183
193
  it('should configure redrive policy to internal error queue', async () => {
184
194
  const appDefinition = {
185
- integrations: [{ Definition: { name: 'test' } }],
195
+ integrations: [
196
+ { Definition: { name: 'test' } },
197
+ ],
186
198
  };
187
199
 
188
200
  const result = await integrationBuilder.build(appDefinition, {});
189
201
 
190
- expect(result.resources.TestQueue.Properties.RedrivePolicy).toEqual(
191
- {
192
- maxReceiveCount: 3,
193
- deadLetterTargetArn: {
194
- 'Fn::GetAtt': ['InternalErrorQueue', 'Arn'],
195
- },
196
- }
197
- );
202
+ expect(result.resources.TestQueue.Properties.RedrivePolicy).toEqual({
203
+ maxReceiveCount: 3,
204
+ deadLetterTargetArn: {
205
+ 'Fn::GetAtt': ['InternalErrorQueue', 'Arn'],
206
+ },
207
+ });
198
208
  });
199
209
 
200
210
  it('should create queue worker function', async () => {
201
211
  const appDefinition = {
202
- integrations: [{ Definition: { name: 'hubspot' } }],
212
+ integrations: [
213
+ { Definition: { name: 'hubspot' } },
214
+ ],
203
215
  };
204
216
 
205
217
  const result = await integrationBuilder.build(appDefinition, {});
@@ -209,7 +221,9 @@ describe('IntegrationBuilder', () => {
209
221
 
210
222
  it('should configure queue worker with SQS event', async () => {
211
223
  const appDefinition = {
212
- integrations: [{ Definition: { name: 'test' } }],
224
+ integrations: [
225
+ { Definition: { name: 'test' } },
226
+ ],
213
227
  };
214
228
 
215
229
  const result = await integrationBuilder.build(appDefinition, {});
@@ -227,7 +241,9 @@ describe('IntegrationBuilder', () => {
227
241
 
228
242
  it('should set queue worker timeout to 600 seconds', async () => {
229
243
  const appDefinition = {
230
- integrations: [{ Definition: { name: 'test' } }],
244
+ integrations: [
245
+ { Definition: { name: 'test' } },
246
+ ],
231
247
  };
232
248
 
233
249
  const result = await integrationBuilder.build(appDefinition, {});
@@ -237,19 +253,21 @@ describe('IntegrationBuilder', () => {
237
253
 
238
254
  it('should set queue worker reserved concurrency', async () => {
239
255
  const appDefinition = {
240
- integrations: [{ Definition: { name: 'test' } }],
256
+ integrations: [
257
+ { Definition: { name: 'test' } },
258
+ ],
241
259
  };
242
260
 
243
261
  const result = await integrationBuilder.build(appDefinition, {});
244
262
 
245
- expect(result.functions.testQueueWorker.reservedConcurrency).toBe(
246
- 20
247
- );
263
+ expect(result.functions.testQueueWorker.reservedConcurrency).toBe(20);
248
264
  });
249
265
 
250
266
  it('should add queue URL to environment variables', async () => {
251
267
  const appDefinition = {
252
- integrations: [{ Definition: { name: 'slack' } }],
268
+ integrations: [
269
+ { Definition: { name: 'slack' } },
270
+ ],
253
271
  };
254
272
 
255
273
  const result = await integrationBuilder.build(appDefinition, {});
@@ -261,14 +279,14 @@ describe('IntegrationBuilder', () => {
261
279
 
262
280
  it('should add queue name to custom variables', async () => {
263
281
  const appDefinition = {
264
- integrations: [{ Definition: { name: 'stripe' } }],
282
+ integrations: [
283
+ { Definition: { name: 'stripe' } },
284
+ ],
265
285
  };
266
286
 
267
287
  const result = await integrationBuilder.build(appDefinition, {});
268
288
 
269
- expect(result.custom.StripeQueue).toBe(
270
- '${self:service}--${self:provider.stage}-StripeQueue'
271
- );
289
+ expect(result.custom.StripeQueue).toBe('${self:service}--${self:provider.stage}-StripeQueue');
272
290
  });
273
291
 
274
292
  it('should handle multiple integrations', async () => {
@@ -303,7 +321,9 @@ describe('IntegrationBuilder', () => {
303
321
 
304
322
  it('should capitalize integration name for queue reference', async () => {
305
323
  const appDefinition = {
306
- integrations: [{ Definition: { name: 'myIntegration' } }],
324
+ integrations: [
325
+ { Definition: { name: 'myIntegration' } },
326
+ ],
307
327
  };
308
328
 
309
329
  const result = await integrationBuilder.build(appDefinition, {});
@@ -314,7 +334,9 @@ describe('IntegrationBuilder', () => {
314
334
 
315
335
  it('should handle integration names with hyphens', async () => {
316
336
  const appDefinition = {
317
- integrations: [{ Definition: { name: 'my-integration' } }],
337
+ integrations: [
338
+ { Definition: { name: 'my-integration' } },
339
+ ],
318
340
  };
319
341
 
320
342
  const result = await integrationBuilder.build(appDefinition, {});
@@ -339,8 +361,7 @@ describe('IntegrationBuilder', () => {
339
361
  };
340
362
 
341
363
  const result = await integrationBuilder.build(appDefinition, {});
342
- const retention =
343
- result.resources.TestQueue.Properties.MessageRetentionPeriod;
364
+ const retention = result.resources.TestQueue.Properties.MessageRetentionPeriod;
344
365
 
345
366
  // The max SQS DelaySeconds is 900. Retention must comfortably
346
367
  // exceed this to ensure delayed messages are never silently lost.
@@ -356,9 +377,7 @@ describe('IntegrationBuilder', () => {
356
377
  };
357
378
 
358
379
  const result = await integrationBuilder.build(appDefinition, {});
359
- const maxReceiveCount =
360
- result.resources.TestQueue.Properties.RedrivePolicy
361
- .maxReceiveCount;
380
+ const maxReceiveCount = result.resources.TestQueue.Properties.RedrivePolicy.maxReceiveCount;
362
381
 
363
382
  // Should allow at least 2 retries (maxReceiveCount >= 3)
364
383
  expect(maxReceiveCount).toBeGreaterThanOrEqual(3);
@@ -374,9 +393,7 @@ describe('IntegrationBuilder', () => {
374
393
  const result = await integrationBuilder.build(appDefinition, {});
375
394
  const sqsEvent = result.functions.testQueueWorker.events[0].sqs;
376
395
 
377
- expect(sqsEvent.functionResponseType).toBe(
378
- 'ReportBatchItemFailures'
379
- );
396
+ expect(sqsEvent.functionResponseType).toBe('ReportBatchItemFailures');
380
397
  });
381
398
  });
382
399
 
@@ -389,18 +406,10 @@ describe('IntegrationBuilder', () => {
389
406
  const result = await integrationBuilder.build(appDefinition, {});
390
407
 
391
408
  expect(result.resources.DLQMessageAlarm).toBeDefined();
392
- expect(result.resources.DLQMessageAlarm.Type).toBe(
393
- 'AWS::CloudWatch::Alarm'
394
- );
395
- expect(result.resources.DLQMessageAlarm.Properties.MetricName).toBe(
396
- 'ApproximateNumberOfMessagesVisible'
397
- );
398
- expect(
399
- result.resources.DLQMessageAlarm.Properties.ComparisonOperator
400
- ).toBe('GreaterThanThreshold');
401
- expect(result.resources.DLQMessageAlarm.Properties.Threshold).toBe(
402
- 500
403
- );
409
+ expect(result.resources.DLQMessageAlarm.Type).toBe('AWS::CloudWatch::Alarm');
410
+ expect(result.resources.DLQMessageAlarm.Properties.MetricName).toBe('ApproximateNumberOfMessagesVisible');
411
+ expect(result.resources.DLQMessageAlarm.Properties.ComparisonOperator).toBe('GreaterThanThreshold');
412
+ expect(result.resources.DLQMessageAlarm.Properties.Threshold).toBe(500);
404
413
  });
405
414
 
406
415
  it('should wire alarm to InternalErrorBridgeTopic for notifications', async () => {
@@ -410,9 +419,9 @@ describe('IntegrationBuilder', () => {
410
419
 
411
420
  const result = await integrationBuilder.build(appDefinition, {});
412
421
 
413
- expect(
414
- result.resources.DLQMessageAlarm.Properties.AlarmActions
415
- ).toEqual([{ Ref: 'InternalErrorBridgeTopic' }]);
422
+ expect(result.resources.DLQMessageAlarm.Properties.AlarmActions).toEqual([
423
+ { Ref: 'InternalErrorBridgeTopic' },
424
+ ]);
416
425
  });
417
426
 
418
427
  it('should create a DLQ processor Lambda triggered by InternalErrorQueue', async () => {
@@ -426,9 +435,7 @@ describe('IntegrationBuilder', () => {
426
435
  expect(result.functions.dlqProcessor.events[0].sqs.arn).toEqual({
427
436
  'Fn::GetAtt': ['InternalErrorQueue', 'Arn'],
428
437
  });
429
- expect(
430
- result.functions.dlqProcessor.events[0].sqs.functionResponseType
431
- ).toBe('ReportBatchItemFailures');
438
+ expect(result.functions.dlqProcessor.events[0].sqs.functionResponseType).toBe('ReportBatchItemFailures');
432
439
  });
433
440
 
434
441
  it('DLQ processor should have skipEsbuild, short timeout, and low concurrency', async () => {
@@ -440,9 +447,7 @@ describe('IntegrationBuilder', () => {
440
447
 
441
448
  expect(result.functions.dlqProcessor.skipEsbuild).toBe(true);
442
449
  expect(result.functions.dlqProcessor.package).toBeDefined();
443
- expect(result.functions.dlqProcessor.timeout).toBeLessThanOrEqual(
444
- 60
445
- );
450
+ expect(result.functions.dlqProcessor.timeout).toBeLessThanOrEqual(60);
446
451
  expect(result.functions.dlqProcessor.reservedConcurrency).toBe(1);
447
452
  });
448
453
  });
@@ -469,7 +474,7 @@ describe('IntegrationBuilder', () => {
469
474
  Definition: {
470
475
  name: 'hubspot',
471
476
  webhooks: true,
472
- },
477
+ }
473
478
  },
474
479
  ],
475
480
  };
@@ -489,7 +494,7 @@ describe('IntegrationBuilder', () => {
489
494
  Definition: {
490
495
  name: 'salesforce',
491
496
  webhooks: { enabled: true },
492
- },
497
+ }
493
498
  },
494
499
  ],
495
500
  };
@@ -506,7 +511,7 @@ describe('IntegrationBuilder', () => {
506
511
  Definition: {
507
512
  name: 'slack',
508
513
  webhooks: false,
509
- },
514
+ }
510
515
  },
511
516
  ],
512
517
  };
@@ -523,7 +528,7 @@ describe('IntegrationBuilder', () => {
523
528
  Definition: {
524
529
  name: 'test',
525
530
  webhooks: { enabled: false },
526
- },
531
+ }
527
532
  },
528
533
  ],
529
534
  };
@@ -540,7 +545,7 @@ describe('IntegrationBuilder', () => {
540
545
  Definition: {
541
546
  name: 'stripe',
542
547
  webhooks: true,
543
- },
548
+ }
544
549
  },
545
550
  ],
546
551
  };
@@ -711,7 +716,7 @@ describe('IntegrationBuilder', () => {
711
716
  Definition: {
712
717
  name: 'asana',
713
718
  webhooks: true,
714
- },
719
+ }
715
720
  },
716
721
  ],
717
722
  };
@@ -737,7 +742,7 @@ describe('IntegrationBuilder', () => {
737
742
  Definition: {
738
743
  name: 'test',
739
744
  webhooks: true,
740
- },
745
+ }
741
746
  },
742
747
  ],
743
748
  };
@@ -765,19 +770,19 @@ describe('IntegrationBuilder', () => {
765
770
  Definition: {
766
771
  name: 'hubspot',
767
772
  webhooks: true,
768
- },
773
+ }
769
774
  },
770
775
  {
771
776
  Definition: {
772
777
  name: 'salesforce',
773
778
  webhooks: false,
774
- },
779
+ }
775
780
  },
776
781
  {
777
782
  Definition: {
778
783
  name: 'slack',
779
784
  webhooks: { enabled: true },
780
- },
785
+ }
781
786
  },
782
787
  ],
783
788
  };
@@ -807,7 +812,7 @@ describe('IntegrationBuilder', () => {
807
812
  Definition: {
808
813
  name: 'test',
809
814
  webhooks: true,
810
- },
815
+ }
811
816
  },
812
817
  ],
813
818
  };
@@ -824,7 +829,7 @@ describe('IntegrationBuilder', () => {
824
829
  Definition: {
825
830
  name: 'test',
826
831
  webhooks: true,
827
- },
832
+ }
828
833
  },
829
834
  ],
830
835
  };
@@ -832,26 +837,24 @@ describe('IntegrationBuilder', () => {
832
837
  const result = await integrationBuilder.build(appDefinition, {});
833
838
 
834
839
  expect(result.functions.testWebhook.package).toBeDefined();
835
- expect(result.functions.testWebhook.package.exclude).toContain(
836
- 'node_modules/aws-sdk/**'
837
- );
838
- expect(result.functions.testWebhook.package.exclude).toContain(
839
- 'node_modules/@prisma/**'
840
- );
840
+ expect(result.functions.testWebhook.package.exclude).toContain('node_modules/aws-sdk/**');
841
+ expect(result.functions.testWebhook.package.exclude).toContain('node_modules/@prisma/**');
841
842
  });
842
843
  });
843
844
 
844
845
  describe('Prisma Layer Configuration', () => {
845
846
  it('should attach Prisma Lambda layer to queue worker functions', async () => {
846
847
  const appDefinition = {
847
- integrations: [{ Definition: { name: 'hubspot' } }],
848
+ integrations: [
849
+ { Definition: { name: 'hubspot' } },
850
+ ],
848
851
  };
849
852
 
850
853
  const result = await integrationBuilder.build(appDefinition, {});
851
854
 
852
855
  // Queue workers need Prisma layer for database operations
853
856
  expect(result.functions.hubspotQueueWorker.layers).toEqual([
854
- { Ref: 'PrismaLambdaLayer' },
857
+ { Ref: 'PrismaLambdaLayer' }
855
858
  ]);
856
859
  });
857
860
 
@@ -867,26 +870,28 @@ describe('IntegrationBuilder', () => {
867
870
  const result = await integrationBuilder.build(appDefinition, {});
868
871
 
869
872
  expect(result.functions.hubspotQueueWorker.layers).toEqual([
870
- { Ref: 'PrismaLambdaLayer' },
873
+ { Ref: 'PrismaLambdaLayer' }
871
874
  ]);
872
875
  expect(result.functions.salesforceQueueWorker.layers).toEqual([
873
- { Ref: 'PrismaLambdaLayer' },
876
+ { Ref: 'PrismaLambdaLayer' }
874
877
  ]);
875
878
  expect(result.functions.slackQueueWorker.layers).toEqual([
876
- { Ref: 'PrismaLambdaLayer' },
879
+ { Ref: 'PrismaLambdaLayer' }
877
880
  ]);
878
881
  });
879
882
 
880
883
  it('should attach Prisma layer to HTTP handlers for database access', async () => {
881
884
  const appDefinition = {
882
- integrations: [{ Definition: { name: 'stripe' } }],
885
+ integrations: [
886
+ { Definition: { name: 'stripe' } },
887
+ ],
883
888
  };
884
889
 
885
890
  const result = await integrationBuilder.build(appDefinition, {});
886
891
 
887
892
  // HTTP handlers also need Prisma for integration queries
888
893
  expect(result.functions.stripe.layers).toEqual([
889
- { Ref: 'PrismaLambdaLayer' },
894
+ { Ref: 'PrismaLambdaLayer' }
890
895
  ]);
891
896
  });
892
897
 
@@ -897,7 +902,7 @@ describe('IntegrationBuilder', () => {
897
902
  Definition: {
898
903
  name: 'hubspot',
899
904
  webhooks: true,
900
- },
905
+ }
901
906
  },
902
907
  ],
903
908
  };
@@ -906,7 +911,7 @@ describe('IntegrationBuilder', () => {
906
911
 
907
912
  // Webhook handlers need Prisma for credential lookups
908
913
  expect(result.functions.hubspotWebhook.layers).toEqual([
909
- { Ref: 'PrismaLambdaLayer' },
914
+ { Ref: 'PrismaLambdaLayer' }
910
915
  ]);
911
916
  });
912
917
 
@@ -1041,10 +1046,7 @@ describe('IntegrationBuilder', () => {
1041
1046
 
1042
1047
  it('omits the Prisma layer on the worker when usePrismaLambdaLayer=false', async () => {
1043
1048
  const result = await integrationBuilder.build(
1044
- {
1045
- usePrismaLambdaLayer: false,
1046
- integrations: [{ Definition: { name: 'test' } }],
1047
- },
1049
+ { usePrismaLambdaLayer: false, integrations: [{ Definition: { name: 'test' } }] },
1048
1050
  {}
1049
1051
  );
1050
1052
 
@@ -1054,3 +1056,4 @@ describe('IntegrationBuilder', () => {
1054
1056
  });
1055
1057
  });
1056
1058
  });
1059
+
@@ -14,7 +14,7 @@ function generateIAMCloudFormation(options = {}) {
14
14
  appName = 'Frigg',
15
15
  features = {},
16
16
  userPrefix = 'frigg-deployment-user',
17
- stackName = 'frigg-deployment-iam',
17
+ stackName = 'frigg-deployment-iam'
18
18
  } = options;
19
19
 
20
20
  const deploymentUserName = userPrefix;
@@ -765,7 +765,8 @@ function getFeatureSummary(appDefinition) {
765
765
  const features = {
766
766
  core: true, // Always enabled
767
767
  vpc: appDefinition.vpc?.enable === true,
768
- kms: appDefinition.encryption?.fieldLevelEncryptionMethod === 'kms',
768
+ kms:
769
+ appDefinition.encryption?.fieldLevelEncryptionMethod === 'kms',
769
770
  ssm: appDefinition.ssm?.enable === true,
770
771
  websockets: appDefinition.websockets?.enable === true,
771
772
  };
@@ -784,10 +785,7 @@ function getFeatureSummary(appDefinition) {
784
785
  * @returns {Object} Basic IAM policy document
785
786
  */
786
787
  function generateBasicIAMPolicy() {
787
- const basicPolicyPath = path.join(
788
- __dirname,
789
- 'templates/iam-policy-basic.json'
790
- );
788
+ const basicPolicyPath = path.join(__dirname, 'templates/iam-policy-basic.json');
791
789
  return require(basicPolicyPath);
792
790
  }
793
791
 
@@ -796,10 +794,7 @@ function generateBasicIAMPolicy() {
796
794
  * @returns {Object} Full IAM policy document
797
795
  */
798
796
  function generateFullIAMPolicy() {
799
- const fullPolicyPath = path.join(
800
- __dirname,
801
- 'templates/iam-policy-full.json'
802
- );
797
+ const fullPolicyPath = path.join(__dirname, 'templates/iam-policy-full.json');
803
798
  return require(fullPolicyPath);
804
799
  }
805
800