unnbound-events 1.0.22 → 2.0.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 CHANGED
@@ -1,340 +1,173 @@
1
1
  # Unnbound Events SDK
2
2
 
3
- Unified routing for HTTP and SQS with one handler registration API.
3
+ Unified HTTP and SQS queue routing with a single API. Register handlers once, handle requests from both transports.
4
4
 
5
5
  ## Install
6
6
 
7
7
  ```bash
8
- pnpm add unnbound-events-sdk
8
+ pnpm add unnbound-events
9
9
  ```
10
10
 
11
- ## Usage
12
-
13
- ### Simple Start (Recommended)
14
-
15
- Register handlers and start with automatic graceful shutdown:
11
+ ## Quick Start
16
12
 
17
13
  ```ts
18
- import { createEventsClient } from 'unnbound-events-sdk';
19
-
20
- const client = createEventsClient();
21
-
22
- // Register handlers
23
- client.post('/email', async (req) => ({ status: 200, body: { ok: true } }));
24
- client.get('/health', async () => ({ status: 200, body: { status: 'ok' } }));
25
-
26
- // Start with defaults (HTTP on port 3000, SQS from environment variables)
27
- await client.start();
28
- ```
14
+ import { createServer } from 'unnbound-events';
29
15
 
30
- The client provides a built-in `GET /healthcheck` handler that responds with `204`, so you can skip registering it unless you need custom logic. Add your own handler with the same path to override the default.
16
+ const server = createServer();
31
17
 
32
- ### Custom Configuration
18
+ // Register route handlers
19
+ server.post('/email/:userId', async (c) => {
20
+ const { userId } = c.request.params;
21
+ const body = c.request.body;
33
22
 
34
- Configure specific transports as needed:
35
-
36
- ```ts
37
- // HTTP only
38
- await client.start({ http: { port: 8080 } });
39
-
40
- // SQS only
41
- await client.start({
42
- sqs: { queueUrl: 'https://sqs.us-west-2.amazonaws.com/123456789012/my-queue' },
23
+ return { status: 200, body: { sent: true } };
43
24
  });
44
25
 
45
- // Both with custom settings
46
- await client.start({
47
- http: { port: 8080 },
48
- sqs: {
49
- queueUrl: 'https://sqs.us-west-2.amazonaws.com/123456789012/my-queue',
50
- region: 'us-west-2',
51
- },
52
- });
26
+ // Start both HTTP and queue listeners
27
+ server.start();
53
28
  ```
54
29
 
55
- ### Manual Control
30
+ The server automatically:
56
31
 
57
- For more control over individual transports:
32
+ - Starts HTTP server on `PORT` (default: `3000`)
33
+ - Starts SQS long-polling if `UNNBOUND_SQS_URL` is set
34
+ - Provides `/healthcheck` endpoint (returns `200 { status: 'ok' }`)
35
+ - Handles graceful shutdown on `SIGINT`/`SIGTERM`
58
36
 
59
- ```ts
60
- import { createEventsClient } from 'unnbound-events-sdk';
61
-
62
- const client = createEventsClient();
63
- client.post('/email', async (req) => ({ status: 200, body: { ok: true } }));
64
-
65
- // Built-in HTTP server
66
- await client.http({ port: 3000 });
37
+ ## Handlers
67
38
 
68
- // SQS Lambda handler
69
- export const handler = client.sqs();
70
-
71
- // SQS long-poll listener (env-driven)
72
- // Env: UNNBOUND_SQS_QUEUE_URL (or SQS_QUEUE_URL), AWS_REGION, AWS_SQS_ENDPOINT (optional)
73
- await client.sqsListen();
74
- ```
75
-
76
- ## Middleware
77
-
78
- Add cross-cutting logic that runs before/after your handlers. Works for both HTTP and SQS.
39
+ Routes receive a context object with request details:
79
40
 
80
41
  ```ts
81
- import { createEventsClient } from 'unnbound-events-sdk';
82
-
83
- const client = createEventsClient();
84
-
85
- // Global middleware
86
- client.use(async (req, next) => {
87
- const startedAt = Date.now();
88
- const res = await next(req);
42
+ server.post('/users/:id', async (c) => {
43
+ // Access request data
44
+ const { id } = c.request.params;
45
+ const query = c.request.query;
46
+ const headers = c.request.headers;
47
+ const body = c.request.body;
48
+
49
+ // Return response
89
50
  return {
90
- ...res,
91
- headers: { ...(res.headers ?? {}), 'x-duration-ms': String(Date.now() - startedAt) },
51
+ status: 200,
52
+ body: { id, ...body },
53
+ headers: { 'x-custom': 'value' },
92
54
  };
93
55
  });
94
-
95
- // Path-scoped middleware (string | RegExp | (path)=>boolean)
96
- client.use(/^\/admin\//, async (req, next) => {
97
- const token = req.headers['authorization'] || req.headers['Authorization'];
98
- if (!token || !String(token).startsWith('Bearer ')) {
99
- return { status: 401, body: { message: 'Unauthorized' } };
100
- }
101
- return next(req);
102
- });
103
-
104
- client.get('/health', async () => ({ status: 200, body: { status: 'ok' } }));
105
- client.post('/echo', async (req) => ({ status: 200, body: req.body }));
106
-
107
- await client.start();
108
56
  ```
109
57
 
110
- SQS-specific example:
111
-
112
- ```ts
113
- const client = createEventsClient();
114
-
115
- client.use(async (req, next) => {
116
- const rid = String(req.headers['x-request-id'] ?? crypto.randomUUID());
117
- return next({
118
- ...req,
119
- headers: { ...req.headers, 'x-request-id': rid },
120
- metadata: { ...(req.metadata ?? {}), requestId: rid },
121
- });
122
- });
123
-
124
- client.post('/jobs/process', async (req) => ({ status: 200, body: { ok: true } }));
125
-
126
- await client.sqsListen();
127
- ```
58
+ **Response format:** `{ status?: number, body?: object, headers?: Record<string, string> }`
128
59
 
129
- API:
60
+ Return `undefined`, `null`, or omit properties to default to `204 No Content`.
130
61
 
131
- - `use(middleware)`
132
- - `use(matcher, middleware)` where matcher is `string | RegExp | (path) => boolean`
133
- - `middleware(req, next) -> Promise<EventResponse>`
134
-
135
- ## Enqueue Events
136
-
137
- The `enqueue` function allows you to send events to the queue by using the async endpoint.
62
+ ## Middleware
138
63
 
139
- ### Basic Usage
64
+ Add logic that runs before/after handlers. Works for both HTTP and queue requests.
140
65
 
141
66
  ```ts
142
- import { createEventsClient } from 'unnbound-events-sdk';
143
-
144
- const client = createEventsClient();
145
-
146
- client.post('/process-job', async (req) => {
147
- console.log('Handle job:', req.body);
148
-
149
- // Split job into multiple tasks
150
- const tasks = [...];
151
-
152
- tasks.map(async (task) => {
153
- await client.enqueue({
154
- method: 'POST',
155
- path: '/process-task',
156
- body: { jobId: '123', data: 'important data' }
157
- });
158
- });
159
-
160
- return { status: 200, body: { processed: true } };
67
+ // Global middleware
68
+ server.use(async (c, next) => {
69
+ console.log('Request:', c.request.method, c.request.path);
70
+ return next();
161
71
  });
162
72
 
163
- client.post('/process-task', async (req) => {
164
- console.log('Handle task:', req.body);
165
-
166
- return { status: 200, body: { processed: true } };
73
+ // Path-based middleware
74
+ server.use('/admin/*', async (c, next) => {
75
+ const token = c.request.headers['authorization'];
76
+ if (!token?.startsWith('Bearer ')) {
77
+ return { status: 401, body: { error: 'Unauthorized' } };
78
+ }
79
+ return next();
167
80
  });
168
81
 
169
- await client.start();
82
+ // Handler-specific middleware
83
+ server.post(
84
+ '/email',
85
+ async (c, next) => {
86
+ // Runs only for this handler
87
+ return next();
88
+ },
89
+ async (c) => {
90
+ return { status: 200, body: { ok: true } };
91
+ }
92
+ );
170
93
  ```
171
94
 
172
- ### Scheduled Jobs
173
-
174
- ```ts
175
- // Trigger scheduled processing
176
- setInterval(async () => {
177
- await client.enqueue({
178
- method: 'POST',
179
- path: '/cleanup',
180
- body: { timestamp: Date.now() },
181
- });
182
- }, 60000); // Every minute
183
- ```
95
+ ## Configuration
184
96
 
185
- ## Message envelope
97
+ ### HTTP Port
186
98
 
187
- The SQS record `body` should be a JSON string matching this shape:
99
+ Set via `PORT` environment variable (default: `3000`):
188
100
 
189
- ```ts
190
- interface QueueEnvelope {
191
- id: string;
192
- timestamp: number;
193
- version: string;
194
- workflow: string;
195
- priority: number;
196
- payload: { type: string; size: number; compressed: boolean };
197
- request: {
198
- method: 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE' | 'OPTIONS' | 'HEAD';
199
- path: string;
200
- headers: Record<string, string>;
201
- query: Record<string, unknown>;
202
- body: unknown;
203
- };
204
- metadata?: Record<string, unknown>;
205
- addons?: Record<string, unknown>;
206
- }
101
+ ```bash
102
+ export PORT=8080
207
103
  ```
208
104
 
209
- ## S3 Payload Offloading for Large Messages
210
-
211
- The SDK supports automatic handling of large payloads (>256KB) stored in S3, which is necessary since SQS has a 256KB message size limit.
105
+ ### Queue Options
212
106
 
213
- The S3 SDK (`@aws-sdk/client-s3`) is included as a dependency, so no additional installation is required.
214
-
215
- ### Automatic Configuration
216
-
217
- The SDK will automatically enable S3 support when environment variables are set:
218
-
219
- - `UNNBOUND_S3_PAYLOAD_BUCKET` or `S3_BUCKET` - S3 bucket name for payload storage
220
-
221
- No code changes needed! Just set the environment variable and the SDK will handle S3 payloads automatically.
222
-
223
- ### Manual Configuration
224
-
225
- Configure S3 support when starting the client:
107
+ Configure SQS polling behavior:
226
108
 
227
109
  ```ts
228
- await client.start({
229
- sqs: { queueUrl: 'https://sqs.us-west-2.amazonaws.com/123456789012/my-queue' },
230
- s3: {
231
- bucketName: 'my-payload-bucket',
232
- region: 'us-west-2',
110
+ const server = createServer({
111
+ queue: {
112
+ maxMessages: 10, // Messages per poll (default: 10)
113
+ visibilityTimeout: 30, // Seconds (default: SQS default)
233
114
  },
234
115
  });
235
116
  ```
236
117
 
237
- Or with `sqsListen()`:
238
-
239
- ```ts
240
- await client.sqsListen({
241
- queueUrl: 'https://sqs.us-west-2.amazonaws.com/123456789012/my-queue',
242
- s3: {
243
- bucketName: 'my-payload-bucket',
244
- region: 'us-west-2',
245
- },
246
- });
247
- ```
118
+ ### SQS Environment Variables
248
119
 
249
- ### Supported Formats
120
+ The queue adapter reads from:
250
121
 
251
- The SDK supports two S3 payload formats:
122
+ - `UNNBOUND_SQS_URL` - Queue URL (required for queue listener)
123
+ - `UNNBOUND_AWS_REGION` - AWS region (default: `us-west-1`)
124
+ - `UNNBOUND_SQS_ENDPOINT` - Custom endpoint (optional)
252
125
 
253
- 1. **ExtendedSQSClient Pointer Format** (AWS Extended Client Library compatible):
254
-
255
- ```json
256
- [
257
- "software.amazon.payloadoffloading.PayloadS3Pointer",
258
- {
259
- "s3BucketName": "bucket-name",
260
- "s3Key": "key-path"
261
- }
262
- ]
263
- ```
264
-
265
- 2. **Envelope-based S3 Payloads** (Unnbound format):
266
-
267
- ```json
268
- {
269
- "payload": {
270
- "type": "s3-single",
271
- "size": 512000,
272
- "compressed": true,
273
- "compressionType": "gzip",
274
- "location": {
275
- "bucket": "bucket-name",
276
- "key": "key-path",
277
- "region": "us-west-2",
278
- "versionId": "optional-version-id"
279
- }
280
- },
281
- "request": { ... }
282
- }
283
- ```
284
-
285
- ### Compression Support
286
-
287
- The SDK automatically decompresses payloads with:
288
-
289
- - `brotli` compression
290
- - `gzip` compression
291
-
292
- When the `compressed` flag is `true` and `compressionType` is specified, the SDK will automatically decompress the payload after fetching from S3.
293
-
294
- ## Environment Variables
295
-
296
- ### SQS Configuration
126
+ ```bash
127
+ export UNNBOUND_SQS_URL="https://sqs.us-west-2.amazonaws.com/123/my-queue"
128
+ export UNNBOUND_AWS_REGION="us-west-2"
129
+ ```
297
130
 
298
- The SDK looks for SQS configuration in this order:
131
+ ### S3 Large Payload Support
299
132
 
300
- 1. **Options passed to `sqsListen()` or `start()`**
301
- 2. **Environment variables** (in order of preference):
302
- - `UNNBOUND_SQS_QUEUE_URL` (custom Unnbound variable)
303
- - `SQS_QUEUE_URL` (standard variable)
304
- - `QUEUE_URL` (fallback)
133
+ For messages >1MB, payloads can be stored in S3. The SDK automatically fetches and processes them.
305
134
 
306
- **Required:**
135
+ **Environment variables:**
307
136
 
308
- - `UNNBOUND_SQS_QUEUE_URL` or `SQS_QUEUE_URL` - The SQS queue URL
137
+ - `UNNBOUND_S3_ENDPOINT` - Custom S3 endpoint (optional)
309
138
 
310
- **Optional:**
139
+ The SDK supports AWS Extended Client Library format (S3 pointer messages).
311
140
 
312
- - `AWS_REGION` - AWS region (defaults to `us-east-1`)
313
- - `AWS_SQS_ENDPOINT` - Custom SQS endpoint (for LocalStack, etc.)
141
+ ## Queue Message Format
314
142
 
315
- **Example:**
143
+ SQS message body should be JSON:
316
144
 
317
- ```bash
318
- export UNNBOUND_SQS_QUEUE_URL="https://sqs.us-west-2.amazonaws.com/123456789012/my-queue"
319
- export AWS_REGION="us-west-2"
145
+ ```json
146
+ {
147
+ "id": "unique-id",
148
+ "timestamp": 1234567890,
149
+ "request": {
150
+ "method": "POST",
151
+ "url": "https://example.com/path?query=value",
152
+ "headers": { "content-type": "application/json" },
153
+ "body": "base64-encoded-body"
154
+ },
155
+ "metadata": {}
156
+ }
320
157
  ```
321
158
 
322
- ### S3 Configuration
323
-
324
- For S3 payload support, configure:
325
-
326
- **Required:**
159
+ The adapter extracts `method`, `path`, `headers`, `query`, and `body` to route the message through your handlers.
327
160
 
328
- - `UNNBOUND_S3_PAYLOAD_BUCKET` or `S3_BUCKET` - S3 bucket name for payload storage
161
+ ## Logger Integration
329
162
 
330
- **Optional:**
163
+ Pass a custom logger:
331
164
 
332
- - `AWS_REGION` - AWS region (defaults to `us-east-1`)
333
- - `AWS_S3_ENDPOINT` - Custom S3 endpoint (for LocalStack, etc.)
334
-
335
- **Example:**
165
+ ```ts
166
+ import { logger } from 'unnbound-logger-sdk';
336
167
 
337
- ```bash
338
- export UNNBOUND_S3_PAYLOAD_BUCKET="my-payload-bucket"
339
- export AWS_REGION="us-west-2"
168
+ const server = createServer({
169
+ logger,
170
+ });
340
171
  ```
172
+
173
+ The logger is used for internal events and traces.
package/dist/index.d.ts CHANGED
@@ -1,4 +1,3 @@
1
- export { createEventsClient } from './lib/client';
2
- export type { EventsClient, EventRequest, EventResponse, RouteHandler, RouteMatcher, RegisteredRoute, SqsBatchEvent, StartOptions, Middleware, } from './lib/types';
3
- export { createExpressMiddleware } from './lib/adapters/express';
4
- export { createSqsConsumer } from './lib/adapters/sqs';
1
+ export type { IncomingEvent, IncomingRequest, EventMetadata, EventSource } from './lib/types';
2
+ export type { EventServerOptions, EventServer } from './lib/server';
3
+ export { createServer } from './lib/server';
package/dist/index.js CHANGED
@@ -1,9 +1,5 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.createSqsConsumer = exports.createExpressMiddleware = exports.createEventsClient = void 0;
4
- var client_1 = require("./lib/client");
5
- Object.defineProperty(exports, "createEventsClient", { enumerable: true, get: function () { return client_1.createEventsClient; } });
6
- var express_1 = require("./lib/adapters/express");
7
- Object.defineProperty(exports, "createExpressMiddleware", { enumerable: true, get: function () { return express_1.createExpressMiddleware; } });
8
- var sqs_1 = require("./lib/adapters/sqs");
9
- Object.defineProperty(exports, "createSqsConsumer", { enumerable: true, get: function () { return sqs_1.createSqsConsumer; } });
3
+ exports.createServer = void 0;
4
+ var server_1 = require("./lib/server");
5
+ Object.defineProperty(exports, "createServer", { enumerable: true, get: function () { return server_1.createServer; } });
@@ -0,0 +1,40 @@
1
+ import { UnnboundLogger } from 'unnbound-logger-sdk';
2
+ import { EventHandlerResult, EventMetadata, EventSource, IncomingEvent, IncomingRequest } from '../types';
3
+ export type EventHandler = (event: IncomingEvent) => Promise<EventHandlerResult>;
4
+ export interface EventAdapterOptions {
5
+ logger?: UnnboundLogger;
6
+ source: EventSource;
7
+ handler: EventHandler;
8
+ }
9
+ export declare abstract class EventAdapter<E> {
10
+ protected readonly logger: UnnboundLogger;
11
+ protected readonly source: EventSource;
12
+ private readonly handler;
13
+ protected constructor(options: EventAdapterOptions);
14
+ protected abstract parseEvent(event: E): IncomingEvent;
15
+ abstract listen(): () => Promise<void>;
16
+ protected getMetadataFromRequest(request: IncomingRequest): EventMetadata;
17
+ protected normalizeHeaders(headers: Record<string, string | string[]>): Record<string, string>;
18
+ protected handle(event: IncomingEvent): Promise<EventHandlerResult>;
19
+ protected buildIncomingPayload(req: IncomingRequest, res?: EventHandlerResult): {
20
+ type: string;
21
+ source: EventSource;
22
+ http: {
23
+ url: string;
24
+ method: string;
25
+ query: Record<string, any>;
26
+ ip: string | undefined;
27
+ incoming: boolean;
28
+ request: {
29
+ headers: Record<string, string>;
30
+ body: any;
31
+ };
32
+ response: {
33
+ status: import("hono/utils/http-status").StatusCode;
34
+ headers: Record<string, string> | undefined;
35
+ body: any;
36
+ } | undefined;
37
+ };
38
+ };
39
+ protected handleError(error: unknown): EventHandlerResult;
40
+ }
@@ -0,0 +1,70 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.EventAdapter = void 0;
4
+ const unnbound_logger_sdk_1 = require("unnbound-logger-sdk");
5
+ const trace_1 = require("unnbound-logger-sdk/dist/trace");
6
+ const types_1 = require("unnbound-logger-sdk/dist/types");
7
+ const utils_1 = require("unnbound-logger-sdk/dist/utils");
8
+ const error_1 = require("../error");
9
+ class EventAdapter {
10
+ logger;
11
+ source;
12
+ handler;
13
+ constructor(options) {
14
+ this.logger = options.logger ?? unnbound_logger_sdk_1.logger;
15
+ this.source = options.source;
16
+ this.handler = options.handler;
17
+ }
18
+ getMetadataFromRequest(request) {
19
+ return {
20
+ traceId: request.headers[types_1.defaultTraceHeaderKey] ?? unnbound_logger_sdk_1.getTraceId,
21
+ messageId: request.headers[types_1.defaultMessageHeaderKey] ?? (0, trace_1.getMessageId)(),
22
+ source: this.source,
23
+ };
24
+ }
25
+ normalizeHeaders(headers) {
26
+ return Object.fromEntries(Object.entries(headers).map(([key, value]) => [
27
+ key,
28
+ Array.isArray(value) ? value.join(',') : (value?.toString() ?? ''),
29
+ ]));
30
+ }
31
+ async handle(event) {
32
+ return await (0, trace_1.withTrace)(() => this.handler(event), event.metadata);
33
+ }
34
+ buildIncomingPayload(req, res) {
35
+ return {
36
+ type: 'http',
37
+ source: this.source,
38
+ http: {
39
+ url: req.url,
40
+ method: req.method.toLowerCase(),
41
+ query: req.query,
42
+ ip: (0, utils_1.normalizeIp)(req.headers['cf-connecting-ip'] ??
43
+ req.headers['x-forwarded-for'] ??
44
+ req.headers['x-real-ip']),
45
+ incoming: true,
46
+ request: {
47
+ headers: req.headers,
48
+ body: (0, utils_1.safeJsonParse)(req.body),
49
+ },
50
+ response: res
51
+ ? {
52
+ status: res.status ?? 204,
53
+ headers: res.headers ? this.normalizeHeaders(res.headers) : undefined,
54
+ body: res.body ? (0, utils_1.safeJsonParse)(res.body) : undefined,
55
+ }
56
+ : undefined,
57
+ },
58
+ };
59
+ }
60
+ handleError(error) {
61
+ if (error instanceof error_1.HandlerError) {
62
+ return {
63
+ status: error.status,
64
+ body: error.toJson(),
65
+ };
66
+ }
67
+ return this.handleError(new error_1.HandlerError({ message: 'Internal server error', status: 500, cause: error }));
68
+ }
69
+ }
70
+ exports.EventAdapter = EventAdapter;
@@ -1,4 +1,6 @@
1
+ import type { EventsClient } from '../types';
1
2
  type Request = {
3
+ url: string;
2
4
  method: string;
3
5
  path: string;
4
6
  headers: Record<string, string | string[] | undefined>;
@@ -11,6 +13,5 @@ type Response = {
11
13
  send: (body: unknown) => void;
12
14
  end: () => void;
13
15
  };
14
- import type { EventsClient } from '../types';
15
16
  export declare function createExpressMiddleware(client: EventsClient): (req: Request, res: Response) => Promise<void>;
16
17
  export {};
@@ -11,6 +11,7 @@ function toEventRequest(req) {
11
11
  headers[key] = value.join(',');
12
12
  }
13
13
  return {
14
+ url: req.url,
14
15
  method,
15
16
  path: req.path,
16
17
  headers,
@@ -0,0 +1,20 @@
1
+ import { S3Client } from '@aws-sdk/client-s3';
2
+ import { ReceiveMessageCommand, ReceiveMessageCommandOutput, SQSClient } from '@aws-sdk/client-sqs';
3
+ export interface S3Pointer {
4
+ bucket: string;
5
+ key: string;
6
+ }
7
+ export type S3PointerMessage = ['software.amazon.payloadoffloading.PayloadS3Pointer', S3Pointer];
8
+ type SQSClientConfig = ConstructorParameters<typeof SQSClient>[0];
9
+ export type ExtendedSQSClientConfig = SQSClientConfig & {
10
+ s3: S3Client;
11
+ };
12
+ export declare class ExtendedSQSClient extends SQSClient {
13
+ private readonly s3;
14
+ private readonly S3_POINTER_CLASS;
15
+ constructor(config: ExtendedSQSClientConfig);
16
+ private isS3PointerMessage;
17
+ private retrieveMessage;
18
+ receive(command: ReceiveMessageCommand): Promise<ReceiveMessageCommandOutput>;
19
+ }
20
+ export {};
@@ -0,0 +1,42 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ExtendedSQSClient = void 0;
4
+ const client_s3_1 = require("@aws-sdk/client-s3");
5
+ const client_sqs_1 = require("@aws-sdk/client-sqs");
6
+ const unnbound_logger_sdk_1 = require("unnbound-logger-sdk");
7
+ const internal_1 = require("unnbound-logger-sdk/dist/internal");
8
+ class ExtendedSQSClient extends client_sqs_1.SQSClient {
9
+ s3;
10
+ S3_POINTER_CLASS = 'software.amazon.payloadoffloading.PayloadS3Pointer';
11
+ constructor(config) {
12
+ super(config);
13
+ this.s3 = config.s3;
14
+ }
15
+ isS3PointerMessage(body) {
16
+ return Array.isArray(body) && body.length === 2 && body[0] === this.S3_POINTER_CLASS;
17
+ }
18
+ async retrieveMessage(message) {
19
+ if (!message.Body)
20
+ return message;
21
+ if (!message.Body.startsWith(`["${this.S3_POINTER_CLASS}",`))
22
+ return message;
23
+ const parsed = JSON.parse(message.Body);
24
+ if (!this.isS3PointerMessage(parsed))
25
+ return message;
26
+ const { bucket, key } = parsed[1];
27
+ unnbound_logger_sdk_1.logger.debug({ bucket, key, ...(0, internal_1.internal)() }, 'Retrieving message from storage.');
28
+ const payload = await this.s3.send(new client_s3_1.GetObjectCommand({ Bucket: bucket, Key: key }));
29
+ const body = await payload.Body?.transformToString();
30
+ if (!body)
31
+ throw new Error('Failed to retrieve queue message from storage.');
32
+ return { ...message, Body: body };
33
+ }
34
+ async receive(command) {
35
+ const result = await this.send(command);
36
+ return {
37
+ ...result,
38
+ Messages: await Promise.all(result.Messages?.map(this.retrieveMessage.bind(this)) ?? []),
39
+ };
40
+ }
41
+ }
42
+ exports.ExtendedSQSClient = ExtendedSQSClient;