@saga-bus/transport-sqs 0.1.2

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/README.md ADDED
@@ -0,0 +1,86 @@
1
+ # @saga-bus/transport-sqs
2
+
3
+ AWS SQS FIFO transport for saga-bus.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ pnpm add @saga-bus/transport-sqs @aws-sdk/client-sqs
9
+ ```
10
+
11
+ ## Usage
12
+
13
+ ```typescript
14
+ import { SQSClient } from "@aws-sdk/client-sqs";
15
+ import { SqsTransport } from "@saga-bus/transport-sqs";
16
+ import { createBus } from "@saga-bus/core";
17
+
18
+ const sqsClient = new SQSClient({ region: "us-east-1" });
19
+
20
+ const transport = new SqsTransport({
21
+ client: sqsClient,
22
+ queueUrl: "https://sqs.us-east-1.amazonaws.com/123456789/my-queue.fifo",
23
+ });
24
+
25
+ const bus = createBus({
26
+ transport,
27
+ sagas: [...],
28
+ });
29
+
30
+ await bus.start();
31
+ ```
32
+
33
+ ## Features
34
+
35
+ - FIFO queue support for ordered message processing
36
+ - Message deduplication via MessageDeduplicationId
37
+ - Message grouping via MessageGroupId (uses correlation ID)
38
+ - Long polling for efficient message retrieval
39
+ - Configurable visibility timeout
40
+ - Concurrent poll loops
41
+
42
+ ## FIFO Queue Requirements
43
+
44
+ This transport requires a FIFO queue (queue URL must end with `.fifo`):
45
+
46
+ ```bash
47
+ aws sqs create-queue \
48
+ --queue-name my-queue.fifo \
49
+ --attributes FifoQueue=true,ContentBasedDeduplication=false
50
+ ```
51
+
52
+ ## Message Attributes
53
+
54
+ Messages are published with:
55
+
56
+ | Attribute | Value |
57
+ |-----------|-------|
58
+ | `MessageGroupId` | Correlation ID or message ID |
59
+ | `MessageDeduplicationId` | Message ID |
60
+ | `MessageAttributes.type` | Message type |
61
+ | `MessageAttributes.correlationId` | Correlation ID |
62
+
63
+ ## Configuration
64
+
65
+ | Option | Type | Default | Description |
66
+ |--------|------|---------|-------------|
67
+ | `client` | `SQSClient` | required | AWS SQS client |
68
+ | `queueUrl` | `string` | required | FIFO queue URL |
69
+ | `maxMessages` | `number` | `10` | Messages per poll |
70
+ | `waitTimeSeconds` | `number` | `20` | Long poll wait time |
71
+ | `visibilityTimeout` | `number` | `30` | Visibility timeout (seconds) |
72
+ | `concurrency` | `number` | `1` | Concurrent poll loops |
73
+
74
+ ## Error Handling
75
+
76
+ Failed messages remain in the queue after visibility timeout expires. Configure a dead-letter queue (DLQ) for messages that repeatedly fail:
77
+
78
+ ```bash
79
+ aws sqs set-queue-attributes \
80
+ --queue-url https://sqs.../my-queue.fifo \
81
+ --attributes RedrivePolicy='{"deadLetterTargetArn":"arn:aws:sqs:...:dlq.fifo","maxReceiveCount":"3"}'
82
+ ```
83
+
84
+ ## License
85
+
86
+ MIT
package/dist/index.cjs ADDED
@@ -0,0 +1,179 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/index.ts
21
+ var index_exports = {};
22
+ __export(index_exports, {
23
+ SqsTransport: () => SqsTransport
24
+ });
25
+ module.exports = __toCommonJS(index_exports);
26
+
27
+ // src/SqsTransport.ts
28
+ var import_node_crypto = require("crypto");
29
+ var import_client_sqs = require("@aws-sdk/client-sqs");
30
+ var SqsTransport = class {
31
+ client;
32
+ queueUrl;
33
+ maxMessages;
34
+ waitTimeSeconds;
35
+ visibilityTimeout;
36
+ concurrency;
37
+ handlers = /* @__PURE__ */ new Map();
38
+ subscriptions = [];
39
+ isRunning = false;
40
+ pollLoops = [];
41
+ constructor(options) {
42
+ this.client = options.client;
43
+ this.queueUrl = options.queueUrl;
44
+ this.maxMessages = options.maxMessages ?? 10;
45
+ this.waitTimeSeconds = options.waitTimeSeconds ?? 20;
46
+ this.visibilityTimeout = options.visibilityTimeout ?? 30;
47
+ this.concurrency = options.concurrency ?? 1;
48
+ if (!this.queueUrl.endsWith(".fifo")) {
49
+ throw new Error(
50
+ "SqsTransport requires a FIFO queue (URL must end with .fifo)"
51
+ );
52
+ }
53
+ }
54
+ async start() {
55
+ if (this.isRunning) {
56
+ return;
57
+ }
58
+ this.isRunning = true;
59
+ for (let i = 0; i < this.concurrency; i++) {
60
+ this.pollLoops.push(this.pollLoop());
61
+ }
62
+ }
63
+ async stop() {
64
+ this.isRunning = false;
65
+ await Promise.all(this.pollLoops);
66
+ this.pollLoops = [];
67
+ }
68
+ async subscribe(options, handler) {
69
+ const { endpoint } = options;
70
+ this.handlers.set(
71
+ endpoint,
72
+ handler
73
+ );
74
+ this.subscriptions.push({
75
+ endpoint,
76
+ concurrency: options.concurrency ?? 1
77
+ });
78
+ }
79
+ async publish(message, options) {
80
+ const { endpoint, key, headers = {}, delayMs } = options;
81
+ const envelope = {
82
+ id: (0, import_node_crypto.randomUUID)(),
83
+ type: message.type,
84
+ payload: message,
85
+ headers: { ...headers, "x-endpoint": endpoint },
86
+ timestamp: /* @__PURE__ */ new Date(),
87
+ partitionKey: key
88
+ };
89
+ const messageGroupId = key ?? envelope.id;
90
+ const delaySeconds = delayMs ? Math.min(Math.floor(delayMs / 1e3), 900) : void 0;
91
+ await this.client.send(
92
+ new import_client_sqs.SendMessageCommand({
93
+ QueueUrl: this.queueUrl,
94
+ MessageBody: JSON.stringify(envelope),
95
+ MessageGroupId: messageGroupId,
96
+ MessageDeduplicationId: envelope.id,
97
+ DelaySeconds: delaySeconds
98
+ })
99
+ );
100
+ }
101
+ async pollLoop() {
102
+ while (this.isRunning) {
103
+ try {
104
+ await this.pollMessages();
105
+ } catch (error) {
106
+ console.error("[SQS] Poll error:", error);
107
+ await this.delay(1e3);
108
+ }
109
+ }
110
+ }
111
+ async pollMessages() {
112
+ const response = await this.client.send(
113
+ new import_client_sqs.ReceiveMessageCommand({
114
+ QueueUrl: this.queueUrl,
115
+ MaxNumberOfMessages: this.maxMessages,
116
+ WaitTimeSeconds: this.waitTimeSeconds,
117
+ VisibilityTimeout: this.visibilityTimeout,
118
+ AttributeNames: ["All"],
119
+ MessageAttributeNames: ["All"]
120
+ })
121
+ );
122
+ if (!response.Messages || response.Messages.length === 0) {
123
+ return;
124
+ }
125
+ await Promise.all(
126
+ response.Messages.map((msg) => this.processMessage(msg))
127
+ );
128
+ }
129
+ async processMessage(sqsMessage) {
130
+ if (!sqsMessage.Body || !sqsMessage.ReceiptHandle) {
131
+ return;
132
+ }
133
+ try {
134
+ const parsed = JSON.parse(sqsMessage.Body);
135
+ const envelope = {
136
+ ...parsed,
137
+ timestamp: new Date(parsed.timestamp)
138
+ };
139
+ const endpoint = envelope.headers["x-endpoint"];
140
+ const handler = endpoint ? this.handlers.get(endpoint) : void 0;
141
+ const actualHandler = handler ?? this.handlers.values().next().value;
142
+ if (actualHandler) {
143
+ await actualHandler(envelope);
144
+ }
145
+ await this.client.send(
146
+ new import_client_sqs.DeleteMessageCommand({
147
+ QueueUrl: this.queueUrl,
148
+ ReceiptHandle: sqsMessage.ReceiptHandle
149
+ })
150
+ );
151
+ } catch (error) {
152
+ console.error("[SQS] Message processing error:", error);
153
+ throw error;
154
+ }
155
+ }
156
+ delay(ms) {
157
+ return new Promise((resolve) => setTimeout(resolve, ms));
158
+ }
159
+ /**
160
+ * Check if the transport is running.
161
+ */
162
+ isStarted() {
163
+ return this.isRunning;
164
+ }
165
+ /**
166
+ * Get transport statistics.
167
+ */
168
+ getStats() {
169
+ return {
170
+ subscriptionCount: this.subscriptions.length,
171
+ isRunning: this.isRunning
172
+ };
173
+ }
174
+ };
175
+ // Annotate the CommonJS export names for ESM import in node:
176
+ 0 && (module.exports = {
177
+ SqsTransport
178
+ });
179
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.ts","../src/SqsTransport.ts"],"sourcesContent":["export { SqsTransport } from \"./SqsTransport.js\";\nexport type { SqsTransportOptions } from \"./types.js\";\n","import { randomUUID } from \"node:crypto\";\nimport {\n SendMessageCommand,\n ReceiveMessageCommand,\n DeleteMessageCommand,\n type SQSClient,\n type Message as SqsMessage,\n} from \"@aws-sdk/client-sqs\";\nimport type {\n Transport,\n TransportSubscribeOptions,\n TransportPublishOptions,\n BaseMessage,\n MessageEnvelope,\n} from \"@saga-bus/core\";\nimport type { SqsTransportOptions, SqsSubscription } from \"./types.js\";\n\n/**\n * AWS SQS FIFO transport for saga-bus.\n *\n * Uses FIFO queues to guarantee message ordering within message groups.\n * The partition key or correlation ID is used as the message group ID.\n *\n * @example\n * ```typescript\n * import { SQSClient } from \"@aws-sdk/client-sqs\";\n *\n * const client = new SQSClient({ region: \"us-east-1\" });\n * const transport = new SqsTransport({\n * client,\n * queueUrl: \"https://sqs.us-east-1.amazonaws.com/123456789/my-queue.fifo\",\n * });\n *\n * await transport.start();\n *\n * await transport.subscribe(\n * { endpoint: \"OrderSubmitted\", concurrency: 5 },\n * async (envelope) => { ... }\n * );\n *\n * await transport.publish(\n * { type: \"OrderSubmitted\", orderId: \"123\" },\n * { endpoint: \"OrderSubmitted\", key: \"order-123\" }\n * );\n * ```\n */\nexport class SqsTransport implements Transport {\n private readonly client: SQSClient;\n private readonly queueUrl: string;\n private readonly maxMessages: number;\n private readonly waitTimeSeconds: number;\n private readonly visibilityTimeout: number;\n private readonly concurrency: number;\n\n private readonly handlers = new Map<\n string,\n (envelope: MessageEnvelope) => Promise<void>\n >();\n private readonly subscriptions: SqsSubscription[] = [];\n private isRunning = false;\n private pollLoops: Promise<void>[] = [];\n\n constructor(options: SqsTransportOptions) {\n this.client = options.client;\n this.queueUrl = options.queueUrl;\n this.maxMessages = options.maxMessages ?? 10;\n this.waitTimeSeconds = options.waitTimeSeconds ?? 20;\n this.visibilityTimeout = options.visibilityTimeout ?? 30;\n this.concurrency = options.concurrency ?? 1;\n\n if (!this.queueUrl.endsWith(\".fifo\")) {\n throw new Error(\n \"SqsTransport requires a FIFO queue (URL must end with .fifo)\"\n );\n }\n }\n\n async start(): Promise<void> {\n if (this.isRunning) {\n return;\n }\n\n this.isRunning = true;\n\n // Start concurrent poll loops\n for (let i = 0; i < this.concurrency; i++) {\n this.pollLoops.push(this.pollLoop());\n }\n }\n\n async stop(): Promise<void> {\n this.isRunning = false;\n await Promise.all(this.pollLoops);\n this.pollLoops = [];\n }\n\n async subscribe<TMessage extends BaseMessage>(\n options: TransportSubscribeOptions,\n handler: (envelope: MessageEnvelope<TMessage>) => Promise<void>\n ): Promise<void> {\n const { endpoint } = options;\n\n // Store handler (SQS uses single queue, filter by endpoint in handler)\n this.handlers.set(\n endpoint,\n handler as (envelope: MessageEnvelope) => Promise<void>\n );\n\n this.subscriptions.push({\n endpoint,\n concurrency: options.concurrency ?? 1,\n });\n }\n\n async publish<TMessage extends BaseMessage>(\n message: TMessage,\n options: TransportPublishOptions\n ): Promise<void> {\n const { endpoint, key, headers = {}, delayMs } = options;\n\n // Create envelope\n const envelope: MessageEnvelope<TMessage> = {\n id: randomUUID(),\n type: message.type,\n payload: message,\n headers: { ...headers, \"x-endpoint\": endpoint },\n timestamp: new Date(),\n partitionKey: key,\n };\n\n // Message group ID for FIFO ordering\n const messageGroupId = key ?? envelope.id;\n\n // Calculate delay in seconds (SQS supports 0-900 seconds)\n const delaySeconds = delayMs ? Math.min(Math.floor(delayMs / 1000), 900) : undefined;\n\n await this.client.send(\n new SendMessageCommand({\n QueueUrl: this.queueUrl,\n MessageBody: JSON.stringify(envelope),\n MessageGroupId: messageGroupId,\n MessageDeduplicationId: envelope.id,\n DelaySeconds: delaySeconds,\n })\n );\n }\n\n private async pollLoop(): Promise<void> {\n while (this.isRunning) {\n try {\n await this.pollMessages();\n } catch (error) {\n // Log error but continue polling\n console.error(\"[SQS] Poll error:\", error);\n await this.delay(1000);\n }\n }\n }\n\n private async pollMessages(): Promise<void> {\n const response = await this.client.send(\n new ReceiveMessageCommand({\n QueueUrl: this.queueUrl,\n MaxNumberOfMessages: this.maxMessages,\n WaitTimeSeconds: this.waitTimeSeconds,\n VisibilityTimeout: this.visibilityTimeout,\n AttributeNames: [\"All\"],\n MessageAttributeNames: [\"All\"],\n })\n );\n\n if (!response.Messages || response.Messages.length === 0) {\n return;\n }\n\n await Promise.all(\n response.Messages.map((msg) => this.processMessage(msg))\n );\n }\n\n private async processMessage(sqsMessage: SqsMessage): Promise<void> {\n if (!sqsMessage.Body || !sqsMessage.ReceiptHandle) {\n return;\n }\n\n try {\n // Parse envelope\n const parsed = JSON.parse(sqsMessage.Body) as MessageEnvelope;\n\n // Reconstruct Date objects\n const envelope: MessageEnvelope = {\n ...parsed,\n timestamp: new Date(parsed.timestamp),\n };\n\n // Get endpoint from headers\n const endpoint = envelope.headers[\"x-endpoint\"];\n\n // Find handler for this endpoint\n const handler = endpoint ? this.handlers.get(endpoint) : undefined;\n\n // If no specific handler, try to find any handler (for simple cases)\n const actualHandler = handler ?? this.handlers.values().next().value;\n\n if (actualHandler) {\n await actualHandler(envelope);\n }\n\n // Delete message on successful processing\n await this.client.send(\n new DeleteMessageCommand({\n QueueUrl: this.queueUrl,\n ReceiptHandle: sqsMessage.ReceiptHandle,\n })\n );\n } catch (error) {\n // Message will return to queue after visibility timeout\n console.error(\"[SQS] Message processing error:\", error);\n throw error;\n }\n }\n\n private delay(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n }\n\n /**\n * Check if the transport is running.\n */\n isStarted(): boolean {\n return this.isRunning;\n }\n\n /**\n * Get transport statistics.\n */\n getStats(): { subscriptionCount: number; isRunning: boolean } {\n return {\n subscriptionCount: this.subscriptions.length,\n isRunning: this.isRunning,\n };\n }\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1,101 @@
1
+ import { Transport, BaseMessage, TransportSubscribeOptions, MessageEnvelope, TransportPublishOptions } from '@saga-bus/core';
2
+ import { SQSClient } from '@aws-sdk/client-sqs';
3
+
4
+ /**
5
+ * SQS transport configuration options.
6
+ */
7
+ interface SqsTransportOptions {
8
+ /**
9
+ * AWS SQS client instance.
10
+ */
11
+ client: SQSClient;
12
+ /**
13
+ * SQS FIFO queue URL.
14
+ * Must end with `.fifo`.
15
+ */
16
+ queueUrl: string;
17
+ /**
18
+ * Maximum number of messages to receive per poll.
19
+ * @default 10
20
+ */
21
+ maxMessages?: number;
22
+ /**
23
+ * Long polling wait time in seconds.
24
+ * @default 20
25
+ */
26
+ waitTimeSeconds?: number;
27
+ /**
28
+ * Visibility timeout in seconds.
29
+ * @default 30
30
+ */
31
+ visibilityTimeout?: number;
32
+ /**
33
+ * Number of concurrent poll loops.
34
+ * @default 1
35
+ */
36
+ concurrency?: number;
37
+ }
38
+
39
+ /**
40
+ * AWS SQS FIFO transport for saga-bus.
41
+ *
42
+ * Uses FIFO queues to guarantee message ordering within message groups.
43
+ * The partition key or correlation ID is used as the message group ID.
44
+ *
45
+ * @example
46
+ * ```typescript
47
+ * import { SQSClient } from "@aws-sdk/client-sqs";
48
+ *
49
+ * const client = new SQSClient({ region: "us-east-1" });
50
+ * const transport = new SqsTransport({
51
+ * client,
52
+ * queueUrl: "https://sqs.us-east-1.amazonaws.com/123456789/my-queue.fifo",
53
+ * });
54
+ *
55
+ * await transport.start();
56
+ *
57
+ * await transport.subscribe(
58
+ * { endpoint: "OrderSubmitted", concurrency: 5 },
59
+ * async (envelope) => { ... }
60
+ * );
61
+ *
62
+ * await transport.publish(
63
+ * { type: "OrderSubmitted", orderId: "123" },
64
+ * { endpoint: "OrderSubmitted", key: "order-123" }
65
+ * );
66
+ * ```
67
+ */
68
+ declare class SqsTransport implements Transport {
69
+ private readonly client;
70
+ private readonly queueUrl;
71
+ private readonly maxMessages;
72
+ private readonly waitTimeSeconds;
73
+ private readonly visibilityTimeout;
74
+ private readonly concurrency;
75
+ private readonly handlers;
76
+ private readonly subscriptions;
77
+ private isRunning;
78
+ private pollLoops;
79
+ constructor(options: SqsTransportOptions);
80
+ start(): Promise<void>;
81
+ stop(): Promise<void>;
82
+ subscribe<TMessage extends BaseMessage>(options: TransportSubscribeOptions, handler: (envelope: MessageEnvelope<TMessage>) => Promise<void>): Promise<void>;
83
+ publish<TMessage extends BaseMessage>(message: TMessage, options: TransportPublishOptions): Promise<void>;
84
+ private pollLoop;
85
+ private pollMessages;
86
+ private processMessage;
87
+ private delay;
88
+ /**
89
+ * Check if the transport is running.
90
+ */
91
+ isStarted(): boolean;
92
+ /**
93
+ * Get transport statistics.
94
+ */
95
+ getStats(): {
96
+ subscriptionCount: number;
97
+ isRunning: boolean;
98
+ };
99
+ }
100
+
101
+ export { SqsTransport, type SqsTransportOptions };
@@ -0,0 +1,101 @@
1
+ import { Transport, BaseMessage, TransportSubscribeOptions, MessageEnvelope, TransportPublishOptions } from '@saga-bus/core';
2
+ import { SQSClient } from '@aws-sdk/client-sqs';
3
+
4
+ /**
5
+ * SQS transport configuration options.
6
+ */
7
+ interface SqsTransportOptions {
8
+ /**
9
+ * AWS SQS client instance.
10
+ */
11
+ client: SQSClient;
12
+ /**
13
+ * SQS FIFO queue URL.
14
+ * Must end with `.fifo`.
15
+ */
16
+ queueUrl: string;
17
+ /**
18
+ * Maximum number of messages to receive per poll.
19
+ * @default 10
20
+ */
21
+ maxMessages?: number;
22
+ /**
23
+ * Long polling wait time in seconds.
24
+ * @default 20
25
+ */
26
+ waitTimeSeconds?: number;
27
+ /**
28
+ * Visibility timeout in seconds.
29
+ * @default 30
30
+ */
31
+ visibilityTimeout?: number;
32
+ /**
33
+ * Number of concurrent poll loops.
34
+ * @default 1
35
+ */
36
+ concurrency?: number;
37
+ }
38
+
39
+ /**
40
+ * AWS SQS FIFO transport for saga-bus.
41
+ *
42
+ * Uses FIFO queues to guarantee message ordering within message groups.
43
+ * The partition key or correlation ID is used as the message group ID.
44
+ *
45
+ * @example
46
+ * ```typescript
47
+ * import { SQSClient } from "@aws-sdk/client-sqs";
48
+ *
49
+ * const client = new SQSClient({ region: "us-east-1" });
50
+ * const transport = new SqsTransport({
51
+ * client,
52
+ * queueUrl: "https://sqs.us-east-1.amazonaws.com/123456789/my-queue.fifo",
53
+ * });
54
+ *
55
+ * await transport.start();
56
+ *
57
+ * await transport.subscribe(
58
+ * { endpoint: "OrderSubmitted", concurrency: 5 },
59
+ * async (envelope) => { ... }
60
+ * );
61
+ *
62
+ * await transport.publish(
63
+ * { type: "OrderSubmitted", orderId: "123" },
64
+ * { endpoint: "OrderSubmitted", key: "order-123" }
65
+ * );
66
+ * ```
67
+ */
68
+ declare class SqsTransport implements Transport {
69
+ private readonly client;
70
+ private readonly queueUrl;
71
+ private readonly maxMessages;
72
+ private readonly waitTimeSeconds;
73
+ private readonly visibilityTimeout;
74
+ private readonly concurrency;
75
+ private readonly handlers;
76
+ private readonly subscriptions;
77
+ private isRunning;
78
+ private pollLoops;
79
+ constructor(options: SqsTransportOptions);
80
+ start(): Promise<void>;
81
+ stop(): Promise<void>;
82
+ subscribe<TMessage extends BaseMessage>(options: TransportSubscribeOptions, handler: (envelope: MessageEnvelope<TMessage>) => Promise<void>): Promise<void>;
83
+ publish<TMessage extends BaseMessage>(message: TMessage, options: TransportPublishOptions): Promise<void>;
84
+ private pollLoop;
85
+ private pollMessages;
86
+ private processMessage;
87
+ private delay;
88
+ /**
89
+ * Check if the transport is running.
90
+ */
91
+ isStarted(): boolean;
92
+ /**
93
+ * Get transport statistics.
94
+ */
95
+ getStats(): {
96
+ subscriptionCount: number;
97
+ isRunning: boolean;
98
+ };
99
+ }
100
+
101
+ export { SqsTransport, type SqsTransportOptions };
package/dist/index.js ADDED
@@ -0,0 +1,156 @@
1
+ // src/SqsTransport.ts
2
+ import { randomUUID } from "crypto";
3
+ import {
4
+ SendMessageCommand,
5
+ ReceiveMessageCommand,
6
+ DeleteMessageCommand
7
+ } from "@aws-sdk/client-sqs";
8
+ var SqsTransport = class {
9
+ client;
10
+ queueUrl;
11
+ maxMessages;
12
+ waitTimeSeconds;
13
+ visibilityTimeout;
14
+ concurrency;
15
+ handlers = /* @__PURE__ */ new Map();
16
+ subscriptions = [];
17
+ isRunning = false;
18
+ pollLoops = [];
19
+ constructor(options) {
20
+ this.client = options.client;
21
+ this.queueUrl = options.queueUrl;
22
+ this.maxMessages = options.maxMessages ?? 10;
23
+ this.waitTimeSeconds = options.waitTimeSeconds ?? 20;
24
+ this.visibilityTimeout = options.visibilityTimeout ?? 30;
25
+ this.concurrency = options.concurrency ?? 1;
26
+ if (!this.queueUrl.endsWith(".fifo")) {
27
+ throw new Error(
28
+ "SqsTransport requires a FIFO queue (URL must end with .fifo)"
29
+ );
30
+ }
31
+ }
32
+ async start() {
33
+ if (this.isRunning) {
34
+ return;
35
+ }
36
+ this.isRunning = true;
37
+ for (let i = 0; i < this.concurrency; i++) {
38
+ this.pollLoops.push(this.pollLoop());
39
+ }
40
+ }
41
+ async stop() {
42
+ this.isRunning = false;
43
+ await Promise.all(this.pollLoops);
44
+ this.pollLoops = [];
45
+ }
46
+ async subscribe(options, handler) {
47
+ const { endpoint } = options;
48
+ this.handlers.set(
49
+ endpoint,
50
+ handler
51
+ );
52
+ this.subscriptions.push({
53
+ endpoint,
54
+ concurrency: options.concurrency ?? 1
55
+ });
56
+ }
57
+ async publish(message, options) {
58
+ const { endpoint, key, headers = {}, delayMs } = options;
59
+ const envelope = {
60
+ id: randomUUID(),
61
+ type: message.type,
62
+ payload: message,
63
+ headers: { ...headers, "x-endpoint": endpoint },
64
+ timestamp: /* @__PURE__ */ new Date(),
65
+ partitionKey: key
66
+ };
67
+ const messageGroupId = key ?? envelope.id;
68
+ const delaySeconds = delayMs ? Math.min(Math.floor(delayMs / 1e3), 900) : void 0;
69
+ await this.client.send(
70
+ new SendMessageCommand({
71
+ QueueUrl: this.queueUrl,
72
+ MessageBody: JSON.stringify(envelope),
73
+ MessageGroupId: messageGroupId,
74
+ MessageDeduplicationId: envelope.id,
75
+ DelaySeconds: delaySeconds
76
+ })
77
+ );
78
+ }
79
+ async pollLoop() {
80
+ while (this.isRunning) {
81
+ try {
82
+ await this.pollMessages();
83
+ } catch (error) {
84
+ console.error("[SQS] Poll error:", error);
85
+ await this.delay(1e3);
86
+ }
87
+ }
88
+ }
89
+ async pollMessages() {
90
+ const response = await this.client.send(
91
+ new ReceiveMessageCommand({
92
+ QueueUrl: this.queueUrl,
93
+ MaxNumberOfMessages: this.maxMessages,
94
+ WaitTimeSeconds: this.waitTimeSeconds,
95
+ VisibilityTimeout: this.visibilityTimeout,
96
+ AttributeNames: ["All"],
97
+ MessageAttributeNames: ["All"]
98
+ })
99
+ );
100
+ if (!response.Messages || response.Messages.length === 0) {
101
+ return;
102
+ }
103
+ await Promise.all(
104
+ response.Messages.map((msg) => this.processMessage(msg))
105
+ );
106
+ }
107
+ async processMessage(sqsMessage) {
108
+ if (!sqsMessage.Body || !sqsMessage.ReceiptHandle) {
109
+ return;
110
+ }
111
+ try {
112
+ const parsed = JSON.parse(sqsMessage.Body);
113
+ const envelope = {
114
+ ...parsed,
115
+ timestamp: new Date(parsed.timestamp)
116
+ };
117
+ const endpoint = envelope.headers["x-endpoint"];
118
+ const handler = endpoint ? this.handlers.get(endpoint) : void 0;
119
+ const actualHandler = handler ?? this.handlers.values().next().value;
120
+ if (actualHandler) {
121
+ await actualHandler(envelope);
122
+ }
123
+ await this.client.send(
124
+ new DeleteMessageCommand({
125
+ QueueUrl: this.queueUrl,
126
+ ReceiptHandle: sqsMessage.ReceiptHandle
127
+ })
128
+ );
129
+ } catch (error) {
130
+ console.error("[SQS] Message processing error:", error);
131
+ throw error;
132
+ }
133
+ }
134
+ delay(ms) {
135
+ return new Promise((resolve) => setTimeout(resolve, ms));
136
+ }
137
+ /**
138
+ * Check if the transport is running.
139
+ */
140
+ isStarted() {
141
+ return this.isRunning;
142
+ }
143
+ /**
144
+ * Get transport statistics.
145
+ */
146
+ getStats() {
147
+ return {
148
+ subscriptionCount: this.subscriptions.length,
149
+ isRunning: this.isRunning
150
+ };
151
+ }
152
+ };
153
+ export {
154
+ SqsTransport
155
+ };
156
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/SqsTransport.ts"],"sourcesContent":["import { randomUUID } from \"node:crypto\";\nimport {\n SendMessageCommand,\n ReceiveMessageCommand,\n DeleteMessageCommand,\n type SQSClient,\n type Message as SqsMessage,\n} from \"@aws-sdk/client-sqs\";\nimport type {\n Transport,\n TransportSubscribeOptions,\n TransportPublishOptions,\n BaseMessage,\n MessageEnvelope,\n} from \"@saga-bus/core\";\nimport type { SqsTransportOptions, SqsSubscription } from \"./types.js\";\n\n/**\n * AWS SQS FIFO transport for saga-bus.\n *\n * Uses FIFO queues to guarantee message ordering within message groups.\n * The partition key or correlation ID is used as the message group ID.\n *\n * @example\n * ```typescript\n * import { SQSClient } from \"@aws-sdk/client-sqs\";\n *\n * const client = new SQSClient({ region: \"us-east-1\" });\n * const transport = new SqsTransport({\n * client,\n * queueUrl: \"https://sqs.us-east-1.amazonaws.com/123456789/my-queue.fifo\",\n * });\n *\n * await transport.start();\n *\n * await transport.subscribe(\n * { endpoint: \"OrderSubmitted\", concurrency: 5 },\n * async (envelope) => { ... }\n * );\n *\n * await transport.publish(\n * { type: \"OrderSubmitted\", orderId: \"123\" },\n * { endpoint: \"OrderSubmitted\", key: \"order-123\" }\n * );\n * ```\n */\nexport class SqsTransport implements Transport {\n private readonly client: SQSClient;\n private readonly queueUrl: string;\n private readonly maxMessages: number;\n private readonly waitTimeSeconds: number;\n private readonly visibilityTimeout: number;\n private readonly concurrency: number;\n\n private readonly handlers = new Map<\n string,\n (envelope: MessageEnvelope) => Promise<void>\n >();\n private readonly subscriptions: SqsSubscription[] = [];\n private isRunning = false;\n private pollLoops: Promise<void>[] = [];\n\n constructor(options: SqsTransportOptions) {\n this.client = options.client;\n this.queueUrl = options.queueUrl;\n this.maxMessages = options.maxMessages ?? 10;\n this.waitTimeSeconds = options.waitTimeSeconds ?? 20;\n this.visibilityTimeout = options.visibilityTimeout ?? 30;\n this.concurrency = options.concurrency ?? 1;\n\n if (!this.queueUrl.endsWith(\".fifo\")) {\n throw new Error(\n \"SqsTransport requires a FIFO queue (URL must end with .fifo)\"\n );\n }\n }\n\n async start(): Promise<void> {\n if (this.isRunning) {\n return;\n }\n\n this.isRunning = true;\n\n // Start concurrent poll loops\n for (let i = 0; i < this.concurrency; i++) {\n this.pollLoops.push(this.pollLoop());\n }\n }\n\n async stop(): Promise<void> {\n this.isRunning = false;\n await Promise.all(this.pollLoops);\n this.pollLoops = [];\n }\n\n async subscribe<TMessage extends BaseMessage>(\n options: TransportSubscribeOptions,\n handler: (envelope: MessageEnvelope<TMessage>) => Promise<void>\n ): Promise<void> {\n const { endpoint } = options;\n\n // Store handler (SQS uses single queue, filter by endpoint in handler)\n this.handlers.set(\n endpoint,\n handler as (envelope: MessageEnvelope) => Promise<void>\n );\n\n this.subscriptions.push({\n endpoint,\n concurrency: options.concurrency ?? 1,\n });\n }\n\n async publish<TMessage extends BaseMessage>(\n message: TMessage,\n options: TransportPublishOptions\n ): Promise<void> {\n const { endpoint, key, headers = {}, delayMs } = options;\n\n // Create envelope\n const envelope: MessageEnvelope<TMessage> = {\n id: randomUUID(),\n type: message.type,\n payload: message,\n headers: { ...headers, \"x-endpoint\": endpoint },\n timestamp: new Date(),\n partitionKey: key,\n };\n\n // Message group ID for FIFO ordering\n const messageGroupId = key ?? envelope.id;\n\n // Calculate delay in seconds (SQS supports 0-900 seconds)\n const delaySeconds = delayMs ? Math.min(Math.floor(delayMs / 1000), 900) : undefined;\n\n await this.client.send(\n new SendMessageCommand({\n QueueUrl: this.queueUrl,\n MessageBody: JSON.stringify(envelope),\n MessageGroupId: messageGroupId,\n MessageDeduplicationId: envelope.id,\n DelaySeconds: delaySeconds,\n })\n );\n }\n\n private async pollLoop(): Promise<void> {\n while (this.isRunning) {\n try {\n await this.pollMessages();\n } catch (error) {\n // Log error but continue polling\n console.error(\"[SQS] Poll error:\", error);\n await this.delay(1000);\n }\n }\n }\n\n private async pollMessages(): Promise<void> {\n const response = await this.client.send(\n new ReceiveMessageCommand({\n QueueUrl: this.queueUrl,\n MaxNumberOfMessages: this.maxMessages,\n WaitTimeSeconds: this.waitTimeSeconds,\n VisibilityTimeout: this.visibilityTimeout,\n AttributeNames: [\"All\"],\n MessageAttributeNames: [\"All\"],\n })\n );\n\n if (!response.Messages || response.Messages.length === 0) {\n return;\n }\n\n await Promise.all(\n response.Messages.map((msg) => this.processMessage(msg))\n );\n }\n\n private async processMessage(sqsMessage: SqsMessage): Promise<void> {\n if (!sqsMessage.Body || !sqsMessage.ReceiptHandle) {\n return;\n }\n\n try {\n // Parse envelope\n const parsed = JSON.parse(sqsMessage.Body) as MessageEnvelope;\n\n // Reconstruct Date objects\n const envelope: MessageEnvelope = {\n ...parsed,\n timestamp: new Date(parsed.timestamp),\n };\n\n // Get endpoint from headers\n const endpoint = envelope.headers[\"x-endpoint\"];\n\n // Find handler for this endpoint\n const handler = endpoint ? this.handlers.get(endpoint) : undefined;\n\n // If no specific handler, try to find any handler (for simple cases)\n const actualHandler = handler ?? this.handlers.values().next().value;\n\n if (actualHandler) {\n await actualHandler(envelope);\n }\n\n // Delete message on successful processing\n await this.client.send(\n new DeleteMessageCommand({\n QueueUrl: this.queueUrl,\n ReceiptHandle: sqsMessage.ReceiptHandle,\n })\n );\n } catch (error) {\n // Message will return to queue after visibility timeout\n console.error(\"[SQS] Message processing error:\", error);\n throw error;\n }\n }\n\n private delay(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n }\n\n /**\n * Check if the transport is running.\n */\n isStarted(): boolean {\n return this.isRunning;\n }\n\n /**\n * Get transport statistics.\n */\n getStats(): { subscriptionCount: number; isRunning: boolean } {\n return {\n subscriptionCount: this.subscriptions.length,\n isRunning: this.isRunning,\n };\n }\n}\n"],"mappings":"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","names":[]}
package/package.json ADDED
@@ -0,0 +1,60 @@
1
+ {
2
+ "name": "@saga-bus/transport-sqs",
3
+ "version": "0.1.2",
4
+ "description": "AWS SQS transport for saga-bus",
5
+ "type": "module",
6
+ "main": "./dist/index.cjs",
7
+ "module": "./dist/index.js",
8
+ "types": "./dist/index.d.ts",
9
+ "exports": {
10
+ ".": {
11
+ "types": "./dist/index.d.ts",
12
+ "import": "./dist/index.js",
13
+ "require": "./dist/index.cjs"
14
+ }
15
+ },
16
+ "files": [
17
+ "dist",
18
+ "README.md"
19
+ ],
20
+ "publishConfig": {
21
+ "access": "public"
22
+ },
23
+ "repository": {
24
+ "type": "git",
25
+ "url": "https://github.com/d-e-a-n-f/saga-bus.git",
26
+ "directory": "packages/transport-sqs"
27
+ },
28
+ "keywords": [
29
+ "saga",
30
+ "message-bus",
31
+ "transport",
32
+ "sqs",
33
+ "aws",
34
+ "fifo"
35
+ ],
36
+ "scripts": {
37
+ "build": "tsup",
38
+ "dev": "tsup --watch",
39
+ "lint": "eslint src/",
40
+ "check-types": "tsc --noEmit",
41
+ "test": "vitest run",
42
+ "test:watch": "vitest"
43
+ },
44
+ "dependencies": {
45
+ "@saga-bus/core": "workspace:*",
46
+ "@aws-sdk/client-sqs": "^3.600.0"
47
+ },
48
+ "devDependencies": {
49
+ "@repo/eslint-config": "workspace:*",
50
+ "@repo/typescript-config": "workspace:*",
51
+ "@testcontainers/localstack": "^10.0.0",
52
+ "@types/node": "^20.0.0",
53
+ "tsup": "^8.0.0",
54
+ "typescript": "^5.9.2",
55
+ "vitest": "^3.0.0"
56
+ },
57
+ "peerDependencies": {
58
+ "@aws-sdk/client-sqs": ">=3.0.0"
59
+ }
60
+ }