@motiadev/adapter-rabbitmq-events 0.13.2-beta.164-732151 → 0.13.2-beta.164-110989

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.
@@ -0,0 +1,3 @@
1
+ import { RabbitMQEventAdapterConfig, RabbitMQSubscribeOptions } from "./types.mjs";
2
+ import { RabbitMQEventAdapter } from "./rabbitmq-event-adapter.mjs";
3
+ export { RabbitMQEventAdapter, type RabbitMQEventAdapterConfig, type RabbitMQSubscribeOptions };
package/dist/index.mjs ADDED
@@ -0,0 +1,3 @@
1
+ import { RabbitMQEventAdapter } from "./rabbitmq-event-adapter.mjs";
2
+
3
+ export { RabbitMQEventAdapter };
@@ -0,0 +1,25 @@
1
+ import { RabbitMQEventAdapterConfig } from "./types.mjs";
2
+ import { Event, EventAdapter, QueueConfig, SubscriptionHandle } from "@motiadev/core";
3
+
4
+ //#region src/rabbitmq-event-adapter.d.ts
5
+ declare class RabbitMQEventAdapter implements EventAdapter {
6
+ private connection;
7
+ private channel;
8
+ private config;
9
+ private subscriptions;
10
+ private reconnecting;
11
+ private shutdownRequested;
12
+ constructor(config: RabbitMQEventAdapterConfig);
13
+ private ensureConnection;
14
+ private connect;
15
+ private handleConnectionError;
16
+ emit<TData>(event: Event<TData>): Promise<void>;
17
+ subscribe<TData>(topic: string, stepName: string, handler: (event: Event<TData>) => void | Promise<void>, options?: QueueConfig): Promise<SubscriptionHandle>;
18
+ unsubscribe(handle: SubscriptionHandle): Promise<void>;
19
+ shutdown(): Promise<void>;
20
+ getSubscriptionCount(topic: string): Promise<number>;
21
+ listTopics(): Promise<string[]>;
22
+ }
23
+ //#endregion
24
+ export { RabbitMQEventAdapter };
25
+ //# sourceMappingURL=rabbitmq-event-adapter.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rabbitmq-event-adapter.d.mts","names":[],"sources":["../src/rabbitmq-event-adapter.ts"],"sourcesContent":[],"mappings":";;;;cAKa,oBAAA,YAAgC;;EAAhC,QAAA,OAAA;EAQS,QAAA,MAAA;EA0FW,QAAA,aAAA;EAAN,QAAA,YAAA;EAAe,QAAA,iBAAA;EA4Bf,WAAA,CAAA,MAAA,EAtHL,0BAsHK;EAAN,QAAA,gBAAA;EAAwB,QAAA,OAAA;EAC/B,QAAA,qBAAA;EACD,IAAA,CAAA,KAAA,CAAA,CAAA,KAAA,EA9Bc,KA8Bd,CA9BoB,KA8BpB,CAAA,CAAA,EA9B6B,OA8B7B,CAAA,IAAA,CAAA;EAAR,SAAA,CAAA,KAAA,CAAA,CAAA,KAAA,EAAA,MAAA,EAAA,QAAA,EAAA,MAAA,EAAA,OAAA,EAAA,CAAA,KAAA,EAFgB,KAEhB,CAFsB,KAEtB,CAAA,EAAA,GAAA,IAAA,GAFwC,OAExC,CAAA,IAAA,CAAA,EAAA,OAAA,CAAA,EADS,WACT,CAAA,EAAA,OAAA,CAAQ,kBAAR,CAAA;EAuGuB,WAAA,CAAA,MAAA,EAAA,kBAAA,CAAA,EAAqB,OAArB,CAAA,IAAA,CAAA;EAAqB,QAAA,CAAA,CAAA,EAM7B,OAN6B,CAAA,IAAA,CAAA;EAM7B,oBAAA,CAAA,KAAA,EAAA,MAAA,CAAA,EAyByB,OAzBzB,CAAA,MAAA,CAAA;EAyByB,UAAA,CAAA,CAAA,EAIvB,OAJuB,CAAA,MAAA,EAAA,CAAA"}
@@ -0,0 +1,194 @@
1
+ import amqp from "amqplib";
2
+
3
+ //#region src/rabbitmq-event-adapter.ts
4
+ var RabbitMQEventAdapter = class {
5
+ constructor(config) {
6
+ this.connection = null;
7
+ this.channel = null;
8
+ this.subscriptions = /* @__PURE__ */ new Map();
9
+ this.reconnecting = false;
10
+ this.shutdownRequested = false;
11
+ this.config = {
12
+ durable: true,
13
+ autoDelete: false,
14
+ connectionTimeout: 1e4,
15
+ reconnectDelay: 5e3,
16
+ prefetch: 1,
17
+ deadLetterExchange: `${config.exchangeName}.dlq`,
18
+ deadLetterRoutingKey: "dlq",
19
+ ...config
20
+ };
21
+ }
22
+ async ensureConnection() {
23
+ if (!this.connection || !this.channel) await this.connect();
24
+ if (!this.channel) throw new Error("Failed to establish channel");
25
+ return this.channel;
26
+ }
27
+ async connect() {
28
+ try {
29
+ this.connection = await amqp.connect(this.config.url, { timeout: this.config.connectionTimeout });
30
+ if (this.connection) {
31
+ this.connection.on("error", (err) => {
32
+ console.error("[RabbitMQ] Connection error:", err);
33
+ this.handleConnectionError();
34
+ });
35
+ this.connection.on("close", () => {
36
+ console.warn("[RabbitMQ] Connection closed");
37
+ this.handleConnectionError();
38
+ });
39
+ this.channel = await this.connection.createChannel();
40
+ if (this.channel) {
41
+ this.channel.on("error", (err) => {
42
+ console.error("[RabbitMQ] Channel error:", err);
43
+ });
44
+ this.channel.on("close", () => {
45
+ console.warn("[RabbitMQ] Channel closed");
46
+ });
47
+ await this.channel.assertExchange(this.config.exchangeName, this.config.exchangeType, {
48
+ durable: this.config.durable,
49
+ autoDelete: this.config.autoDelete
50
+ });
51
+ if (this.config.prefetch) await this.channel.prefetch(this.config.prefetch);
52
+ }
53
+ }
54
+ } catch (error) {
55
+ console.error("[RabbitMQ] Failed to connect:", error);
56
+ throw error;
57
+ }
58
+ }
59
+ async handleConnectionError() {
60
+ if (this.shutdownRequested || this.reconnecting) return;
61
+ this.reconnecting = true;
62
+ this.connection = null;
63
+ this.channel = null;
64
+ console.log(`[RabbitMQ] Attempting to reconnect in ${this.config.reconnectDelay}ms...`);
65
+ setTimeout(async () => {
66
+ try {
67
+ await this.connect();
68
+ this.reconnecting = false;
69
+ } catch (error) {
70
+ console.error("[RabbitMQ] Reconnection failed:", error);
71
+ this.reconnecting = false;
72
+ this.handleConnectionError();
73
+ }
74
+ }, this.config.reconnectDelay);
75
+ }
76
+ async emit(event) {
77
+ const channel = await this.ensureConnection();
78
+ const message = {
79
+ topic: event.topic,
80
+ data: event.data,
81
+ traceId: event.traceId,
82
+ flows: event.flows,
83
+ messageGroupId: event.messageGroupId,
84
+ timestamp: Date.now()
85
+ };
86
+ const content = Buffer.from(JSON.stringify(message));
87
+ if (!channel.publish(this.config.exchangeName, event.topic, content, {
88
+ persistent: this.config.durable,
89
+ contentType: "application/json",
90
+ timestamp: Date.now()
91
+ })) throw new Error(`Failed to publish message to RabbitMQ for topic: ${event.topic}`);
92
+ }
93
+ async subscribe(topic, stepName, handler, options) {
94
+ const channel = await this.ensureConnection();
95
+ const queueName = `motia.${topic}.${stepName}`;
96
+ const subscribeOptions = {
97
+ durable: this.config.durable,
98
+ exclusive: false,
99
+ prefetch: this.config.prefetch
100
+ };
101
+ const queueArgs = {};
102
+ if (options?.visibilityTimeout && options.visibilityTimeout > 0) queueArgs["x-consumer-timeout"] = options.visibilityTimeout * 1e3;
103
+ if (options?.type === "fifo") queueArgs["x-single-active-consumer"] = true;
104
+ const dlqExchange = this.config.deadLetterExchange;
105
+ const dlqQueueName = `${queueName}.dlq`;
106
+ const dlqRoutingKey = `${queueName}.${this.config.deadLetterRoutingKey}`;
107
+ await channel.assertExchange(dlqExchange, "direct", {
108
+ durable: this.config.durable,
109
+ autoDelete: this.config.autoDelete
110
+ });
111
+ await channel.assertQueue(dlqQueueName, {
112
+ durable: this.config.durable,
113
+ exclusive: false,
114
+ autoDelete: !this.config.durable
115
+ });
116
+ await channel.bindQueue(dlqQueueName, dlqExchange, dlqRoutingKey);
117
+ queueArgs["x-dead-letter-exchange"] = dlqExchange;
118
+ queueArgs["x-dead-letter-routing-key"] = dlqRoutingKey;
119
+ const queue = await channel.assertQueue(queueName, {
120
+ durable: subscribeOptions.durable ?? true,
121
+ exclusive: subscribeOptions.exclusive ?? false,
122
+ autoDelete: !subscribeOptions.durable,
123
+ arguments: Object.keys(queueArgs).length > 0 ? queueArgs : void 0
124
+ });
125
+ await channel.bindQueue(queue.queue, this.config.exchangeName, topic);
126
+ if (subscribeOptions.prefetch) await channel.prefetch(subscribeOptions.prefetch);
127
+ const handle = {
128
+ topic,
129
+ id: (await channel.consume(queue.queue, async (msg) => {
130
+ if (!msg) return;
131
+ const retryCount = msg.properties.headers?.["x-retry-count"] || 0;
132
+ if (options?.maxRetries && retryCount >= options.maxRetries) {
133
+ console.warn(`[RabbitMQ] Message exceeded max retries (${options.maxRetries}), sending to DLQ`);
134
+ channel.nack(msg, false, false);
135
+ return;
136
+ }
137
+ try {
138
+ await handler(JSON.parse(msg.content.toString()));
139
+ channel.ack(msg);
140
+ } catch (error) {
141
+ console.error("[RabbitMQ] Error processing message:", error);
142
+ if (options?.maxRetries && retryCount < options.maxRetries) {
143
+ const headers = msg.properties.headers || {};
144
+ headers["x-retry-count"] = retryCount + 1;
145
+ const republishOptions = {
146
+ ...msg.properties,
147
+ headers,
148
+ persistent: this.config.durable
149
+ };
150
+ channel.sendToQueue(queue.queue, msg.content, republishOptions);
151
+ channel.ack(msg);
152
+ return;
153
+ }
154
+ console.warn("[RabbitMQ] Sending failed message to DLQ");
155
+ channel.nack(msg, false, false);
156
+ }
157
+ })).consumerTag,
158
+ unsubscribe: async () => {
159
+ await this.unsubscribe(handle);
160
+ }
161
+ };
162
+ this.subscriptions.set(handle.id, handle);
163
+ return handle;
164
+ }
165
+ async unsubscribe(handle) {
166
+ await (await this.ensureConnection()).cancel(handle.id);
167
+ this.subscriptions.delete(handle.id);
168
+ }
169
+ async shutdown() {
170
+ this.shutdownRequested = true;
171
+ if (this.channel) try {
172
+ await this.channel.close();
173
+ } catch (error) {
174
+ console.error("[RabbitMQ] Error closing channel:", error);
175
+ }
176
+ if (this.connection) try {
177
+ const conn = this.connection;
178
+ if (typeof conn.close === "function") await conn.close();
179
+ } catch (error) {
180
+ console.error("[RabbitMQ] Error closing connection:", error);
181
+ }
182
+ this.subscriptions.clear();
183
+ }
184
+ async getSubscriptionCount(topic) {
185
+ return Array.from(this.subscriptions.values()).filter((sub) => sub.topic === topic).length;
186
+ }
187
+ async listTopics() {
188
+ return Array.from(new Set(Array.from(this.subscriptions.values()).map((sub) => sub.topic)));
189
+ }
190
+ };
191
+
192
+ //#endregion
193
+ export { RabbitMQEventAdapter };
194
+ //# sourceMappingURL=rabbitmq-event-adapter.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rabbitmq-event-adapter.mjs","names":["subscribeOptions: RabbitMQSubscribeOptions","queueArgs: any","handle: SubscriptionHandle"],"sources":["../src/rabbitmq-event-adapter.ts"],"sourcesContent":["import type { Event, EventAdapter, QueueConfig, SubscriptionHandle } from '@motiadev/core'\nimport type { Channel, ChannelModel, ConsumeMessage } from 'amqplib'\nimport amqp from 'amqplib'\nimport type { RabbitMQEventAdapterConfig, RabbitMQSubscribeOptions } from './types'\n\nexport class RabbitMQEventAdapter implements EventAdapter {\n private connection: ChannelModel | null = null\n private channel: Channel | null = null\n private config: Required<RabbitMQEventAdapterConfig>\n private subscriptions: Map<string, SubscriptionHandle> = new Map()\n private reconnecting = false\n private shutdownRequested = false\n\n constructor(config: RabbitMQEventAdapterConfig) {\n this.config = {\n durable: true,\n autoDelete: false,\n connectionTimeout: 10000,\n reconnectDelay: 5000,\n prefetch: 1,\n deadLetterExchange: `${config.exchangeName}.dlq`,\n deadLetterRoutingKey: 'dlq',\n ...config,\n }\n }\n\n private async ensureConnection(): Promise<any> {\n if (!this.connection || !this.channel) {\n await this.connect()\n }\n if (!this.channel) {\n throw new Error('Failed to establish channel')\n }\n return this.channel\n }\n\n private async connect(): Promise<void> {\n try {\n this.connection = await amqp.connect(this.config.url, {\n timeout: this.config.connectionTimeout,\n })\n\n if (this.connection) {\n this.connection.on('error', (err: any) => {\n console.error('[RabbitMQ] Connection error:', err)\n this.handleConnectionError()\n })\n\n this.connection.on('close', () => {\n console.warn('[RabbitMQ] Connection closed')\n this.handleConnectionError()\n })\n\n this.channel = await this.connection.createChannel()\n\n if (this.channel) {\n this.channel.on('error', (err: any) => {\n console.error('[RabbitMQ] Channel error:', err)\n })\n\n this.channel.on('close', () => {\n console.warn('[RabbitMQ] Channel closed')\n })\n\n await this.channel.assertExchange(this.config.exchangeName, this.config.exchangeType, {\n durable: this.config.durable,\n autoDelete: this.config.autoDelete,\n })\n\n if (this.config.prefetch) {\n await this.channel.prefetch(this.config.prefetch)\n }\n }\n }\n } catch (error) {\n console.error('[RabbitMQ] Failed to connect:', error)\n throw error\n }\n }\n\n private async handleConnectionError(): Promise<void> {\n if (this.shutdownRequested || this.reconnecting) {\n return\n }\n\n this.reconnecting = true\n this.connection = null\n this.channel = null\n\n console.log(`[RabbitMQ] Attempting to reconnect in ${this.config.reconnectDelay}ms...`)\n\n setTimeout(async () => {\n try {\n await this.connect()\n this.reconnecting = false\n } catch (error) {\n console.error('[RabbitMQ] Reconnection failed:', error)\n this.reconnecting = false\n this.handleConnectionError()\n }\n }, this.config.reconnectDelay)\n }\n\n async emit<TData>(event: Event<TData>): Promise<void> {\n const channel = await this.ensureConnection()\n\n const message = {\n topic: event.topic,\n data: event.data,\n traceId: event.traceId,\n flows: event.flows,\n messageGroupId: event.messageGroupId,\n timestamp: Date.now(),\n }\n\n const content = Buffer.from(JSON.stringify(message))\n\n const published = channel.publish(this.config.exchangeName, event.topic, content, {\n persistent: this.config.durable,\n contentType: 'application/json',\n timestamp: Date.now(),\n })\n\n if (!published) {\n throw new Error(`Failed to publish message to RabbitMQ for topic: ${event.topic}`)\n }\n }\n\n async subscribe<TData>(\n topic: string,\n stepName: string,\n handler: (event: Event<TData>) => void | Promise<void>,\n options?: QueueConfig,\n ): Promise<SubscriptionHandle> {\n const channel = await this.ensureConnection()\n const queueName = `motia.${topic}.${stepName}`\n\n const subscribeOptions: RabbitMQSubscribeOptions = {\n durable: this.config.durable,\n exclusive: false,\n prefetch: this.config.prefetch,\n }\n\n const queueArgs: any = {}\n\n if (options?.visibilityTimeout && options.visibilityTimeout > 0) {\n queueArgs['x-consumer-timeout'] = options.visibilityTimeout * 1000\n }\n\n if (options?.type === 'fifo') {\n queueArgs['x-single-active-consumer'] = true\n }\n\n const dlqExchange = this.config.deadLetterExchange\n const dlqQueueName = `${queueName}.dlq`\n const dlqRoutingKey = `${queueName}.${this.config.deadLetterRoutingKey}`\n\n await channel.assertExchange(dlqExchange, 'direct', {\n durable: this.config.durable,\n autoDelete: this.config.autoDelete,\n })\n\n await channel.assertQueue(dlqQueueName, {\n durable: this.config.durable,\n exclusive: false,\n autoDelete: !this.config.durable,\n })\n\n await channel.bindQueue(dlqQueueName, dlqExchange, dlqRoutingKey)\n\n queueArgs['x-dead-letter-exchange'] = dlqExchange\n queueArgs['x-dead-letter-routing-key'] = dlqRoutingKey\n\n const queue = await channel.assertQueue(queueName, {\n durable: subscribeOptions.durable ?? true,\n exclusive: subscribeOptions.exclusive ?? false,\n autoDelete: !subscribeOptions.durable,\n arguments: Object.keys(queueArgs).length > 0 ? queueArgs : undefined,\n })\n\n await channel.bindQueue(queue.queue, this.config.exchangeName, topic)\n\n if (subscribeOptions.prefetch) {\n await channel.prefetch(subscribeOptions.prefetch)\n }\n\n const consumerTag = await channel.consume(queue.queue, async (msg: ConsumeMessage | null) => {\n if (!msg) return\n\n const retryCount = (msg.properties.headers?.['x-retry-count'] as number) || 0\n\n if (options?.maxRetries && retryCount >= options.maxRetries) {\n console.warn(`[RabbitMQ] Message exceeded max retries (${options.maxRetries}), sending to DLQ`)\n channel.nack(msg, false, false)\n return\n }\n\n try {\n const content = JSON.parse(msg.content.toString())\n await handler(content as Event<TData>)\n channel.ack(msg)\n } catch (error) {\n console.error('[RabbitMQ] Error processing message:', error)\n\n if (options?.maxRetries && retryCount < options.maxRetries) {\n const headers = msg.properties.headers || {}\n headers['x-retry-count'] = retryCount + 1\n\n const republishOptions = {\n ...msg.properties,\n headers,\n persistent: this.config.durable,\n }\n\n channel.sendToQueue(queue.queue, msg.content, republishOptions)\n channel.ack(msg)\n return\n }\n\n console.warn('[RabbitMQ] Sending failed message to DLQ')\n channel.nack(msg, false, false)\n }\n })\n\n const handle: SubscriptionHandle = {\n topic,\n id: consumerTag.consumerTag,\n unsubscribe: async () => {\n await this.unsubscribe(handle)\n },\n }\n\n this.subscriptions.set(handle.id, handle)\n return handle\n }\n\n async unsubscribe(handle: SubscriptionHandle): Promise<void> {\n const channel = await this.ensureConnection()\n await channel.cancel(handle.id)\n this.subscriptions.delete(handle.id)\n }\n\n async shutdown(): Promise<void> {\n this.shutdownRequested = true\n\n if (this.channel) {\n try {\n await this.channel.close()\n } catch (error) {\n console.error('[RabbitMQ] Error closing channel:', error)\n }\n }\n\n if (this.connection) {\n try {\n const conn = this.connection as any\n if (typeof conn.close === 'function') {\n await conn.close()\n }\n } catch (error) {\n console.error('[RabbitMQ] Error closing connection:', error)\n }\n }\n\n this.subscriptions.clear()\n }\n\n async getSubscriptionCount(topic: string): Promise<number> {\n return Array.from(this.subscriptions.values()).filter((sub) => sub.topic === topic).length\n }\n\n async listTopics(): Promise<string[]> {\n return Array.from(new Set(Array.from(this.subscriptions.values()).map((sub) => sub.topic)))\n }\n}\n"],"mappings":"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"}
@@ -0,0 +1,22 @@
1
+ //#region src/types.d.ts
2
+ interface RabbitMQEventAdapterConfig {
3
+ url: string;
4
+ exchangeType: 'direct' | 'topic' | 'fanout' | 'headers';
5
+ exchangeName: string;
6
+ durable?: boolean;
7
+ autoDelete?: boolean;
8
+ connectionTimeout?: number;
9
+ reconnectDelay?: number;
10
+ prefetch?: number;
11
+ deadLetterExchange?: string;
12
+ deadLetterRoutingKey?: string;
13
+ }
14
+ interface RabbitMQSubscribeOptions {
15
+ queue?: string;
16
+ exclusive?: boolean;
17
+ durable?: boolean;
18
+ prefetch?: number;
19
+ }
20
+ //#endregion
21
+ export { RabbitMQEventAdapterConfig, RabbitMQSubscribeOptions };
22
+ //# sourceMappingURL=types.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.mts","names":[],"sources":["../src/types.ts"],"sourcesContent":[],"mappings":";UAAiB,0BAAA;EAAA,GAAA,EAAA,MAAA;EAaA,YAAA,EAAA,QAAA,GAAA,OAAwB,GAAA,QAAA,GAAA,SAAA;;;;;;;;;;UAAxB,wBAAA"}
package/package.json CHANGED
@@ -1,24 +1,28 @@
1
1
  {
2
2
  "name": "@motiadev/adapter-rabbitmq-events",
3
3
  "description": "RabbitMQ event adapter for Motia framework, enabling distributed event handling across multiple instances.",
4
- "main": "dist/index.js",
5
- "types": "dist/index.d.ts",
6
- "version": "0.13.2-beta.164-732151",
4
+ "type": "module",
5
+ "main": "dist/index.mjs",
6
+ "module": "dist/index.mjs",
7
+ "types": "dist/index.d.mts",
8
+ "version": "0.13.2-beta.164-110989",
7
9
  "dependencies": {
8
10
  "amqplib": "^0.10.4",
9
11
  "uuid": "^11.1.0",
10
- "@motiadev/core": "0.13.2-beta.164-732151"
12
+ "@motiadev/core": "0.13.2-beta.164-110989"
11
13
  },
12
14
  "devDependencies": {
13
15
  "@types/amqplib": "^0.10.5",
16
+ "tsdown": "^0.16.6",
14
17
  "typescript": "^5.7.2"
15
18
  },
16
19
  "peerDependencies": {
17
20
  "@motiadev/core": "^0.8.0"
18
21
  },
19
22
  "scripts": {
20
- "build": "rm -rf dist && tsc",
23
+ "build": "tsdown",
24
+ "dev": "tsdown --watch",
21
25
  "lint": "biome check .",
22
- "watch": "tsc --watch"
26
+ "clean": "rm -rf dist"
23
27
  }
24
28
  }
@@ -0,0 +1,17 @@
1
+ import { defineConfig } from 'tsdown'
2
+
3
+ export default defineConfig({
4
+ entry: {
5
+ index: './src/index.ts',
6
+ },
7
+ format: 'esm',
8
+ platform: 'node',
9
+ external: ['@motiadev/core', 'amqplib', 'uuid'],
10
+ dts: {
11
+ build: true,
12
+ },
13
+ clean: true,
14
+ outDir: 'dist',
15
+ sourcemap: true,
16
+ unbundle: true,
17
+ })
package/dist/index.d.ts DELETED
@@ -1,3 +0,0 @@
1
- export { RabbitMQEventAdapter } from './rabbitmq-event-adapter';
2
- export type { RabbitMQEventAdapterConfig, RabbitMQSubscribeOptions } from './types';
3
- //# sourceMappingURL=index.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,oBAAoB,EAAE,MAAM,0BAA0B,CAAA;AAC/D,YAAY,EAAE,0BAA0B,EAAE,wBAAwB,EAAE,MAAM,SAAS,CAAA"}
package/dist/index.js DELETED
@@ -1,5 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.RabbitMQEventAdapter = void 0;
4
- var rabbitmq_event_adapter_1 = require("./rabbitmq-event-adapter");
5
- Object.defineProperty(exports, "RabbitMQEventAdapter", { enumerable: true, get: function () { return rabbitmq_event_adapter_1.RabbitMQEventAdapter; } });
@@ -1,21 +0,0 @@
1
- import type { Event, EventAdapter, QueueConfig, SubscriptionHandle } from '@motiadev/core';
2
- import type { RabbitMQEventAdapterConfig } from './types';
3
- export declare class RabbitMQEventAdapter implements EventAdapter {
4
- private connection;
5
- private channel;
6
- private config;
7
- private subscriptions;
8
- private reconnecting;
9
- private shutdownRequested;
10
- constructor(config: RabbitMQEventAdapterConfig);
11
- private ensureConnection;
12
- private connect;
13
- private handleConnectionError;
14
- emit<TData>(event: Event<TData>): Promise<void>;
15
- subscribe<TData>(topic: string, stepName: string, handler: (event: Event<TData>) => void | Promise<void>, options?: QueueConfig): Promise<SubscriptionHandle>;
16
- unsubscribe(handle: SubscriptionHandle): Promise<void>;
17
- shutdown(): Promise<void>;
18
- getSubscriptionCount(topic: string): Promise<number>;
19
- listTopics(): Promise<string[]>;
20
- }
21
- //# sourceMappingURL=rabbitmq-event-adapter.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"rabbitmq-event-adapter.d.ts","sourceRoot":"","sources":["../src/rabbitmq-event-adapter.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,YAAY,EAAE,WAAW,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAA;AAG1F,OAAO,KAAK,EAAE,0BAA0B,EAA4B,MAAM,SAAS,CAAA;AAEnF,qBAAa,oBAAqB,YAAW,YAAY;IACvD,OAAO,CAAC,UAAU,CAA4B;IAC9C,OAAO,CAAC,OAAO,CAAuB;IACtC,OAAO,CAAC,MAAM,CAAsC;IACpD,OAAO,CAAC,aAAa,CAA6C;IAClE,OAAO,CAAC,YAAY,CAAQ;IAC5B,OAAO,CAAC,iBAAiB,CAAQ;gBAErB,MAAM,EAAE,0BAA0B;YAahC,gBAAgB;YAUhB,OAAO;YA4CP,qBAAqB;IAuB7B,IAAI,CAAC,KAAK,EAAE,KAAK,EAAE,KAAK,CAAC,KAAK,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;IAyB/C,SAAS,CAAC,KAAK,EACnB,KAAK,EAAE,MAAM,EACb,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,CAAC,KAAK,EAAE,KAAK,CAAC,KAAK,CAAC,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,EACtD,OAAO,CAAC,EAAE,WAAW,GACpB,OAAO,CAAC,kBAAkB,CAAC;IAuGxB,WAAW,CAAC,MAAM,EAAE,kBAAkB,GAAG,OAAO,CAAC,IAAI,CAAC;IAMtD,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC;IAyBzB,oBAAoB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAIpD,UAAU,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC;CAGtC"}
@@ -1,229 +0,0 @@
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.RabbitMQEventAdapter = void 0;
7
- const amqplib_1 = __importDefault(require("amqplib"));
8
- class RabbitMQEventAdapter {
9
- constructor(config) {
10
- this.connection = null;
11
- this.channel = null;
12
- this.subscriptions = new Map();
13
- this.reconnecting = false;
14
- this.shutdownRequested = false;
15
- this.config = {
16
- durable: true,
17
- autoDelete: false,
18
- connectionTimeout: 10000,
19
- reconnectDelay: 5000,
20
- prefetch: 1,
21
- deadLetterExchange: `${config.exchangeName}.dlq`,
22
- deadLetterRoutingKey: 'dlq',
23
- ...config,
24
- };
25
- }
26
- async ensureConnection() {
27
- if (!this.connection || !this.channel) {
28
- await this.connect();
29
- }
30
- if (!this.channel) {
31
- throw new Error('Failed to establish channel');
32
- }
33
- return this.channel;
34
- }
35
- async connect() {
36
- try {
37
- this.connection = await amqplib_1.default.connect(this.config.url, {
38
- timeout: this.config.connectionTimeout,
39
- });
40
- if (this.connection) {
41
- this.connection.on('error', (err) => {
42
- console.error('[RabbitMQ] Connection error:', err);
43
- this.handleConnectionError();
44
- });
45
- this.connection.on('close', () => {
46
- console.warn('[RabbitMQ] Connection closed');
47
- this.handleConnectionError();
48
- });
49
- this.channel = await this.connection.createChannel();
50
- if (this.channel) {
51
- this.channel.on('error', (err) => {
52
- console.error('[RabbitMQ] Channel error:', err);
53
- });
54
- this.channel.on('close', () => {
55
- console.warn('[RabbitMQ] Channel closed');
56
- });
57
- await this.channel.assertExchange(this.config.exchangeName, this.config.exchangeType, {
58
- durable: this.config.durable,
59
- autoDelete: this.config.autoDelete,
60
- });
61
- if (this.config.prefetch) {
62
- await this.channel.prefetch(this.config.prefetch);
63
- }
64
- }
65
- }
66
- }
67
- catch (error) {
68
- console.error('[RabbitMQ] Failed to connect:', error);
69
- throw error;
70
- }
71
- }
72
- async handleConnectionError() {
73
- if (this.shutdownRequested || this.reconnecting) {
74
- return;
75
- }
76
- this.reconnecting = true;
77
- this.connection = null;
78
- this.channel = null;
79
- console.log(`[RabbitMQ] Attempting to reconnect in ${this.config.reconnectDelay}ms...`);
80
- setTimeout(async () => {
81
- try {
82
- await this.connect();
83
- this.reconnecting = false;
84
- }
85
- catch (error) {
86
- console.error('[RabbitMQ] Reconnection failed:', error);
87
- this.reconnecting = false;
88
- this.handleConnectionError();
89
- }
90
- }, this.config.reconnectDelay);
91
- }
92
- async emit(event) {
93
- const channel = await this.ensureConnection();
94
- const message = {
95
- topic: event.topic,
96
- data: event.data,
97
- traceId: event.traceId,
98
- flows: event.flows,
99
- messageGroupId: event.messageGroupId,
100
- timestamp: Date.now(),
101
- };
102
- const content = Buffer.from(JSON.stringify(message));
103
- const published = channel.publish(this.config.exchangeName, event.topic, content, {
104
- persistent: this.config.durable,
105
- contentType: 'application/json',
106
- timestamp: Date.now(),
107
- });
108
- if (!published) {
109
- throw new Error(`Failed to publish message to RabbitMQ for topic: ${event.topic}`);
110
- }
111
- }
112
- async subscribe(topic, stepName, handler, options) {
113
- const channel = await this.ensureConnection();
114
- const queueName = `motia.${topic}.${stepName}`;
115
- const subscribeOptions = {
116
- durable: this.config.durable,
117
- exclusive: false,
118
- prefetch: this.config.prefetch,
119
- };
120
- const queueArgs = {};
121
- if (options?.visibilityTimeout && options.visibilityTimeout > 0) {
122
- queueArgs['x-consumer-timeout'] = options.visibilityTimeout * 1000;
123
- }
124
- if (options?.type === 'fifo') {
125
- queueArgs['x-single-active-consumer'] = true;
126
- }
127
- const dlqExchange = this.config.deadLetterExchange;
128
- const dlqQueueName = `${queueName}.dlq`;
129
- const dlqRoutingKey = `${queueName}.${this.config.deadLetterRoutingKey}`;
130
- await channel.assertExchange(dlqExchange, 'direct', {
131
- durable: this.config.durable,
132
- autoDelete: this.config.autoDelete,
133
- });
134
- await channel.assertQueue(dlqQueueName, {
135
- durable: this.config.durable,
136
- exclusive: false,
137
- autoDelete: !this.config.durable,
138
- });
139
- await channel.bindQueue(dlqQueueName, dlqExchange, dlqRoutingKey);
140
- queueArgs['x-dead-letter-exchange'] = dlqExchange;
141
- queueArgs['x-dead-letter-routing-key'] = dlqRoutingKey;
142
- const queue = await channel.assertQueue(queueName, {
143
- durable: subscribeOptions.durable ?? true,
144
- exclusive: subscribeOptions.exclusive ?? false,
145
- autoDelete: !subscribeOptions.durable,
146
- arguments: Object.keys(queueArgs).length > 0 ? queueArgs : undefined,
147
- });
148
- await channel.bindQueue(queue.queue, this.config.exchangeName, topic);
149
- if (subscribeOptions.prefetch) {
150
- await channel.prefetch(subscribeOptions.prefetch);
151
- }
152
- const consumerTag = await channel.consume(queue.queue, async (msg) => {
153
- if (!msg)
154
- return;
155
- const retryCount = msg.properties.headers?.['x-retry-count'] || 0;
156
- if (options?.maxRetries && retryCount >= options.maxRetries) {
157
- console.warn(`[RabbitMQ] Message exceeded max retries (${options.maxRetries}), sending to DLQ`);
158
- channel.nack(msg, false, false);
159
- return;
160
- }
161
- try {
162
- const content = JSON.parse(msg.content.toString());
163
- await handler(content);
164
- channel.ack(msg);
165
- }
166
- catch (error) {
167
- console.error('[RabbitMQ] Error processing message:', error);
168
- if (options?.maxRetries && retryCount < options.maxRetries) {
169
- const headers = msg.properties.headers || {};
170
- headers['x-retry-count'] = retryCount + 1;
171
- const republishOptions = {
172
- ...msg.properties,
173
- headers,
174
- persistent: this.config.durable,
175
- };
176
- channel.sendToQueue(queue.queue, msg.content, republishOptions);
177
- channel.ack(msg);
178
- return;
179
- }
180
- console.warn('[RabbitMQ] Sending failed message to DLQ');
181
- channel.nack(msg, false, false);
182
- }
183
- });
184
- const handle = {
185
- topic,
186
- id: consumerTag.consumerTag,
187
- unsubscribe: async () => {
188
- await this.unsubscribe(handle);
189
- },
190
- };
191
- this.subscriptions.set(handle.id, handle);
192
- return handle;
193
- }
194
- async unsubscribe(handle) {
195
- const channel = await this.ensureConnection();
196
- await channel.cancel(handle.id);
197
- this.subscriptions.delete(handle.id);
198
- }
199
- async shutdown() {
200
- this.shutdownRequested = true;
201
- if (this.channel) {
202
- try {
203
- await this.channel.close();
204
- }
205
- catch (error) {
206
- console.error('[RabbitMQ] Error closing channel:', error);
207
- }
208
- }
209
- if (this.connection) {
210
- try {
211
- const conn = this.connection;
212
- if (typeof conn.close === 'function') {
213
- await conn.close();
214
- }
215
- }
216
- catch (error) {
217
- console.error('[RabbitMQ] Error closing connection:', error);
218
- }
219
- }
220
- this.subscriptions.clear();
221
- }
222
- async getSubscriptionCount(topic) {
223
- return Array.from(this.subscriptions.values()).filter((sub) => sub.topic === topic).length;
224
- }
225
- async listTopics() {
226
- return Array.from(new Set(Array.from(this.subscriptions.values()).map((sub) => sub.topic)));
227
- }
228
- }
229
- exports.RabbitMQEventAdapter = RabbitMQEventAdapter;
package/dist/types.d.ts DELETED
@@ -1,19 +0,0 @@
1
- export interface RabbitMQEventAdapterConfig {
2
- url: string;
3
- exchangeType: 'direct' | 'topic' | 'fanout' | 'headers';
4
- exchangeName: string;
5
- durable?: boolean;
6
- autoDelete?: boolean;
7
- connectionTimeout?: number;
8
- reconnectDelay?: number;
9
- prefetch?: number;
10
- deadLetterExchange?: string;
11
- deadLetterRoutingKey?: string;
12
- }
13
- export interface RabbitMQSubscribeOptions {
14
- queue?: string;
15
- exclusive?: boolean;
16
- durable?: boolean;
17
- prefetch?: number;
18
- }
19
- //# sourceMappingURL=types.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,0BAA0B;IACzC,GAAG,EAAE,MAAM,CAAA;IACX,YAAY,EAAE,QAAQ,GAAG,OAAO,GAAG,QAAQ,GAAG,SAAS,CAAA;IACvD,YAAY,EAAE,MAAM,CAAA;IACpB,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,UAAU,CAAC,EAAE,OAAO,CAAA;IACpB,iBAAiB,CAAC,EAAE,MAAM,CAAA;IAC1B,cAAc,CAAC,EAAE,MAAM,CAAA;IACvB,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,kBAAkB,CAAC,EAAE,MAAM,CAAA;IAC3B,oBAAoB,CAAC,EAAE,MAAM,CAAA;CAC9B;AAED,MAAM,WAAW,wBAAwB;IACvC,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,SAAS,CAAC,EAAE,OAAO,CAAA;IACnB,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAA;CAClB"}
package/dist/types.js DELETED
@@ -1,2 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });