@lafken/queue 0.12.8 → 0.12.9
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 +62 -24
- package/lib/main/event/event.d.ts +14 -5
- package/lib/main/event/event.js +14 -5
- package/lib/main/event/event.types.d.ts +33 -2
- package/lib/main/queue/queue.js +12 -4
- package/lib/main/queue/queue.types.d.ts +4 -0
- package/package.json +4 -4
package/README.md
CHANGED
|
@@ -20,7 +20,7 @@ import { Queue, Standard, Payload, Param, Event } from '@lafken/queue/main';
|
|
|
20
20
|
// 1. Define the message payload
|
|
21
21
|
@Payload()
|
|
22
22
|
export class OrderMessage {
|
|
23
|
-
@Param({ source: '
|
|
23
|
+
@Param({ source: 'attribute' })
|
|
24
24
|
orderId: string;
|
|
25
25
|
|
|
26
26
|
@Param({ source: 'body', parse: true })
|
|
@@ -31,8 +31,10 @@ export class OrderMessage {
|
|
|
31
31
|
@Queue()
|
|
32
32
|
export class OrderQueue {
|
|
33
33
|
@Standard({ batchSize: 5, visibilityTimeout: 60 })
|
|
34
|
-
processOrder(@Event(OrderMessage)
|
|
35
|
-
|
|
34
|
+
processOrder(@Event(OrderMessage) messages: OrderMessage[]) {
|
|
35
|
+
for (const message of messages) {
|
|
36
|
+
console.log(`Processing order ${message.orderId}`);
|
|
37
|
+
}
|
|
36
38
|
}
|
|
37
39
|
}
|
|
38
40
|
|
|
@@ -50,7 +52,7 @@ createApp({
|
|
|
50
52
|
});
|
|
51
53
|
```
|
|
52
54
|
|
|
53
|
-
Each `@Standard` or `@Fifo` method becomes an independent Lambda function with its own SQS queue and event source mapping.
|
|
55
|
+
Each `@Standard` or `@Fifo` method becomes an independent Lambda function with its own SQS queue and event source mapping. The handler always receives an **array** of mapped payload objects — one per SQS record in the batch.
|
|
54
56
|
|
|
55
57
|
## Features
|
|
56
58
|
|
|
@@ -82,8 +84,9 @@ Use the `@Standard` decorator to create a standard (non-FIFO) SQS queue consumer
|
|
|
82
84
|
visibilityTimeout: 120,
|
|
83
85
|
maxConcurrency: 5,
|
|
84
86
|
})
|
|
85
|
-
generateReport(@Event(ReportMessage)
|
|
87
|
+
generateReport(@Event(ReportMessage) messages: ReportMessage[]) {
|
|
86
88
|
// Process up to 10 messages per invocation
|
|
89
|
+
for (const message of messages) { ... }
|
|
87
90
|
}
|
|
88
91
|
```
|
|
89
92
|
|
|
@@ -113,8 +116,9 @@ Use the `@Fifo` decorator for FIFO (First-In-First-Out) queues. FIFO queues guar
|
|
|
113
116
|
contentBasedDeduplication: true,
|
|
114
117
|
batchSize: 1,
|
|
115
118
|
})
|
|
116
|
-
processPayment(@Event(PaymentMessage)
|
|
119
|
+
processPayment(@Event(PaymentMessage) messages: PaymentMessage[]) {
|
|
117
120
|
// Messages are processed in exact send order
|
|
121
|
+
for (const message of messages) { ... }
|
|
118
122
|
}
|
|
119
123
|
```
|
|
120
124
|
|
|
@@ -136,31 +140,63 @@ import { Payload, Param } from '@lafken/queue/main';
|
|
|
136
140
|
|
|
137
141
|
@Payload()
|
|
138
142
|
export class TaskMessage {
|
|
139
|
-
@Param({ source: 'attribute'
|
|
143
|
+
@Param({ source: 'attribute' })
|
|
140
144
|
correlationId: string;
|
|
141
145
|
|
|
142
|
-
@Param({ source: '
|
|
143
|
-
|
|
146
|
+
@Param({ source: 'attribute', type: Number })
|
|
147
|
+
priority: number;
|
|
144
148
|
|
|
145
149
|
@Param({ source: 'body', parse: true })
|
|
146
|
-
|
|
150
|
+
taskName: string;
|
|
147
151
|
}
|
|
148
152
|
```
|
|
149
153
|
|
|
150
154
|
#### @Param Options
|
|
151
155
|
|
|
152
|
-
| Option | Type
|
|
153
|
-
| -------- |
|
|
154
|
-
| `source` | `'attribute' \| 'body'`
|
|
155
|
-
| `parse` | `boolean`
|
|
156
|
-
| `type` | `String \| Number \| ...`
|
|
157
|
-
| `name` | `string`
|
|
156
|
+
| Option | Type | Default | Description |
|
|
157
|
+
| -------- | ------------------------------------------ | ------------- | -------------------------------------------------------- |
|
|
158
|
+
| `source` | `'attribute' \| 'body' \| 'record'` | `'attribute'` | Where to extract the value from the SQS record |
|
|
159
|
+
| `parse` | `boolean` | `false` | JSON-parse the message body before extraction (`'body'` only) |
|
|
160
|
+
| `type` | `String \| Number \| ...` | inferred | Data type of the extracted value |
|
|
161
|
+
| `name` | `string` | property name | Override the source field name used for extraction |
|
|
158
162
|
|
|
159
|
-
- **`source: 'attribute'`** — reads from SQS message attributes (supports `String` and `Number` types)
|
|
160
|
-
- **`source: 'body'`** — reads from the message body. Set `parse: true` to JSON-parse the body and extract a specific
|
|
163
|
+
- **`source: 'attribute'`** — reads from SQS message attributes (supports `String` and `Number` types).
|
|
164
|
+
- **`source: 'body'`** — reads from the message body. Set `parse: true` to JSON-parse the body and extract a specific key by property name.
|
|
165
|
+
- **`source: 'record'`** — reads a top-level SQS record field such as `messageId`, `receiptHandle`, `awsRegion`, etc. The `name` option accepts only valid `SQSRecordField` values and defaults to the property name.
|
|
161
166
|
|
|
162
|
-
> [!
|
|
163
|
-
> Only one `@Param` with `source: 'body'`
|
|
167
|
+
> [!IMPORTANT]
|
|
168
|
+
> Only **one** `@Param` with `source: 'body'` is allowed per payload class.
|
|
169
|
+
|
|
170
|
+
#### `source: 'record'` — SQS Record Fields
|
|
171
|
+
|
|
172
|
+
Use `source: 'record'` to bind SQS envelope metadata (e.g. the message identifier or region) directly to a payload property. The available fields are:
|
|
173
|
+
|
|
174
|
+
| Field | Description |
|
|
175
|
+
| ---------------- | --------------------------------------------------- |
|
|
176
|
+
| `messageId` | Unique identifier assigned by SQS to the message |
|
|
177
|
+
| `receiptHandle` | Token used to delete or extend message visibility |
|
|
178
|
+
| `md5OfBody` | MD5 digest of the message body |
|
|
179
|
+
| `eventSource` | Always `"aws:sqs"` |
|
|
180
|
+
| `eventSourceARN` | ARN of the source SQS queue |
|
|
181
|
+
| `awsRegion` | AWS region where the queue resides |
|
|
182
|
+
|
|
183
|
+
```typescript
|
|
184
|
+
import { Payload, Param } from '@lafken/queue/main';
|
|
185
|
+
|
|
186
|
+
@Payload()
|
|
187
|
+
export class AuditMessage {
|
|
188
|
+
// property name matches record field → no 'name' needed
|
|
189
|
+
@Param({ source: 'record' })
|
|
190
|
+
messageId: string;
|
|
191
|
+
|
|
192
|
+
// alias: property 'region' extracts record.awsRegion
|
|
193
|
+
@Param({ source: 'record', name: 'awsRegion' })
|
|
194
|
+
region: string;
|
|
195
|
+
|
|
196
|
+
@Param({ source: 'attribute' })
|
|
197
|
+
userId: string;
|
|
198
|
+
}
|
|
199
|
+
```
|
|
164
200
|
|
|
165
201
|
#### @Field Decorator
|
|
166
202
|
|
|
@@ -181,7 +217,7 @@ export class SimpleMessage {
|
|
|
181
217
|
|
|
182
218
|
### Consuming Messages
|
|
183
219
|
|
|
184
|
-
Bind a typed payload to a handler method using the `@Event` parameter decorator. Pass the payload class so the framework can automatically extract and map fields from
|
|
220
|
+
Bind a typed payload to a handler method using the `@Event` parameter decorator. Pass the payload class so the framework can automatically extract and map fields from every SQS record in the batch. The handler always receives an **array** of mapped objects:
|
|
185
221
|
|
|
186
222
|
```typescript
|
|
187
223
|
import { Queue, Standard, Event } from '@lafken/queue/main';
|
|
@@ -189,8 +225,10 @@ import { Queue, Standard, Event } from '@lafken/queue/main';
|
|
|
189
225
|
@Queue()
|
|
190
226
|
export class AlertQueue {
|
|
191
227
|
@Standard({ queueName: 'alerts', batchSize: 5 })
|
|
192
|
-
processAlert(@Event(AlertMessage)
|
|
193
|
-
|
|
228
|
+
processAlert(@Event(AlertMessage) alerts: AlertMessage[]) {
|
|
229
|
+
for (const alert of alerts) {
|
|
230
|
+
console.log(`Alert from ${alert.source}: ${alert.message}`);
|
|
231
|
+
}
|
|
194
232
|
}
|
|
195
233
|
}
|
|
196
234
|
```
|
|
@@ -224,7 +262,7 @@ await QueueService.sendMessage({
|
|
|
224
262
|
| ------------------- | ---------------------------------- | -------------------------------------------------- |
|
|
225
263
|
| `url` | `string` | Full SQS queue URL |
|
|
226
264
|
| `body` | `any` | Message body (automatically JSON-stringified) |
|
|
227
|
-
| `attributes` | `Record<string, string \| number>` | SQS message attributes
|
|
265
|
+
| `attributes` | `Record<string, string \| number>` | SQS message attributes |
|
|
228
266
|
| `delay` | `number` | Delay in seconds before the message becomes visible |
|
|
229
267
|
| `groupId` | `string` | Message group ID (FIFO queues only) |
|
|
230
268
|
| `deduplicationId` | `string` | Deduplication ID (FIFO queues only) |
|
|
@@ -31,12 +31,15 @@ export declare const Payload: (props?: import("@lafken/common").PayloadProps | u
|
|
|
31
31
|
* Property decorator that maps a class field to a value extracted
|
|
32
32
|
* from an SQS message.
|
|
33
33
|
*
|
|
34
|
-
*
|
|
35
|
-
* matches the property
|
|
36
|
-
*
|
|
37
|
-
* JSON-parse the
|
|
34
|
+
* - `source: 'attribute'` (default) — reads a **message attribute** whose
|
|
35
|
+
* name matches the property (or the `name` option).
|
|
36
|
+
* - `source: 'body'` — reads from the raw message body string; set
|
|
37
|
+
* `parse: true` to JSON-parse it and pick the matching key.
|
|
38
|
+
* - `source: 'record'` — reads a top-level SQS record field such as
|
|
39
|
+
* `messageId`, `receiptHandle`, `awsRegion`, etc. The `name` option
|
|
40
|
+
* can override which record field is read.
|
|
38
41
|
*
|
|
39
|
-
* @param props - Optional extraction configuration (source, type, parse).
|
|
42
|
+
* @param props - Optional extraction configuration (source, type, parse, name).
|
|
40
43
|
*
|
|
41
44
|
* @example
|
|
42
45
|
* ```ts
|
|
@@ -47,6 +50,12 @@ export declare const Payload: (props?: import("@lafken/common").PayloadProps | u
|
|
|
47
50
|
*
|
|
48
51
|
* @Param({ source: 'body', parse: true })
|
|
49
52
|
* content: NotificationContent;
|
|
53
|
+
*
|
|
54
|
+
* @Param({ source: 'record' })
|
|
55
|
+
* messageId: string;
|
|
56
|
+
*
|
|
57
|
+
* @Param({ source: 'record', name: 'messageId' })
|
|
58
|
+
* sqsId: string;
|
|
50
59
|
* }
|
|
51
60
|
* ```
|
|
52
61
|
*/
|
package/lib/main/event/event.js
CHANGED
|
@@ -39,12 +39,15 @@ exports.Payload = (0, common_1.createPayloadDecorator)({
|
|
|
39
39
|
* Property decorator that maps a class field to a value extracted
|
|
40
40
|
* from an SQS message.
|
|
41
41
|
*
|
|
42
|
-
*
|
|
43
|
-
* matches the property
|
|
44
|
-
*
|
|
45
|
-
* JSON-parse the
|
|
42
|
+
* - `source: 'attribute'` (default) — reads a **message attribute** whose
|
|
43
|
+
* name matches the property (or the `name` option).
|
|
44
|
+
* - `source: 'body'` — reads from the raw message body string; set
|
|
45
|
+
* `parse: true` to JSON-parse it and pick the matching key.
|
|
46
|
+
* - `source: 'record'` — reads a top-level SQS record field such as
|
|
47
|
+
* `messageId`, `receiptHandle`, `awsRegion`, etc. The `name` option
|
|
48
|
+
* can override which record field is read.
|
|
46
49
|
*
|
|
47
|
-
* @param props - Optional extraction configuration (source, type, parse).
|
|
50
|
+
* @param props - Optional extraction configuration (source, type, parse, name).
|
|
48
51
|
*
|
|
49
52
|
* @example
|
|
50
53
|
* ```ts
|
|
@@ -55,6 +58,12 @@ exports.Payload = (0, common_1.createPayloadDecorator)({
|
|
|
55
58
|
*
|
|
56
59
|
* @Param({ source: 'body', parse: true })
|
|
57
60
|
* content: NotificationContent;
|
|
61
|
+
*
|
|
62
|
+
* @Param({ source: 'record' })
|
|
63
|
+
* messageId: string;
|
|
64
|
+
*
|
|
65
|
+
* @Param({ source: 'record', name: 'messageId' })
|
|
66
|
+
* sqsId: string;
|
|
58
67
|
* }
|
|
59
68
|
* ```
|
|
60
69
|
*/
|
|
@@ -1,4 +1,9 @@
|
|
|
1
1
|
import type { ArrayField, BooleanField, FieldProps, NumberField, ObjectField, StringField } from '@lafken/common';
|
|
2
|
+
/**
|
|
3
|
+
* Top-level string fields available on every SQS record that can be
|
|
4
|
+
* extracted with `@Param({ source: 'record' })`.
|
|
5
|
+
*/
|
|
6
|
+
export type SQSRecordField = 'messageId' | 'receiptHandle' | 'md5OfBody' | 'eventSource' | 'eventSourceARN' | 'awsRegion';
|
|
2
7
|
export interface ParamAttributeProps extends Omit<FieldProps, 'type'> {
|
|
3
8
|
/**
|
|
4
9
|
* Attribute source.
|
|
@@ -67,7 +72,30 @@ export interface ParamBodyParsedProps extends Omit<FieldProps, 'type'> {
|
|
|
67
72
|
*/
|
|
68
73
|
type?: StringConstructor | Function | [Function];
|
|
69
74
|
}
|
|
70
|
-
export
|
|
75
|
+
export interface ParamRecordProps extends Omit<FieldProps, 'type' | 'name'> {
|
|
76
|
+
/**
|
|
77
|
+
* Record source.
|
|
78
|
+
*
|
|
79
|
+
* Extracts a top-level field directly from the raw SQS record object
|
|
80
|
+
* (e.g. `messageId`, `receiptHandle`, `awsRegion`).
|
|
81
|
+
*/
|
|
82
|
+
source: 'record';
|
|
83
|
+
/**
|
|
84
|
+
* SQS record field name.
|
|
85
|
+
*
|
|
86
|
+
* Specifies which top-level property of the SQS record to extract.
|
|
87
|
+
* Defaults to the decorated property name when omitted.
|
|
88
|
+
*/
|
|
89
|
+
name?: SQSRecordField;
|
|
90
|
+
/**
|
|
91
|
+
* Attribute type.
|
|
92
|
+
*
|
|
93
|
+
* All SQS record metadata fields are strings, so only `String`
|
|
94
|
+
* is accepted here.
|
|
95
|
+
*/
|
|
96
|
+
type?: StringConstructor;
|
|
97
|
+
}
|
|
98
|
+
export type ParamProps = ParamAttributeProps | ParamBodyUnparsedProps | ParamBodyParsedProps | ParamRecordProps;
|
|
71
99
|
export type Source = Exclude<ParamProps['source'], undefined>;
|
|
72
100
|
interface QueueParamBase {
|
|
73
101
|
source: Source;
|
|
@@ -85,5 +113,8 @@ export interface QueueObjectParam extends Omit<ObjectField, 'properties'>, Queue
|
|
|
85
113
|
export interface QueueArrayParam extends Omit<ArrayField, 'items'>, QueueParamBase {
|
|
86
114
|
items: QueueParamMetadata;
|
|
87
115
|
}
|
|
88
|
-
export
|
|
116
|
+
export interface QueueRecordParam extends StringField, QueueParamBase {
|
|
117
|
+
source: 'record';
|
|
118
|
+
}
|
|
119
|
+
export type QueueParamMetadata = QueueStringParam | QueueNumberParam | QueueBooleanParam | QueueObjectParam | QueueArrayParam | QueueRecordParam;
|
|
89
120
|
export {};
|
package/lib/main/queue/queue.js
CHANGED
|
@@ -42,6 +42,9 @@ const getValueFormBody = (param, record) => {
|
|
|
42
42
|
}
|
|
43
43
|
return JSON.parse(String(value))?.[param.name];
|
|
44
44
|
};
|
|
45
|
+
const getValueFromRecord = (param, record) => {
|
|
46
|
+
return record[param.name];
|
|
47
|
+
};
|
|
45
48
|
const argumentParser = {
|
|
46
49
|
[common_1.LambdaArgumentTypes.event]: ({ event, methodName, target }) => {
|
|
47
50
|
const queueEvent = event;
|
|
@@ -57,10 +60,15 @@ const argumentParser = {
|
|
|
57
60
|
continue;
|
|
58
61
|
}
|
|
59
62
|
for (const param of paramsByHandler.properties) {
|
|
60
|
-
|
|
61
|
-
param.
|
|
62
|
-
|
|
63
|
-
|
|
63
|
+
if (param.source === 'attribute') {
|
|
64
|
+
attributes[param.destinationName] = getValueFromAttribute(param, record);
|
|
65
|
+
}
|
|
66
|
+
else if (param.source === 'record') {
|
|
67
|
+
attributes[param.destinationName] = getValueFromRecord(param, record);
|
|
68
|
+
}
|
|
69
|
+
else {
|
|
70
|
+
attributes[param.destinationName] = getValueFormBody(param, record);
|
|
71
|
+
}
|
|
64
72
|
}
|
|
65
73
|
data.push(attributes);
|
|
66
74
|
}
|
|
@@ -138,6 +138,10 @@ export interface ExternalQueueProps extends SourceMappingProps {
|
|
|
138
138
|
*
|
|
139
139
|
*/
|
|
140
140
|
queueName: string | ((props: GetResourceProps) => string);
|
|
141
|
+
/**
|
|
142
|
+
* Lambda configuration for processing messages from this queue.
|
|
143
|
+
*/
|
|
144
|
+
lambda?: LambdaProps;
|
|
141
145
|
}
|
|
142
146
|
export interface InternalFifoProps extends InternalStandardProps {
|
|
143
147
|
/**
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lafken/queue",
|
|
3
|
-
"version": "0.12.
|
|
3
|
+
"version": "0.12.9",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "Define SQS queues and consumers using TypeScript decorators - automatic infrastructure generation with Lafken",
|
|
6
6
|
"keywords": [
|
|
@@ -54,7 +54,7 @@
|
|
|
54
54
|
"dependencies": {
|
|
55
55
|
"@aws-sdk/client-sqs": "^3.1045.0",
|
|
56
56
|
"reflect-metadata": "^0.2.2",
|
|
57
|
-
"@lafken/resolver": "0.12.
|
|
57
|
+
"@lafken/resolver": "0.12.9"
|
|
58
58
|
},
|
|
59
59
|
"devDependencies": {
|
|
60
60
|
"@cdktn/provider-aws": "^24.0.0",
|
|
@@ -68,13 +68,13 @@
|
|
|
68
68
|
"typescript": "6.0.3",
|
|
69
69
|
"unplugin-swc": "^1.5.9",
|
|
70
70
|
"vitest": "^4.1.5",
|
|
71
|
-
"@lafken/common": "0.12.
|
|
71
|
+
"@lafken/common": "0.12.9"
|
|
72
72
|
},
|
|
73
73
|
"peerDependencies": {
|
|
74
74
|
"@cdktn/provider-aws": ">=23.0.0",
|
|
75
75
|
"cdktn": ">=0.22.0",
|
|
76
76
|
"constructs": "^10.4.5",
|
|
77
|
-
"@lafken/common": "0.12.
|
|
77
|
+
"@lafken/common": "0.12.9"
|
|
78
78
|
},
|
|
79
79
|
"engines": {
|
|
80
80
|
"node": ">=20.19"
|