@geekmidas/events 10.0.0-alpha.13 → 10.0.0-alpha.14
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/dist/{SQSSubscriber-cpo624qU.mjs → SQSSubscriber-BXLE_1L3.mjs} +2 -2
- package/dist/{SQSSubscriber-cpo624qU.mjs.map → SQSSubscriber-BXLE_1L3.mjs.map} +1 -1
- package/dist/{SQSSubscriber-DLPZCzFE.cjs → SQSSubscriber-DtRUaZB3.cjs} +2 -2
- package/dist/{SQSSubscriber-DLPZCzFE.cjs.map → SQSSubscriber-DtRUaZB3.cjs.map} +1 -1
- package/dist/basic/index.cjs +1 -1
- package/dist/basic/index.d.cts +1 -1
- package/dist/basic/index.d.mts +1 -1
- package/dist/basic/index.mjs +1 -1
- package/dist/index.cjs +7 -6
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +2 -2
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.mts +2 -2
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +7 -7
- package/dist/index.mjs.map +1 -1
- package/dist/pgboss/index.cjs +13 -3
- package/dist/pgboss/index.cjs.map +1 -1
- package/dist/pgboss/index.d.cts +7 -1
- package/dist/pgboss/index.d.cts.map +1 -1
- package/dist/pgboss/index.d.mts +7 -1
- package/dist/pgboss/index.d.mts.map +1 -1
- package/dist/pgboss/index.mjs +13 -4
- package/dist/pgboss/index.mjs.map +1 -1
- package/dist/rabbitmq/index.cjs +13 -3
- package/dist/rabbitmq/index.cjs.map +1 -1
- package/dist/rabbitmq/index.d.cts +7 -1
- package/dist/rabbitmq/index.d.cts.map +1 -1
- package/dist/rabbitmq/index.d.mts +7 -1
- package/dist/rabbitmq/index.d.mts.map +1 -1
- package/dist/rabbitmq/index.mjs +13 -4
- package/dist/rabbitmq/index.mjs.map +1 -1
- package/dist/sns/index.cjs +16 -3
- package/dist/sns/index.cjs.map +1 -1
- package/dist/sns/index.d.cts +9 -1
- package/dist/sns/index.d.cts.map +1 -1
- package/dist/sns/index.d.mts +9 -1
- package/dist/sns/index.d.mts.map +1 -1
- package/dist/sns/index.mjs +16 -4
- package/dist/sns/index.mjs.map +1 -1
- package/dist/sqs/index.cjs +17 -6
- package/dist/sqs/index.cjs.map +1 -1
- package/dist/sqs/index.d.cts +14 -1
- package/dist/sqs/index.d.cts.map +1 -1
- package/dist/sqs/index.d.mts +14 -1
- package/dist/sqs/index.d.mts.map +1 -1
- package/dist/sqs/index.mjs +17 -7
- package/dist/sqs/index.mjs.map +1 -1
- package/dist/types-BcZjLxVV.cjs +51 -0
- package/dist/types-BcZjLxVV.cjs.map +1 -0
- package/dist/types-Bk5JM6MP.mjs +40 -0
- package/dist/types-Bk5JM6MP.mjs.map +1 -0
- package/dist/{types-lnM-qoe_.d.cts → types-gGaoT8qc.d.cts} +11 -2
- package/dist/{types-lnM-qoe_.d.cts.map → types-gGaoT8qc.d.cts.map} +1 -1
- package/dist/{types-lnM-qoe_.d.mts → types-gGaoT8qc.d.mts} +11 -2
- package/dist/{types-lnM-qoe_.d.mts.map → types-gGaoT8qc.d.mts.map} +1 -1
- package/package.json +3 -3
- package/dist/types-C15FC0qp.mjs +0 -18
- package/dist/types-C15FC0qp.mjs.map +0 -1
- package/dist/types-DXNwyRyg.cjs +0 -23
- package/dist/types-DXNwyRyg.cjs.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.mts","names":[],"sources":["../../src/pgboss/PgBossConnection.ts","../../src/pgboss/PgBossPublisher.ts","../../src/pgboss/PgBossSubscriber.ts"],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.d.mts","names":[],"sources":["../../src/pgboss/errors.ts","../../src/pgboss/PgBossConnection.ts","../../src/pgboss/PgBossPublisher.ts","../../src/pgboss/PgBossSubscriber.ts"],"mappings":";;;;qBAKa,yBAAyB;EAAA;;;;UCDrB;EAChB;EACA;;qBAGY,4BAA4B;UAKpB;WAJX,OAAI,mBAAA;UACL;UACA;EAEY,YAAA,QAAQ;;;;;SAMf,qBACZ,2BACE,QAAQ;EAgBL,WAAW;EA2BX,SAAS;EAUf;UAIQ;;;;MAQJ,YAAY;;;;qBCnFJ,gBAAgB,iBAAiB,4CAClC,eAAe;UAEN;EAAA,YAAA,YAAY;;;;;SAMnB,qBACZ,iBAAiB,iCAChB,2BAA2B,QAAQ,gBAAgB;EAO/C,QAAQ,UAAU,aAAa;EAwB/B,SAAS;;;;UC1CC;EAChB;EACA;;qBAGY,iBAAiB,iBAAiB,4CACnC,gBAAgB;UAGlB;UACA;EADA,YAAA,YAAY,kBACZ,UAAS;;;;;SAOL,qBACZ,iBAAiB,iCAChB,2BAA2B,QAAQ,iBAAiB;EAoBhD,UACL,UAAU,oBACV,WAAW,SAAS,aAAa,gBAC/B"}
|
package/dist/pgboss/index.mjs
CHANGED
|
@@ -1,5 +1,14 @@
|
|
|
1
|
-
import "../types-
|
|
1
|
+
import "../types-Bk5JM6MP.mjs";
|
|
2
2
|
import { PgBoss } from "pg-boss";
|
|
3
|
+
//#region src/pgboss/errors.ts
|
|
4
|
+
/** pg-boss connected but its instance never started. */
|
|
5
|
+
var PgBossNotStarted = class extends Error {
|
|
6
|
+
constructor() {
|
|
7
|
+
super("pg-boss did not start. Check the database in the connection string is reachable and the role may create the pg-boss schema.");
|
|
8
|
+
this.name = "PgBossNotStarted";
|
|
9
|
+
}
|
|
10
|
+
};
|
|
11
|
+
//#endregion
|
|
3
12
|
//#region src/pgboss/PgBossConnection.ts
|
|
4
13
|
var PgBossConnection = class PgBossConnection {
|
|
5
14
|
config;
|
|
@@ -86,7 +95,7 @@ var PgBossPublisher = class PgBossPublisher {
|
|
|
86
95
|
async publish(messages) {
|
|
87
96
|
if (!this.connection.isConnected()) await this.connection.connect();
|
|
88
97
|
const boss = this.connection.instance;
|
|
89
|
-
if (!boss) throw new
|
|
98
|
+
if (!boss) throw new PgBossNotStarted();
|
|
90
99
|
const groups = /* @__PURE__ */ new Map();
|
|
91
100
|
for (const m of messages) {
|
|
92
101
|
const list = groups.get(m.type) ?? [];
|
|
@@ -126,7 +135,7 @@ var PgBossSubscriber = class PgBossSubscriber {
|
|
|
126
135
|
async subscribe(messages, listener) {
|
|
127
136
|
if (!this.connection.isConnected()) await this.connection.connect();
|
|
128
137
|
const boss = this.connection.instance;
|
|
129
|
-
if (!boss) throw new
|
|
138
|
+
if (!boss) throw new PgBossNotStarted();
|
|
130
139
|
for (const messageType of messages) {
|
|
131
140
|
await boss.createQueue(messageType);
|
|
132
141
|
await boss.work(messageType, {
|
|
@@ -142,6 +151,6 @@ var PgBossSubscriber = class PgBossSubscriber {
|
|
|
142
151
|
}
|
|
143
152
|
};
|
|
144
153
|
//#endregion
|
|
145
|
-
export { PgBossConnection, PgBossPublisher, PgBossSubscriber };
|
|
154
|
+
export { PgBossConnection, PgBossNotStarted, PgBossPublisher, PgBossSubscriber };
|
|
146
155
|
|
|
147
156
|
//# sourceMappingURL=index.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","names":[],"sources":["../../src/pgboss/PgBossConnection.ts","../../src/pgboss/PgBossPublisher.ts","../../src/pgboss/PgBossSubscriber.ts"],"sourcesContent":["import { PgBoss } from 'pg-boss';\nimport type { EventConnection } from '../types';\nimport { EventPublisherType } from '../types';\n\nexport interface PgBossConnectionConfig {\n\tconnectionString: string;\n\tschema?: string;\n}\n\nexport class PgBossConnection implements EventConnection {\n\treadonly type = EventPublisherType.PgBoss;\n\tprivate boss?: PgBoss;\n\tprivate connecting?: Promise<void>;\n\n\tconstructor(private config: PgBossConnectionConfig) {}\n\n\t/**\n\t * Create a PgBossConnection from a connection string.\n\t * Format: pgboss://user:pass@host:5432/database?schema=pgboss\n\t */\n\tstatic async fromConnectionString(\n\t\tconnectionString: string,\n\t): Promise<PgBossConnection> {\n\t\tconst url = new URL(connectionString);\n\t\tconst params = url.searchParams;\n\n\t\tconst postgresUrl = `postgres://${url.username ? `${url.username}:${url.password}@` : ''}${url.host}${url.pathname}`;\n\n\t\tconst config: PgBossConnectionConfig = {\n\t\t\tconnectionString: postgresUrl,\n\t\t\tschema: params.get('schema') || undefined,\n\t\t};\n\n\t\tconst connection = new PgBossConnection(config);\n\t\tawait connection.connect();\n\t\treturn connection;\n\t}\n\n\tasync connect(): Promise<void> {\n\t\tif (this.boss) return;\n\n\t\tif (this.connecting) {\n\t\t\tawait this.connecting;\n\t\t\treturn;\n\t\t}\n\n\t\tthis.connecting = (async () => {\n\t\t\ttry {\n\t\t\t\tconst boss = new PgBoss({\n\t\t\t\t\tconnectionString: this.config.connectionString,\n\t\t\t\t\t...(this.config.schema && { schema: this.config.schema }),\n\t\t\t\t});\n\n\t\t\t\tawait boss.start();\n\t\t\t\tthis.boss = boss;\n\t\t\t} catch (error) {\n\t\t\t\tthis.cleanup();\n\t\t\t\tthrow error;\n\t\t\t}\n\t\t})();\n\n\t\tawait this.connecting;\n\t\tthis.connecting = undefined;\n\t}\n\n\tasync close(): Promise<void> {\n\t\ttry {\n\t\t\tawait this.boss?.stop({ graceful: true });\n\t\t} catch {\n\t\t\t// Ignore errors during close\n\t\t} finally {\n\t\t\tthis.cleanup();\n\t\t}\n\t}\n\n\tisConnected(): boolean {\n\t\treturn !!this.boss;\n\t}\n\n\tprivate cleanup(): void {\n\t\tthis.boss = undefined;\n\t\tthis.connecting = undefined;\n\t}\n\n\t/**\n\t * Get the underlying pg-boss instance for publishing/subscribing\n\t */\n\tget instance(): PgBoss | undefined {\n\t\treturn this.boss;\n\t}\n}\n","import type { EventPublisher, PublishableMessage } from '../types';\nimport type { PgBossConnection } from './PgBossConnection';\n\nexport class PgBossPublisher<TMessage extends PublishableMessage<string, any>>\n\timplements EventPublisher<TMessage>\n{\n\tconstructor(private connection: PgBossConnection) {}\n\n\t/**\n\t * Create a PgBossPublisher from a connection string.\n\t * Format: pgboss://user:pass@host:5432/database?schema=pgboss\n\t */\n\tstatic async fromConnectionString<\n\t\tTMessage extends PublishableMessage<string, any>,\n\t>(connectionString: string): Promise<PgBossPublisher<TMessage>> {\n\t\tconst { PgBossConnection } = await import('./PgBossConnection');\n\t\tconst connection =\n\t\t\tawait PgBossConnection.fromConnectionString(connectionString);\n\t\treturn new PgBossPublisher<TMessage>(connection);\n\t}\n\n\tasync publish(messages: TMessage[]): Promise<void> {\n\t\tif (!this.connection.isConnected()) {\n\t\t\tawait this.connection.connect();\n\t\t}\n\n\t\tconst boss = this.connection.instance;\n\t\tif (!boss) {\n\t\t\tthrow new
|
|
1
|
+
{"version":3,"file":"index.mjs","names":[],"sources":["../../src/pgboss/errors.ts","../../src/pgboss/PgBossConnection.ts","../../src/pgboss/PgBossPublisher.ts","../../src/pgboss/PgBossSubscriber.ts"],"sourcesContent":["// Beside the adapter but apart from its connection module, which imports\n// the broker's client: the publisher and subscriber load that lazily, and\n// importing an error class from it would load it eagerly.\n\n/** pg-boss connected but its instance never started. */\nexport class PgBossNotStarted extends Error {\n\tconstructor() {\n\t\tsuper(\n\t\t\t'pg-boss did not start. Check the database in the connection string is reachable and the role may create the pg-boss schema.',\n\t\t);\n\t\tthis.name = 'PgBossNotStarted';\n\t}\n}\n","import { PgBoss } from 'pg-boss';\nimport type { EventConnection } from '../types';\nimport { EventPublisherType } from '../types';\n\nexport interface PgBossConnectionConfig {\n\tconnectionString: string;\n\tschema?: string;\n}\n\nexport class PgBossConnection implements EventConnection {\n\treadonly type = EventPublisherType.PgBoss;\n\tprivate boss?: PgBoss;\n\tprivate connecting?: Promise<void>;\n\n\tconstructor(private config: PgBossConnectionConfig) {}\n\n\t/**\n\t * Create a PgBossConnection from a connection string.\n\t * Format: pgboss://user:pass@host:5432/database?schema=pgboss\n\t */\n\tstatic async fromConnectionString(\n\t\tconnectionString: string,\n\t): Promise<PgBossConnection> {\n\t\tconst url = new URL(connectionString);\n\t\tconst params = url.searchParams;\n\n\t\tconst postgresUrl = `postgres://${url.username ? `${url.username}:${url.password}@` : ''}${url.host}${url.pathname}`;\n\n\t\tconst config: PgBossConnectionConfig = {\n\t\t\tconnectionString: postgresUrl,\n\t\t\tschema: params.get('schema') || undefined,\n\t\t};\n\n\t\tconst connection = new PgBossConnection(config);\n\t\tawait connection.connect();\n\t\treturn connection;\n\t}\n\n\tasync connect(): Promise<void> {\n\t\tif (this.boss) return;\n\n\t\tif (this.connecting) {\n\t\t\tawait this.connecting;\n\t\t\treturn;\n\t\t}\n\n\t\tthis.connecting = (async () => {\n\t\t\ttry {\n\t\t\t\tconst boss = new PgBoss({\n\t\t\t\t\tconnectionString: this.config.connectionString,\n\t\t\t\t\t...(this.config.schema && { schema: this.config.schema }),\n\t\t\t\t});\n\n\t\t\t\tawait boss.start();\n\t\t\t\tthis.boss = boss;\n\t\t\t} catch (error) {\n\t\t\t\tthis.cleanup();\n\t\t\t\tthrow error;\n\t\t\t}\n\t\t})();\n\n\t\tawait this.connecting;\n\t\tthis.connecting = undefined;\n\t}\n\n\tasync close(): Promise<void> {\n\t\ttry {\n\t\t\tawait this.boss?.stop({ graceful: true });\n\t\t} catch {\n\t\t\t// Ignore errors during close\n\t\t} finally {\n\t\t\tthis.cleanup();\n\t\t}\n\t}\n\n\tisConnected(): boolean {\n\t\treturn !!this.boss;\n\t}\n\n\tprivate cleanup(): void {\n\t\tthis.boss = undefined;\n\t\tthis.connecting = undefined;\n\t}\n\n\t/**\n\t * Get the underlying pg-boss instance for publishing/subscribing\n\t */\n\tget instance(): PgBoss | undefined {\n\t\treturn this.boss;\n\t}\n}\n","import type { EventPublisher, PublishableMessage } from '../types';\nimport { PgBossNotStarted } from './errors';\nimport type { PgBossConnection } from './PgBossConnection';\n\nexport class PgBossPublisher<TMessage extends PublishableMessage<string, any>>\n\timplements EventPublisher<TMessage>\n{\n\tconstructor(private connection: PgBossConnection) {}\n\n\t/**\n\t * Create a PgBossPublisher from a connection string.\n\t * Format: pgboss://user:pass@host:5432/database?schema=pgboss\n\t */\n\tstatic async fromConnectionString<\n\t\tTMessage extends PublishableMessage<string, any>,\n\t>(connectionString: string): Promise<PgBossPublisher<TMessage>> {\n\t\tconst { PgBossConnection } = await import('./PgBossConnection');\n\t\tconst connection =\n\t\t\tawait PgBossConnection.fromConnectionString(connectionString);\n\t\treturn new PgBossPublisher<TMessage>(connection);\n\t}\n\n\tasync publish(messages: TMessage[]): Promise<void> {\n\t\tif (!this.connection.isConnected()) {\n\t\t\tawait this.connection.connect();\n\t\t}\n\n\t\tconst boss = this.connection.instance;\n\t\tif (!boss) {\n\t\t\tthrow new PgBossNotStarted();\n\t\t}\n\n\t\t// Group jobs by queue name (v11+ requires per-queue insert calls)\n\t\tconst groups = new Map<string, { data: TMessage['payload'] }[]>();\n\t\tfor (const m of messages) {\n\t\t\tconst list = groups.get(m.type) ?? [];\n\t\t\tlist.push({ data: m.payload });\n\t\t\tgroups.set(m.type, list);\n\t\t}\n\n\t\tfor (const [name, jobs] of groups) {\n\t\t\tawait boss.createQueue(name);\n\t\t\tawait boss.insert(name, jobs);\n\t\t}\n\t}\n\n\tasync close(): Promise<void> {\n\t\t// Publisher doesn't own the connection\n\t}\n}\n","import type { EventSubscriber, PublishableMessage } from '../types';\nimport { PgBossNotStarted } from './errors';\nimport type { PgBossConnection } from './PgBossConnection';\n\nexport interface PgBossSubscriberOptions {\n\tbatchSize?: number;\n\tpollingIntervalSeconds?: number;\n}\n\nexport class PgBossSubscriber<TMessage extends PublishableMessage<string, any>>\n\timplements EventSubscriber<TMessage>\n{\n\tconstructor(\n\t\tprivate connection: PgBossConnection,\n\t\tprivate options: PgBossSubscriberOptions = {},\n\t) {}\n\n\t/**\n\t * Create a PgBossSubscriber from a connection string.\n\t * Format: pgboss://user:pass@host:5432/database?schema=pgboss&batchSize=5\n\t */\n\tstatic async fromConnectionString<\n\t\tTMessage extends PublishableMessage<string, any>,\n\t>(connectionString: string): Promise<PgBossSubscriber<TMessage>> {\n\t\tconst url = new URL(connectionString);\n\t\tconst params = url.searchParams;\n\n\t\tconst { PgBossConnection } = await import('./PgBossConnection');\n\t\tconst connection =\n\t\t\tawait PgBossConnection.fromConnectionString(connectionString);\n\n\t\tconst options: PgBossSubscriberOptions = {\n\t\t\tbatchSize: params.get('batchSize')\n\t\t\t\t? Number.parseInt(params.get('batchSize')!, 10)\n\t\t\t\t: undefined,\n\t\t\tpollingIntervalSeconds: params.get('pollingIntervalSeconds')\n\t\t\t\t? Number.parseInt(params.get('pollingIntervalSeconds')!, 10)\n\t\t\t\t: undefined,\n\t\t};\n\n\t\treturn new PgBossSubscriber<TMessage>(connection, options);\n\t}\n\n\tasync subscribe(\n\t\tmessages: TMessage['type'][],\n\t\tlistener: (payload: TMessage) => Promise<void>,\n\t): Promise<void> {\n\t\tif (!this.connection.isConnected()) {\n\t\t\tawait this.connection.connect();\n\t\t}\n\n\t\tconst boss = this.connection.instance;\n\t\tif (!boss) {\n\t\t\tthrow new PgBossNotStarted();\n\t\t}\n\n\t\tfor (const messageType of messages) {\n\t\t\tawait boss.createQueue(messageType);\n\t\t\tawait boss.work(\n\t\t\t\tmessageType,\n\t\t\t\t{\n\t\t\t\t\t...(this.options.batchSize && {\n\t\t\t\t\t\tbatchSize: this.options.batchSize,\n\t\t\t\t\t}),\n\t\t\t\t\t...(this.options.pollingIntervalSeconds && {\n\t\t\t\t\t\tpollingIntervalSeconds: this.options.pollingIntervalSeconds,\n\t\t\t\t\t}),\n\t\t\t\t},\n\t\t\t\tasync (jobs) => {\n\t\t\t\t\tfor (const job of jobs) {\n\t\t\t\t\t\tconst fullMessage = {\n\t\t\t\t\t\t\ttype: job.name,\n\t\t\t\t\t\t\tpayload: job.data,\n\t\t\t\t\t\t} as TMessage;\n\n\t\t\t\t\t\tawait listener(fullMessage);\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t);\n\t\t}\n\t}\n}\n"],"mappings":"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"}
|
package/dist/rabbitmq/index.cjs
CHANGED
|
@@ -1,8 +1,17 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
2
|
const require_rolldown_runtime = require("../rolldown-runtime-C0BPl7ul.cjs");
|
|
3
|
-
require("../types-
|
|
3
|
+
require("../types-BcZjLxVV.cjs");
|
|
4
4
|
let amqplib = require("amqplib");
|
|
5
5
|
amqplib = require_rolldown_runtime.__toESM(amqplib, 1);
|
|
6
|
+
//#region src/rabbitmq/errors.ts
|
|
7
|
+
/** The connection opened but has no channel to publish or consume on. */
|
|
8
|
+
var RabbitMQChannelUnavailable = class extends Error {
|
|
9
|
+
constructor() {
|
|
10
|
+
super("RabbitMQ connected without a channel. Check the broker is reachable and the connection string names a vhost the user can open channels on.");
|
|
11
|
+
this.name = "RabbitMQChannelUnavailable";
|
|
12
|
+
}
|
|
13
|
+
};
|
|
14
|
+
//#endregion
|
|
6
15
|
//#region src/rabbitmq/RabbitMQConnection.ts
|
|
7
16
|
var RabbitMQConnection = class RabbitMQConnection {
|
|
8
17
|
type = "rabbitmq";
|
|
@@ -124,7 +133,7 @@ var RabbitMQPublisher = class RabbitMQPublisher {
|
|
|
124
133
|
async publish(messages) {
|
|
125
134
|
if (!this.connection.isConnected()) await this.connection.connect();
|
|
126
135
|
const channel = this.connection.amqpChannel;
|
|
127
|
-
if (!channel) throw new
|
|
136
|
+
if (!channel) throw new RabbitMQChannelUnavailable();
|
|
128
137
|
const exchange = this.connection.exchangeName;
|
|
129
138
|
for (const message of messages) {
|
|
130
139
|
const content = Buffer.from(JSON.stringify(message.payload));
|
|
@@ -166,7 +175,7 @@ var RabbitMQSubscriber = class RabbitMQSubscriber {
|
|
|
166
175
|
async subscribe(messages, listener) {
|
|
167
176
|
if (!this.connection.isConnected()) await this.connection.connect();
|
|
168
177
|
const channel = this.connection.amqpChannel;
|
|
169
|
-
if (!channel) throw new
|
|
178
|
+
if (!channel) throw new RabbitMQChannelUnavailable();
|
|
170
179
|
const exchange = this.connection.exchangeName;
|
|
171
180
|
if (this.options.prefetch) await channel.prefetch(this.options.prefetch);
|
|
172
181
|
const queueName = this.options.queueName || "";
|
|
@@ -193,6 +202,7 @@ var RabbitMQSubscriber = class RabbitMQSubscriber {
|
|
|
193
202
|
}
|
|
194
203
|
};
|
|
195
204
|
//#endregion
|
|
205
|
+
exports.RabbitMQChannelUnavailable = RabbitMQChannelUnavailable;
|
|
196
206
|
exports.RabbitMQConnection = RabbitMQConnection;
|
|
197
207
|
exports.RabbitMQConnection$1 = RabbitMQConnection;
|
|
198
208
|
exports.RabbitMQPublisher = RabbitMQPublisher;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.cjs","names":[],"sources":["../../src/rabbitmq/RabbitMQConnection.ts","../../src/rabbitmq/RabbitMQPublisher.ts","../../src/rabbitmq/RabbitMQSubscriber.ts"],"sourcesContent":["import amqplib from 'amqplib';\nimport type { EventConnection } from '../types';\nimport { EventPublisherType } from '../types';\n\nexport interface RabbitMQConnectionConfig {\n\turl: string;\n\texchange?: string;\n\texchangeType?: 'topic' | 'direct' | 'fanout' | 'headers';\n\texchangeOptions?: amqplib.Options.AssertExchange;\n\ttimeout?: number; // Connection timeout in milliseconds (default: 5000)\n}\n\nexport class RabbitMQConnection implements EventConnection {\n\treadonly type = EventPublisherType.RabbitMQ;\n\tprivate connection?: amqplib.ChannelModel;\n\tprivate channel?: amqplib.Channel;\n\tprivate connecting?: Promise<void>;\n\tprivate config: Required<\n\t\tPick<RabbitMQConnectionConfig, 'exchange' | 'exchangeType' | 'timeout'>\n\t> &\n\t\tRabbitMQConnectionConfig;\n\n\tconstructor(config: RabbitMQConnectionConfig) {\n\t\tthis.config = {\n\t\t\t...config,\n\t\t\texchange: config.exchange || 'geekmidas.events',\n\t\t\texchangeType: config.exchangeType || 'topic',\n\t\t\ttimeout: config.timeout ?? 5000,\n\t\t\texchangeOptions: {\n\t\t\t\tdurable: true,\n\t\t\t\t...config.exchangeOptions,\n\t\t\t},\n\t\t};\n\t}\n\n\t/**\n\t * Create a RabbitMQConnection from a connection string\n\t * Format: rabbitmq://user:pass@host:port/vhost?exchange=name&type=topic&timeout=5000\n\t */\n\tstatic async fromConnectionString(\n\t\tconnectionString: string,\n\t): Promise<RabbitMQConnection> {\n\t\tconst url = new URL(connectionString);\n\t\tconst params = url.searchParams;\n\n\t\t// Extract connection URL without query params\n\t\tconst baseUrl = `amqp://${url.username ? `${url.username}:${url.password}@` : ''}${url.host}${url.pathname}`;\n\n\t\tconst timeoutParam = params.get('timeout');\n\t\tconst config: RabbitMQConnectionConfig = {\n\t\t\turl: baseUrl,\n\t\t\texchange: params.get('exchange') || 'geekmidas.events',\n\t\t\texchangeType: (params.get('type') as any) || 'topic',\n\t\t\ttimeout: timeoutParam ? Number.parseInt(timeoutParam, 10) : undefined,\n\t\t};\n\n\t\tconst connection = new RabbitMQConnection(config);\n\t\tawait connection.connect();\n\t\treturn connection;\n\t}\n\n\tasync connect(): Promise<void> {\n\t\tif (this.channel) return;\n\n\t\t// Prevent multiple simultaneous connection attempts\n\t\tif (this.connecting) {\n\t\t\tawait this.connecting;\n\t\t\treturn;\n\t\t}\n\n\t\tthis.connecting = (async () => {\n\t\t\ttry {\n\t\t\t\tconst connection = await amqplib.connect(this.config.url, {\n\t\t\t\t\ttimeout: this.config.timeout,\n\t\t\t\t});\n\t\t\t\tconst channel = await connection.createChannel();\n\n\t\t\t\t// Assert the exchange exists\n\t\t\t\tawait channel.assertExchange(\n\t\t\t\t\tthis.config.exchange!,\n\t\t\t\t\tthis.config.exchangeType!,\n\t\t\t\t\tthis.config.exchangeOptions,\n\t\t\t\t);\n\n\t\t\t\t// Handle connection errors\n\t\t\t\tconnection.on('error', (_err: Error) => {\n\t\t\t\t\tthis.cleanup();\n\t\t\t\t});\n\n\t\t\t\tconnection.on('close', () => {\n\t\t\t\t\tthis.cleanup();\n\t\t\t\t});\n\n\t\t\t\t// Only set after successful setup\n\t\t\t\tthis.connection = connection;\n\t\t\t\tthis.channel = channel;\n\t\t\t} catch (error) {\n\t\t\t\tthis.cleanup();\n\t\t\t\tthrow error;\n\t\t\t}\n\t\t})();\n\n\t\tawait this.connecting;\n\t\tthis.connecting = undefined;\n\t}\n\n\tasync close(): Promise<void> {\n\t\ttry {\n\t\t\tawait this.channel?.close();\n\t\t\tawait this.connection?.close();\n\t\t} catch (_error) {\n\t\t\t// Ignore errors during close\n\t\t} finally {\n\t\t\tthis.cleanup();\n\t\t}\n\t}\n\n\tisConnected(): boolean {\n\t\treturn !!this.channel;\n\t}\n\n\tprivate cleanup(): void {\n\t\tthis.channel = undefined;\n\t\tthis.connection = undefined;\n\t\tthis.connecting = undefined;\n\t}\n\n\t/**\n\t * Get the underlying channel for publishing/subscribing\n\t */\n\tget amqpChannel(): amqplib.Channel | undefined {\n\t\treturn this.channel;\n\t}\n\n\t/**\n\t * Get the exchange name for this connection\n\t */\n\tget exchangeName(): string {\n\t\treturn this.config.exchange;\n\t}\n\n\t/**\n\t * Get the exchange type for this connection\n\t */\n\tget exchangeType(): string {\n\t\treturn this.config.exchangeType;\n\t}\n}\n","import type amqplib from 'amqplib';\nimport type { EventPublisher, PublishableMessage } from '../types';\nimport type { RabbitMQConnection } from './RabbitMQConnection';\n\nexport interface RabbitMQPublisherOptions {\n\tpublishOptions?: amqplib.Options.Publish;\n}\n\nexport class RabbitMQPublisher<TMessage extends PublishableMessage<string, any>>\n\timplements EventPublisher<TMessage>\n{\n\tconstructor(\n\t\tprivate connection: RabbitMQConnection,\n\t\tprivate options: RabbitMQPublisherOptions = {},\n\t) {}\n\n\t/**\n\t * Create a RabbitMQPublisher from a connection string\n\t * Format: rabbitmq://user:pass@host:port/vhost?exchange=name&type=topic&timeout=5000\n\t */\n\tstatic async fromConnectionString<\n\t\tTMessage extends PublishableMessage<string, any>,\n\t>(connectionString: string): Promise<RabbitMQPublisher<TMessage>> {\n\t\tconst { RabbitMQConnection } = await import('./RabbitMQConnection');\n\t\tconst connection =\n\t\t\tawait RabbitMQConnection.fromConnectionString(connectionString);\n\t\treturn new RabbitMQPublisher<TMessage>(connection);\n\t}\n\n\tasync publish(messages: TMessage[]): Promise<void> {\n\t\tif (!this.connection.isConnected()) {\n\t\t\tawait this.connection.connect();\n\t\t}\n\n\t\tconst channel = this.connection.amqpChannel;\n\t\tif (!channel) {\n\t\t\tthrow new Error('Channel not initialized');\n\t\t}\n\n\t\tconst exchange = this.connection.exchangeName;\n\n\t\tfor (const message of messages) {\n\t\t\tconst content = Buffer.from(JSON.stringify(message.payload));\n\n\t\t\t// Use message type as routing key for topic exchanges\n\t\t\tconst routingKey = message.type;\n\n\t\t\tconst published = channel.publish(exchange, routingKey, content, {\n\t\t\t\tcontentType: 'application/json',\n\t\t\t\ttimestamp: Date.now(),\n\t\t\t\ttype: message.type,\n\t\t\t\tpersistent: true,\n\t\t\t\t...this.options.publishOptions,\n\t\t\t});\n\n\t\t\t// Handle backpressure\n\t\t\tif (!published) {\n\t\t\t\tawait new Promise((resolve) => channel.once('drain', resolve));\n\t\t\t}\n\t\t}\n\t}\n\n\tasync close(): Promise<void> {\n\t\t// Publisher doesn't own the connection\n\t\t// Connection should be closed by whoever created it\n\t}\n}\n","import type amqplib from 'amqplib';\nimport type { EventSubscriber, PublishableMessage } from '../types';\nimport type { RabbitMQConnection } from './RabbitMQConnection';\n\nexport interface RabbitMQSubscriberOptions {\n\tqueueName?: string; // If not provided, a unique queue will be generated\n\tqueueOptions?: amqplib.Options.AssertQueue;\n\tconsumeOptions?: amqplib.Options.Consume;\n\tprefetch?: number; // Number of messages to prefetch (default: 1)\n}\n\nexport class RabbitMQSubscriber<\n\tTMessage extends PublishableMessage<string, any>,\n> implements EventSubscriber<TMessage>\n{\n\tconstructor(\n\t\tprivate connection: RabbitMQConnection,\n\t\tprivate options: RabbitMQSubscriberOptions = {},\n\t) {}\n\n\t/**\n\t * Create a RabbitMQSubscriber from a connection string\n\t * Format: rabbitmq://user:pass@host:port/vhost?exchange=name&type=topic&queueName=myqueue&prefetch=10\n\t */\n\tstatic async fromConnectionString<\n\t\tTMessage extends PublishableMessage<string, any>,\n\t>(connectionString: string): Promise<RabbitMQSubscriber<TMessage>> {\n\t\tconst url = new URL(connectionString);\n\t\tconst params = url.searchParams;\n\n\t\tconst { RabbitMQConnection } = await import('./RabbitMQConnection');\n\t\tconst connection =\n\t\t\tawait RabbitMQConnection.fromConnectionString(connectionString);\n\n\t\tconst options: RabbitMQSubscriberOptions = {\n\t\t\tqueueName: params.get('queueName') || undefined,\n\t\t\tprefetch: params.get('prefetch')\n\t\t\t\t? Number.parseInt(params.get('prefetch')!, 10)\n\t\t\t\t: undefined,\n\t\t};\n\n\t\treturn new RabbitMQSubscriber<TMessage>(connection, options);\n\t}\n\n\tasync subscribe(\n\t\tmessages: TMessage['type'][],\n\t\tlistener: (payload: TMessage) => Promise<void>,\n\t): Promise<void> {\n\t\tif (!this.connection.isConnected()) {\n\t\t\tawait this.connection.connect();\n\t\t}\n\n\t\tconst channel = this.connection.amqpChannel;\n\t\tif (!channel) {\n\t\t\tthrow new Error('Channel not initialized');\n\t\t}\n\n\t\tconst exchange = this.connection.exchangeName;\n\n\t\t// Set prefetch if specified\n\t\tif (this.options.prefetch) {\n\t\t\tawait channel.prefetch(this.options.prefetch);\n\t\t}\n\n\t\t// Assert queue\n\t\tconst queueName = this.options.queueName || '';\n\t\tconst queueOptions: amqplib.Options.AssertQueue = {\n\t\t\tdurable: true,\n\t\t\t...this.options.queueOptions,\n\t\t};\n\n\t\tconst { queue } = await channel.assertQueue(queueName, queueOptions);\n\n\t\t// Bind queue to exchange for each message type\n\t\tfor (const messageType of messages) {\n\t\t\tawait channel.bindQueue(queue, exchange, messageType);\n\t\t}\n\n\t\t// Consume messages\n\t\tawait channel.consume(\n\t\t\tqueue,\n\t\t\tasync (msg) => {\n\t\t\t\tif (!msg) return;\n\n\t\t\t\ttry {\n\t\t\t\t\t// Parse message payload\n\t\t\t\t\tconst content = msg.content.toString();\n\t\t\t\t\tconst payload = JSON.parse(content);\n\n\t\t\t\t\t// Get message type from properties\n\t\t\t\t\tconst messageType = msg.properties.type;\n\n\t\t\t\t\t// Reconstruct full message\n\t\t\t\t\tconst fullMessage: TMessage = {\n\t\t\t\t\t\ttype: messageType,\n\t\t\t\t\t\tpayload,\n\t\t\t\t\t} as TMessage;\n\n\t\t\t\t\t// Call listener\n\t\t\t\t\tawait listener(fullMessage);\n\n\t\t\t\t\t// Ack message\n\t\t\t\t\tchannel.ack(msg);\n\t\t\t\t} catch (_error) {\n\t\t\t\t\tchannel.nack(msg, false, true);\n\t\t\t\t}\n\t\t\t},\n\t\t\tthis.options.consumeOptions,\n\t\t);\n\t}\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.cjs","names":[],"sources":["../../src/rabbitmq/errors.ts","../../src/rabbitmq/RabbitMQConnection.ts","../../src/rabbitmq/RabbitMQPublisher.ts","../../src/rabbitmq/RabbitMQSubscriber.ts"],"sourcesContent":["// Beside the adapter but apart from its connection module, which imports\n// the broker's client: the publisher and subscriber load that lazily, and\n// importing an error class from it would load it eagerly.\n\n/** The connection opened but has no channel to publish or consume on. */\nexport class RabbitMQChannelUnavailable extends Error {\n\tconstructor() {\n\t\tsuper(\n\t\t\t'RabbitMQ connected without a channel. Check the broker is reachable and the connection string names a vhost the user can open channels on.',\n\t\t);\n\t\tthis.name = 'RabbitMQChannelUnavailable';\n\t}\n}\n","import amqplib from 'amqplib';\nimport type { EventConnection } from '../types';\nimport { EventPublisherType } from '../types';\n\nexport interface RabbitMQConnectionConfig {\n\turl: string;\n\texchange?: string;\n\texchangeType?: 'topic' | 'direct' | 'fanout' | 'headers';\n\texchangeOptions?: amqplib.Options.AssertExchange;\n\ttimeout?: number; // Connection timeout in milliseconds (default: 5000)\n}\n\nexport class RabbitMQConnection implements EventConnection {\n\treadonly type = EventPublisherType.RabbitMQ;\n\tprivate connection?: amqplib.ChannelModel;\n\tprivate channel?: amqplib.Channel;\n\tprivate connecting?: Promise<void>;\n\tprivate config: Required<\n\t\tPick<RabbitMQConnectionConfig, 'exchange' | 'exchangeType' | 'timeout'>\n\t> &\n\t\tRabbitMQConnectionConfig;\n\n\tconstructor(config: RabbitMQConnectionConfig) {\n\t\tthis.config = {\n\t\t\t...config,\n\t\t\texchange: config.exchange || 'geekmidas.events',\n\t\t\texchangeType: config.exchangeType || 'topic',\n\t\t\ttimeout: config.timeout ?? 5000,\n\t\t\texchangeOptions: {\n\t\t\t\tdurable: true,\n\t\t\t\t...config.exchangeOptions,\n\t\t\t},\n\t\t};\n\t}\n\n\t/**\n\t * Create a RabbitMQConnection from a connection string\n\t * Format: rabbitmq://user:pass@host:port/vhost?exchange=name&type=topic&timeout=5000\n\t */\n\tstatic async fromConnectionString(\n\t\tconnectionString: string,\n\t): Promise<RabbitMQConnection> {\n\t\tconst url = new URL(connectionString);\n\t\tconst params = url.searchParams;\n\n\t\t// Extract connection URL without query params\n\t\tconst baseUrl = `amqp://${url.username ? `${url.username}:${url.password}@` : ''}${url.host}${url.pathname}`;\n\n\t\tconst timeoutParam = params.get('timeout');\n\t\tconst config: RabbitMQConnectionConfig = {\n\t\t\turl: baseUrl,\n\t\t\texchange: params.get('exchange') || 'geekmidas.events',\n\t\t\texchangeType: (params.get('type') as any) || 'topic',\n\t\t\ttimeout: timeoutParam ? Number.parseInt(timeoutParam, 10) : undefined,\n\t\t};\n\n\t\tconst connection = new RabbitMQConnection(config);\n\t\tawait connection.connect();\n\t\treturn connection;\n\t}\n\n\tasync connect(): Promise<void> {\n\t\tif (this.channel) return;\n\n\t\t// Prevent multiple simultaneous connection attempts\n\t\tif (this.connecting) {\n\t\t\tawait this.connecting;\n\t\t\treturn;\n\t\t}\n\n\t\tthis.connecting = (async () => {\n\t\t\ttry {\n\t\t\t\tconst connection = await amqplib.connect(this.config.url, {\n\t\t\t\t\ttimeout: this.config.timeout,\n\t\t\t\t});\n\t\t\t\tconst channel = await connection.createChannel();\n\n\t\t\t\t// Assert the exchange exists\n\t\t\t\tawait channel.assertExchange(\n\t\t\t\t\tthis.config.exchange!,\n\t\t\t\t\tthis.config.exchangeType!,\n\t\t\t\t\tthis.config.exchangeOptions,\n\t\t\t\t);\n\n\t\t\t\t// Handle connection errors\n\t\t\t\tconnection.on('error', (_err: Error) => {\n\t\t\t\t\tthis.cleanup();\n\t\t\t\t});\n\n\t\t\t\tconnection.on('close', () => {\n\t\t\t\t\tthis.cleanup();\n\t\t\t\t});\n\n\t\t\t\t// Only set after successful setup\n\t\t\t\tthis.connection = connection;\n\t\t\t\tthis.channel = channel;\n\t\t\t} catch (error) {\n\t\t\t\tthis.cleanup();\n\t\t\t\tthrow error;\n\t\t\t}\n\t\t})();\n\n\t\tawait this.connecting;\n\t\tthis.connecting = undefined;\n\t}\n\n\tasync close(): Promise<void> {\n\t\ttry {\n\t\t\tawait this.channel?.close();\n\t\t\tawait this.connection?.close();\n\t\t} catch (_error) {\n\t\t\t// Ignore errors during close\n\t\t} finally {\n\t\t\tthis.cleanup();\n\t\t}\n\t}\n\n\tisConnected(): boolean {\n\t\treturn !!this.channel;\n\t}\n\n\tprivate cleanup(): void {\n\t\tthis.channel = undefined;\n\t\tthis.connection = undefined;\n\t\tthis.connecting = undefined;\n\t}\n\n\t/**\n\t * Get the underlying channel for publishing/subscribing\n\t */\n\tget amqpChannel(): amqplib.Channel | undefined {\n\t\treturn this.channel;\n\t}\n\n\t/**\n\t * Get the exchange name for this connection\n\t */\n\tget exchangeName(): string {\n\t\treturn this.config.exchange;\n\t}\n\n\t/**\n\t * Get the exchange type for this connection\n\t */\n\tget exchangeType(): string {\n\t\treturn this.config.exchangeType;\n\t}\n}\n","import type amqplib from 'amqplib';\nimport type { EventPublisher, PublishableMessage } from '../types';\nimport { RabbitMQChannelUnavailable } from './errors';\nimport type { RabbitMQConnection } from './RabbitMQConnection';\n\nexport interface RabbitMQPublisherOptions {\n\tpublishOptions?: amqplib.Options.Publish;\n}\n\nexport class RabbitMQPublisher<TMessage extends PublishableMessage<string, any>>\n\timplements EventPublisher<TMessage>\n{\n\tconstructor(\n\t\tprivate connection: RabbitMQConnection,\n\t\tprivate options: RabbitMQPublisherOptions = {},\n\t) {}\n\n\t/**\n\t * Create a RabbitMQPublisher from a connection string\n\t * Format: rabbitmq://user:pass@host:port/vhost?exchange=name&type=topic&timeout=5000\n\t */\n\tstatic async fromConnectionString<\n\t\tTMessage extends PublishableMessage<string, any>,\n\t>(connectionString: string): Promise<RabbitMQPublisher<TMessage>> {\n\t\tconst { RabbitMQConnection } = await import('./RabbitMQConnection');\n\t\tconst connection =\n\t\t\tawait RabbitMQConnection.fromConnectionString(connectionString);\n\t\treturn new RabbitMQPublisher<TMessage>(connection);\n\t}\n\n\tasync publish(messages: TMessage[]): Promise<void> {\n\t\tif (!this.connection.isConnected()) {\n\t\t\tawait this.connection.connect();\n\t\t}\n\n\t\tconst channel = this.connection.amqpChannel;\n\t\tif (!channel) {\n\t\t\tthrow new RabbitMQChannelUnavailable();\n\t\t}\n\n\t\tconst exchange = this.connection.exchangeName;\n\n\t\tfor (const message of messages) {\n\t\t\tconst content = Buffer.from(JSON.stringify(message.payload));\n\n\t\t\t// Use message type as routing key for topic exchanges\n\t\t\tconst routingKey = message.type;\n\n\t\t\tconst published = channel.publish(exchange, routingKey, content, {\n\t\t\t\tcontentType: 'application/json',\n\t\t\t\ttimestamp: Date.now(),\n\t\t\t\ttype: message.type,\n\t\t\t\tpersistent: true,\n\t\t\t\t...this.options.publishOptions,\n\t\t\t});\n\n\t\t\t// Handle backpressure\n\t\t\tif (!published) {\n\t\t\t\tawait new Promise((resolve) => channel.once('drain', resolve));\n\t\t\t}\n\t\t}\n\t}\n\n\tasync close(): Promise<void> {\n\t\t// Publisher doesn't own the connection\n\t\t// Connection should be closed by whoever created it\n\t}\n}\n","import type amqplib from 'amqplib';\nimport type { EventSubscriber, PublishableMessage } from '../types';\nimport { RabbitMQChannelUnavailable } from './errors';\nimport type { RabbitMQConnection } from './RabbitMQConnection';\n\nexport interface RabbitMQSubscriberOptions {\n\tqueueName?: string; // If not provided, a unique queue will be generated\n\tqueueOptions?: amqplib.Options.AssertQueue;\n\tconsumeOptions?: amqplib.Options.Consume;\n\tprefetch?: number; // Number of messages to prefetch (default: 1)\n}\n\nexport class RabbitMQSubscriber<\n\tTMessage extends PublishableMessage<string, any>,\n> implements EventSubscriber<TMessage>\n{\n\tconstructor(\n\t\tprivate connection: RabbitMQConnection,\n\t\tprivate options: RabbitMQSubscriberOptions = {},\n\t) {}\n\n\t/**\n\t * Create a RabbitMQSubscriber from a connection string\n\t * Format: rabbitmq://user:pass@host:port/vhost?exchange=name&type=topic&queueName=myqueue&prefetch=10\n\t */\n\tstatic async fromConnectionString<\n\t\tTMessage extends PublishableMessage<string, any>,\n\t>(connectionString: string): Promise<RabbitMQSubscriber<TMessage>> {\n\t\tconst url = new URL(connectionString);\n\t\tconst params = url.searchParams;\n\n\t\tconst { RabbitMQConnection } = await import('./RabbitMQConnection');\n\t\tconst connection =\n\t\t\tawait RabbitMQConnection.fromConnectionString(connectionString);\n\n\t\tconst options: RabbitMQSubscriberOptions = {\n\t\t\tqueueName: params.get('queueName') || undefined,\n\t\t\tprefetch: params.get('prefetch')\n\t\t\t\t? Number.parseInt(params.get('prefetch')!, 10)\n\t\t\t\t: undefined,\n\t\t};\n\n\t\treturn new RabbitMQSubscriber<TMessage>(connection, options);\n\t}\n\n\tasync subscribe(\n\t\tmessages: TMessage['type'][],\n\t\tlistener: (payload: TMessage) => Promise<void>,\n\t): Promise<void> {\n\t\tif (!this.connection.isConnected()) {\n\t\t\tawait this.connection.connect();\n\t\t}\n\n\t\tconst channel = this.connection.amqpChannel;\n\t\tif (!channel) {\n\t\t\tthrow new RabbitMQChannelUnavailable();\n\t\t}\n\n\t\tconst exchange = this.connection.exchangeName;\n\n\t\t// Set prefetch if specified\n\t\tif (this.options.prefetch) {\n\t\t\tawait channel.prefetch(this.options.prefetch);\n\t\t}\n\n\t\t// Assert queue\n\t\tconst queueName = this.options.queueName || '';\n\t\tconst queueOptions: amqplib.Options.AssertQueue = {\n\t\t\tdurable: true,\n\t\t\t...this.options.queueOptions,\n\t\t};\n\n\t\tconst { queue } = await channel.assertQueue(queueName, queueOptions);\n\n\t\t// Bind queue to exchange for each message type\n\t\tfor (const messageType of messages) {\n\t\t\tawait channel.bindQueue(queue, exchange, messageType);\n\t\t}\n\n\t\t// Consume messages\n\t\tawait channel.consume(\n\t\t\tqueue,\n\t\t\tasync (msg) => {\n\t\t\t\tif (!msg) return;\n\n\t\t\t\ttry {\n\t\t\t\t\t// Parse message payload\n\t\t\t\t\tconst content = msg.content.toString();\n\t\t\t\t\tconst payload = JSON.parse(content);\n\n\t\t\t\t\t// Get message type from properties\n\t\t\t\t\tconst messageType = msg.properties.type;\n\n\t\t\t\t\t// Reconstruct full message\n\t\t\t\t\tconst fullMessage: TMessage = {\n\t\t\t\t\t\ttype: messageType,\n\t\t\t\t\t\tpayload,\n\t\t\t\t\t} as TMessage;\n\n\t\t\t\t\t// Call listener\n\t\t\t\t\tawait listener(fullMessage);\n\n\t\t\t\t\t// Ack message\n\t\t\t\t\tchannel.ack(msg);\n\t\t\t\t} catch (_error) {\n\t\t\t\t\tchannel.nack(msg, false, true);\n\t\t\t\t}\n\t\t\t},\n\t\t\tthis.options.consumeOptions,\n\t\t);\n\t}\n}\n"],"mappings":"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"}
|
|
@@ -1,5 +1,11 @@
|
|
|
1
|
-
import { i as EventSubscriber, n as EventPublisher, r as EventPublisherType, s as PublishableMessage, t as EventConnection } from "../types-
|
|
1
|
+
import { i as EventSubscriber, n as EventPublisher, r as EventPublisherType, s as PublishableMessage, t as EventConnection } from "../types-gGaoT8qc.cjs";
|
|
2
2
|
import amqplib from "amqplib";
|
|
3
|
+
//#region src/rabbitmq/errors.d.ts
|
|
4
|
+
/** The connection opened but has no channel to publish or consume on. */
|
|
5
|
+
export declare class RabbitMQChannelUnavailable extends Error {
|
|
6
|
+
constructor();
|
|
7
|
+
}
|
|
8
|
+
//#endregion
|
|
3
9
|
//#region src/rabbitmq/RabbitMQConnection.d.ts
|
|
4
10
|
interface RabbitMQConnectionConfig {
|
|
5
11
|
url: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.cts","names":[],"sources":["../../src/rabbitmq/RabbitMQConnection.ts","../../src/rabbitmq/RabbitMQPublisher.ts","../../src/rabbitmq/RabbitMQSubscriber.ts"],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.d.cts","names":[],"sources":["../../src/rabbitmq/errors.ts","../../src/rabbitmq/RabbitMQConnection.ts","../../src/rabbitmq/RabbitMQPublisher.ts","../../src/rabbitmq/RabbitMQSubscriber.ts"],"mappings":";;;;qBAKa,mCAAmC;EAAA;;;;UCD/B;EAChB;EACA;EACA;EACA,kBAAkB,QAAQ,QAAQ;EAClC;;qBAGY,8BAA8B;WACjC,OAAI,mBAAA;UACL;UACA;UACA;UACA;EAKI,YAAA,QAAQ;;;;;SAiBP,qBACZ,2BACE,QAAQ;EAoBL,WAAW;EA6CX,SAAS;EAWf;UAIQ;;;;MASJ,eAAe,QAAQ;;;;MAOvB;;;;MAOA;;;;UC3IY;EAChB,iBAAiB,QAAQ,QAAQ;;qBAGrB,kBAAkB,iBAAiB,4CACpC,eAAe;UAGjB;UACA;EADA,YAAA,YAAY,oBACZ,UAAS;;;;;SAOL,qBACZ,iBAAiB,iCAChB,2BAA2B,QAAQ,kBAAkB;EAOjD,QAAQ,UAAU,aAAa;EAiC/B,SAAS;;;;UC1DC;EAChB;EACA,eAAe,QAAQ,QAAQ;EAC/B,iBAAiB,QAAQ,QAAQ;EACjC;;qBAGY,mBACZ,iBAAiB,4CACL,gBAAgB;UAGnB;UACA;EADA,YAAA,YAAY,oBACZ,UAAS;;;;;SAOL,qBACZ,iBAAiB,iCAChB,2BAA2B,QAAQ,mBAAmB;EAkBlD,UACL,UAAU,oBACV,WAAW,SAAS,aAAa,gBAC/B"}
|
|
@@ -1,5 +1,11 @@
|
|
|
1
|
-
import { i as EventSubscriber, n as EventPublisher, r as EventPublisherType, s as PublishableMessage, t as EventConnection } from "../types-
|
|
1
|
+
import { i as EventSubscriber, n as EventPublisher, r as EventPublisherType, s as PublishableMessage, t as EventConnection } from "../types-gGaoT8qc.mjs";
|
|
2
2
|
import amqplib from "amqplib";
|
|
3
|
+
//#region src/rabbitmq/errors.d.ts
|
|
4
|
+
/** The connection opened but has no channel to publish or consume on. */
|
|
5
|
+
export declare class RabbitMQChannelUnavailable extends Error {
|
|
6
|
+
constructor();
|
|
7
|
+
}
|
|
8
|
+
//#endregion
|
|
3
9
|
//#region src/rabbitmq/RabbitMQConnection.d.ts
|
|
4
10
|
interface RabbitMQConnectionConfig {
|
|
5
11
|
url: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.mts","names":[],"sources":["../../src/rabbitmq/RabbitMQConnection.ts","../../src/rabbitmq/RabbitMQPublisher.ts","../../src/rabbitmq/RabbitMQSubscriber.ts"],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.d.mts","names":[],"sources":["../../src/rabbitmq/errors.ts","../../src/rabbitmq/RabbitMQConnection.ts","../../src/rabbitmq/RabbitMQPublisher.ts","../../src/rabbitmq/RabbitMQSubscriber.ts"],"mappings":";;;;qBAKa,mCAAmC;EAAA;;;;UCD/B;EAChB;EACA;EACA;EACA,kBAAkB,QAAQ,QAAQ;EAClC;;qBAGY,8BAA8B;WACjC,OAAI,mBAAA;UACL;UACA;UACA;UACA;EAKI,YAAA,QAAQ;;;;;SAiBP,qBACZ,2BACE,QAAQ;EAoBL,WAAW;EA6CX,SAAS;EAWf;UAIQ;;;;MASJ,eAAe,QAAQ;;;;MAOvB;;;;MAOA;;;;UC3IY;EAChB,iBAAiB,QAAQ,QAAQ;;qBAGrB,kBAAkB,iBAAiB,4CACpC,eAAe;UAGjB;UACA;EADA,YAAA,YAAY,oBACZ,UAAS;;;;;SAOL,qBACZ,iBAAiB,iCAChB,2BAA2B,QAAQ,kBAAkB;EAOjD,QAAQ,UAAU,aAAa;EAiC/B,SAAS;;;;UC1DC;EAChB;EACA,eAAe,QAAQ,QAAQ;EAC/B,iBAAiB,QAAQ,QAAQ;EACjC;;qBAGY,mBACZ,iBAAiB,4CACL,gBAAgB;UAGnB;UACA;EADA,YAAA,YAAY,oBACZ,UAAS;;;;;SAOL,qBACZ,iBAAiB,iCAChB,2BAA2B,QAAQ,mBAAmB;EAkBlD,UACL,UAAU,oBACV,WAAW,SAAS,aAAa,gBAC/B"}
|
package/dist/rabbitmq/index.mjs
CHANGED
|
@@ -1,5 +1,14 @@
|
|
|
1
|
-
import "../types-
|
|
1
|
+
import "../types-Bk5JM6MP.mjs";
|
|
2
2
|
import amqplib from "amqplib";
|
|
3
|
+
//#region src/rabbitmq/errors.ts
|
|
4
|
+
/** The connection opened but has no channel to publish or consume on. */
|
|
5
|
+
var RabbitMQChannelUnavailable = class extends Error {
|
|
6
|
+
constructor() {
|
|
7
|
+
super("RabbitMQ connected without a channel. Check the broker is reachable and the connection string names a vhost the user can open channels on.");
|
|
8
|
+
this.name = "RabbitMQChannelUnavailable";
|
|
9
|
+
}
|
|
10
|
+
};
|
|
11
|
+
//#endregion
|
|
3
12
|
//#region src/rabbitmq/RabbitMQConnection.ts
|
|
4
13
|
var RabbitMQConnection = class RabbitMQConnection {
|
|
5
14
|
type = "rabbitmq";
|
|
@@ -121,7 +130,7 @@ var RabbitMQPublisher = class RabbitMQPublisher {
|
|
|
121
130
|
async publish(messages) {
|
|
122
131
|
if (!this.connection.isConnected()) await this.connection.connect();
|
|
123
132
|
const channel = this.connection.amqpChannel;
|
|
124
|
-
if (!channel) throw new
|
|
133
|
+
if (!channel) throw new RabbitMQChannelUnavailable();
|
|
125
134
|
const exchange = this.connection.exchangeName;
|
|
126
135
|
for (const message of messages) {
|
|
127
136
|
const content = Buffer.from(JSON.stringify(message.payload));
|
|
@@ -163,7 +172,7 @@ var RabbitMQSubscriber = class RabbitMQSubscriber {
|
|
|
163
172
|
async subscribe(messages, listener) {
|
|
164
173
|
if (!this.connection.isConnected()) await this.connection.connect();
|
|
165
174
|
const channel = this.connection.amqpChannel;
|
|
166
|
-
if (!channel) throw new
|
|
175
|
+
if (!channel) throw new RabbitMQChannelUnavailable();
|
|
167
176
|
const exchange = this.connection.exchangeName;
|
|
168
177
|
if (this.options.prefetch) await channel.prefetch(this.options.prefetch);
|
|
169
178
|
const queueName = this.options.queueName || "";
|
|
@@ -190,6 +199,6 @@ var RabbitMQSubscriber = class RabbitMQSubscriber {
|
|
|
190
199
|
}
|
|
191
200
|
};
|
|
192
201
|
//#endregion
|
|
193
|
-
export { RabbitMQConnection, RabbitMQPublisher, RabbitMQSubscriber };
|
|
202
|
+
export { RabbitMQChannelUnavailable, RabbitMQConnection, RabbitMQPublisher, RabbitMQSubscriber };
|
|
194
203
|
|
|
195
204
|
//# sourceMappingURL=index.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","names":[],"sources":["../../src/rabbitmq/RabbitMQConnection.ts","../../src/rabbitmq/RabbitMQPublisher.ts","../../src/rabbitmq/RabbitMQSubscriber.ts"],"sourcesContent":["import amqplib from 'amqplib';\nimport type { EventConnection } from '../types';\nimport { EventPublisherType } from '../types';\n\nexport interface RabbitMQConnectionConfig {\n\turl: string;\n\texchange?: string;\n\texchangeType?: 'topic' | 'direct' | 'fanout' | 'headers';\n\texchangeOptions?: amqplib.Options.AssertExchange;\n\ttimeout?: number; // Connection timeout in milliseconds (default: 5000)\n}\n\nexport class RabbitMQConnection implements EventConnection {\n\treadonly type = EventPublisherType.RabbitMQ;\n\tprivate connection?: amqplib.ChannelModel;\n\tprivate channel?: amqplib.Channel;\n\tprivate connecting?: Promise<void>;\n\tprivate config: Required<\n\t\tPick<RabbitMQConnectionConfig, 'exchange' | 'exchangeType' | 'timeout'>\n\t> &\n\t\tRabbitMQConnectionConfig;\n\n\tconstructor(config: RabbitMQConnectionConfig) {\n\t\tthis.config = {\n\t\t\t...config,\n\t\t\texchange: config.exchange || 'geekmidas.events',\n\t\t\texchangeType: config.exchangeType || 'topic',\n\t\t\ttimeout: config.timeout ?? 5000,\n\t\t\texchangeOptions: {\n\t\t\t\tdurable: true,\n\t\t\t\t...config.exchangeOptions,\n\t\t\t},\n\t\t};\n\t}\n\n\t/**\n\t * Create a RabbitMQConnection from a connection string\n\t * Format: rabbitmq://user:pass@host:port/vhost?exchange=name&type=topic&timeout=5000\n\t */\n\tstatic async fromConnectionString(\n\t\tconnectionString: string,\n\t): Promise<RabbitMQConnection> {\n\t\tconst url = new URL(connectionString);\n\t\tconst params = url.searchParams;\n\n\t\t// Extract connection URL without query params\n\t\tconst baseUrl = `amqp://${url.username ? `${url.username}:${url.password}@` : ''}${url.host}${url.pathname}`;\n\n\t\tconst timeoutParam = params.get('timeout');\n\t\tconst config: RabbitMQConnectionConfig = {\n\t\t\turl: baseUrl,\n\t\t\texchange: params.get('exchange') || 'geekmidas.events',\n\t\t\texchangeType: (params.get('type') as any) || 'topic',\n\t\t\ttimeout: timeoutParam ? Number.parseInt(timeoutParam, 10) : undefined,\n\t\t};\n\n\t\tconst connection = new RabbitMQConnection(config);\n\t\tawait connection.connect();\n\t\treturn connection;\n\t}\n\n\tasync connect(): Promise<void> {\n\t\tif (this.channel) return;\n\n\t\t// Prevent multiple simultaneous connection attempts\n\t\tif (this.connecting) {\n\t\t\tawait this.connecting;\n\t\t\treturn;\n\t\t}\n\n\t\tthis.connecting = (async () => {\n\t\t\ttry {\n\t\t\t\tconst connection = await amqplib.connect(this.config.url, {\n\t\t\t\t\ttimeout: this.config.timeout,\n\t\t\t\t});\n\t\t\t\tconst channel = await connection.createChannel();\n\n\t\t\t\t// Assert the exchange exists\n\t\t\t\tawait channel.assertExchange(\n\t\t\t\t\tthis.config.exchange!,\n\t\t\t\t\tthis.config.exchangeType!,\n\t\t\t\t\tthis.config.exchangeOptions,\n\t\t\t\t);\n\n\t\t\t\t// Handle connection errors\n\t\t\t\tconnection.on('error', (_err: Error) => {\n\t\t\t\t\tthis.cleanup();\n\t\t\t\t});\n\n\t\t\t\tconnection.on('close', () => {\n\t\t\t\t\tthis.cleanup();\n\t\t\t\t});\n\n\t\t\t\t// Only set after successful setup\n\t\t\t\tthis.connection = connection;\n\t\t\t\tthis.channel = channel;\n\t\t\t} catch (error) {\n\t\t\t\tthis.cleanup();\n\t\t\t\tthrow error;\n\t\t\t}\n\t\t})();\n\n\t\tawait this.connecting;\n\t\tthis.connecting = undefined;\n\t}\n\n\tasync close(): Promise<void> {\n\t\ttry {\n\t\t\tawait this.channel?.close();\n\t\t\tawait this.connection?.close();\n\t\t} catch (_error) {\n\t\t\t// Ignore errors during close\n\t\t} finally {\n\t\t\tthis.cleanup();\n\t\t}\n\t}\n\n\tisConnected(): boolean {\n\t\treturn !!this.channel;\n\t}\n\n\tprivate cleanup(): void {\n\t\tthis.channel = undefined;\n\t\tthis.connection = undefined;\n\t\tthis.connecting = undefined;\n\t}\n\n\t/**\n\t * Get the underlying channel for publishing/subscribing\n\t */\n\tget amqpChannel(): amqplib.Channel | undefined {\n\t\treturn this.channel;\n\t}\n\n\t/**\n\t * Get the exchange name for this connection\n\t */\n\tget exchangeName(): string {\n\t\treturn this.config.exchange;\n\t}\n\n\t/**\n\t * Get the exchange type for this connection\n\t */\n\tget exchangeType(): string {\n\t\treturn this.config.exchangeType;\n\t}\n}\n","import type amqplib from 'amqplib';\nimport type { EventPublisher, PublishableMessage } from '../types';\nimport type { RabbitMQConnection } from './RabbitMQConnection';\n\nexport interface RabbitMQPublisherOptions {\n\tpublishOptions?: amqplib.Options.Publish;\n}\n\nexport class RabbitMQPublisher<TMessage extends PublishableMessage<string, any>>\n\timplements EventPublisher<TMessage>\n{\n\tconstructor(\n\t\tprivate connection: RabbitMQConnection,\n\t\tprivate options: RabbitMQPublisherOptions = {},\n\t) {}\n\n\t/**\n\t * Create a RabbitMQPublisher from a connection string\n\t * Format: rabbitmq://user:pass@host:port/vhost?exchange=name&type=topic&timeout=5000\n\t */\n\tstatic async fromConnectionString<\n\t\tTMessage extends PublishableMessage<string, any>,\n\t>(connectionString: string): Promise<RabbitMQPublisher<TMessage>> {\n\t\tconst { RabbitMQConnection } = await import('./RabbitMQConnection');\n\t\tconst connection =\n\t\t\tawait RabbitMQConnection.fromConnectionString(connectionString);\n\t\treturn new RabbitMQPublisher<TMessage>(connection);\n\t}\n\n\tasync publish(messages: TMessage[]): Promise<void> {\n\t\tif (!this.connection.isConnected()) {\n\t\t\tawait this.connection.connect();\n\t\t}\n\n\t\tconst channel = this.connection.amqpChannel;\n\t\tif (!channel) {\n\t\t\tthrow new Error('Channel not initialized');\n\t\t}\n\n\t\tconst exchange = this.connection.exchangeName;\n\n\t\tfor (const message of messages) {\n\t\t\tconst content = Buffer.from(JSON.stringify(message.payload));\n\n\t\t\t// Use message type as routing key for topic exchanges\n\t\t\tconst routingKey = message.type;\n\n\t\t\tconst published = channel.publish(exchange, routingKey, content, {\n\t\t\t\tcontentType: 'application/json',\n\t\t\t\ttimestamp: Date.now(),\n\t\t\t\ttype: message.type,\n\t\t\t\tpersistent: true,\n\t\t\t\t...this.options.publishOptions,\n\t\t\t});\n\n\t\t\t// Handle backpressure\n\t\t\tif (!published) {\n\t\t\t\tawait new Promise((resolve) => channel.once('drain', resolve));\n\t\t\t}\n\t\t}\n\t}\n\n\tasync close(): Promise<void> {\n\t\t// Publisher doesn't own the connection\n\t\t// Connection should be closed by whoever created it\n\t}\n}\n","import type amqplib from 'amqplib';\nimport type { EventSubscriber, PublishableMessage } from '../types';\nimport type { RabbitMQConnection } from './RabbitMQConnection';\n\nexport interface RabbitMQSubscriberOptions {\n\tqueueName?: string; // If not provided, a unique queue will be generated\n\tqueueOptions?: amqplib.Options.AssertQueue;\n\tconsumeOptions?: amqplib.Options.Consume;\n\tprefetch?: number; // Number of messages to prefetch (default: 1)\n}\n\nexport class RabbitMQSubscriber<\n\tTMessage extends PublishableMessage<string, any>,\n> implements EventSubscriber<TMessage>\n{\n\tconstructor(\n\t\tprivate connection: RabbitMQConnection,\n\t\tprivate options: RabbitMQSubscriberOptions = {},\n\t) {}\n\n\t/**\n\t * Create a RabbitMQSubscriber from a connection string\n\t * Format: rabbitmq://user:pass@host:port/vhost?exchange=name&type=topic&queueName=myqueue&prefetch=10\n\t */\n\tstatic async fromConnectionString<\n\t\tTMessage extends PublishableMessage<string, any>,\n\t>(connectionString: string): Promise<RabbitMQSubscriber<TMessage>> {\n\t\tconst url = new URL(connectionString);\n\t\tconst params = url.searchParams;\n\n\t\tconst { RabbitMQConnection } = await import('./RabbitMQConnection');\n\t\tconst connection =\n\t\t\tawait RabbitMQConnection.fromConnectionString(connectionString);\n\n\t\tconst options: RabbitMQSubscriberOptions = {\n\t\t\tqueueName: params.get('queueName') || undefined,\n\t\t\tprefetch: params.get('prefetch')\n\t\t\t\t? Number.parseInt(params.get('prefetch')!, 10)\n\t\t\t\t: undefined,\n\t\t};\n\n\t\treturn new RabbitMQSubscriber<TMessage>(connection, options);\n\t}\n\n\tasync subscribe(\n\t\tmessages: TMessage['type'][],\n\t\tlistener: (payload: TMessage) => Promise<void>,\n\t): Promise<void> {\n\t\tif (!this.connection.isConnected()) {\n\t\t\tawait this.connection.connect();\n\t\t}\n\n\t\tconst channel = this.connection.amqpChannel;\n\t\tif (!channel) {\n\t\t\tthrow new Error('Channel not initialized');\n\t\t}\n\n\t\tconst exchange = this.connection.exchangeName;\n\n\t\t// Set prefetch if specified\n\t\tif (this.options.prefetch) {\n\t\t\tawait channel.prefetch(this.options.prefetch);\n\t\t}\n\n\t\t// Assert queue\n\t\tconst queueName = this.options.queueName || '';\n\t\tconst queueOptions: amqplib.Options.AssertQueue = {\n\t\t\tdurable: true,\n\t\t\t...this.options.queueOptions,\n\t\t};\n\n\t\tconst { queue } = await channel.assertQueue(queueName, queueOptions);\n\n\t\t// Bind queue to exchange for each message type\n\t\tfor (const messageType of messages) {\n\t\t\tawait channel.bindQueue(queue, exchange, messageType);\n\t\t}\n\n\t\t// Consume messages\n\t\tawait channel.consume(\n\t\t\tqueue,\n\t\t\tasync (msg) => {\n\t\t\t\tif (!msg) return;\n\n\t\t\t\ttry {\n\t\t\t\t\t// Parse message payload\n\t\t\t\t\tconst content = msg.content.toString();\n\t\t\t\t\tconst payload = JSON.parse(content);\n\n\t\t\t\t\t// Get message type from properties\n\t\t\t\t\tconst messageType = msg.properties.type;\n\n\t\t\t\t\t// Reconstruct full message\n\t\t\t\t\tconst fullMessage: TMessage = {\n\t\t\t\t\t\ttype: messageType,\n\t\t\t\t\t\tpayload,\n\t\t\t\t\t} as TMessage;\n\n\t\t\t\t\t// Call listener\n\t\t\t\t\tawait listener(fullMessage);\n\n\t\t\t\t\t// Ack message\n\t\t\t\t\tchannel.ack(msg);\n\t\t\t\t} catch (_error) {\n\t\t\t\t\tchannel.nack(msg, false, true);\n\t\t\t\t}\n\t\t\t},\n\t\t\tthis.options.consumeOptions,\n\t\t);\n\t}\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.mjs","names":[],"sources":["../../src/rabbitmq/errors.ts","../../src/rabbitmq/RabbitMQConnection.ts","../../src/rabbitmq/RabbitMQPublisher.ts","../../src/rabbitmq/RabbitMQSubscriber.ts"],"sourcesContent":["// Beside the adapter but apart from its connection module, which imports\n// the broker's client: the publisher and subscriber load that lazily, and\n// importing an error class from it would load it eagerly.\n\n/** The connection opened but has no channel to publish or consume on. */\nexport class RabbitMQChannelUnavailable extends Error {\n\tconstructor() {\n\t\tsuper(\n\t\t\t'RabbitMQ connected without a channel. Check the broker is reachable and the connection string names a vhost the user can open channels on.',\n\t\t);\n\t\tthis.name = 'RabbitMQChannelUnavailable';\n\t}\n}\n","import amqplib from 'amqplib';\nimport type { EventConnection } from '../types';\nimport { EventPublisherType } from '../types';\n\nexport interface RabbitMQConnectionConfig {\n\turl: string;\n\texchange?: string;\n\texchangeType?: 'topic' | 'direct' | 'fanout' | 'headers';\n\texchangeOptions?: amqplib.Options.AssertExchange;\n\ttimeout?: number; // Connection timeout in milliseconds (default: 5000)\n}\n\nexport class RabbitMQConnection implements EventConnection {\n\treadonly type = EventPublisherType.RabbitMQ;\n\tprivate connection?: amqplib.ChannelModel;\n\tprivate channel?: amqplib.Channel;\n\tprivate connecting?: Promise<void>;\n\tprivate config: Required<\n\t\tPick<RabbitMQConnectionConfig, 'exchange' | 'exchangeType' | 'timeout'>\n\t> &\n\t\tRabbitMQConnectionConfig;\n\n\tconstructor(config: RabbitMQConnectionConfig) {\n\t\tthis.config = {\n\t\t\t...config,\n\t\t\texchange: config.exchange || 'geekmidas.events',\n\t\t\texchangeType: config.exchangeType || 'topic',\n\t\t\ttimeout: config.timeout ?? 5000,\n\t\t\texchangeOptions: {\n\t\t\t\tdurable: true,\n\t\t\t\t...config.exchangeOptions,\n\t\t\t},\n\t\t};\n\t}\n\n\t/**\n\t * Create a RabbitMQConnection from a connection string\n\t * Format: rabbitmq://user:pass@host:port/vhost?exchange=name&type=topic&timeout=5000\n\t */\n\tstatic async fromConnectionString(\n\t\tconnectionString: string,\n\t): Promise<RabbitMQConnection> {\n\t\tconst url = new URL(connectionString);\n\t\tconst params = url.searchParams;\n\n\t\t// Extract connection URL without query params\n\t\tconst baseUrl = `amqp://${url.username ? `${url.username}:${url.password}@` : ''}${url.host}${url.pathname}`;\n\n\t\tconst timeoutParam = params.get('timeout');\n\t\tconst config: RabbitMQConnectionConfig = {\n\t\t\turl: baseUrl,\n\t\t\texchange: params.get('exchange') || 'geekmidas.events',\n\t\t\texchangeType: (params.get('type') as any) || 'topic',\n\t\t\ttimeout: timeoutParam ? Number.parseInt(timeoutParam, 10) : undefined,\n\t\t};\n\n\t\tconst connection = new RabbitMQConnection(config);\n\t\tawait connection.connect();\n\t\treturn connection;\n\t}\n\n\tasync connect(): Promise<void> {\n\t\tif (this.channel) return;\n\n\t\t// Prevent multiple simultaneous connection attempts\n\t\tif (this.connecting) {\n\t\t\tawait this.connecting;\n\t\t\treturn;\n\t\t}\n\n\t\tthis.connecting = (async () => {\n\t\t\ttry {\n\t\t\t\tconst connection = await amqplib.connect(this.config.url, {\n\t\t\t\t\ttimeout: this.config.timeout,\n\t\t\t\t});\n\t\t\t\tconst channel = await connection.createChannel();\n\n\t\t\t\t// Assert the exchange exists\n\t\t\t\tawait channel.assertExchange(\n\t\t\t\t\tthis.config.exchange!,\n\t\t\t\t\tthis.config.exchangeType!,\n\t\t\t\t\tthis.config.exchangeOptions,\n\t\t\t\t);\n\n\t\t\t\t// Handle connection errors\n\t\t\t\tconnection.on('error', (_err: Error) => {\n\t\t\t\t\tthis.cleanup();\n\t\t\t\t});\n\n\t\t\t\tconnection.on('close', () => {\n\t\t\t\t\tthis.cleanup();\n\t\t\t\t});\n\n\t\t\t\t// Only set after successful setup\n\t\t\t\tthis.connection = connection;\n\t\t\t\tthis.channel = channel;\n\t\t\t} catch (error) {\n\t\t\t\tthis.cleanup();\n\t\t\t\tthrow error;\n\t\t\t}\n\t\t})();\n\n\t\tawait this.connecting;\n\t\tthis.connecting = undefined;\n\t}\n\n\tasync close(): Promise<void> {\n\t\ttry {\n\t\t\tawait this.channel?.close();\n\t\t\tawait this.connection?.close();\n\t\t} catch (_error) {\n\t\t\t// Ignore errors during close\n\t\t} finally {\n\t\t\tthis.cleanup();\n\t\t}\n\t}\n\n\tisConnected(): boolean {\n\t\treturn !!this.channel;\n\t}\n\n\tprivate cleanup(): void {\n\t\tthis.channel = undefined;\n\t\tthis.connection = undefined;\n\t\tthis.connecting = undefined;\n\t}\n\n\t/**\n\t * Get the underlying channel for publishing/subscribing\n\t */\n\tget amqpChannel(): amqplib.Channel | undefined {\n\t\treturn this.channel;\n\t}\n\n\t/**\n\t * Get the exchange name for this connection\n\t */\n\tget exchangeName(): string {\n\t\treturn this.config.exchange;\n\t}\n\n\t/**\n\t * Get the exchange type for this connection\n\t */\n\tget exchangeType(): string {\n\t\treturn this.config.exchangeType;\n\t}\n}\n","import type amqplib from 'amqplib';\nimport type { EventPublisher, PublishableMessage } from '../types';\nimport { RabbitMQChannelUnavailable } from './errors';\nimport type { RabbitMQConnection } from './RabbitMQConnection';\n\nexport interface RabbitMQPublisherOptions {\n\tpublishOptions?: amqplib.Options.Publish;\n}\n\nexport class RabbitMQPublisher<TMessage extends PublishableMessage<string, any>>\n\timplements EventPublisher<TMessage>\n{\n\tconstructor(\n\t\tprivate connection: RabbitMQConnection,\n\t\tprivate options: RabbitMQPublisherOptions = {},\n\t) {}\n\n\t/**\n\t * Create a RabbitMQPublisher from a connection string\n\t * Format: rabbitmq://user:pass@host:port/vhost?exchange=name&type=topic&timeout=5000\n\t */\n\tstatic async fromConnectionString<\n\t\tTMessage extends PublishableMessage<string, any>,\n\t>(connectionString: string): Promise<RabbitMQPublisher<TMessage>> {\n\t\tconst { RabbitMQConnection } = await import('./RabbitMQConnection');\n\t\tconst connection =\n\t\t\tawait RabbitMQConnection.fromConnectionString(connectionString);\n\t\treturn new RabbitMQPublisher<TMessage>(connection);\n\t}\n\n\tasync publish(messages: TMessage[]): Promise<void> {\n\t\tif (!this.connection.isConnected()) {\n\t\t\tawait this.connection.connect();\n\t\t}\n\n\t\tconst channel = this.connection.amqpChannel;\n\t\tif (!channel) {\n\t\t\tthrow new RabbitMQChannelUnavailable();\n\t\t}\n\n\t\tconst exchange = this.connection.exchangeName;\n\n\t\tfor (const message of messages) {\n\t\t\tconst content = Buffer.from(JSON.stringify(message.payload));\n\n\t\t\t// Use message type as routing key for topic exchanges\n\t\t\tconst routingKey = message.type;\n\n\t\t\tconst published = channel.publish(exchange, routingKey, content, {\n\t\t\t\tcontentType: 'application/json',\n\t\t\t\ttimestamp: Date.now(),\n\t\t\t\ttype: message.type,\n\t\t\t\tpersistent: true,\n\t\t\t\t...this.options.publishOptions,\n\t\t\t});\n\n\t\t\t// Handle backpressure\n\t\t\tif (!published) {\n\t\t\t\tawait new Promise((resolve) => channel.once('drain', resolve));\n\t\t\t}\n\t\t}\n\t}\n\n\tasync close(): Promise<void> {\n\t\t// Publisher doesn't own the connection\n\t\t// Connection should be closed by whoever created it\n\t}\n}\n","import type amqplib from 'amqplib';\nimport type { EventSubscriber, PublishableMessage } from '../types';\nimport { RabbitMQChannelUnavailable } from './errors';\nimport type { RabbitMQConnection } from './RabbitMQConnection';\n\nexport interface RabbitMQSubscriberOptions {\n\tqueueName?: string; // If not provided, a unique queue will be generated\n\tqueueOptions?: amqplib.Options.AssertQueue;\n\tconsumeOptions?: amqplib.Options.Consume;\n\tprefetch?: number; // Number of messages to prefetch (default: 1)\n}\n\nexport class RabbitMQSubscriber<\n\tTMessage extends PublishableMessage<string, any>,\n> implements EventSubscriber<TMessage>\n{\n\tconstructor(\n\t\tprivate connection: RabbitMQConnection,\n\t\tprivate options: RabbitMQSubscriberOptions = {},\n\t) {}\n\n\t/**\n\t * Create a RabbitMQSubscriber from a connection string\n\t * Format: rabbitmq://user:pass@host:port/vhost?exchange=name&type=topic&queueName=myqueue&prefetch=10\n\t */\n\tstatic async fromConnectionString<\n\t\tTMessage extends PublishableMessage<string, any>,\n\t>(connectionString: string): Promise<RabbitMQSubscriber<TMessage>> {\n\t\tconst url = new URL(connectionString);\n\t\tconst params = url.searchParams;\n\n\t\tconst { RabbitMQConnection } = await import('./RabbitMQConnection');\n\t\tconst connection =\n\t\t\tawait RabbitMQConnection.fromConnectionString(connectionString);\n\n\t\tconst options: RabbitMQSubscriberOptions = {\n\t\t\tqueueName: params.get('queueName') || undefined,\n\t\t\tprefetch: params.get('prefetch')\n\t\t\t\t? Number.parseInt(params.get('prefetch')!, 10)\n\t\t\t\t: undefined,\n\t\t};\n\n\t\treturn new RabbitMQSubscriber<TMessage>(connection, options);\n\t}\n\n\tasync subscribe(\n\t\tmessages: TMessage['type'][],\n\t\tlistener: (payload: TMessage) => Promise<void>,\n\t): Promise<void> {\n\t\tif (!this.connection.isConnected()) {\n\t\t\tawait this.connection.connect();\n\t\t}\n\n\t\tconst channel = this.connection.amqpChannel;\n\t\tif (!channel) {\n\t\t\tthrow new RabbitMQChannelUnavailable();\n\t\t}\n\n\t\tconst exchange = this.connection.exchangeName;\n\n\t\t// Set prefetch if specified\n\t\tif (this.options.prefetch) {\n\t\t\tawait channel.prefetch(this.options.prefetch);\n\t\t}\n\n\t\t// Assert queue\n\t\tconst queueName = this.options.queueName || '';\n\t\tconst queueOptions: amqplib.Options.AssertQueue = {\n\t\t\tdurable: true,\n\t\t\t...this.options.queueOptions,\n\t\t};\n\n\t\tconst { queue } = await channel.assertQueue(queueName, queueOptions);\n\n\t\t// Bind queue to exchange for each message type\n\t\tfor (const messageType of messages) {\n\t\t\tawait channel.bindQueue(queue, exchange, messageType);\n\t\t}\n\n\t\t// Consume messages\n\t\tawait channel.consume(\n\t\t\tqueue,\n\t\t\tasync (msg) => {\n\t\t\t\tif (!msg) return;\n\n\t\t\t\ttry {\n\t\t\t\t\t// Parse message payload\n\t\t\t\t\tconst content = msg.content.toString();\n\t\t\t\t\tconst payload = JSON.parse(content);\n\n\t\t\t\t\t// Get message type from properties\n\t\t\t\t\tconst messageType = msg.properties.type;\n\n\t\t\t\t\t// Reconstruct full message\n\t\t\t\t\tconst fullMessage: TMessage = {\n\t\t\t\t\t\ttype: messageType,\n\t\t\t\t\t\tpayload,\n\t\t\t\t\t} as TMessage;\n\n\t\t\t\t\t// Call listener\n\t\t\t\t\tawait listener(fullMessage);\n\n\t\t\t\t\t// Ack message\n\t\t\t\t\tchannel.ack(msg);\n\t\t\t\t} catch (_error) {\n\t\t\t\t\tchannel.nack(msg, false, true);\n\t\t\t\t}\n\t\t\t},\n\t\t\tthis.options.consumeOptions,\n\t\t);\n\t}\n}\n"],"mappings":";;;;AAKA,IAAa,6BAAb,cAAgD,MAAM;CACrD,cAAc;EACb,MACC,4IACD;EACA,KAAK,OAAO;CACb;AACD;;;ACAA,IAAa,qBAAb,MAAa,mBAA8C;CAC1D,OAAS;CACT;CACA;CACA;CACA;CAKA,YAAY,QAAkC;EAC7C,KAAK,SAAS;GACb,GAAG;GACH,UAAU,OAAO,YAAY;GAC7B,cAAc,OAAO,gBAAgB;GACrC,SAAS,OAAO,WAAW;GAC3B,iBAAiB;IAChB,SAAS;IACT,GAAG,OAAO;GACX;EACD;CACD;;;;;CAMA,aAAa,qBACZ,kBAC8B;EAC9B,MAAM,MAAM,IAAI,IAAI,gBAAgB;EACpC,MAAM,SAAS,IAAI;EAGnB,MAAM,UAAU,UAAU,IAAI,WAAW,GAAG,IAAI,SAAS,GAAG,IAAI,SAAS,KAAK,KAAK,IAAI,OAAO,IAAI;EAElG,MAAM,eAAe,OAAO,IAAI,SAAS;EACzC,MAAM,SAAmC;GACxC,KAAK;GACL,UAAU,OAAO,IAAI,UAAU,KAAK;GACpC,cAAe,OAAO,IAAI,MAAM,KAAa;GAC7C,SAAS,eAAe,OAAO,SAAS,cAAc,EAAE,IAAI,KAAA;EAC7D;EAEA,MAAM,aAAa,IAAI,mBAAmB,MAAM;EAChD,MAAM,WAAW,QAAQ;EACzB,OAAO;CACR;CAEA,MAAM,UAAyB;EAC9B,IAAI,KAAK,SAAS;EAGlB,IAAI,KAAK,YAAY;GACpB,MAAM,KAAK;GACX;EACD;EAEA,KAAK,cAAc,YAAY;GAC9B,IAAI;IACH,MAAM,aAAa,MAAM,QAAQ,QAAQ,KAAK,OAAO,KAAK,EACzD,SAAS,KAAK,OAAO,QACtB,CAAC;IACD,MAAM,UAAU,MAAM,WAAW,cAAc;IAG/C,MAAM,QAAQ,eACb,KAAK,OAAO,UACZ,KAAK,OAAO,cACZ,KAAK,OAAO,eACb;IAGA,WAAW,GAAG,UAAU,SAAgB;KACvC,KAAK,QAAQ;IACd,CAAC;IAED,WAAW,GAAG,eAAe;KAC5B,KAAK,QAAQ;IACd,CAAC;IAGD,KAAK,aAAa;IAClB,KAAK,UAAU;GAChB,SAAS,OAAO;IACf,KAAK,QAAQ;IACb,MAAM;GACP;EACD,EAAA,CAAG;EAEH,MAAM,KAAK;EACX,KAAK,aAAa,KAAA;CACnB;CAEA,MAAM,QAAuB;EAC5B,IAAI;GACH,MAAM,KAAK,SAAS,MAAM;GAC1B,MAAM,KAAK,YAAY,MAAM;EAC9B,SAAS,QAAQ,CAEjB,UAAU;GACT,KAAK,QAAQ;EACd;CACD;CAEA,cAAuB;EACtB,OAAO,CAAC,CAAC,KAAK;CACf;CAEA,UAAwB;EACvB,KAAK,UAAU,KAAA;EACf,KAAK,aAAa,KAAA;EAClB,KAAK,aAAa,KAAA;CACnB;;;;CAKA,IAAI,cAA2C;EAC9C,OAAO,KAAK;CACb;;;;CAKA,IAAI,eAAuB;EAC1B,OAAO,KAAK,OAAO;CACpB;;;;CAKA,IAAI,eAAuB;EAC1B,OAAO,KAAK,OAAO;CACpB;AACD;;;AC1IA,IAAa,oBAAb,MAAa,kBAEb;CAEU;CACA;CAFT,YACC,YACA,UAA4C,CAAC,GAC5C;EAFO,KAAA,aAAA;EACA,KAAA,UAAA;CACN;;;;;CAMH,aAAa,qBAEX,kBAAgE;EACjE,MAAM,EAAE,uBAAuB,MAAM,OAAO;EAC5C,MAAM,aACL,MAAM,mBAAmB,qBAAqB,gBAAgB;EAC/D,OAAO,IAAI,kBAA4B,UAAU;CAClD;CAEA,MAAM,QAAQ,UAAqC;EAClD,IAAI,CAAC,KAAK,WAAW,YAAY,GAChC,MAAM,KAAK,WAAW,QAAQ;EAG/B,MAAM,UAAU,KAAK,WAAW;EAChC,IAAI,CAAC,SACJ,MAAM,IAAI,2BAA2B;EAGtC,MAAM,WAAW,KAAK,WAAW;EAEjC,KAAK,MAAM,WAAW,UAAU;GAC/B,MAAM,UAAU,OAAO,KAAK,KAAK,UAAU,QAAQ,OAAO,CAAC;GAG3D,MAAM,aAAa,QAAQ;GAW3B,IAAI,CATc,QAAQ,QAAQ,UAAU,YAAY,SAAS;IAChE,aAAa;IACb,WAAW,KAAK,IAAI;IACpB,MAAM,QAAQ;IACd,YAAY;IACZ,GAAG,KAAK,QAAQ;GACjB,CAGa,GACZ,MAAM,IAAI,SAAS,YAAY,QAAQ,KAAK,SAAS,OAAO,CAAC;EAE/D;CACD;CAEA,MAAM,QAAuB,CAG7B;AACD;;;ACvDA,IAAa,qBAAb,MAAa,mBAGb;CAEU;CACA;CAFT,YACC,YACA,UAA6C,CAAC,GAC7C;EAFO,KAAA,aAAA;EACA,KAAA,UAAA;CACN;;;;;CAMH,aAAa,qBAEX,kBAAiE;EAElE,MAAM,SAAS,IADC,IAAI,gBACH,CAAC,CAAC;EAEnB,MAAM,EAAE,uBAAuB,MAAM,OAAO;EAC5C,MAAM,aACL,MAAM,mBAAmB,qBAAqB,gBAAgB;EAE/D,MAAM,UAAqC;GAC1C,WAAW,OAAO,IAAI,WAAW,KAAK,KAAA;GACtC,UAAU,OAAO,IAAI,UAAU,IAC5B,OAAO,SAAS,OAAO,IAAI,UAAU,GAAI,EAAE,IAC3C,KAAA;EACJ;EAEA,OAAO,IAAI,mBAA6B,YAAY,OAAO;CAC5D;CAEA,MAAM,UACL,UACA,UACgB;EAChB,IAAI,CAAC,KAAK,WAAW,YAAY,GAChC,MAAM,KAAK,WAAW,QAAQ;EAG/B,MAAM,UAAU,KAAK,WAAW;EAChC,IAAI,CAAC,SACJ,MAAM,IAAI,2BAA2B;EAGtC,MAAM,WAAW,KAAK,WAAW;EAGjC,IAAI,KAAK,QAAQ,UAChB,MAAM,QAAQ,SAAS,KAAK,QAAQ,QAAQ;EAI7C,MAAM,YAAY,KAAK,QAAQ,aAAa;EAC5C,MAAM,eAA4C;GACjD,SAAS;GACT,GAAG,KAAK,QAAQ;EACjB;EAEA,MAAM,EAAE,UAAU,MAAM,QAAQ,YAAY,WAAW,YAAY;EAGnE,KAAK,MAAM,eAAe,UACzB,MAAM,QAAQ,UAAU,OAAO,UAAU,WAAW;EAIrD,MAAM,QAAQ,QACb,OACA,OAAO,QAAQ;GACd,IAAI,CAAC,KAAK;GAEV,IAAI;IAEH,MAAM,UAAU,IAAI,QAAQ,SAAS;IACrC,MAAM,UAAU,KAAK,MAAM,OAAO;IAYlC,MAAM,SAAS;KALd,MAJmB,IAAI,WAAW;KAKlC;IAIwB,CAAC;IAG1B,QAAQ,IAAI,GAAG;GAChB,SAAS,QAAQ;IAChB,QAAQ,KAAK,KAAK,OAAO,IAAI;GAC9B;EACD,GACA,KAAK,QAAQ,cACd;CACD;AACD"}
|
package/dist/sns/index.cjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
-
require("../types-
|
|
3
|
-
const require_SQSSubscriber = require("../SQSSubscriber-
|
|
2
|
+
require("../types-BcZjLxVV.cjs");
|
|
3
|
+
const require_SQSSubscriber = require("../SQSSubscriber-DtRUaZB3.cjs");
|
|
4
4
|
const require_sns_snsUrl = require("./snsUrl.cjs");
|
|
5
5
|
let _aws_sdk_client_sqs = require("@aws-sdk/client-sqs");
|
|
6
6
|
let _aws_sdk_client_sns = require("@aws-sdk/client-sns");
|
|
@@ -197,7 +197,7 @@ var SNSSubscriber = class SNSSubscriber {
|
|
|
197
197
|
const createCommand = new _aws_sdk_client_sqs.CreateQueueCommand({ QueueName: this.options.queueName });
|
|
198
198
|
const response = await this.sqsClient.send(createCommand);
|
|
199
199
|
this.queueUrl = response.QueueUrl;
|
|
200
|
-
} else throw new
|
|
200
|
+
} else throw new SnsQueueMissing(this.options.queueName);
|
|
201
201
|
const attrsCommand = new _aws_sdk_client_sqs.GetQueueAttributesCommand({
|
|
202
202
|
QueueUrl: this.queueUrl,
|
|
203
203
|
AttributeNames: ["QueueArn"]
|
|
@@ -233,11 +233,24 @@ var SNSSubscriber = class SNSSubscriber {
|
|
|
233
233
|
this.subscriptionArn = response.SubscriptionArn;
|
|
234
234
|
}
|
|
235
235
|
};
|
|
236
|
+
/**
|
|
237
|
+
* A subscriber asked to use a queue it may not create. The SQS queue behind an
|
|
238
|
+
* SNS subscription has to exist first, or `createQueue` has to allow it.
|
|
239
|
+
*/
|
|
240
|
+
var SnsQueueMissing = class extends Error {
|
|
241
|
+
queueName;
|
|
242
|
+
constructor(queueName) {
|
|
243
|
+
super(`The SNS subscriber's queue${queueName ? ` "${queueName}"` : ""} is not created here: pass createQueue: true, or create the queue first.`);
|
|
244
|
+
this.queueName = queueName;
|
|
245
|
+
this.name = "SnsQueueMissing";
|
|
246
|
+
}
|
|
247
|
+
};
|
|
236
248
|
//#endregion
|
|
237
249
|
exports.SNSConnection = SNSConnection;
|
|
238
250
|
exports.SNSConnection$1 = SNSConnection;
|
|
239
251
|
exports.SNSPublisher = SNSPublisher;
|
|
240
252
|
exports.SNSSubscriber = SNSSubscriber;
|
|
253
|
+
exports.SnsQueueMissing = SnsQueueMissing;
|
|
241
254
|
Object.defineProperty(exports, "snsUrl", {
|
|
242
255
|
enumerable: true,
|
|
243
256
|
get: function() {
|
package/dist/sns/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.cjs","names":["SNSClient","PublishCommand","SQSClient","SQSConnection","SQSSubscriber","UnsubscribeCommand","DeleteQueueCommand","CreateQueueCommand","GetQueueAttributesCommand","SetQueueAttributesCommand","SubscribeCommand"],"sources":["../../src/sns/SNSConnection.ts","../../src/sns/SNSPublisher.ts","../../src/sns/SNSSubscriber.ts"],"sourcesContent":["import { SNSClient, type SNSClientConfig } from '@aws-sdk/client-sns';\nimport type { EventConnection } from '../types';\nimport { EventPublisherType } from '../types';\nimport * as snsUrl from './snsUrl';\n\nexport interface SNSConnectionConfig {\n\ttopicArn: string;\n\tregion?: string;\n\tendpoint?: string; // Custom endpoint (e.g., for LocalStack)\n\tcredentials?: {\n\t\taccessKeyId: string;\n\t\tsecretAccessKey: string;\n\t\tsessionToken?: string;\n\t};\n}\n\nexport class SNSConnection implements EventConnection {\n\treadonly type = EventPublisherType.SNS;\n\tprivate client: SNSClient;\n\tprivate connected = false;\n\tprivate config: SNSConnectionConfig;\n\n\tconstructor(config: SNSConnectionConfig) {\n\t\tthis.config = config;\n\n\t\tconst clientConfig: SNSClientConfig = {\n\t\t\tregion: config.region,\n\t\t\tendpoint: config.endpoint,\n\t\t\tcredentials: config.credentials,\n\t\t};\n\n\t\tthis.client = new SNSClient(clientConfig);\n\t}\n\n\t/**\n\t * Create an SNSConnection from a connection string\n\t * Format: sns://?topicArn=arn:aws:sns:region:account:topic®ion=us-east-1&endpoint=http://localhost:4566\n\t */\n\tstatic async fromConnectionString(\n\t\tconnectionString: string,\n\t): Promise<SNSConnection> {\n\t\t// Through the codec, not a second hand-rolled parse: the deploy target\n\t\t// composes these with `snsUrl.build`, and a string it cannot read back is\n\t\t// exactly the drift one shared module rules out.\n\t\tconst { topicArn, region, endpoint } = snsUrl.parse(connectionString);\n\t\tconst params = new URL(connectionString).searchParams;\n\n\t\tconst config: SNSConnectionConfig = {\n\t\t\ttopicArn,\n\t\t\tregion,\n\t\t\tendpoint,\n\t\t};\n\n\t\t// Credentials are not part of the address — see `snsUrl` — but a caller\n\t\t// may still pin them here, so they are read beside it rather than in it.\n\t\tconst accessKeyId = params.get('accessKeyId');\n\t\tconst secretAccessKey = params.get('secretAccessKey');\n\t\tif (accessKeyId && secretAccessKey) {\n\t\t\tconfig.credentials = {\n\t\t\t\taccessKeyId,\n\t\t\t\tsecretAccessKey,\n\t\t\t\tsessionToken: params.get('sessionToken') || undefined,\n\t\t\t};\n\t\t}\n\n\t\tconst connection = new SNSConnection(config);\n\t\tawait connection.connect();\n\t\treturn connection;\n\t}\n\n\tasync connect(): Promise<void> {\n\t\t// SNS doesn't have a traditional connection, but we mark as connected\n\t\tthis.connected = true;\n\t}\n\n\tasync close(): Promise<void> {\n\t\tthis.client.destroy();\n\t\tthis.connected = false;\n\t}\n\n\tisConnected(): boolean {\n\t\treturn this.connected;\n\t}\n\n\t/**\n\t * Get the underlying SNS client\n\t */\n\tget snsClient(): SNSClient {\n\t\treturn this.client;\n\t}\n\n\t/**\n\t * Get the topic ARN for this connection\n\t */\n\tget topicArn(): string {\n\t\treturn this.config.topicArn;\n\t}\n}\n","import { PublishCommand } from '@aws-sdk/client-sns';\nimport type { EventPublisher, PublishableMessage } from '../types';\nimport type { SNSConnection } from './SNSConnection';\n\nexport type SNSPublisherOptions = {};\n\nexport class SNSPublisher<TMessage extends PublishableMessage<string, any>>\n\timplements EventPublisher<TMessage>\n{\n\tconstructor(\n\t\tprivate connection: SNSConnection,\n\t\t_options: SNSPublisherOptions = {},\n\t) {}\n\n\t/**\n\t * Create an SNSPublisher from a connection string\n\t * Format: sns://?topicArn=arn:aws:sns:region:account:topic®ion=us-east-1&endpoint=http://localhost:4566\n\t */\n\tstatic async fromConnectionString<\n\t\tTMessage extends PublishableMessage<string, any>,\n\t>(connectionString: string): Promise<SNSPublisher<TMessage>> {\n\t\tconst { SNSConnection } = await import('./SNSConnection');\n\t\tconst connection =\n\t\t\tawait SNSConnection.fromConnectionString(connectionString);\n\n\t\treturn new SNSPublisher<TMessage>(connection);\n\t}\n\n\tasync publish(messages: TMessage[]): Promise<void> {\n\t\tif (messages.length === 0) return;\n\n\t\tif (!this.connection.isConnected()) {\n\t\t\tawait this.connection.connect();\n\t\t}\n\n\t\t// Publish all messages\n\t\tawait Promise.all(messages.map((message) => this.publishMessage(message)));\n\t}\n\n\tprivate async publishMessage(message: TMessage): Promise<void> {\n\t\tconst command = new PublishCommand({\n\t\t\tTopicArn: this.connection.topicArn,\n\t\t\tMessage: JSON.stringify({\n\t\t\t\ttype: message.type,\n\t\t\t\tpayload: message.payload,\n\t\t\t}),\n\t\t\tMessageAttributes: {\n\t\t\t\ttype: {\n\t\t\t\t\tDataType: 'String',\n\t\t\t\t\tStringValue: message.type,\n\t\t\t\t},\n\t\t\t},\n\t\t});\n\n\t\tawait this.connection.snsClient.send(command);\n\t}\n\n\tasync close(): Promise<void> {\n\t\t// Publisher doesn't own the connection\n\t\t// Connection should be closed by whoever created it\n\t}\n}\n","import { SubscribeCommand, UnsubscribeCommand } from '@aws-sdk/client-sns';\nimport {\n\tCreateQueueCommand,\n\tDeleteQueueCommand,\n\tGetQueueAttributesCommand,\n\tSetQueueAttributesCommand,\n\tSQSClient,\n} from '@aws-sdk/client-sqs';\nimport { SQSConnection } from '../sqs/SQSConnection';\nimport { SQSSubscriber } from '../sqs/SQSSubscriber';\nimport type { EventSubscriber, PublishableMessage } from '../types';\nimport type { SNSConnection } from './SNSConnection';\n\nexport interface SNSSubscriberOptions {\n\tqueueName?: string; // Optional queue name (defaults to auto-generated)\n\tcreateQueue?: boolean; // Auto-create SQS queue (default: true)\n\tdeleteQueueOnClose?: boolean; // Delete queue when subscriber closes (default: false for named queues, true for auto-generated)\n\twaitTimeSeconds?: number; // SQS long polling (default: 20)\n\tmaxMessages?: number; // Max messages per poll (default: 10)\n}\n\n/**\n * SNS Subscriber - receives push-based messages from SNS via SQS queue\n *\n * Architecture:\n * SNS Topic → (push) → SQS Queue → Subscriber\n *\n * This subscriber automatically:\n * 1. Creates an SQS queue (or uses existing one)\n * 2. Subscribes the queue to the SNS topic\n * 3. Receives messages pushed from SNS to the queue\n * 4. Filters messages by topic ARN and message type\n * 5. Processes and acknowledges messages\n *\n * Note: While SQS long-polling is used internally to receive messages,\n * this is an implementation detail of the SNS push delivery mechanism.\n * Messages are pushed from SNS to SQS, and we receive those pushed messages.\n */\nexport class SNSSubscriber<TMessage extends PublishableMessage<string, any>>\n\timplements EventSubscriber<TMessage>\n{\n\tprivate sqsClient: SQSClient;\n\tprivate queueUrl?: string;\n\tprivate queueArn?: string;\n\tprivate subscriptionArn?: string;\n\tprivate sqsSubscriber?: SQSSubscriber<TMessage>;\n\tprivate options: Required<SNSSubscriberOptions>;\n\n\tconstructor(\n\t\tprivate connection: SNSConnection,\n\t\toptions: SNSSubscriberOptions = {},\n\t) {\n\t\tthis.options = {\n\t\t\tqueueName:\n\t\t\t\toptions.queueName ||\n\t\t\t\t`sns-sub-${Date.now()}-${Math.random().toString(36).substring(7)}`,\n\t\t\tcreateQueue: options.createQueue ?? true,\n\t\t\tdeleteQueueOnClose: options.deleteQueueOnClose ?? !options.queueName, // Delete auto-generated queues\n\t\t\twaitTimeSeconds: options.waitTimeSeconds ?? 20,\n\t\t\tmaxMessages: options.maxMessages ?? 10,\n\t\t};\n\n\t\t// Create SQS client with same config as SNS\n\t\tthis.sqsClient = new SQSClient({\n\t\t\tregion: (this.connection as any).config.region,\n\t\t\tendpoint: (this.connection as any).config.endpoint,\n\t\t\tcredentials: (this.connection as any).config.credentials,\n\t\t});\n\t}\n\n\t/**\n\t * Create an SNSSubscriber from a connection string\n\t * Format: sns://?topicArn=...&queueName=myqueue®ion=us-east-1\n\t */\n\tstatic async fromConnectionString<\n\t\tTMessage extends PublishableMessage<string, any>,\n\t>(connectionString: string): Promise<SNSSubscriber<TMessage>> {\n\t\tconst url = new URL(connectionString);\n\t\tconst params = url.searchParams;\n\n\t\tconst { SNSConnection } = await import('./SNSConnection');\n\t\tconst connection =\n\t\t\tawait SNSConnection.fromConnectionString(connectionString);\n\n\t\tconst options: SNSSubscriberOptions = {\n\t\t\tqueueName: params.get('queueName') || undefined,\n\t\t\tcreateQueue: params.get('createQueue') !== 'false',\n\t\t\tdeleteQueueOnClose: params.get('deleteQueueOnClose') === 'true',\n\t\t\twaitTimeSeconds: params.get('waitTimeSeconds')\n\t\t\t\t? Number.parseInt(params.get('waitTimeSeconds')!, 10)\n\t\t\t\t: undefined,\n\t\t};\n\n\t\treturn new SNSSubscriber<TMessage>(connection, options);\n\t}\n\n\tasync subscribe(\n\t\tmessages: TMessage['type'][],\n\t\tlistener: (payload: TMessage) => Promise<void>,\n\t): Promise<void> {\n\t\t// Setup SQS queue and SNS subscription\n\t\tawait this.setupQueue();\n\t\tawait this.subscribeToTopic();\n\n\t\t// Create SQS connection for the subscriber\n\t\tconst sqsConnection = new SQSConnection({\n\t\t\tqueueUrl: this.queueUrl!,\n\t\t\tregion: (this.connection as any).config.region,\n\t\t\tendpoint: (this.connection as any).config.endpoint,\n\t\t\tcredentials: (this.connection as any).config.credentials,\n\t\t});\n\t\tawait sqsConnection.connect();\n\n\t\t// Use SQSSubscriber to receive messages, with topic filtering\n\t\tthis.sqsSubscriber = new SQSSubscriber<TMessage>(sqsConnection, {\n\t\t\twaitTimeSeconds: this.options.waitTimeSeconds,\n\t\t\tmaxMessages: this.options.maxMessages,\n\t\t\texpectedTopicArn: this.connection.topicArn,\n\t\t});\n\n\t\tawait this.sqsSubscriber.subscribe(messages, listener);\n\t}\n\n\t/**\n\t * Stop receiving messages and clean up resources\n\t */\n\tasync stop(): Promise<void> {\n\t\t// Stop SQS subscriber\n\t\tif (this.sqsSubscriber) {\n\t\t\tthis.sqsSubscriber.stop();\n\t\t}\n\n\t\t// Unsubscribe from topic\n\t\tif (this.subscriptionArn) {\n\t\t\ttry {\n\t\t\t\tconst command = new UnsubscribeCommand({\n\t\t\t\t\tSubscriptionArn: this.subscriptionArn,\n\t\t\t\t});\n\t\t\t\tawait this.connection.snsClient.send(command);\n\t\t\t} catch (_error) {}\n\t\t}\n\n\t\t// Delete queue if configured\n\t\tif (this.queueUrl && this.options.deleteQueueOnClose) {\n\t\t\ttry {\n\t\t\t\tconst command = new DeleteQueueCommand({\n\t\t\t\t\tQueueUrl: this.queueUrl,\n\t\t\t\t});\n\t\t\t\tawait this.sqsClient.send(command);\n\t\t\t} catch (_error) {}\n\t\t}\n\n\t\tthis.sqsClient.destroy();\n\t}\n\n\tprivate async setupQueue(): Promise<void> {\n\t\tif (this.options.createQueue) {\n\t\t\t// Create queue\n\t\t\tconst createCommand = new CreateQueueCommand({\n\t\t\t\tQueueName: this.options.queueName,\n\t\t\t});\n\t\t\tconst response = await this.sqsClient.send(createCommand);\n\t\t\tthis.queueUrl = response.QueueUrl!;\n\t\t} else {\n\t\t\tthrow new Error('Queue must exist or createQueue option must be true');\n\t\t}\n\n\t\t// Get queue ARN\n\t\tconst attrsCommand = new GetQueueAttributesCommand({\n\t\t\tQueueUrl: this.queueUrl,\n\t\t\tAttributeNames: ['QueueArn'],\n\t\t});\n\t\tconst attrsResponse = await this.sqsClient.send(attrsCommand);\n\t\tthis.queueArn = attrsResponse.Attributes?.QueueArn!;\n\n\t\t// Set queue policy to allow SNS\n\t\tawait this.setQueuePolicy();\n\t}\n\n\tprivate async setQueuePolicy(): Promise<void> {\n\t\tconst policy = {\n\t\t\tVersion: '2012-10-17',\n\t\t\tStatement: [\n\t\t\t\t{\n\t\t\t\t\tEffect: 'Allow',\n\t\t\t\t\tPrincipal: '*',\n\t\t\t\t\tAction: 'SQS:SendMessage',\n\t\t\t\t\tResource: this.queueArn,\n\t\t\t\t\tCondition: {\n\t\t\t\t\t\tArnEquals: {\n\t\t\t\t\t\t\t'aws:SourceArn': this.connection.topicArn,\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t],\n\t\t};\n\n\t\tconst command = new SetQueueAttributesCommand({\n\t\t\tQueueUrl: this.queueUrl,\n\t\t\tAttributes: {\n\t\t\t\tPolicy: JSON.stringify(policy),\n\t\t\t},\n\t\t});\n\n\t\tawait this.sqsClient.send(command);\n\t}\n\n\tprivate async subscribeToTopic(): Promise<void> {\n\t\tconst command = new SubscribeCommand({\n\t\t\tTopicArn: this.connection.topicArn,\n\t\t\tProtocol: 'sqs',\n\t\t\tEndpoint: this.queueArn,\n\t\t});\n\n\t\tconst response = await this.connection.snsClient.send(command);\n\t\tthis.subscriptionArn = response.SubscriptionArn!;\n\t}\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.cjs","names":["SNSClient","PublishCommand","SQSClient","SQSConnection","SQSSubscriber","UnsubscribeCommand","DeleteQueueCommand","CreateQueueCommand","GetQueueAttributesCommand","SetQueueAttributesCommand","SubscribeCommand"],"sources":["../../src/sns/SNSConnection.ts","../../src/sns/SNSPublisher.ts","../../src/sns/SNSSubscriber.ts"],"sourcesContent":["import { SNSClient, type SNSClientConfig } from '@aws-sdk/client-sns';\nimport type { EventConnection } from '../types';\nimport { EventPublisherType } from '../types';\nimport * as snsUrl from './snsUrl';\n\nexport interface SNSConnectionConfig {\n\ttopicArn: string;\n\tregion?: string;\n\tendpoint?: string; // Custom endpoint (e.g., for LocalStack)\n\tcredentials?: {\n\t\taccessKeyId: string;\n\t\tsecretAccessKey: string;\n\t\tsessionToken?: string;\n\t};\n}\n\nexport class SNSConnection implements EventConnection {\n\treadonly type = EventPublisherType.SNS;\n\tprivate client: SNSClient;\n\tprivate connected = false;\n\tprivate config: SNSConnectionConfig;\n\n\tconstructor(config: SNSConnectionConfig) {\n\t\tthis.config = config;\n\n\t\tconst clientConfig: SNSClientConfig = {\n\t\t\tregion: config.region,\n\t\t\tendpoint: config.endpoint,\n\t\t\tcredentials: config.credentials,\n\t\t};\n\n\t\tthis.client = new SNSClient(clientConfig);\n\t}\n\n\t/**\n\t * Create an SNSConnection from a connection string\n\t * Format: sns://?topicArn=arn:aws:sns:region:account:topic®ion=us-east-1&endpoint=http://localhost:4566\n\t */\n\tstatic async fromConnectionString(\n\t\tconnectionString: string,\n\t): Promise<SNSConnection> {\n\t\t// Through the codec, not a second hand-rolled parse: the deploy target\n\t\t// composes these with `snsUrl.build`, and a string it cannot read back is\n\t\t// exactly the drift one shared module rules out.\n\t\tconst { topicArn, region, endpoint } = snsUrl.parse(connectionString);\n\t\tconst params = new URL(connectionString).searchParams;\n\n\t\tconst config: SNSConnectionConfig = {\n\t\t\ttopicArn,\n\t\t\tregion,\n\t\t\tendpoint,\n\t\t};\n\n\t\t// Credentials are not part of the address — see `snsUrl` — but a caller\n\t\t// may still pin them here, so they are read beside it rather than in it.\n\t\tconst accessKeyId = params.get('accessKeyId');\n\t\tconst secretAccessKey = params.get('secretAccessKey');\n\t\tif (accessKeyId && secretAccessKey) {\n\t\t\tconfig.credentials = {\n\t\t\t\taccessKeyId,\n\t\t\t\tsecretAccessKey,\n\t\t\t\tsessionToken: params.get('sessionToken') || undefined,\n\t\t\t};\n\t\t}\n\n\t\tconst connection = new SNSConnection(config);\n\t\tawait connection.connect();\n\t\treturn connection;\n\t}\n\n\tasync connect(): Promise<void> {\n\t\t// SNS doesn't have a traditional connection, but we mark as connected\n\t\tthis.connected = true;\n\t}\n\n\tasync close(): Promise<void> {\n\t\tthis.client.destroy();\n\t\tthis.connected = false;\n\t}\n\n\tisConnected(): boolean {\n\t\treturn this.connected;\n\t}\n\n\t/**\n\t * Get the underlying SNS client\n\t */\n\tget snsClient(): SNSClient {\n\t\treturn this.client;\n\t}\n\n\t/**\n\t * Get the topic ARN for this connection\n\t */\n\tget topicArn(): string {\n\t\treturn this.config.topicArn;\n\t}\n}\n","import { PublishCommand } from '@aws-sdk/client-sns';\nimport type { EventPublisher, PublishableMessage } from '../types';\nimport type { SNSConnection } from './SNSConnection';\n\nexport type SNSPublisherOptions = {};\n\nexport class SNSPublisher<TMessage extends PublishableMessage<string, any>>\n\timplements EventPublisher<TMessage>\n{\n\tconstructor(\n\t\tprivate connection: SNSConnection,\n\t\t_options: SNSPublisherOptions = {},\n\t) {}\n\n\t/**\n\t * Create an SNSPublisher from a connection string\n\t * Format: sns://?topicArn=arn:aws:sns:region:account:topic®ion=us-east-1&endpoint=http://localhost:4566\n\t */\n\tstatic async fromConnectionString<\n\t\tTMessage extends PublishableMessage<string, any>,\n\t>(connectionString: string): Promise<SNSPublisher<TMessage>> {\n\t\tconst { SNSConnection } = await import('./SNSConnection');\n\t\tconst connection =\n\t\t\tawait SNSConnection.fromConnectionString(connectionString);\n\n\t\treturn new SNSPublisher<TMessage>(connection);\n\t}\n\n\tasync publish(messages: TMessage[]): Promise<void> {\n\t\tif (messages.length === 0) return;\n\n\t\tif (!this.connection.isConnected()) {\n\t\t\tawait this.connection.connect();\n\t\t}\n\n\t\t// Publish all messages\n\t\tawait Promise.all(messages.map((message) => this.publishMessage(message)));\n\t}\n\n\tprivate async publishMessage(message: TMessage): Promise<void> {\n\t\tconst command = new PublishCommand({\n\t\t\tTopicArn: this.connection.topicArn,\n\t\t\tMessage: JSON.stringify({\n\t\t\t\ttype: message.type,\n\t\t\t\tpayload: message.payload,\n\t\t\t}),\n\t\t\tMessageAttributes: {\n\t\t\t\ttype: {\n\t\t\t\t\tDataType: 'String',\n\t\t\t\t\tStringValue: message.type,\n\t\t\t\t},\n\t\t\t},\n\t\t});\n\n\t\tawait this.connection.snsClient.send(command);\n\t}\n\n\tasync close(): Promise<void> {\n\t\t// Publisher doesn't own the connection\n\t\t// Connection should be closed by whoever created it\n\t}\n}\n","import { SubscribeCommand, UnsubscribeCommand } from '@aws-sdk/client-sns';\nimport {\n\tCreateQueueCommand,\n\tDeleteQueueCommand,\n\tGetQueueAttributesCommand,\n\tSetQueueAttributesCommand,\n\tSQSClient,\n} from '@aws-sdk/client-sqs';\nimport { SQSConnection } from '../sqs/SQSConnection';\nimport { SQSSubscriber } from '../sqs/SQSSubscriber';\nimport type { EventSubscriber, PublishableMessage } from '../types';\nimport type { SNSConnection } from './SNSConnection';\n\nexport interface SNSSubscriberOptions {\n\tqueueName?: string; // Optional queue name (defaults to auto-generated)\n\tcreateQueue?: boolean; // Auto-create SQS queue (default: true)\n\tdeleteQueueOnClose?: boolean; // Delete queue when subscriber closes (default: false for named queues, true for auto-generated)\n\twaitTimeSeconds?: number; // SQS long polling (default: 20)\n\tmaxMessages?: number; // Max messages per poll (default: 10)\n}\n\n/**\n * SNS Subscriber - receives push-based messages from SNS via SQS queue\n *\n * Architecture:\n * SNS Topic → (push) → SQS Queue → Subscriber\n *\n * This subscriber automatically:\n * 1. Creates an SQS queue (or uses existing one)\n * 2. Subscribes the queue to the SNS topic\n * 3. Receives messages pushed from SNS to the queue\n * 4. Filters messages by topic ARN and message type\n * 5. Processes and acknowledges messages\n *\n * Note: While SQS long-polling is used internally to receive messages,\n * this is an implementation detail of the SNS push delivery mechanism.\n * Messages are pushed from SNS to SQS, and we receive those pushed messages.\n */\nexport class SNSSubscriber<TMessage extends PublishableMessage<string, any>>\n\timplements EventSubscriber<TMessage>\n{\n\tprivate sqsClient: SQSClient;\n\tprivate queueUrl?: string;\n\tprivate queueArn?: string;\n\tprivate subscriptionArn?: string;\n\tprivate sqsSubscriber?: SQSSubscriber<TMessage>;\n\tprivate options: Required<SNSSubscriberOptions>;\n\n\tconstructor(\n\t\tprivate connection: SNSConnection,\n\t\toptions: SNSSubscriberOptions = {},\n\t) {\n\t\tthis.options = {\n\t\t\tqueueName:\n\t\t\t\toptions.queueName ||\n\t\t\t\t`sns-sub-${Date.now()}-${Math.random().toString(36).substring(7)}`,\n\t\t\tcreateQueue: options.createQueue ?? true,\n\t\t\tdeleteQueueOnClose: options.deleteQueueOnClose ?? !options.queueName, // Delete auto-generated queues\n\t\t\twaitTimeSeconds: options.waitTimeSeconds ?? 20,\n\t\t\tmaxMessages: options.maxMessages ?? 10,\n\t\t};\n\n\t\t// Create SQS client with same config as SNS\n\t\tthis.sqsClient = new SQSClient({\n\t\t\tregion: (this.connection as any).config.region,\n\t\t\tendpoint: (this.connection as any).config.endpoint,\n\t\t\tcredentials: (this.connection as any).config.credentials,\n\t\t});\n\t}\n\n\t/**\n\t * Create an SNSSubscriber from a connection string\n\t * Format: sns://?topicArn=...&queueName=myqueue®ion=us-east-1\n\t */\n\tstatic async fromConnectionString<\n\t\tTMessage extends PublishableMessage<string, any>,\n\t>(connectionString: string): Promise<SNSSubscriber<TMessage>> {\n\t\tconst url = new URL(connectionString);\n\t\tconst params = url.searchParams;\n\n\t\tconst { SNSConnection } = await import('./SNSConnection');\n\t\tconst connection =\n\t\t\tawait SNSConnection.fromConnectionString(connectionString);\n\n\t\tconst options: SNSSubscriberOptions = {\n\t\t\tqueueName: params.get('queueName') || undefined,\n\t\t\tcreateQueue: params.get('createQueue') !== 'false',\n\t\t\tdeleteQueueOnClose: params.get('deleteQueueOnClose') === 'true',\n\t\t\twaitTimeSeconds: params.get('waitTimeSeconds')\n\t\t\t\t? Number.parseInt(params.get('waitTimeSeconds')!, 10)\n\t\t\t\t: undefined,\n\t\t};\n\n\t\treturn new SNSSubscriber<TMessage>(connection, options);\n\t}\n\n\tasync subscribe(\n\t\tmessages: TMessage['type'][],\n\t\tlistener: (payload: TMessage) => Promise<void>,\n\t): Promise<void> {\n\t\t// Setup SQS queue and SNS subscription\n\t\tawait this.setupQueue();\n\t\tawait this.subscribeToTopic();\n\n\t\t// Create SQS connection for the subscriber\n\t\tconst sqsConnection = new SQSConnection({\n\t\t\tqueueUrl: this.queueUrl!,\n\t\t\tregion: (this.connection as any).config.region,\n\t\t\tendpoint: (this.connection as any).config.endpoint,\n\t\t\tcredentials: (this.connection as any).config.credentials,\n\t\t});\n\t\tawait sqsConnection.connect();\n\n\t\t// Use SQSSubscriber to receive messages, with topic filtering\n\t\tthis.sqsSubscriber = new SQSSubscriber<TMessage>(sqsConnection, {\n\t\t\twaitTimeSeconds: this.options.waitTimeSeconds,\n\t\t\tmaxMessages: this.options.maxMessages,\n\t\t\texpectedTopicArn: this.connection.topicArn,\n\t\t});\n\n\t\tawait this.sqsSubscriber.subscribe(messages, listener);\n\t}\n\n\t/**\n\t * Stop receiving messages and clean up resources\n\t */\n\tasync stop(): Promise<void> {\n\t\t// Stop SQS subscriber\n\t\tif (this.sqsSubscriber) {\n\t\t\tthis.sqsSubscriber.stop();\n\t\t}\n\n\t\t// Unsubscribe from topic\n\t\tif (this.subscriptionArn) {\n\t\t\ttry {\n\t\t\t\tconst command = new UnsubscribeCommand({\n\t\t\t\t\tSubscriptionArn: this.subscriptionArn,\n\t\t\t\t});\n\t\t\t\tawait this.connection.snsClient.send(command);\n\t\t\t} catch (_error) {}\n\t\t}\n\n\t\t// Delete queue if configured\n\t\tif (this.queueUrl && this.options.deleteQueueOnClose) {\n\t\t\ttry {\n\t\t\t\tconst command = new DeleteQueueCommand({\n\t\t\t\t\tQueueUrl: this.queueUrl,\n\t\t\t\t});\n\t\t\t\tawait this.sqsClient.send(command);\n\t\t\t} catch (_error) {}\n\t\t}\n\n\t\tthis.sqsClient.destroy();\n\t}\n\n\tprivate async setupQueue(): Promise<void> {\n\t\tif (this.options.createQueue) {\n\t\t\t// Create queue\n\t\t\tconst createCommand = new CreateQueueCommand({\n\t\t\t\tQueueName: this.options.queueName,\n\t\t\t});\n\t\t\tconst response = await this.sqsClient.send(createCommand);\n\t\t\tthis.queueUrl = response.QueueUrl!;\n\t\t} else {\n\t\t\tthrow new SnsQueueMissing(this.options.queueName);\n\t\t}\n\n\t\t// Get queue ARN\n\t\tconst attrsCommand = new GetQueueAttributesCommand({\n\t\t\tQueueUrl: this.queueUrl,\n\t\t\tAttributeNames: ['QueueArn'],\n\t\t});\n\t\tconst attrsResponse = await this.sqsClient.send(attrsCommand);\n\t\tthis.queueArn = attrsResponse.Attributes?.QueueArn!;\n\n\t\t// Set queue policy to allow SNS\n\t\tawait this.setQueuePolicy();\n\t}\n\n\tprivate async setQueuePolicy(): Promise<void> {\n\t\tconst policy = {\n\t\t\tVersion: '2012-10-17',\n\t\t\tStatement: [\n\t\t\t\t{\n\t\t\t\t\tEffect: 'Allow',\n\t\t\t\t\tPrincipal: '*',\n\t\t\t\t\tAction: 'SQS:SendMessage',\n\t\t\t\t\tResource: this.queueArn,\n\t\t\t\t\tCondition: {\n\t\t\t\t\t\tArnEquals: {\n\t\t\t\t\t\t\t'aws:SourceArn': this.connection.topicArn,\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t],\n\t\t};\n\n\t\tconst command = new SetQueueAttributesCommand({\n\t\t\tQueueUrl: this.queueUrl,\n\t\t\tAttributes: {\n\t\t\t\tPolicy: JSON.stringify(policy),\n\t\t\t},\n\t\t});\n\n\t\tawait this.sqsClient.send(command);\n\t}\n\n\tprivate async subscribeToTopic(): Promise<void> {\n\t\tconst command = new SubscribeCommand({\n\t\t\tTopicArn: this.connection.topicArn,\n\t\t\tProtocol: 'sqs',\n\t\t\tEndpoint: this.queueArn,\n\t\t});\n\n\t\tconst response = await this.connection.snsClient.send(command);\n\t\tthis.subscriptionArn = response.SubscriptionArn!;\n\t}\n}\n\n/**\n * A subscriber asked to use a queue it may not create. The SQS queue behind an\n * SNS subscription has to exist first, or `createQueue` has to allow it.\n */\nexport class SnsQueueMissing extends Error {\n\tconstructor(readonly queueName: string | undefined) {\n\t\tsuper(\n\t\t\t`The SNS subscriber's queue${queueName ? ` \"${queueName}\"` : ''} is not created here: pass createQueue: true, or create the queue first.`,\n\t\t);\n\t\tthis.name = 'SnsQueueMissing';\n\t}\n}\n"],"mappings":"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"}
|
package/dist/sns/index.d.cts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { i as EventSubscriber, n as EventPublisher, r as EventPublisherType, s as PublishableMessage, t as EventConnection } from "../types-
|
|
1
|
+
import { i as EventSubscriber, n as EventPublisher, r as EventPublisherType, s as PublishableMessage, t as EventConnection } from "../types-gGaoT8qc.cjs";
|
|
2
2
|
import { SnsAddress, t as snsUrl_d_exports } from "./snsUrl.cjs";
|
|
3
3
|
import { SNSClient } from "@aws-sdk/client-sns";
|
|
4
4
|
//#region src/sns/SNSConnection.d.ts
|
|
@@ -99,6 +99,14 @@ export declare class SNSSubscriber<TMessage extends PublishableMessage<string, a
|
|
|
99
99
|
private setQueuePolicy;
|
|
100
100
|
private subscribeToTopic;
|
|
101
101
|
}
|
|
102
|
+
/**
|
|
103
|
+
* A subscriber asked to use a queue it may not create. The SQS queue behind an
|
|
104
|
+
* SNS subscription has to exist first, or `createQueue` has to allow it.
|
|
105
|
+
*/
|
|
106
|
+
export declare class SnsQueueMissing extends Error {
|
|
107
|
+
readonly queueName: string | undefined;
|
|
108
|
+
constructor(queueName: string | undefined);
|
|
109
|
+
}
|
|
102
110
|
//#endregion
|
|
103
111
|
export { type SNSConnectionConfig, type SNSPublisherOptions, type SNSSubscriberOptions, type SnsAddress, snsUrl_d_exports as snsUrl };
|
|
104
112
|
//# sourceMappingURL=index.d.cts.map
|
package/dist/sns/index.d.cts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.cts","names":[],"sources":["../../src/sns/SNSConnection.ts","../../src/sns/SNSPublisher.ts","../../src/sns/SNSSubscriber.ts"],"mappings":";;;;UAKiB;EAChB;EACA;EACA;EACA;IACC;IACA;IACA;;;qBAIW,yBAAyB;WAC5B,OAAI,mBAAA;UACL;UACA;UACA;EAEI,YAAA,QAAQ;;;;;SAgBP,qBACZ,2BACE,QAAQ;EA8BL,WAAW;EAKX,SAAS;EAKf;;;;MAOI,aAAa;;;;MAOb;;;;KC1FO;qBAEC,aAAa,iBAAiB,4CAC/B,eAAe;UAGjB;EAAA,YAAA,YAAY,eACpB,WAAU;;;;;SAOE,qBACZ,iBAAiB,iCAChB,2BAA2B,QAAQ,aAAa;EAQ5C,QAAQ,UAAU,aAAa;UAWvB;EAkBR,SAAS;;;;UC5CC;EAChB;EACA;EACA;EACA;EACA;;;;;;;;;;;;;;;;;;;qBAoBY,cAAc,iBAAiB,4CAChC,gBAAgB;UAUlB;UARD;UACA;UACA;UACA;UACA;UACA;EAGC,YAAA,YAAY,eACpB,UAAS;;;;;SAwBG,qBACZ,iBAAiB,iCAChB,2BAA2B,QAAQ,cAAc;EAoB7C,UACL,UAAU,oBACV,WAAW,SAAS,aAAa,gBAC/B;;;;EA2BG,QAAQ;UA6BA;UAwBA;UA4BA"}
|
|
1
|
+
{"version":3,"file":"index.d.cts","names":[],"sources":["../../src/sns/SNSConnection.ts","../../src/sns/SNSPublisher.ts","../../src/sns/SNSSubscriber.ts"],"mappings":";;;;UAKiB;EAChB;EACA;EACA;EACA;IACC;IACA;IACA;;;qBAIW,yBAAyB;WAC5B,OAAI,mBAAA;UACL;UACA;UACA;EAEI,YAAA,QAAQ;;;;;SAgBP,qBACZ,2BACE,QAAQ;EA8BL,WAAW;EAKX,SAAS;EAKf;;;;MAOI,aAAa;;;;MAOb;;;;KC1FO;qBAEC,aAAa,iBAAiB,4CAC/B,eAAe;UAGjB;EAAA,YAAA,YAAY,eACpB,WAAU;;;;;SAOE,qBACZ,iBAAiB,iCAChB,2BAA2B,QAAQ,aAAa;EAQ5C,QAAQ,UAAU,aAAa;UAWvB;EAkBR,SAAS;;;;UC5CC;EAChB;EACA;EACA;EACA;EACA;;;;;;;;;;;;;;;;;;;qBAoBY,cAAc,iBAAiB,4CAChC,gBAAgB;UAUlB;UARD;UACA;UACA;UACA;UACA;UACA;EAGC,YAAA,YAAY,eACpB,UAAS;;;;;SAwBG,qBACZ,iBAAiB,iCAChB,2BAA2B,QAAQ,cAAc;EAoB7C,UACL,UAAU,oBACV,WAAW,SAAS,aAAa,gBAC/B;;;;EA2BG,QAAQ;UA6BA;UAwBA;UA4BA;;;;;;qBAgBF,wBAAwB;WACf;EAAA,YAAA"}
|
package/dist/sns/index.d.mts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { i as EventSubscriber, n as EventPublisher, r as EventPublisherType, s as PublishableMessage, t as EventConnection } from "../types-
|
|
1
|
+
import { i as EventSubscriber, n as EventPublisher, r as EventPublisherType, s as PublishableMessage, t as EventConnection } from "../types-gGaoT8qc.mjs";
|
|
2
2
|
import { SnsAddress, t as snsUrl_d_exports } from "./snsUrl.mjs";
|
|
3
3
|
import { SNSClient } from "@aws-sdk/client-sns";
|
|
4
4
|
//#region src/sns/SNSConnection.d.ts
|
|
@@ -99,6 +99,14 @@ export declare class SNSSubscriber<TMessage extends PublishableMessage<string, a
|
|
|
99
99
|
private setQueuePolicy;
|
|
100
100
|
private subscribeToTopic;
|
|
101
101
|
}
|
|
102
|
+
/**
|
|
103
|
+
* A subscriber asked to use a queue it may not create. The SQS queue behind an
|
|
104
|
+
* SNS subscription has to exist first, or `createQueue` has to allow it.
|
|
105
|
+
*/
|
|
106
|
+
export declare class SnsQueueMissing extends Error {
|
|
107
|
+
readonly queueName: string | undefined;
|
|
108
|
+
constructor(queueName: string | undefined);
|
|
109
|
+
}
|
|
102
110
|
//#endregion
|
|
103
111
|
export { type SNSConnectionConfig, type SNSPublisherOptions, type SNSSubscriberOptions, type SnsAddress, snsUrl_d_exports as snsUrl };
|
|
104
112
|
//# sourceMappingURL=index.d.mts.map
|
package/dist/sns/index.d.mts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.mts","names":[],"sources":["../../src/sns/SNSConnection.ts","../../src/sns/SNSPublisher.ts","../../src/sns/SNSSubscriber.ts"],"mappings":";;;;UAKiB;EAChB;EACA;EACA;EACA;IACC;IACA;IACA;;;qBAIW,yBAAyB;WAC5B,OAAI,mBAAA;UACL;UACA;UACA;EAEI,YAAA,QAAQ;;;;;SAgBP,qBACZ,2BACE,QAAQ;EA8BL,WAAW;EAKX,SAAS;EAKf;;;;MAOI,aAAa;;;;MAOb;;;;KC1FO;qBAEC,aAAa,iBAAiB,4CAC/B,eAAe;UAGjB;EAAA,YAAA,YAAY,eACpB,WAAU;;;;;SAOE,qBACZ,iBAAiB,iCAChB,2BAA2B,QAAQ,aAAa;EAQ5C,QAAQ,UAAU,aAAa;UAWvB;EAkBR,SAAS;;;;UC5CC;EAChB;EACA;EACA;EACA;EACA;;;;;;;;;;;;;;;;;;;qBAoBY,cAAc,iBAAiB,4CAChC,gBAAgB;UAUlB;UARD;UACA;UACA;UACA;UACA;UACA;EAGC,YAAA,YAAY,eACpB,UAAS;;;;;SAwBG,qBACZ,iBAAiB,iCAChB,2BAA2B,QAAQ,cAAc;EAoB7C,UACL,UAAU,oBACV,WAAW,SAAS,aAAa,gBAC/B;;;;EA2BG,QAAQ;UA6BA;UAwBA;UA4BA"}
|
|
1
|
+
{"version":3,"file":"index.d.mts","names":[],"sources":["../../src/sns/SNSConnection.ts","../../src/sns/SNSPublisher.ts","../../src/sns/SNSSubscriber.ts"],"mappings":";;;;UAKiB;EAChB;EACA;EACA;EACA;IACC;IACA;IACA;;;qBAIW,yBAAyB;WAC5B,OAAI,mBAAA;UACL;UACA;UACA;EAEI,YAAA,QAAQ;;;;;SAgBP,qBACZ,2BACE,QAAQ;EA8BL,WAAW;EAKX,SAAS;EAKf;;;;MAOI,aAAa;;;;MAOb;;;;KC1FO;qBAEC,aAAa,iBAAiB,4CAC/B,eAAe;UAGjB;EAAA,YAAA,YAAY,eACpB,WAAU;;;;;SAOE,qBACZ,iBAAiB,iCAChB,2BAA2B,QAAQ,aAAa;EAQ5C,QAAQ,UAAU,aAAa;UAWvB;EAkBR,SAAS;;;;UC5CC;EAChB;EACA;EACA;EACA;EACA;;;;;;;;;;;;;;;;;;;qBAoBY,cAAc,iBAAiB,4CAChC,gBAAgB;UAUlB;UARD;UACA;UACA;UACA;UACA;UACA;EAGC,YAAA,YAAY,eACpB,UAAS;;;;;SAwBG,qBACZ,iBAAiB,iCAChB,2BAA2B,QAAQ,cAAc;EAoB7C,UACL,UAAU,oBACV,WAAW,SAAS,aAAa,gBAC/B;;;;EA2BG,QAAQ;UA6BA;UAwBA;UA4BA;;;;;;qBAgBF,wBAAwB;WACf;EAAA,YAAA"}
|
package/dist/sns/index.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import "../types-
|
|
2
|
-
import { n as SQSConnection, t as SQSSubscriber } from "../SQSSubscriber-
|
|
1
|
+
import "../types-Bk5JM6MP.mjs";
|
|
2
|
+
import { n as SQSConnection, t as SQSSubscriber } from "../SQSSubscriber-BXLE_1L3.mjs";
|
|
3
3
|
import { parse, t as snsUrl_exports } from "./snsUrl.mjs";
|
|
4
4
|
import { CreateQueueCommand, DeleteQueueCommand, GetQueueAttributesCommand, SQSClient, SetQueueAttributesCommand } from "@aws-sdk/client-sqs";
|
|
5
5
|
import { PublishCommand, SNSClient, SubscribeCommand, UnsubscribeCommand } from "@aws-sdk/client-sns";
|
|
@@ -196,7 +196,7 @@ var SNSSubscriber = class SNSSubscriber {
|
|
|
196
196
|
const createCommand = new CreateQueueCommand({ QueueName: this.options.queueName });
|
|
197
197
|
const response = await this.sqsClient.send(createCommand);
|
|
198
198
|
this.queueUrl = response.QueueUrl;
|
|
199
|
-
} else throw new
|
|
199
|
+
} else throw new SnsQueueMissing(this.options.queueName);
|
|
200
200
|
const attrsCommand = new GetQueueAttributesCommand({
|
|
201
201
|
QueueUrl: this.queueUrl,
|
|
202
202
|
AttributeNames: ["QueueArn"]
|
|
@@ -232,7 +232,19 @@ var SNSSubscriber = class SNSSubscriber {
|
|
|
232
232
|
this.subscriptionArn = response.SubscriptionArn;
|
|
233
233
|
}
|
|
234
234
|
};
|
|
235
|
+
/**
|
|
236
|
+
* A subscriber asked to use a queue it may not create. The SQS queue behind an
|
|
237
|
+
* SNS subscription has to exist first, or `createQueue` has to allow it.
|
|
238
|
+
*/
|
|
239
|
+
var SnsQueueMissing = class extends Error {
|
|
240
|
+
queueName;
|
|
241
|
+
constructor(queueName) {
|
|
242
|
+
super(`The SNS subscriber's queue${queueName ? ` "${queueName}"` : ""} is not created here: pass createQueue: true, or create the queue first.`);
|
|
243
|
+
this.queueName = queueName;
|
|
244
|
+
this.name = "SnsQueueMissing";
|
|
245
|
+
}
|
|
246
|
+
};
|
|
235
247
|
//#endregion
|
|
236
|
-
export { SNSConnection, SNSPublisher, SNSSubscriber, snsUrl_exports as snsUrl };
|
|
248
|
+
export { SNSConnection, SNSPublisher, SNSSubscriber, SnsQueueMissing, snsUrl_exports as snsUrl };
|
|
237
249
|
|
|
238
250
|
//# sourceMappingURL=index.mjs.map
|