@rocketmq/amqp 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.
- package/.turbo/turbo-build.log +21 -0
- package/dist/index.cjs +108 -0
- package/dist/index.d.cts +70 -0
- package/dist/index.d.ts +70 -0
- package/dist/index.js +70 -0
- package/package.json +21 -0
- package/src/channel.test.ts +105 -0
- package/src/channel.ts +80 -0
- package/src/connection.test.ts +54 -0
- package/src/connection.ts +34 -0
- package/src/index.ts +13 -0
- package/src/types.ts +18 -0
- package/tsconfig.json +8 -0
- package/tsup.config.ts +8 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
|
|
2
|
+
|
|
3
|
+
> @rocketmq/amqp@0.1.0 build /home/edilson/learnspace/rocketmq-broker/client-ts/packages/amqp
|
|
4
|
+
> tsup
|
|
5
|
+
|
|
6
|
+
CLI Building entry: src/index.ts
|
|
7
|
+
CLI Using tsconfig: tsconfig.json
|
|
8
|
+
CLI tsup v8.5.1
|
|
9
|
+
CLI Using tsup config: /home/edilson/learnspace/rocketmq-broker/client-ts/packages/amqp/tsup.config.ts
|
|
10
|
+
CLI Target: es2022
|
|
11
|
+
CLI Cleaning output folder
|
|
12
|
+
ESM Build start
|
|
13
|
+
CJS Build start
|
|
14
|
+
ESM dist/index.js 1.64 KB
|
|
15
|
+
ESM ⚡️ Build success in 13ms
|
|
16
|
+
CJS dist/index.cjs 3.31 KB
|
|
17
|
+
CJS ⚡️ Build success in 13ms
|
|
18
|
+
DTS Build start
|
|
19
|
+
DTS ⚡️ Build success in 965ms
|
|
20
|
+
DTS dist/index.d.ts 2.74 KB
|
|
21
|
+
DTS dist/index.d.cts 2.74 KB
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __export = (target, all) => {
|
|
9
|
+
for (var name in all)
|
|
10
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
11
|
+
};
|
|
12
|
+
var __copyProps = (to, from, except, desc) => {
|
|
13
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
+
for (let key of __getOwnPropNames(from))
|
|
15
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
16
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
17
|
+
}
|
|
18
|
+
return to;
|
|
19
|
+
};
|
|
20
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
+
mod
|
|
27
|
+
));
|
|
28
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
|
+
|
|
30
|
+
// src/index.ts
|
|
31
|
+
var index_exports = {};
|
|
32
|
+
__export(index_exports, {
|
|
33
|
+
AmqpChannel: () => AmqpChannel,
|
|
34
|
+
AmqpConnection: () => AmqpConnection
|
|
35
|
+
});
|
|
36
|
+
module.exports = __toCommonJS(index_exports);
|
|
37
|
+
|
|
38
|
+
// src/connection.ts
|
|
39
|
+
var import_amqplib = __toESM(require("amqplib"), 1);
|
|
40
|
+
|
|
41
|
+
// src/channel.ts
|
|
42
|
+
var AmqpChannel = class {
|
|
43
|
+
constructor(ch) {
|
|
44
|
+
this.ch = ch;
|
|
45
|
+
}
|
|
46
|
+
ch;
|
|
47
|
+
/** Exposes the raw amqplib channel for event listeners (e.g. "error"). */
|
|
48
|
+
get raw() {
|
|
49
|
+
return this.ch;
|
|
50
|
+
}
|
|
51
|
+
async assertQueue(name, opts) {
|
|
52
|
+
return this.ch.assertQueue(name, opts);
|
|
53
|
+
}
|
|
54
|
+
async assertExchange(name, type, opts) {
|
|
55
|
+
return this.ch.assertExchange(name, type, opts);
|
|
56
|
+
}
|
|
57
|
+
async bindQueue(queue, exchange, routingKey) {
|
|
58
|
+
return this.ch.bindQueue(queue, exchange, routingKey);
|
|
59
|
+
}
|
|
60
|
+
sendToQueue(queue, content, opts) {
|
|
61
|
+
return this.ch.sendToQueue(queue, content, opts);
|
|
62
|
+
}
|
|
63
|
+
publish(exchange, routingKey, content, opts) {
|
|
64
|
+
return this.ch.publish(exchange, routingKey, content, opts);
|
|
65
|
+
}
|
|
66
|
+
async consume(queue, handler, opts) {
|
|
67
|
+
return this.ch.consume(queue, handler, opts);
|
|
68
|
+
}
|
|
69
|
+
ack(msg) {
|
|
70
|
+
this.ch.ack(msg);
|
|
71
|
+
}
|
|
72
|
+
nack(msg, requeue) {
|
|
73
|
+
this.ch.nack(msg, false, requeue);
|
|
74
|
+
}
|
|
75
|
+
async prefetch(count) {
|
|
76
|
+
await this.ch.prefetch(count);
|
|
77
|
+
}
|
|
78
|
+
async close() {
|
|
79
|
+
await this.ch.close();
|
|
80
|
+
}
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
// src/connection.ts
|
|
84
|
+
var AmqpConnection = class _AmqpConnection {
|
|
85
|
+
constructor(conn) {
|
|
86
|
+
this.conn = conn;
|
|
87
|
+
}
|
|
88
|
+
conn;
|
|
89
|
+
/** Opens a new AMQP connection to the given URL. */
|
|
90
|
+
static async connect(url) {
|
|
91
|
+
const conn = await import_amqplib.default.connect(url);
|
|
92
|
+
return new _AmqpConnection(conn);
|
|
93
|
+
}
|
|
94
|
+
/** Creates a new channel on this connection. */
|
|
95
|
+
async createChannel() {
|
|
96
|
+
const ch = await this.conn.createChannel();
|
|
97
|
+
return new AmqpChannel(ch);
|
|
98
|
+
}
|
|
99
|
+
/** Closes the underlying TCP connection. */
|
|
100
|
+
async close() {
|
|
101
|
+
await this.conn.close();
|
|
102
|
+
}
|
|
103
|
+
};
|
|
104
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
105
|
+
0 && (module.exports = {
|
|
106
|
+
AmqpChannel,
|
|
107
|
+
AmqpConnection
|
|
108
|
+
});
|
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import amqp from 'amqplib';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Re-exported amqplib types used across the SDK.
|
|
5
|
+
*
|
|
6
|
+
* Centralizes the amqplib dependency so other packages never
|
|
7
|
+
* import from amqplib directly.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
type ConsumeMessage = amqp.ConsumeMessage;
|
|
11
|
+
type PublishOptions = amqp.Options.Publish;
|
|
12
|
+
type ConsumeOptions = amqp.Options.Consume;
|
|
13
|
+
type AssertQueueOptions = amqp.Options.AssertQueue;
|
|
14
|
+
type AssertExchangeOptions = amqp.Options.AssertExchange;
|
|
15
|
+
type AssertQueueReply = amqp.Replies.AssertQueue;
|
|
16
|
+
type AssertExchangeReply = amqp.Replies.AssertExchange;
|
|
17
|
+
type ConsumeReply = amqp.Replies.Consume;
|
|
18
|
+
type EmptyReply = amqp.Replies.Empty;
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Thin wrapper around an amqplib Channel.
|
|
22
|
+
*
|
|
23
|
+
* Exposes only the operations the SDK needs without leaking the full
|
|
24
|
+
* amqplib surface. No schema or validation logic lives here.
|
|
25
|
+
*
|
|
26
|
+
* Usage:
|
|
27
|
+
* const ch = await connection.createChannel();
|
|
28
|
+
* ch.sendToQueue("orders", buffer, { persistent: true });
|
|
29
|
+
*/
|
|
30
|
+
|
|
31
|
+
declare class AmqpChannel {
|
|
32
|
+
private readonly ch;
|
|
33
|
+
constructor(ch: amqp.Channel);
|
|
34
|
+
/** Exposes the raw amqplib channel for event listeners (e.g. "error"). */
|
|
35
|
+
get raw(): amqp.Channel;
|
|
36
|
+
assertQueue(name: string, opts?: AssertQueueOptions): Promise<AssertQueueReply>;
|
|
37
|
+
assertExchange(name: string, type: string, opts?: AssertExchangeOptions): Promise<AssertExchangeReply>;
|
|
38
|
+
bindQueue(queue: string, exchange: string, routingKey: string): Promise<EmptyReply>;
|
|
39
|
+
sendToQueue(queue: string, content: Buffer, opts?: PublishOptions): boolean;
|
|
40
|
+
publish(exchange: string, routingKey: string, content: Buffer, opts?: PublishOptions): boolean;
|
|
41
|
+
consume(queue: string, handler: (msg: ConsumeMessage | null) => void, opts?: ConsumeOptions): Promise<ConsumeReply>;
|
|
42
|
+
ack(msg: ConsumeMessage): void;
|
|
43
|
+
nack(msg: ConsumeMessage, requeue?: boolean): void;
|
|
44
|
+
prefetch(count: number): Promise<void>;
|
|
45
|
+
close(): Promise<void>;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Manages the AMQP connection lifecycle.
|
|
50
|
+
*
|
|
51
|
+
* Wraps amqplib's connect() and exposes a single createChannel() method.
|
|
52
|
+
* No schema, validation, or serialization logic lives here.
|
|
53
|
+
*
|
|
54
|
+
* Usage:
|
|
55
|
+
* const conn = await AmqpConnection.connect("amqp://localhost");
|
|
56
|
+
* const ch = await conn.createChannel();
|
|
57
|
+
*/
|
|
58
|
+
|
|
59
|
+
declare class AmqpConnection {
|
|
60
|
+
private readonly conn;
|
|
61
|
+
private constructor();
|
|
62
|
+
/** Opens a new AMQP connection to the given URL. */
|
|
63
|
+
static connect(url: string): Promise<AmqpConnection>;
|
|
64
|
+
/** Creates a new channel on this connection. */
|
|
65
|
+
createChannel(): Promise<AmqpChannel>;
|
|
66
|
+
/** Closes the underlying TCP connection. */
|
|
67
|
+
close(): Promise<void>;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export { AmqpChannel, AmqpConnection, type AssertExchangeOptions, type AssertExchangeReply, type AssertQueueOptions, type AssertQueueReply, type ConsumeMessage, type ConsumeOptions, type ConsumeReply, type EmptyReply, type PublishOptions };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import amqp from 'amqplib';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Re-exported amqplib types used across the SDK.
|
|
5
|
+
*
|
|
6
|
+
* Centralizes the amqplib dependency so other packages never
|
|
7
|
+
* import from amqplib directly.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
type ConsumeMessage = amqp.ConsumeMessage;
|
|
11
|
+
type PublishOptions = amqp.Options.Publish;
|
|
12
|
+
type ConsumeOptions = amqp.Options.Consume;
|
|
13
|
+
type AssertQueueOptions = amqp.Options.AssertQueue;
|
|
14
|
+
type AssertExchangeOptions = amqp.Options.AssertExchange;
|
|
15
|
+
type AssertQueueReply = amqp.Replies.AssertQueue;
|
|
16
|
+
type AssertExchangeReply = amqp.Replies.AssertExchange;
|
|
17
|
+
type ConsumeReply = amqp.Replies.Consume;
|
|
18
|
+
type EmptyReply = amqp.Replies.Empty;
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Thin wrapper around an amqplib Channel.
|
|
22
|
+
*
|
|
23
|
+
* Exposes only the operations the SDK needs without leaking the full
|
|
24
|
+
* amqplib surface. No schema or validation logic lives here.
|
|
25
|
+
*
|
|
26
|
+
* Usage:
|
|
27
|
+
* const ch = await connection.createChannel();
|
|
28
|
+
* ch.sendToQueue("orders", buffer, { persistent: true });
|
|
29
|
+
*/
|
|
30
|
+
|
|
31
|
+
declare class AmqpChannel {
|
|
32
|
+
private readonly ch;
|
|
33
|
+
constructor(ch: amqp.Channel);
|
|
34
|
+
/** Exposes the raw amqplib channel for event listeners (e.g. "error"). */
|
|
35
|
+
get raw(): amqp.Channel;
|
|
36
|
+
assertQueue(name: string, opts?: AssertQueueOptions): Promise<AssertQueueReply>;
|
|
37
|
+
assertExchange(name: string, type: string, opts?: AssertExchangeOptions): Promise<AssertExchangeReply>;
|
|
38
|
+
bindQueue(queue: string, exchange: string, routingKey: string): Promise<EmptyReply>;
|
|
39
|
+
sendToQueue(queue: string, content: Buffer, opts?: PublishOptions): boolean;
|
|
40
|
+
publish(exchange: string, routingKey: string, content: Buffer, opts?: PublishOptions): boolean;
|
|
41
|
+
consume(queue: string, handler: (msg: ConsumeMessage | null) => void, opts?: ConsumeOptions): Promise<ConsumeReply>;
|
|
42
|
+
ack(msg: ConsumeMessage): void;
|
|
43
|
+
nack(msg: ConsumeMessage, requeue?: boolean): void;
|
|
44
|
+
prefetch(count: number): Promise<void>;
|
|
45
|
+
close(): Promise<void>;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Manages the AMQP connection lifecycle.
|
|
50
|
+
*
|
|
51
|
+
* Wraps amqplib's connect() and exposes a single createChannel() method.
|
|
52
|
+
* No schema, validation, or serialization logic lives here.
|
|
53
|
+
*
|
|
54
|
+
* Usage:
|
|
55
|
+
* const conn = await AmqpConnection.connect("amqp://localhost");
|
|
56
|
+
* const ch = await conn.createChannel();
|
|
57
|
+
*/
|
|
58
|
+
|
|
59
|
+
declare class AmqpConnection {
|
|
60
|
+
private readonly conn;
|
|
61
|
+
private constructor();
|
|
62
|
+
/** Opens a new AMQP connection to the given URL. */
|
|
63
|
+
static connect(url: string): Promise<AmqpConnection>;
|
|
64
|
+
/** Creates a new channel on this connection. */
|
|
65
|
+
createChannel(): Promise<AmqpChannel>;
|
|
66
|
+
/** Closes the underlying TCP connection. */
|
|
67
|
+
close(): Promise<void>;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export { AmqpChannel, AmqpConnection, type AssertExchangeOptions, type AssertExchangeReply, type AssertQueueOptions, type AssertQueueReply, type ConsumeMessage, type ConsumeOptions, type ConsumeReply, type EmptyReply, type PublishOptions };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
// src/connection.ts
|
|
2
|
+
import amqp from "amqplib";
|
|
3
|
+
|
|
4
|
+
// src/channel.ts
|
|
5
|
+
var AmqpChannel = class {
|
|
6
|
+
constructor(ch) {
|
|
7
|
+
this.ch = ch;
|
|
8
|
+
}
|
|
9
|
+
ch;
|
|
10
|
+
/** Exposes the raw amqplib channel for event listeners (e.g. "error"). */
|
|
11
|
+
get raw() {
|
|
12
|
+
return this.ch;
|
|
13
|
+
}
|
|
14
|
+
async assertQueue(name, opts) {
|
|
15
|
+
return this.ch.assertQueue(name, opts);
|
|
16
|
+
}
|
|
17
|
+
async assertExchange(name, type, opts) {
|
|
18
|
+
return this.ch.assertExchange(name, type, opts);
|
|
19
|
+
}
|
|
20
|
+
async bindQueue(queue, exchange, routingKey) {
|
|
21
|
+
return this.ch.bindQueue(queue, exchange, routingKey);
|
|
22
|
+
}
|
|
23
|
+
sendToQueue(queue, content, opts) {
|
|
24
|
+
return this.ch.sendToQueue(queue, content, opts);
|
|
25
|
+
}
|
|
26
|
+
publish(exchange, routingKey, content, opts) {
|
|
27
|
+
return this.ch.publish(exchange, routingKey, content, opts);
|
|
28
|
+
}
|
|
29
|
+
async consume(queue, handler, opts) {
|
|
30
|
+
return this.ch.consume(queue, handler, opts);
|
|
31
|
+
}
|
|
32
|
+
ack(msg) {
|
|
33
|
+
this.ch.ack(msg);
|
|
34
|
+
}
|
|
35
|
+
nack(msg, requeue) {
|
|
36
|
+
this.ch.nack(msg, false, requeue);
|
|
37
|
+
}
|
|
38
|
+
async prefetch(count) {
|
|
39
|
+
await this.ch.prefetch(count);
|
|
40
|
+
}
|
|
41
|
+
async close() {
|
|
42
|
+
await this.ch.close();
|
|
43
|
+
}
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
// src/connection.ts
|
|
47
|
+
var AmqpConnection = class _AmqpConnection {
|
|
48
|
+
constructor(conn) {
|
|
49
|
+
this.conn = conn;
|
|
50
|
+
}
|
|
51
|
+
conn;
|
|
52
|
+
/** Opens a new AMQP connection to the given URL. */
|
|
53
|
+
static async connect(url) {
|
|
54
|
+
const conn = await amqp.connect(url);
|
|
55
|
+
return new _AmqpConnection(conn);
|
|
56
|
+
}
|
|
57
|
+
/** Creates a new channel on this connection. */
|
|
58
|
+
async createChannel() {
|
|
59
|
+
const ch = await this.conn.createChannel();
|
|
60
|
+
return new AmqpChannel(ch);
|
|
61
|
+
}
|
|
62
|
+
/** Closes the underlying TCP connection. */
|
|
63
|
+
async close() {
|
|
64
|
+
await this.conn.close();
|
|
65
|
+
}
|
|
66
|
+
};
|
|
67
|
+
export {
|
|
68
|
+
AmqpChannel,
|
|
69
|
+
AmqpConnection
|
|
70
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@rocketmq/amqp",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"exports": {
|
|
6
|
+
".": {
|
|
7
|
+
"import": "./dist/index.js",
|
|
8
|
+
"types": "./dist/index.d.ts"
|
|
9
|
+
}
|
|
10
|
+
},
|
|
11
|
+
"dependencies": {
|
|
12
|
+
"amqplib": "^0.10.5"
|
|
13
|
+
},
|
|
14
|
+
"devDependencies": {
|
|
15
|
+
"@types/amqplib": "^0.10.6"
|
|
16
|
+
},
|
|
17
|
+
"scripts": {
|
|
18
|
+
"build": "tsup",
|
|
19
|
+
"test": "vitest run"
|
|
20
|
+
}
|
|
21
|
+
}
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for AmqpChannel — thin wrapper over amqplib Channel.
|
|
3
|
+
*
|
|
4
|
+
* Uses a FakeChannel mock class to avoid real AMQP connections.
|
|
5
|
+
* Covers every method: assertQueue, assertExchange, bindQueue,
|
|
6
|
+
* sendToQueue, publish, consume, ack, nack, prefetch, close.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { describe, it, expect, beforeEach, vi } from 'vitest';
|
|
10
|
+
import { AmqpChannel } from './channel.js';
|
|
11
|
+
|
|
12
|
+
/** Named fake class per user rules — no inline stubs. */
|
|
13
|
+
class FakeChannel {
|
|
14
|
+
assertQueue = vi.fn().mockResolvedValue({ queue: 'q', messageCount: 0, consumerCount: 0 });
|
|
15
|
+
assertExchange = vi.fn().mockResolvedValue({ exchange: 'ex' });
|
|
16
|
+
bindQueue = vi.fn().mockResolvedValue({});
|
|
17
|
+
sendToQueue = vi.fn().mockReturnValue(true);
|
|
18
|
+
publish = vi.fn().mockReturnValue(true);
|
|
19
|
+
consume = vi.fn().mockResolvedValue({ consumerTag: 'tag-1' });
|
|
20
|
+
ack = vi.fn();
|
|
21
|
+
nack = vi.fn();
|
|
22
|
+
prefetch = vi.fn().mockResolvedValue(undefined);
|
|
23
|
+
close = vi.fn().mockResolvedValue(undefined);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
describe('AmqpChannel', () => {
|
|
27
|
+
let fake: FakeChannel;
|
|
28
|
+
let channel: AmqpChannel;
|
|
29
|
+
|
|
30
|
+
beforeEach(() => {
|
|
31
|
+
fake = new FakeChannel();
|
|
32
|
+
// Cast to satisfy amqp.Channel type — only the methods we call exist
|
|
33
|
+
channel = new AmqpChannel(fake as never);
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
it('exposes raw channel via .raw', () => {
|
|
37
|
+
expect(channel.raw).toBe(fake);
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
it('delegates assertQueue', async () => {
|
|
41
|
+
const result = await channel.assertQueue('orders', { durable: true });
|
|
42
|
+
expect(fake.assertQueue).toHaveBeenCalledWith('orders', { durable: true });
|
|
43
|
+
expect(result.queue).toBe('q');
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
it('delegates assertExchange', async () => {
|
|
47
|
+
const result = await channel.assertExchange('ex', 'direct', { durable: true });
|
|
48
|
+
expect(fake.assertExchange).toHaveBeenCalledWith('ex', 'direct', { durable: true });
|
|
49
|
+
expect(result.exchange).toBe('ex');
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
it('delegates bindQueue', async () => {
|
|
53
|
+
await channel.bindQueue('q', 'ex', 'key');
|
|
54
|
+
expect(fake.bindQueue).toHaveBeenCalledWith('q', 'ex', 'key');
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
it('delegates sendToQueue', () => {
|
|
58
|
+
const buf = Buffer.from('test');
|
|
59
|
+
const result = channel.sendToQueue('q', buf, { persistent: true });
|
|
60
|
+
expect(fake.sendToQueue).toHaveBeenCalledWith('q', buf, { persistent: true });
|
|
61
|
+
expect(result).toBe(true);
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
it('delegates publish', () => {
|
|
65
|
+
const buf = Buffer.from('test');
|
|
66
|
+
const result = channel.publish('ex', 'key', buf, { persistent: true });
|
|
67
|
+
expect(fake.publish).toHaveBeenCalledWith('ex', 'key', buf, { persistent: true });
|
|
68
|
+
expect(result).toBe(true);
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
it('delegates consume', async () => {
|
|
72
|
+
const handler = vi.fn();
|
|
73
|
+
const result = await channel.consume('q', handler, { noAck: false });
|
|
74
|
+
expect(fake.consume).toHaveBeenCalledWith('q', handler, { noAck: false });
|
|
75
|
+
expect(result.consumerTag).toBe('tag-1');
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
it('delegates ack', () => {
|
|
79
|
+
const msg = { fields: {}, properties: {}, content: Buffer.from('') } as never;
|
|
80
|
+
channel.ack(msg);
|
|
81
|
+
expect(fake.ack).toHaveBeenCalledWith(msg);
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
it('delegates nack with requeue', () => {
|
|
85
|
+
const msg = { fields: {}, properties: {}, content: Buffer.from('') } as never;
|
|
86
|
+
channel.nack(msg, true);
|
|
87
|
+
expect(fake.nack).toHaveBeenCalledWith(msg, false, true);
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
it('delegates nack without requeue', () => {
|
|
91
|
+
const msg = { fields: {}, properties: {}, content: Buffer.from('') } as never;
|
|
92
|
+
channel.nack(msg);
|
|
93
|
+
expect(fake.nack).toHaveBeenCalledWith(msg, false, undefined);
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
it('delegates prefetch', async () => {
|
|
97
|
+
await channel.prefetch(10);
|
|
98
|
+
expect(fake.prefetch).toHaveBeenCalledWith(10);
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
it('delegates close', async () => {
|
|
102
|
+
await channel.close();
|
|
103
|
+
expect(fake.close).toHaveBeenCalled();
|
|
104
|
+
});
|
|
105
|
+
});
|
package/src/channel.ts
ADDED
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Thin wrapper around an amqplib Channel.
|
|
3
|
+
*
|
|
4
|
+
* Exposes only the operations the SDK needs without leaking the full
|
|
5
|
+
* amqplib surface. No schema or validation logic lives here.
|
|
6
|
+
*
|
|
7
|
+
* Usage:
|
|
8
|
+
* const ch = await connection.createChannel();
|
|
9
|
+
* ch.sendToQueue("orders", buffer, { persistent: true });
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import type amqp from 'amqplib';
|
|
13
|
+
import type {
|
|
14
|
+
AssertQueueOptions,
|
|
15
|
+
AssertQueueReply,
|
|
16
|
+
AssertExchangeOptions,
|
|
17
|
+
AssertExchangeReply,
|
|
18
|
+
ConsumeMessage,
|
|
19
|
+
ConsumeOptions,
|
|
20
|
+
ConsumeReply,
|
|
21
|
+
EmptyReply,
|
|
22
|
+
PublishOptions,
|
|
23
|
+
} from './types.js';
|
|
24
|
+
|
|
25
|
+
export class AmqpChannel {
|
|
26
|
+
constructor(private readonly ch: amqp.Channel) {}
|
|
27
|
+
|
|
28
|
+
/** Exposes the raw amqplib channel for event listeners (e.g. "error"). */
|
|
29
|
+
get raw(): amqp.Channel {
|
|
30
|
+
return this.ch;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
async assertQueue(name: string, opts?: AssertQueueOptions): Promise<AssertQueueReply> {
|
|
34
|
+
return this.ch.assertQueue(name, opts);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
async assertExchange(
|
|
38
|
+
name: string,
|
|
39
|
+
type: string,
|
|
40
|
+
opts?: AssertExchangeOptions,
|
|
41
|
+
): Promise<AssertExchangeReply> {
|
|
42
|
+
return this.ch.assertExchange(name, type, opts);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
async bindQueue(queue: string, exchange: string, routingKey: string): Promise<EmptyReply> {
|
|
46
|
+
return this.ch.bindQueue(queue, exchange, routingKey);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
sendToQueue(queue: string, content: Buffer, opts?: PublishOptions): boolean {
|
|
50
|
+
return this.ch.sendToQueue(queue, content, opts);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
publish(exchange: string, routingKey: string, content: Buffer, opts?: PublishOptions): boolean {
|
|
54
|
+
return this.ch.publish(exchange, routingKey, content, opts);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
async consume(
|
|
58
|
+
queue: string,
|
|
59
|
+
handler: (msg: ConsumeMessage | null) => void,
|
|
60
|
+
opts?: ConsumeOptions,
|
|
61
|
+
): Promise<ConsumeReply> {
|
|
62
|
+
return this.ch.consume(queue, handler, opts);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
ack(msg: ConsumeMessage): void {
|
|
66
|
+
this.ch.ack(msg);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
nack(msg: ConsumeMessage, requeue?: boolean): void {
|
|
70
|
+
this.ch.nack(msg, false, requeue);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
async prefetch(count: number): Promise<void> {
|
|
74
|
+
await this.ch.prefetch(count);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
async close(): Promise<void> {
|
|
78
|
+
await this.ch.close();
|
|
79
|
+
}
|
|
80
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for AmqpConnection.
|
|
3
|
+
*
|
|
4
|
+
* Mocks the amqplib module to avoid real TCP connections.
|
|
5
|
+
* Covers connect, createChannel, and close.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
9
|
+
import { AmqpConnection } from './connection.js';
|
|
10
|
+
|
|
11
|
+
// Mock amqplib at the module level
|
|
12
|
+
vi.mock('amqplib', () => {
|
|
13
|
+
const fakeChannel = {
|
|
14
|
+
assertQueue: vi.fn().mockResolvedValue({ queue: 'q', messageCount: 0, consumerCount: 0 }),
|
|
15
|
+
close: vi.fn().mockResolvedValue(undefined),
|
|
16
|
+
};
|
|
17
|
+
const fakeConn = {
|
|
18
|
+
createChannel: vi.fn().mockResolvedValue(fakeChannel),
|
|
19
|
+
close: vi.fn().mockResolvedValue(undefined),
|
|
20
|
+
};
|
|
21
|
+
return {
|
|
22
|
+
default: {
|
|
23
|
+
connect: vi.fn().mockResolvedValue(fakeConn),
|
|
24
|
+
},
|
|
25
|
+
};
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
describe('AmqpConnection', () => {
|
|
29
|
+
beforeEach(() => {
|
|
30
|
+
vi.clearAllMocks();
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
it('connects to the given URL', async () => {
|
|
34
|
+
const conn = await AmqpConnection.connect('amqp://localhost');
|
|
35
|
+
expect(conn).toBeInstanceOf(AmqpConnection);
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
it('creates an AmqpChannel from the connection', async () => {
|
|
39
|
+
const conn = await AmqpConnection.connect('amqp://localhost');
|
|
40
|
+
const ch = await conn.createChannel();
|
|
41
|
+
// AmqpChannel wraps the fake channel
|
|
42
|
+
expect(ch).toBeDefined();
|
|
43
|
+
expect(typeof ch.assertQueue).toBe('function');
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
it('closes the underlying connection', async () => {
|
|
47
|
+
const amqplib = await import('amqplib');
|
|
48
|
+
const conn = await AmqpConnection.connect('amqp://localhost');
|
|
49
|
+
await conn.close();
|
|
50
|
+
// Verify the mock connection's close was called
|
|
51
|
+
const mockConn = await amqplib.default.connect('amqp://localhost');
|
|
52
|
+
expect(mockConn.close).toHaveBeenCalled();
|
|
53
|
+
});
|
|
54
|
+
});
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Manages the AMQP connection lifecycle.
|
|
3
|
+
*
|
|
4
|
+
* Wraps amqplib's connect() and exposes a single createChannel() method.
|
|
5
|
+
* No schema, validation, or serialization logic lives here.
|
|
6
|
+
*
|
|
7
|
+
* Usage:
|
|
8
|
+
* const conn = await AmqpConnection.connect("amqp://localhost");
|
|
9
|
+
* const ch = await conn.createChannel();
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import amqp from 'amqplib';
|
|
13
|
+
import { AmqpChannel } from './channel.js';
|
|
14
|
+
|
|
15
|
+
export class AmqpConnection {
|
|
16
|
+
private constructor(private readonly conn: amqp.ChannelModel) {}
|
|
17
|
+
|
|
18
|
+
/** Opens a new AMQP connection to the given URL. */
|
|
19
|
+
static async connect(url: string): Promise<AmqpConnection> {
|
|
20
|
+
const conn = await amqp.connect(url);
|
|
21
|
+
return new AmqpConnection(conn);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/** Creates a new channel on this connection. */
|
|
25
|
+
async createChannel(): Promise<AmqpChannel> {
|
|
26
|
+
const ch = await this.conn.createChannel();
|
|
27
|
+
return new AmqpChannel(ch);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/** Closes the underlying TCP connection. */
|
|
31
|
+
async close(): Promise<void> {
|
|
32
|
+
await this.conn.close();
|
|
33
|
+
}
|
|
34
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export { AmqpConnection } from './connection.js';
|
|
2
|
+
export { AmqpChannel } from './channel.js';
|
|
3
|
+
export type {
|
|
4
|
+
ConsumeMessage,
|
|
5
|
+
PublishOptions,
|
|
6
|
+
ConsumeOptions,
|
|
7
|
+
AssertQueueOptions,
|
|
8
|
+
AssertExchangeOptions,
|
|
9
|
+
AssertQueueReply,
|
|
10
|
+
AssertExchangeReply,
|
|
11
|
+
ConsumeReply,
|
|
12
|
+
EmptyReply,
|
|
13
|
+
} from './types.js';
|
package/src/types.ts
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Re-exported amqplib types used across the SDK.
|
|
3
|
+
*
|
|
4
|
+
* Centralizes the amqplib dependency so other packages never
|
|
5
|
+
* import from amqplib directly.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import type amqp from 'amqplib';
|
|
9
|
+
|
|
10
|
+
export type ConsumeMessage = amqp.ConsumeMessage;
|
|
11
|
+
export type PublishOptions = amqp.Options.Publish;
|
|
12
|
+
export type ConsumeOptions = amqp.Options.Consume;
|
|
13
|
+
export type AssertQueueOptions = amqp.Options.AssertQueue;
|
|
14
|
+
export type AssertExchangeOptions = amqp.Options.AssertExchange;
|
|
15
|
+
export type AssertQueueReply = amqp.Replies.AssertQueue;
|
|
16
|
+
export type AssertExchangeReply = amqp.Replies.AssertExchange;
|
|
17
|
+
export type ConsumeReply = amqp.Replies.Consume;
|
|
18
|
+
export type EmptyReply = amqp.Replies.Empty;
|
package/tsconfig.json
ADDED