@lafkn/queue 0.1.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.
Files changed (41) hide show
  1. package/LICENCE +21 -0
  2. package/README.md +129 -0
  3. package/lib/index.d.ts +1 -0
  4. package/lib/index.js +17 -0
  5. package/lib/main/event/event.d.ts +7 -0
  6. package/lib/main/event/event.js +33 -0
  7. package/lib/main/event/event.types.d.ts +89 -0
  8. package/lib/main/event/event.types.js +2 -0
  9. package/lib/main/event/index.d.ts +2 -0
  10. package/lib/main/event/index.js +18 -0
  11. package/lib/main/index.d.ts +2 -0
  12. package/lib/main/index.js +18 -0
  13. package/lib/main/queue/index.d.ts +2 -0
  14. package/lib/main/queue/index.js +18 -0
  15. package/lib/main/queue/queue.d.ts +6 -0
  16. package/lib/main/queue/queue.js +70 -0
  17. package/lib/main/queue/queue.types.d.ts +75 -0
  18. package/lib/main/queue/queue.types.js +2 -0
  19. package/lib/resolver/index.d.ts +1 -0
  20. package/lib/resolver/index.js +17 -0
  21. package/lib/resolver/queue/queue.d.ts +20 -0
  22. package/lib/resolver/queue/queue.js +86 -0
  23. package/lib/resolver/queue/queue.types.d.ts +7 -0
  24. package/lib/resolver/queue/queue.types.js +2 -0
  25. package/lib/resolver/resolver.d.ts +6 -0
  26. package/lib/resolver/resolver.js +29 -0
  27. package/lib/service/client/client.d.ts +2 -0
  28. package/lib/service/client/client.js +5 -0
  29. package/lib/service/commands/send-message/send-message.d.ts +7 -0
  30. package/lib/service/commands/send-message/send-message.js +25 -0
  31. package/lib/service/commands/send-message/send-message.types.d.ts +14 -0
  32. package/lib/service/commands/send-message/send-message.types.js +2 -0
  33. package/lib/service/commands/send-message-base/send-message-base.d.ts +5 -0
  34. package/lib/service/commands/send-message-base/send-message-base.js +26 -0
  35. package/lib/service/commands/send-message-batch/send-message-batch.d.ts +7 -0
  36. package/lib/service/commands/send-message-batch/send-message-batch.js +28 -0
  37. package/lib/service/commands/send-message-batch/send-message-batch.types.d.ts +5 -0
  38. package/lib/service/commands/send-message-batch/send-message-batch.types.js +2 -0
  39. package/lib/service/index.d.ts +6 -0
  40. package/lib/service/index.js +16 -0
  41. package/package.json +64 -0
package/LICENCE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Aníbal Emilio Jorquera Cornejo
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,129 @@
1
+ # @lafkn/queue
2
+
3
+ `@lafkn/queue` simplifies the creation and management of Amazon SQS queues and their integration with AWS Lambda. It provides decorators to define Standard and FIFO queues, configure their properties, and map message attributes and body content directly to your handler parameters.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install @lafkn/queue
9
+ ```
10
+
11
+ ## Configuration
12
+
13
+ Add the `QueueResolver` from the `@lafkn/queue/resolver` library to your application configuration.
14
+
15
+ ```typescript
16
+ import { QueueResolver } from '@lafkn/queue/resolver';
17
+
18
+ createApp({
19
+ name: 'awesome-app',
20
+ resolvers: [
21
+ new QueueResolver(),
22
+ ],
23
+ // ...
24
+ });
25
+
26
+ @Queue()
27
+ class GreetingQueue {
28
+ // ...
29
+ }
30
+
31
+ const greetingModule = createModule({
32
+ name: 'greeting',
33
+ resources: [
34
+ GreetingQueue
35
+ ]
36
+ });
37
+ ```
38
+
39
+ ## Features
40
+
41
+ ### Defining a Queue Resource
42
+
43
+ Use the `@Queue` decorator to define a class that contains queue-processing methods.
44
+
45
+ ```typescript
46
+ import { Queue, Standard, Fifo } from '@lafkn/queue';
47
+
48
+ @Queue()
49
+ export class NotificationService {
50
+ // ... queue handlers
51
+ }
52
+ ```
53
+
54
+ ### Standard Queues
55
+
56
+ Use the `@Standard` decorator to define a handler for a specific Standard SQS queue. You can configure properties such as visibility timeout, delivery delay, and batch size.
57
+
58
+ ```typescript
59
+ @Standard({
60
+ queueName: 'emails',
61
+ visibilityTimeout: 30,
62
+ batchSize: 10,
63
+ })
64
+ sendEmail(@Event() event: SQSEvent) {
65
+ // Process messages
66
+ }
67
+ ```
68
+
69
+ ### FIFO Queues
70
+
71
+ Use the `@Fifo` decorator for FIFO (First-In-First-Out) queues. This ensures that the message order is preserved. You can enable content-based deduplication.
72
+
73
+ ```typescript
74
+ @Fifo({
75
+ queueName: 'orders.fifo',
76
+ contentBasedDeduplication: true,
77
+ })
78
+ processOrder(@Event() event: SQSEvent) {
79
+ // Process ordered messages
80
+ }
81
+ ```
82
+
83
+ ### Message Handling & Parameter Mapping
84
+
85
+ `@lafkn/queue` allows you to extract specific data from the SQS message directly into your method parameters using `@Param` and `@Payload`.
86
+
87
+ #### Accessing Message Body Fields
88
+
89
+ You can parse the message body (if it's JSON) and inject specific fields.
90
+
91
+ ```typescript
92
+ @Standard({ queueName: 'registrations' })
93
+ registerUser(
94
+ @Param({ source: 'body', parse: true, name: 'userId' }) userId: string,
95
+ @Param({ source: 'body', parse: true, name: 'email' }) email: string
96
+ ) {
97
+ console.log(`Registering user ${userId} with email ${email}`);
98
+ }
99
+ ```
100
+
101
+ #### Accessing Message Attributes
102
+
103
+ You can also access SQS Message Attributes.
104
+
105
+ ```typescript
106
+ @Standard({ queueName: 'logging' })
107
+ logMessage(
108
+ @Param({ source: 'attribute', name: 'TraceId' }) traceId: string,
109
+ @Event() event: any
110
+ ) {
111
+ // ...
112
+ }
113
+ ```
114
+
115
+ #### Using Payload DTOs
116
+
117
+ You can use the `@Payload` decorator to map the entire message body to a typed object.
118
+
119
+ ```typescript
120
+ class UserDto {
121
+ userId: string;
122
+ email: string;
123
+ }
124
+
125
+ @Standard({ queueName: 'users' })
126
+ updateUser(@Payload() user: UserDto) {
127
+ // user is fully typed and parsed from the message body
128
+ }
129
+ ```
package/lib/index.d.ts ADDED
@@ -0,0 +1 @@
1
+ export * from './main';
package/lib/index.js ADDED
@@ -0,0 +1,17 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ __exportStar(require("./main"), exports);
@@ -0,0 +1,7 @@
1
+ import type { ParamProps } from './event.types';
2
+ export declare const queueFieldKey: string;
3
+ export declare const queuePayloadKey: string;
4
+ export declare const Payload: (props?: import("@lafkn/common").PayloadProps | undefined) => (target: Function) => void;
5
+ export declare const Param: (props?: ParamProps | undefined) => (target: any, destinationName: string) => void;
6
+ export declare const Field: (props?: import("@lafkn/common").FieldProps | undefined) => (target: any, destinationName: string) => void;
7
+ export declare const Event: (eventField: Function) => (target: any, methodName: string, _number: number) => void;
@@ -0,0 +1,33 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.Event = exports.Field = exports.Param = exports.Payload = exports.queuePayloadKey = exports.queueFieldKey = void 0;
4
+ const common_1 = require("@lafkn/common");
5
+ const queue_1 = require("../queue");
6
+ exports.queueFieldKey = (0, common_1.createFieldName)(queue_1.RESOURCE_TYPE, common_1.FieldProperties.field);
7
+ exports.queuePayloadKey = (0, common_1.createFieldName)(queue_1.RESOURCE_TYPE, common_1.FieldProperties.payload);
8
+ exports.Payload = (0, common_1.createPayloadDecorator)({
9
+ prefix: queue_1.RESOURCE_TYPE,
10
+ createUniqueId: false,
11
+ enableInLambdaInvocation: true,
12
+ });
13
+ exports.Param = (0, common_1.createFieldDecorator)({
14
+ prefix: queue_1.RESOURCE_TYPE,
15
+ enableInLambdaInvocation: true,
16
+ getMetadata: (props) => {
17
+ const source = props?.source || 'attribute';
18
+ return {
19
+ source,
20
+ parse: props?.source === 'body' && !!props?.parse,
21
+ };
22
+ },
23
+ });
24
+ exports.Field = (0, common_1.createFieldDecorator)({
25
+ prefix: queue_1.RESOURCE_TYPE,
26
+ enableInLambdaInvocation: true,
27
+ getMetadata: () => ({}),
28
+ });
29
+ const Event = (eventField) => (0, common_1.createEventDecorator)({
30
+ prefix: queue_1.RESOURCE_TYPE,
31
+ enableInLambdaInvocation: true,
32
+ })(eventField);
33
+ exports.Event = Event;
@@ -0,0 +1,89 @@
1
+ import type { ArrayField, BooleanField, FieldProps, NumberField, ObjectField, StringField } from '@lafkn/common';
2
+ export interface ParamAttributeProps extends Omit<FieldProps, 'type'> {
3
+ /**
4
+ * Attribute source.
5
+ *
6
+ * Specifies the source from which to obtain the attribute value.
7
+ * This can be used to map values from the event
8
+ * when processing messages.
9
+ */
10
+ source?: 'attribute';
11
+ /**
12
+ * Attribute type.
13
+ *
14
+ * Specifies the data type of the attribute. This can be used to
15
+ * enforce the type of the value extracted from the source before
16
+ * passing it to the consumer or processing logic.
17
+ */
18
+ type?: NumberConstructor | StringConstructor;
19
+ }
20
+ export interface ParamBodyUnparsedProps extends Omit<FieldProps, 'type'> {
21
+ /**
22
+ * Attribute source.
23
+ *
24
+ * Specifies the source from which to obtain the attribute value.
25
+ * This can be used to map values from the event
26
+ * when processing messages.
27
+ */
28
+ source: 'body';
29
+ /**
30
+ * Parse message body.
31
+ *
32
+ * Specifies whether the message body should be parsed and converted
33
+ * into a JavaScript object before being passed to the consumer.
34
+ */
35
+ parse?: false;
36
+ /**
37
+ * Attribute type.
38
+ *
39
+ * Specifies the data type of the attribute. This can be used to
40
+ * enforce the type of the value extracted from the source before
41
+ * passing it to the consumer or processing logic.
42
+ */
43
+ type?: StringConstructor;
44
+ }
45
+ export interface ParamBodyParsedProps extends Omit<FieldProps, 'type'> {
46
+ /**
47
+ * Attribute source.
48
+ *
49
+ * Specifies the source from which to obtain the attribute value.
50
+ * This can be used to map values from the event
51
+ * when processing messages.
52
+ */
53
+ source: 'body';
54
+ /**
55
+ * Parse message body.
56
+ *
57
+ * Specifies whether the message body should be parsed and converted
58
+ * into a JavaScript object before being passed to the consumer.
59
+ */
60
+ parse: true;
61
+ /**
62
+ * Attribute type.
63
+ *
64
+ * Specifies the data type of the attribute. This can be used to
65
+ * enforce the type of the value extracted from the source before
66
+ * passing it to the consumer or processing logic.
67
+ */
68
+ type?: StringConstructor | Function | [Function];
69
+ }
70
+ export type ParamProps = ParamAttributeProps | ParamBodyUnparsedProps | ParamBodyParsedProps;
71
+ export type Source = Exclude<ParamProps['source'], undefined>;
72
+ interface QueueParamBase {
73
+ source: Source;
74
+ parse: boolean;
75
+ }
76
+ export interface QueueStringParam extends StringField, QueueParamBase {
77
+ }
78
+ export interface QueueNumberParam extends NumberField, QueueParamBase {
79
+ }
80
+ export interface QueueBooleanParam extends BooleanField, QueueParamBase {
81
+ }
82
+ export interface QueueObjectParam extends Omit<ObjectField, 'properties'>, QueueParamBase {
83
+ properties: QueueParamMetadata[];
84
+ }
85
+ export interface QueueArrayParam extends Omit<ArrayField, 'items'>, QueueParamBase {
86
+ items: QueueParamMetadata;
87
+ }
88
+ export type QueueParamMetadata = QueueStringParam | QueueNumberParam | QueueBooleanParam | QueueObjectParam | QueueArrayParam;
89
+ export {};
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,2 @@
1
+ export * from './event';
2
+ export * from './event.types';
@@ -0,0 +1,18 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ __exportStar(require("./event"), exports);
18
+ __exportStar(require("./event.types"), exports);
@@ -0,0 +1,2 @@
1
+ export * from './event';
2
+ export * from './queue';
@@ -0,0 +1,18 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ __exportStar(require("./event"), exports);
18
+ __exportStar(require("./queue"), exports);
@@ -0,0 +1,2 @@
1
+ export * from './queue';
2
+ export * from './queue.types';
@@ -0,0 +1,18 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ __exportStar(require("./queue"), exports);
18
+ __exportStar(require("./queue.types"), exports);
@@ -0,0 +1,6 @@
1
+ import 'reflect-metadata';
2
+ import type { FifoProps, StandardProps } from './queue.types';
3
+ export declare const RESOURCE_TYPE: "QUEUE";
4
+ export declare const Queue: (props?: import("@lafkn/common").ResourceProps | undefined) => (constructor: Function) => void;
5
+ export declare const Standard: (props?: StandardProps | undefined) => (target: any, methodName: string, descriptor: PropertyDescriptor) => any;
6
+ export declare const Fifo: (props?: FifoProps | undefined) => (target: any, methodName: string, descriptor: PropertyDescriptor) => any;
@@ -0,0 +1,70 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.Fifo = exports.Standard = exports.Queue = exports.RESOURCE_TYPE = void 0;
4
+ require("reflect-metadata");
5
+ const common_1 = require("@lafkn/common");
6
+ exports.RESOURCE_TYPE = 'QUEUE';
7
+ exports.Queue = (0, common_1.createResourceDecorator)({
8
+ type: exports.RESOURCE_TYPE,
9
+ callerFileIndex: 5,
10
+ });
11
+ const getValueFromAttribute = (param, record) => {
12
+ const value = record.messageAttributes[param.name];
13
+ if (!value) {
14
+ return;
15
+ }
16
+ if (param.type === 'Number') {
17
+ return Number(value.stringValue);
18
+ }
19
+ return value.stringValue;
20
+ };
21
+ const getValueFormBody = (param, record) => {
22
+ const value = record.body;
23
+ if (!value || !param.parse) {
24
+ return value;
25
+ }
26
+ return JSON.parse(String(value))?.[param.name];
27
+ };
28
+ const argumentParser = {
29
+ [common_1.LambdaArgumentTypes.event]: ({ event, methodName, target }) => {
30
+ const queueEvent = event;
31
+ const data = [];
32
+ const params = Reflect.getMetadata(common_1.LambdaReflectKeys.event_param, target) || {};
33
+ const paramsByHandler = params[methodName];
34
+ if (!event || !queueEvent.Records || !paramsByHandler) {
35
+ return event;
36
+ }
37
+ for (const record of queueEvent.Records) {
38
+ const attributes = {};
39
+ if (paramsByHandler.type !== 'Object') {
40
+ continue;
41
+ }
42
+ for (const param of paramsByHandler.properties) {
43
+ attributes[param.destinationName] =
44
+ param.source === 'attribute'
45
+ ? getValueFromAttribute(param, record)
46
+ : getValueFormBody(param, record);
47
+ }
48
+ data.push(attributes);
49
+ }
50
+ return data;
51
+ },
52
+ };
53
+ exports.Standard = (0, common_1.createLambdaDecorator)({
54
+ getLambdaMetadata: (props, methodName) => ({
55
+ ...props,
56
+ queueName: props.queueName || methodName,
57
+ name: methodName,
58
+ isFifo: false,
59
+ }),
60
+ argumentParser,
61
+ });
62
+ exports.Fifo = (0, common_1.createLambdaDecorator)({
63
+ getLambdaMetadata: (props, methodName) => ({
64
+ ...props,
65
+ queueName: props.queueName || methodName,
66
+ name: methodName,
67
+ isFifo: true,
68
+ }),
69
+ argumentParser,
70
+ });
@@ -0,0 +1,75 @@
1
+ import type { LambdaMetadata, LambdaProps, QueueNames } from '@lafkn/common';
2
+ export interface StandardProps {
3
+ /**
4
+ * Delivery delay in seconds.
5
+ *
6
+ * Specifies the amount of time to delay the delivery of a message
7
+ * to the queue after it is sent.
8
+ */
9
+ deliveryDelay?: number;
10
+ /**
11
+ * Maximum message size in bytes.
12
+ *
13
+ * Specifies the limit of message size that the queue can accept.
14
+ * Messages exceeding this size will be rejected.
15
+ */
16
+ maxMessageSizeBytes?: number;
17
+ /**
18
+ * Message retention period in seconds.
19
+ *
20
+ * The duration that messages are kept in the queue before being deleted.
21
+ */
22
+ retentionPeriod?: number;
23
+ /**
24
+ * Visibility timeout in seconds.
25
+ *
26
+ * The duration that a received message is hidden from other consumers
27
+ * while being processed.
28
+ */
29
+ visibilityTimeout?: number;
30
+ /**
31
+ * Maximum number of messages to retrieve in a single batch.
32
+ *
33
+ * Only applicable when consuming messages with a Lambda or batch processor.
34
+ */
35
+ batchSize?: 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10;
36
+ /**
37
+ * Maximum concurrency for Lambda consumers.
38
+ *
39
+ * Specifies the maximum number of Lambda functions that
40
+ * can process messages from the queue concurrently.
41
+ */
42
+ maxConcurrency?: number;
43
+ /**
44
+ * Maximum batching window in seconds.
45
+ *
46
+ * Defines the maximum amount of time to gather messages into a batch
47
+ * before sending them to the consumer.
48
+ */
49
+ maxBatchingWindow?: number;
50
+ /**
51
+ * Lambda configuration for processing messages from this queue.
52
+ */
53
+ lambda?: LambdaProps;
54
+ /**
55
+ * Name of the queue.
56
+ *
57
+ * If not specified, a default name based on the resource or class is used.
58
+ */
59
+ queueName?: QueueNames;
60
+ }
61
+ export interface FifoProps extends StandardProps {
62
+ /**
63
+ * Enable content-based deduplication.
64
+ *
65
+ * Specifies whether the queue should use the content of the message
66
+ * to generate the deduplication ID automatically. This ensures that
67
+ * messages with identical content are treated as duplicates and
68
+ * are not delivered multiple times within the deduplication interval.
69
+ */
70
+ contentBasedDeduplication?: boolean;
71
+ }
72
+ export interface QueueLambdaMetadata extends LambdaMetadata, Omit<FifoProps, 'queueName'> {
73
+ queueName: string;
74
+ isFifo: boolean;
75
+ }
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1 @@
1
+ export * from './resolver';
@@ -0,0 +1,17 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ __exportStar(require("./resolver"), exports);
@@ -0,0 +1,20 @@
1
+ import { SqsQueue } from '@cdktf/provider-aws/lib/sqs-queue';
2
+ import { type AppModule } from '@lafkn/resolver';
3
+ import type { QueueProps } from './queue.types';
4
+ declare const Queue_base: {
5
+ new (...args: any[]): {
6
+ isGlobal(module: import("@lafkn/common").ModuleGlobalReferenceNames | (string & {}), id: string): void;
7
+ isDependent(resolveDependency: () => void): void;
8
+ readonly node: import("constructs").Node;
9
+ toString(): string;
10
+ };
11
+ } & typeof SqsQueue;
12
+ export declare class Queue extends Queue_base {
13
+ private props;
14
+ constructor(scope: AppModule, id: string, props: QueueProps);
15
+ private addEventSource;
16
+ private validateEventParams;
17
+ private getParams;
18
+ private validateParamType;
19
+ }
20
+ export {};
@@ -0,0 +1,86 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.Queue = void 0;
4
+ const lambda_event_source_mapping_1 = require("@cdktf/provider-aws/lib/lambda-event-source-mapping");
5
+ const sqs_queue_1 = require("@cdktf/provider-aws/lib/sqs-queue");
6
+ const common_1 = require("@lafkn/common");
7
+ const resolver_1 = require("@lafkn/resolver");
8
+ const attributeAllowedTypes = new Set(['String', 'Number']);
9
+ const bodyParsedTypes = new Set(['String', 'Object', 'Array']);
10
+ const bodyUnparsedTypes = new Set(['String']);
11
+ class Queue extends resolver_1.lafknResource.make(sqs_queue_1.SqsQueue) {
12
+ props;
13
+ constructor(scope, id, props) {
14
+ const { handler } = props;
15
+ super(scope, `${id}-queue`, {
16
+ name: `${handler.queueName}${handler.isFifo ? '.fifo' : ''}`,
17
+ fifoQueue: handler.isFifo,
18
+ contentBasedDeduplication: handler.contentBasedDeduplication,
19
+ visibilityTimeoutSeconds: handler.visibilityTimeout,
20
+ messageRetentionSeconds: handler.retentionPeriod,
21
+ maxMessageSize: handler.maxMessageSizeBytes,
22
+ delaySeconds: handler.deliveryDelay,
23
+ });
24
+ this.props = props;
25
+ this.isGlobal(scope.id, handler.queueName);
26
+ this.validateEventParams();
27
+ this.addEventSource(id);
28
+ }
29
+ addEventSource(id) {
30
+ const { handler, resourceMetadata } = this.props;
31
+ const lambdaHandler = new resolver_1.LambdaHandler(this, `${id}-handler`, {
32
+ ...handler,
33
+ originalName: resourceMetadata.originalName,
34
+ filename: resourceMetadata.filename,
35
+ foldername: resourceMetadata.foldername,
36
+ suffix: 'queue',
37
+ principal: 'sqs.amazonaws.com',
38
+ });
39
+ new lambda_event_source_mapping_1.LambdaEventSourceMapping(this, 'event-mapping', {
40
+ batchSize: handler.batchSize,
41
+ eventSourceArn: this.arn,
42
+ functionName: lambdaHandler.arn,
43
+ maximumBatchingWindowInSeconds: handler.maxBatchingWindow,
44
+ functionResponseTypes: handler.isFifo ? ['ReportBatchItemFailures'] : undefined,
45
+ scalingConfig: handler.maxConcurrency
46
+ ? {
47
+ maximumConcurrency: handler.maxConcurrency,
48
+ }
49
+ : undefined,
50
+ dependsOn: [lambdaHandler, this],
51
+ });
52
+ }
53
+ validateEventParams() {
54
+ const param = this.getParams();
55
+ if (!param) {
56
+ return;
57
+ }
58
+ let bodyCount = 0;
59
+ for (const property of param.properties) {
60
+ this.validateParamType(property);
61
+ if (property.source === 'body') {
62
+ bodyCount++;
63
+ }
64
+ if (bodyCount >= 2) {
65
+ throw new Error('Queue event only support one body param');
66
+ }
67
+ }
68
+ }
69
+ getParams() {
70
+ const { classResource, handler } = this.props;
71
+ const params = (0, common_1.getMetadataPrototypeByKey)(classResource, common_1.LambdaReflectKeys.event_param) || {};
72
+ return params[handler.name];
73
+ }
74
+ validateParamType(param) {
75
+ if (param.source === 'attribute' && !attributeAllowedTypes.has(param.type)) {
76
+ throw new Error(`Attribute params only support ${[...attributeAllowedTypes].join(', ')} values`);
77
+ }
78
+ if (param.source === 'body' && param.parse && !bodyParsedTypes.has(param.type)) {
79
+ throw new Error(`Body params only support ${[...bodyParsedTypes].join(', ')} values`);
80
+ }
81
+ if (param?.source === 'body' && !param.parse && !bodyUnparsedTypes.has(param.type)) {
82
+ throw new Error(`Body params only support ${[...bodyUnparsedTypes].join(', ')} values`);
83
+ }
84
+ }
85
+ }
86
+ exports.Queue = Queue;
@@ -0,0 +1,7 @@
1
+ import type { ClassResource, ResourceMetadata } from '@lafkn/common';
2
+ import type { QueueLambdaMetadata } from '../../main';
3
+ export interface QueueProps {
4
+ handler: QueueLambdaMetadata;
5
+ resourceMetadata: ResourceMetadata;
6
+ classResource: ClassResource;
7
+ }
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,6 @@
1
+ import { type ClassResource } from '@lafkn/common';
2
+ import { type AppModule, type ResolverType } from '@lafkn/resolver';
3
+ export declare class QueueResolver implements ResolverType {
4
+ type: "QUEUE";
5
+ create(module: AppModule, resource: ClassResource): void;
6
+ }
@@ -0,0 +1,29 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.QueueResolver = void 0;
4
+ const common_1 = require("@lafkn/common");
5
+ const resolver_1 = require("@lafkn/resolver");
6
+ const main_1 = require("../main");
7
+ const queue_1 = require("./queue/queue");
8
+ class QueueResolver {
9
+ type = main_1.RESOURCE_TYPE;
10
+ create(module, resource) {
11
+ const metadata = (0, common_1.getResourceMetadata)(resource);
12
+ const handlers = (0, common_1.getResourceHandlerMetadata)(resource);
13
+ resolver_1.lambdaAssets.initializeMetadata({
14
+ foldername: metadata.foldername,
15
+ filename: metadata.filename,
16
+ minify: metadata.minify,
17
+ className: metadata.originalName,
18
+ methods: handlers.map((handler) => handler.name),
19
+ });
20
+ for (const handler of handlers) {
21
+ new queue_1.Queue(module, `${metadata.name}-${handler.name}`, {
22
+ resourceMetadata: metadata,
23
+ classResource: resource,
24
+ handler,
25
+ });
26
+ }
27
+ }
28
+ }
29
+ exports.QueueResolver = QueueResolver;
@@ -0,0 +1,2 @@
1
+ import { SQSClient } from '@aws-sdk/client-sqs';
2
+ export declare const client: SQSClient;
@@ -0,0 +1,5 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.client = void 0;
4
+ const client_sqs_1 = require("@aws-sdk/client-sqs");
5
+ exports.client = new client_sqs_1.SQSClient();
@@ -0,0 +1,7 @@
1
+ import { SendMessageBase } from '../send-message-base/send-message-base';
2
+ import type { SendMessageProps } from './send-message.types';
3
+ export declare class SendMessage extends SendMessageBase {
4
+ private props;
5
+ constructor(props: SendMessageProps);
6
+ exec(): Promise<import("@aws-sdk/client-sqs").SendMessageCommandOutput>;
7
+ }
@@ -0,0 +1,25 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.SendMessage = void 0;
4
+ const client_sqs_1 = require("@aws-sdk/client-sqs");
5
+ const client_1 = require("../../client/client");
6
+ const send_message_base_1 = require("../send-message-base/send-message-base");
7
+ class SendMessage extends send_message_base_1.SendMessageBase {
8
+ props;
9
+ constructor(props) {
10
+ super();
11
+ this.props = props;
12
+ }
13
+ exec() {
14
+ const command = new client_sqs_1.SendMessageCommand({
15
+ QueueUrl: this.props.url,
16
+ MessageBody: this.getBody(this.props.body),
17
+ MessageAttributes: this.getAttributes(this.props.body),
18
+ DelaySeconds: this.props.delay,
19
+ MessageGroupId: this.props.groupId,
20
+ MessageDeduplicationId: this.props.deduplicationId,
21
+ });
22
+ return client_1.client.send(command);
23
+ }
24
+ }
25
+ exports.SendMessage = SendMessage;
@@ -0,0 +1,14 @@
1
+ export interface SendMessageProps {
2
+ url: string;
3
+ attributes?: Record<string, number | string>;
4
+ body?: any;
5
+ delay?: number;
6
+ /**
7
+ * only for fifo queues
8
+ */
9
+ deduplicationId?: string;
10
+ /**
11
+ * only for fifo queues
12
+ */
13
+ groupId?: string;
14
+ }
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,5 @@
1
+ import type { MessageAttributeValue } from '@aws-sdk/client-sqs';
2
+ export declare class SendMessageBase {
3
+ getAttributes(messageAttributes?: Record<string, number | string>): Record<string, MessageAttributeValue> | undefined;
4
+ getBody(body?: any): string | undefined;
5
+ }
@@ -0,0 +1,26 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.SendMessageBase = void 0;
4
+ class SendMessageBase {
5
+ getAttributes(messageAttributes) {
6
+ if (!messageAttributes) {
7
+ return undefined;
8
+ }
9
+ const attributes = {};
10
+ for (const attributeKey in messageAttributes) {
11
+ const value = messageAttributes[attributeKey];
12
+ attributes[attributeKey] = {
13
+ StringValue: String(messageAttributes[attributeKey]),
14
+ DataType: typeof value === 'number' ? 'Number' : 'String',
15
+ };
16
+ }
17
+ return attributes;
18
+ }
19
+ getBody(body) {
20
+ if (!body) {
21
+ return undefined;
22
+ }
23
+ return JSON.stringify(body);
24
+ }
25
+ }
26
+ exports.SendMessageBase = SendMessageBase;
@@ -0,0 +1,7 @@
1
+ import { SendMessageBase } from '../send-message-base/send-message-base';
2
+ import type { SendMessagesBatchProps } from './send-message-batch.types';
3
+ export declare class SendMessageBatch extends SendMessageBase {
4
+ private props;
5
+ constructor(props: SendMessagesBatchProps);
6
+ exec(): Promise<import("@aws-sdk/client-sqs").SendMessageBatchCommandOutput>;
7
+ }
@@ -0,0 +1,28 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.SendMessageBatch = void 0;
4
+ const client_sqs_1 = require("@aws-sdk/client-sqs");
5
+ const client_1 = require("../../client/client");
6
+ const send_message_base_1 = require("../send-message-base/send-message-base");
7
+ class SendMessageBatch extends send_message_base_1.SendMessageBase {
8
+ props;
9
+ constructor(props) {
10
+ super();
11
+ this.props = props;
12
+ }
13
+ exec() {
14
+ const command = new client_sqs_1.SendMessageBatchCommand({
15
+ QueueUrl: this.props.url,
16
+ Entries: this.props.messages.map((message, index) => ({
17
+ Id: index.toString(),
18
+ MessageBody: this.getBody(message.body),
19
+ MessageAttributes: this.getAttributes(message.attributes),
20
+ DelaySeconds: message.delay,
21
+ MessageGroupId: message.groupId,
22
+ MessageDeduplicationId: message.deduplicationId,
23
+ })),
24
+ });
25
+ return client_1.client.send(command);
26
+ }
27
+ }
28
+ exports.SendMessageBatch = SendMessageBatch;
@@ -0,0 +1,5 @@
1
+ import type { SendMessageProps } from '../send-message/send-message.types';
2
+ export interface SendMessagesBatchProps {
3
+ url: string;
4
+ messages: Omit<SendMessageProps, 'url'>[];
5
+ }
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,6 @@
1
+ import type { SendMessageProps } from './commands/send-message/send-message.types';
2
+ import type { SendMessagesBatchProps } from './commands/send-message-batch/send-message-batch.types';
3
+ export declare class QueueService {
4
+ static sendMessage(props: SendMessageProps): Promise<void>;
5
+ static sendBatchMessage(props: SendMessagesBatchProps): Promise<void>;
6
+ }
@@ -0,0 +1,16 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.QueueService = void 0;
4
+ const send_message_1 = require("./commands/send-message/send-message");
5
+ const send_message_batch_1 = require("./commands/send-message-batch/send-message-batch");
6
+ class QueueService {
7
+ static async sendMessage(props) {
8
+ const queueCommand = new send_message_1.SendMessage(props);
9
+ await queueCommand.exec();
10
+ }
11
+ static async sendBatchMessage(props) {
12
+ const queueCommand = new send_message_batch_1.SendMessageBatch(props);
13
+ await queueCommand.exec();
14
+ }
15
+ }
16
+ exports.QueueService = QueueService;
package/package.json ADDED
@@ -0,0 +1,64 @@
1
+ {
2
+ "name": "@lafkn/queue",
3
+ "version": "0.1.0",
4
+ "private": false,
5
+ "license": "MIT",
6
+ "exports": {
7
+ "./main": {
8
+ "import": "./lib/main/index.js",
9
+ "types": "./lib/main/index.d.ts",
10
+ "require": "./lib/main/index.js"
11
+ },
12
+ "./service": {
13
+ "import": "./lib/service/index.js",
14
+ "types": "./lib/service/index.d.ts",
15
+ "require": "./lib/service/index.js"
16
+ },
17
+ "./resolver": {
18
+ "import": "./lib/resolver/index.js",
19
+ "types": "./lib/resolver/index.d.ts",
20
+ "require": "./lib/resolver/index.js"
21
+ }
22
+ },
23
+ "typesVersions": {
24
+ "*": {
25
+ "main": [
26
+ "./lib/main/index.d.ts"
27
+ ],
28
+ "resolver": [
29
+ "./lib/resolver/index.d.ts"
30
+ ]
31
+ }
32
+ },
33
+ "files": [
34
+ "lib"
35
+ ],
36
+ "dependencies": {
37
+ "@aws-sdk/client-sqs": "3.956.0",
38
+ "@cdktf/provider-aws": "21.22.0",
39
+ "aws-lambda": "1.0.7",
40
+ "cdktf": "0.21.0",
41
+ "constructs": "10.4.4",
42
+ "reflect-metadata": "0.2.2",
43
+ "@lafkn/common": "0.1.0",
44
+ "@lafkn/resolver": "0.1.0"
45
+ },
46
+ "devDependencies": {
47
+ "@jest/types": "^30.2.0",
48
+ "@types/aws-lambda": "8.10.159",
49
+ "@types/jest": "30.0.0",
50
+ "jest": "30.2.0",
51
+ "ts-jest": "29.4.6",
52
+ "ts-node": "10.9.2"
53
+ },
54
+ "publishConfig": {
55
+ "access": "public"
56
+ },
57
+ "scripts": {
58
+ "build": "pnpm clean && tsc -p ./tsconfig.build.json",
59
+ "clean": "rm -rf ./lib",
60
+ "dev": "tsc -w",
61
+ "test": "jest",
62
+ "test:coverage": "jest --coverage"
63
+ }
64
+ }