@geekmidas/events 0.0.2 → 0.2.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.
- package/dist/{BasicConnection-C4_Y0nIj.d.mts → BasicConnection-BTRsXiuH.d.cts} +3 -2
- package/dist/BasicConnection-BTRsXiuH.d.cts.map +1 -0
- package/dist/BasicConnection-C-Cg2FJT.cjs.map +1 -1
- package/dist/BasicConnection-CpwybcC9.mjs.map +1 -1
- package/dist/{BasicConnection-CddnULuT.d.cts → BasicConnection-DIxtA7jK.d.mts} +3 -2
- package/dist/BasicConnection-DIxtA7jK.d.mts.map +1 -0
- package/dist/BasicPublisher-CXAVKxgq.cjs.map +1 -1
- package/dist/{BasicPublisher-DCBxaTwC.d.mts → BasicPublisher-CpJFK2oS.d.mts} +5 -3
- package/dist/BasicPublisher-CpJFK2oS.d.mts.map +1 -0
- package/dist/{BasicPublisher-V5hELHG9.d.cts → BasicPublisher-D1q0a2xp.d.cts} +5 -3
- package/dist/BasicPublisher-D1q0a2xp.d.cts.map +1 -0
- package/dist/BasicPublisher-DogbeHAH.mjs.map +1 -1
- package/dist/{BasicSubscriber-BB3aKeph.d.mts → BasicSubscriber-6w0Af4DD.d.mts} +5 -3
- package/dist/BasicSubscriber-6w0Af4DD.d.mts.map +1 -0
- package/dist/{BasicSubscriber-CCKfbRMo.mjs → BasicSubscriber-BJSZN22M.mjs} +2 -4
- package/dist/BasicSubscriber-BJSZN22M.mjs.map +1 -0
- package/dist/{BasicSubscriber-DOwgFw5j.d.cts → BasicSubscriber-DXxQewjc.d.cts} +5 -3
- package/dist/BasicSubscriber-DXxQewjc.d.cts.map +1 -0
- package/dist/{BasicSubscriber-BGqv3c8x.cjs → BasicSubscriber-mVJRMXhZ.cjs} +2 -4
- package/dist/BasicSubscriber-mVJRMXhZ.cjs.map +1 -0
- package/dist/{EventConnection-DZhn1Msl.d.cts → EventConnection-BaJRXdg7.d.cts} +2 -2
- package/dist/EventConnection-BaJRXdg7.d.cts.map +1 -0
- package/dist/EventConnection-ClLMoIUU.mjs.map +1 -1
- package/dist/EventConnection-F_N81SXu.cjs.map +1 -1
- package/dist/{EventConnection-DeXfLGom.d.mts → EventConnection-SOhtmxib.d.mts} +2 -2
- package/dist/EventConnection-SOhtmxib.d.mts.map +1 -0
- package/dist/EventConnection.d.cts +2 -2
- package/dist/EventConnection.d.mts +2 -2
- package/dist/{Publisher-C3m8JFXm.d.mts → Publisher-1CfcYRnm.d.mts} +4 -2
- package/dist/Publisher-1CfcYRnm.d.mts.map +1 -0
- package/dist/{Publisher-nXNVjL0e.d.cts → Publisher-BfNgvHwQ.d.cts} +4 -2
- package/dist/Publisher-BfNgvHwQ.d.cts.map +1 -0
- package/dist/Publisher-DS0aOh8r.mjs.map +1 -1
- package/dist/Publisher-ceu6OxCj.cjs.map +1 -1
- package/dist/Publisher.d.cts +2 -2
- package/dist/Publisher.d.mts +2 -2
- package/dist/{RabbitMQConnection-ungB0Zl6.cjs → RabbitMQConnection-Blj9Lo05.cjs} +2 -2
- package/dist/RabbitMQConnection-Blj9Lo05.cjs.map +1 -0
- package/dist/{RabbitMQConnection-7cWp_-_Q.d.cts → RabbitMQConnection-Bm8VCI2R.d.cts} +3 -2
- package/dist/RabbitMQConnection-Bm8VCI2R.d.cts.map +1 -0
- package/dist/{RabbitMQConnection-C2HC7J7K.d.mts → RabbitMQConnection-Ck-ktCp_.d.mts} +3 -2
- package/dist/RabbitMQConnection-Ck-ktCp_.d.mts.map +1 -0
- package/dist/{RabbitMQConnection-kU3MvN5P.mjs → RabbitMQConnection-Co7f5Xsx.mjs} +2 -2
- package/dist/RabbitMQConnection-Co7f5Xsx.mjs.map +1 -0
- package/dist/RabbitMQPublisher--DL6GM6g.cjs.map +1 -1
- package/dist/{RabbitMQPublisher-CpcbgrXj.d.cts → RabbitMQPublisher-COnxcWEZ.d.mts} +5 -3
- package/dist/RabbitMQPublisher-COnxcWEZ.d.mts.map +1 -0
- package/dist/RabbitMQPublisher-o_oVI3wr.mjs.map +1 -1
- package/dist/{RabbitMQPublisher-DDZi15Ro.d.mts → RabbitMQPublisher-qQT7-1-p.d.cts} +5 -3
- package/dist/RabbitMQPublisher-qQT7-1-p.d.cts.map +1 -0
- package/dist/{RabbitMQSubscriber-BplEJEk8.cjs → RabbitMQSubscriber-C8MYAihn.cjs} +2 -3
- package/dist/RabbitMQSubscriber-C8MYAihn.cjs.map +1 -0
- package/dist/{RabbitMQSubscriber-BJ9PTGny.d.cts → RabbitMQSubscriber-C_sZjDPU.d.mts} +5 -3
- package/dist/RabbitMQSubscriber-C_sZjDPU.d.mts.map +1 -0
- package/dist/{RabbitMQSubscriber-CIvIw8MG.d.mts → RabbitMQSubscriber-D12sssMb.d.cts} +5 -3
- package/dist/RabbitMQSubscriber-D12sssMb.d.cts.map +1 -0
- package/dist/{RabbitMQSubscriber-BaXqs5nO.mjs → RabbitMQSubscriber-DlGL0pC2.mjs} +2 -3
- package/dist/RabbitMQSubscriber-DlGL0pC2.mjs.map +1 -0
- package/dist/{SNSConnection-CAImaMM2.d.mts → SNSConnection-BDfQrVBO.d.cts} +3 -2
- package/dist/SNSConnection-BDfQrVBO.d.cts.map +1 -0
- package/dist/SNSConnection-D7wCf9Si.mjs.map +1 -1
- package/dist/SNSConnection-DVsC_pbB.cjs.map +1 -1
- package/dist/{SNSConnection-Cqbr7-Ho.d.cts → SNSConnection-mHb10nQ6.d.mts} +3 -2
- package/dist/SNSConnection-mHb10nQ6.d.mts.map +1 -0
- package/dist/{SNSPublisher-BZ6N3JI9.cjs → SNSPublisher-BoA8uLnw.cjs} +2 -3
- package/dist/SNSPublisher-BoA8uLnw.cjs.map +1 -0
- package/dist/{SNSPublisher-Dkhb4LU-.d.mts → SNSPublisher-CM7UAWtf.d.mts} +7 -6
- package/dist/SNSPublisher-CM7UAWtf.d.mts.map +1 -0
- package/dist/{SNSPublisher-BfWjzNiB.mjs → SNSPublisher-Drr4kHn6.mjs} +2 -3
- package/dist/SNSPublisher-Drr4kHn6.mjs.map +1 -0
- package/dist/{SNSPublisher-RVZFa7kL.d.cts → SNSPublisher-kpsSlGDn.d.cts} +7 -6
- package/dist/SNSPublisher-kpsSlGDn.d.cts.map +1 -0
- package/dist/{SNSSubscriber-C9YJWa7m.mjs → SNSSubscriber-CBIvQpqK.mjs} +4 -8
- package/dist/SNSSubscriber-CBIvQpqK.mjs.map +1 -0
- package/dist/{SNSSubscriber-UE0ia4XM.cjs → SNSSubscriber-CWiAJjpU.cjs} +4 -8
- package/dist/SNSSubscriber-CWiAJjpU.cjs.map +1 -0
- package/dist/{SNSSubscriber-B31JmkAE.d.mts → SNSSubscriber-DDB3--pm.d.cts} +4 -3
- package/dist/SNSSubscriber-DDB3--pm.d.cts.map +1 -0
- package/dist/{SNSSubscriber-k2UwKUxu.d.cts → SNSSubscriber-Dwo8391f.d.mts} +4 -3
- package/dist/SNSSubscriber-Dwo8391f.d.mts.map +1 -0
- package/dist/{SQSConnection-C5WFqZW7.d.mts → SQSConnection-B5Ep9Tma.d.cts} +3 -2
- package/dist/SQSConnection-B5Ep9Tma.d.cts.map +1 -0
- package/dist/{SQSConnection-geav2Rsn.d.cts → SQSConnection-BI_xOzyJ.d.mts} +3 -2
- package/dist/SQSConnection-BI_xOzyJ.d.mts.map +1 -0
- package/dist/SQSConnection-BwNjUPr4.cjs.map +1 -1
- package/dist/SQSConnection-Eleqqre3.mjs.map +1 -1
- package/dist/{SQSPublisher-CdsayOLV.d.mts → SQSPublisher-BN1ds6SN.d.mts} +5 -3
- package/dist/SQSPublisher-BN1ds6SN.d.mts.map +1 -0
- package/dist/{SQSPublisher-Utb_FTe5.d.cts → SQSPublisher-DFTqwzRW.d.cts} +5 -3
- package/dist/SQSPublisher-DFTqwzRW.d.cts.map +1 -0
- package/dist/SQSPublisher-DG9o4ME3.mjs.map +1 -1
- package/dist/SQSPublisher-Dq5353oZ.cjs.map +1 -1
- package/dist/{SQSSubscriber-DZDNgLE5.cjs → SQSSubscriber-B4sRXVl6.cjs} +5 -12
- package/dist/SQSSubscriber-B4sRXVl6.cjs.map +1 -0
- package/dist/{SQSSubscriber-B6iL1EnX.d.cts → SQSSubscriber-BO2me_gs.d.cts} +4 -3
- package/dist/SQSSubscriber-BO2me_gs.d.cts.map +1 -0
- package/dist/{SQSSubscriber-CD66P7hH.mjs → SQSSubscriber-CVGhZ8Nr.mjs} +5 -12
- package/dist/SQSSubscriber-CVGhZ8Nr.mjs.map +1 -0
- package/dist/{SQSSubscriber-DBeE4XAR.d.mts → SQSSubscriber-FyZFfr2-.d.mts} +4 -3
- package/dist/SQSSubscriber-FyZFfr2-.d.mts.map +1 -0
- package/dist/Subscriber-Bd9hb_jp.mjs.map +1 -1
- package/dist/{Subscriber-Cff9EIpv.d.mts → Subscriber-BxT-KzFJ.d.mts} +4 -2
- package/dist/Subscriber-BxT-KzFJ.d.mts.map +1 -0
- package/dist/{Subscriber-CpAwcUzb.d.cts → Subscriber-DDD_4Nlx.d.cts} +4 -2
- package/dist/Subscriber-DDD_4Nlx.d.cts.map +1 -0
- package/dist/Subscriber-saVOWuMq.cjs.map +1 -1
- package/dist/Subscriber.d.cts +2 -2
- package/dist/Subscriber.d.mts +2 -2
- package/dist/basic/BasicConnection.d.cts +2 -2
- package/dist/basic/BasicConnection.d.mts +2 -2
- package/dist/basic/BasicPublisher.d.cts +3 -3
- package/dist/basic/BasicPublisher.d.mts +3 -3
- package/dist/basic/BasicSubscriber.cjs +1 -1
- package/dist/basic/BasicSubscriber.d.cts +3 -3
- package/dist/basic/BasicSubscriber.d.mts +3 -3
- package/dist/basic/BasicSubscriber.mjs +1 -1
- package/dist/basic/index.cjs +1 -1
- package/dist/basic/index.d.cts +4 -4
- package/dist/basic/index.d.mts +4 -4
- package/dist/basic/index.mjs +1 -1
- package/dist/index.d.cts +4 -4
- package/dist/index.d.mts +4 -4
- package/dist/rabbitmq/RabbitMQConnection.cjs +1 -1
- package/dist/rabbitmq/RabbitMQConnection.d.cts +2 -2
- package/dist/rabbitmq/RabbitMQConnection.d.mts +2 -2
- package/dist/rabbitmq/RabbitMQConnection.mjs +1 -1
- package/dist/rabbitmq/RabbitMQPublisher.d.cts +3 -3
- package/dist/rabbitmq/RabbitMQPublisher.d.mts +3 -3
- package/dist/rabbitmq/RabbitMQSubscriber.cjs +1 -1
- package/dist/rabbitmq/RabbitMQSubscriber.d.cts +3 -3
- package/dist/rabbitmq/RabbitMQSubscriber.d.mts +3 -3
- package/dist/rabbitmq/RabbitMQSubscriber.mjs +1 -1
- package/dist/rabbitmq/index.cjs +2 -2
- package/dist/rabbitmq/index.d.cts +4 -4
- package/dist/rabbitmq/index.d.mts +4 -4
- package/dist/rabbitmq/index.mjs +2 -2
- package/dist/sns/SNSConnection.d.cts +2 -2
- package/dist/sns/SNSConnection.d.mts +2 -2
- package/dist/sns/SNSPublisher.cjs +1 -1
- package/dist/sns/SNSPublisher.d.cts +3 -3
- package/dist/sns/SNSPublisher.d.mts +3 -3
- package/dist/sns/SNSPublisher.mjs +1 -1
- package/dist/sns/SNSSubscriber.cjs +2 -2
- package/dist/sns/SNSSubscriber.d.cts +3 -3
- package/dist/sns/SNSSubscriber.d.mts +3 -3
- package/dist/sns/SNSSubscriber.mjs +2 -2
- package/dist/sns/index.cjs +3 -3
- package/dist/sns/index.d.cts +4 -4
- package/dist/sns/index.d.mts +4 -4
- package/dist/sns/index.mjs +3 -3
- package/dist/sqs/SQSConnection.d.cts +2 -2
- package/dist/sqs/SQSConnection.d.mts +2 -2
- package/dist/sqs/SQSPublisher.d.cts +3 -3
- package/dist/sqs/SQSPublisher.d.mts +3 -3
- package/dist/sqs/SQSSubscriber.cjs +1 -1
- package/dist/sqs/SQSSubscriber.d.cts +3 -3
- package/dist/sqs/SQSSubscriber.d.mts +3 -3
- package/dist/sqs/SQSSubscriber.mjs +1 -1
- package/dist/sqs/index.cjs +1 -1
- package/dist/sqs/index.d.cts +4 -4
- package/dist/sqs/index.d.mts +4 -4
- package/dist/sqs/index.mjs +1 -1
- package/dist/types-B63qHVOG.mjs.map +1 -1
- package/dist/types-CvSMEO5z.cjs.map +1 -1
- package/dist/{types-D0g-o6wX.d.mts → types-DSVsPgKd.d.mts} +2 -1
- package/dist/types-DSVsPgKd.d.mts.map +1 -0
- package/dist/{types-DEGUg5BA.d.cts → types-Dmbp1U-6.d.cts} +2 -1
- package/dist/types-Dmbp1U-6.d.cts.map +1 -0
- package/dist/types.d.cts +1 -1
- package/dist/types.d.mts +1 -1
- package/package.json +24 -5
- package/src/EventConnection.ts +31 -31
- package/src/Publisher.ts +78 -78
- package/src/Subscriber.ts +91 -91
- package/src/__tests__/EventConnection.spec.ts +42 -0
- package/src/__tests__/Publisher.spec.ts +96 -0
- package/src/__tests__/Subscriber.spec.ts +91 -23
- package/src/basic/BasicConnection.ts +35 -35
- package/src/basic/BasicPublisher.ts +8 -8
- package/src/basic/BasicSubscriber.ts +13 -16
- package/src/basic/__tests__/BasicSubscriber.spec.ts +116 -116
- package/src/index.ts +13 -13
- package/src/rabbitmq/RabbitMQConnection.ts +139 -139
- package/src/rabbitmq/RabbitMQPublisher.ts +48 -48
- package/src/rabbitmq/RabbitMQSubscriber.ts +100 -102
- package/src/rabbitmq/__tests__/RabbitMQPublisher.spec.ts +364 -364
- package/src/rabbitmq/__tests__/RabbitMQSubscriber.spec.ts +204 -204
- package/src/rabbitmq/index.ts +3 -3
- package/src/sns/SNSConnection.ts +76 -76
- package/src/sns/SNSPublisher.ts +54 -56
- package/src/sns/SNSSubscriber.ts +187 -191
- package/src/sns/__tests__/SNS-SQS-Integration.spec.ts +321 -321
- package/src/sns/__tests__/SNSPublisher.spec.ts +449 -0
- package/src/sns/__tests__/SNSSubscriber.spec.ts +527 -0
- package/src/sns/index.ts +3 -3
- package/src/sqs/SQSConnection.ts +75 -75
- package/src/sqs/SQSPublisher.ts +93 -93
- package/src/sqs/SQSSubscriber.ts +157 -165
- package/src/sqs/__tests__/SQSPublisher.spec.ts +178 -178
- package/src/sqs/index.ts +3 -3
- package/src/types.ts +28 -29
- package/tsconfig.json +9 -0
- package/tsdown.config.ts +1 -1
- package/dist/BasicSubscriber-BGqv3c8x.cjs.map +0 -1
- package/dist/BasicSubscriber-CCKfbRMo.mjs.map +0 -1
- package/dist/RabbitMQConnection-kU3MvN5P.mjs.map +0 -1
- package/dist/RabbitMQConnection-ungB0Zl6.cjs.map +0 -1
- package/dist/RabbitMQSubscriber-BaXqs5nO.mjs.map +0 -1
- package/dist/RabbitMQSubscriber-BplEJEk8.cjs.map +0 -1
- package/dist/SNSPublisher-BZ6N3JI9.cjs.map +0 -1
- package/dist/SNSPublisher-BfWjzNiB.mjs.map +0 -1
- package/dist/SNSSubscriber-C9YJWa7m.mjs.map +0 -1
- package/dist/SNSSubscriber-UE0ia4XM.cjs.map +0 -1
- package/dist/SQSSubscriber-CD66P7hH.mjs.map +0 -1
- package/dist/SQSSubscriber-DZDNgLE5.cjs.map +0 -1
package/src/sqs/SQSPublisher.ts
CHANGED
|
@@ -1,105 +1,105 @@
|
|
|
1
1
|
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
2
|
+
SendMessageBatchCommand,
|
|
3
|
+
type SendMessageBatchCommandInput,
|
|
4
|
+
type SendMessageBatchRequestEntry,
|
|
5
5
|
} from '@aws-sdk/client-sqs';
|
|
6
6
|
import chunk from 'lodash.chunk';
|
|
7
7
|
import type { EventPublisher, PublishableMessage } from '../types';
|
|
8
8
|
import type { SQSConnection } from './SQSConnection';
|
|
9
9
|
|
|
10
10
|
export interface SQSPublisherOptions {
|
|
11
|
-
|
|
11
|
+
maxBatchSize?: number; // Default: 10 (SQS limit)
|
|
12
12
|
}
|
|
13
13
|
|
|
14
14
|
export class SQSPublisher<TMessage extends PublishableMessage<string, any>>
|
|
15
|
-
|
|
15
|
+
implements EventPublisher<TMessage>
|
|
16
16
|
{
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
17
|
+
readonly options: Required<SQSPublisherOptions>;
|
|
18
|
+
|
|
19
|
+
constructor(
|
|
20
|
+
readonly connection: SQSConnection,
|
|
21
|
+
options: SQSPublisherOptions = {},
|
|
22
|
+
) {
|
|
23
|
+
this.options = {
|
|
24
|
+
maxBatchSize: options.maxBatchSize ?? 10, // SQS limit
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Create an SQSPublisher from a connection string
|
|
30
|
+
* Format: sqs://?queueUrl=https://sqs.region.amazonaws.com/accountId/queueName®ion=us-east-1&endpoint=http://localhost:4566&maxBatchSize=10
|
|
31
|
+
*/
|
|
32
|
+
static async fromConnectionString<
|
|
33
|
+
TMessage extends PublishableMessage<string, any>,
|
|
34
|
+
>(connectionString: string): Promise<SQSPublisher<TMessage>> {
|
|
35
|
+
const url = new URL(connectionString);
|
|
36
|
+
const params = url.searchParams;
|
|
37
|
+
|
|
38
|
+
const { SQSConnection } = await import('./SQSConnection');
|
|
39
|
+
const connection =
|
|
40
|
+
await SQSConnection.fromConnectionString(connectionString);
|
|
41
|
+
|
|
42
|
+
const options: SQSPublisherOptions = {
|
|
43
|
+
maxBatchSize: params.get('maxBatchSize')
|
|
44
|
+
? Number.parseInt(params.get('maxBatchSize')!, 10)
|
|
45
|
+
: undefined,
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
return new SQSPublisher<TMessage>(connection, options);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
async publish(messages: TMessage[]): Promise<void> {
|
|
52
|
+
if (messages.length === 0) return;
|
|
53
|
+
|
|
54
|
+
if (!this.connection.isConnected()) {
|
|
55
|
+
await this.connection.connect();
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// Split messages into batches (SQS limit is 10 messages per batch)
|
|
59
|
+
const batches = chunk(messages, this.options.maxBatchSize);
|
|
60
|
+
|
|
61
|
+
// Send all batches
|
|
62
|
+
await Promise.all(batches.map((batch) => this.sendBatch(batch)));
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
private async sendBatch(messages: TMessage[]): Promise<void> {
|
|
66
|
+
const entries: SendMessageBatchRequestEntry[] = messages.map(
|
|
67
|
+
(message, index) => ({
|
|
68
|
+
Id: `${index}`,
|
|
69
|
+
MessageBody: JSON.stringify({
|
|
70
|
+
type: message.type,
|
|
71
|
+
payload: message.payload,
|
|
72
|
+
}),
|
|
73
|
+
MessageAttributes: {
|
|
74
|
+
type: {
|
|
75
|
+
DataType: 'String',
|
|
76
|
+
StringValue: message.type,
|
|
77
|
+
},
|
|
78
|
+
},
|
|
79
|
+
}),
|
|
80
|
+
);
|
|
81
|
+
|
|
82
|
+
const input: SendMessageBatchCommandInput = {
|
|
83
|
+
QueueUrl: this.connection.queueUrl,
|
|
84
|
+
Entries: entries,
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
const command = new SendMessageBatchCommand(input);
|
|
88
|
+
const response = await this.connection.sqsClient.send(command);
|
|
89
|
+
|
|
90
|
+
// Check for failures
|
|
91
|
+
if (response.Failed && response.Failed.length > 0) {
|
|
92
|
+
const errors = response.Failed.map(
|
|
93
|
+
(f) => `${f.Id}: ${f.Code} - ${f.Message}`,
|
|
94
|
+
).join(', ');
|
|
95
|
+
throw new Error(
|
|
96
|
+
`Failed to send ${response.Failed.length} messages: ${errors}`,
|
|
97
|
+
);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
async close(): Promise<void> {
|
|
102
|
+
// Publisher doesn't own the connection
|
|
103
|
+
// Connection should be closed by whoever created it
|
|
104
|
+
}
|
|
105
105
|
}
|
package/src/sqs/SQSSubscriber.ts
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
2
|
+
DeleteMessageCommand,
|
|
3
|
+
type Message,
|
|
4
|
+
ReceiveMessageCommand,
|
|
5
5
|
} from '@aws-sdk/client-sqs';
|
|
6
6
|
import type { EventSubscriber, PublishableMessage } from '../types';
|
|
7
7
|
import type { SQSConnection } from './SQSConnection';
|
|
8
8
|
|
|
9
9
|
export interface SQSSubscriberOptions {
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
10
|
+
waitTimeSeconds?: number; // SQS long polling (default: 20)
|
|
11
|
+
maxMessages?: number; // Max messages per poll (default: 10)
|
|
12
|
+
expectedTopicArn?: string; // Optional: verify messages are from specific SNS topic
|
|
13
13
|
}
|
|
14
14
|
|
|
15
15
|
/**
|
|
@@ -26,164 +26,156 @@ export interface SQSSubscriberOptions {
|
|
|
26
26
|
* - Processes the actual message payload
|
|
27
27
|
*/
|
|
28
28
|
export class SQSSubscriber<TMessage extends PublishableMessage<string, any>>
|
|
29
|
-
|
|
29
|
+
implements EventSubscriber<TMessage>
|
|
30
30
|
{
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
} catch (error) {
|
|
182
|
-
console.error('Error deleting message:', error);
|
|
183
|
-
}
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
private sleep(ms: number): Promise<void> {
|
|
187
|
-
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
188
|
-
}
|
|
31
|
+
private polling = false;
|
|
32
|
+
private messageTypes: Set<TMessage['type']> = new Set();
|
|
33
|
+
private listener?: (payload: TMessage) => Promise<void>;
|
|
34
|
+
private options: Required<SQSSubscriberOptions>;
|
|
35
|
+
|
|
36
|
+
constructor(
|
|
37
|
+
private connection: SQSConnection,
|
|
38
|
+
options: SQSSubscriberOptions = {},
|
|
39
|
+
) {
|
|
40
|
+
this.options = {
|
|
41
|
+
waitTimeSeconds: options.waitTimeSeconds ?? 20,
|
|
42
|
+
maxMessages: options.maxMessages ?? 10,
|
|
43
|
+
expectedTopicArn: options.expectedTopicArn ?? '',
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
async subscribe(
|
|
48
|
+
messages: TMessage['type'][],
|
|
49
|
+
listener: (payload: TMessage) => Promise<void>,
|
|
50
|
+
): Promise<void> {
|
|
51
|
+
this.messageTypes = new Set(messages);
|
|
52
|
+
this.listener = listener;
|
|
53
|
+
|
|
54
|
+
if (!this.connection.isConnected()) {
|
|
55
|
+
await this.connection.connect();
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
this.polling = true;
|
|
59
|
+
this.poll();
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
stop(): void {
|
|
63
|
+
this.polling = false;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
private async poll(): Promise<void> {
|
|
67
|
+
while (this.polling) {
|
|
68
|
+
try {
|
|
69
|
+
const command = new ReceiveMessageCommand({
|
|
70
|
+
QueueUrl: this.connection.queueUrl,
|
|
71
|
+
MaxNumberOfMessages: this.options.maxMessages,
|
|
72
|
+
WaitTimeSeconds: this.options.waitTimeSeconds,
|
|
73
|
+
MessageAttributeNames: ['All'],
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
const response = await this.connection.sqsClient.send(command);
|
|
77
|
+
|
|
78
|
+
if (response.Messages && response.Messages.length > 0) {
|
|
79
|
+
await this.processMessages(response.Messages);
|
|
80
|
+
}
|
|
81
|
+
} catch (_error) {
|
|
82
|
+
await this.sleep(1000);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
private async processMessages(messages: Message[]): Promise<void> {
|
|
88
|
+
for (const message of messages) {
|
|
89
|
+
try {
|
|
90
|
+
if (!message.Body) continue;
|
|
91
|
+
|
|
92
|
+
// Try to parse as SNS message first
|
|
93
|
+
const parsedMessage = this.parseMessage(message.Body);
|
|
94
|
+
|
|
95
|
+
if (!parsedMessage) {
|
|
96
|
+
// Not a valid message format
|
|
97
|
+
await this.deleteMessage(message.ReceiptHandle!);
|
|
98
|
+
continue;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
const { payload, messageType, topicArn } = parsedMessage;
|
|
102
|
+
|
|
103
|
+
// If expectedTopicArn is set, verify the message is from that topic
|
|
104
|
+
if (this.options.expectedTopicArn && topicArn) {
|
|
105
|
+
if (topicArn !== this.options.expectedTopicArn) {
|
|
106
|
+
await this.deleteMessage(message.ReceiptHandle!);
|
|
107
|
+
continue;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// Check if we're subscribed to this message type
|
|
112
|
+
if (
|
|
113
|
+
messageType &&
|
|
114
|
+
this.messageTypes.has(messageType as TMessage['type'])
|
|
115
|
+
) {
|
|
116
|
+
// Call listener
|
|
117
|
+
if (this.listener) {
|
|
118
|
+
await this.listener(payload as TMessage);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// Delete message after successful processing
|
|
122
|
+
await this.deleteMessage(message.ReceiptHandle!);
|
|
123
|
+
} else {
|
|
124
|
+
// Delete messages we're not subscribed to
|
|
125
|
+
await this.deleteMessage(message.ReceiptHandle!);
|
|
126
|
+
}
|
|
127
|
+
} catch (_error) {
|
|
128
|
+
// Message will become visible again
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* Parse message body - handles both direct SQS messages and SNS-wrapped messages
|
|
135
|
+
*/
|
|
136
|
+
private parseMessage(body: string): {
|
|
137
|
+
payload: unknown;
|
|
138
|
+
messageType: string | undefined;
|
|
139
|
+
topicArn?: string;
|
|
140
|
+
} | null {
|
|
141
|
+
try {
|
|
142
|
+
const parsed = JSON.parse(body);
|
|
143
|
+
|
|
144
|
+
// Check if this is an SNS notification wrapper
|
|
145
|
+
if (parsed.Type === 'Notification' && parsed.Message) {
|
|
146
|
+
// This is an SNS message
|
|
147
|
+
const snsMessage = JSON.parse(parsed.Message);
|
|
148
|
+
const messageType =
|
|
149
|
+
parsed.MessageAttributes?.type?.Value || snsMessage.type;
|
|
150
|
+
|
|
151
|
+
return {
|
|
152
|
+
payload: snsMessage,
|
|
153
|
+
messageType,
|
|
154
|
+
topicArn: parsed.TopicArn,
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
// This is a direct SQS message
|
|
159
|
+
return {
|
|
160
|
+
payload: parsed,
|
|
161
|
+
messageType: parsed.type,
|
|
162
|
+
};
|
|
163
|
+
} catch (_error) {
|
|
164
|
+
return null;
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
private async deleteMessage(receiptHandle: string): Promise<void> {
|
|
169
|
+
try {
|
|
170
|
+
const command = new DeleteMessageCommand({
|
|
171
|
+
QueueUrl: this.connection.queueUrl,
|
|
172
|
+
ReceiptHandle: receiptHandle,
|
|
173
|
+
});
|
|
174
|
+
await this.connection.sqsClient.send(command);
|
|
175
|
+
} catch (_error) {}
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
private sleep(ms: number): Promise<void> {
|
|
179
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
180
|
+
}
|
|
189
181
|
}
|