@geekmidas/events 10.0.0-alpha.9 → 10.0.0-alpha.91

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.
Files changed (137) hide show
  1. package/README.md +46 -1
  2. package/dist/SQSSubscriber-BEJBfNTe.mjs +264 -0
  3. package/dist/SQSSubscriber-BEJBfNTe.mjs.map +1 -0
  4. package/dist/SQSSubscriber-COTqWvOk.cjs +288 -0
  5. package/dist/SQSSubscriber-COTqWvOk.cjs.map +1 -0
  6. package/dist/basic/index.cjs +41 -11
  7. package/dist/basic/index.cjs.map +1 -1
  8. package/dist/basic/index.d.cts +11 -10
  9. package/dist/basic/index.d.cts.map +1 -1
  10. package/dist/basic/index.d.mts +11 -10
  11. package/dist/basic/index.d.mts.map +1 -1
  12. package/dist/basic/index.mjs +39 -10
  13. package/dist/basic/index.mjs.map +1 -1
  14. package/dist/index.cjs +47 -151
  15. package/dist/index.cjs.map +1 -1
  16. package/dist/index.d.cts +82 -26
  17. package/dist/index.d.cts.map +1 -1
  18. package/dist/index.d.mts +82 -26
  19. package/dist/index.d.mts.map +1 -1
  20. package/dist/index.mjs +34 -151
  21. package/dist/index.mjs.map +1 -1
  22. package/dist/pgboss/index.cjs +198 -34
  23. package/dist/pgboss/index.cjs.map +1 -1
  24. package/dist/pgboss/index.d.cts +54 -12
  25. package/dist/pgboss/index.d.cts.map +1 -1
  26. package/dist/pgboss/index.d.mts +54 -12
  27. package/dist/pgboss/index.d.mts.map +1 -1
  28. package/dist/pgboss/index.mjs +193 -34
  29. package/dist/pgboss/index.mjs.map +1 -1
  30. package/dist/rabbitmq/index.cjs +173 -27
  31. package/dist/rabbitmq/index.cjs.map +1 -1
  32. package/dist/rabbitmq/index.d.cts +19 -10
  33. package/dist/rabbitmq/index.d.cts.map +1 -1
  34. package/dist/rabbitmq/index.d.mts +19 -10
  35. package/dist/rabbitmq/index.d.mts.map +1 -1
  36. package/dist/rabbitmq/index.mjs +169 -27
  37. package/dist/rabbitmq/index.mjs.map +1 -1
  38. package/dist/registry-DIx25Q5s.d.cts +255 -0
  39. package/dist/registry-DIx25Q5s.d.cts.map +1 -0
  40. package/dist/registry-DIx25Q5s.d.mts +255 -0
  41. package/dist/registry-DIx25Q5s.d.mts.map +1 -0
  42. package/dist/registry-DXLY1Xf6.mjs +99 -0
  43. package/dist/registry-DXLY1Xf6.mjs.map +1 -0
  44. package/dist/registry-DlT36229.cjs +122 -0
  45. package/dist/registry-DlT36229.cjs.map +1 -0
  46. package/dist/{chunk-CsX-DzYB.cjs → rolldown-runtime-C0BPl7ul.cjs} +17 -16
  47. package/dist/rolldown-runtime-D7D4PA-g.mjs +13 -0
  48. package/dist/sns/index.cjs +403 -40
  49. package/dist/sns/index.cjs.map +1 -1
  50. package/dist/sns/index.d.cts +175 -11
  51. package/dist/sns/index.d.cts.map +1 -1
  52. package/dist/sns/index.d.mts +175 -11
  53. package/dist/sns/index.d.mts.map +1 -1
  54. package/dist/sns/index.mjs +372 -24
  55. package/dist/sns/index.mjs.map +1 -1
  56. package/dist/sns/snsUrl.cjs +91 -6
  57. package/dist/sns/snsUrl.cjs.map +1 -0
  58. package/dist/sns/snsUrl.d.cts +57 -2
  59. package/dist/sns/snsUrl.d.cts.map +1 -0
  60. package/dist/sns/snsUrl.d.mts +57 -2
  61. package/dist/sns/snsUrl.d.mts.map +1 -0
  62. package/dist/sns/snsUrl.mjs +81 -2
  63. package/dist/sns/snsUrl.mjs.map +1 -0
  64. package/dist/sqs/index.cjs +31 -65
  65. package/dist/sqs/index.cjs.map +1 -1
  66. package/dist/sqs/index.d.cts +28 -11
  67. package/dist/sqs/index.d.cts.map +1 -1
  68. package/dist/sqs/index.d.mts +28 -11
  69. package/dist/sqs/index.d.mts.map +1 -1
  70. package/dist/sqs/index.mjs +22 -58
  71. package/dist/sqs/index.mjs.map +1 -1
  72. package/dist/sqs/sqsUrl.cjs +92 -6
  73. package/dist/sqs/sqsUrl.cjs.map +1 -0
  74. package/dist/sqs/sqsUrl.d.cts +56 -2
  75. package/dist/sqs/sqsUrl.d.cts.map +1 -0
  76. package/dist/sqs/sqsUrl.d.mts +56 -2
  77. package/dist/sqs/sqsUrl.d.mts.map +1 -0
  78. package/dist/sqs/sqsUrl.mjs +82 -2
  79. package/dist/sqs/sqsUrl.mjs.map +1 -0
  80. package/dist/telemetry-DG3l807O.cjs +309 -0
  81. package/dist/telemetry-DG3l807O.cjs.map +1 -0
  82. package/dist/telemetry-DjjgJMul.mjs +244 -0
  83. package/dist/telemetry-DjjgJMul.mjs.map +1 -0
  84. package/package.json +8 -3
  85. package/dist/PgBossConnection-23soZ_aM.cjs +0 -80
  86. package/dist/PgBossConnection-23soZ_aM.cjs.map +0 -1
  87. package/dist/PgBossConnection-CCXhoNzb.cjs +0 -4
  88. package/dist/PgBossConnection-DvNbd4Zk.mjs +0 -74
  89. package/dist/PgBossConnection-DvNbd4Zk.mjs.map +0 -1
  90. package/dist/PgBossConnection-_gdM40JB.mjs +0 -4
  91. package/dist/RabbitMQConnection-BKEdCUro.cjs +0 -113
  92. package/dist/RabbitMQConnection-BKEdCUro.cjs.map +0 -1
  93. package/dist/RabbitMQConnection-CCOqDDnb.cjs +0 -4
  94. package/dist/RabbitMQConnection-DJEyM-Bu.mjs +0 -4
  95. package/dist/RabbitMQConnection-DJgUPc19.mjs +0 -107
  96. package/dist/RabbitMQConnection-DJgUPc19.mjs.map +0 -1
  97. package/dist/SNSConnection-B2K86hpz.cjs +0 -75
  98. package/dist/SNSConnection-B2K86hpz.cjs.map +0 -1
  99. package/dist/SNSConnection-BVo84FeW.mjs +0 -5
  100. package/dist/SNSConnection-DIHRjrQF.cjs +0 -5
  101. package/dist/SNSConnection-v9jjF99w.mjs +0 -69
  102. package/dist/SNSConnection-v9jjF99w.mjs.map +0 -1
  103. package/dist/SQSConnection-4tBMkVZE.mjs +0 -69
  104. package/dist/SQSConnection-4tBMkVZE.mjs.map +0 -1
  105. package/dist/SQSConnection-BBLAvF-1.cjs +0 -5
  106. package/dist/SQSConnection-BaC7rGhI.cjs +0 -75
  107. package/dist/SQSConnection-BaC7rGhI.cjs.map +0 -1
  108. package/dist/SQSConnection-DbavOq8_.mjs +0 -5
  109. package/dist/SQSSubscriber-BMWZ3s5q.mjs +0 -114
  110. package/dist/SQSSubscriber-BMWZ3s5q.mjs.map +0 -1
  111. package/dist/SQSSubscriber-YXPlg2rm.cjs +0 -120
  112. package/dist/SQSSubscriber-YXPlg2rm.cjs.map +0 -1
  113. package/dist/chunk-B9dir_RE.mjs +0 -11
  114. package/dist/snsUrl-ASdc0D1v.d.cts +0 -61
  115. package/dist/snsUrl-ASdc0D1v.d.cts.map +0 -1
  116. package/dist/snsUrl-CifYhPAa.mjs +0 -79
  117. package/dist/snsUrl-CifYhPAa.mjs.map +0 -1
  118. package/dist/snsUrl-FRbRcwF0.d.mts +0 -61
  119. package/dist/snsUrl-FRbRcwF0.d.mts.map +0 -1
  120. package/dist/snsUrl-f2gU3CzK.cjs +0 -114
  121. package/dist/snsUrl-f2gU3CzK.cjs.map +0 -1
  122. package/dist/sqsUrl-BV6zS-mi.d.mts +0 -60
  123. package/dist/sqsUrl-BV6zS-mi.d.mts.map +0 -1
  124. package/dist/sqsUrl-CbAsBFHI.mjs +0 -80
  125. package/dist/sqsUrl-CbAsBFHI.mjs.map +0 -1
  126. package/dist/sqsUrl-CdisN3GU.cjs +0 -115
  127. package/dist/sqsUrl-CdisN3GU.cjs.map +0 -1
  128. package/dist/sqsUrl-DZDPHxIk.d.cts +0 -60
  129. package/dist/sqsUrl-DZDPHxIk.d.cts.map +0 -1
  130. package/dist/types-BmixGrOD.d.cts +0 -67
  131. package/dist/types-BmixGrOD.d.cts.map +0 -1
  132. package/dist/types-Byv8N9ma.cjs +0 -24
  133. package/dist/types-Byv8N9ma.cjs.map +0 -1
  134. package/dist/types-CYDt-2RQ.mjs +0 -18
  135. package/dist/types-CYDt-2RQ.mjs.map +0 -1
  136. package/dist/types-DycjsYms.d.mts +0 -67
  137. package/dist/types-DycjsYms.d.mts.map +0 -1
package/README.md CHANGED
@@ -28,8 +28,38 @@ pnpm add amqplib
28
28
 
29
29
  # For AWS SQS/SNS
30
30
  pnpm add @aws-sdk/client-sqs @aws-sdk/client-sns
31
+
32
+ # For pg-boss (PostgreSQL)
33
+ pnpm add pg-boss
31
34
  ```
32
35
 
36
+ ### Registering a Broker
37
+
38
+ Each broker is a driver on its own subpath — the only module that imports its
39
+ peer dependency — and the core uses whichever ones the process registered. A
40
+ bundle therefore contains only the brokers its entry point registers.
41
+
42
+ | Subpath | Driver |
43
+ | --- | --- |
44
+ | `@geekmidas/events/basic` | `basicEventsDriver` |
45
+ | `@geekmidas/events/pgboss` | `pgbossEventsDriver` |
46
+ | `@geekmidas/events/rabbitmq` | `rabbitmqEventsDriver` |
47
+ | `@geekmidas/events/sns` | `snsEventsDriver` |
48
+ | `@geekmidas/events/sqs` | `sqsEventsDriver` |
49
+
50
+ Code generated by `gkm` (dev, build, test, Lambda handlers) registers its
51
+ target's broker. A script that builds its own publisher registers it once:
52
+
53
+ ```typescript
54
+ import { registerEventsDriver } from '@geekmidas/events';
55
+ import { pgbossEventsDriver } from '@geekmidas/events/pgboss';
56
+
57
+ registerEventsDriver(pgbossEventsDriver);
58
+ ```
59
+
60
+ Without it, `Publisher.fromConnectionString('pgboss://…')` throws
61
+ `UnregisteredEventsScheme`, which names the subpath and the call to add.
62
+
33
63
  ## Quick Start
34
64
 
35
65
  ### 1. Define Your Message Types
@@ -46,7 +76,14 @@ type UserEvents =
46
76
  ### 2. Create Publisher and Subscriber
47
77
 
48
78
  ```typescript
49
- import { Publisher, Subscriber } from '@geekmidas/events';
79
+ import {
80
+ Publisher,
81
+ registerEventsDriver,
82
+ Subscriber,
83
+ } from '@geekmidas/events';
84
+ import { rabbitmqEventsDriver } from '@geekmidas/events/rabbitmq';
85
+
86
+ registerEventsDriver(rabbitmqEventsDriver);
50
87
 
51
88
  // Create publisher from connection string
52
89
  const publisher = await Publisher.fromConnectionString<UserEvents>(
@@ -411,6 +448,14 @@ Then use `endpoint: 'http://localhost:4566'` in your configuration.
411
448
 
412
449
  ## API Reference
413
450
 
451
+ ### Registry
452
+
453
+ ```typescript
454
+ function registerEventsDriver(driver: EventsDriver): void
455
+ function eventsDriverFor(scheme: string): EventsDriver // throws UnregisteredEventsScheme
456
+ function registeredEventsSchemes(): string[]
457
+ ```
458
+
414
459
  ### Publisher
415
460
 
416
461
  ```typescript
@@ -0,0 +1,264 @@
1
+ import { t as __exportAll } from "./rolldown-runtime-D7D4PA-g.mjs";
2
+ import { a as consumeTraced, n as carrierAsAttributes, r as carrierFromAttributes, s as publishTraced } from "./telemetry-DjjgJMul.mjs";
3
+ import { parse } from "./sqs/sqsUrl.mjs";
4
+ import { DeleteMessageCommand, ReceiveMessageCommand, SQSClient, SendMessageBatchCommand } from "@aws-sdk/client-sqs";
5
+ import chunk from "lodash.chunk";
6
+ //#region src/sqs/SQSConnection.ts
7
+ var SQSConnection_exports = /* @__PURE__ */ __exportAll({ SQSConnection: () => SQSConnection });
8
+ var SQSConnection = class SQSConnection {
9
+ config;
10
+ type = "sqs";
11
+ client;
12
+ connected = false;
13
+ constructor(config) {
14
+ this.config = config;
15
+ this.config = config;
16
+ const clientConfig = {
17
+ region: config.region,
18
+ endpoint: config.endpoint,
19
+ credentials: config.credentials
20
+ };
21
+ this.client = new SQSClient(clientConfig);
22
+ }
23
+ /**
24
+ * Create an SQSConnection from a connection string
25
+ * Format: sqs://?queueUrl=https://sqs.region.amazonaws.com/accountId/queueName&region=us-east-1&endpoint=http://localhost:4566
26
+ */
27
+ static async fromConnectionString(connectionString) {
28
+ const { queueUrl, region, endpoint } = parse(connectionString);
29
+ const params = new URL(connectionString).searchParams;
30
+ const config = {
31
+ queueUrl,
32
+ region,
33
+ endpoint
34
+ };
35
+ const accessKeyId = params.get("accessKeyId");
36
+ const secretAccessKey = params.get("secretAccessKey");
37
+ if (accessKeyId && secretAccessKey) config.credentials = {
38
+ accessKeyId,
39
+ secretAccessKey,
40
+ sessionToken: params.get("sessionToken") || void 0
41
+ };
42
+ const connection = new SQSConnection(config);
43
+ await connection.connect();
44
+ return connection;
45
+ }
46
+ async connect() {
47
+ this.connected = true;
48
+ }
49
+ async close() {
50
+ this.client.destroy();
51
+ this.connected = false;
52
+ }
53
+ isConnected() {
54
+ return this.connected;
55
+ }
56
+ /**
57
+ * Get the underlying SQS client
58
+ */
59
+ get sqsClient() {
60
+ return this.client;
61
+ }
62
+ /**
63
+ * Get the queue URL for this connection
64
+ */
65
+ get queueUrl() {
66
+ return this.config.queueUrl;
67
+ }
68
+ };
69
+ //#endregion
70
+ //#region src/sqs/SQSPublisher.ts
71
+ var SQSPublisher = class SQSPublisher {
72
+ connection;
73
+ options;
74
+ constructor(connection, options = {}) {
75
+ this.connection = connection;
76
+ this.options = { maxBatchSize: options.maxBatchSize ?? 10 };
77
+ }
78
+ /**
79
+ * Create an SQSPublisher from a connection string
80
+ * Format: sqs://?queueUrl=https://sqs.region.amazonaws.com/accountId/queueName&region=us-east-1&endpoint=http://localhost:4566&maxBatchSize=10
81
+ */
82
+ static async fromConnectionString(connectionString) {
83
+ const params = new URL(connectionString).searchParams;
84
+ const { SQSConnection } = await Promise.resolve().then(() => SQSConnection_exports);
85
+ const connection = await SQSConnection.fromConnectionString(connectionString);
86
+ const options = { maxBatchSize: params.get("maxBatchSize") ? Number.parseInt(params.get("maxBatchSize"), 10) : void 0 };
87
+ return new SQSPublisher(connection, options);
88
+ }
89
+ async publish(messages) {
90
+ if (messages.length === 0) return;
91
+ if (!this.connection.isConnected()) await this.connection.connect();
92
+ const batches = chunk(messages, this.options.maxBatchSize);
93
+ await Promise.all(batches.map((batch) => this.sendBatch(batch)));
94
+ }
95
+ async sendBatch(messages) {
96
+ const destination = sqsQueueName(this.connection.queueUrl);
97
+ await publishTraced(messages, (message) => ({
98
+ system: "aws_sqs",
99
+ destination,
100
+ type: message.type
101
+ }), (carriers) => this.send(messages.map((message, index) => ({
102
+ Id: `${index}`,
103
+ MessageBody: JSON.stringify({
104
+ type: message.type,
105
+ payload: message.payload
106
+ }),
107
+ MessageAttributes: {
108
+ type: {
109
+ DataType: "String",
110
+ StringValue: message.type
111
+ },
112
+ ...carrierAsAttributes(carriers[index])
113
+ }
114
+ }))));
115
+ }
116
+ async send(entries) {
117
+ const input = {
118
+ QueueUrl: this.connection.queueUrl,
119
+ Entries: entries
120
+ };
121
+ const command = new SendMessageBatchCommand(input);
122
+ const response = await this.connection.sqsClient.send(command);
123
+ if (response.Failed && response.Failed.length > 0) throw new SqsBatchPartlyFailed(response.Failed.map((f) => ({
124
+ id: f.Id,
125
+ code: f.Code,
126
+ message: f.Message
127
+ })));
128
+ }
129
+ async close() {}
130
+ };
131
+ /** SQS accepted the batch but refused some of its messages; the rest were sent. */
132
+ var SqsBatchPartlyFailed = class extends Error {
133
+ failed;
134
+ constructor(failed) {
135
+ super(`Failed to send ${failed.length} messages: ${failed.map((f) => `${f.id}: ${f.code} - ${f.message}`).join(", ")}. The others in the batch were sent; retry only these.`);
136
+ this.failed = failed;
137
+ this.name = "SqsBatchPartlyFailed";
138
+ }
139
+ };
140
+ /** The queue's name, the last segment of its URL. */
141
+ function sqsQueueName(queueUrl) {
142
+ return queueUrl.split("/").filter(Boolean).pop() ?? queueUrl;
143
+ }
144
+ //#endregion
145
+ //#region src/sqs/SQSSubscriber.ts
146
+ /**
147
+ * SQS Subscriber - receives messages from an SQS queue
148
+ *
149
+ * This subscriber can receive:
150
+ * 1. Direct SQS messages
151
+ * 2. SNS messages that were pushed to the queue (SNS → SQS pattern)
152
+ *
153
+ * When receiving SNS messages, it:
154
+ * - Automatically detects and parses the SNS wrapper format
155
+ * - Filters by TopicArn (if expectedTopicArn is provided)
156
+ * - Filters by message type
157
+ * - Processes the actual message payload
158
+ */
159
+ var SQSSubscriber = class {
160
+ connection;
161
+ polling = false;
162
+ messageTypes = /* @__PURE__ */ new Set();
163
+ listener;
164
+ options;
165
+ constructor(connection, options = {}) {
166
+ this.connection = connection;
167
+ this.options = {
168
+ waitTimeSeconds: options.waitTimeSeconds ?? 20,
169
+ maxMessages: options.maxMessages ?? 10,
170
+ expectedTopicArn: options.expectedTopicArn ?? ""
171
+ };
172
+ }
173
+ async subscribe(messages, listener) {
174
+ this.messageTypes = new Set(messages);
175
+ this.listener = listener;
176
+ if (!this.connection.isConnected()) await this.connection.connect();
177
+ this.polling = true;
178
+ this.poll();
179
+ }
180
+ stop() {
181
+ this.polling = false;
182
+ }
183
+ async poll() {
184
+ while (this.polling) try {
185
+ const command = new ReceiveMessageCommand({
186
+ QueueUrl: this.connection.queueUrl,
187
+ MaxNumberOfMessages: this.options.maxMessages,
188
+ WaitTimeSeconds: this.options.waitTimeSeconds,
189
+ MessageAttributeNames: ["All"]
190
+ });
191
+ const response = await this.connection.sqsClient.send(command);
192
+ if (response.Messages && response.Messages.length > 0) await this.processMessages(response.Messages);
193
+ } catch (_error) {
194
+ await this.sleep(1e3);
195
+ }
196
+ }
197
+ async processMessages(messages) {
198
+ for (const message of messages) try {
199
+ if (!message.Body) continue;
200
+ const parsedMessage = this.parseMessage(message.Body, message.MessageAttributes);
201
+ if (!parsedMessage) {
202
+ await this.deleteMessage(message.ReceiptHandle);
203
+ continue;
204
+ }
205
+ const { payload, messageType, topicArn, carrier } = parsedMessage;
206
+ if (this.options.expectedTopicArn && topicArn) {
207
+ if (topicArn !== this.options.expectedTopicArn) {
208
+ await this.deleteMessage(message.ReceiptHandle);
209
+ continue;
210
+ }
211
+ }
212
+ if (messageType && this.messageTypes.has(messageType)) {
213
+ const listener = this.listener;
214
+ if (listener) await consumeTraced({
215
+ system: "aws_sqs",
216
+ destination: sqsQueueName(this.connection.queueUrl),
217
+ type: messageType,
218
+ messageId: message.MessageId
219
+ }, carrier, () => listener(payload));
220
+ await this.deleteMessage(message.ReceiptHandle);
221
+ } else await this.deleteMessage(message.ReceiptHandle);
222
+ } catch (_error) {}
223
+ }
224
+ /**
225
+ * Parse message body - handles both direct SQS messages and SNS-wrapped messages
226
+ */
227
+ parseMessage(body, attributes) {
228
+ try {
229
+ const parsed = JSON.parse(body);
230
+ if (parsed.Type === "Notification" && parsed.Message) {
231
+ const snsMessage = JSON.parse(parsed.Message);
232
+ return {
233
+ payload: snsMessage,
234
+ messageType: parsed.MessageAttributes?.type?.Value || snsMessage.type,
235
+ topicArn: parsed.TopicArn,
236
+ carrier: carrierFromAttributes(parsed.MessageAttributes)
237
+ };
238
+ }
239
+ return {
240
+ payload: parsed,
241
+ messageType: parsed.type,
242
+ carrier: carrierFromAttributes(attributes)
243
+ };
244
+ } catch (_error) {
245
+ return null;
246
+ }
247
+ }
248
+ async deleteMessage(receiptHandle) {
249
+ try {
250
+ const command = new DeleteMessageCommand({
251
+ QueueUrl: this.connection.queueUrl,
252
+ ReceiptHandle: receiptHandle
253
+ });
254
+ await this.connection.sqsClient.send(command);
255
+ } catch (_error) {}
256
+ }
257
+ sleep(ms) {
258
+ return new Promise((resolve) => setTimeout(resolve, ms));
259
+ }
260
+ };
261
+ //#endregion
262
+ export { SQSConnection as i, SQSPublisher as n, SqsBatchPartlyFailed as r, SQSSubscriber as t };
263
+
264
+ //# sourceMappingURL=SQSSubscriber-BEJBfNTe.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SQSSubscriber-BEJBfNTe.mjs","names":["sqsUrl.parse"],"sources":["../src/sqs/SQSConnection.ts","../src/sqs/SQSPublisher.ts","../src/sqs/SQSSubscriber.ts"],"sourcesContent":["import { SQSClient, type SQSClientConfig } from '@aws-sdk/client-sqs';\nimport type { EventConnection } from '../types';\nimport { EventPublisherType } from '../types';\nimport * as sqsUrl from './sqsUrl';\n\nexport interface SQSConnectionConfig {\n\tqueueUrl: string;\n\tregion?: string;\n\tendpoint?: string; // Custom endpoint (e.g., for LocalStack)\n\tcredentials?: {\n\t\taccessKeyId: string;\n\t\tsecretAccessKey: string;\n\t\tsessionToken?: string;\n\t};\n}\n\nexport class SQSConnection implements EventConnection {\n\treadonly type = EventPublisherType.SQS;\n\tprivate client: SQSClient;\n\tprivate connected = false;\n\n\tconstructor(readonly config: SQSConnectionConfig) {\n\t\tthis.config = config;\n\n\t\tconst clientConfig: SQSClientConfig = {\n\t\t\tregion: config.region,\n\t\t\tendpoint: config.endpoint,\n\t\t\tcredentials: config.credentials,\n\t\t};\n\n\t\tthis.client = new SQSClient(clientConfig);\n\t}\n\n\t/**\n\t * Create an SQSConnection from a connection string\n\t * Format: sqs://?queueUrl=https://sqs.region.amazonaws.com/accountId/queueName&region=us-east-1&endpoint=http://localhost:4566\n\t */\n\tstatic async fromConnectionString(\n\t\tconnectionString: string,\n\t): Promise<SQSConnection> {\n\t\t// Through the codec, not a second hand-rolled parse: the deploy target\n\t\t// composes these with `sqsUrl.build`, and a string it cannot read back is\n\t\t// exactly the drift one shared module rules out.\n\t\tconst { queueUrl, region, endpoint } = sqsUrl.parse(connectionString);\n\t\tconst params = new URL(connectionString).searchParams;\n\n\t\tconst config: SQSConnectionConfig = {\n\t\t\tqueueUrl,\n\t\t\tregion,\n\t\t\tendpoint,\n\t\t};\n\n\t\t// Credentials are not part of the address — see `sqsUrl` — but a caller\n\t\t// may still pin them here, so they are read beside it rather than in it.\n\t\tconst accessKeyId = params.get('accessKeyId');\n\t\tconst secretAccessKey = params.get('secretAccessKey');\n\t\tif (accessKeyId && secretAccessKey) {\n\t\t\tconfig.credentials = {\n\t\t\t\taccessKeyId,\n\t\t\t\tsecretAccessKey,\n\t\t\t\tsessionToken: params.get('sessionToken') || undefined,\n\t\t\t};\n\t\t}\n\n\t\tconst connection = new SQSConnection(config);\n\t\tawait connection.connect();\n\t\treturn connection;\n\t}\n\n\tasync connect(): Promise<void> {\n\t\t// SQS doesn't have a traditional connection, but we mark as connected\n\t\tthis.connected = true;\n\t}\n\n\tasync close(): Promise<void> {\n\t\tthis.client.destroy();\n\t\tthis.connected = false;\n\t}\n\n\tisConnected(): boolean {\n\t\treturn this.connected;\n\t}\n\n\t/**\n\t * Get the underlying SQS client\n\t */\n\tget sqsClient(): SQSClient {\n\t\treturn this.client;\n\t}\n\n\t/**\n\t * Get the queue URL for this connection\n\t */\n\tget queueUrl(): string {\n\t\treturn this.config.queueUrl;\n\t}\n}\n","import {\n\tSendMessageBatchCommand,\n\ttype SendMessageBatchCommandInput,\n\ttype SendMessageBatchRequestEntry,\n} from '@aws-sdk/client-sqs';\nimport chunk from 'lodash.chunk';\nimport { carrierAsAttributes, publishTraced } from '../telemetry';\nimport type { EventPublisher, PublishableMessage } from '../types';\nimport type { SQSConnection } from './SQSConnection';\n\nexport interface SQSPublisherOptions {\n\tmaxBatchSize?: number; // Default: 10 (SQS limit)\n}\n\nexport class SQSPublisher<TMessage extends PublishableMessage<string, any>>\n\timplements EventPublisher<TMessage>\n{\n\treadonly options: Required<SQSPublisherOptions>;\n\n\tconstructor(\n\t\treadonly connection: SQSConnection,\n\t\toptions: SQSPublisherOptions = {},\n\t) {\n\t\tthis.options = {\n\t\t\tmaxBatchSize: options.maxBatchSize ?? 10, // SQS limit\n\t\t};\n\t}\n\n\t/**\n\t * Create an SQSPublisher from a connection string\n\t * Format: sqs://?queueUrl=https://sqs.region.amazonaws.com/accountId/queueName&region=us-east-1&endpoint=http://localhost:4566&maxBatchSize=10\n\t */\n\tstatic async fromConnectionString<\n\t\tTMessage extends PublishableMessage<string, any>,\n\t>(connectionString: string): Promise<SQSPublisher<TMessage>> {\n\t\tconst url = new URL(connectionString);\n\t\tconst params = url.searchParams;\n\n\t\tconst { SQSConnection } = await import('./SQSConnection');\n\t\tconst connection =\n\t\t\tawait SQSConnection.fromConnectionString(connectionString);\n\n\t\tconst options: SQSPublisherOptions = {\n\t\t\tmaxBatchSize: params.get('maxBatchSize')\n\t\t\t\t? Number.parseInt(params.get('maxBatchSize')!, 10)\n\t\t\t\t: undefined,\n\t\t};\n\n\t\treturn new SQSPublisher<TMessage>(connection, options);\n\t}\n\n\tasync publish(messages: TMessage[]): Promise<void> {\n\t\tif (messages.length === 0) return;\n\n\t\tif (!this.connection.isConnected()) {\n\t\t\tawait this.connection.connect();\n\t\t}\n\n\t\t// Split messages into batches (SQS limit is 10 messages per batch)\n\t\tconst batches = chunk(messages, this.options.maxBatchSize);\n\n\t\t// Send all batches\n\t\tawait Promise.all(batches.map((batch) => this.sendBatch(batch)));\n\t}\n\n\tprivate async sendBatch(messages: TMessage[]): Promise<void> {\n\t\tconst destination = sqsQueueName(this.connection.queueUrl);\n\t\tawait publishTraced(\n\t\t\tmessages,\n\t\t\t(message) => ({ system: 'aws_sqs', destination, type: message.type }),\n\t\t\t(carriers) =>\n\t\t\t\tthis.send(\n\t\t\t\t\tmessages.map((message, index) => ({\n\t\t\t\t\t\tId: `${index}`,\n\t\t\t\t\t\tMessageBody: JSON.stringify({\n\t\t\t\t\t\t\ttype: message.type,\n\t\t\t\t\t\t\tpayload: message.payload,\n\t\t\t\t\t\t}),\n\t\t\t\t\t\t// The trace context as message attributes — SQS's own header\n\t\t\t\t\t\t// field — beside the type.\n\t\t\t\t\t\tMessageAttributes: {\n\t\t\t\t\t\t\ttype: {\n\t\t\t\t\t\t\t\tDataType: 'String',\n\t\t\t\t\t\t\t\tStringValue: message.type,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t...carrierAsAttributes(carriers[index]),\n\t\t\t\t\t\t},\n\t\t\t\t\t})),\n\t\t\t\t),\n\t\t);\n\t}\n\n\tprivate async send(entries: SendMessageBatchRequestEntry[]): Promise<void> {\n\t\tconst input: SendMessageBatchCommandInput = {\n\t\t\tQueueUrl: this.connection.queueUrl,\n\t\t\tEntries: entries,\n\t\t};\n\n\t\tconst command = new SendMessageBatchCommand(input);\n\t\tconst response = await this.connection.sqsClient.send(command);\n\n\t\t// Check for failures\n\t\tif (response.Failed && response.Failed.length > 0) {\n\t\t\tthrow new SqsBatchPartlyFailed(\n\t\t\t\tresponse.Failed.map((f) => ({\n\t\t\t\t\tid: f.Id,\n\t\t\t\t\tcode: f.Code,\n\t\t\t\t\tmessage: f.Message,\n\t\t\t\t})),\n\t\t\t);\n\t\t}\n\t}\n\n\tasync close(): Promise<void> {\n\t\t// Publisher doesn't own the connection\n\t\t// Connection should be closed by whoever created it\n\t}\n}\n\n/** SQS accepted the batch but refused some of its messages; the rest were sent. */\nexport class SqsBatchPartlyFailed extends Error {\n\tconstructor(\n\t\treadonly failed: readonly {\n\t\t\tid?: string;\n\t\t\tcode?: string;\n\t\t\tmessage?: string;\n\t\t}[],\n\t) {\n\t\tsuper(\n\t\t\t`Failed to send ${failed.length} messages: ${failed.map((f) => `${f.id}: ${f.code} - ${f.message}`).join(', ')}. The others in the batch were sent; retry only these.`,\n\t\t);\n\t\tthis.name = 'SqsBatchPartlyFailed';\n\t}\n}\n\n/** The queue's name, the last segment of its URL. */\nexport function sqsQueueName(queueUrl: string): string {\n\treturn queueUrl.split('/').filter(Boolean).pop() ?? queueUrl;\n}\n","import {\n\tDeleteMessageCommand,\n\ttype Message,\n\tReceiveMessageCommand,\n} from '@aws-sdk/client-sqs';\nimport {\n\tcarrierFromAttributes,\n\tconsumeTraced,\n\ttype TraceCarrier,\n} from '../telemetry';\nimport type { EventSubscriber, PublishableMessage } from '../types';\nimport type { SQSConnection } from './SQSConnection';\nimport { sqsQueueName } from './SQSPublisher';\n\nexport interface SQSSubscriberOptions {\n\twaitTimeSeconds?: number; // SQS long polling (default: 20)\n\tmaxMessages?: number; // Max messages per poll (default: 10)\n\texpectedTopicArn?: string; // Optional: verify messages are from specific SNS topic\n}\n\n/**\n * SQS Subscriber - receives messages from an SQS queue\n *\n * This subscriber can receive:\n * 1. Direct SQS messages\n * 2. SNS messages that were pushed to the queue (SNS → SQS pattern)\n *\n * When receiving SNS messages, it:\n * - Automatically detects and parses the SNS wrapper format\n * - Filters by TopicArn (if expectedTopicArn is provided)\n * - Filters by message type\n * - Processes the actual message payload\n */\nexport class SQSSubscriber<TMessage extends PublishableMessage<string, any>>\n\timplements EventSubscriber<TMessage>\n{\n\tprivate polling = false;\n\tprivate messageTypes: Set<TMessage['type']> = new Set();\n\tprivate listener?: (payload: TMessage) => Promise<void>;\n\tprivate options: Required<SQSSubscriberOptions>;\n\n\tconstructor(\n\t\tprivate connection: SQSConnection,\n\t\toptions: SQSSubscriberOptions = {},\n\t) {\n\t\tthis.options = {\n\t\t\twaitTimeSeconds: options.waitTimeSeconds ?? 20,\n\t\t\tmaxMessages: options.maxMessages ?? 10,\n\t\t\texpectedTopicArn: options.expectedTopicArn ?? '',\n\t\t};\n\t}\n\n\tasync subscribe(\n\t\tmessages: TMessage['type'][],\n\t\tlistener: (payload: TMessage) => Promise<void>,\n\t): Promise<void> {\n\t\tthis.messageTypes = new Set(messages);\n\t\tthis.listener = listener;\n\n\t\tif (!this.connection.isConnected()) {\n\t\t\tawait this.connection.connect();\n\t\t}\n\n\t\tthis.polling = true;\n\t\tthis.poll();\n\t}\n\n\tstop(): void {\n\t\tthis.polling = false;\n\t}\n\n\tprivate async poll(): Promise<void> {\n\t\twhile (this.polling) {\n\t\t\ttry {\n\t\t\t\tconst command = new ReceiveMessageCommand({\n\t\t\t\t\tQueueUrl: this.connection.queueUrl,\n\t\t\t\t\tMaxNumberOfMessages: this.options.maxMessages,\n\t\t\t\t\tWaitTimeSeconds: this.options.waitTimeSeconds,\n\t\t\t\t\tMessageAttributeNames: ['All'],\n\t\t\t\t});\n\n\t\t\t\tconst response = await this.connection.sqsClient.send(command);\n\n\t\t\t\tif (response.Messages && response.Messages.length > 0) {\n\t\t\t\t\tawait this.processMessages(response.Messages);\n\t\t\t\t}\n\t\t\t} catch (_error) {\n\t\t\t\tawait this.sleep(1000);\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate async processMessages(messages: Message[]): Promise<void> {\n\t\tfor (const message of messages) {\n\t\t\ttry {\n\t\t\t\tif (!message.Body) continue;\n\n\t\t\t\t// Try to parse as SNS message first\n\t\t\t\tconst parsedMessage = this.parseMessage(\n\t\t\t\t\tmessage.Body,\n\t\t\t\t\tmessage.MessageAttributes,\n\t\t\t\t);\n\n\t\t\t\tif (!parsedMessage) {\n\t\t\t\t\t// Not a valid message format\n\t\t\t\t\tawait this.deleteMessage(message.ReceiptHandle!);\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\tconst { payload, messageType, topicArn, carrier } = parsedMessage;\n\n\t\t\t\t// If expectedTopicArn is set, verify the message is from that topic\n\t\t\t\tif (this.options.expectedTopicArn && topicArn) {\n\t\t\t\t\tif (topicArn !== this.options.expectedTopicArn) {\n\t\t\t\t\t\tawait this.deleteMessage(message.ReceiptHandle!);\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// Check if we're subscribed to this message type\n\t\t\t\tif (\n\t\t\t\t\tmessageType &&\n\t\t\t\t\tthis.messageTypes.has(messageType as TMessage['type'])\n\t\t\t\t) {\n\t\t\t\t\t// Call listener\n\t\t\t\t\tconst listener = this.listener;\n\t\t\t\t\tif (listener) {\n\t\t\t\t\t\tawait consumeTraced(\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tsystem: 'aws_sqs',\n\t\t\t\t\t\t\t\tdestination: sqsQueueName(this.connection.queueUrl),\n\t\t\t\t\t\t\t\ttype: messageType,\n\t\t\t\t\t\t\t\tmessageId: message.MessageId,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tcarrier,\n\t\t\t\t\t\t\t() => listener(payload as TMessage),\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\n\t\t\t\t\t// Delete message after successful processing\n\t\t\t\t\tawait this.deleteMessage(message.ReceiptHandle!);\n\t\t\t\t} else {\n\t\t\t\t\t// Delete messages we're not subscribed to\n\t\t\t\t\tawait this.deleteMessage(message.ReceiptHandle!);\n\t\t\t\t}\n\t\t\t} catch (_error) {\n\t\t\t\t// Message will become visible again\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Parse message body - handles both direct SQS messages and SNS-wrapped messages\n\t */\n\tprivate parseMessage(\n\t\tbody: string,\n\t\tattributes: Record<string, unknown> | undefined,\n\t): {\n\t\tpayload: unknown;\n\t\tmessageType: string | undefined;\n\t\ttopicArn?: string;\n\t\tcarrier?: TraceCarrier;\n\t} | null {\n\t\ttry {\n\t\t\tconst parsed = JSON.parse(body);\n\n\t\t\t// Check if this is an SNS notification wrapper\n\t\t\tif (parsed.Type === 'Notification' && parsed.Message) {\n\t\t\t\t// This is an SNS message\n\t\t\t\tconst snsMessage = JSON.parse(parsed.Message);\n\t\t\t\tconst messageType =\n\t\t\t\t\tparsed.MessageAttributes?.type?.Value || snsMessage.type;\n\n\t\t\t\treturn {\n\t\t\t\t\tpayload: snsMessage,\n\t\t\t\t\tmessageType,\n\t\t\t\t\ttopicArn: parsed.TopicArn,\n\t\t\t\t\t// SNS's message attributes, carried in its envelope.\n\t\t\t\t\tcarrier: carrierFromAttributes(parsed.MessageAttributes),\n\t\t\t\t};\n\t\t\t}\n\n\t\t\t// This is a direct SQS message\n\t\t\treturn {\n\t\t\t\tpayload: parsed,\n\t\t\t\tmessageType: parsed.type,\n\t\t\t\tcarrier: carrierFromAttributes(attributes),\n\t\t\t};\n\t\t} catch (_error) {\n\t\t\treturn null;\n\t\t}\n\t}\n\n\tprivate async deleteMessage(receiptHandle: string): Promise<void> {\n\t\ttry {\n\t\t\tconst command = new DeleteMessageCommand({\n\t\t\t\tQueueUrl: this.connection.queueUrl,\n\t\t\t\tReceiptHandle: receiptHandle,\n\t\t\t});\n\t\t\tawait this.connection.sqsClient.send(command);\n\t\t} catch (_error) {}\n\t}\n\n\tprivate sleep(ms: number): Promise<void> {\n\t\treturn new Promise((resolve) => setTimeout(resolve, ms));\n\t}\n}\n"],"mappings":"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"}
@@ -0,0 +1,288 @@
1
+ const require_rolldown_runtime = require("./rolldown-runtime-C0BPl7ul.cjs");
2
+ const require_telemetry = require("./telemetry-DG3l807O.cjs");
3
+ const require_sqs_sqsUrl = require("./sqs/sqsUrl.cjs");
4
+ let _aws_sdk_client_sqs = require("@aws-sdk/client-sqs");
5
+ let lodash_chunk = require("lodash.chunk");
6
+ lodash_chunk = require_rolldown_runtime.__toESM(lodash_chunk, 1);
7
+ //#region src/sqs/SQSConnection.ts
8
+ var SQSConnection_exports = /* @__PURE__ */ require_rolldown_runtime.__exportAll({ SQSConnection: () => SQSConnection });
9
+ var SQSConnection = class SQSConnection {
10
+ config;
11
+ type = "sqs";
12
+ client;
13
+ connected = false;
14
+ constructor(config) {
15
+ this.config = config;
16
+ this.config = config;
17
+ const clientConfig = {
18
+ region: config.region,
19
+ endpoint: config.endpoint,
20
+ credentials: config.credentials
21
+ };
22
+ this.client = new _aws_sdk_client_sqs.SQSClient(clientConfig);
23
+ }
24
+ /**
25
+ * Create an SQSConnection from a connection string
26
+ * Format: sqs://?queueUrl=https://sqs.region.amazonaws.com/accountId/queueName&region=us-east-1&endpoint=http://localhost:4566
27
+ */
28
+ static async fromConnectionString(connectionString) {
29
+ const { queueUrl, region, endpoint } = require_sqs_sqsUrl.parse(connectionString);
30
+ const params = new URL(connectionString).searchParams;
31
+ const config = {
32
+ queueUrl,
33
+ region,
34
+ endpoint
35
+ };
36
+ const accessKeyId = params.get("accessKeyId");
37
+ const secretAccessKey = params.get("secretAccessKey");
38
+ if (accessKeyId && secretAccessKey) config.credentials = {
39
+ accessKeyId,
40
+ secretAccessKey,
41
+ sessionToken: params.get("sessionToken") || void 0
42
+ };
43
+ const connection = new SQSConnection(config);
44
+ await connection.connect();
45
+ return connection;
46
+ }
47
+ async connect() {
48
+ this.connected = true;
49
+ }
50
+ async close() {
51
+ this.client.destroy();
52
+ this.connected = false;
53
+ }
54
+ isConnected() {
55
+ return this.connected;
56
+ }
57
+ /**
58
+ * Get the underlying SQS client
59
+ */
60
+ get sqsClient() {
61
+ return this.client;
62
+ }
63
+ /**
64
+ * Get the queue URL for this connection
65
+ */
66
+ get queueUrl() {
67
+ return this.config.queueUrl;
68
+ }
69
+ };
70
+ //#endregion
71
+ //#region src/sqs/SQSPublisher.ts
72
+ var SQSPublisher = class SQSPublisher {
73
+ connection;
74
+ options;
75
+ constructor(connection, options = {}) {
76
+ this.connection = connection;
77
+ this.options = { maxBatchSize: options.maxBatchSize ?? 10 };
78
+ }
79
+ /**
80
+ * Create an SQSPublisher from a connection string
81
+ * Format: sqs://?queueUrl=https://sqs.region.amazonaws.com/accountId/queueName&region=us-east-1&endpoint=http://localhost:4566&maxBatchSize=10
82
+ */
83
+ static async fromConnectionString(connectionString) {
84
+ const params = new URL(connectionString).searchParams;
85
+ const { SQSConnection } = await Promise.resolve().then(() => SQSConnection_exports);
86
+ const connection = await SQSConnection.fromConnectionString(connectionString);
87
+ const options = { maxBatchSize: params.get("maxBatchSize") ? Number.parseInt(params.get("maxBatchSize"), 10) : void 0 };
88
+ return new SQSPublisher(connection, options);
89
+ }
90
+ async publish(messages) {
91
+ if (messages.length === 0) return;
92
+ if (!this.connection.isConnected()) await this.connection.connect();
93
+ const batches = (0, lodash_chunk.default)(messages, this.options.maxBatchSize);
94
+ await Promise.all(batches.map((batch) => this.sendBatch(batch)));
95
+ }
96
+ async sendBatch(messages) {
97
+ const destination = sqsQueueName(this.connection.queueUrl);
98
+ await require_telemetry.publishTraced(messages, (message) => ({
99
+ system: "aws_sqs",
100
+ destination,
101
+ type: message.type
102
+ }), (carriers) => this.send(messages.map((message, index) => ({
103
+ Id: `${index}`,
104
+ MessageBody: JSON.stringify({
105
+ type: message.type,
106
+ payload: message.payload
107
+ }),
108
+ MessageAttributes: {
109
+ type: {
110
+ DataType: "String",
111
+ StringValue: message.type
112
+ },
113
+ ...require_telemetry.carrierAsAttributes(carriers[index])
114
+ }
115
+ }))));
116
+ }
117
+ async send(entries) {
118
+ const input = {
119
+ QueueUrl: this.connection.queueUrl,
120
+ Entries: entries
121
+ };
122
+ const command = new _aws_sdk_client_sqs.SendMessageBatchCommand(input);
123
+ const response = await this.connection.sqsClient.send(command);
124
+ if (response.Failed && response.Failed.length > 0) throw new SqsBatchPartlyFailed(response.Failed.map((f) => ({
125
+ id: f.Id,
126
+ code: f.Code,
127
+ message: f.Message
128
+ })));
129
+ }
130
+ async close() {}
131
+ };
132
+ /** SQS accepted the batch but refused some of its messages; the rest were sent. */
133
+ var SqsBatchPartlyFailed = class extends Error {
134
+ failed;
135
+ constructor(failed) {
136
+ super(`Failed to send ${failed.length} messages: ${failed.map((f) => `${f.id}: ${f.code} - ${f.message}`).join(", ")}. The others in the batch were sent; retry only these.`);
137
+ this.failed = failed;
138
+ this.name = "SqsBatchPartlyFailed";
139
+ }
140
+ };
141
+ /** The queue's name, the last segment of its URL. */
142
+ function sqsQueueName(queueUrl) {
143
+ return queueUrl.split("/").filter(Boolean).pop() ?? queueUrl;
144
+ }
145
+ //#endregion
146
+ //#region src/sqs/SQSSubscriber.ts
147
+ /**
148
+ * SQS Subscriber - receives messages from an SQS queue
149
+ *
150
+ * This subscriber can receive:
151
+ * 1. Direct SQS messages
152
+ * 2. SNS messages that were pushed to the queue (SNS → SQS pattern)
153
+ *
154
+ * When receiving SNS messages, it:
155
+ * - Automatically detects and parses the SNS wrapper format
156
+ * - Filters by TopicArn (if expectedTopicArn is provided)
157
+ * - Filters by message type
158
+ * - Processes the actual message payload
159
+ */
160
+ var SQSSubscriber = class {
161
+ connection;
162
+ polling = false;
163
+ messageTypes = /* @__PURE__ */ new Set();
164
+ listener;
165
+ options;
166
+ constructor(connection, options = {}) {
167
+ this.connection = connection;
168
+ this.options = {
169
+ waitTimeSeconds: options.waitTimeSeconds ?? 20,
170
+ maxMessages: options.maxMessages ?? 10,
171
+ expectedTopicArn: options.expectedTopicArn ?? ""
172
+ };
173
+ }
174
+ async subscribe(messages, listener) {
175
+ this.messageTypes = new Set(messages);
176
+ this.listener = listener;
177
+ if (!this.connection.isConnected()) await this.connection.connect();
178
+ this.polling = true;
179
+ this.poll();
180
+ }
181
+ stop() {
182
+ this.polling = false;
183
+ }
184
+ async poll() {
185
+ while (this.polling) try {
186
+ const command = new _aws_sdk_client_sqs.ReceiveMessageCommand({
187
+ QueueUrl: this.connection.queueUrl,
188
+ MaxNumberOfMessages: this.options.maxMessages,
189
+ WaitTimeSeconds: this.options.waitTimeSeconds,
190
+ MessageAttributeNames: ["All"]
191
+ });
192
+ const response = await this.connection.sqsClient.send(command);
193
+ if (response.Messages && response.Messages.length > 0) await this.processMessages(response.Messages);
194
+ } catch (_error) {
195
+ await this.sleep(1e3);
196
+ }
197
+ }
198
+ async processMessages(messages) {
199
+ for (const message of messages) try {
200
+ if (!message.Body) continue;
201
+ const parsedMessage = this.parseMessage(message.Body, message.MessageAttributes);
202
+ if (!parsedMessage) {
203
+ await this.deleteMessage(message.ReceiptHandle);
204
+ continue;
205
+ }
206
+ const { payload, messageType, topicArn, carrier } = parsedMessage;
207
+ if (this.options.expectedTopicArn && topicArn) {
208
+ if (topicArn !== this.options.expectedTopicArn) {
209
+ await this.deleteMessage(message.ReceiptHandle);
210
+ continue;
211
+ }
212
+ }
213
+ if (messageType && this.messageTypes.has(messageType)) {
214
+ const listener = this.listener;
215
+ if (listener) await require_telemetry.consumeTraced({
216
+ system: "aws_sqs",
217
+ destination: sqsQueueName(this.connection.queueUrl),
218
+ type: messageType,
219
+ messageId: message.MessageId
220
+ }, carrier, () => listener(payload));
221
+ await this.deleteMessage(message.ReceiptHandle);
222
+ } else await this.deleteMessage(message.ReceiptHandle);
223
+ } catch (_error) {}
224
+ }
225
+ /**
226
+ * Parse message body - handles both direct SQS messages and SNS-wrapped messages
227
+ */
228
+ parseMessage(body, attributes) {
229
+ try {
230
+ const parsed = JSON.parse(body);
231
+ if (parsed.Type === "Notification" && parsed.Message) {
232
+ const snsMessage = JSON.parse(parsed.Message);
233
+ return {
234
+ payload: snsMessage,
235
+ messageType: parsed.MessageAttributes?.type?.Value || snsMessage.type,
236
+ topicArn: parsed.TopicArn,
237
+ carrier: require_telemetry.carrierFromAttributes(parsed.MessageAttributes)
238
+ };
239
+ }
240
+ return {
241
+ payload: parsed,
242
+ messageType: parsed.type,
243
+ carrier: require_telemetry.carrierFromAttributes(attributes)
244
+ };
245
+ } catch (_error) {
246
+ return null;
247
+ }
248
+ }
249
+ async deleteMessage(receiptHandle) {
250
+ try {
251
+ const command = new _aws_sdk_client_sqs.DeleteMessageCommand({
252
+ QueueUrl: this.connection.queueUrl,
253
+ ReceiptHandle: receiptHandle
254
+ });
255
+ await this.connection.sqsClient.send(command);
256
+ } catch (_error) {}
257
+ }
258
+ sleep(ms) {
259
+ return new Promise((resolve) => setTimeout(resolve, ms));
260
+ }
261
+ };
262
+ //#endregion
263
+ Object.defineProperty(exports, "SQSConnection", {
264
+ enumerable: true,
265
+ get: function() {
266
+ return SQSConnection;
267
+ }
268
+ });
269
+ Object.defineProperty(exports, "SQSPublisher", {
270
+ enumerable: true,
271
+ get: function() {
272
+ return SQSPublisher;
273
+ }
274
+ });
275
+ Object.defineProperty(exports, "SQSSubscriber", {
276
+ enumerable: true,
277
+ get: function() {
278
+ return SQSSubscriber;
279
+ }
280
+ });
281
+ Object.defineProperty(exports, "SqsBatchPartlyFailed", {
282
+ enumerable: true,
283
+ get: function() {
284
+ return SqsBatchPartlyFailed;
285
+ }
286
+ });
287
+
288
+ //# sourceMappingURL=SQSSubscriber-COTqWvOk.cjs.map