@ido_kawaz/amqp-client 1.0.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 +109 -0
- package/dist/__tests__/amqpClient.test.d.ts +2 -0
- package/dist/__tests__/amqpClient.test.d.ts.map +1 -0
- package/dist/__tests__/amqpClient.test.js +83 -0
- package/dist/__tests__/amqpClient.test.js.map +1 -0
- package/dist/__tests__/consumer.test.d.ts +2 -0
- package/dist/__tests__/consumer.test.d.ts.map +1 -0
- package/dist/__tests__/consumer.test.js +81 -0
- package/dist/__tests__/consumer.test.js.map +1 -0
- package/dist/amqpClient.d.ts +14 -0
- package/dist/amqpClient.d.ts.map +1 -0
- package/dist/amqpClient.js +52 -0
- package/dist/amqpClient.js.map +1 -0
- package/dist/config.d.ts +6 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +3 -0
- package/dist/config.js.map +1 -0
- package/dist/consumer.d.ts +16 -0
- package/dist/consumer.d.ts.map +1 -0
- package/dist/consumer.js +64 -0
- package/dist/consumer.js.map +1 -0
- package/dist/errors.d.ts +24 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +47 -0
- package/dist/errors.js.map +1 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +15 -0
- package/dist/index.js.map +1 -0
- package/dist/types.d.ts +9 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +3 -0
- package/dist/types.js.map +1 -0
- package/package.json +40 -0
package/README.md
ADDED
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
# @ido_kawaz/amqp-client
|
|
2
|
+
|
|
3
|
+
[](https://github.com/kawaz-video-streaming/amqp-client/actions/workflows/ci.yml)
|
|
4
|
+
|
|
5
|
+
TypeScript Amqp client for RabbitMQ publishers and consumers.
|
|
6
|
+
|
|
7
|
+
## Installation
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npm install @ido_kawaz/amqp-client
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Quick start
|
|
14
|
+
|
|
15
|
+
```ts
|
|
16
|
+
import { AmqpClient, Consumer, AmqpConfig } from '@ido_kawaz/amqp-client';
|
|
17
|
+
|
|
18
|
+
const config: AmqpConfig = {
|
|
19
|
+
amqpConnectionString: 'localhost',
|
|
20
|
+
amqpUser: 'guest',
|
|
21
|
+
amqpPassword: 'guest',
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
async function bootstrap() {
|
|
25
|
+
const consumer = new Consumer(
|
|
26
|
+
'orders.queue',
|
|
27
|
+
'orders.exchange',
|
|
28
|
+
'orders.created',
|
|
29
|
+
async (message) => {
|
|
30
|
+
if (!message) {
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const payload = JSON.parse(message.content.toString());
|
|
35
|
+
console.log('received:', payload);
|
|
36
|
+
},
|
|
37
|
+
);
|
|
38
|
+
|
|
39
|
+
const client = new AmqpClient(config, [consumer]);
|
|
40
|
+
await client.start();
|
|
41
|
+
|
|
42
|
+
client.publish('orders.exchange', 'orders.created', {
|
|
43
|
+
orderId: '123',
|
|
44
|
+
total: 45.5,
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
process.on('SIGTERM', async () => {
|
|
48
|
+
await client.stop();
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
bootstrap().catch(console.error);
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
## Configuration
|
|
56
|
+
|
|
57
|
+
`AmqpConfig`
|
|
58
|
+
|
|
59
|
+
- `amqpConnectionString`: RabbitMQ hostname
|
|
60
|
+
- `amqpUser`: RabbitMQ username
|
|
61
|
+
- `amqpPassword`: RabbitMQ password
|
|
62
|
+
|
|
63
|
+
## API
|
|
64
|
+
|
|
65
|
+
### `AmqpClient`
|
|
66
|
+
|
|
67
|
+
- `new AmqpClient(config: AmqpConfig, consumers: Consumer[])`
|
|
68
|
+
- `start(): Promise<void>`
|
|
69
|
+
- Connects to RabbitMQ and starts all consumer registrations.
|
|
70
|
+
- `publish<T>(exchange: string, topic: string, message: T): void`
|
|
71
|
+
- JSON serializes payload and publishes it.
|
|
72
|
+
- Throws `AmqpUninitializedError` if `start()` has not been called.
|
|
73
|
+
- Throws `AmqpPublisherError` if publish returns false.
|
|
74
|
+
- `stop(): Promise<void>`
|
|
75
|
+
- Closes channel and connection.
|
|
76
|
+
|
|
77
|
+
### `Consumer`
|
|
78
|
+
|
|
79
|
+
- `new Consumer(queue, exchange, topic, handler)`
|
|
80
|
+
- Asserts queue and exchange and binds queue to topic.
|
|
81
|
+
- Calls handler for each message.
|
|
82
|
+
- `ack`s on success.
|
|
83
|
+
- `nack`s with requeue logic for `AmqpRetriableError`.
|
|
84
|
+
- `nack`s without requeue for all other errors.
|
|
85
|
+
|
|
86
|
+
## Errors
|
|
87
|
+
|
|
88
|
+
- `AmqpError`
|
|
89
|
+
- `AmqpConnectionError`
|
|
90
|
+
- `AmqpUninitializedError`
|
|
91
|
+
- `AmqpPublisherError`
|
|
92
|
+
- `AmqpConsumerError`
|
|
93
|
+
- `AmqpRetriableError`
|
|
94
|
+
- `AmqpFatalError`
|
|
95
|
+
|
|
96
|
+
## Development
|
|
97
|
+
|
|
98
|
+
- `npm run build` — clean and compile TypeScript
|
|
99
|
+
- `npm run build:watch` — compile TypeScript in watch mode
|
|
100
|
+
- `npm run clean` — remove build output
|
|
101
|
+
- `npm test` — run unit tests
|
|
102
|
+
|
|
103
|
+
## Publish
|
|
104
|
+
|
|
105
|
+
- `npm run publish`
|
|
106
|
+
- Cleans workspace deeply
|
|
107
|
+
- Reinstalls dependencies
|
|
108
|
+
- Builds library
|
|
109
|
+
- Publishes with public access
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"amqpClient.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/amqpClient.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
const amqplib_1 = __importDefault(require("amqplib"));
|
|
7
|
+
const amqpClient_1 = require("../amqpClient");
|
|
8
|
+
const errors_1 = require("../errors");
|
|
9
|
+
jest.mock('amqplib', () => ({
|
|
10
|
+
__esModule: true,
|
|
11
|
+
default: {
|
|
12
|
+
connect: jest.fn(),
|
|
13
|
+
},
|
|
14
|
+
}));
|
|
15
|
+
describe('AmqpClient', () => {
|
|
16
|
+
const baseConfig = {
|
|
17
|
+
amqpConnectionString: 'localhost',
|
|
18
|
+
amqpUser: 'guest',
|
|
19
|
+
amqpPassword: 'guest',
|
|
20
|
+
};
|
|
21
|
+
it('starts client and starts registered consumers', async () => {
|
|
22
|
+
const consumerStart = jest.fn().mockResolvedValue(undefined);
|
|
23
|
+
const consumer = { start: consumerStart };
|
|
24
|
+
const channel = {
|
|
25
|
+
publish: jest.fn().mockReturnValue(true),
|
|
26
|
+
close: jest.fn().mockResolvedValue(undefined),
|
|
27
|
+
};
|
|
28
|
+
const connection = {
|
|
29
|
+
createChannel: jest.fn().mockResolvedValue(channel),
|
|
30
|
+
close: jest.fn().mockResolvedValue(undefined),
|
|
31
|
+
};
|
|
32
|
+
amqplib_1.default.connect.mockResolvedValue(connection);
|
|
33
|
+
const client = new amqpClient_1.AmqpClient(baseConfig, [consumer]);
|
|
34
|
+
await client.start();
|
|
35
|
+
expect(amqplib_1.default.connect).toHaveBeenCalledWith({
|
|
36
|
+
username: 'guest',
|
|
37
|
+
password: 'guest',
|
|
38
|
+
hostname: 'localhost',
|
|
39
|
+
});
|
|
40
|
+
expect(connection.createChannel).toHaveBeenCalledTimes(1);
|
|
41
|
+
expect(consumerStart).toHaveBeenCalledWith(channel);
|
|
42
|
+
});
|
|
43
|
+
it('throws AmqpConnectionError when connect fails', async () => {
|
|
44
|
+
amqplib_1.default.connect.mockRejectedValue(new Error('boom'));
|
|
45
|
+
const client = new amqpClient_1.AmqpClient(baseConfig, []);
|
|
46
|
+
await expect(client.start()).rejects.toBeInstanceOf(errors_1.AmqpConnectionError);
|
|
47
|
+
});
|
|
48
|
+
it('throws AmqpUninitializedError when publishing before start', () => {
|
|
49
|
+
const client = new amqpClient_1.AmqpClient(baseConfig, []);
|
|
50
|
+
expect(() => client.publish('ex', 'topic', { x: 1 })).toThrow(errors_1.AmqpUninitializedError);
|
|
51
|
+
});
|
|
52
|
+
it('throws AmqpPublisherError when publish returns false', async () => {
|
|
53
|
+
const channel = {
|
|
54
|
+
publish: jest.fn().mockReturnValue(false),
|
|
55
|
+
close: jest.fn().mockResolvedValue(undefined),
|
|
56
|
+
};
|
|
57
|
+
const connection = {
|
|
58
|
+
createChannel: jest.fn().mockResolvedValue(channel),
|
|
59
|
+
close: jest.fn().mockResolvedValue(undefined),
|
|
60
|
+
};
|
|
61
|
+
amqplib_1.default.connect.mockResolvedValue(connection);
|
|
62
|
+
const client = new amqpClient_1.AmqpClient(baseConfig, []);
|
|
63
|
+
await client.start();
|
|
64
|
+
expect(() => client.publish('ex', 'rk', { ok: true })).toThrow(errors_1.AmqpPublisherError);
|
|
65
|
+
});
|
|
66
|
+
it('stops by closing channel and connection', async () => {
|
|
67
|
+
const channel = {
|
|
68
|
+
publish: jest.fn().mockReturnValue(true),
|
|
69
|
+
close: jest.fn().mockResolvedValue(undefined),
|
|
70
|
+
};
|
|
71
|
+
const connection = {
|
|
72
|
+
createChannel: jest.fn().mockResolvedValue(channel),
|
|
73
|
+
close: jest.fn().mockResolvedValue(undefined),
|
|
74
|
+
};
|
|
75
|
+
amqplib_1.default.connect.mockResolvedValue(connection);
|
|
76
|
+
const client = new amqpClient_1.AmqpClient(baseConfig, []);
|
|
77
|
+
await client.start();
|
|
78
|
+
await client.stop();
|
|
79
|
+
expect(channel.close).toHaveBeenCalledTimes(1);
|
|
80
|
+
expect(connection.close).toHaveBeenCalledTimes(1);
|
|
81
|
+
});
|
|
82
|
+
});
|
|
83
|
+
//# sourceMappingURL=amqpClient.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"amqpClient.test.js","sourceRoot":"","sources":["../../src/__tests__/amqpClient.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"consumer.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/consumer.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const consumer_1 = require("../consumer");
|
|
4
|
+
const errors_1 = require("../errors");
|
|
5
|
+
describe('Consumer', () => {
|
|
6
|
+
function createMessage(deliveryCount) {
|
|
7
|
+
return {
|
|
8
|
+
properties: {
|
|
9
|
+
headers: deliveryCount === undefined ? {} : { 'x-delivery-count': deliveryCount },
|
|
10
|
+
},
|
|
11
|
+
content: Buffer.from(JSON.stringify({ id: '1' })),
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
function createChannel() {
|
|
15
|
+
return {
|
|
16
|
+
assertQueue: jest.fn().mockResolvedValue(undefined),
|
|
17
|
+
assertExchange: jest.fn().mockResolvedValue(undefined),
|
|
18
|
+
bindQueue: jest.fn().mockResolvedValue(undefined),
|
|
19
|
+
consume: jest.fn(),
|
|
20
|
+
ack: jest.fn(),
|
|
21
|
+
nack: jest.fn(),
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
it('binds queue and acknowledges message on success', async () => {
|
|
25
|
+
const handler = jest.fn().mockResolvedValue(undefined);
|
|
26
|
+
const consumer = new consumer_1.Consumer('orders.queue', 'orders.exchange', 'orders.created', handler);
|
|
27
|
+
const channel = createChannel();
|
|
28
|
+
const message = createMessage();
|
|
29
|
+
channel.consume.mockImplementation(async (_queue, onMessage) => {
|
|
30
|
+
await onMessage(message);
|
|
31
|
+
return { consumerTag: 'tag' };
|
|
32
|
+
});
|
|
33
|
+
await consumer.start(channel);
|
|
34
|
+
expect(channel.assertQueue).toHaveBeenCalledWith('orders.queue', {
|
|
35
|
+
durable: true,
|
|
36
|
+
arguments: { 'x-queue-type': 'quorum' },
|
|
37
|
+
});
|
|
38
|
+
expect(channel.assertExchange).toHaveBeenCalledWith('orders.exchange', 'topic', { durable: true });
|
|
39
|
+
expect(channel.bindQueue).toHaveBeenCalledWith('orders.queue', 'orders.exchange', 'orders.created');
|
|
40
|
+
expect(handler).toHaveBeenCalledWith(message);
|
|
41
|
+
expect(channel.ack).toHaveBeenCalledWith(message);
|
|
42
|
+
expect(channel.nack).not.toHaveBeenCalled();
|
|
43
|
+
});
|
|
44
|
+
it('requeues retriable error when delivery count is below retry limit', async () => {
|
|
45
|
+
const handler = jest.fn().mockRejectedValue(new errors_1.AmqpRetriableError(new Error('retry'), 3));
|
|
46
|
+
const consumer = new consumer_1.Consumer('q', 'ex', 'topic', handler);
|
|
47
|
+
const channel = createChannel();
|
|
48
|
+
const message = createMessage(1);
|
|
49
|
+
channel.consume.mockImplementation(async (_queue, onMessage) => {
|
|
50
|
+
await onMessage(message);
|
|
51
|
+
return { consumerTag: 'tag' };
|
|
52
|
+
});
|
|
53
|
+
await consumer.start(channel);
|
|
54
|
+
expect(channel.nack).toHaveBeenCalledWith(message, false, true);
|
|
55
|
+
});
|
|
56
|
+
it('does not requeue retriable error after retry limit', async () => {
|
|
57
|
+
const handler = jest.fn().mockRejectedValue(new errors_1.AmqpRetriableError(new Error('retry'), 2));
|
|
58
|
+
const consumer = new consumer_1.Consumer('q', 'ex', 'topic', handler);
|
|
59
|
+
const channel = createChannel();
|
|
60
|
+
const message = createMessage('2');
|
|
61
|
+
channel.consume.mockImplementation(async (_queue, onMessage) => {
|
|
62
|
+
await onMessage(message);
|
|
63
|
+
return { consumerTag: 'tag' };
|
|
64
|
+
});
|
|
65
|
+
await consumer.start(channel);
|
|
66
|
+
expect(channel.nack).toHaveBeenCalledWith(message, false, false);
|
|
67
|
+
});
|
|
68
|
+
it('nacks without requeue for non-retriable errors', async () => {
|
|
69
|
+
const handler = jest.fn().mockRejectedValue(new Error('bad-message'));
|
|
70
|
+
const consumer = new consumer_1.Consumer('q', 'ex', 'topic', handler);
|
|
71
|
+
const channel = createChannel();
|
|
72
|
+
const message = createMessage();
|
|
73
|
+
channel.consume.mockImplementation(async (_queue, onMessage) => {
|
|
74
|
+
await onMessage(message);
|
|
75
|
+
return { consumerTag: 'tag' };
|
|
76
|
+
});
|
|
77
|
+
await consumer.start(channel);
|
|
78
|
+
expect(channel.nack).toHaveBeenCalledWith(message, false, false);
|
|
79
|
+
});
|
|
80
|
+
});
|
|
81
|
+
//# sourceMappingURL=consumer.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"consumer.test.js","sourceRoot":"","sources":["../../src/__tests__/consumer.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { Channel, ChannelModel } from 'amqplib';
|
|
2
|
+
import { AmqpConfig } from './config';
|
|
3
|
+
import { Consumer } from './consumer';
|
|
4
|
+
export declare class AmqpClient {
|
|
5
|
+
private readonly config;
|
|
6
|
+
private readonly consumers;
|
|
7
|
+
connection: ChannelModel | undefined;
|
|
8
|
+
channel: Channel | undefined;
|
|
9
|
+
constructor(config: AmqpConfig, consumers: Consumer[]);
|
|
10
|
+
start(): Promise<void>;
|
|
11
|
+
publish<T>(exchange: string, topic: string, message: T): void;
|
|
12
|
+
stop(): Promise<void>;
|
|
13
|
+
}
|
|
14
|
+
//# sourceMappingURL=amqpClient.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"amqpClient.d.ts","sourceRoot":"","sources":["../src/amqpClient.ts"],"names":[],"mappings":"AAAA,OAAa,EAAE,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACtD,OAAO,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AAGtC,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAEtC,qBAAa,UAAU;IAGP,OAAO,CAAC,QAAQ,CAAC,MAAM;IAAc,OAAO,CAAC,QAAQ,CAAC,SAAS;IAF3E,UAAU,EAAE,YAAY,GAAG,SAAS,CAAC;IACrC,OAAO,EAAE,OAAO,GAAG,SAAS,CAAC;gBACA,MAAM,EAAE,UAAU,EAAmB,SAAS,EAAE,QAAQ,EAAE;IAGjF,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAiB5B,OAAO,CAAC,CAAC,EAAE,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC,GAAG,IAAI;IAWvD,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;CAQ9B"}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.AmqpClient = void 0;
|
|
7
|
+
const amqplib_1 = __importDefault(require("amqplib"));
|
|
8
|
+
const errors_1 = require("./errors");
|
|
9
|
+
const ramda_1 = require("ramda");
|
|
10
|
+
class AmqpClient {
|
|
11
|
+
constructor(config, consumers) {
|
|
12
|
+
this.config = config;
|
|
13
|
+
this.consumers = consumers;
|
|
14
|
+
}
|
|
15
|
+
async start() {
|
|
16
|
+
try {
|
|
17
|
+
const connection = await amqplib_1.default.connect({
|
|
18
|
+
username: this.config.amqpUser,
|
|
19
|
+
password: this.config.amqpPassword,
|
|
20
|
+
hostname: this.config.amqpConnectionString,
|
|
21
|
+
});
|
|
22
|
+
this.connection = connection;
|
|
23
|
+
this.channel = await connection.createChannel().then(async (channel) => {
|
|
24
|
+
await Promise.all(this.consumers.map((consumer) => consumer.start(channel)));
|
|
25
|
+
return channel;
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
catch (error) {
|
|
29
|
+
throw new errors_1.AmqpConnectionError(error.message, { amqpConnectionString: this.config.amqpConnectionString });
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
publish(exchange, topic, message) {
|
|
33
|
+
if ((0, ramda_1.isNil)(this.channel)) {
|
|
34
|
+
throw new errors_1.AmqpUninitializedError();
|
|
35
|
+
}
|
|
36
|
+
const messageData = Buffer.from(JSON.stringify(message));
|
|
37
|
+
const isPublished = this.channel.publish(exchange, topic, messageData);
|
|
38
|
+
if (!isPublished) {
|
|
39
|
+
throw new errors_1.AmqpPublisherError({ exchange, topic, message });
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
async stop() {
|
|
43
|
+
if ((0, ramda_1.isNotNil)(this.channel)) {
|
|
44
|
+
await this.channel.close();
|
|
45
|
+
}
|
|
46
|
+
if ((0, ramda_1.isNotNil)(this.connection)) {
|
|
47
|
+
await this.connection.close();
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
exports.AmqpClient = AmqpClient;
|
|
52
|
+
//# sourceMappingURL=amqpClient.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"amqpClient.js","sourceRoot":"","sources":["../src/amqpClient.ts"],"names":[],"mappings":";;;;;;AAAA,sDAAsD;AAEtD,qCAA2F;AAC3F,iCAAwC;AAGxC,MAAa,UAAU;IAGnB,YAA6B,MAAkB,EAAmB,SAAqB;QAA1D,WAAM,GAAN,MAAM,CAAY;QAAmB,cAAS,GAAT,SAAS,CAAY;IACvF,CAAC;IAED,KAAK,CAAC,KAAK;QACP,IAAI,CAAC;YACD,MAAM,UAAU,GAAG,MAAM,iBAAI,CAAC,OAAO,CAAC;gBAClC,QAAQ,EAAE,IAAI,CAAC,MAAM,CAAC,QAAQ;gBAC9B,QAAQ,EAAE,IAAI,CAAC,MAAM,CAAC,YAAY;gBAClC,QAAQ,EAAE,IAAI,CAAC,MAAM,CAAC,oBAAoB;aAC7C,CAAC,CAAC;YACH,IAAI,CAAC,UAAU,GAAG,UAAU,CAAC;YAC7B,IAAI,CAAC,OAAO,GAAG,MAAM,UAAU,CAAC,aAAa,EAAE,CAAC,IAAI,CAAC,KAAK,EAAE,OAAO,EAAE,EAAE;gBACnE,MAAM,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,QAAQ,EAAE,EAAE,CAAC,QAAQ,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;gBAC7E,OAAO,OAAO,CAAC;YACnB,CAAC,CAAC,CAAC;QACP,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACb,MAAM,IAAI,4BAAmB,CAAE,KAAe,CAAC,OAAO,EAAE,EAAE,oBAAoB,EAAE,IAAI,CAAC,MAAM,CAAC,oBAAoB,EAAE,CAAC,CAAC;QACxH,CAAC;IACL,CAAC;IAED,OAAO,CAAI,QAAgB,EAAE,KAAa,EAAE,OAAU;QAClD,IAAI,IAAA,aAAK,EAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC;YACtB,MAAM,IAAI,+BAAsB,EAAE,CAAC;QACvC,CAAC;QACD,MAAM,WAAW,GAAG,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC,CAAC;QACzD,MAAM,WAAW,GAAG,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,QAAQ,EAAE,KAAK,EAAE,WAAW,CAAC,CAAC;QACvE,IAAI,CAAC,WAAW,EAAE,CAAC;YACf,MAAM,IAAI,2BAAkB,CAAC,EAAE,QAAQ,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC,CAAC;QAC/D,CAAC;IACL,CAAC;IAED,KAAK,CAAC,IAAI;QACN,IAAI,IAAA,gBAAQ,EAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC;YACzB,MAAM,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,CAAC;QAC/B,CAAC;QACD,IAAI,IAAA,gBAAQ,EAAC,IAAI,CAAC,UAAU,CAAC,EAAE,CAAC;YAC5B,MAAM,IAAI,CAAC,UAAU,CAAC,KAAK,EAAE,CAAC;QAClC,CAAC;IACL,CAAC;CACJ;AA1CD,gCA0CC"}
|
package/dist/config.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,UAAU;IACvB,oBAAoB,EAAE,MAAM,CAAC;IAC7B,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,MAAM,CAAC;CACxB"}
|
package/dist/config.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import amqp, { Channel } from 'amqplib';
|
|
2
|
+
export declare class Consumer {
|
|
3
|
+
private readonly queue;
|
|
4
|
+
private readonly exchange;
|
|
5
|
+
private readonly topic;
|
|
6
|
+
private readonly handler;
|
|
7
|
+
constructor(queue: string, exchange: string, topic: string, handler: (message: amqp.Message | null) => Promise<void>);
|
|
8
|
+
start(channel: Channel): Promise<void>;
|
|
9
|
+
private shouldRequeue;
|
|
10
|
+
private getDeliveryCount;
|
|
11
|
+
private createAndBindQueue;
|
|
12
|
+
private assertQueue;
|
|
13
|
+
private assertExchange;
|
|
14
|
+
private bindQueue;
|
|
15
|
+
}
|
|
16
|
+
//# sourceMappingURL=consumer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"consumer.d.ts","sourceRoot":"","sources":["../src/consumer.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,EAAE,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAGxC,qBAAa,QAAQ;IAEb,OAAO,CAAC,QAAQ,CAAC,KAAK;IACtB,OAAO,CAAC,QAAQ,CAAC,QAAQ;IACzB,OAAO,CAAC,QAAQ,CAAC,KAAK;IACtB,OAAO,CAAC,QAAQ,CAAC,OAAO;gBAHP,KAAK,EAAE,MAAM,EACb,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,CAAC,OAAO,EAAE,IAAI,CAAC,OAAO,GAAG,IAAI,KAAK,OAAO,CAAC,IAAI,CAAC;IAGvE,KAAK,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC;IAmB5C,OAAO,CAAC,aAAa;IAIrB,OAAO,CAAC,gBAAgB;YAYV,kBAAkB;YAMlB,WAAW;YAIX,cAAc;YAId,SAAS;CAG1B"}
|
package/dist/consumer.js
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.Consumer = void 0;
|
|
4
|
+
const errors_1 = require("./errors");
|
|
5
|
+
class Consumer {
|
|
6
|
+
constructor(queue, exchange, topic, handler) {
|
|
7
|
+
this.queue = queue;
|
|
8
|
+
this.exchange = exchange;
|
|
9
|
+
this.topic = topic;
|
|
10
|
+
this.handler = handler;
|
|
11
|
+
}
|
|
12
|
+
async start(channel) {
|
|
13
|
+
await this.createAndBindQueue(channel, this.queue, this.exchange, this.topic);
|
|
14
|
+
await channel.consume(this.queue, async (message) => {
|
|
15
|
+
if (!message) {
|
|
16
|
+
return;
|
|
17
|
+
}
|
|
18
|
+
try {
|
|
19
|
+
await this.handler(message);
|
|
20
|
+
channel.ack(message);
|
|
21
|
+
}
|
|
22
|
+
catch (error) {
|
|
23
|
+
if (error instanceof errors_1.AmqpRetriableError) {
|
|
24
|
+
channel.nack(message, false, this.shouldRequeue(message, error.retryLimit));
|
|
25
|
+
}
|
|
26
|
+
else {
|
|
27
|
+
channel.nack(message, false, false);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
shouldRequeue(message, retryLimit) {
|
|
33
|
+
return this.getDeliveryCount(message) < retryLimit;
|
|
34
|
+
}
|
|
35
|
+
getDeliveryCount(message) {
|
|
36
|
+
const deliveryCount = message.properties.headers?.['x-delivery-count'];
|
|
37
|
+
if (typeof deliveryCount === 'number') {
|
|
38
|
+
return deliveryCount;
|
|
39
|
+
}
|
|
40
|
+
else if (typeof deliveryCount === 'string') {
|
|
41
|
+
const parsedValue = Number(deliveryCount);
|
|
42
|
+
if (!Number.isNaN(parsedValue)) {
|
|
43
|
+
return parsedValue;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
return 0;
|
|
47
|
+
}
|
|
48
|
+
async createAndBindQueue(channel, queue, exchange, topic) {
|
|
49
|
+
await this.assertQueue(channel, queue);
|
|
50
|
+
await this.assertExchange(channel, exchange);
|
|
51
|
+
await this.bindQueue(channel, queue, exchange, topic);
|
|
52
|
+
}
|
|
53
|
+
async assertQueue(channel, queue) {
|
|
54
|
+
await channel.assertQueue(queue, { durable: true, arguments: { 'x-queue-type': 'quorum' } });
|
|
55
|
+
}
|
|
56
|
+
async assertExchange(channel, exchange) {
|
|
57
|
+
await channel.assertExchange(exchange, "topic", { durable: true });
|
|
58
|
+
}
|
|
59
|
+
async bindQueue(channel, queue, exchange, topic) {
|
|
60
|
+
await channel.bindQueue(queue, exchange, topic);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
exports.Consumer = Consumer;
|
|
64
|
+
//# sourceMappingURL=consumer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"consumer.js","sourceRoot":"","sources":["../src/consumer.ts"],"names":[],"mappings":"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"}
|
package/dist/errors.d.ts
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { AmqpConnectionArgs, AmqpPublishArgs } from "./types";
|
|
2
|
+
export declare class AmqpError extends Error {
|
|
3
|
+
constructor(message: string, args?: Object);
|
|
4
|
+
}
|
|
5
|
+
export declare class AmqpUninitializedError extends AmqpError {
|
|
6
|
+
constructor();
|
|
7
|
+
}
|
|
8
|
+
export declare class AmqpConnectionError extends AmqpError {
|
|
9
|
+
constructor(errorMessage: string, args: AmqpConnectionArgs);
|
|
10
|
+
}
|
|
11
|
+
export declare class AmqpPublisherError<T> extends AmqpError {
|
|
12
|
+
constructor(args: AmqpPublishArgs<T>);
|
|
13
|
+
}
|
|
14
|
+
export declare class AmqpConsumerError extends AmqpError {
|
|
15
|
+
constructor(error: Error);
|
|
16
|
+
}
|
|
17
|
+
export declare class AmqpRetriableError extends AmqpConsumerError {
|
|
18
|
+
readonly retryLimit: number;
|
|
19
|
+
constructor(error: Error, retryLimit?: number);
|
|
20
|
+
}
|
|
21
|
+
export declare class AmqpFatalError extends AmqpConsumerError {
|
|
22
|
+
constructor(error: Error);
|
|
23
|
+
}
|
|
24
|
+
//# sourceMappingURL=errors.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAE9D,qBAAa,SAAU,SAAQ,KAAK;gBACpB,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM;CAG7C;AAED,qBAAa,sBAAuB,SAAQ,SAAS;;CAIpD;AAED,qBAAa,mBAAoB,SAAQ,SAAS;gBAClC,YAAY,EAAE,MAAM,EAAE,IAAI,EAAE,kBAAkB;CAG7D;AAED,qBAAa,kBAAkB,CAAC,CAAC,CAAE,SAAQ,SAAS;gBACpC,IAAI,EAAE,eAAe,CAAC,CAAC,CAAC;CAGvC;AAED,qBAAa,iBAAkB,SAAQ,SAAS;gBAChC,KAAK,EAAE,KAAK;CAG3B;AAED,qBAAa,kBAAmB,SAAQ,iBAAiB;aACX,UAAU,EAAE,MAAM;gBAAhD,KAAK,EAAE,KAAK,EAAkB,UAAU,GAAE,MAAU;CAGnE;AAED,qBAAa,cAAe,SAAQ,iBAAiB;gBACrC,KAAK,EAAE,KAAK;CAG3B"}
|
package/dist/errors.js
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.AmqpFatalError = exports.AmqpRetriableError = exports.AmqpConsumerError = exports.AmqpPublisherError = exports.AmqpConnectionError = exports.AmqpUninitializedError = exports.AmqpError = void 0;
|
|
4
|
+
class AmqpError extends Error {
|
|
5
|
+
constructor(message, args) {
|
|
6
|
+
super(`amqp error: ${message}, ${args ? JSON.stringify(args) : ''}`);
|
|
7
|
+
}
|
|
8
|
+
}
|
|
9
|
+
exports.AmqpError = AmqpError;
|
|
10
|
+
class AmqpUninitializedError extends AmqpError {
|
|
11
|
+
constructor() {
|
|
12
|
+
super("AMQP client is not initialized");
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
exports.AmqpUninitializedError = AmqpUninitializedError;
|
|
16
|
+
class AmqpConnectionError extends AmqpError {
|
|
17
|
+
constructor(errorMessage, args) {
|
|
18
|
+
super(`Failed to connect to AMQP server: ${errorMessage}`, args);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
exports.AmqpConnectionError = AmqpConnectionError;
|
|
22
|
+
class AmqpPublisherError extends AmqpError {
|
|
23
|
+
constructor(args) {
|
|
24
|
+
super("Failed to publish message", args);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
exports.AmqpPublisherError = AmqpPublisherError;
|
|
28
|
+
class AmqpConsumerError extends AmqpError {
|
|
29
|
+
constructor(error) {
|
|
30
|
+
super("Failed to consume message", { error });
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
exports.AmqpConsumerError = AmqpConsumerError;
|
|
34
|
+
class AmqpRetriableError extends AmqpConsumerError {
|
|
35
|
+
constructor(error, retryLimit = 0) {
|
|
36
|
+
super(error);
|
|
37
|
+
this.retryLimit = retryLimit;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
exports.AmqpRetriableError = AmqpRetriableError;
|
|
41
|
+
class AmqpFatalError extends AmqpConsumerError {
|
|
42
|
+
constructor(error) {
|
|
43
|
+
super(error);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
exports.AmqpFatalError = AmqpFatalError;
|
|
47
|
+
//# sourceMappingURL=errors.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.js","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":";;;AAEA,MAAa,SAAU,SAAQ,KAAK;IAChC,YAAY,OAAe,EAAE,IAAa;QACtC,KAAK,CAAC,eAAe,OAAO,KAAK,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;IACzE,CAAC;CACJ;AAJD,8BAIC;AAED,MAAa,sBAAuB,SAAQ,SAAS;IACjD;QACI,KAAK,CAAC,gCAAgC,CAAC,CAAC;IAC5C,CAAC;CACJ;AAJD,wDAIC;AAED,MAAa,mBAAoB,SAAQ,SAAS;IAC9C,YAAY,YAAoB,EAAE,IAAwB;QACtD,KAAK,CAAC,qCAAqC,YAAY,EAAE,EAAE,IAAI,CAAC,CAAC;IACrE,CAAC;CACJ;AAJD,kDAIC;AAED,MAAa,kBAAsB,SAAQ,SAAS;IAChD,YAAY,IAAwB;QAChC,KAAK,CAAC,2BAA2B,EAAE,IAAI,CAAC,CAAC;IAC7C,CAAC;CACJ;AAJD,gDAIC;AAED,MAAa,iBAAkB,SAAQ,SAAS;IAC5C,YAAY,KAAY;QACpB,KAAK,CAAC,2BAA2B,EAAE,EAAE,KAAK,EAAE,CAAC,CAAC;IAClD,CAAC;CACJ;AAJD,8CAIC;AAED,MAAa,kBAAmB,SAAQ,iBAAiB;IACrD,YAAY,KAAY,EAAkB,aAAqB,CAAC;QAC5D,KAAK,CAAC,KAAK,CAAC,CAAC;QADyB,eAAU,GAAV,UAAU,CAAY;IAEhE,CAAC;CACJ;AAJD,gDAIC;AAED,MAAa,cAAe,SAAQ,iBAAiB;IACjD,YAAY,KAAY;QACpB,KAAK,CAAC,KAAK,CAAC,CAAC;IACjB,CAAC;CACJ;AAJD,wCAIC"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export { AmqpClient } from './amqpClient';
|
|
2
|
+
export { AmqpConfig } from './config';
|
|
3
|
+
export { Consumer } from './consumer';
|
|
4
|
+
export { AmqpError, AmqpConnectionError, AmqpPublisherError, AmqpConsumerError, AmqpRetriableError, AmqpFatalError, } from './errors';
|
|
5
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC1C,OAAO,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AACtC,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AACtC,OAAO,EACH,SAAS,EACT,mBAAmB,EACnB,kBAAkB,EAClB,iBAAiB,EACjB,kBAAkB,EAClB,cAAc,GACjB,MAAM,UAAU,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.AmqpFatalError = exports.AmqpRetriableError = exports.AmqpConsumerError = exports.AmqpPublisherError = exports.AmqpConnectionError = exports.AmqpError = exports.Consumer = exports.AmqpClient = void 0;
|
|
4
|
+
var amqpClient_1 = require("./amqpClient");
|
|
5
|
+
Object.defineProperty(exports, "AmqpClient", { enumerable: true, get: function () { return amqpClient_1.AmqpClient; } });
|
|
6
|
+
var consumer_1 = require("./consumer");
|
|
7
|
+
Object.defineProperty(exports, "Consumer", { enumerable: true, get: function () { return consumer_1.Consumer; } });
|
|
8
|
+
var errors_1 = require("./errors");
|
|
9
|
+
Object.defineProperty(exports, "AmqpError", { enumerable: true, get: function () { return errors_1.AmqpError; } });
|
|
10
|
+
Object.defineProperty(exports, "AmqpConnectionError", { enumerable: true, get: function () { return errors_1.AmqpConnectionError; } });
|
|
11
|
+
Object.defineProperty(exports, "AmqpPublisherError", { enumerable: true, get: function () { return errors_1.AmqpPublisherError; } });
|
|
12
|
+
Object.defineProperty(exports, "AmqpConsumerError", { enumerable: true, get: function () { return errors_1.AmqpConsumerError; } });
|
|
13
|
+
Object.defineProperty(exports, "AmqpRetriableError", { enumerable: true, get: function () { return errors_1.AmqpRetriableError; } });
|
|
14
|
+
Object.defineProperty(exports, "AmqpFatalError", { enumerable: true, get: function () { return errors_1.AmqpFatalError; } });
|
|
15
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;AAAA,2CAA0C;AAAjC,wGAAA,UAAU,OAAA;AAEnB,uCAAsC;AAA7B,oGAAA,QAAQ,OAAA;AACjB,mCAOkB;AANd,mGAAA,SAAS,OAAA;AACT,6GAAA,mBAAmB,OAAA;AACnB,4GAAA,kBAAkB,OAAA;AAClB,2GAAA,iBAAiB,OAAA;AACjB,4GAAA,kBAAkB,OAAA;AAClB,wGAAA,cAAc,OAAA"}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,kBAAkB;IAC/B,oBAAoB,EAAE,MAAM,CAAC;CAChC;AAED,MAAM,WAAW,eAAe,CAAC,CAAC;IAC9B,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,CAAC,CAAC;CACd"}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":""}
|
package/package.json
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@ido_kawaz/amqp-client",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "AMQP client library for Kawaz Plus services",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"types": "dist/index.d.ts",
|
|
7
|
+
"files": [
|
|
8
|
+
"dist"
|
|
9
|
+
],
|
|
10
|
+
"scripts": {
|
|
11
|
+
"clean": "rimraf dist",
|
|
12
|
+
"clean:advanced": "rimraf dist node_modules package-lock.json",
|
|
13
|
+
"build": "npm run clean && tsc",
|
|
14
|
+
"build:watch": "tsc --watch",
|
|
15
|
+
"build:advanced": "npm run clean:advanced && npm i && tsc",
|
|
16
|
+
"test": "npm run build && jest --runInBand",
|
|
17
|
+
"publish": "npm run build:advanced && npm publish --access public"
|
|
18
|
+
},
|
|
19
|
+
"keywords": [
|
|
20
|
+
"amqp",
|
|
21
|
+
"rabbitmq",
|
|
22
|
+
"messaging"
|
|
23
|
+
],
|
|
24
|
+
"author": "",
|
|
25
|
+
"license": "MIT",
|
|
26
|
+
"dependencies": {
|
|
27
|
+
"amqplib": "^0.10.9",
|
|
28
|
+
"ramda": "^0.32.0"
|
|
29
|
+
},
|
|
30
|
+
"devDependencies": {
|
|
31
|
+
"@types/amqplib": "^0.10.8",
|
|
32
|
+
"@types/jest": "^30.0.0",
|
|
33
|
+
"@types/node": "^20.0.0",
|
|
34
|
+
"@types/ramda": "^0.31.1",
|
|
35
|
+
"jest": "^30.0.0",
|
|
36
|
+
"rimraf": "^6.1.3",
|
|
37
|
+
"ts-jest": "^29.4.6",
|
|
38
|
+
"typescript": "^5.9.3"
|
|
39
|
+
}
|
|
40
|
+
}
|