construct-hub 0.3.313 → 0.3.316

Sign up to get free protection for your applications and to get access to all the features.
Files changed (48) hide show
  1. package/.gitattributes +1 -0
  2. package/.jsii +199 -93
  3. package/API.md +86 -0
  4. package/changelog.md +2 -2
  5. package/lib/api.d.ts +1 -0
  6. package/lib/api.js +2 -1
  7. package/lib/backend/catalog-builder/index.d.ts +5 -0
  8. package/lib/backend/catalog-builder/index.js +2 -1
  9. package/lib/backend/deny-list/deny-list.d.ts +2 -0
  10. package/lib/backend/deny-list/deny-list.js +2 -1
  11. package/lib/backend/deny-list/prune.d.ts +5 -0
  12. package/lib/backend/deny-list/prune.js +3 -1
  13. package/lib/backend/ingestion/index.d.ts +2 -0
  14. package/lib/backend/ingestion/index.js +3 -1
  15. package/lib/backend/inventory/index.d.ts +5 -0
  16. package/lib/backend/inventory/index.js +3 -1
  17. package/lib/backend/orchestration/index.d.ts +5 -0
  18. package/lib/backend/orchestration/index.js +3 -1
  19. package/lib/construct-hub.js +13 -3
  20. package/lib/monitoring/index.js +1 -1
  21. package/lib/overview-dashboard/api.d.ts +21 -0
  22. package/lib/overview-dashboard/api.js +3 -0
  23. package/lib/overview-dashboard/index.d.ts +53 -0
  24. package/lib/overview-dashboard/index.js +168 -0
  25. package/lib/overview-dashboard/sqs-dlq-stats-widget-function.d.ts +7 -0
  26. package/lib/overview-dashboard/sqs-dlq-stats-widget-function.js +21 -0
  27. package/lib/overview-dashboard/sqs-dlq-stats-widget-function.lambda.bundle/index.js +181 -0
  28. package/lib/overview-dashboard/sqs-dlq-stats-widget-function.lambda.bundle/index.js.map +7 -0
  29. package/lib/overview-dashboard/sqs-dlq-stats-widget-function.lambda.d.ts +56 -0
  30. package/lib/overview-dashboard/sqs-dlq-stats-widget-function.lambda.js +147 -0
  31. package/lib/overview-dashboard/sqs-dlq-widget.d.ts +59 -0
  32. package/lib/overview-dashboard/sqs-dlq-widget.js +82 -0
  33. package/lib/package-source.d.ts +5 -0
  34. package/lib/package-source.js +1 -1
  35. package/lib/package-sources/code-artifact.d.ts +1 -1
  36. package/lib/package-sources/code-artifact.js +5 -3
  37. package/lib/package-sources/npmjs.d.ts +1 -1
  38. package/lib/package-sources/npmjs.js +7 -3
  39. package/lib/package-tag/index.js +3 -3
  40. package/lib/package-tag-group/index.js +2 -2
  41. package/lib/preload-file/index.js +1 -1
  42. package/lib/s3/storage.js +1 -1
  43. package/lib/spdx-license.js +1 -1
  44. package/lib/webapp/index.d.ts +5 -0
  45. package/lib/webapp/index.js +2 -1
  46. package/package.json +3 -1
  47. package/releasetag.txt +1 -1
  48. package/version.txt +1 -1
@@ -0,0 +1,21 @@
1
+ import { IFunction } from '@aws-cdk/aws-lambda';
2
+ import { IQueue } from '@aws-cdk/aws-sqs';
3
+ /**
4
+ * ConstructHub overview dashboard exposed to extension points.
5
+ */
6
+ export interface IOverviewDashboard {
7
+ /**
8
+ * Adds widgets to overview dashboard with link to the dashboard and number of visible messages.
9
+ * @param name of the DLQ that will be used in the dashboard
10
+ * @param deadLetterQueue Dead Letter Queue to be used in the dashboard
11
+ * @param reDriveFunction a lambda function that will be used to re-drive the DLQ
12
+ */
13
+ addDLQMetricToDashboard(name: string, deadLetterQueue: IQueue, reDriveFunction?: IFunction): void;
14
+ /**
15
+ * Adds a metric widget to the overview dashboard showing the total number concurrent executions
16
+ * of a Lambda function and the percentage of SERVICE_QUOTA utilized by the function. This can be
17
+ * used to see which function has the most impact of the service quota.
18
+ * @param fn Lambda function to be monitored
19
+ */
20
+ addConcurrentExecutionMetricToDashboard(fn: IFunction, name?: string): void;
21
+ }
@@ -0,0 +1,3 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYXBpLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL292ZXJ2aWV3LWRhc2hib2FyZC9hcGkudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IiIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7IElGdW5jdGlvbiB9IGZyb20gJ0Bhd3MtY2RrL2F3cy1sYW1iZGEnO1xuaW1wb3J0IHsgSVF1ZXVlIH0gZnJvbSAnQGF3cy1jZGsvYXdzLXNxcyc7XG5cbi8qKlxuICogQ29uc3RydWN0SHViIG92ZXJ2aWV3IGRhc2hib2FyZCBleHBvc2VkIHRvIGV4dGVuc2lvbiBwb2ludHMuXG4gKi9cbmV4cG9ydCBpbnRlcmZhY2UgSU92ZXJ2aWV3RGFzaGJvYXJkIHtcbiAgLyoqXG4gICAqIEFkZHMgd2lkZ2V0cyB0byBvdmVydmlldyBkYXNoYm9hcmQgd2l0aCBsaW5rIHRvIHRoZSBkYXNoYm9hcmQgYW5kIG51bWJlciBvZiB2aXNpYmxlIG1lc3NhZ2VzLlxuICAgKiBAcGFyYW0gbmFtZSBvZiB0aGUgRExRIHRoYXQgd2lsbCBiZSB1c2VkIGluIHRoZSBkYXNoYm9hcmRcbiAgICogQHBhcmFtIGRlYWRMZXR0ZXJRdWV1ZSBEZWFkIExldHRlciBRdWV1ZSB0byBiZSB1c2VkIGluIHRoZSBkYXNoYm9hcmRcbiAgICogQHBhcmFtIHJlRHJpdmVGdW5jdGlvbiBhIGxhbWJkYSBmdW5jdGlvbiB0aGF0IHdpbGwgYmUgdXNlZCB0byByZS1kcml2ZSB0aGUgRExRXG4gICAqL1xuICBhZGRETFFNZXRyaWNUb0Rhc2hib2FyZChuYW1lOiBzdHJpbmcsIGRlYWRMZXR0ZXJRdWV1ZTogSVF1ZXVlLCByZURyaXZlRnVuY3Rpb24/OiBJRnVuY3Rpb24pOiB2b2lkO1xuXG4gIC8qKlxuICAgKiBBZGRzIGEgbWV0cmljIHdpZGdldCB0byB0aGUgb3ZlcnZpZXcgZGFzaGJvYXJkIHNob3dpbmcgdGhlIHRvdGFsIG51bWJlciBjb25jdXJyZW50IGV4ZWN1dGlvbnNcbiAgICogb2YgYSBMYW1iZGEgZnVuY3Rpb24gYW5kIHRoZSBwZXJjZW50YWdlIG9mIFNFUlZJQ0VfUVVPVEEgdXRpbGl6ZWQgYnkgdGhlIGZ1bmN0aW9uLiBUaGlzIGNhbiBiZVxuICAgKiB1c2VkIHRvIHNlZSB3aGljaCBmdW5jdGlvbiBoYXMgdGhlIG1vc3QgaW1wYWN0IG9mIHRoZSBzZXJ2aWNlIHF1b3RhLlxuICAgKiBAcGFyYW0gZm4gTGFtYmRhIGZ1bmN0aW9uIHRvIGJlIG1vbml0b3JlZFxuICAgKi9cbiAgYWRkQ29uY3VycmVudEV4ZWN1dGlvbk1ldHJpY1RvRGFzaGJvYXJkKGZuOiBJRnVuY3Rpb24sIG5hbWU/OiBzdHJpbmcpOiB2b2lkO1xuXG59XG4iXX0=
@@ -0,0 +1,53 @@
1
+ import { IDistribution } from '@aws-cdk/aws-cloudfront';
2
+ import { IFunction } from '@aws-cdk/aws-lambda';
3
+ import { IQueue } from '@aws-cdk/aws-sqs';
4
+ import { Construct } from '@aws-cdk/core';
5
+ import { Inventory } from '../backend/inventory';
6
+ import { IOverviewDashboard } from './api';
7
+ /**
8
+ * Properties for OverviewDashboard
9
+ */
10
+ export interface OverviewDashboardProps {
11
+ readonly lambdaServiceAlarmThreshold?: number;
12
+ readonly dashboardName?: string;
13
+ }
14
+ /**
15
+ * Construct hub overview
16
+ *
17
+ * This construct generates a dashboard to provide Overview of the Construct Hub operation
18
+ * The dashboard includes details like DLQ, CloudFront errors and Lambda Service Quota
19
+ *
20
+ * Components should use the APIs of this module to add widgets to overview dashboard
21
+ */
22
+ export declare class OverviewDashboard extends Construct implements IOverviewDashboard {
23
+ private static mLambdaUsage;
24
+ private static mLambdaQuota;
25
+ private readonly dashboard;
26
+ private queueMetricWidget?;
27
+ private cloudFrontMetricWidget?;
28
+ private readonly lambdaServiceLimitGraph;
29
+ private readonly lambdaServiceAlarmThreshold;
30
+ private metricCount;
31
+ constructor(scope: Construct, id: string, props?: OverviewDashboardProps);
32
+ /**
33
+ * Adds a metric widget to the Overview dashboard showing the total number concurrent executions
34
+ * of a Lambda function and the percentage of SERVICE_QUOTA utilized by the function. This can be
35
+ * used to see which function has the most impact of the service quota.
36
+ * @param fn Lambda function to be monitored
37
+ */
38
+ addConcurrentExecutionMetricToDashboard(fn: IFunction, name?: string): void;
39
+ /**
40
+ * Adds widgets to Overview dashboard with link to the dashboard and number of visible messages.
41
+ * @param name of the DLQ that will be used in the dashboard
42
+ * @param deadLetterQueue Dead Letter Queue to be used in the dashboard
43
+ * @param reDriveFunction a lambda function that will be used to re-drive the DLQ
44
+ */
45
+ addDLQMetricToDashboard(name: string, deadLetterQueue: IQueue, reDriveFunction?: IFunction): void;
46
+ /**
47
+ * Adds a widget to the Overview dashboard showing the number of requests to CloudFront
48
+ */
49
+ addDistributionMetricToDashboard(distribution: IDistribution): void;
50
+ private addCloudFrontMetricWidget;
51
+ private addLambdaServiceQuotaWidgetToDashboard;
52
+ addInventoryMetrics(inventory: Inventory): void;
53
+ }
@@ -0,0 +1,168 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.OverviewDashboard = void 0;
4
+ const aws_cloudwatch_1 = require("@aws-cdk/aws-cloudwatch");
5
+ const core_1 = require("@aws-cdk/core");
6
+ const runbook_url_1 = require("../runbook-url");
7
+ const sqs_dlq_widget_1 = require("./sqs-dlq-widget");
8
+ /**
9
+ * Construct hub overview
10
+ *
11
+ * This construct generates a dashboard to provide Overview of the Construct Hub operation
12
+ * The dashboard includes details like DLQ, CloudFront errors and Lambda Service Quota
13
+ *
14
+ * Components should use the APIs of this module to add widgets to overview dashboard
15
+ */
16
+ class OverviewDashboard extends core_1.Construct {
17
+ constructor(scope, id, props) {
18
+ var _a;
19
+ super(scope, id);
20
+ this.metricCount = 1;
21
+ this.lambdaServiceAlarmThreshold = (_a = props === null || props === void 0 ? void 0 : props.lambdaServiceAlarmThreshold) !== null && _a !== void 0 ? _a : 70;
22
+ this.dashboard = new aws_cloudwatch_1.Dashboard(this, 'Overview dashboard', { dashboardName: props === null || props === void 0 ? void 0 : props.dashboardName });
23
+ this.lambdaServiceLimitGraph = this.addLambdaServiceQuotaWidgetToDashboard();
24
+ this.dashboard.addWidgets(this.lambdaServiceLimitGraph);
25
+ }
26
+ /**
27
+ * Adds a metric widget to the Overview dashboard showing the total number concurrent executions
28
+ * of a Lambda function and the percentage of SERVICE_QUOTA utilized by the function. This can be
29
+ * used to see which function has the most impact of the service quota.
30
+ * @param fn Lambda function to be monitored
31
+ */
32
+ addConcurrentExecutionMetricToDashboard(fn, name) {
33
+ const metricName = `m${this.metricCount++}`;
34
+ const invocationCount = fn.metricInvocations({
35
+ statistic: aws_cloudwatch_1.Statistic.MAXIMUM,
36
+ label: name !== null && name !== void 0 ? name : `${fn.functionName}`,
37
+ });
38
+ this.lambdaServiceLimitGraph.addRightMetric(new aws_cloudwatch_1.MathExpression({
39
+ expression: `${metricName} / mLambdaQuota * 100`,
40
+ usingMetrics: { [metricName]: invocationCount, lambdaQuotaLimit: OverviewDashboard.mLambdaQuota },
41
+ label: `${name !== null && name !== void 0 ? name : fn.functionName} quota usage %`,
42
+ }));
43
+ }
44
+ /**
45
+ * Adds widgets to Overview dashboard with link to the dashboard and number of visible messages.
46
+ * @param name of the DLQ that will be used in the dashboard
47
+ * @param deadLetterQueue Dead Letter Queue to be used in the dashboard
48
+ * @param reDriveFunction a lambda function that will be used to re-drive the DLQ
49
+ */
50
+ addDLQMetricToDashboard(name, deadLetterQueue, reDriveFunction) {
51
+ if (!this.queueMetricWidget) {
52
+ this.queueMetricWidget = this.queueMetricWidget = new sqs_dlq_widget_1.SQSDLQWidget(this, 'QueueMetricWidget', {
53
+ queues: [],
54
+ key: 'QueueMetricWidget',
55
+ });
56
+ this.dashboard.addWidgets(this.queueMetricWidget);
57
+ }
58
+ this.queueMetricWidget.addQueue(name, deadLetterQueue, reDriveFunction);
59
+ }
60
+ /**
61
+ * Adds a widget to the Overview dashboard showing the number of requests to CloudFront
62
+ */
63
+ addDistributionMetricToDashboard(distribution) {
64
+ if (!this.cloudFrontMetricWidget) {
65
+ this.cloudFrontMetricWidget = this.addCloudFrontMetricWidget();
66
+ this.dashboard.addWidgets(this.cloudFrontMetricWidget);
67
+ }
68
+ const totalRequest = new aws_cloudwatch_1.Metric({
69
+ metricName: 'Requests',
70
+ namespace: 'AWS/CloudFront',
71
+ statistic: aws_cloudwatch_1.Statistic.AVERAGE,
72
+ dimensionsMap: {
73
+ DistributionId: distribution.distributionId,
74
+ Region: 'Global',
75
+ },
76
+ region: 'us-east-1',
77
+ });
78
+ const errorRate4xx = new aws_cloudwatch_1.Metric({
79
+ metricName: '4xxErrorRate',
80
+ namespace: 'AWS/CloudFront',
81
+ statistic: aws_cloudwatch_1.Statistic.AVERAGE,
82
+ dimensionsMap: {
83
+ DistributionId: distribution.distributionId,
84
+ Region: 'Global',
85
+ },
86
+ region: 'us-east-1',
87
+ });
88
+ const errorRate5xx = new aws_cloudwatch_1.Metric({
89
+ metricName: '5xxErrorRate',
90
+ namespace: 'AWS/CloudFront',
91
+ statistic: aws_cloudwatch_1.Statistic.AVERAGE,
92
+ dimensionsMap: {
93
+ DistributionId: distribution.distributionId,
94
+ Region: 'Global',
95
+ },
96
+ region: 'us-east-1',
97
+ });
98
+ this.cloudFrontMetricWidget.addLeftMetric(totalRequest);
99
+ this.cloudFrontMetricWidget.addRightMetric(errorRate4xx);
100
+ this.cloudFrontMetricWidget.addRightMetric(errorRate5xx);
101
+ }
102
+ addCloudFrontMetricWidget() {
103
+ const widget = new aws_cloudwatch_1.GraphWidget({
104
+ title: 'CloudFront Metrics',
105
+ height: 8,
106
+ width: 12,
107
+ leftYAxis: { label: 'Requests count' },
108
+ rightYAxis: { label: 'Request Percent', min: 0, max: 100 },
109
+ });
110
+ return widget;
111
+ }
112
+ addLambdaServiceQuotaWidgetToDashboard() {
113
+ const serviceQuotaLimit = new aws_cloudwatch_1.MathExpression({
114
+ expression: 'mLambdaUsage / mLambdaQuota * 100',
115
+ label: 'Concurrent executions quota usage %',
116
+ usingMetrics: { mLambdaUsage: OverviewDashboard.mLambdaUsage, mLambdaQuota: OverviewDashboard.mLambdaQuota },
117
+ });
118
+ const alarm = serviceQuotaLimit.createAlarm(this, 'OverviewDashboard/lambdaServiceQuota', {
119
+ alarmDescription: [
120
+ `Lambda concurrent execution exceeded ${this.lambdaServiceAlarmThreshold}% of SERVICE_QUOTA`,
121
+ '',
122
+ `RunBook: ${runbook_url_1.RUNBOOK_URL}`,
123
+ '',
124
+ 'Request a service quota increase for lambda functions',
125
+ ].join('\n'),
126
+ comparisonOperator: aws_cloudwatch_1.ComparisonOperator.GREATER_THAN_THRESHOLD,
127
+ evaluationPeriods: 5,
128
+ threshold: this.lambdaServiceAlarmThreshold,
129
+ treatMissingData: aws_cloudwatch_1.TreatMissingData.MISSING,
130
+ });
131
+ const lambdaServiceQuotaWidget = new aws_cloudwatch_1.GraphWidget({
132
+ title: 'Lambda concurrent execution quota',
133
+ height: 8,
134
+ width: 12,
135
+ right: [
136
+ alarm.metric,
137
+ ],
138
+ rightYAxis: { label: 'Quota Percent', min: 0, max: 100 },
139
+ rightAnnotations: [{
140
+ value: this.lambdaServiceAlarmThreshold,
141
+ }],
142
+ });
143
+ return lambdaServiceQuotaWidget;
144
+ }
145
+ addInventoryMetrics(inventory) {
146
+ this.dashboard.addWidgets(new aws_cloudwatch_1.GraphWidget({
147
+ title: 'Construct Hub Inventory',
148
+ height: 8,
149
+ width: 12,
150
+ left: [
151
+ inventory.metricPackageCount(),
152
+ inventory.metricPackageMajorCount(),
153
+ ],
154
+ leftYAxis: { label: 'Package count' },
155
+ }));
156
+ }
157
+ }
158
+ exports.OverviewDashboard = OverviewDashboard;
159
+ OverviewDashboard.mLambdaUsage = new aws_cloudwatch_1.Metric({
160
+ metricName: 'ConcurrentExecutions',
161
+ namespace: 'AWS/Lambda',
162
+ statistic: aws_cloudwatch_1.Statistic.MAXIMUM,
163
+ });
164
+ OverviewDashboard.mLambdaQuota = new aws_cloudwatch_1.MathExpression({
165
+ expression: 'SERVICE_QUOTA(mLambdaUsage)',
166
+ usingMetrics: { mLambdaUsage: OverviewDashboard.mLambdaUsage },
167
+ });
168
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvb3ZlcnZpZXctZGFzaGJvYXJkL2luZGV4LnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7OztBQUNBLDREQUEwSTtBQUcxSSx3Q0FBMEM7QUFFMUMsZ0RBQTZDO0FBRTdDLHFEQUFnRDtBQVVoRDs7Ozs7OztHQU9HO0FBQ0gsTUFBYSxpQkFBa0IsU0FBUSxnQkFBUztJQXlCOUMsWUFBWSxLQUFnQixFQUFFLEVBQVUsRUFBRSxLQUE4Qjs7UUFDdEUsS0FBSyxDQUFDLEtBQUssRUFBRSxFQUFFLENBQUMsQ0FBQztRQUhYLGdCQUFXLEdBQVcsQ0FBQyxDQUFDO1FBSTlCLElBQUksQ0FBQywyQkFBMkIsU0FBRyxLQUFLLGFBQUwsS0FBSyx1QkFBTCxLQUFLLENBQUUsMkJBQTJCLG1DQUFJLEVBQUUsQ0FBQztRQUM1RSxJQUFJLENBQUMsU0FBUyxHQUFHLElBQUksMEJBQVMsQ0FBQyxJQUFJLEVBQUUsb0JBQW9CLEVBQUUsRUFBRSxhQUFhLEVBQUUsS0FBSyxhQUFMLEtBQUssdUJBQUwsS0FBSyxDQUFFLGFBQWEsRUFBRSxDQUFDLENBQUM7UUFDcEcsSUFBSSxDQUFDLHVCQUF1QixHQUFHLElBQUksQ0FBQyxzQ0FBc0MsRUFBRSxDQUFDO1FBQzdFLElBQUksQ0FBQyxTQUFTLENBQUMsVUFBVSxDQUFDLElBQUksQ0FBQyx1QkFBdUIsQ0FBQyxDQUFDO0lBQzFELENBQUM7SUFFRDs7Ozs7T0FLRztJQUVJLHVDQUF1QyxDQUFDLEVBQWEsRUFBRSxJQUFhO1FBQ3pFLE1BQU0sVUFBVSxHQUFHLElBQUksSUFBSSxDQUFDLFdBQVcsRUFBRSxFQUFFLENBQUM7UUFDNUMsTUFBTSxlQUFlLEdBQUcsRUFBRSxDQUFDLGlCQUFpQixDQUFDO1lBQzNDLFNBQVMsRUFBRSwwQkFBUyxDQUFDLE9BQU87WUFDNUIsS0FBSyxFQUFFLElBQUksYUFBSixJQUFJLGNBQUosSUFBSSxHQUFJLEdBQUcsRUFB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SyxDQUFDLE1BQU07YUFDYjtZQUNELFVBQVUsRUFBRSxFQUFFLEtBQUssRUFBRSxlQUFlLEVBQUUsR0FBRyxFQUFFLENBQUMsRUFBRSxHQUFHLEVBQUUsR0FBRyxFQUFFO1lBQ3hELGdCQUFnQixFQUFFLENBQUM7b0JBQ2pCLEtBQUssRUFBRSxJQUFJLENBQUMsMkJBQTJCO2lCQUN4QyxDQUFDO1NBQ0gsQ0FBQyxDQUFDO1FBRUgsT0FBTyx3QkFBd0IsQ0FBQztJQUNsQyxDQUFDO0lBRU0sbUJBQW1CLENBQUMsU0FBb0I7UUFDN0MsSUFBSSxDQUFDLFNBQVMsQ0FBQyxVQUFVLENBQUMsSUFBSSw0QkFBVyxDQUFDO1lBQ3hDLEtBQUssRUFBRSx5QkFBeUI7WUFDaEMsTUFBTSxFQUFFLENBQUM7WUFDVCxLQUFLLEVBQUUsRUFBRTtZQUNULElBQUksRUFBRTtnQkFDSixTQUFTLENBQUMsa0JBQWtCLEVBQUU7Z0JBQzlCLFNBQVMsQ0FBQyx1QkFBdUIsRUFBRTthQUNwQztZQUNELFNBQVMsRUFBRSxFQUFFLEtBQUssRUFBRSxlQUFlLEVBQUU7U0FDdEMsQ0FBQyxDQUFDLENBQUM7SUFDTixDQUFDOztBQWpMSCw4Q0FrTEM7QUFqTGdCLDhCQUFZLEdBQVcsSUFBSSx1QkFBTSxDQUFDO0lBQy9DLFVBQVUsRUFBRSxzQkFBc0I7SUFDbEMsU0FBUyxFQUFFLFlBQVk7SUFDdkIsU0FBUyxFQUFFLDBCQUFTLENBQUMsT0FBTztDQUM3QixDQUFDLENBQUM7QUFFWSw4QkFBWSxHQUFtQixJQUFJLCtCQUFjLENBQUM7SUFDL0QsVUFBVSxFQUFFLDZCQUE2QjtJQUN6QyxZQUFZLEVBQUUsRUFBRSxZQUFZLEVBQUUsaUJBQWlCLENBQUMsWUFBWSxFQUFFO0NBQy9ELENBQUMsQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7IElEaXN0cmlidXRpb24gfSBmcm9tICdAYXdzLWNkay9hd3MtY2xvdWRmcm9udCc7XG5pbXBvcnQgeyBDb21wYXJpc29uT3BlcmF0b3IsIERhc2hib2FyZCwgR3JhcGhXaWRnZXQsIE1hdGhFeHByZXNzaW9uLCBNZXRyaWMsIFN0YXRpc3RpYywgVHJlYXRNaXNzaW5nRGF0YSB9IGZyb20gJ0Bhd3MtY2RrL2F3cy1jbG91ZHdhdGNoJztcbmltcG9ydCB7IElGdW5jdGlvbiB9IGZyb20gJ0Bhd3MtY2RrL2F3cy1sYW1iZGEnO1xuaW1wb3J0IHsgSVF1ZXVlIH0gZnJvbSAnQGF3cy1jZGsvYXdzLXNxcyc7XG5pbXBvcnQgeyBDb25zdHJ1Y3QgfSBmcm9tICdAYXdzLWNkay9jb3JlJztcbmltcG9ydCB7IEludmVudG9yeSB9IGZyb20gJy4uL2JhY2tlbmQvaW52ZW50b3J5JztcbmltcG9ydCB7IFJVTkJPT0tfVVJMIH0gZnJvbSAnLi4vcnVuYm9vay11cmwnO1xuaW1wb3J0IHsgSU92ZXJ2aWV3RGFzaGJvYXJkIH0gZnJvbSAnLi9hcGknO1xuaW1wb3J0IHsgU1FTRExRV2lkZ2V0IH0gZnJvbSAnLi9zcXMtZGxxLXdpZGdldCc7XG5cbi8qKlxuICogUHJvcGVydGllcyBmb3IgT3ZlcnZpZXdEYXNoYm9hcmRcbiAqL1xuZXhwb3J0IGludGVyZmFjZSBPdmVydmlld0Rhc2hib2FyZFByb3BzIHtcbiAgcmVhZG9ubHkgbGFtYmRhU2VydmljZUFsYXJtVGhyZXNob2xkPzogbnVtYmVyO1xuICByZWFkb25seSBkYXNoYm9hcmROYW1lPzogc3RyaW5nO1xufVxuXG4vKipcbiAqIENvbnN0cnVjdCBodWIgb3ZlcnZpZXdcbiAqXG4gKiBUaGlzIGNvbnN0cnVjdCBnZW5lcmF0ZXMgYSBkYXNoYm9hcmQgdG8gcHJvdmlkZSBPdmVydmlldyBvZiB0aGUgQ29uc3RydWN0IEh1YiBvcGVyYXRpb25cbiAqIFRoZSBkYXNoYm9hcmQgaW5jbHVkZXMgZGV0YWlscyBsaWtlIERMUSwgQ2xvdWRGcm9udCBlcnJvcnMgYW5kIExhbWJkYSBTZXJ2aWNlIFF1b3RhXG4gKlxuICogQ29tcG9uZW50cyBzaG91bGQgdXNlIHRoZSBBUElzIG9mIHRoaXMgbW9kdWxlIHRvIGFkZCB3aWRnZXRzIHRvIG92ZXJ2aWV3IGRhc2hib2FyZFxuICovXG5leHBvcnQgY2xhc3MgT3ZlcnZpZXdEYXNoYm9hcmQgZXh0ZW5kcyBDb25zdHJ1Y3QgaW1wbGVtZW50cyBJT3ZlcnZpZXdEYXNoYm9hcmQge1xuICBwcml2YXRlIHN0YXRpYyBtTGFtYmRhVXNhZ2U6IE1ldHJpYyA9IG5ldyBNZXRyaWMoe1xuICAgIG1ldHJpY05hbWU6ICdDb25jdXJyZW50RXhlY3V0aW9ucycsXG4gICAgbmFtZXNwYWNlOiAnQVdTL0xhbWJkYScsXG4gICAgc3RhdGlzdGljOiBTdGF0aXN0aWMuTUFYSU1VTSxcbiAgfSk7XG5cbiAgcHJpdmF0ZSBzdGF0aWMgbUxhbWJkYVF1b3RhOiBNYXRoRXhwcmVzc2lvbiA9IG5ldyBNYXRoRXhwcmVzc2lvbih7XG4gICAgZXhwcmVzc2lvbjogJ1NFUlZJQ0VfUVVPVEEobUxhbWJkYVVzYWdlKScsXG4gICAgdXNpbmdNZXRyaWNzOiB7IG1MYW1iZGFVc2FnZTogT3ZlcnZpZXdEYXNoYm9hcmQubUxhbWJkYVVzYWdlIH0sXG4gIH0pO1xuXG4gIHByaXZhdGUgcmVhZG9ubHkgZGFzaGJvYXJkOiBEYXNoYm9hcmQ7XG5cbiAgcHJpdmF0ZSBxdWV1ZU1ldHJpY1dpZGdldD86IFNRU0RMUVdpZGdldDtcblxuICBwcml2YXRlIGNsb3VkRnJvbnRNZXRyaWNXaWRnZXQ/OiBHcmFwaFdpZGdldDtcblxuICBwcml2YXRlIHJlYWRvbmx5IGxhbWJkYVNlcnZpY2VMaW1pdEdyYXBoOiBHcmFwaFdpZGdldDtcblxuICBwcml2YXRlIHJlYWRvbmx5IGxhbWJkYVNlcnZpY2VBbGFybVRocmVzaG9sZDogbnVtYmVyO1xuXG5cbiAgcHJpdmF0ZSBtZXRyaWNDb3VudDogbnVtYmVyID0gMTtcblxuICBjb25zdHJ1Y3RvcihzY29wZTogQ29uc3RydWN0LCBpZDogc3RyaW5nLCBwcm9wcz86IE92ZXJ2aWV3RGFzaGJvYXJkUHJvcHMpIHtcbiAgICBzdXBlcihzY29wZSwgaWQpO1xuICAgIHRoaXMubGFtYmRhU2VydmljZUFsYXJtVGhyZXNob2xkID0gcHJvcHM/LmxhbWJkYVNlcnZpY2VBbGFybVRocmVzaG9sZCA/PyA3MDtcbiAgICB0aGlzLmRhc2hib2FyZCA9IG5ldyBEYXNoYm9hcmQodGhpcywgJ092ZXJ2aWV3IGRhc2hib2FyZCcsIHsgZGFzaGJvYXJkTmFtZTogcHJvcHM/LmRhc2hib2FyZE5hbWUgfSk7XG4gICAgdGhpcy5sYW1iZGFTZXJ2aWNlTGltaXRHcmFwaCA9IHRoaXMuYWRkTGFtYmRhU2VydmljZVF1b3RhV2lkZ2V0VG9EYXNoYm9hcmQoKTtcbiAgICB0aGlzLmRhc2hib2FyZC5hZGRXaWRnZXRzKHRoaXMubGFtYmRhU2VydmljZUxpbWl0R3JhcGgpO1xuICB9XG5cbiAgLyoqXG4gICAqIEFkZHMgYSBtZXRyaWMgd2lkZ2V0IHRvIHRoZSBPdmVydmlldyBkYXNoYm9hcmQgc2hvd2luZyB0aGUgdG90YWwgbnVtYmVyIGNvbmN1cnJlbnQgZXhlY3V0aW9uc1xuICAgICogb2YgYSBMYW1iZGEgZnVuY3Rpb24gYW5kIHRoZSBwZXJjZW50YWdlIG9mIFNFUlZJQ0VfUVVPVEEgdXRpbGl6ZWQgYnkgdGhlIGZ1bmN0aW9uLiBUaGlzIGNhbiBiZVxuICAgICogdXNlZCB0byBzZWUgd2hpY2ggZnVuY3Rpb24gaGFzIHRoZSBtb3N0IGltcGFjdCBvZiB0aGUgc2VydmljZSBxdW90YS5cbiAgICogQHBhcmFtIGZuIExhbWJkYSBmdW5jdGlvbiB0byBiZSBtb25pdG9yZWRcbiAgICovXG5cbiAgcHVibGljIGFkZENvbmN1cnJlbnRFeGVjdXRpb25NZXRyaWNUb0Rhc2hib2FyZChmbjogSUZ1bmN0aW9uLCBuYW1lPzogc3RyaW5nKSB7XG4gICAgY29uc3QgbWV0cmljTmFtZSA9IGBtJHt0aGlzLm1ldHJpY0NvdW50Kyt9YDtcbiAgICBjb25zdCBpbnZvY2F0aW9uQ291bnQgPSBmbi5tZXRyaWNJbnZvY2F0aW9ucyh7XG4gICAgICBzdGF0aXN0aWM6IFN0YXRpc3RpYy5NQVhJTVVNLFxuICAgICAgbGFiZWw6IG5hbWUgPz8gYCR7Zm4uZnVuY3Rpb25OYW1lfWAsXG4gICAgfSk7XG4gICAgdGhpcy5sYW1iZGFTZXJ2aWNlTGltaXRHcmFwaC5hZGRSaWdodE1ldHJpYyhuZXcgTWF0aEV4cHJlc3Npb24oe1xuICAgICAgZXhwcmVzc2lvbjogYCR7bWV0cmljTmFtZX0gLyBtTGFtYmRhUXVvdGEgKiAxMDBgLFxuICAgICAgdXNpbmdNZXRyaWNzOiB7IFttZXRyaWNOYW1lXTogaW52b2NhdGlvbkNvdW50LCBsYW1iZGFRdW90YUxpbWl0OiBPdmVydmlld0Rhc2hib2FyZC5tTGFtYmRhUXVvdGEgfSxcbiAgICAgIGxhYmVsOiBgJHtuYW1lID8/IGZuLmZ1bmN0aW9uTmFtZX0gcXVvdGEgdXNhZ2UgJWAsXG4gICAgfSkpO1xuICB9XG5cbiAgLyoqXG4gICAqIEFkZHMgd2lkZ2V0cyB0byBPdmVydmlldyBkYXNoYm9hcmQgd2l0aCBsaW5rIHRvIHRoZSBkYXNoYm9hcmQgYW5kIG51bWJlciBvZiB2aXNpYmxlIG1lc3NhZ2VzLlxuICAgKiBAcGFyYW0gbmFtZSBvZiB0aGUgRExRIHRoYXQgd2lsbCBiZSB1c2VkIGluIHRoZSBkYXNoYm9hcmRcbiAgICogQHBhcmFtIGRlYWRMZXR0ZXJRdWV1ZSBEZWFkIExldHRlciBRdWV1ZSB0byBiZSB1c2VkIGluIHRoZSBkYXNoYm9hcmRcbiAgICogQHBhcmFtIHJlRHJpdmVGdW5jdGlvbiBhIGxhbWJkYSBmdW5jdGlvbiB0aGF0IHdpbGwgYmUgdXNlZCB0byByZS1kcml2ZSB0aGUgRExRXG4gICAqL1xuICBwdWJsaWMgYWRkRExRTWV0cmljVG9EYXNoYm9hcmQobmFtZTogc3RyaW5nLCBkZWFkTGV0dGVyUXVldWU6IElRdWV1ZSwgcmVEcml2ZUZ1bmN0aW9uPzogSUZ1bmN0aW9uKSB7XG4gICAgaWYgKCF0aGlzLnF1ZXVlTWV0cmljV2lkZ2V0KSB7XG4gICAgICB0aGlzLnF1ZXVlTWV0cmljV2lkZ2V0ID0gdGhpcy5xdWV1ZU1ldHJpY1dpZGdldCA9IG5ldyBTUVNETFFXaWRnZXQodGhpcywgJ1F1ZXVlTWV0cmljV2lkZ2V0Jywge1xuICAgICAgICBxdWV1ZXM6IFtdLFxuICAgICAgICBrZXk6ICdRdWV1ZU1ldHJpY1dpZGdldCcsXG4gICAgICB9KTtcbiAgICAgIHRoaXMuZGFzaGJvYXJkLmFkZFdpZGdldHModGhpcy5xdWV1ZU1ldHJpY1dpZGdldCk7XG4gICAgfVxuICAgIHRoaXMucXVldWVNZXRyaWNXaWRnZXQuYWRkUXVldWUobmFtZSwgZGVhZExldHRlclF1ZXVlLCByZURyaXZlRnVuY3Rpb24pO1xuICB9XG5cbiAgLyoqXG4gICAqICBBZGRzIGEgd2lkZ2V0IHRvIHRoZSBPdmVydmlldyBkYXNoYm9hcmQgc2hvd2luZyB0aGUgbnVtYmVyIG9mIHJlcXVlc3RzIHRvIENsb3VkRnJvbnRcbiAgICovXG5cbiAgcHVibGljIGFkZERpc3RyaWJ1dGlvbk1ldHJpY1RvRGFzaGJvYXJkKGRpc3RyaWJ1dGlvbjogSURpc3RyaWJ1dGlvbikge1xuICAgIGlmICghdGhpcy5jbG91ZEZyb250TWV0cmljV2lkZ2V0KSB7XG4gICAgICB0aGlzLmNsb3VkRnJvbnRNZXRyaWNXaWRnZXQgPSB0aGlzLmFkZENsb3VkRnJvbnRNZXRyaWNXaWRnZXQoKTtcbiAgICAgIHRoaXMuZGFzaGJvYXJkLmFkZFdpZGdldHModGhpcy5jbG91ZEZyb250TWV0cmljV2lkZ2V0KTtcbiAgICB9XG5cbiAgICBjb25zdCB0b3RhbFJlcXVlc3QgPSBuZXcgTWV0cmljKHtcbiAgICAgIG1ldHJpY05hbWU6ICdSZXF1ZXN0cycsXG4gICAgICBuYW1lc3BhY2U6ICdBV1MvQ2xvdWRGcm9udCcsXG4gICAgICBzdGF0aXN0aWM6IFN0YXRpc3RpYy5BVkVSQUdFLFxuICAgICAgZGltZW5zaW9uc01hcDoge1xuICAgICAgICBEaXN0cmlidXRpb25JZDogZGlzdHJpYnV0aW9uLmRpc3RyaWJ1dGlvbklkLFxuICAgICAgICBSZWdpb246ICdHbG9iYWwnLFxuICAgICAgfSxcbiAgICAgIHJlZ2lvbjogJ3VzLWVhc3QtMScsIC8vIGdsb2JhbCBtZXRyaWNcbiAgICB9KTtcblxuICAgIGNvbnN0IGVycm9yUmF0ZTR4eCA9IG5ldyBNZXRyaWMoe1xuICAgICAgbWV0cmljTmFtZTogJzR4eEVycm9yUmF0ZScsXG4gICAgICBuYW1lc3BhY2U6ICdBV1MvQ2xvdWRGcm9udCcsXG4gICAgICBzdGF0aXN0aWM6IFN0YXRpc3RpYy5BVkVSQUdFLFxuICAgICAgZGltZW5zaW9uc01hcDoge1xuICAgICAgICBEaXN0cmlidXRpb25JZDogZGlzdHJpYnV0aW9uLmRpc3RyaWJ1dGlvbklkLFxuICAgICAgICBSZWdpb246ICdHbG9iYWwnLFxuICAgICAgfSxcbiAgICAgIHJlZ2lvbjogJ3VzLWVhc3QtMScsIC8vIGdsb2JhbCBtZXRyaWNcbiAgICB9KTtcblxuICAgIGNvbnN0IGVycm9yUmF0ZTV4eCA9IG5ldyBNZXRyaWMoe1xuICAgICAgbWV0cmljTmFtZTogJzV4eEVycm9yUmF0ZScsXG4gICAgICBuYW1lc3BhY2U6ICdBV1MvQ2xvdWRGcm9udCcsXG4gICAgICBzdGF0aXN0aWM6IFN0YXRpc3RpYy5BVkVSQUdFLFxuICAgICAgZGltZW5zaW9uc01hcDoge1xuICAgICAgICBEaXN0cmlidXRpb25JZDogZGlzdHJpYnV0aW9uLmRpc3RyaWJ1dGlvbklkLFxuICAgICAgICBSZWdpb246ICdHbG9iYWwnLFxuICAgICAgfSxcbiAgICAgIHJlZ2lvbjogJ3VzLWVhc3QtMScsIC8vIGdsb2JhbCBtZXRyaWNcbiAgICB9KTtcblxuICAgIHRoaXMuY2xvdWRGcm9udE1ldHJpY1dpZGdldC5hZGRMZWZ0TWV0cmljKHRvdGFsUmVxdWVzdCk7XG4gICAgdGhpcy5jbG91ZEZyb250TWV0cmljV2lkZ2V0LmFkZFJpZ2h0TWV0cmljKGVycm9yUmF0ZTR4eCk7XG4gICAgdGhpcy5jbG91ZEZyb250TWV0cmljV2lkZ2V0LmFkZFJpZ2h0TWV0cmljKGVycm9yUmF0ZTV4eCk7XG4gIH1cblxuICBwcml2YXRlIGFkZENsb3VkRnJvbnRNZXRyaWNXaWRnZXQoKSB7XG4gICAgY29uc3Qgd2lkZ2V0ID0gbmV3IEdyYXBoV2lkZ2V0KHtcbiAgICAgIHRpdGxlOiAnQ2xvdWRGcm9udCBNZXRyaWNzJyxcbiAgICAgIGhlaWdodDogOCxcbiAgICAgIHdpZHRoOiAxMixcbiAgICAgIGxlZnRZQXhpczogeyBsYWJlbDogJ1JlcXVlc3RzIGNvdW50JyB9LFxuICAgICAgcmlnaHRZQXhpczogeyBsYWJlbDogJ1JlcXVlc3QgUGVyY2VudCcsIG1pbjogMCwgbWF4OiAxMDAgfSxcbiAgICB9KTtcbiAgICByZXR1cm4gd2lkZ2V0O1xuICB9XG5cbiAgcHJpdmF0ZSBhZGRMYW1iZGFTZXJ2aWNlUXVvdGFXaWRnZXRUb0Rhc2hib2FyZCgpIHtcbiAgICBjb25zdCBzZXJ2aWNlUXVvdGFMaW1pdCA9IG5ldyBNYXRoRXhwcmVzc2lvbih7XG4gICAgICBleHByZXNzaW9uOiAnbUxhbWJkYVVzYWdlIC8gbUxhbWJkYVF1b3RhICogMTAwJyxcbiAgICAgIGxhYmVsOiAnQ29uY3VycmVudCBleGVjdXRpb25zIHF1b3RhIHVzYWdlICUnLFxuICAgICAgdXNpbmdNZXRyaWNzOiB7IG1MYW1iZGFVc2FnZTogT3ZlcnZpZXdEYXNoYm9hcmQubUxhbWJkYVVzYWdlLCBtTGFtYmRhUXVvdGE6IE92ZXJ2aWV3RGFzaGJvYXJkLm1MYW1iZGFRdW90YSB9LFxuICAgIH0pO1xuXG4gICAgY29uc3QgYWxhcm0gPSBzZXJ2aWNlUXVvdGFMaW1pdC5jcmVhdGVBbGFybSh0aGlzLCAnT3ZlcnZpZXdEYXNoYm9hcmQvbGFtYmRhU2VydmljZVF1b3RhJywge1xuICAgICAgYWxhcm1EZXNjcmlwdGlvbjogW1xuICAgICAgICBgTGFtYmRhIGNvbmN1cnJlbnQgZXhlY3V0aW9uIGV4Y2VlZGVkICR7dGhpcy5sYW1iZGFTZXJ2aWNlQWxhcm1UaHJlc2hvbGR9JSBvZiBTRVJWSUNFX1FVT1RBYCxcbiAgICAgICAgJycsXG4gICAgICAgIGBSdW5Cb29rOiAke1JVTkJPT0tfVVJMfWAsXG4gICAgICAgICcnLFxuICAgICAgICAnUmVxdWVzdCBhIHNlcnZpY2UgcXVvdGEgaW5jcmVhc2UgZm9yIGxhbWJkYSBmdW5jdGlvbnMnLFxuICAgICAgXS5qb2luKCdcXG4nKSxcbiAgICAgIGNvbXBhcmlzb25PcGVyYXRvcjogQ29tcGFyaXNvbk9wZXJhdG9yLkdSRUFURVJfVEhBTl9USFJFU0hPTEQsXG4gICAgICBldmFsdWF0aW9uUGVyaW9kczogNSxcbiAgICAgIHRocmVzaG9sZDogdGhpcy5sYW1iZGFTZXJ2aWNlQWxhcm1UaHJlc2hvbGQsXG4gICAgICB0cmVhdE1pc3NpbmdEYXRhOiBUcmVhdE1pc3NpbmdEYXRhLk1JU1NJTkcsXG4gICAgfSk7XG5cbiAgICBjb25zdCBsYW1iZGFTZXJ2aWNlUXVvdGFXaWRnZXQgPSBuZXcgR3JhcGhXaWRnZXQoe1xuICAgICAgdGl0bGU6ICdMYW1iZGEgY29uY3VycmVudCBleGVjdXRpb24gcXVvdGEnLFxuICAgICAgaGVpZ2h0OiA4LFxuICAgICAgd2lkdGg6IDEyLFxuICAgICAgcmlnaHQ6IFtcbiAgICAgICAgYWxhcm0ubWV0cmljLFxuICAgICAgXSxcbiAgICAgIHJpZ2h0WUF4aXM6IHsgbGFiZWw6ICdRdW90YSBQZXJjZW50JywgbWluOiAwLCBtYXg6IDEwMCB9LFxuICAgICAgcmlnaHRBbm5vdGF0aW9uczogW3tcbiAgICAgICAgdmFsdWU6IHRoaXMubGFtYmRhU2VydmljZUFsYXJtVGhyZXNob2xkLFxuICAgICAgfV0sXG4gICAgfSk7XG5cbiAgICByZXR1cm4gbGFtYmRhU2VydmljZVF1b3RhV2lkZ2V0O1xuICB9XG5cbiAgcHVibGljIGFkZEludmVudG9yeU1ldHJpY3MoaW52ZW50b3J5OiBJbnZlbnRvcnkpIHtcbiAgICB0aGlzLmRhc2hib2FyZC5hZGRXaWRnZXRzKG5ldyBHcmFwaFdpZGdldCh7XG4gICAgICB0aXRsZTogJ0NvbnN0cnVjdCBIdWIgSW52ZW50b3J5JyxcbiAgICAgIGhlaWdodDogOCxcbiAgICAgIHdpZHRoOiAxMixcbiAgICAgIGxlZnQ6IFtcbiAgICAgICAgaW52ZW50b3J5Lm1ldHJpY1BhY2thZ2VDb3VudCgpLFxuICAgICAgICBpbnZlbnRvcnkubWV0cmljUGFja2FnZU1ham9yQ291bnQoKSxcbiAgICAgIF0sXG4gICAgICBsZWZ0WUF4aXM6IHsgbGFiZWw6ICdQYWNrYWdlIGNvdW50JyB9LFxuICAgIH0pKTtcbiAgfVxufSJdfQ==
@@ -0,0 +1,7 @@
1
+ import * as lambda from '@aws-cdk/aws-lambda';
2
+ import { Construct } from '@aws-cdk/core';
3
+ export interface SqsDlqStatsWidgetFunctionProps extends lambda.FunctionOptions {
4
+ }
5
+ export declare class SqsDlqStatsWidgetFunction extends lambda.SingletonFunction {
6
+ constructor(scope: Construct, id: string, props?: SqsDlqStatsWidgetFunctionProps);
7
+ }
@@ -0,0 +1,21 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.SqsDlqStatsWidgetFunction = void 0;
4
+ // ~~ Generated by projen. To modify, edit .projenrc.js and run "npx projen".
5
+ const path = require("path");
6
+ const lambda = require("@aws-cdk/aws-lambda");
7
+ class SqsDlqStatsWidgetFunction extends lambda.SingletonFunction {
8
+ constructor(scope, id, props) {
9
+ super(scope, id, {
10
+ description: 'overview-dashboard/sqs-dlq-stats-widget-function.lambda.ts',
11
+ ...props,
12
+ uuid: 'fc176846-044f-5e56-baf2-c71723501885',
13
+ lambdaPurpose: 'SQSDLQStatsWidget-Handler',
14
+ runtime: lambda.Runtime.NODEJS_14_X,
15
+ handler: 'index.handler',
16
+ code: lambda.Code.fromAsset(path.join(__dirname, '/sqs-dlq-stats-widget-function.lambda.bundle')),
17
+ });
18
+ }
19
+ }
20
+ exports.SqsDlqStatsWidgetFunction = SqsDlqStatsWidgetFunction;
21
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic3FzLWRscS1zdGF0cy13aWRnZXQtZnVuY3Rpb24uanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvb3ZlcnZpZXctZGFzaGJvYXJkL3Nxcy1kbHEtc3RhdHMtd2lkZ2V0LWZ1bmN0aW9uLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7OztBQUFBLDZFQUE2RTtBQUM3RSw2QkFBNkI7QUFDN0IsOENBQThDO0FBTTlDLE1BQWEseUJBQTBCLFNBQVEsTUFBTSxDQUFDLGlCQUFpQjtJQUNyRSxZQUFZLEtBQWdCLEVBQUUsRUFBVSxFQUFFLEtBQXNDO1FBQzlFLEtBQUssQ0FBQyxLQUFLLEVBQUUsRUFBRSxFQUFFO1lBQ2YsV0FBVyxFQUFFLDREQUE0RDtZQUN6RSxHQUFHLEtBQUs7WUFDUixJQUFJLEVBQUUsc0NBQXNDO1lBQzVDLGFBQWEsRUFBRSwyQkFBMkI7WUFDMUMsT0FBTyxFQUFFLE1BQU0sQ0FBQyxPQUFPLENBQUMsV0FBVztZQUNuQyxPQUFPLEVBQUUsZUFBZTtZQUN4QixJQUFJLEVBQUUsTUFBTSxDQUFDLElBQUksQ0FBQyxTQUFTLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxTQUFTLEVBQUUsOENBQThDLENBQUMsQ0FBQztTQUNsRyxDQUFDLENBQUM7SUFDTCxDQUFDO0NBQ0Y7QUFaRCw4REFZQyIsInNvdXJjZXNDb250ZW50IjpbIi8vIH5+IEdlbmVyYXRlZCBieSBwcm9qZW4uIFRvIG1vZGlmeSwgZWRpdCAucHJvamVucmMuanMgYW5kIHJ1biBcIm5weCBwcm9qZW5cIi5cbmltcG9ydCAqIGFzIHBhdGggZnJvbSAncGF0aCc7XG5pbXBvcnQgKiBhcyBsYW1iZGEgZnJvbSAnQGF3cy1jZGsvYXdzLWxhbWJkYSc7XG5pbXBvcnQgeyBDb25zdHJ1Y3QgfSBmcm9tICdAYXdzLWNkay9jb3JlJztcblxuZXhwb3J0IGludGVyZmFjZSBTcXNEbHFTdGF0c1dpZGdldEZ1bmN0aW9uUHJvcHMgZXh0ZW5kcyBsYW1iZGEuRnVuY3Rpb25PcHRpb25zIHtcbn1cblxuZXhwb3J0IGNsYXNzIFNxc0RscVN0YXRzV2lkZ2V0RnVuY3Rpb24gZXh0ZW5kcyBsYW1iZGEuU2luZ2xldG9uRnVuY3Rpb24ge1xuICBjb25zdHJ1Y3RvcihzY29wZTogQ29uc3RydWN0LCBpZDogc3RyaW5nLCBwcm9wcz86IFNxc0RscVN0YXRzV2lkZ2V0RnVuY3Rpb25Qcm9wcykge1xuICAgIHN1cGVyKHNjb3BlLCBpZCwge1xuICAgICAgZGVzY3JpcHRpb246ICdvdmVydmlldy1kYXNoYm9hcmQvc3FzLWRscS1zdGF0cy13aWRnZXQtZnVuY3Rpb24ubGFtYmRhLnRzJyxcbiAgICAgIC4uLnByb3BzLFxuICAgICAgdXVpZDogJ2ZjMTc2ODQ2LTA0NGYtNWU1Ni1iYWYyLWM3MTcyMzUwMTg4NScsXG4gICAgICBsYW1iZGFQdXJwb3NlOiAnU1FTRExRU3RhdHNXaWRnZXQtSGFuZGxlcicsXG4gICAgICBydW50aW1lOiBsYW1iZGEuUnVudGltZS5OT0RFSlNfMTRfWCxcbiAgICAgIGhhbmRsZXI6ICdpbmRleC5oYW5kbGVyJyxcbiAgICAgIGNvZGU6IGxhbWJkYS5Db2RlLmZyb21Bc3NldChwYXRoLmpvaW4oX19kaXJuYW1lLCAnL3Nxcy1kbHEtc3RhdHMtd2lkZ2V0LWZ1bmN0aW9uLmxhbWJkYS5idW5kbGUnKSksXG4gICAgfSk7XG4gIH1cbn0iXX0=
@@ -0,0 +1,181 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __defProps = Object.defineProperties;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropDescs = Object.getOwnPropertyDescriptors;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getOwnPropSymbols = Object.getOwnPropertySymbols;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __propIsEnum = Object.prototype.propertyIsEnumerable;
9
+ var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
10
+ var __spreadValues = (a, b) => {
11
+ for (var prop in b || (b = {}))
12
+ if (__hasOwnProp.call(b, prop))
13
+ __defNormalProp(a, prop, b[prop]);
14
+ if (__getOwnPropSymbols)
15
+ for (var prop of __getOwnPropSymbols(b)) {
16
+ if (__propIsEnum.call(b, prop))
17
+ __defNormalProp(a, prop, b[prop]);
18
+ }
19
+ return a;
20
+ };
21
+ var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b));
22
+ var __export = (target, all) => {
23
+ for (var name in all)
24
+ __defProp(target, name, { get: all[name], enumerable: true });
25
+ };
26
+ var __copyProps = (to, from, except, desc) => {
27
+ if (from && typeof from === "object" || typeof from === "function") {
28
+ for (let key of __getOwnPropNames(from))
29
+ if (!__hasOwnProp.call(to, key) && key !== except)
30
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
31
+ }
32
+ return to;
33
+ };
34
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
35
+
36
+ // src/overview-dashboard/sqs-dlq-stats-widget-function.lambda.ts
37
+ var sqs_dlq_stats_widget_function_lambda_exports = {};
38
+ __export(sqs_dlq_stats_widget_function_lambda_exports, {
39
+ handler: () => handler
40
+ });
41
+ module.exports = __toCommonJS(sqs_dlq_stats_widget_function_lambda_exports);
42
+ var import_aws_sdk = require("aws-sdk");
43
+ var DOCS = `
44
+
45
+ ### Widget parameters
46
+ Param | Description
47
+ ---|---
48
+ **title** | Title of the widget
49
+ **emptyQueueMessage** | Message to display when there are no visible messages in any of the queues
50
+ **nonEmptyQueueMessage** | Message to display when any of the queue contains a message
51
+ **queues** | Object containing information about the queues to display
52
+
53
+ ### Example parameters
54
+ \`\`\` yaml
55
+ {
56
+ "description": "",
57
+ "title": "SQS DLQ",
58
+ "emptyQueueMessage": "There are no messages in the DLQs. This is normal and no action is required.",
59
+ "nonEmptyQueueMessage": "There are some message in the DLQ. The table below lists the queues with non-empty DLQs. Please check the DLQs and re-drive the messages.",
60
+ "queues": {
61
+ "construct-hub-dev-ConstructHubOrchestrationDLQ9C6D9BD4-rlooG5g8o3YA": {
62
+ "name": "Orchestration DLQ",
63
+ "queueName": "construct-hub-dev-ConstructHubOrchestrationDLQ9C6D9BD4-rlooG5g8o3YA",
64
+ "reDriveFunctionArn": "arn:aws:lambda:us-east-2:123457424:function:construct-hub-dev-ConstructHubOrchestrationRedrive-bWYSY5KWfUUU"
65
+ },
66
+ "construct-hub-dev-ConstructHubIngestionDLQ3E96A5F2-6niMeQlEH0X0": {
67
+ "name": "Ingestion DLQ",
68
+ "queueName": "construct-hub-dev-ConstructHubIngestionDLQ3E96A5F2-6niMeQlEH0X0"
69
+ },
70
+ "construct-hub-dev-ConstructHubSourcesStagerDLQ80BD2600-AlCVN3KCXFPo": {
71
+ "name": "NPM JS Stager DLQ",
72
+ "queueName": "construct-hub-dev-ConstructHubSourcesStagerDLQ80BD2600-AlCVN3KCXFPo"
73
+ }
74
+ }
75
+ }
76
+ \`\`\`
77
+ `;
78
+ var DURATION = 5;
79
+ async function handler(event) {
80
+ var _a, _b;
81
+ console.log(JSON.stringify(event));
82
+ try {
83
+ if (event.describe) {
84
+ return DOCS;
85
+ }
86
+ const queues = (_a = event.widgetContext.params) == null ? void 0 : _a.queues;
87
+ if (Object.keys(queues).length === 0) {
88
+ return "No queue names specified";
89
+ }
90
+ const data = await getVisibleMessageCount(queues);
91
+ const heading = `<h4>${event.widgetContext.title}</h4>`;
92
+ const description = event.widgetContext.params.description ? `<p>${event.widgetContext.params.description}</p>` : "";
93
+ let rest = "";
94
+ if (Object.keys(data).length > 0 && Object.values(data).some((d) => d > 0)) {
95
+ const filteredData = Object.entries(data).filter(([_, count]) => count > 0).reduce((acc, [queueName, count]) => __spreadProps(__spreadValues({}, acc), { [queueName]: count }), {});
96
+ const table = generateTable(filteredData, queues, event.widgetContext);
97
+ console.log(table);
98
+ rest = `<p>${event.widgetContext.params.nonEmptyQueueMessage}</p>${table}`;
99
+ } else {
100
+ rest = `<p>${event.widgetContext.params.emptyQueueMessage}</p>`;
101
+ }
102
+ return [heading, description, rest].join("\n");
103
+ } catch (error) {
104
+ if (error instanceof Error) {
105
+ return {
106
+ markdown: [
107
+ "**\u26A0\uFE0F An error occurred**",
108
+ `- **name:** \`${error.name}\``,
109
+ `- **message:** ${error.message}`,
110
+ "- **stack:**",
111
+ " ```",
112
+ (_b = error.stack) == null ? void 0 : _b.replace(/^/g, " "),
113
+ " ```"
114
+ ].join("\n")
115
+ };
116
+ }
117
+ ;
118
+ throw error;
119
+ }
120
+ }
121
+ var getVisibleMessageCount = async (queueConfigs) => {
122
+ const queues = Object.values(queueConfigs);
123
+ const params = {
124
+ StartTime: new Date(new Date().getTime() - DURATION * 60 * 1e3),
125
+ EndTime: new Date(),
126
+ ScanBy: "TimestampDescending",
127
+ MetricDataQueries: queues.map((queue, index) => ({
128
+ Id: `m${index}`,
129
+ MetricStat: {
130
+ Period: 60,
131
+ Stat: "Maximum",
132
+ Metric: {
133
+ Namespace: "AWS/SQS",
134
+ MetricName: "ApproximateNumberOfMessagesVisible",
135
+ Dimensions: [
136
+ {
137
+ Name: "QueueName",
138
+ Value: queue.queueName
139
+ }
140
+ ]
141
+ }
142
+ }
143
+ }))
144
+ };
145
+ const cloudwatch = new import_aws_sdk.CloudWatch();
146
+ const response = await cloudwatch.getMetricData(params).promise();
147
+ const data = (response.MetricDataResults ?? []).reduce((acc, result) => {
148
+ var _a;
149
+ if (result.Id) {
150
+ const id = Number.parseInt(result.Id.replace("m", ""), 10);
151
+ return __spreadProps(__spreadValues({}, acc), { [queues[id].queueName]: ((_a = result.Values) == null ? void 0 : _a[0]) ?? 0 });
152
+ }
153
+ return acc;
154
+ }, {});
155
+ return data;
156
+ };
157
+ function generateTable(data, queueConfigs, widgetContext) {
158
+ const rows = Object.entries(data).map(([queueName, count]) => {
159
+ const queueConfig = queueConfigs[queueName];
160
+ if (queueConfig) {
161
+ const url = sqsQueueUrl(queueName, widgetContext);
162
+ const reDriveButton = (queueConfig == null ? void 0 : queueConfig.reDriveFunctionArn) ? `<a class="btn btn-primary">ReDrive</a><cwdb-action action="call" display="widget" endpoint="${queueConfig.reDriveFunctionArn}" event="click"></<cwdb-action>` : "";
163
+ const queueLink = `<a class="btn ${!reDriveButton ? "btn-primary" : ""}" href="${url}">Goto Queue</a>`;
164
+ const row = [queueConfig.name, count, `${queueLink} ${reDriveButton}`].map((d) => `<td>${d}</td>`).join("");
165
+ return `<tr>${row}</tr>`;
166
+ }
167
+ return false;
168
+ }).filter(Boolean);
169
+ const tableHeader = ["Queue Name", "Visible Messages", "Action"].map((h) => `<th>${h}</th>`);
170
+ const tableHeaderRow = `<tr>${tableHeader.join("")}</tr>`;
171
+ const table = ["<table>", tableHeaderRow, ...rows, "</table>"].join("\n");
172
+ return table;
173
+ }
174
+ function sqsQueueUrl(queueName, widgetContext) {
175
+ return `/sqs/v2/home#/queues/https%3A%2F%2Fsqs.${process.env.AWS_REGION}.amazonaws.com%2F${widgetContext.accountId}%2F${queueName}`;
176
+ }
177
+ // Annotate the CommonJS export names for ESM import in node:
178
+ 0 && (module.exports = {
179
+ handler
180
+ });
181
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../src/overview-dashboard/sqs-dlq-stats-widget-function.lambda.ts"],
4
+ "sourcesContent": ["/// @singleton SQSDLQStatsWidget-Handler\nconst DOCS = `\n\n### Widget parameters\nParam | Description\n---|---\n**title** | Title of the widget\n**emptyQueueMessage** | Message to display when there are no visible messages in any of the queues\n**nonEmptyQueueMessage** | Message to display when any of the queue contains a message\n**queues** | Object containing information about the queues to display\n\n### Example parameters\n\\`\\`\\` yaml\n{\n \"description\": \"\",\n \"title\": \"SQS DLQ\",\n \"emptyQueueMessage\": \"There are no messages in the DLQs. This is normal and no action is required.\",\n \"nonEmptyQueueMessage\": \"There are some message in the DLQ. The table below lists the queues with non-empty DLQs. Please check the DLQs and re-drive the messages.\",\n \"queues\": {\n \"construct-hub-dev-ConstructHubOrchestrationDLQ9C6D9BD4-rlooG5g8o3YA\": {\n \"name\": \"Orchestration DLQ\",\n \"queueName\": \"construct-hub-dev-ConstructHubOrchestrationDLQ9C6D9BD4-rlooG5g8o3YA\",\n \"reDriveFunctionArn\": \"arn:aws:lambda:us-east-2:123457424:function:construct-hub-dev-ConstructHubOrchestrationRedrive-bWYSY5KWfUUU\"\n },\n \"construct-hub-dev-ConstructHubIngestionDLQ3E96A5F2-6niMeQlEH0X0\": {\n \"name\": \"Ingestion DLQ\",\n \"queueName\": \"construct-hub-dev-ConstructHubIngestionDLQ3E96A5F2-6niMeQlEH0X0\"\n },\n \"construct-hub-dev-ConstructHubSourcesStagerDLQ80BD2600-AlCVN3KCXFPo\": {\n \"name\": \"NPM JS Stager DLQ\",\n \"queueName\": \"construct-hub-dev-ConstructHubSourcesStagerDLQ80BD2600-AlCVN3KCXFPo\"\n }\n }\n}\n\\`\\`\\`\n`;\n\nimport { CloudWatch } from 'aws-sdk';\nconst DURATION = 5; // minutes\n\ninterface Event {\n readonly key: string;\n readonly description: string;\n readonly widgetContext: WidgetContext;\n describe?: unknown;\n}\n\ntype QueueConfig = {\n queueName: string;\n name: string;\n reDriveFunctionArn?: string;\n}\n\nexport async function handler(event: Event): Promise<string | { markdown: string }> {\n console.log(JSON.stringify(event));\n try {\n if (event.describe) {\n return DOCS;\n }\n\n const queues = event.widgetContext.params?.queues;\n if (Object.keys(queues).length === 0) {\n return 'No queue names specified';\n }\n\n const data = await getVisibleMessageCount(queues);\n\n const heading = `<h4>${event.widgetContext.title}</h4>`;\n const description = event.widgetContext.params.description ? `<p>${event.widgetContext.params.description}</p>` : '';\n let rest = '';\n if (Object.keys(data).length > 0 && Object.values(data).some(d => d > 0)) {\n const filteredData = Object.entries(data)\n .filter(([_, count]) => count > 0)\n .reduce((acc, [queueName, count]) => ({ ...acc, [queueName]: count }), {} as Record<string, number>);\n const table = generateTable(filteredData, queues, event.widgetContext);\n console.log(table);\n rest = `<p>${event.widgetContext.params.nonEmptyQueueMessage}</p>${table}`;\n } else {\n rest = `<p>${event.widgetContext.params.emptyQueueMessage}</p>`;\n }\n return [heading, description, rest].join('\\n');\n } catch (error) {\n if (error instanceof Error) {\n return {\n markdown: [\n '**\u26A0\uFE0F An error occurred**',\n `- **name:** \\`${error.name}\\``,\n `- **message:** ${error.message}`,\n '- **stack:**',\n ' ```',\n error.stack?.replace(/^/g, ' '),\n ' ```',\n ].join('\\n'),\n };\n };\n throw error;\n }\n}\n\nexport interface WidgetContext {\n readonly dashboardName: string;\n readonly widgetId: string;\n readonly domain: string;\n readonly accountId: string;\n readonly locale: string;\n readonly timezone: {\n readonly label: string;\n readonly offsetISO: string;\n readonly offsetInMinutes: number;\n };\n readonly period: number;\n readonly isAutoPeriod: true;\n readonly timeRange: {\n readonly mode: 'relative' | 'absolute';\n readonly start: number;\n readonly end: number;\n readonly relativeStart: number;\n readonly zoom: {\n readonly start: number;\n readonly end: number;\n };\n };\n readonly theme: 'light' | 'dark';\n readonly linkCharts: boolean;\n readonly title: string;\n readonly forms: {\n readonly all: { readonly [key: string]: string };\n };\n readonly params: {\n queues: Record<string, QueueConfig>;\n title?: string;\n description?: string;\n emptyQueueMessage?: string;\n nonEmptyQueueMessage?: string;\n };\n readonly width: number;\n readonly height: number;\n}\n\nconst getVisibleMessageCount = async (queueConfigs: Record<string, QueueConfig>): Promise<Record<string, number>> => {\n // Keeping the time period to 5 minutes to show current state of the queue when re-drive happens\n const queues = Object.values(queueConfigs);\n const params: CloudWatch.GetMetricDataInput = {\n StartTime: new Date(new Date().getTime() - (DURATION * 60 * 1000)), // 5 minutes ago\n EndTime: new Date(), // now\n ScanBy: 'TimestampDescending',\n MetricDataQueries: queues.map((queue, index) => ({\n Id: `m${index}`,\n MetricStat: {\n Period: 60,\n Stat: 'Maximum',\n Metric: {\n Namespace: 'AWS/SQS',\n MetricName: 'ApproximateNumberOfMessagesVisible',\n Dimensions: [\n {\n Name: 'QueueName',\n Value: queue.queueName,\n },\n ],\n },\n },\n })),\n };\n const cloudwatch = new CloudWatch();\n const response = await cloudwatch.getMetricData(params).promise();\n const data = (response.MetricDataResults ?? []).reduce((acc, result) => {\n if (result.Id) {\n const id = Number.parseInt(result.Id.replace('m', ''), 10);\n return { ...acc, [queues[id].queueName]: result.Values?.[0] ?? 0 };\n }\n return acc;\n }, {} as Record<string, number>);\n return data;\n\n};\n\nfunction generateTable(data: Record<string, number>, queueConfigs: Record<string, QueueConfig>, widgetContext: WidgetContext): string {\n\n const rows = Object.entries(data).map(([queueName, count]) => {\n const queueConfig = queueConfigs[queueName];\n if (queueConfig) {\n const url = sqsQueueUrl(queueName, widgetContext);\n const reDriveButton = queueConfig?.reDriveFunctionArn ? `<a class=\"btn btn-primary\">ReDrive</a><cwdb-action action=\"call\" display=\"widget\" endpoint=\"${queueConfig.reDriveFunctionArn}\" event=\"click\"></<cwdb-action>` : '';\n const queueLink = `<a class=\"btn ${!reDriveButton ? 'btn-primary' : ''}\" href=\"${url}\">Goto Queue</a>`;\n const row = [queueConfig.name, count, `${queueLink} ${reDriveButton}`].map(d => `<td>${d}</td>`).join('');\n return `<tr>${row}</tr>`;\n }\n return false;\n }).filter(Boolean);\n\n const tableHeader = ['Queue Name', 'Visible Messages', 'Action'].map(h => `<th>${h}</th>`);\n const tableHeaderRow = `<tr>${tableHeader.join('')}</tr>`;\n const table = ['<table>', tableHeaderRow, ...rows, '</table>'].join('\\n');\n return table;\n}\n\nfunction sqsQueueUrl(queueName: string, widgetContext: WidgetContext): string {\n return `/sqs/v2/home#/queues/https%3A%2F%2Fsqs.${process.env.AWS_REGION}.amazonaws.com%2F${widgetContext.accountId}%2F${queueName}`;\n}\n"],
5
+ "mappings": "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",
6
+ "names": []
7
+ }
@@ -0,0 +1,56 @@
1
+ interface Event {
2
+ readonly key: string;
3
+ readonly description: string;
4
+ readonly widgetContext: WidgetContext;
5
+ describe?: unknown;
6
+ }
7
+ declare type QueueConfig = {
8
+ queueName: string;
9
+ name: string;
10
+ reDriveFunctionArn?: string;
11
+ };
12
+ export declare function handler(event: Event): Promise<string | {
13
+ markdown: string;
14
+ }>;
15
+ export interface WidgetContext {
16
+ readonly dashboardName: string;
17
+ readonly widgetId: string;
18
+ readonly domain: string;
19
+ readonly accountId: string;
20
+ readonly locale: string;
21
+ readonly timezone: {
22
+ readonly label: string;
23
+ readonly offsetISO: string;
24
+ readonly offsetInMinutes: number;
25
+ };
26
+ readonly period: number;
27
+ readonly isAutoPeriod: true;
28
+ readonly timeRange: {
29
+ readonly mode: 'relative' | 'absolute';
30
+ readonly start: number;
31
+ readonly end: number;
32
+ readonly relativeStart: number;
33
+ readonly zoom: {
34
+ readonly start: number;
35
+ readonly end: number;
36
+ };
37
+ };
38
+ readonly theme: 'light' | 'dark';
39
+ readonly linkCharts: boolean;
40
+ readonly title: string;
41
+ readonly forms: {
42
+ readonly all: {
43
+ readonly [key: string]: string;
44
+ };
45
+ };
46
+ readonly params: {
47
+ queues: Record<string, QueueConfig>;
48
+ title?: string;
49
+ description?: string;
50
+ emptyQueueMessage?: string;
51
+ nonEmptyQueueMessage?: string;
52
+ };
53
+ readonly width: number;
54
+ readonly height: number;
55
+ }
56
+ export {};