@workflow-stack/sqs 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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Rahul Radhakrishnan
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,378 @@
1
+ # @workflow-stack/sqs
2
+
3
+ AWS SQS plugin for @workflow-stack/core - SQS message publishing orchestration using AWS SDK v3.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install @workflow-stack/core @workflow-stack/sqs
9
+ ```
10
+
11
+ ## Quick Start
12
+
13
+ ```javascript
14
+ import { runOrchestration, registerPlugin } from '@workflow-stack/core';
15
+ import { sqsPlugin } from '@workflow-stack/sqs';
16
+
17
+ // Register the SQS plugin
18
+ registerPlugin(sqsPlugin);
19
+
20
+ const services = [
21
+ {
22
+ id: 'sendMessage',
23
+ service: {
24
+ type: 'sqs',
25
+ queueUrl: 'https://sqs.us-east-1.amazonaws.com/123456789/my-queue',
26
+ region: 'us-east-1',
27
+ message: {
28
+ body: { userId: '{request.body.userId}', event: 'signup' }
29
+ }
30
+ }
31
+ }
32
+ ];
33
+
34
+ const result = await runOrchestration(services, {
35
+ request: { body: { userId: '123' } }
36
+ });
37
+ ```
38
+
39
+ ## SQS Service Configuration
40
+
41
+ ```typescript
42
+ interface SqsServiceConfig {
43
+ type: 'sqs';
44
+ queueUrl: string;
45
+ region: string;
46
+ credentials?: SqsAccessKeyCredentials | SqsAssumeRoleCredentials;
47
+ message: {
48
+ body: any;
49
+ messageGroupId?: string;
50
+ messageDeduplicationId?: string;
51
+ delaySeconds?: number;
52
+ messageAttributes?: Record<string, SqsMessageAttribute>;
53
+ };
54
+ retry?: {
55
+ maxAttempts?: number;
56
+ retryMode?: 'standard' | 'adaptive';
57
+ };
58
+ async?: boolean;
59
+ fallback?: {
60
+ status?: number | null;
61
+ data: any;
62
+ };
63
+ errorStrategy?: 'silent' | 'throw';
64
+ }
65
+ ```
66
+
67
+ ## Authentication
68
+
69
+ ### Using Access Key Credentials
70
+
71
+ ```javascript
72
+ {
73
+ id: 'sendMessage',
74
+ service: {
75
+ type: 'sqs',
76
+ queueUrl: 'https://sqs.us-east-1.amazonaws.com/123456789/my-queue',
77
+ region: 'us-east-1',
78
+ credentials: {
79
+ type: 'accessKey',
80
+ accessKeyId: '{env.AWS_ACCESS_KEY_ID}',
81
+ secretAccessKey: '{env.AWS_SECRET_ACCESS_KEY}',
82
+ sessionToken: '{env.AWS_SESSION_TOKEN}' // optional
83
+ },
84
+ message: {
85
+ body: 'Hello World'
86
+ }
87
+ }
88
+ }
89
+ ```
90
+
91
+ ### Using Assume Role
92
+
93
+ ```javascript
94
+ {
95
+ id: 'sendMessage',
96
+ service: {
97
+ type: 'sqs',
98
+ queueUrl: 'https://sqs.us-east-1.amazonaws.com/123456789/my-queue',
99
+ region: 'us-east-1',
100
+ credentials: {
101
+ type: 'assumeRole',
102
+ roleArn: 'arn:aws:iam::123456789012:role/SQSPublisherRole',
103
+ roleSessionName: 'flow-stack-session', // optional
104
+ externalId: 'external-id', // optional
105
+ durationSeconds: 3600 // optional
106
+ },
107
+ message: {
108
+ body: 'Hello World'
109
+ }
110
+ }
111
+ }
112
+ ```
113
+
114
+ ### Using Default Credential Chain
115
+
116
+ If no credentials are specified, the AWS SDK default credential chain is used (environment variables, shared credentials file, EC2 instance profile, etc.).
117
+
118
+ ```javascript
119
+ {
120
+ id: 'sendMessage',
121
+ service: {
122
+ type: 'sqs',
123
+ queueUrl: 'https://sqs.us-east-1.amazonaws.com/123456789/my-queue',
124
+ region: 'us-east-1',
125
+ message: {
126
+ body: 'Hello World'
127
+ }
128
+ }
129
+ }
130
+ ```
131
+
132
+ ## Examples
133
+
134
+ ### Basic Message
135
+
136
+ ```javascript
137
+ {
138
+ id: 'notify',
139
+ service: {
140
+ type: 'sqs',
141
+ queueUrl: 'https://sqs.us-east-1.amazonaws.com/123456789/notifications',
142
+ region: 'us-east-1',
143
+ message: {
144
+ body: {
145
+ userId: '{request.body.userId}',
146
+ action: 'user_created',
147
+ timestamp: '{request.body.timestamp}'
148
+ }
149
+ }
150
+ }
151
+ }
152
+ ```
153
+
154
+ ### FIFO Queue
155
+
156
+ ```javascript
157
+ {
158
+ id: 'orderEvent',
159
+ service: {
160
+ type: 'sqs',
161
+ queueUrl: 'https://sqs.us-east-1.amazonaws.com/123456789/orders.fifo',
162
+ region: 'us-east-1',
163
+ message: {
164
+ body: { orderId: '{request.body.orderId}', status: 'placed' },
165
+ messageGroupId: '{request.body.orderId}',
166
+ messageDeduplicationId: '{request.body.eventId}'
167
+ }
168
+ }
169
+ }
170
+ ```
171
+
172
+ ### Delayed Message
173
+
174
+ ```javascript
175
+ {
176
+ id: 'scheduledTask',
177
+ service: {
178
+ type: 'sqs',
179
+ queueUrl: 'https://sqs.us-east-1.amazonaws.com/123456789/tasks',
180
+ region: 'us-east-1',
181
+ message: {
182
+ body: { task: 'send_reminder', userId: '123' },
183
+ delaySeconds: 300 // 5 minutes delay
184
+ }
185
+ }
186
+ }
187
+ ```
188
+
189
+ ### Message with Attributes
190
+
191
+ ```javascript
192
+ {
193
+ id: 'eventWithMetadata',
194
+ service: {
195
+ type: 'sqs',
196
+ queueUrl: 'https://sqs.us-east-1.amazonaws.com/123456789/events',
197
+ region: 'us-east-1',
198
+ message: {
199
+ body: { event: 'user_signup' },
200
+ messageAttributes: {
201
+ correlationId: {
202
+ DataType: 'String',
203
+ StringValue: '{request.headers.x-correlation-id}'
204
+ },
205
+ priority: {
206
+ DataType: 'Number',
207
+ StringValue: '1'
208
+ }
209
+ }
210
+ }
211
+ }
212
+ }
213
+ ```
214
+
215
+ ### With Retry Configuration
216
+
217
+ ```javascript
218
+ {
219
+ id: 'reliableMessage',
220
+ service: {
221
+ type: 'sqs',
222
+ queueUrl: 'https://sqs.us-east-1.amazonaws.com/123456789/critical',
223
+ region: 'us-east-1',
224
+ message: {
225
+ body: { event: 'payment_processed' }
226
+ },
227
+ retry: {
228
+ maxAttempts: 5,
229
+ retryMode: 'adaptive'
230
+ }
231
+ }
232
+ }
233
+ ```
234
+
235
+ ### Async (Fire-and-Forget) Mode
236
+
237
+ ```javascript
238
+ {
239
+ id: 'asyncNotification',
240
+ service: {
241
+ type: 'sqs',
242
+ queueUrl: 'https://sqs.us-east-1.amazonaws.com/123456789/notifications',
243
+ region: 'us-east-1',
244
+ message: {
245
+ body: { event: 'page_viewed' }
246
+ },
247
+ async: true // Returns immediately without waiting for SQS response
248
+ }
249
+ }
250
+ ```
251
+
252
+ ### With Fallback
253
+
254
+ ```javascript
255
+ {
256
+ id: 'messageWithFallback',
257
+ service: {
258
+ type: 'sqs',
259
+ queueUrl: 'https://sqs.us-east-1.amazonaws.com/123456789/my-queue',
260
+ region: 'us-east-1',
261
+ message: {
262
+ body: { event: 'user_action' }
263
+ },
264
+ fallback: {
265
+ status: 200,
266
+ data: { queued: false, reason: 'SQS unavailable' }
267
+ }
268
+ }
269
+ }
270
+ ```
271
+
272
+ ### Using Dependencies
273
+
274
+ ```javascript
275
+ const services = [
276
+ {
277
+ id: 'createOrder',
278
+ service: {
279
+ type: 'rest',
280
+ url: 'https://api.example.com/orders',
281
+ method: 'POST',
282
+ body: { items: '{request.body.items}' }
283
+ }
284
+ },
285
+ {
286
+ id: 'notifyWarehouse',
287
+ dependsOn: ['createOrder'],
288
+ service: {
289
+ type: 'sqs',
290
+ queueUrl: 'https://sqs.us-east-1.amazonaws.com/123456789/warehouse',
291
+ region: 'us-east-1',
292
+ message: {
293
+ body: {
294
+ orderId: '{createOrder.body.id}',
295
+ action: 'prepare_shipment'
296
+ }
297
+ }
298
+ }
299
+ }
300
+ ];
301
+ ```
302
+
303
+ ## Sync vs Async Mode
304
+
305
+ | Mode | `async` | Behavior | Response |
306
+ |------|---------|----------|----------|
307
+ | Sync (default) | `false` or omitted | Waits for SQS response | `{ status: 200, body: { messageId, sequenceNumber? } }` |
308
+ | Async | `true` | Returns immediately | `{ status: 202, body: { message: 'Message queued for delivery', async: true } }` |
309
+
310
+ ## Response Structure
311
+
312
+ ### Successful Response (Sync)
313
+
314
+ ```typescript
315
+ {
316
+ status: 200,
317
+ body: {
318
+ messageId: 'abc123-def456-...',
319
+ sequenceNumber: '12345...' // Only for FIFO queues
320
+ },
321
+ metadata: {
322
+ executionStatus: 'executed',
323
+ serviceType: 'sqs'
324
+ }
325
+ }
326
+ ```
327
+
328
+ ### Successful Response (Async)
329
+
330
+ ```typescript
331
+ {
332
+ status: 202,
333
+ body: {
334
+ message: 'Message queued for delivery',
335
+ async: true
336
+ },
337
+ metadata: {
338
+ executionStatus: 'executed',
339
+ serviceType: 'sqs'
340
+ }
341
+ }
342
+ ```
343
+
344
+ ### Error Response
345
+
346
+ ```typescript
347
+ {
348
+ status: null,
349
+ body: null,
350
+ error: {
351
+ message: 'Access Denied',
352
+ code: 'AccessDeniedException'
353
+ },
354
+ metadata: {
355
+ executionStatus: 'failed',
356
+ serviceType: 'sqs'
357
+ }
358
+ }
359
+ ```
360
+
361
+ ## TypeScript Support
362
+
363
+ ```typescript
364
+ import type {
365
+ SqsServiceConfig,
366
+ SqsCredentials,
367
+ SqsAccessKeyCredentials,
368
+ SqsAssumeRoleCredentials,
369
+ SqsRetryConfig,
370
+ SqsMessageAttribute
371
+ } from '@workflow-stack/sqs';
372
+
373
+ import { sqsPlugin, executeSqsService } from '@workflow-stack/sqs';
374
+ ```
375
+
376
+ ## License
377
+
378
+ MIT
@@ -0,0 +1,4 @@
1
+ export { sqsPlugin } from './plugin.js';
2
+ export { executeSqsService } from './sqs-executor.js';
3
+ export type { SqsServiceConfig, SqsCredentials, SqsAccessKeyCredentials, SqsAssumeRoleCredentials, SqsRetryConfig, SqsMessageAttribute, } from './types.js';
4
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAGxC,OAAO,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AAGtD,YAAY,EACV,gBAAgB,EAChB,cAAc,EACd,uBAAuB,EACvB,wBAAwB,EACxB,cAAc,EACd,mBAAmB,GACpB,MAAM,YAAY,CAAC"}
@@ -0,0 +1,3 @@
1
+ export { sqsPlugin } from './plugin.js';
2
+ export { executeSqsService } from './sqs-executor.js';
3
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAGxC,OAAO,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC"}
@@ -0,0 +1,4 @@
1
+ import type { FlowStackPlugin } from '@workflow-stack/core';
2
+ import type { SqsServiceConfig } from './types.js';
3
+ export declare const sqsPlugin: FlowStackPlugin<SqsServiceConfig>;
4
+ //# sourceMappingURL=plugin.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../../src/plugin.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAC5D,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAOnD,eAAO,MAAM,SAAS,EAAE,eAAe,CAAC,gBAAgB,CAMvD,CAAC"}
@@ -0,0 +1,9 @@
1
+ import { executeSqsService } from './sqs-executor.js';
2
+ export const sqsPlugin = {
3
+ type: 'sqs',
4
+ name: '@workflow-stack/sqs',
5
+ version: '1.0.0',
6
+ execute: executeSqsService,
7
+ interpolate: true,
8
+ };
9
+ //# sourceMappingURL=plugin.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plugin.js","sourceRoot":"","sources":["../../src/plugin.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AAMtD,MAAM,CAAC,MAAM,SAAS,GAAsC;IAC1D,IAAI,EAAE,KAAK;IACX,IAAI,EAAE,qBAAqB;IAC3B,OAAO,EAAE,OAAO;IAChB,OAAO,EAAE,iBAAiB;IAC1B,WAAW,EAAE,IAAI;CAClB,CAAC"}
@@ -0,0 +1,4 @@
1
+ import type { ServiceResult, OrchestrationContext } from '@workflow-stack/core';
2
+ import type { SqsServiceConfig } from './types.js';
3
+ export declare function executeSqsService(config: SqsServiceConfig, context: OrchestrationContext, serviceId: string): Promise<ServiceResult>;
4
+ //# sourceMappingURL=sqs-executor.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sqs-executor.d.ts","sourceRoot":"","sources":["../../src/sqs-executor.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,aAAa,EAAE,oBAAoB,EAAE,MAAM,sBAAsB,CAAC;AAEhF,OAAO,KAAK,EAAE,gBAAgB,EAA4B,MAAM,YAAY,CAAC;AAsE7E,wBAAsB,iBAAiB,CACrC,MAAM,EAAE,gBAAgB,EACxB,OAAO,EAAE,oBAAoB,EAC7B,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,aAAa,CAAC,CAoFxB"}
@@ -0,0 +1,123 @@
1
+ import { SQSClient, SendMessageCommand } from '@aws-sdk/client-sqs';
2
+ import { fromTemporaryCredentials } from '@aws-sdk/credential-providers';
3
+ import { emitServiceStart, emitServiceComplete, emitServiceError } from '@workflow-stack/core';
4
+ function createSqsClient(config) {
5
+ const clientConfig = {
6
+ region: config.region,
7
+ };
8
+ if (config.retry) {
9
+ clientConfig.maxAttempts = config.retry.maxAttempts;
10
+ clientConfig.retryMode = config.retry.retryMode;
11
+ }
12
+ if (config.credentials) {
13
+ if (config.credentials.type === 'accessKey') {
14
+ clientConfig.credentials = {
15
+ accessKeyId: config.credentials.accessKeyId,
16
+ secretAccessKey: config.credentials.secretAccessKey,
17
+ sessionToken: config.credentials.sessionToken,
18
+ };
19
+ }
20
+ else if (config.credentials.type === 'assumeRole') {
21
+ const roleConfig = config.credentials;
22
+ clientConfig.credentials = fromTemporaryCredentials({
23
+ params: {
24
+ RoleArn: roleConfig.roleArn,
25
+ RoleSessionName: roleConfig.roleSessionName || 'flow-stack-sqs-session',
26
+ ExternalId: roleConfig.externalId,
27
+ DurationSeconds: roleConfig.durationSeconds,
28
+ },
29
+ });
30
+ }
31
+ }
32
+ return new SQSClient(clientConfig);
33
+ }
34
+ async function sendMessage(client, config) {
35
+ const messageBody = typeof config.message.body === 'string'
36
+ ? config.message.body
37
+ : JSON.stringify(config.message.body);
38
+ const command = new SendMessageCommand({
39
+ QueueUrl: config.queueUrl,
40
+ MessageBody: messageBody,
41
+ MessageGroupId: config.message.messageGroupId,
42
+ MessageDeduplicationId: config.message.messageDeduplicationId,
43
+ DelaySeconds: config.message.delaySeconds,
44
+ MessageAttributes: config.message.messageAttributes,
45
+ });
46
+ const response = await client.send(command);
47
+ return {
48
+ messageId: response.MessageId,
49
+ sequenceNumber: response.SequenceNumber,
50
+ };
51
+ }
52
+ export async function executeSqsService(config, context, serviceId) {
53
+ const startTime = Date.now();
54
+ emitServiceStart(serviceId, 'sqs', config, context);
55
+ const client = createSqsClient(config);
56
+ try {
57
+ const isAsync = config.async === true;
58
+ if (isAsync) {
59
+ sendMessage(client, config).catch((error) => {
60
+ const processingTime = Date.now() - startTime;
61
+ emitServiceError(serviceId, 'sqs', config, context, processingTime, error);
62
+ });
63
+ const processingTime = Date.now() - startTime;
64
+ emitServiceComplete(serviceId, 'sqs', config, context, processingTime, 202);
65
+ return {
66
+ status: 202,
67
+ body: {
68
+ message: 'Message queued for delivery',
69
+ async: true,
70
+ },
71
+ metadata: {
72
+ executionStatus: 'executed',
73
+ serviceType: 'sqs',
74
+ },
75
+ };
76
+ }
77
+ const result = await sendMessage(client, config);
78
+ const processingTime = Date.now() - startTime;
79
+ emitServiceComplete(serviceId, 'sqs', config, context, processingTime, 200);
80
+ return {
81
+ status: 200,
82
+ body: {
83
+ messageId: result.messageId,
84
+ sequenceNumber: result.sequenceNumber,
85
+ },
86
+ metadata: {
87
+ executionStatus: 'executed',
88
+ serviceType: 'sqs',
89
+ },
90
+ };
91
+ }
92
+ catch (error) {
93
+ const processingTime = Date.now() - startTime;
94
+ emitServiceError(serviceId, 'sqs', config, context, processingTime, error);
95
+ if (config.fallback) {
96
+ return {
97
+ status: config.fallback.status || null,
98
+ body: config.fallback.data,
99
+ metadata: {
100
+ executionStatus: 'failed',
101
+ fallbackUsed: true,
102
+ serviceType: 'sqs',
103
+ },
104
+ };
105
+ }
106
+ return {
107
+ status: null,
108
+ body: null,
109
+ error: {
110
+ message: error.message,
111
+ code: error.code || error.name,
112
+ },
113
+ metadata: {
114
+ executionStatus: 'failed',
115
+ serviceType: 'sqs',
116
+ },
117
+ };
118
+ }
119
+ finally {
120
+ client.destroy();
121
+ }
122
+ }
123
+ //# sourceMappingURL=sqs-executor.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sqs-executor.js","sourceRoot":"","sources":["../../src/sqs-executor.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,40 @@
1
+ import type { BaseServiceConfig } from '@workflow-stack/core';
2
+ export interface SqsAccessKeyCredentials {
3
+ type: 'accessKey';
4
+ accessKeyId: string;
5
+ secretAccessKey: string;
6
+ sessionToken?: string;
7
+ }
8
+ export interface SqsAssumeRoleCredentials {
9
+ type: 'assumeRole';
10
+ roleArn: string;
11
+ roleSessionName?: string;
12
+ externalId?: string;
13
+ durationSeconds?: number;
14
+ }
15
+ export type SqsCredentials = SqsAccessKeyCredentials | SqsAssumeRoleCredentials;
16
+ export interface SqsRetryConfig {
17
+ maxAttempts?: number;
18
+ retryMode?: 'standard' | 'adaptive';
19
+ }
20
+ export interface SqsMessageAttribute {
21
+ DataType: 'String' | 'Number' | 'Binary';
22
+ StringValue?: string;
23
+ BinaryValue?: Uint8Array;
24
+ }
25
+ export interface SqsServiceConfig extends BaseServiceConfig {
26
+ type: 'sqs';
27
+ queueUrl: string;
28
+ region: string;
29
+ credentials?: SqsCredentials;
30
+ message: {
31
+ body: any;
32
+ messageGroupId?: string;
33
+ messageDeduplicationId?: string;
34
+ delaySeconds?: number;
35
+ messageAttributes?: Record<string, SqsMessageAttribute>;
36
+ };
37
+ retry?: SqsRetryConfig;
38
+ async?: boolean;
39
+ }
40
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAC;AAK9D,MAAM,WAAW,uBAAuB;IACtC,IAAI,EAAE,WAAW,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,eAAe,EAAE,MAAM,CAAC;IACxB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAKD,MAAM,WAAW,wBAAwB;IACvC,IAAI,EAAE,YAAY,CAAC;IACnB,OAAO,EAAE,MAAM,CAAC;IAChB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B;AAKD,MAAM,MAAM,cAAc,GAAG,uBAAuB,GAAG,wBAAwB,CAAC;AAKhF,MAAM,WAAW,cAAc;IAC7B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,SAAS,CAAC,EAAE,UAAU,GAAG,UAAU,CAAC;CACrC;AAKD,MAAM,WAAW,mBAAmB;IAClC,QAAQ,EAAE,QAAQ,GAAG,QAAQ,GAAG,QAAQ,CAAC;IACzC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,WAAW,CAAC,EAAE,UAAU,CAAC;CAC1B;AAKD,MAAM,WAAW,gBAAiB,SAAQ,iBAAiB;IACzD,IAAI,EAAE,KAAK,CAAC;IACZ,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,cAAc,CAAC;IAC7B,OAAO,EAAE;QACP,IAAI,EAAE,GAAG,CAAC;QACV,cAAc,CAAC,EAAE,MAAM,CAAC;QACxB,sBAAsB,CAAC,EAAE,MAAM,CAAC;QAChC,YAAY,CAAC,EAAE,MAAM,CAAC;QACtB,iBAAiB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,mBAAmB,CAAC,CAAC;KACzD,CAAC;IACF,KAAK,CAAC,EAAE,cAAc,CAAC;IACvB,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/types.ts"],"names":[],"mappings":""}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=sqs-executor.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sqs-executor.test.d.ts","sourceRoot":"","sources":["../../tests/sqs-executor.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,205 @@
1
+ import { describe, it } from 'node:test';
2
+ import assert from 'node:assert';
3
+ import { sqsPlugin } from '../src/plugin.js';
4
+ describe('SQS Plugin', () => {
5
+ describe('Plugin definition', () => {
6
+ it('should have correct type identifier', () => {
7
+ assert.strictEqual(sqsPlugin.type, 'sqs');
8
+ });
9
+ it('should have correct name', () => {
10
+ assert.strictEqual(sqsPlugin.name, '@workflow-stack/sqs');
11
+ });
12
+ it('should have interpolation enabled', () => {
13
+ assert.strictEqual(sqsPlugin.interpolate, true);
14
+ });
15
+ it('should have an execute function', () => {
16
+ assert.strictEqual(typeof sqsPlugin.execute, 'function');
17
+ });
18
+ });
19
+ });
20
+ describe('SQS Types', () => {
21
+ describe('SqsServiceConfig', () => {
22
+ it('should accept valid access key credentials config', () => {
23
+ const config = {
24
+ type: 'sqs',
25
+ queueUrl: 'https://sqs.us-east-1.amazonaws.com/123456789/my-queue',
26
+ region: 'us-east-1',
27
+ credentials: {
28
+ type: 'accessKey',
29
+ accessKeyId: 'AKIAIOSFODNN7EXAMPLE',
30
+ secretAccessKey: 'wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY',
31
+ },
32
+ message: {
33
+ body: 'Test message',
34
+ },
35
+ };
36
+ assert.strictEqual(config.type, 'sqs');
37
+ assert.strictEqual(config.credentials?.type, 'accessKey');
38
+ });
39
+ it('should accept valid assume role credentials config', () => {
40
+ const config = {
41
+ type: 'sqs',
42
+ queueUrl: 'https://sqs.us-east-1.amazonaws.com/123456789/my-queue',
43
+ region: 'us-east-1',
44
+ credentials: {
45
+ type: 'assumeRole',
46
+ roleArn: 'arn:aws:iam::123456789012:role/MyRole',
47
+ roleSessionName: 'my-session',
48
+ externalId: 'external-123',
49
+ durationSeconds: 3600,
50
+ },
51
+ message: {
52
+ body: 'Test message',
53
+ },
54
+ };
55
+ assert.strictEqual(config.type, 'sqs');
56
+ assert.strictEqual(config.credentials?.type, 'assumeRole');
57
+ });
58
+ it('should accept config without explicit credentials (uses default chain)', () => {
59
+ const config = {
60
+ type: 'sqs',
61
+ queueUrl: 'https://sqs.us-east-1.amazonaws.com/123456789/my-queue',
62
+ region: 'us-east-1',
63
+ message: {
64
+ body: 'Test message',
65
+ },
66
+ };
67
+ assert.strictEqual(config.type, 'sqs');
68
+ assert.strictEqual(config.credentials, undefined);
69
+ });
70
+ it('should accept retry configuration', () => {
71
+ const config = {
72
+ type: 'sqs',
73
+ queueUrl: 'https://sqs.us-east-1.amazonaws.com/123456789/my-queue',
74
+ region: 'us-east-1',
75
+ message: {
76
+ body: 'Test message',
77
+ },
78
+ retry: {
79
+ maxAttempts: 5,
80
+ retryMode: 'adaptive',
81
+ },
82
+ };
83
+ assert.strictEqual(config.retry?.maxAttempts, 5);
84
+ assert.strictEqual(config.retry?.retryMode, 'adaptive');
85
+ });
86
+ it('should accept async mode configuration', () => {
87
+ const configAsync = {
88
+ type: 'sqs',
89
+ queueUrl: 'https://sqs.us-east-1.amazonaws.com/123456789/my-queue',
90
+ region: 'us-east-1',
91
+ message: {
92
+ body: 'Test message',
93
+ },
94
+ async: true,
95
+ };
96
+ const configSync = {
97
+ type: 'sqs',
98
+ queueUrl: 'https://sqs.us-east-1.amazonaws.com/123456789/my-queue',
99
+ region: 'us-east-1',
100
+ message: {
101
+ body: 'Test message',
102
+ },
103
+ async: false,
104
+ };
105
+ assert.strictEqual(configAsync.async, true);
106
+ assert.strictEqual(configSync.async, false);
107
+ });
108
+ it('should accept FIFO queue configuration', () => {
109
+ const config = {
110
+ type: 'sqs',
111
+ queueUrl: 'https://sqs.us-east-1.amazonaws.com/123456789/my-queue.fifo',
112
+ region: 'us-east-1',
113
+ message: {
114
+ body: 'FIFO message',
115
+ messageGroupId: 'group-1',
116
+ messageDeduplicationId: 'dedup-1',
117
+ },
118
+ };
119
+ assert.strictEqual(config.message.messageGroupId, 'group-1');
120
+ assert.strictEqual(config.message.messageDeduplicationId, 'dedup-1');
121
+ });
122
+ it('should accept message with delay seconds', () => {
123
+ const config = {
124
+ type: 'sqs',
125
+ queueUrl: 'https://sqs.us-east-1.amazonaws.com/123456789/my-queue',
126
+ region: 'us-east-1',
127
+ message: {
128
+ body: 'Delayed message',
129
+ delaySeconds: 60,
130
+ },
131
+ };
132
+ assert.strictEqual(config.message.delaySeconds, 60);
133
+ });
134
+ it('should accept message with attributes', () => {
135
+ const config = {
136
+ type: 'sqs',
137
+ queueUrl: 'https://sqs.us-east-1.amazonaws.com/123456789/my-queue',
138
+ region: 'us-east-1',
139
+ message: {
140
+ body: 'Message with attributes',
141
+ messageAttributes: {
142
+ correlationId: {
143
+ DataType: 'String',
144
+ StringValue: 'corr-123',
145
+ },
146
+ priority: {
147
+ DataType: 'Number',
148
+ StringValue: '1',
149
+ },
150
+ },
151
+ },
152
+ };
153
+ assert.strictEqual(config.message.messageAttributes?.correlationId?.DataType, 'String');
154
+ });
155
+ it('should accept fallback configuration', () => {
156
+ const config = {
157
+ type: 'sqs',
158
+ queueUrl: 'https://sqs.us-east-1.amazonaws.com/123456789/my-queue',
159
+ region: 'us-east-1',
160
+ message: {
161
+ body: 'Test message',
162
+ },
163
+ fallback: {
164
+ status: 200,
165
+ data: { fallback: true, queued: false },
166
+ },
167
+ };
168
+ assert.strictEqual(config.fallback?.status, 200);
169
+ assert.deepStrictEqual(config.fallback?.data, { fallback: true, queued: false });
170
+ });
171
+ it('should accept JSON object as message body', () => {
172
+ const config = {
173
+ type: 'sqs',
174
+ queueUrl: 'https://sqs.us-east-1.amazonaws.com/123456789/my-queue',
175
+ region: 'us-east-1',
176
+ message: {
177
+ body: { userId: 1, action: 'create', timestamp: new Date().toISOString() },
178
+ },
179
+ };
180
+ assert.strictEqual(typeof config.message.body, 'object');
181
+ assert.strictEqual(config.message.body.userId, 1);
182
+ });
183
+ it('should accept access key credentials with session token', () => {
184
+ const config = {
185
+ type: 'sqs',
186
+ queueUrl: 'https://sqs.us-east-1.amazonaws.com/123456789/my-queue',
187
+ region: 'us-east-1',
188
+ credentials: {
189
+ type: 'accessKey',
190
+ accessKeyId: 'AKIAIOSFODNN7EXAMPLE',
191
+ secretAccessKey: 'wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY',
192
+ sessionToken: 'AQoDYXdzEJr...',
193
+ },
194
+ message: {
195
+ body: 'Test message',
196
+ },
197
+ };
198
+ assert.strictEqual(config.credentials?.type, 'accessKey');
199
+ if (config.credentials?.type === 'accessKey') {
200
+ assert.strictEqual(config.credentials.sessionToken, 'AQoDYXdzEJr...');
201
+ }
202
+ });
203
+ });
204
+ });
205
+ //# sourceMappingURL=sqs-executor.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sqs-executor.test.js","sourceRoot":"","sources":["../../tests/sqs-executor.test.ts"],"names":[],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,72 @@
1
+ {
2
+ "name": "@workflow-stack/sqs",
3
+ "version": "0.2.0",
4
+ "description": "SQS plugin for @workflow-stack/core - AWS SQS message publishing orchestration",
5
+ "main": "dist/src/index.js",
6
+ "types": "dist/src/index.d.ts",
7
+ "type": "module",
8
+ "private": false,
9
+ "exports": {
10
+ ".": {
11
+ "types": "./dist/src/index.d.ts",
12
+ "default": "./dist/src/index.js"
13
+ }
14
+ },
15
+ "files": [
16
+ "src/**",
17
+ "dist/**/*.js",
18
+ "dist/**/*.js.map",
19
+ "dist/**/*.d.ts",
20
+ "dist/**/*.d.ts.map",
21
+ "README.md",
22
+ "LICENSE"
23
+ ],
24
+ "publishConfig": {
25
+ "access": "public"
26
+ },
27
+ "scripts": {
28
+ "build": "tsc",
29
+ "test": "borp --coverage tests/*.test.ts --coverage-exclude '**/node_modules/**' --coverage-exclude '**/tests/**'",
30
+ "clean": "rm -rf dist",
31
+ "prepublishOnly": "npm run build"
32
+ },
33
+ "keywords": [
34
+ "flow-stack",
35
+ "plugin",
36
+ "sqs",
37
+ "aws",
38
+ "queue",
39
+ "message",
40
+ "orchestrator",
41
+ "workflow"
42
+ ],
43
+ "author": "Rahul Radhakrishnan",
44
+ "license": "MIT",
45
+ "peerDependencies": {
46
+ "@workflow-stack/core": "^0.1.0"
47
+ },
48
+ "dependencies": {
49
+ "@aws-sdk/client-sqs": "^3.700.0",
50
+ "@aws-sdk/client-sts": "^3.700.0",
51
+ "@aws-sdk/credential-providers": "^3.700.0"
52
+ },
53
+ "devDependencies": {
54
+ "@types/node": "^20.0.0",
55
+ "borp": "^0.21.0",
56
+ "ts-node": "^10.9.2",
57
+ "typescript": "^5.0.0"
58
+ },
59
+ "engines": {
60
+ "node": ">=18.0.0"
61
+ },
62
+ "repository": {
63
+ "type": "git",
64
+ "url": "git+https://github.com/rahulrkr08/flow-stack.git",
65
+ "directory": "packages/sqs"
66
+ },
67
+ "bugs": {
68
+ "url": "https://github.com/rahulrkr08/flow-stack/issues"
69
+ },
70
+ "homepage": "https://github.com/rahulrkr08/flow-stack/tree/main/packages/sqs#readme",
71
+ "gitHead": "099ec70ce5b0473dc6a211a875d45372262a773e"
72
+ }
package/src/index.ts ADDED
@@ -0,0 +1,15 @@
1
+ // SQS plugin
2
+ export { sqsPlugin } from './plugin.js';
3
+
4
+ // SQS executor (for advanced use cases)
5
+ export { executeSqsService } from './sqs-executor.js';
6
+
7
+ // Type definitions
8
+ export type {
9
+ SqsServiceConfig,
10
+ SqsCredentials,
11
+ SqsAccessKeyCredentials,
12
+ SqsAssumeRoleCredentials,
13
+ SqsRetryConfig,
14
+ SqsMessageAttribute,
15
+ } from './types.js';
package/src/plugin.ts ADDED
@@ -0,0 +1,15 @@
1
+ import type { FlowStackPlugin } from '@workflow-stack/core';
2
+ import type { SqsServiceConfig } from './types.js';
3
+ import { executeSqsService } from './sqs-executor.js';
4
+
5
+ /**
6
+ * SQS plugin for flow-stack
7
+ * Provides AWS SQS message publishing using AWS SDK v3
8
+ */
9
+ export const sqsPlugin: FlowStackPlugin<SqsServiceConfig> = {
10
+ type: 'sqs',
11
+ name: '@workflow-stack/sqs',
12
+ version: '1.0.0',
13
+ execute: executeSqsService,
14
+ interpolate: true,
15
+ };
@@ -0,0 +1,163 @@
1
+ import { SQSClient, SendMessageCommand } from '@aws-sdk/client-sqs';
2
+ import { fromTemporaryCredentials } from '@aws-sdk/credential-providers';
3
+ import type { ServiceResult, OrchestrationContext } from '@workflow-stack/core';
4
+ import { emitServiceStart, emitServiceComplete, emitServiceError } from '@workflow-stack/core';
5
+ import type { SqsServiceConfig, SqsAssumeRoleCredentials } from './types.js';
6
+
7
+ /**
8
+ * Creates an SQS client based on the provided configuration
9
+ */
10
+ function createSqsClient(config: SqsServiceConfig): SQSClient {
11
+ const clientConfig: any = {
12
+ region: config.region,
13
+ };
14
+
15
+ if (config.retry) {
16
+ clientConfig.maxAttempts = config.retry.maxAttempts;
17
+ clientConfig.retryMode = config.retry.retryMode;
18
+ }
19
+
20
+ if (config.credentials) {
21
+ if (config.credentials.type === 'accessKey') {
22
+ clientConfig.credentials = {
23
+ accessKeyId: config.credentials.accessKeyId,
24
+ secretAccessKey: config.credentials.secretAccessKey,
25
+ sessionToken: config.credentials.sessionToken,
26
+ };
27
+ } else if (config.credentials.type === 'assumeRole') {
28
+ const roleConfig = config.credentials as SqsAssumeRoleCredentials;
29
+ clientConfig.credentials = fromTemporaryCredentials({
30
+ params: {
31
+ RoleArn: roleConfig.roleArn,
32
+ RoleSessionName: roleConfig.roleSessionName || 'flow-stack-sqs-session',
33
+ ExternalId: roleConfig.externalId,
34
+ DurationSeconds: roleConfig.durationSeconds,
35
+ },
36
+ });
37
+ }
38
+ }
39
+
40
+ return new SQSClient(clientConfig);
41
+ }
42
+
43
+ /**
44
+ * Executes SQS message publishing
45
+ */
46
+ async function sendMessage(
47
+ client: SQSClient,
48
+ config: SqsServiceConfig
49
+ ): Promise<{ messageId: string; sequenceNumber?: string }> {
50
+ const messageBody =
51
+ typeof config.message.body === 'string'
52
+ ? config.message.body
53
+ : JSON.stringify(config.message.body);
54
+
55
+ const command = new SendMessageCommand({
56
+ QueueUrl: config.queueUrl,
57
+ MessageBody: messageBody,
58
+ MessageGroupId: config.message.messageGroupId,
59
+ MessageDeduplicationId: config.message.messageDeduplicationId,
60
+ DelaySeconds: config.message.delaySeconds,
61
+ MessageAttributes: config.message.messageAttributes,
62
+ });
63
+
64
+ const response = await client.send(command);
65
+
66
+ return {
67
+ messageId: response.MessageId!,
68
+ sequenceNumber: response.SequenceNumber,
69
+ };
70
+ }
71
+
72
+ /**
73
+ * Executes a single SQS publish service call
74
+ */
75
+ export async function executeSqsService(
76
+ config: SqsServiceConfig,
77
+ context: OrchestrationContext,
78
+ serviceId: string
79
+ ): Promise<ServiceResult> {
80
+ const startTime = Date.now();
81
+
82
+ emitServiceStart(serviceId, 'sqs', config, context);
83
+
84
+ const client = createSqsClient(config);
85
+
86
+ try {
87
+ const isAsync = config.async === true;
88
+
89
+ if (isAsync) {
90
+ // Fire and forget - don't wait for response
91
+ sendMessage(client, config).catch((error) => {
92
+ // Log error but don't fail the service
93
+ const processingTime = Date.now() - startTime;
94
+ emitServiceError(serviceId, 'sqs', config, context, processingTime, error);
95
+ });
96
+
97
+ const processingTime = Date.now() - startTime;
98
+ emitServiceComplete(serviceId, 'sqs', config, context, processingTime, 202);
99
+
100
+ return {
101
+ status: 202,
102
+ body: {
103
+ message: 'Message queued for delivery',
104
+ async: true,
105
+ },
106
+ metadata: {
107
+ executionStatus: 'executed',
108
+ serviceType: 'sqs',
109
+ },
110
+ };
111
+ }
112
+
113
+ // Sync mode - wait for response
114
+ const result = await sendMessage(client, config);
115
+
116
+ const processingTime = Date.now() - startTime;
117
+ emitServiceComplete(serviceId, 'sqs', config, context, processingTime, 200);
118
+
119
+ return {
120
+ status: 200,
121
+ body: {
122
+ messageId: result.messageId,
123
+ sequenceNumber: result.sequenceNumber,
124
+ },
125
+ metadata: {
126
+ executionStatus: 'executed',
127
+ serviceType: 'sqs',
128
+ },
129
+ };
130
+ } catch (error: any) {
131
+ const processingTime = Date.now() - startTime;
132
+ emitServiceError(serviceId, 'sqs', config, context, processingTime, error);
133
+
134
+ // If fallback is configured, return it
135
+ if (config.fallback) {
136
+ return {
137
+ status: config.fallback.status || null,
138
+ body: config.fallback.data,
139
+ metadata: {
140
+ executionStatus: 'failed',
141
+ fallbackUsed: true,
142
+ serviceType: 'sqs',
143
+ },
144
+ };
145
+ }
146
+
147
+ // Otherwise, return error
148
+ return {
149
+ status: null,
150
+ body: null,
151
+ error: {
152
+ message: error.message,
153
+ code: error.code || error.name,
154
+ },
155
+ metadata: {
156
+ executionStatus: 'failed',
157
+ serviceType: 'sqs',
158
+ },
159
+ };
160
+ } finally {
161
+ client.destroy();
162
+ }
163
+ }
package/src/types.ts ADDED
@@ -0,0 +1,63 @@
1
+ import type { BaseServiceConfig } from '@workflow-stack/core';
2
+
3
+ /**
4
+ * AWS credentials configuration using access key and secret
5
+ */
6
+ export interface SqsAccessKeyCredentials {
7
+ type: 'accessKey';
8
+ accessKeyId: string;
9
+ secretAccessKey: string;
10
+ sessionToken?: string;
11
+ }
12
+
13
+ /**
14
+ * AWS credentials configuration using assume role
15
+ */
16
+ export interface SqsAssumeRoleCredentials {
17
+ type: 'assumeRole';
18
+ roleArn: string;
19
+ roleSessionName?: string;
20
+ externalId?: string;
21
+ durationSeconds?: number;
22
+ }
23
+
24
+ /**
25
+ * AWS credentials - either access key or assume role
26
+ */
27
+ export type SqsCredentials = SqsAccessKeyCredentials | SqsAssumeRoleCredentials;
28
+
29
+ /**
30
+ * Retry configuration for SQS operations
31
+ */
32
+ export interface SqsRetryConfig {
33
+ maxAttempts?: number;
34
+ retryMode?: 'standard' | 'adaptive';
35
+ }
36
+
37
+ /**
38
+ * SQS message attributes
39
+ */
40
+ export interface SqsMessageAttribute {
41
+ DataType: 'String' | 'Number' | 'Binary';
42
+ StringValue?: string;
43
+ BinaryValue?: Uint8Array;
44
+ }
45
+
46
+ /**
47
+ * SQS service configuration
48
+ */
49
+ export interface SqsServiceConfig extends BaseServiceConfig {
50
+ type: 'sqs';
51
+ queueUrl: string;
52
+ region: string;
53
+ credentials?: SqsCredentials;
54
+ message: {
55
+ body: any;
56
+ messageGroupId?: string;
57
+ messageDeduplicationId?: string;
58
+ delaySeconds?: number;
59
+ messageAttributes?: Record<string, SqsMessageAttribute>;
60
+ };
61
+ retry?: SqsRetryConfig;
62
+ async?: boolean;
63
+ }