@friggframework/devtools 2.0.0--canary.553.7a28e41.0 → 2.0.0--canary.553.4228f35.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.
@@ -145,20 +145,20 @@ class IntegrationBuilder extends InfrastructureBuilder {
145
145
  * Build integration resources based on ownership decisions
146
146
  */
147
147
  async buildFromDecisions(decisions, appDefinition, result, usePrismaLayer = true) {
148
+ // Create package config first — needed by all Lambda functions including DLQ processor
149
+ const functionPackageConfig = this.createFunctionPackageConfig(usePrismaLayer);
150
+
148
151
  // Create InternalErrorQueue if ownership = STACK
149
152
  const shouldCreateInternalErrorQueue = decisions.internalErrorQueue.ownership === ResourceOwnership.STACK;
150
153
 
151
154
  if (shouldCreateInternalErrorQueue) {
152
155
  console.log(' → Creating InternalErrorQueue in stack');
153
- this.createInternalErrorQueue(result);
156
+ this.createInternalErrorQueue(result, functionPackageConfig);
154
157
  } else {
155
158
  console.log(' → Using external InternalErrorQueue');
156
- this.useExternalInternalErrorQueue(decisions.internalErrorQueue, result);
159
+ this.useExternalInternalErrorQueue(decisions.internalErrorQueue, result, functionPackageConfig);
157
160
  }
158
161
 
159
- // Create Lambda function definitions and queue resources for each integration
160
- const functionPackageConfig = this.createFunctionPackageConfig(usePrismaLayer);
161
-
162
162
  for (const integration of appDefinition.integrations) {
163
163
  const integrationName = integration.Definition.name;
164
164
  const queueDecision = decisions.integrations[integrationName].queue;
@@ -328,16 +328,46 @@ class IntegrationBuilder extends InfrastructureBuilder {
328
328
  /**
329
329
  * Create InternalErrorQueue CloudFormation resource
330
330
  */
331
- createInternalErrorQueue(result) {
331
+ createInternalErrorQueue(result, functionPackageConfig) {
332
332
  result.resources.InternalErrorQueue = {
333
333
  Type: 'AWS::SQS::Queue',
334
334
  Properties: {
335
335
  QueueName: '${self:service}-${self:provider.stage}-InternalErrorQueue',
336
336
  MessageRetentionPeriod: 1209600, // 14 days
337
- VisibilityTimeout: 60, // Must be >= DLQ processor Lambda timeout
337
+ VisibilityTimeout: 60, // Must be >= DLQ processor Lambda timeout (30s)
338
338
  },
339
339
  };
340
340
 
341
+ this.createDLQObservability(result, functionPackageConfig, {
342
+ 'Fn::GetAtt': ['InternalErrorQueue', 'Arn'],
343
+ }, {
344
+ 'Fn::GetAtt': ['InternalErrorQueue', 'QueueName'],
345
+ });
346
+
347
+ console.log(' ✓ Created InternalErrorQueue resource');
348
+ }
349
+
350
+ /**
351
+ * Use external InternalErrorQueue
352
+ */
353
+ useExternalInternalErrorQueue(decision, result, functionPackageConfig) {
354
+ // Add ARN to environment for Lambda functions
355
+ result.environment.INTERNAL_ERROR_QUEUE_ARN = decision.physicalId;
356
+
357
+ // Extract queue name from ARN for CloudWatch dimensions
358
+ const arnParts = decision.physicalId.split(':');
359
+ const queueName = arnParts[arnParts.length - 1];
360
+
361
+ this.createDLQObservability(result, functionPackageConfig, decision.physicalId, queueName);
362
+
363
+ console.log(` ✓ Using external InternalErrorQueue: ${decision.physicalId}`);
364
+ }
365
+
366
+ /**
367
+ * Create DLQ observability resources (alarm + processor Lambda).
368
+ * Called for both stack-owned and external InternalErrorQueues.
369
+ */
370
+ createDLQObservability(result, functionPackageConfig, queueArn, queueName) {
341
371
  // CloudWatch Alarm: fires when any message lands in the DLQ
342
372
  result.resources.DLQMessageAlarm = {
343
373
  Type: 'AWS::CloudWatch::Alarm',
@@ -350,11 +380,9 @@ class IntegrationBuilder extends InfrastructureBuilder {
350
380
  ComparisonOperator: 'GreaterThanThreshold',
351
381
  EvaluationPeriods: 1,
352
382
  Period: 60,
383
+ AlarmActions: [{ Ref: 'InternalErrorBridgeTopic' }],
353
384
  Dimensions: [
354
- {
355
- Name: 'QueueName',
356
- Value: { 'Fn::GetAtt': ['InternalErrorQueue', 'QueueName'] },
357
- },
385
+ { Name: 'QueueName', Value: queueName },
358
386
  ],
359
387
  },
360
388
  };
@@ -362,33 +390,25 @@ class IntegrationBuilder extends InfrastructureBuilder {
362
390
  // DLQ processor Lambda: logs failed messages with structured context
363
391
  result.functions.dlqProcessor = {
364
392
  handler: 'node_modules/@friggframework/core/handlers/workers/dlq-processor.dlqProcessor',
393
+ skipEsbuild: true,
394
+ package: functionPackageConfig,
365
395
  reservedConcurrency: 1,
366
396
  timeout: 30,
367
397
  events: [
368
398
  {
369
399
  sqs: {
370
- arn: { 'Fn::GetAtt': ['InternalErrorQueue', 'Arn'] },
400
+ arn: queueArn,
371
401
  batchSize: 10,
402
+ functionResponseType: 'ReportBatchItemFailures',
372
403
  },
373
404
  },
374
405
  ],
375
406
  };
376
407
 
377
- console.log(' ✓ Created InternalErrorQueue resource');
378
408
  console.log(' ✓ Created DLQ CloudWatch alarm');
379
409
  console.log(' ✓ Created DLQ processor Lambda');
380
410
  }
381
411
 
382
- /**
383
- * Use external InternalErrorQueue
384
- */
385
- useExternalInternalErrorQueue(decision, result) {
386
- // Add ARN to environment for Lambda functions
387
- result.environment.INTERNAL_ERROR_QUEUE_ARN = decision.physicalId;
388
-
389
- console.log(` ✓ Using external InternalErrorQueue: ${decision.physicalId}`);
390
- }
391
-
392
412
  /**
393
413
  * Create integration-specific SQS queue CloudFormation resource
394
414
  */
@@ -412,6 +412,18 @@ describe('IntegrationBuilder', () => {
412
412
  expect(result.resources.DLQMessageAlarm.Properties.Threshold).toBe(0);
413
413
  });
414
414
 
415
+ it('should wire alarm to InternalErrorBridgeTopic for notifications', async () => {
416
+ const appDefinition = {
417
+ integrations: [{ Definition: { name: 'test' } }],
418
+ };
419
+
420
+ const result = await integrationBuilder.build(appDefinition, {});
421
+
422
+ expect(result.resources.DLQMessageAlarm.Properties.AlarmActions).toEqual([
423
+ { Ref: 'InternalErrorBridgeTopic' },
424
+ ]);
425
+ });
426
+
415
427
  it('should create a DLQ processor Lambda triggered by InternalErrorQueue', async () => {
416
428
  const appDefinition = {
417
429
  integrations: [{ Definition: { name: 'test' } }],
@@ -420,19 +432,21 @@ describe('IntegrationBuilder', () => {
420
432
  const result = await integrationBuilder.build(appDefinition, {});
421
433
 
422
434
  expect(result.functions.dlqProcessor).toBeDefined();
423
- expect(result.functions.dlqProcessor.events[0].sqs).toBeDefined();
424
435
  expect(result.functions.dlqProcessor.events[0].sqs.arn).toEqual({
425
436
  'Fn::GetAtt': ['InternalErrorQueue', 'Arn'],
426
437
  });
438
+ expect(result.functions.dlqProcessor.events[0].sqs.functionResponseType).toBe('ReportBatchItemFailures');
427
439
  });
428
440
 
429
- it('DLQ processor should have short timeout and low concurrency', async () => {
441
+ it('DLQ processor should have skipEsbuild, short timeout, and low concurrency', async () => {
430
442
  const appDefinition = {
431
443
  integrations: [{ Definition: { name: 'test' } }],
432
444
  };
433
445
 
434
446
  const result = await integrationBuilder.build(appDefinition, {});
435
447
 
448
+ expect(result.functions.dlqProcessor.skipEsbuild).toBe(true);
449
+ expect(result.functions.dlqProcessor.package).toBeDefined();
436
450
  expect(result.functions.dlqProcessor.timeout).toBeLessThanOrEqual(60);
437
451
  expect(result.functions.dlqProcessor.reservedConcurrency).toBe(1);
438
452
  });
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.553.7a28e41.0",
4
+ "version": "2.0.0--canary.553.4228f35.0",
5
5
  "bin": {
6
6
  "frigg": "./frigg-cli/index.js"
7
7
  },
@@ -25,9 +25,9 @@
25
25
  "@babel/eslint-parser": "^7.18.9",
26
26
  "@babel/parser": "^7.25.3",
27
27
  "@babel/traverse": "^7.25.3",
28
- "@friggframework/core": "2.0.0--canary.553.7a28e41.0",
29
- "@friggframework/schemas": "2.0.0--canary.553.7a28e41.0",
30
- "@friggframework/test": "2.0.0--canary.553.7a28e41.0",
28
+ "@friggframework/core": "2.0.0--canary.553.4228f35.0",
29
+ "@friggframework/schemas": "2.0.0--canary.553.4228f35.0",
30
+ "@friggframework/test": "2.0.0--canary.553.4228f35.0",
31
31
  "@hapi/boom": "^10.0.1",
32
32
  "@inquirer/prompts": "^5.3.8",
33
33
  "axios": "^1.7.2",
@@ -55,8 +55,8 @@
55
55
  "validate-npm-package-name": "^5.0.0"
56
56
  },
57
57
  "devDependencies": {
58
- "@friggframework/eslint-config": "2.0.0--canary.553.7a28e41.0",
59
- "@friggframework/prettier-config": "2.0.0--canary.553.7a28e41.0",
58
+ "@friggframework/eslint-config": "2.0.0--canary.553.4228f35.0",
59
+ "@friggframework/prettier-config": "2.0.0--canary.553.4228f35.0",
60
60
  "aws-sdk-client-mock": "^4.1.0",
61
61
  "aws-sdk-client-mock-jest": "^4.1.0",
62
62
  "jest": "^30.1.3",
@@ -88,5 +88,5 @@
88
88
  "publishConfig": {
89
89
  "access": "public"
90
90
  },
91
- "gitHead": "7a28e41ce8d5620e78b80c4db24ebd32a626da0e"
91
+ "gitHead": "4228f350b711214a27e5d3229b2099243282035b"
92
92
  }