@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.
Files changed (61) hide show
  1. package/dist/{SQSSubscriber-cpo624qU.mjs → SQSSubscriber-BXLE_1L3.mjs} +2 -2
  2. package/dist/{SQSSubscriber-cpo624qU.mjs.map → SQSSubscriber-BXLE_1L3.mjs.map} +1 -1
  3. package/dist/{SQSSubscriber-DLPZCzFE.cjs → SQSSubscriber-DtRUaZB3.cjs} +2 -2
  4. package/dist/{SQSSubscriber-DLPZCzFE.cjs.map → SQSSubscriber-DtRUaZB3.cjs.map} +1 -1
  5. package/dist/basic/index.cjs +1 -1
  6. package/dist/basic/index.d.cts +1 -1
  7. package/dist/basic/index.d.mts +1 -1
  8. package/dist/basic/index.mjs +1 -1
  9. package/dist/index.cjs +7 -6
  10. package/dist/index.cjs.map +1 -1
  11. package/dist/index.d.cts +2 -2
  12. package/dist/index.d.cts.map +1 -1
  13. package/dist/index.d.mts +2 -2
  14. package/dist/index.d.mts.map +1 -1
  15. package/dist/index.mjs +7 -7
  16. package/dist/index.mjs.map +1 -1
  17. package/dist/pgboss/index.cjs +13 -3
  18. package/dist/pgboss/index.cjs.map +1 -1
  19. package/dist/pgboss/index.d.cts +7 -1
  20. package/dist/pgboss/index.d.cts.map +1 -1
  21. package/dist/pgboss/index.d.mts +7 -1
  22. package/dist/pgboss/index.d.mts.map +1 -1
  23. package/dist/pgboss/index.mjs +13 -4
  24. package/dist/pgboss/index.mjs.map +1 -1
  25. package/dist/rabbitmq/index.cjs +13 -3
  26. package/dist/rabbitmq/index.cjs.map +1 -1
  27. package/dist/rabbitmq/index.d.cts +7 -1
  28. package/dist/rabbitmq/index.d.cts.map +1 -1
  29. package/dist/rabbitmq/index.d.mts +7 -1
  30. package/dist/rabbitmq/index.d.mts.map +1 -1
  31. package/dist/rabbitmq/index.mjs +13 -4
  32. package/dist/rabbitmq/index.mjs.map +1 -1
  33. package/dist/sns/index.cjs +16 -3
  34. package/dist/sns/index.cjs.map +1 -1
  35. package/dist/sns/index.d.cts +9 -1
  36. package/dist/sns/index.d.cts.map +1 -1
  37. package/dist/sns/index.d.mts +9 -1
  38. package/dist/sns/index.d.mts.map +1 -1
  39. package/dist/sns/index.mjs +16 -4
  40. package/dist/sns/index.mjs.map +1 -1
  41. package/dist/sqs/index.cjs +17 -6
  42. package/dist/sqs/index.cjs.map +1 -1
  43. package/dist/sqs/index.d.cts +14 -1
  44. package/dist/sqs/index.d.cts.map +1 -1
  45. package/dist/sqs/index.d.mts +14 -1
  46. package/dist/sqs/index.d.mts.map +1 -1
  47. package/dist/sqs/index.mjs +17 -7
  48. package/dist/sqs/index.mjs.map +1 -1
  49. package/dist/types-BcZjLxVV.cjs +51 -0
  50. package/dist/types-BcZjLxVV.cjs.map +1 -0
  51. package/dist/types-Bk5JM6MP.mjs +40 -0
  52. package/dist/types-Bk5JM6MP.mjs.map +1 -0
  53. package/dist/{types-lnM-qoe_.d.cts → types-gGaoT8qc.d.cts} +11 -2
  54. package/dist/{types-lnM-qoe_.d.cts.map → types-gGaoT8qc.d.cts.map} +1 -1
  55. package/dist/{types-lnM-qoe_.d.mts → types-gGaoT8qc.d.mts} +11 -2
  56. package/dist/{types-lnM-qoe_.d.mts.map → types-gGaoT8qc.d.mts.map} +1 -1
  57. package/package.json +3 -3
  58. package/dist/types-C15FC0qp.mjs +0 -18
  59. package/dist/types-C15FC0qp.mjs.map +0 -1
  60. package/dist/types-DXNwyRyg.cjs +0 -23
  61. 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":";;;UAIiB;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;;;;qBCpFJ,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"}
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"}
@@ -1,5 +1,14 @@
1
- import "../types-C15FC0qp.mjs";
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 Error("PgBoss instance not initialized");
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 Error("PgBoss instance not initialized");
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 Error('PgBoss instance not initialized');\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 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 Error('PgBoss instance not initialized');\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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"}
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":";;;;AAKA,IAAa,mBAAb,cAAsC,MAAM;CAC3C,cAAc;EACb,MACC,6HACD;EACA,KAAK,OAAO;CACb;AACD;;;ACHA,IAAa,mBAAb,MAAa,iBAA4C;CAKpC;CAJpB,OAAS;CACT;CACA;CAEA,YAAY,QAAwC;EAAhC,KAAA,SAAA;CAAiC;;;;;CAMrD,aAAa,qBACZ,kBAC4B;EAC5B,MAAM,MAAM,IAAI,IAAI,gBAAgB;EACpC,MAAM,SAAS,IAAI;EAInB,MAAM,SAAiC;GACtC,kBAAkB,cAHe,IAAI,WAAW,GAAG,IAAI,SAAS,GAAG,IAAI,SAAS,KAAK,KAAK,IAAI,OAAO,IAAI;GAIzG,QAAQ,OAAO,IAAI,QAAQ,KAAK,KAAA;EACjC;EAEA,MAAM,aAAa,IAAI,iBAAiB,MAAM;EAC9C,MAAM,WAAW,QAAQ;EACzB,OAAO;CACR;CAEA,MAAM,UAAyB;EAC9B,IAAI,KAAK,MAAM;EAEf,IAAI,KAAK,YAAY;GACpB,MAAM,KAAK;GACX;EACD;EAEA,KAAK,cAAc,YAAY;GAC9B,IAAI;IACH,MAAM,OAAO,IAAI,OAAO;KACvB,kBAAkB,KAAK,OAAO;KAC9B,GAAI,KAAK,OAAO,UAAU,EAAE,QAAQ,KAAK,OAAO,OAAO;IACxD,CAAC;IAED,MAAM,KAAK,MAAM;IACjB,KAAK,OAAO;GACb,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,MAAM,KAAK,EAAE,UAAU,KAAK,CAAC;EACzC,QAAQ,CAER,UAAU;GACT,KAAK,QAAQ;EACd;CACD;CAEA,cAAuB;EACtB,OAAO,CAAC,CAAC,KAAK;CACf;CAEA,UAAwB;EACvB,KAAK,OAAO,KAAA;EACZ,KAAK,aAAa,KAAA;CACnB;;;;CAKA,IAAI,WAA+B;EAClC,OAAO,KAAK;CACb;AACD;;;ACtFA,IAAa,kBAAb,MAAa,gBAEb;CACqB;CAApB,YAAY,YAAsC;EAA9B,KAAA,aAAA;CAA+B;;;;;CAMnD,aAAa,qBAEX,kBAA8D;EAC/D,MAAM,EAAE,qBAAqB,MAAM,OAAO;EAC1C,MAAM,aACL,MAAM,iBAAiB,qBAAqB,gBAAgB;EAC7D,OAAO,IAAI,gBAA0B,UAAU;CAChD;CAEA,MAAM,QAAQ,UAAqC;EAClD,IAAI,CAAC,KAAK,WAAW,YAAY,GAChC,MAAM,KAAK,WAAW,QAAQ;EAG/B,MAAM,OAAO,KAAK,WAAW;EAC7B,IAAI,CAAC,MACJ,MAAM,IAAI,iBAAiB;EAI5B,MAAM,yBAAS,IAAI,IAA6C;EAChE,KAAK,MAAM,KAAK,UAAU;GACzB,MAAM,OAAO,OAAO,IAAI,EAAE,IAAI,KAAK,CAAC;GACpC,KAAK,KAAK,EAAE,MAAM,EAAE,QAAQ,CAAC;GAC7B,OAAO,IAAI,EAAE,MAAM,IAAI;EACxB;EAEA,KAAK,MAAM,CAAC,MAAM,SAAS,QAAQ;GAClC,MAAM,KAAK,YAAY,IAAI;GAC3B,MAAM,KAAK,OAAO,MAAM,IAAI;EAC7B;CACD;CAEA,MAAM,QAAuB,CAE7B;AACD;;;ACxCA,IAAa,mBAAb,MAAa,iBAEb;CAEU;CACA;CAFT,YACC,YACA,UAA2C,CAAC,GAC3C;EAFO,KAAA,aAAA;EACA,KAAA,UAAA;CACN;;;;;CAMH,aAAa,qBAEX,kBAA+D;EAEhE,MAAM,SAAS,IADC,IAAI,gBACH,CAAC,CAAC;EAEnB,MAAM,EAAE,qBAAqB,MAAM,OAAO;EAC1C,MAAM,aACL,MAAM,iBAAiB,qBAAqB,gBAAgB;EAE7D,MAAM,UAAmC;GACxC,WAAW,OAAO,IAAI,WAAW,IAC9B,OAAO,SAAS,OAAO,IAAI,WAAW,GAAI,EAAE,IAC5C,KAAA;GACH,wBAAwB,OAAO,IAAI,wBAAwB,IACxD,OAAO,SAAS,OAAO,IAAI,wBAAwB,GAAI,EAAE,IACzD,KAAA;EACJ;EAEA,OAAO,IAAI,iBAA2B,YAAY,OAAO;CAC1D;CAEA,MAAM,UACL,UACA,UACgB;EAChB,IAAI,CAAC,KAAK,WAAW,YAAY,GAChC,MAAM,KAAK,WAAW,QAAQ;EAG/B,MAAM,OAAO,KAAK,WAAW;EAC7B,IAAI,CAAC,MACJ,MAAM,IAAI,iBAAiB;EAG5B,KAAK,MAAM,eAAe,UAAU;GACnC,MAAM,KAAK,YAAY,WAAW;GAClC,MAAM,KAAK,KACV,aACA;IACC,GAAI,KAAK,QAAQ,aAAa,EAC7B,WAAW,KAAK,QAAQ,UACzB;IACA,GAAI,KAAK,QAAQ,0BAA0B,EAC1C,wBAAwB,KAAK,QAAQ,uBACtC;GACD,GACA,OAAO,SAAS;IACf,KAAK,MAAM,OAAO,MAMjB,MAAM,SAAS;KAJd,MAAM,IAAI;KACV,SAAS,IAAI;IAGW,CAAC;GAE5B,CACD;EACD;CACD;AACD"}
@@ -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-DXNwyRyg.cjs");
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 Error("Channel not initialized");
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 Error("Channel not initialized");
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":";;;;;;AAYA,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,QAAA,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;;;AC3IA,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,MAAA,QAAA,QAAA,CAAA,CAAA,WAAA,QAAM,oCAAA,CAAA;EACrC,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,MAAM,yBAAyB;EAG1C,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,MAAA,QAAA,QAAA,CAAA,CAAA,WAAA,QAAM,oCAAA,CAAA;EACrC,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,MAAM,yBAAyB;EAG1C,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"}
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-lnM-qoe_.cjs";
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":";;;UAIiB;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;;;;UC5IY;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
+ {"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-lnM-qoe_.mjs";
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":";;;UAIiB;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;;;;UC5IY;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
+ {"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"}
@@ -1,5 +1,14 @@
1
- import "../types-C15FC0qp.mjs";
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 Error("Channel not initialized");
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 Error("Channel not initialized");
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":";;;AAYA,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;;;AC3IA,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,MAAM,yBAAyB;EAG1C,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,MAAM,yBAAyB;EAG1C,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"}
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":"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"}
@@ -1,6 +1,6 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
- require("../types-DXNwyRyg.cjs");
3
- const require_SQSSubscriber = require("../SQSSubscriber-DLPZCzFE.cjs");
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 Error("Queue must exist or createQueue option must be true");
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() {
@@ -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&region=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&region=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&region=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&region=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&region=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&region=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":";;;;;;;AAgBA,IAAa,gBAAb,MAAa,cAAyC;CACrD,OAAS;CACT;CACA,YAAoB;CACpB;CAEA,YAAY,QAA6B;EACxC,KAAK,SAAS;EAEd,MAAM,eAAgC;GACrC,QAAQ,OAAO;GACf,UAAU,OAAO;GACjB,aAAa,OAAO;EACrB;EAEA,KAAK,SAAS,IAAIA,oBAAAA,UAAU,YAAY;CACzC;;;;;CAMA,aAAa,qBACZ,kBACyB;EAIzB,MAAM,EAAE,UAAU,QAAQ,aAAA,mBAAA,MAA0B,gBAAgB;EACpE,MAAM,SAAS,IAAI,IAAI,gBAAgB,CAAC,CAAC;EAEzC,MAAM,SAA8B;GACnC;GACA;GACA;EACD;EAIA,MAAM,cAAc,OAAO,IAAI,aAAa;EAC5C,MAAM,kBAAkB,OAAO,IAAI,iBAAiB;EACpD,IAAI,eAAe,iBAClB,OAAO,cAAc;GACpB;GACA;GACA,cAAc,OAAO,IAAI,cAAc,KAAK,KAAA;EAC7C;EAGD,MAAM,aAAa,IAAI,cAAc,MAAM;EAC3C,MAAM,WAAW,QAAQ;EACzB,OAAO;CACR;CAEA,MAAM,UAAyB;EAE9B,KAAK,YAAY;CAClB;CAEA,MAAM,QAAuB;EAC5B,KAAK,OAAO,QAAQ;EACpB,KAAK,YAAY;CAClB;CAEA,cAAuB;EACtB,OAAO,KAAK;CACb;;;;CAKA,IAAI,YAAuB;EAC1B,OAAO,KAAK;CACb;;;;CAKA,IAAI,WAAmB;EACtB,OAAO,KAAK,OAAO;CACpB;AACD;;;AC3FA,IAAa,eAAb,MAAa,aAEb;CAEU;CADT,YACC,YACA,WAAgC,CAAC,GAChC;EAFO,KAAA,aAAA;CAEN;;;;;CAMH,aAAa,qBAEX,kBAA2D;EAC5D,MAAM,EAAE,kBAAkB,MAAA,QAAA,QAAA,CAAA,CAAA,WAAA,QAAM,+BAAA,CAAA;EAChC,MAAM,aACL,MAAM,cAAc,qBAAqB,gBAAgB;EAE1D,OAAO,IAAI,aAAuB,UAAU;CAC7C;CAEA,MAAM,QAAQ,UAAqC;EAClD,IAAI,SAAS,WAAW,GAAG;EAE3B,IAAI,CAAC,KAAK,WAAW,YAAY,GAChC,MAAM,KAAK,WAAW,QAAQ;EAI/B,MAAM,QAAQ,IAAI,SAAS,KAAK,YAAY,KAAK,eAAe,OAAO,CAAC,CAAC;CAC1E;CAEA,MAAc,eAAe,SAAkC;EAC9D,MAAM,UAAU,IAAIC,oBAAAA,eAAe;GAClC,UAAU,KAAK,WAAW;GAC1B,SAAS,KAAK,UAAU;IACvB,MAAM,QAAQ;IACd,SAAS,QAAQ;GAClB,CAAC;GACD,mBAAmB,EAClB,MAAM;IACL,UAAU;IACV,aAAa,QAAQ;GACtB,EACD;EACD,CAAC;EAED,MAAM,KAAK,WAAW,UAAU,KAAK,OAAO;CAC7C;CAEA,MAAM,QAAuB,CAG7B;AACD;;;;;;;;;;;;;;;;;;;;ACvBA,IAAa,gBAAb,MAAa,cAEb;CASU;CART;CACA;CACA;CACA;CACA;CACA;CAEA,YACC,YACA,UAAgC,CAAC,GAChC;EAFO,KAAA,aAAA;EAGR,KAAK,UAAU;GACd,WACC,QAAQ,aACR,WAAW,KAAK,IAAI,EAAE,GAAG,KAAK,OAAO,CAAC,CAAC,SAAS,EAAE,CAAC,CAAC,UAAU,CAAC;GAChE,aAAa,QAAQ,eAAe;GACpC,oBAAoB,QAAQ,sBAAsB,CAAC,QAAQ;GAC3D,iBAAiB,QAAQ,mBAAmB;GAC5C,aAAa,QAAQ,eAAe;EACrC;EAGA,KAAK,YAAY,IAAIC,oBAAAA,UAAU;GAC9B,QAAS,KAAK,WAAmB,OAAO;GACxC,UAAW,KAAK,WAAmB,OAAO;GAC1C,aAAc,KAAK,WAAmB,OAAO;EAC9C,CAAC;CACF;;;;;CAMA,aAAa,qBAEX,kBAA4D;EAE7D,MAAM,SAAS,IADC,IAAI,gBACH,CAAC,CAAC;EAEnB,MAAM,EAAE,kBAAkB,MAAA,QAAA,QAAA,CAAA,CAAA,WAAA,QAAM,+BAAA,CAAA;EAChC,MAAM,aACL,MAAM,cAAc,qBAAqB,gBAAgB;EAE1D,MAAM,UAAgC;GACrC,WAAW,OAAO,IAAI,WAAW,KAAK,KAAA;GACtC,aAAa,OAAO,IAAI,aAAa,MAAM;GAC3C,oBAAoB,OAAO,IAAI,oBAAoB,MAAM;GACzD,iBAAiB,OAAO,IAAI,iBAAiB,IAC1C,OAAO,SAAS,OAAO,IAAI,iBAAiB,GAAI,EAAE,IAClD,KAAA;EACJ;EAEA,OAAO,IAAI,cAAwB,YAAY,OAAO;CACvD;CAEA,MAAM,UACL,UACA,UACgB;EAEhB,MAAM,KAAK,WAAW;EACtB,MAAM,KAAK,iBAAiB;EAG5B,MAAM,gBAAgB,IAAIC,sBAAAA,cAAc;GACvC,UAAU,KAAK;GACf,QAAS,KAAK,WAAmB,OAAO;GACxC,UAAW,KAAK,WAAmB,OAAO;GAC1C,aAAc,KAAK,WAAmB,OAAO;EAC9C,CAAC;EACD,MAAM,cAAc,QAAQ;EAG5B,KAAK,gBAAgB,IAAIC,sBAAAA,cAAwB,eAAe;GAC/D,iBAAiB,KAAK,QAAQ;GAC9B,aAAa,KAAK,QAAQ;GAC1B,kBAAkB,KAAK,WAAW;EACnC,CAAC;EAED,MAAM,KAAK,cAAc,UAAU,UAAU,QAAQ;CACtD;;;;CAKA,MAAM,OAAsB;EAE3B,IAAI,KAAK,eACR,KAAK,cAAc,KAAK;EAIzB,IAAI,KAAK,iBACR,IAAI;GACH,MAAM,UAAU,IAAIC,oBAAAA,mBAAmB,EACtC,iBAAiB,KAAK,gBACvB,CAAC;GACD,MAAM,KAAK,WAAW,UAAU,KAAK,OAAO;EAC7C,SAAS,QAAQ,CAAC;EAInB,IAAI,KAAK,YAAY,KAAK,QAAQ,oBACjC,IAAI;GACH,MAAM,UAAU,IAAIC,oBAAAA,mBAAmB,EACtC,UAAU,KAAK,SAChB,CAAC;GACD,MAAM,KAAK,UAAU,KAAK,OAAO;EAClC,SAAS,QAAQ,CAAC;EAGnB,KAAK,UAAU,QAAQ;CACxB;CAEA,MAAc,aAA4B;EACzC,IAAI,KAAK,QAAQ,aAAa;GAE7B,MAAM,gBAAgB,IAAIC,oBAAAA,mBAAmB,EAC5C,WAAW,KAAK,QAAQ,UACzB,CAAC;GACD,MAAM,WAAW,MAAM,KAAK,UAAU,KAAK,aAAa;GACxD,KAAK,WAAW,SAAS;EAC1B,OACC,MAAM,IAAI,gBAAgB,KAAK,QAAQ,SAAS;EAIjD,MAAM,eAAe,IAAIC,oBAAAA,0BAA0B;GAClD,UAAU,KAAK;GACf,gBAAgB,CAAC,UAAU;EAC5B,CAAC;EACD,MAAM,gBAAgB,MAAM,KAAK,UAAU,KAAK,YAAY;EAC5D,KAAK,WAAW,cAAc,YAAY;EAG1C,MAAM,KAAK,eAAe;CAC3B;CAEA,MAAc,iBAAgC;EAC7C,MAAM,SAAS;GACd,SAAS;GACT,WAAW,CACV;IACC,QAAQ;IACR,WAAW;IACX,QAAQ;IACR,UAAU,KAAK;IACf,WAAW,EACV,WAAW,EACV,iBAAiB,KAAK,WAAW,SAClC,EACD;GACD,CACD;EACD;EAEA,MAAM,UAAU,IAAIC,oBAAAA,0BAA0B;GAC7C,UAAU,KAAK;GACf,YAAY,EACX,QAAQ,KAAK,UAAU,MAAM,EAC9B;EACD,CAAC;EAED,MAAM,KAAK,UAAU,KAAK,OAAO;CAClC;CAEA,MAAc,mBAAkC;EAC/C,MAAM,UAAU,IAAIC,oBAAAA,iBAAiB;GACpC,UAAU,KAAK,WAAW;GAC1B,UAAU;GACV,UAAU,KAAK;EAChB,CAAC;EAED,MAAM,WAAW,MAAM,KAAK,WAAW,UAAU,KAAK,OAAO;EAC7D,KAAK,kBAAkB,SAAS;CACjC;AACD;;;;;AAMA,IAAa,kBAAb,cAAqC,MAAM;CACrB;CAArB,YAAY,WAAwC;EACnD,MACC,6BAA6B,YAAY,KAAK,UAAU,KAAK,GAAG,yEACjE;EAHoB,KAAA,YAAA;EAIpB,KAAK,OAAO;CACb;AACD"}
@@ -1,4 +1,4 @@
1
- import { i as EventSubscriber, n as EventPublisher, r as EventPublisherType, s as PublishableMessage, t as EventConnection } from "../types-lnM-qoe_.cjs";
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
@@ -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"}
@@ -1,4 +1,4 @@
1
- import { i as EventSubscriber, n as EventPublisher, r as EventPublisherType, s as PublishableMessage, t as EventConnection } from "../types-lnM-qoe_.mjs";
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
@@ -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"}
@@ -1,5 +1,5 @@
1
- import "../types-C15FC0qp.mjs";
2
- import { n as SQSConnection, t as SQSSubscriber } from "../SQSSubscriber-cpo624qU.mjs";
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 Error("Queue must exist or createQueue option must be true");
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