@fgiova/sqs-consumer 2.0.1 → 2.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.
- package/README.md +3 -3
- package/dist/{hooks.d.ts → cjs/hooks.d.ts} +2 -3
- package/dist/{hooks.js → cjs/hooks.js} +55 -50
- package/dist/{index.d.ts → cjs/index.d.ts} +4 -4
- package/dist/{index.js → cjs/index.js} +35 -35
- package/dist/{logger.d.ts → cjs/logger.d.ts} +1 -0
- package/dist/esm/errors.d.mts +3 -0
- package/dist/esm/errors.mjs +7 -0
- package/dist/esm/hooks.d.mts +13 -0
- package/dist/esm/hooks.mjs +116 -0
- package/dist/esm/index.d.mts +76 -0
- package/dist/esm/index.mjs +213 -0
- package/dist/esm/logger.d.mts +12 -0
- package/dist/esm/logger.mjs +1 -0
- package/package.json +27 -13
- /package/dist/{errors.d.ts → cjs/errors.d.ts} +0 -0
- /package/dist/{errors.js → cjs/errors.js} +0 -0
- /package/dist/{logger.js → cjs/logger.js} +0 -0
package/README.md
CHANGED
|
@@ -3,8 +3,8 @@
|
|
|
3
3
|
[](https://www.npmjs.com/package/@fgiova/sqs-consumer)
|
|
4
4
|

|
|
5
5
|
[](http://www.typescriptlang.org/)
|
|
6
|
-
[](https://qlty.sh/gh/fgiova/projects/sqs-consumer)
|
|
7
|
+
[](https://qlty.sh/gh/fgiova/projects/sqs-consumer)
|
|
8
8
|
|
|
9
9
|
## Description
|
|
10
10
|
This module allows consuming SQS messages using @fgiova/mini-sqs-client thorough the aws-json protocol with "undici" as http agent .<br />
|
|
@@ -88,4 +88,4 @@ SQSConsumer.addHook(hookname: string, func: Function): void
|
|
|
88
88
|
```
|
|
89
89
|
|
|
90
90
|
## License
|
|
91
|
-
Licensed under [MIT](./LICENSE).
|
|
91
|
+
Licensed under [MIT](./LICENSE).
|
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import { Logger } from "./logger";
|
|
2
|
-
import { Message } from "@fgiova/mini-sqs-client";
|
|
1
|
+
import type { Logger } from "./logger";
|
|
3
2
|
export type HookName = "onPoll" | "onMessage" | "onHandlerSuccess" | "onHandlerTimeout" | "onHandlerError" | "onSuccess" | "onError" | "onSQSError";
|
|
4
3
|
export declare class Hooks {
|
|
5
4
|
private readonly hookSymbols;
|
|
@@ -7,7 +6,7 @@ export declare class Hooks {
|
|
|
7
6
|
private readonly logger;
|
|
8
7
|
constructor(logger?: Logger);
|
|
9
8
|
addHook(hook: HookName, fn: Function): void;
|
|
10
|
-
runHook(hook: HookName, ...args: any[]): Promise<
|
|
9
|
+
runHook(hook: HookName, ...args: any[]): Promise<any>;
|
|
11
10
|
private runHookWithVoidReturn;
|
|
12
11
|
private runHookWithMessageReturn;
|
|
13
12
|
private runHookWithBooleanReturn;
|
|
@@ -2,52 +2,49 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.Hooks = void 0;
|
|
4
4
|
class Hooks {
|
|
5
|
-
hookSymbols = {
|
|
6
|
-
onPoll: {
|
|
7
|
-
S: Symbol("poll"),
|
|
8
|
-
throwable: true,
|
|
9
|
-
type: "message-return"
|
|
10
|
-
},
|
|
11
|
-
onMessage: {
|
|
12
|
-
S: Symbol("message"),
|
|
13
|
-
throwable: true,
|
|
14
|
-
type: "message-return"
|
|
15
|
-
},
|
|
16
|
-
onHandlerSuccess: {
|
|
17
|
-
S: Symbol("handlerSuccess"),
|
|
18
|
-
throwable: false,
|
|
19
|
-
type: "message-return"
|
|
20
|
-
},
|
|
21
|
-
onHandlerTimeout: {
|
|
22
|
-
S: Symbol("handlerTimeout"),
|
|
23
|
-
throwable: false,
|
|
24
|
-
type: "boolean-return"
|
|
25
|
-
},
|
|
26
|
-
onHandlerError: {
|
|
27
|
-
S: Symbol("handlerError"),
|
|
28
|
-
throwable: false,
|
|
29
|
-
type: "boolean-return"
|
|
30
|
-
},
|
|
31
|
-
onSuccess: {
|
|
32
|
-
S: Symbol("success"),
|
|
33
|
-
throwable: false,
|
|
34
|
-
type: "boolean-return"
|
|
35
|
-
},
|
|
36
|
-
onError: {
|
|
37
|
-
S: Symbol("error"),
|
|
38
|
-
throwable: false,
|
|
39
|
-
type: "boolean-return"
|
|
40
|
-
},
|
|
41
|
-
onSQSError: {
|
|
42
|
-
S: Symbol("sqsError"),
|
|
43
|
-
throwable: false,
|
|
44
|
-
type: "void"
|
|
45
|
-
},
|
|
46
|
-
};
|
|
47
|
-
// eslint-disable-next-line @typescript-eslint/ban-types
|
|
48
|
-
hooks;
|
|
49
|
-
logger;
|
|
50
5
|
constructor(logger = console) {
|
|
6
|
+
this.hookSymbols = {
|
|
7
|
+
onPoll: {
|
|
8
|
+
S: Symbol("poll"),
|
|
9
|
+
throwable: true,
|
|
10
|
+
type: "message-return",
|
|
11
|
+
},
|
|
12
|
+
onMessage: {
|
|
13
|
+
S: Symbol("message"),
|
|
14
|
+
throwable: true,
|
|
15
|
+
type: "message-return",
|
|
16
|
+
},
|
|
17
|
+
onHandlerSuccess: {
|
|
18
|
+
S: Symbol("handlerSuccess"),
|
|
19
|
+
throwable: false,
|
|
20
|
+
type: "message-return",
|
|
21
|
+
},
|
|
22
|
+
onHandlerTimeout: {
|
|
23
|
+
S: Symbol("handlerTimeout"),
|
|
24
|
+
throwable: false,
|
|
25
|
+
type: "boolean-return",
|
|
26
|
+
},
|
|
27
|
+
onHandlerError: {
|
|
28
|
+
S: Symbol("handlerError"),
|
|
29
|
+
throwable: false,
|
|
30
|
+
type: "boolean-return",
|
|
31
|
+
},
|
|
32
|
+
onSuccess: {
|
|
33
|
+
S: Symbol("success"),
|
|
34
|
+
throwable: false,
|
|
35
|
+
type: "boolean-return",
|
|
36
|
+
},
|
|
37
|
+
onError: {
|
|
38
|
+
S: Symbol("error"),
|
|
39
|
+
throwable: false,
|
|
40
|
+
type: "boolean-return",
|
|
41
|
+
},
|
|
42
|
+
onSQSError: {
|
|
43
|
+
S: Symbol("sqsError"),
|
|
44
|
+
throwable: false,
|
|
45
|
+
type: "void",
|
|
46
|
+
},
|
|
47
|
+
};
|
|
51
48
|
this.logger = logger;
|
|
52
49
|
this.hooks = {
|
|
53
50
|
[this.hookSymbols.onPoll.S]: [],
|
|
@@ -60,13 +57,13 @@ class Hooks {
|
|
|
60
57
|
[this.hookSymbols.onSQSError.S]: [],
|
|
61
58
|
};
|
|
62
59
|
}
|
|
63
|
-
// eslint-disable-next-line @typescript-eslint/ban-types
|
|
64
60
|
addHook(hook, fn) {
|
|
65
61
|
const hookSymbol = this.hookSymbols[hook];
|
|
66
62
|
if (!hookSymbol)
|
|
67
63
|
throw new Error(`Invalid hook ${hook}`);
|
|
68
64
|
this.hooks[hookSymbol.S]?.push(fn);
|
|
69
65
|
}
|
|
66
|
+
// biome-ignore lint/suspicious/noExplicitAny: args can be anything
|
|
70
67
|
async runHook(hook, ...args) {
|
|
71
68
|
const hookSymbol = this.hookSymbols[hook];
|
|
72
69
|
try {
|
|
@@ -75,13 +72,15 @@ class Hooks {
|
|
|
75
72
|
switch (hookSymbol.type) {
|
|
76
73
|
case "boolean-return":
|
|
77
74
|
return await this.runHookWithBooleanReturn(hookSymbol, ...args);
|
|
78
|
-
case "message-return":
|
|
75
|
+
case "message-return": {
|
|
79
76
|
const [message, ...someArgs] = args;
|
|
80
77
|
return await this.runHookWithMessageReturn(hookSymbol, message, ...someArgs);
|
|
78
|
+
}
|
|
81
79
|
case "void":
|
|
82
80
|
return await this.runHookWithVoidReturn(hookSymbol, ...args);
|
|
83
81
|
}
|
|
84
82
|
/* c8 ignore next 1 */
|
|
83
|
+
// biome-ignore lint/suspicious/noExplicitAny: error type is not important
|
|
85
84
|
}
|
|
86
85
|
catch (error) {
|
|
87
86
|
error.message = `Error running hook ${hook}: ${error.message}`;
|
|
@@ -90,19 +89,25 @@ class Hooks {
|
|
|
90
89
|
this.logger.error(error);
|
|
91
90
|
}
|
|
92
91
|
}
|
|
93
|
-
async runHookWithVoidReturn(hookSymbol,
|
|
92
|
+
async runHookWithVoidReturn(hookSymbol,
|
|
93
|
+
// biome-ignore lint/suspicious/noExplicitAny: args can be anything
|
|
94
|
+
...args) {
|
|
94
95
|
for (const fn of this.hooks[hookSymbol.S]) {
|
|
95
96
|
await fn(...args);
|
|
96
97
|
}
|
|
97
98
|
return;
|
|
98
99
|
}
|
|
99
|
-
async runHookWithMessageReturn(hookSymbol, message,
|
|
100
|
+
async runHookWithMessageReturn(hookSymbol, message,
|
|
101
|
+
// biome-ignore lint/suspicious/noExplicitAny: args can be anything
|
|
102
|
+
...args) {
|
|
100
103
|
for (const fn of this.hooks[hookSymbol.S]) {
|
|
101
104
|
message = await fn(message, ...args);
|
|
102
105
|
}
|
|
103
106
|
return message;
|
|
104
107
|
}
|
|
105
|
-
async runHookWithBooleanReturn(hookSymbol,
|
|
108
|
+
async runHookWithBooleanReturn(hookSymbol,
|
|
109
|
+
// biome-ignore lint/suspicious/noExplicitAny: args can be anything
|
|
110
|
+
...args) {
|
|
106
111
|
for (const fn of this.hooks[hookSymbol.S]) {
|
|
107
112
|
const continueLoop = await fn(...args);
|
|
108
113
|
if (!continueLoop) {
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { Signer, SignerOptions } from "@fgiova/aws-signature";
|
|
1
|
+
import type { Signer, SignerOptions } from "@fgiova/aws-signature";
|
|
2
|
+
import { type Message, MiniSQSClient } from "@fgiova/mini-sqs-client";
|
|
2
3
|
import type { Pool } from "undici";
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import { Logger } from "./logger";
|
|
4
|
+
import { type HookName } from "./hooks";
|
|
5
|
+
import type { Logger } from "./logger";
|
|
6
6
|
type HandlerOptions = {
|
|
7
7
|
deleteMessage?: boolean;
|
|
8
8
|
extendVisibilityTimeout?: boolean;
|
|
@@ -4,26 +4,21 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.SQSConsumer = void 0;
|
|
7
|
+
/** biome-ignore-all lint/complexity/noBannedTypes: Function required as generic */
|
|
8
|
+
const promises_1 = require("node:timers/promises");
|
|
7
9
|
const mini_sqs_client_1 = require("@fgiova/mini-sqs-client");
|
|
8
|
-
|
|
9
|
-
const errors_1 = require("./errors");
|
|
10
|
-
const promises_1 = require("timers/promises");
|
|
11
|
-
const p_map_1 = __importDefault(require("p-map"));
|
|
10
|
+
// @ts-expect-error
|
|
12
11
|
const unpromise_1 = require("@watchable/unpromise");
|
|
12
|
+
// @ts-expect-error
|
|
13
|
+
const p_map_1 = __importDefault(require("p-map"));
|
|
14
|
+
const errors_1 = require("./errors");
|
|
15
|
+
const hooks_1 = require("./hooks");
|
|
13
16
|
class SQSConsumer {
|
|
14
|
-
queueARN;
|
|
15
|
-
handlerOptions;
|
|
16
|
-
clientOptions;
|
|
17
|
-
consumerOptions;
|
|
18
|
-
sqsClient;
|
|
19
|
-
hooks;
|
|
20
|
-
messageHandler;
|
|
21
|
-
messagesOnFly = 0;
|
|
22
|
-
running = false;
|
|
23
|
-
polling = false;
|
|
24
|
-
destroyed = false;
|
|
25
|
-
logger;
|
|
26
17
|
constructor(options) {
|
|
18
|
+
this.messagesOnFly = 0;
|
|
19
|
+
this.running = false;
|
|
20
|
+
this.polling = false;
|
|
21
|
+
this.destroyed = false;
|
|
27
22
|
if (!options.queueARN)
|
|
28
23
|
throw new Error("queueARN is required");
|
|
29
24
|
if (!options.handler || typeof options.handler !== "function")
|
|
@@ -32,20 +27,24 @@ class SQSConsumer {
|
|
|
32
27
|
this.hooks = new hooks_1.Hooks(this.logger);
|
|
33
28
|
this.queueARN = options.queueARN;
|
|
34
29
|
const region = this.queueARN.split(":").reverse()[2];
|
|
35
|
-
this.clientOptions = {
|
|
30
|
+
this.clientOptions = {
|
|
31
|
+
destroySigner: options.clientOptions?.destroySigner ?? true,
|
|
32
|
+
};
|
|
36
33
|
this.handlerOptions = {
|
|
37
34
|
deleteMessage: options.handlerOptions?.deleteMessage ?? true,
|
|
38
35
|
extendVisibilityTimeout: options.handlerOptions?.extendVisibilityTimeout ?? true,
|
|
39
|
-
executionTimeout: options.handlerOptions?.executionTimeout ??
|
|
40
|
-
parallelExecution: options.handlerOptions?.parallelExecution ?? true
|
|
36
|
+
executionTimeout: options.handlerOptions?.executionTimeout ?? 30000,
|
|
37
|
+
parallelExecution: options.handlerOptions?.parallelExecution ?? true,
|
|
41
38
|
};
|
|
42
39
|
this.consumerOptions = {
|
|
43
40
|
visibilityTimeout: options.consumerOptions?.visibilityTimeout ?? 30,
|
|
44
41
|
waitTimeSeconds: options.consumerOptions?.waitTimeSeconds ?? 20,
|
|
45
42
|
itemsPerRequest: options.consumerOptions?.itemsPerRequest ?? 10,
|
|
46
|
-
messageAttributeNames: options.consumerOptions?.messageAttributeNames ?? []
|
|
43
|
+
messageAttributeNames: options.consumerOptions?.messageAttributeNames ?? [],
|
|
47
44
|
};
|
|
48
|
-
this.sqsClient =
|
|
45
|
+
this.sqsClient =
|
|
46
|
+
options.clientOptions?.sqsClient ??
|
|
47
|
+
new mini_sqs_client_1.MiniSQSClient(region, options.clientOptions?.endpoint, options.clientOptions?.undiciOptions, options.clientOptions?.signer);
|
|
49
48
|
if (options.hooks) {
|
|
50
49
|
for (const [key, value] of Object.entries(options.hooks)) {
|
|
51
50
|
if (typeof value !== "function")
|
|
@@ -65,14 +64,14 @@ class SQSConsumer {
|
|
|
65
64
|
await this.hooks.runHook("onSuccess", message);
|
|
66
65
|
return {
|
|
67
66
|
message,
|
|
68
|
-
result
|
|
67
|
+
result,
|
|
69
68
|
};
|
|
70
69
|
}
|
|
71
70
|
catch (e) {
|
|
72
71
|
await this.hooks.runHook("onError", "onMessage", message, e);
|
|
73
72
|
return {
|
|
74
73
|
message,
|
|
75
|
-
error: e
|
|
74
|
+
error: e,
|
|
76
75
|
};
|
|
77
76
|
}
|
|
78
77
|
}
|
|
@@ -82,20 +81,18 @@ class SQSConsumer {
|
|
|
82
81
|
if (this.handlerOptions.executionTimeout) {
|
|
83
82
|
const response = await unpromise_1.Unpromise.race([
|
|
84
83
|
handler(message),
|
|
85
|
-
new Promise((
|
|
84
|
+
new Promise((_resolve, reject) => {
|
|
86
85
|
setTimeout(() => {
|
|
87
86
|
reject(new errors_1.TimeoutError("Handler execution timed out"));
|
|
88
87
|
}, this.handlerOptions.executionTimeout);
|
|
89
|
-
})
|
|
88
|
+
}),
|
|
90
89
|
]);
|
|
91
90
|
handlerResult = response;
|
|
92
91
|
}
|
|
93
92
|
else {
|
|
94
93
|
handlerResult = await handler(message);
|
|
95
94
|
}
|
|
96
|
-
await Promise.all([
|
|
97
|
-
this.hooks.runHook("onHandlerSuccess", message),
|
|
98
|
-
]);
|
|
95
|
+
await Promise.all([this.hooks.runHook("onHandlerSuccess", message)]);
|
|
99
96
|
return handlerResult;
|
|
100
97
|
}
|
|
101
98
|
catch (error) {
|
|
@@ -110,16 +107,17 @@ class SQSConsumer {
|
|
|
110
107
|
}
|
|
111
108
|
haExtendVisibilityTimeout(messages, visibilityTimeout) {
|
|
112
109
|
if (this.handlerOptions.extendVisibilityTimeout !== false) {
|
|
113
|
-
const processingMessages = messages.map(
|
|
110
|
+
const processingMessages = messages.map(
|
|
111
|
+
// biome-ignore lint/style/noNonNullAssertion: ReceiptHandle must be present here
|
|
112
|
+
(message) => message.ReceiptHandle);
|
|
114
113
|
return setInterval(async () => {
|
|
115
114
|
try {
|
|
116
115
|
await this.sqsClient.changeMessageVisibilityBatch(this.queueARN, processingMessages, visibilityTimeout);
|
|
117
|
-
}
|
|
118
|
-
/* c8 ignore next 3 */
|
|
116
|
+
} /* c8 ignore next 3 */
|
|
119
117
|
catch (e) {
|
|
120
118
|
await this.hooks.runHook("onSQSError", e);
|
|
121
119
|
}
|
|
122
|
-
},
|
|
120
|
+
}, visibilityTimeout * 1000 - 5);
|
|
123
121
|
}
|
|
124
122
|
return null;
|
|
125
123
|
}
|
|
@@ -128,9 +126,11 @@ class SQSConsumer {
|
|
|
128
126
|
const candidateToRelease = [];
|
|
129
127
|
for (const result of messagesResults) {
|
|
130
128
|
if (result.result && this.handlerOptions.deleteMessage !== false) {
|
|
129
|
+
// biome-ignore lint/style/noNonNullAssertion: ReceiptHandle must be present here
|
|
131
130
|
candidatesToDelete.push(result.message.ReceiptHandle);
|
|
132
131
|
}
|
|
133
132
|
else if (result.error) {
|
|
133
|
+
// biome-ignore lint/style/noNonNullAssertion: ReceiptHandle must be present here
|
|
134
134
|
candidateToRelease.push(result.message.ReceiptHandle);
|
|
135
135
|
}
|
|
136
136
|
if (candidatesToDelete.length)
|
|
@@ -144,7 +144,8 @@ class SQSConsumer {
|
|
|
144
144
|
return;
|
|
145
145
|
try {
|
|
146
146
|
this.polling = true;
|
|
147
|
-
|
|
147
|
+
/* c8 ignore next 1 */
|
|
148
|
+
const visibilityTimeout = this.consumerOptions.visibilityTimeout ?? 30;
|
|
148
149
|
const messagesResult = await this.sqsClient.receiveMessage(this.queueARN, {
|
|
149
150
|
WaitTimeSeconds: this.consumerOptions.waitTimeSeconds,
|
|
150
151
|
MaxNumberOfMessages: this.consumerOptions.itemsPerRequest,
|
|
@@ -158,7 +159,7 @@ class SQSConsumer {
|
|
|
158
159
|
await this.hooks.runHook("onPoll", messages);
|
|
159
160
|
if (messages.length) {
|
|
160
161
|
const haTimeout = this.haExtendVisibilityTimeout(messages, visibilityTimeout);
|
|
161
|
-
this.messagesOnFly +=
|
|
162
|
+
this.messagesOnFly += messages.length - totalMessages;
|
|
162
163
|
if (this.handlerOptions.parallelExecution !== false) {
|
|
163
164
|
const results = await (0, p_map_1.default)(messages, async (message) => {
|
|
164
165
|
return await this.runOnMessageHook(message);
|
|
@@ -212,7 +213,6 @@ class SQSConsumer {
|
|
|
212
213
|
this.destroyed = true;
|
|
213
214
|
}
|
|
214
215
|
}
|
|
215
|
-
// eslint-disable-next-line @typescript-eslint/ban-types
|
|
216
216
|
addHook(hookName, value) {
|
|
217
217
|
return this.hooks.addHook(hookName, value);
|
|
218
218
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Represents a generic logger that could be a simple console, pino etc.
|
|
3
3
|
*/
|
|
4
|
+
/** biome-ignore-all lint/suspicious/noExplicitAny: leave message logger as any */
|
|
4
5
|
export interface Logger {
|
|
5
6
|
trace(message?: any, ...optionalParams: any[]): void;
|
|
6
7
|
debug(message?: any, ...optionalParams: any[]): void;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { Logger } from "./logger.mjs";
|
|
2
|
+
export type HookName = "onPoll" | "onMessage" | "onHandlerSuccess" | "onHandlerTimeout" | "onHandlerError" | "onSuccess" | "onError" | "onSQSError";
|
|
3
|
+
export declare class Hooks {
|
|
4
|
+
private readonly hookSymbols;
|
|
5
|
+
private readonly hooks;
|
|
6
|
+
private readonly logger;
|
|
7
|
+
constructor(logger?: Logger);
|
|
8
|
+
addHook(hook: HookName, fn: Function): void;
|
|
9
|
+
runHook(hook: HookName, ...args: any[]): Promise<any>;
|
|
10
|
+
private runHookWithVoidReturn;
|
|
11
|
+
private runHookWithMessageReturn;
|
|
12
|
+
private runHookWithBooleanReturn;
|
|
13
|
+
}
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
export class Hooks {
|
|
2
|
+
constructor(logger = console) {
|
|
3
|
+
this.hookSymbols = {
|
|
4
|
+
onPoll: {
|
|
5
|
+
S: Symbol("poll"),
|
|
6
|
+
throwable: true,
|
|
7
|
+
type: "message-return",
|
|
8
|
+
},
|
|
9
|
+
onMessage: {
|
|
10
|
+
S: Symbol("message"),
|
|
11
|
+
throwable: true,
|
|
12
|
+
type: "message-return",
|
|
13
|
+
},
|
|
14
|
+
onHandlerSuccess: {
|
|
15
|
+
S: Symbol("handlerSuccess"),
|
|
16
|
+
throwable: false,
|
|
17
|
+
type: "message-return",
|
|
18
|
+
},
|
|
19
|
+
onHandlerTimeout: {
|
|
20
|
+
S: Symbol("handlerTimeout"),
|
|
21
|
+
throwable: false,
|
|
22
|
+
type: "boolean-return",
|
|
23
|
+
},
|
|
24
|
+
onHandlerError: {
|
|
25
|
+
S: Symbol("handlerError"),
|
|
26
|
+
throwable: false,
|
|
27
|
+
type: "boolean-return",
|
|
28
|
+
},
|
|
29
|
+
onSuccess: {
|
|
30
|
+
S: Symbol("success"),
|
|
31
|
+
throwable: false,
|
|
32
|
+
type: "boolean-return",
|
|
33
|
+
},
|
|
34
|
+
onError: {
|
|
35
|
+
S: Symbol("error"),
|
|
36
|
+
throwable: false,
|
|
37
|
+
type: "boolean-return",
|
|
38
|
+
},
|
|
39
|
+
onSQSError: {
|
|
40
|
+
S: Symbol("sqsError"),
|
|
41
|
+
throwable: false,
|
|
42
|
+
type: "void",
|
|
43
|
+
},
|
|
44
|
+
};
|
|
45
|
+
this.logger = logger;
|
|
46
|
+
this.hooks = {
|
|
47
|
+
[this.hookSymbols.onPoll.S]: [],
|
|
48
|
+
[this.hookSymbols.onMessage.S]: [],
|
|
49
|
+
[this.hookSymbols.onHandlerSuccess.S]: [],
|
|
50
|
+
[this.hookSymbols.onHandlerTimeout.S]: [],
|
|
51
|
+
[this.hookSymbols.onHandlerError.S]: [],
|
|
52
|
+
[this.hookSymbols.onSuccess.S]: [],
|
|
53
|
+
[this.hookSymbols.onError.S]: [],
|
|
54
|
+
[this.hookSymbols.onSQSError.S]: [],
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
addHook(hook, fn) {
|
|
58
|
+
const hookSymbol = this.hookSymbols[hook];
|
|
59
|
+
if (!hookSymbol)
|
|
60
|
+
throw new Error(`Invalid hook ${hook}`);
|
|
61
|
+
this.hooks[hookSymbol.S]?.push(fn);
|
|
62
|
+
}
|
|
63
|
+
// biome-ignore lint/suspicious/noExplicitAny: args can be anything
|
|
64
|
+
async runHook(hook, ...args) {
|
|
65
|
+
const hookSymbol = this.hookSymbols[hook];
|
|
66
|
+
try {
|
|
67
|
+
if (!hookSymbol)
|
|
68
|
+
throw new Error(`Invalid hook ${hook}`);
|
|
69
|
+
switch (hookSymbol.type) {
|
|
70
|
+
case "boolean-return":
|
|
71
|
+
return await this.runHookWithBooleanReturn(hookSymbol, ...args);
|
|
72
|
+
case "message-return": {
|
|
73
|
+
const [message, ...someArgs] = args;
|
|
74
|
+
return await this.runHookWithMessageReturn(hookSymbol, message, ...someArgs);
|
|
75
|
+
}
|
|
76
|
+
case "void":
|
|
77
|
+
return await this.runHookWithVoidReturn(hookSymbol, ...args);
|
|
78
|
+
}
|
|
79
|
+
/* c8 ignore next 1 */
|
|
80
|
+
// biome-ignore lint/suspicious/noExplicitAny: error type is not important
|
|
81
|
+
}
|
|
82
|
+
catch (error) {
|
|
83
|
+
error.message = `Error running hook ${hook}: ${error.message}`;
|
|
84
|
+
if (hookSymbol.throwable !== false)
|
|
85
|
+
throw error;
|
|
86
|
+
this.logger.error(error);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
async runHookWithVoidReturn(hookSymbol,
|
|
90
|
+
// biome-ignore lint/suspicious/noExplicitAny: args can be anything
|
|
91
|
+
...args) {
|
|
92
|
+
for (const fn of this.hooks[hookSymbol.S]) {
|
|
93
|
+
await fn(...args);
|
|
94
|
+
}
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
async runHookWithMessageReturn(hookSymbol, message,
|
|
98
|
+
// biome-ignore lint/suspicious/noExplicitAny: args can be anything
|
|
99
|
+
...args) {
|
|
100
|
+
for (const fn of this.hooks[hookSymbol.S]) {
|
|
101
|
+
message = await fn(message, ...args);
|
|
102
|
+
}
|
|
103
|
+
return message;
|
|
104
|
+
}
|
|
105
|
+
async runHookWithBooleanReturn(hookSymbol,
|
|
106
|
+
// biome-ignore lint/suspicious/noExplicitAny: args can be anything
|
|
107
|
+
...args) {
|
|
108
|
+
for (const fn of this.hooks[hookSymbol.S]) {
|
|
109
|
+
const continueLoop = await fn(...args);
|
|
110
|
+
if (!continueLoop) {
|
|
111
|
+
return false;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
return true;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import type { Signer, SignerOptions } from "@fgiova/aws-signature";
|
|
2
|
+
import { type Message, MiniSQSClient } from "@fgiova/mini-sqs-client";
|
|
3
|
+
import type { Pool } from "undici";
|
|
4
|
+
import { type HookName } from "./hooks.mjs";
|
|
5
|
+
import type { Logger } from "./logger.mjs";
|
|
6
|
+
type HandlerOptions = {
|
|
7
|
+
deleteMessage?: boolean;
|
|
8
|
+
extendVisibilityTimeout?: boolean;
|
|
9
|
+
executionTimeout?: number;
|
|
10
|
+
parallelExecution?: boolean;
|
|
11
|
+
};
|
|
12
|
+
type ClientOptions = {
|
|
13
|
+
sqsClient?: MiniSQSClient;
|
|
14
|
+
endpoint?: string;
|
|
15
|
+
undiciOptions?: Pool.Options;
|
|
16
|
+
signer?: Signer | SignerOptions;
|
|
17
|
+
destroySigner?: boolean;
|
|
18
|
+
};
|
|
19
|
+
type ConsumerOptions = {
|
|
20
|
+
visibilityTimeout?: number;
|
|
21
|
+
waitTimeSeconds?: number;
|
|
22
|
+
itemsPerRequest?: number;
|
|
23
|
+
messageAttributeNames?: string[];
|
|
24
|
+
};
|
|
25
|
+
type HooksOptions = {
|
|
26
|
+
onPoll?: (messages: Message[]) => Promise<Message[]>;
|
|
27
|
+
onMessage?: (message: Message) => Promise<Message>;
|
|
28
|
+
onHandlerSuccess?: (message: Message) => Promise<Message>;
|
|
29
|
+
onHandlerTimeout?: (message: Message) => Promise<boolean>;
|
|
30
|
+
onHandlerError?: (message: Message, error: Error) => Promise<boolean>;
|
|
31
|
+
onSuccess?: (message: Message) => Promise<boolean>;
|
|
32
|
+
onError?: (hook: HookName, message: Message, error: Error) => Promise<boolean>;
|
|
33
|
+
onSQSError?: (error: Error, message?: Message) => Promise<void>;
|
|
34
|
+
};
|
|
35
|
+
export type SQSConsumerOptions = {
|
|
36
|
+
queueARN: string;
|
|
37
|
+
handler: (message: Message) => Promise<any>;
|
|
38
|
+
logger?: Logger;
|
|
39
|
+
autostart?: boolean;
|
|
40
|
+
handlerOptions?: HandlerOptions;
|
|
41
|
+
clientOptions?: ClientOptions;
|
|
42
|
+
consumerOptions?: ConsumerOptions;
|
|
43
|
+
hooks?: HooksOptions;
|
|
44
|
+
};
|
|
45
|
+
export declare class SQSConsumer {
|
|
46
|
+
private readonly queueARN;
|
|
47
|
+
private readonly handlerOptions;
|
|
48
|
+
private readonly clientOptions;
|
|
49
|
+
private readonly consumerOptions;
|
|
50
|
+
private readonly sqsClient;
|
|
51
|
+
private readonly hooks;
|
|
52
|
+
private readonly messageHandler;
|
|
53
|
+
private messagesOnFly;
|
|
54
|
+
private running;
|
|
55
|
+
private polling;
|
|
56
|
+
private destroyed;
|
|
57
|
+
private readonly logger;
|
|
58
|
+
constructor(options: SQSConsumerOptions);
|
|
59
|
+
private runOnMessageHook;
|
|
60
|
+
private runHandler;
|
|
61
|
+
private haExtendVisibilityTimeout;
|
|
62
|
+
private deleteMessages;
|
|
63
|
+
private pollMessages;
|
|
64
|
+
start(): Promise<void>;
|
|
65
|
+
stop(destroy?: boolean): Promise<void>;
|
|
66
|
+
addHook(hookName: "onPoll", value: (messages: Message[]) => Promise<Message[]>): void;
|
|
67
|
+
addHook(hookName: "onMessage", value: (message: Message) => Promise<Message>): void;
|
|
68
|
+
addHook(hookName: "onHandlerSuccess", value: (message: Message) => Promise<Message>): void;
|
|
69
|
+
addHook(hookName: "onHandlerTimeout", value: (message: Message) => Promise<boolean>): void;
|
|
70
|
+
addHook(hookName: "onHandlerError", value: (message: Message, error: Error) => Promise<boolean>): void;
|
|
71
|
+
addHook(hookName: "onSuccess", value: (message: Message) => Promise<boolean>): void;
|
|
72
|
+
addHook(hookName: "onError", value: (hook: HookName, message: Message, error: Error) => Promise<boolean>): void;
|
|
73
|
+
addHook(hookName: "onSQSError", value: (error: Error, message?: Message) => Promise<void>): void;
|
|
74
|
+
addHook(hookName: HookName, value: Function): void;
|
|
75
|
+
}
|
|
76
|
+
export {};
|
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
/** biome-ignore-all lint/complexity/noBannedTypes: Function required as generic */
|
|
2
|
+
import { setTimeout as setTimeoutAsync } from "node:timers/promises";
|
|
3
|
+
import { MiniSQSClient } from "@fgiova/mini-sqs-client";
|
|
4
|
+
// @ts-expect-error
|
|
5
|
+
import { Unpromise } from "@watchable/unpromise";
|
|
6
|
+
// @ts-expect-error
|
|
7
|
+
import pMap from "p-map";
|
|
8
|
+
import { TimeoutError } from "./errors.mjs";
|
|
9
|
+
import { Hooks } from "./hooks.mjs";
|
|
10
|
+
export class SQSConsumer {
|
|
11
|
+
constructor(options) {
|
|
12
|
+
this.messagesOnFly = 0;
|
|
13
|
+
this.running = false;
|
|
14
|
+
this.polling = false;
|
|
15
|
+
this.destroyed = false;
|
|
16
|
+
if (!options.queueARN)
|
|
17
|
+
throw new Error("queueARN is required");
|
|
18
|
+
if (!options.handler || typeof options.handler !== "function")
|
|
19
|
+
throw new Error("handler is required and must be a function");
|
|
20
|
+
this.logger = options.logger ?? console;
|
|
21
|
+
this.hooks = new Hooks(this.logger);
|
|
22
|
+
this.queueARN = options.queueARN;
|
|
23
|
+
const region = this.queueARN.split(":").reverse()[2];
|
|
24
|
+
this.clientOptions = {
|
|
25
|
+
destroySigner: options.clientOptions?.destroySigner ?? true,
|
|
26
|
+
};
|
|
27
|
+
this.handlerOptions = {
|
|
28
|
+
deleteMessage: options.handlerOptions?.deleteMessage ?? true,
|
|
29
|
+
extendVisibilityTimeout: options.handlerOptions?.extendVisibilityTimeout ?? true,
|
|
30
|
+
executionTimeout: options.handlerOptions?.executionTimeout ?? 30000,
|
|
31
|
+
parallelExecution: options.handlerOptions?.parallelExecution ?? true,
|
|
32
|
+
};
|
|
33
|
+
this.consumerOptions = {
|
|
34
|
+
visibilityTimeout: options.consumerOptions?.visibilityTimeout ?? 30,
|
|
35
|
+
waitTimeSeconds: options.consumerOptions?.waitTimeSeconds ?? 20,
|
|
36
|
+
itemsPerRequest: options.consumerOptions?.itemsPerRequest ?? 10,
|
|
37
|
+
messageAttributeNames: options.consumerOptions?.messageAttributeNames ?? [],
|
|
38
|
+
};
|
|
39
|
+
this.sqsClient =
|
|
40
|
+
options.clientOptions?.sqsClient ??
|
|
41
|
+
new MiniSQSClient(region, options.clientOptions?.endpoint, options.clientOptions?.undiciOptions, options.clientOptions?.signer);
|
|
42
|
+
if (options.hooks) {
|
|
43
|
+
for (const [key, value] of Object.entries(options.hooks)) {
|
|
44
|
+
if (typeof value !== "function")
|
|
45
|
+
throw new Error(`${key} must be a function`);
|
|
46
|
+
this.addHook(key, value);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
this.messageHandler = options.handler;
|
|
50
|
+
if (options.autostart !== false) {
|
|
51
|
+
void this.start();
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
async runOnMessageHook(message) {
|
|
55
|
+
try {
|
|
56
|
+
await this.hooks.runHook("onMessage", message);
|
|
57
|
+
const result = await this.runHandler(this.messageHandler, message);
|
|
58
|
+
await this.hooks.runHook("onSuccess", message);
|
|
59
|
+
return {
|
|
60
|
+
message,
|
|
61
|
+
result,
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
catch (e) {
|
|
65
|
+
await this.hooks.runHook("onError", "onMessage", message, e);
|
|
66
|
+
return {
|
|
67
|
+
message,
|
|
68
|
+
error: e,
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
async runHandler(handler, message) {
|
|
73
|
+
try {
|
|
74
|
+
let handlerResult;
|
|
75
|
+
if (this.handlerOptions.executionTimeout) {
|
|
76
|
+
const response = await Unpromise.race([
|
|
77
|
+
handler(message),
|
|
78
|
+
new Promise((_resolve, reject) => {
|
|
79
|
+
setTimeout(() => {
|
|
80
|
+
reject(new TimeoutError("Handler execution timed out"));
|
|
81
|
+
}, this.handlerOptions.executionTimeout);
|
|
82
|
+
}),
|
|
83
|
+
]);
|
|
84
|
+
handlerResult = response;
|
|
85
|
+
}
|
|
86
|
+
else {
|
|
87
|
+
handlerResult = await handler(message);
|
|
88
|
+
}
|
|
89
|
+
await Promise.all([this.hooks.runHook("onHandlerSuccess", message)]);
|
|
90
|
+
return handlerResult;
|
|
91
|
+
}
|
|
92
|
+
catch (error) {
|
|
93
|
+
if (error instanceof TimeoutError) {
|
|
94
|
+
await this.hooks.runHook("onHandlerTimeout", message);
|
|
95
|
+
}
|
|
96
|
+
else {
|
|
97
|
+
await this.hooks.runHook("onHandlerError", message, error);
|
|
98
|
+
}
|
|
99
|
+
throw error;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
haExtendVisibilityTimeout(messages, visibilityTimeout) {
|
|
103
|
+
if (this.handlerOptions.extendVisibilityTimeout !== false) {
|
|
104
|
+
const processingMessages = messages.map(
|
|
105
|
+
// biome-ignore lint/style/noNonNullAssertion: ReceiptHandle must be present here
|
|
106
|
+
(message) => message.ReceiptHandle);
|
|
107
|
+
return setInterval(async () => {
|
|
108
|
+
try {
|
|
109
|
+
await this.sqsClient.changeMessageVisibilityBatch(this.queueARN, processingMessages, visibilityTimeout);
|
|
110
|
+
} /* c8 ignore next 3 */
|
|
111
|
+
catch (e) {
|
|
112
|
+
await this.hooks.runHook("onSQSError", e);
|
|
113
|
+
}
|
|
114
|
+
}, visibilityTimeout * 1000 - 5);
|
|
115
|
+
}
|
|
116
|
+
return null;
|
|
117
|
+
}
|
|
118
|
+
async deleteMessages(messagesResults) {
|
|
119
|
+
const candidatesToDelete = [];
|
|
120
|
+
const candidateToRelease = [];
|
|
121
|
+
for (const result of messagesResults) {
|
|
122
|
+
if (result.result && this.handlerOptions.deleteMessage !== false) {
|
|
123
|
+
// biome-ignore lint/style/noNonNullAssertion: ReceiptHandle must be present here
|
|
124
|
+
candidatesToDelete.push(result.message.ReceiptHandle);
|
|
125
|
+
}
|
|
126
|
+
else if (result.error) {
|
|
127
|
+
// biome-ignore lint/style/noNonNullAssertion: ReceiptHandle must be present here
|
|
128
|
+
candidateToRelease.push(result.message.ReceiptHandle);
|
|
129
|
+
}
|
|
130
|
+
if (candidatesToDelete.length)
|
|
131
|
+
await this.sqsClient.deleteMessageBatch(this.queueARN, candidatesToDelete);
|
|
132
|
+
if (candidateToRelease.length)
|
|
133
|
+
await this.sqsClient.changeMessageVisibilityBatch(this.queueARN, candidateToRelease, 0);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
async pollMessages() {
|
|
137
|
+
if (!this.running)
|
|
138
|
+
return;
|
|
139
|
+
try {
|
|
140
|
+
this.polling = true;
|
|
141
|
+
/* c8 ignore next 1 */
|
|
142
|
+
const visibilityTimeout = this.consumerOptions.visibilityTimeout ?? 30;
|
|
143
|
+
const messagesResult = await this.sqsClient.receiveMessage(this.queueARN, {
|
|
144
|
+
WaitTimeSeconds: this.consumerOptions.waitTimeSeconds,
|
|
145
|
+
MaxNumberOfMessages: this.consumerOptions.itemsPerRequest,
|
|
146
|
+
VisibilityTimeout: visibilityTimeout,
|
|
147
|
+
MessageAttributeNames: this.consumerOptions.messageAttributeNames,
|
|
148
|
+
});
|
|
149
|
+
/* c8 ignore next 1 */
|
|
150
|
+
const messages = messagesResult.Messages ?? [];
|
|
151
|
+
const totalMessages = messages.length;
|
|
152
|
+
this.messagesOnFly += totalMessages;
|
|
153
|
+
await this.hooks.runHook("onPoll", messages);
|
|
154
|
+
if (messages.length) {
|
|
155
|
+
const haTimeout = this.haExtendVisibilityTimeout(messages, visibilityTimeout);
|
|
156
|
+
this.messagesOnFly += messages.length - totalMessages;
|
|
157
|
+
if (this.handlerOptions.parallelExecution !== false) {
|
|
158
|
+
const results = await pMap(messages, async (message) => {
|
|
159
|
+
return await this.runOnMessageHook(message);
|
|
160
|
+
}, {
|
|
161
|
+
concurrency: 10,
|
|
162
|
+
stopOnError: false,
|
|
163
|
+
});
|
|
164
|
+
await this.deleteMessages(results);
|
|
165
|
+
this.messagesOnFly -= messages.length;
|
|
166
|
+
}
|
|
167
|
+
else {
|
|
168
|
+
const handlingMessages = [...messages];
|
|
169
|
+
for (let i = 0; i < handlingMessages.length; i++) {
|
|
170
|
+
const result = await this.runOnMessageHook(handlingMessages[i]);
|
|
171
|
+
await this.deleteMessages([result]);
|
|
172
|
+
messages.splice(i, 1);
|
|
173
|
+
this.messagesOnFly -= 1;
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
if (haTimeout)
|
|
177
|
+
clearInterval(haTimeout);
|
|
178
|
+
}
|
|
179
|
+
else {
|
|
180
|
+
this.messagesOnFly -= totalMessages;
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
catch (e) {
|
|
184
|
+
await this.hooks.runHook("onSQSError", e);
|
|
185
|
+
}
|
|
186
|
+
this.polling = false;
|
|
187
|
+
await this.pollMessages();
|
|
188
|
+
}
|
|
189
|
+
// eslint-disable-next-line @typescript-eslint/require-await
|
|
190
|
+
async start() {
|
|
191
|
+
if (this.running)
|
|
192
|
+
throw new Error("Consumer is already running");
|
|
193
|
+
if (this.destroyed)
|
|
194
|
+
throw new Error("Consumer is destroyed");
|
|
195
|
+
this.running = true;
|
|
196
|
+
void this.pollMessages();
|
|
197
|
+
}
|
|
198
|
+
async stop(destroy = false) {
|
|
199
|
+
if (!this.running)
|
|
200
|
+
throw new Error("Consumer is not running");
|
|
201
|
+
this.running = false;
|
|
202
|
+
while (this.messagesOnFly > 0 || this.polling) {
|
|
203
|
+
await setTimeoutAsync(500);
|
|
204
|
+
}
|
|
205
|
+
if (destroy) {
|
|
206
|
+
await this.sqsClient.destroy(this.clientOptions.destroySigner);
|
|
207
|
+
this.destroyed = true;
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
addHook(hookName, value) {
|
|
211
|
+
return this.hooks.addHook(hookName, value);
|
|
212
|
+
}
|
|
213
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Represents a generic logger that could be a simple console, pino etc.
|
|
3
|
+
*/
|
|
4
|
+
/** biome-ignore-all lint/suspicious/noExplicitAny: leave message logger as any */
|
|
5
|
+
export interface Logger {
|
|
6
|
+
trace(message?: any, ...optionalParams: any[]): void;
|
|
7
|
+
debug(message?: any, ...optionalParams: any[]): void;
|
|
8
|
+
info(message?: any, ...optionalParams: any[]): void;
|
|
9
|
+
warn(message?: any, ...optionalParams: any[]): void;
|
|
10
|
+
error(message?: any, ...optionalParams: any[]): void;
|
|
11
|
+
[x: string]: any;
|
|
12
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/package.json
CHANGED
|
@@ -1,14 +1,29 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fgiova/sqs-consumer",
|
|
3
|
-
"version": "2.0
|
|
4
|
-
"main": "dist/index.js",
|
|
3
|
+
"version": "2.1.0",
|
|
4
|
+
"main": "./dist/index.js",
|
|
5
|
+
"types": "./dist/index.d.ts",
|
|
6
|
+
"exports": {
|
|
7
|
+
".": {
|
|
8
|
+
"import": {
|
|
9
|
+
"types": "./dist/esm/index.d.mts",
|
|
10
|
+
"default": "./dist/esm/index.mjs"
|
|
11
|
+
},
|
|
12
|
+
"require": {
|
|
13
|
+
"types": "./dist/cjs/index.d.ts",
|
|
14
|
+
"default": "./dist/cjs/index.js"
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
},
|
|
5
18
|
"files": [
|
|
6
19
|
"dist"
|
|
7
20
|
],
|
|
8
21
|
"scripts": {
|
|
9
|
-
"
|
|
10
|
-
"
|
|
11
|
-
"
|
|
22
|
+
"clean": "rimraf dist",
|
|
23
|
+
"preBuild": "npm run clean",
|
|
24
|
+
"build": "esmBuilder",
|
|
25
|
+
"lint": "biome ci ./src ./test",
|
|
26
|
+
"fix": "biome check --write ./src ./test",
|
|
12
27
|
"test": "tap",
|
|
13
28
|
"test:debug": "tap --only --timeout=0",
|
|
14
29
|
"test:coverage": "tap --coverage-report=lcovonly --coverage-report=text",
|
|
@@ -41,26 +56,25 @@
|
|
|
41
56
|
]
|
|
42
57
|
},
|
|
43
58
|
"dependencies": {
|
|
44
|
-
"@fgiova/mini-sqs-client": "^
|
|
59
|
+
"@fgiova/mini-sqs-client": "^3.2.0",
|
|
45
60
|
"@watchable/unpromise": "^1.0.2",
|
|
46
|
-
"p-map": "^
|
|
61
|
+
"p-map": "^7.0.3"
|
|
47
62
|
},
|
|
48
63
|
"peerDependencies": {
|
|
49
|
-
"@fgiova/aws-signature": "^
|
|
64
|
+
"@fgiova/aws-signature": "^3.1.0"
|
|
50
65
|
},
|
|
51
66
|
"devDependencies": {
|
|
52
67
|
"@aws-sdk/client-sqs": "^3.758.0",
|
|
68
|
+
"@biomejs/biome": "2.2.2",
|
|
69
|
+
"@fgiova/cjs-esm-ts-builder": "^1.2.0",
|
|
53
70
|
"@semantic-release/changelog": "^6.0.3",
|
|
54
71
|
"@semantic-release/git": "^10.0.1",
|
|
72
|
+
"@tsconfig/node20": "^20.1.4",
|
|
55
73
|
"@types/node": "^20.17.23",
|
|
56
|
-
"@typescript-eslint/eslint-plugin": "^6.21.0",
|
|
57
|
-
"@typescript-eslint/parser": "^6.21.0",
|
|
58
74
|
"conventional-changelog-conventionalcommits": "^8.0.0",
|
|
59
|
-
"eslint": "^8.57.1",
|
|
60
|
-
"eslint-plugin-import": "^2.31.0",
|
|
61
75
|
"semantic-release": "^24.2.3",
|
|
62
76
|
"tap": "^21.1.0",
|
|
63
|
-
"testcontainers": "^
|
|
77
|
+
"testcontainers": "^11.5.1",
|
|
64
78
|
"ts-node": "^10.9.2",
|
|
65
79
|
"typescript": "^5.8.2"
|
|
66
80
|
}
|
|
File without changes
|
|
File without changes
|
|
File without changes
|