@geekmidas/events 10.0.0-alpha.4 → 10.0.0-alpha.40

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 (136) hide show
  1. package/dist/PgBossConnection-CywKoAZU.cjs +2 -0
  2. package/dist/PgBossConnection-DjoqT84r.mjs +2 -0
  3. package/dist/RabbitMQConnection-C9eccBL8.cjs +2 -0
  4. package/dist/RabbitMQConnection-CAp7VqyT.mjs +2 -0
  5. package/dist/SNSConnection-BWDC1wAF.cjs +2 -0
  6. package/dist/SNSConnection-ezQvVrDv.mjs +2 -0
  7. package/dist/{SQSSubscriber-BMWZ3s5q.mjs → SQSSubscriber-BXLE_1L3.mjs} +73 -7
  8. package/dist/SQSSubscriber-BXLE_1L3.mjs.map +1 -0
  9. package/dist/{SQSSubscriber-YXPlg2rm.cjs → SQSSubscriber-DtRUaZB3.cjs} +91 -14
  10. package/dist/SQSSubscriber-DtRUaZB3.cjs.map +1 -0
  11. package/dist/basic/index.cjs +7 -8
  12. package/dist/basic/index.cjs.map +1 -1
  13. package/dist/basic/index.d.cts +4 -10
  14. package/dist/basic/index.d.cts.map +1 -1
  15. package/dist/basic/index.d.mts +4 -10
  16. package/dist/basic/index.d.mts.map +1 -1
  17. package/dist/basic/index.mjs +5 -6
  18. package/dist/basic/index.mjs.map +1 -1
  19. package/dist/index.cjs +43 -50
  20. package/dist/index.cjs.map +1 -1
  21. package/dist/index.d.cts +26 -11
  22. package/dist/index.d.cts.map +1 -1
  23. package/dist/index.d.mts +26 -11
  24. package/dist/index.d.mts.map +1 -1
  25. package/dist/index.mjs +42 -51
  26. package/dist/index.mjs.map +1 -1
  27. package/dist/pgboss/index.cjs +145 -28
  28. package/dist/pgboss/index.cjs.map +1 -1
  29. package/dist/pgboss/index.d.cts +39 -12
  30. package/dist/pgboss/index.d.cts.map +1 -1
  31. package/dist/pgboss/index.d.mts +39 -12
  32. package/dist/pgboss/index.d.mts.map +1 -1
  33. package/dist/pgboss/index.mjs +140 -28
  34. package/dist/pgboss/index.mjs.map +1 -1
  35. package/dist/rabbitmq/index.cjs +135 -22
  36. package/dist/rabbitmq/index.cjs.map +1 -1
  37. package/dist/rabbitmq/index.d.cts +11 -10
  38. package/dist/rabbitmq/index.d.cts.map +1 -1
  39. package/dist/rabbitmq/index.d.mts +11 -10
  40. package/dist/rabbitmq/index.d.mts.map +1 -1
  41. package/dist/rabbitmq/index.mjs +130 -22
  42. package/dist/rabbitmq/index.mjs.map +1 -1
  43. package/dist/{chunk-CsX-DzYB.cjs → rolldown-runtime-C0BPl7ul.cjs} +17 -16
  44. package/dist/rolldown-runtime-D7D4PA-g.mjs +13 -0
  45. package/dist/sns/index.cjs +371 -34
  46. package/dist/sns/index.cjs.map +1 -1
  47. package/dist/sns/index.d.cts +167 -11
  48. package/dist/sns/index.d.cts.map +1 -1
  49. package/dist/sns/index.d.mts +167 -11
  50. package/dist/sns/index.d.mts.map +1 -1
  51. package/dist/sns/index.mjs +340 -18
  52. package/dist/sns/index.mjs.map +1 -1
  53. package/dist/sns/snsUrl.cjs +91 -6
  54. package/dist/sns/snsUrl.cjs.map +1 -0
  55. package/dist/sns/snsUrl.d.cts +57 -2
  56. package/dist/sns/snsUrl.d.cts.map +1 -0
  57. package/dist/sns/snsUrl.d.mts +57 -2
  58. package/dist/sns/snsUrl.d.mts.map +1 -0
  59. package/dist/sns/snsUrl.mjs +81 -2
  60. package/dist/sns/snsUrl.mjs.map +1 -0
  61. package/dist/sqs/index.cjs +34 -24
  62. package/dist/sqs/index.cjs.map +1 -1
  63. package/dist/sqs/index.d.cts +19 -11
  64. package/dist/sqs/index.d.cts.map +1 -1
  65. package/dist/sqs/index.d.mts +19 -11
  66. package/dist/sqs/index.d.mts.map +1 -1
  67. package/dist/sqs/index.mjs +22 -15
  68. package/dist/sqs/index.mjs.map +1 -1
  69. package/dist/sqs/sqsUrl.cjs +92 -6
  70. package/dist/sqs/sqsUrl.cjs.map +1 -0
  71. package/dist/sqs/sqsUrl.d.cts +56 -2
  72. package/dist/sqs/sqsUrl.d.cts.map +1 -0
  73. package/dist/sqs/sqsUrl.d.mts +56 -2
  74. package/dist/sqs/sqsUrl.d.mts.map +1 -0
  75. package/dist/sqs/sqsUrl.mjs +82 -2
  76. package/dist/sqs/sqsUrl.mjs.map +1 -0
  77. package/dist/types-BcZjLxVV.cjs +51 -0
  78. package/dist/types-BcZjLxVV.cjs.map +1 -0
  79. package/dist/types-Bk5JM6MP.mjs +40 -0
  80. package/dist/types-Bk5JM6MP.mjs.map +1 -0
  81. package/dist/types-gGaoT8qc.d.cts +149 -0
  82. package/dist/types-gGaoT8qc.d.cts.map +1 -0
  83. package/dist/types-gGaoT8qc.d.mts +149 -0
  84. package/dist/types-gGaoT8qc.d.mts.map +1 -0
  85. package/package.json +3 -3
  86. package/dist/PgBossConnection-C3qYNfm5.cjs +0 -80
  87. package/dist/PgBossConnection-C3qYNfm5.cjs.map +0 -1
  88. package/dist/PgBossConnection-Cvgv2l1G.mjs +0 -74
  89. package/dist/PgBossConnection-Cvgv2l1G.mjs.map +0 -1
  90. package/dist/PgBossConnection-DCUxQLZz.cjs +0 -4
  91. package/dist/PgBossConnection-DbO0ypJf.mjs +0 -4
  92. package/dist/RabbitMQConnection-8PbOD8hM.cjs +0 -4
  93. package/dist/RabbitMQConnection-Bpx3Lgij.mjs +0 -4
  94. package/dist/RabbitMQConnection-DGAk4aUb.cjs +0 -113
  95. package/dist/RabbitMQConnection-DGAk4aUb.cjs.map +0 -1
  96. package/dist/RabbitMQConnection-aE0dtb63.mjs +0 -107
  97. package/dist/RabbitMQConnection-aE0dtb63.mjs.map +0 -1
  98. package/dist/SNSConnection-B2K86hpz.cjs +0 -75
  99. package/dist/SNSConnection-B2K86hpz.cjs.map +0 -1
  100. package/dist/SNSConnection-BVo84FeW.mjs +0 -5
  101. package/dist/SNSConnection-DIHRjrQF.cjs +0 -5
  102. package/dist/SNSConnection-v9jjF99w.mjs +0 -69
  103. package/dist/SNSConnection-v9jjF99w.mjs.map +0 -1
  104. package/dist/SQSConnection-4tBMkVZE.mjs +0 -69
  105. package/dist/SQSConnection-4tBMkVZE.mjs.map +0 -1
  106. package/dist/SQSConnection-BBLAvF-1.cjs +0 -5
  107. package/dist/SQSConnection-BaC7rGhI.cjs +0 -75
  108. package/dist/SQSConnection-BaC7rGhI.cjs.map +0 -1
  109. package/dist/SQSConnection-DbavOq8_.mjs +0 -5
  110. package/dist/SQSSubscriber-BMWZ3s5q.mjs.map +0 -1
  111. package/dist/SQSSubscriber-YXPlg2rm.cjs.map +0 -1
  112. package/dist/chunk-B9dir_RE.mjs +0 -11
  113. package/dist/snsUrl-ASdc0D1v.d.cts +0 -61
  114. package/dist/snsUrl-ASdc0D1v.d.cts.map +0 -1
  115. package/dist/snsUrl-CifYhPAa.mjs +0 -79
  116. package/dist/snsUrl-CifYhPAa.mjs.map +0 -1
  117. package/dist/snsUrl-FRbRcwF0.d.mts +0 -61
  118. package/dist/snsUrl-FRbRcwF0.d.mts.map +0 -1
  119. package/dist/snsUrl-f2gU3CzK.cjs +0 -114
  120. package/dist/snsUrl-f2gU3CzK.cjs.map +0 -1
  121. package/dist/sqsUrl-BV6zS-mi.d.mts +0 -60
  122. package/dist/sqsUrl-BV6zS-mi.d.mts.map +0 -1
  123. package/dist/sqsUrl-CbAsBFHI.mjs +0 -80
  124. package/dist/sqsUrl-CbAsBFHI.mjs.map +0 -1
  125. package/dist/sqsUrl-CdisN3GU.cjs +0 -115
  126. package/dist/sqsUrl-CdisN3GU.cjs.map +0 -1
  127. package/dist/sqsUrl-DZDPHxIk.d.cts +0 -60
  128. package/dist/sqsUrl-DZDPHxIk.d.cts.map +0 -1
  129. package/dist/types-BmixGrOD.d.cts +0 -67
  130. package/dist/types-BmixGrOD.d.cts.map +0 -1
  131. package/dist/types-Byv8N9ma.cjs +0 -24
  132. package/dist/types-Byv8N9ma.cjs.map +0 -1
  133. package/dist/types-CYDt-2RQ.mjs +0 -18
  134. package/dist/types-CYDt-2RQ.mjs.map +0 -1
  135. package/dist/types-DycjsYms.d.mts +0 -67
  136. package/dist/types-DycjsYms.d.mts.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","names":["connection: RabbitMQConnection","options: RabbitMQPublisherOptions","connectionString: string","messages: TMessage[]","connection: RabbitMQConnection","options: RabbitMQSubscriberOptions","connectionString: string","messages: TMessage['type'][]","listener: (payload: TMessage) => Promise<void>","queueOptions: amqplib.Options.AssertQueue","fullMessage: TMessage"],"sources":["../../src/rabbitmq/RabbitMQPublisher.ts","../../src/rabbitmq/RabbitMQSubscriber.ts"],"sourcesContent":["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":";;;;AAQA,IAAa,oBAAb,MAAa,kBAEb;CACC,YACSA,YACAC,UAAoC,CAAE,GAC7C;EAFO;EACA;CACL;;;;;CAMJ,aAAa,qBAEXC,kBAAgE;EACjE,MAAM,EAAE,0CAAoB,GAAG,MAAM,OAAO;EAC5C,MAAM,aACL,MAAM,qBAAmB,qBAAqB,iBAAiB;AAChE,SAAO,IAAI,kBAA4B;CACvC;CAED,MAAM,QAAQC,UAAqC;AAClD,OAAK,KAAK,WAAW,aAAa,CACjC,OAAM,KAAK,WAAW,SAAS;EAGhC,MAAM,UAAU,KAAK,WAAW;AAChC,OAAK,QACJ,OAAM,IAAI,MAAM;EAGjB,MAAM,WAAW,KAAK,WAAW;AAEjC,OAAK,MAAM,WAAW,UAAU;GAC/B,MAAM,UAAU,OAAO,KAAK,KAAK,UAAU,QAAQ,QAAQ,CAAC;GAG5D,MAAM,aAAa,QAAQ;GAE3B,MAAM,YAAY,QAAQ,QAAQ,UAAU,YAAY,SAAS;IAChE,aAAa;IACb,WAAW,KAAK,KAAK;IACrB,MAAM,QAAQ;IACd,YAAY;IACZ,GAAG,KAAK,QAAQ;GAChB,EAAC;AAGF,QAAK,UACJ,OAAM,IAAI,QAAQ,CAAC,YAAY,QAAQ,KAAK,SAAS,QAAQ;EAE9D;CACD;CAED,MAAM,QAAuB,CAG5B;AACD;;;;ACvDD,IAAa,qBAAb,MAAa,mBAGb;CACC,YACSC,YACAC,UAAqC,CAAE,GAC9C;EAFO;EACA;CACL;;;;;CAMJ,aAAa,qBAEXC,kBAAiE;EAClE,MAAM,MAAM,IAAI,IAAI;EACpB,MAAM,SAAS,IAAI;EAEnB,MAAM,EAAE,0CAAoB,GAAG,MAAM,OAAO;EAC5C,MAAM,aACL,MAAM,qBAAmB,qBAAqB,iBAAiB;EAEhE,MAAMD,UAAqC;GAC1C,WAAW,OAAO,IAAI,YAAY;GAClC,UAAU,OAAO,IAAI,WAAW,GAC7B,OAAO,SAAS,OAAO,IAAI,WAAW,EAAG,GAAG;EAE/C;AAED,SAAO,IAAI,mBAA6B,YAAY;CACpD;CAED,MAAM,UACLE,UACAC,UACgB;AAChB,OAAK,KAAK,WAAW,aAAa,CACjC,OAAM,KAAK,WAAW,SAAS;EAGhC,MAAM,UAAU,KAAK,WAAW;AAChC,OAAK,QACJ,OAAM,IAAI,MAAM;EAGjB,MAAM,WAAW,KAAK,WAAW;AAGjC,MAAI,KAAK,QAAQ,SAChB,OAAM,QAAQ,SAAS,KAAK,QAAQ,SAAS;EAI9C,MAAM,YAAY,KAAK,QAAQ,aAAa;EAC5C,MAAMC,eAA4C;GACjD,SAAS;GACT,GAAG,KAAK,QAAQ;EAChB;EAED,MAAM,EAAE,OAAO,GAAG,MAAM,QAAQ,YAAY,WAAW,aAAa;AAGpE,OAAK,MAAM,eAAe,SACzB,OAAM,QAAQ,UAAU,OAAO,UAAU,YAAY;AAItD,QAAM,QAAQ,QACb,OACA,OAAO,QAAQ;AACd,QAAK,IAAK;AAEV,OAAI;IAEH,MAAM,UAAU,IAAI,QAAQ,UAAU;IACtC,MAAM,UAAU,KAAK,MAAM,QAAQ;IAGnC,MAAM,cAAc,IAAI,WAAW;IAGnC,MAAMC,cAAwB;KAC7B,MAAM;KACN;IACA;AAGD,UAAM,SAAS,YAAY;AAG3B,YAAQ,IAAI,IAAI;GAChB,SAAQ,QAAQ;AAChB,YAAQ,KAAK,KAAK,OAAO,KAAK;GAC9B;EACD,GACD,KAAK,QAAQ,eACb;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,15 +1,18 @@
1
- //#region rolldown:runtime
1
+ //#region \0rolldown/runtime.js
2
2
  var __create = Object.create;
3
3
  var __defProp = Object.defineProperty;
4
4
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
5
  var __getOwnPropNames = Object.getOwnPropertyNames;
6
6
  var __getProtoOf = Object.getPrototypeOf;
7
7
  var __hasOwnProp = Object.prototype.hasOwnProperty;
8
- var __export = (target, all) => {
8
+ var __exportAll = (all, no_symbols) => {
9
+ let target = {};
9
10
  for (var name in all) __defProp(target, name, {
10
11
  get: all[name],
11
12
  enumerable: true
12
13
  });
14
+ if (!no_symbols) __defProp(target, Symbol.toStringTag, { value: "Module" });
15
+ return target;
13
16
  };
14
17
  var __copyProps = (to, from, except, desc) => {
15
18
  if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
@@ -21,22 +24,20 @@ var __copyProps = (to, from, except, desc) => {
21
24
  }
22
25
  return to;
23
26
  };
24
- var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
27
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule || !__hasOwnProp.call(mod, "default") ? __defProp(target, "default", {
25
28
  value: mod,
26
29
  enumerable: true
27
30
  }) : target, mod));
28
-
29
31
  //#endregion
30
-
31
- Object.defineProperty(exports, '__export', {
32
- enumerable: true,
33
- get: function () {
34
- return __export;
35
- }
32
+ Object.defineProperty(exports, "__exportAll", {
33
+ enumerable: true,
34
+ get: function() {
35
+ return __exportAll;
36
+ }
37
+ });
38
+ Object.defineProperty(exports, "__toESM", {
39
+ enumerable: true,
40
+ get: function() {
41
+ return __toESM;
42
+ }
36
43
  });
37
- Object.defineProperty(exports, '__toESM', {
38
- enumerable: true,
39
- get: function () {
40
- return __toESM;
41
- }
42
- });
@@ -0,0 +1,13 @@
1
+ //#region \0rolldown/runtime.js
2
+ var __defProp = Object.defineProperty;
3
+ var __exportAll = (all, no_symbols) => {
4
+ let target = {};
5
+ for (var name in all) __defProp(target, name, {
6
+ get: all[name],
7
+ enumerable: true
8
+ });
9
+ if (!no_symbols) __defProp(target, Symbol.toStringTag, { value: "Module" });
10
+ return target;
11
+ };
12
+ //#endregion
13
+ export { __exportAll as t };
@@ -1,15 +1,326 @@
1
- const require_chunk = require('../chunk-CsX-DzYB.cjs');
2
- require('../types-Byv8N9ma.cjs');
3
- const require_snsUrl = require('../snsUrl-f2gU3CzK.cjs');
4
- const require_SNSConnection = require('../SNSConnection-B2K86hpz.cjs');
5
- require('../sqsUrl-CdisN3GU.cjs');
6
- const require_SQSConnection = require('../SQSConnection-BaC7rGhI.cjs');
7
- const require_SQSSubscriber = require('../SQSSubscriber-YXPlg2rm.cjs');
8
- const __aws_sdk_client_sns = require_chunk.__toESM(require("@aws-sdk/client-sns"));
9
- const __aws_sdk_client_sqs = require_chunk.__toESM(require("@aws-sdk/client-sqs"));
10
-
1
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
+ require("../types-BcZjLxVV.cjs");
3
+ const require_SQSSubscriber = require("../SQSSubscriber-DtRUaZB3.cjs");
4
+ const require_sns_snsUrl = require("./snsUrl.cjs");
5
+ let _aws_sdk_client_sqs = require("@aws-sdk/client-sqs");
6
+ let node_crypto = require("node:crypto");
7
+ let _aws_sdk_client_sns = require("@aws-sdk/client-sns");
8
+ //#region src/sns/push.ts
9
+ /** The fields signed, in the order SNS signs them, per message type. */
10
+ const SIGNED_FIELDS = {
11
+ Notification: [
12
+ "Message",
13
+ "MessageId",
14
+ "Subject",
15
+ "Timestamp",
16
+ "TopicArn",
17
+ "Type"
18
+ ],
19
+ SubscriptionConfirmation: [
20
+ "Message",
21
+ "MessageId",
22
+ "SubscribeURL",
23
+ "Timestamp",
24
+ "Token",
25
+ "TopicArn",
26
+ "Type"
27
+ ],
28
+ UnsubscribeConfirmation: [
29
+ "Message",
30
+ "MessageId",
31
+ "SubscribeURL",
32
+ "Timestamp",
33
+ "Token",
34
+ "TopicArn",
35
+ "Type"
36
+ ]
37
+ };
38
+ /**
39
+ * Where SNS serves its signing certificates. Anywhere else is a certificate
40
+ * the sender chose, which would let the sender sign its own forgeries.
41
+ */
42
+ const SNS_CERTIFICATE_HOST = /^sns\.[a-z0-9-]+\.amazonaws\.com(\.cn)?$/;
43
+ const certificates = /* @__PURE__ */ new Map();
44
+ async function defaultFetchCertificate(url) {
45
+ const response = await fetch(url);
46
+ if (!response.ok) throw new SnsCertificateUnavailable(url, response.status);
47
+ return response.text();
48
+ }
49
+ /**
50
+ * A URL, or undefined for a value that is not one at all — what an emulator
51
+ * sends — so it is untrusted rather than a crash. (`URL.parse` is Node 22.1+.)
52
+ */
53
+ function parseUrl(value) {
54
+ try {
55
+ return new URL(String(value));
56
+ } catch {
57
+ return;
58
+ }
59
+ }
60
+ /** The string SNS signed for this message. */
61
+ function snsStringToSign(message) {
62
+ const fields = SIGNED_FIELDS[message.Type];
63
+ if (!fields) throw new SnsMessageTypeUnknown(String(message.Type));
64
+ return fields.filter((field) => message[field] !== void 0).map((field) => `${field}\n${message[field]}\n`).join("");
65
+ }
66
+ /**
67
+ * Check that SNS sent this message: the certificate is SNS's own, and the
68
+ * signature over the message's signed fields verifies against it.
69
+ *
70
+ * Throws rather than returning false, so a caller cannot forget to look.
71
+ */
72
+ async function verifySnsMessage(message, options = {}) {
73
+ const url = parseUrl(message.SigningCertURL);
74
+ if (url?.protocol !== "https:" || !SNS_CERTIFICATE_HOST.test(url.hostname)) throw new SnsCertificateUntrusted(message.SigningCertURL);
75
+ const algorithm = message.SignatureVersion === "1" ? "RSA-SHA1" : message.SignatureVersion === "2" ? "RSA-SHA256" : void 0;
76
+ if (!algorithm) throw new SnsSignatureVersionUnknown(message.SignatureVersion);
77
+ const fetchCertificate = options.fetchCertificate ?? defaultFetchCertificate;
78
+ let certificate = certificates.get(message.SigningCertURL);
79
+ if (!certificate || options.fetchCertificate) {
80
+ certificate = fetchCertificate(message.SigningCertURL);
81
+ if (!options.fetchCertificate) {
82
+ certificates.set(message.SigningCertURL, certificate);
83
+ certificate.catch(() => certificates.delete(message.SigningCertURL));
84
+ }
85
+ }
86
+ const verifier = (0, node_crypto.createVerify)(algorithm);
87
+ verifier.update(snsStringToSign(message), "utf8");
88
+ if (!verifier.verify(await certificate, message.Signature, "base64")) throw new SnsSignatureInvalid(message.MessageId, message.TopicArn);
89
+ }
90
+ /**
91
+ * Confirm a subscription by visiting the URL SNS sent. Until this happens SNS
92
+ * delivers nothing to the endpoint.
93
+ */
94
+ async function confirmSnsSubscription(message, request = fetch) {
95
+ if (message.Type !== "SubscriptionConfirmation" || !message.SubscribeURL) throw new SnsNotAConfirmation(message.Type);
96
+ const response = await request(message.SubscribeURL);
97
+ if (!response.ok) throw new SnsConfirmationFailed(message.TopicArn, response.status);
98
+ }
99
+ /**
100
+ * Subscribe an HTTP endpoint to the connection's topic, converging on one
101
+ * confirmed subscription with the filter asked for — so it is safe on every
102
+ * start:
103
+ *
104
+ * - **none yet** — subscribed; SNS sends a confirmation to the endpoint.
105
+ * - **confirmed** — its filter and dead-letter queue are brought up to date.
106
+ * Subscribing again with different attributes is an error on SNS, and the
107
+ * events a subscriber names change.
108
+ * - **stuck pending** — a confirmation that never landed (the endpoint was
109
+ * down, or answered wrong). Subscribing the same endpoint again is a no-op
110
+ * on an emulator, so it is dropped and subscribed afresh, which sends a new
111
+ * confirmation.
112
+ */
113
+ async function subscribeHttpEndpoint(connection, options) {
114
+ const protocol = new URL(options.endpoint).protocol.replace(":", "");
115
+ if (protocol !== "http" && protocol !== "https") throw new SnsEndpointNotHttp(options.endpoint);
116
+ const attributes = {
117
+ FilterPolicyScope: "MessageAttributes",
118
+ FilterPolicy: JSON.stringify({ type: [...options.events] })
119
+ };
120
+ if (options.deadLetterQueueArn) attributes.RedrivePolicy = JSON.stringify({ deadLetterTargetArn: options.deadLetterQueueArn });
121
+ const client = connection.snsClient;
122
+ const existing = await existingSubscription(connection, options.endpoint);
123
+ if (existing) {
124
+ const { Attributes = {} } = await client.send(new _aws_sdk_client_sns.GetSubscriptionAttributesCommand({ SubscriptionArn: existing }));
125
+ if (Attributes.PendingConfirmation === "true") await client.send(new _aws_sdk_client_sns.UnsubscribeCommand({ SubscriptionArn: existing }));
126
+ else {
127
+ for (const [name, value] of Object.entries(attributes)) {
128
+ if (Attributes[name] === value) continue;
129
+ await client.send(new _aws_sdk_client_sns.SetSubscriptionAttributesCommand({
130
+ SubscriptionArn: existing,
131
+ AttributeName: name,
132
+ AttributeValue: value
133
+ }));
134
+ }
135
+ return existing;
136
+ }
137
+ }
138
+ const { SubscriptionArn } = await client.send(new _aws_sdk_client_sns.SubscribeCommand({
139
+ TopicArn: connection.topicArn,
140
+ Protocol: protocol,
141
+ Endpoint: options.endpoint,
142
+ Attributes: attributes,
143
+ ReturnSubscriptionArn: true
144
+ }));
145
+ return SubscriptionArn;
146
+ }
147
+ /**
148
+ * The ARN of the endpoint's subscription to the topic, if it has one SNS will
149
+ * name. (Real SNS lists a pending one as `PendingConfirmation`, with no ARN to
150
+ * act on — and resends its confirmation on a new Subscribe anyway.)
151
+ */
152
+ async function existingSubscription(connection, endpoint) {
153
+ let NextToken;
154
+ do {
155
+ const page = await connection.snsClient.send(new _aws_sdk_client_sns.ListSubscriptionsByTopicCommand({
156
+ TopicArn: connection.topicArn,
157
+ NextToken
158
+ }));
159
+ const match = page.Subscriptions?.find((s) => s.Endpoint === endpoint && s.SubscriptionArn?.startsWith("arn:"));
160
+ if (match) return match.SubscriptionArn;
161
+ NextToken = page.NextToken;
162
+ } while (NextToken);
163
+ }
164
+ /**
165
+ * A pushed notification as the Lambda event SNS would have invoked a function
166
+ * with, so it can be handed to the same adaptor.
167
+ */
168
+ function toSnsEvent(message, subscriptionArn = "") {
169
+ return { Records: [{
170
+ EventSource: "aws:sns",
171
+ EventVersion: "1.0",
172
+ EventSubscriptionArn: subscriptionArn,
173
+ Sns: {
174
+ Type: message.Type,
175
+ MessageId: message.MessageId,
176
+ TopicArn: message.TopicArn,
177
+ Subject: message.Subject ?? null,
178
+ Message: message.Message,
179
+ Timestamp: message.Timestamp,
180
+ SignatureVersion: message.SignatureVersion,
181
+ Signature: message.Signature,
182
+ SigningCertUrl: message.SigningCertURL,
183
+ UnsubscribeUrl: message.UnsubscribeURL ?? "",
184
+ MessageAttributes: message.MessageAttributes ?? {}
185
+ }
186
+ }] };
187
+ }
188
+ var SnsCertificateUntrusted = class extends Error {
189
+ url;
190
+ constructor(url) {
191
+ super(`The SNS message names a signing certificate at '${url}', which is not an https URL on sns.<region>.amazonaws.com. Only SNS can send to this endpoint; reject the request.`);
192
+ this.url = url;
193
+ this.name = "SnsCertificateUntrusted";
194
+ }
195
+ };
196
+ var SnsCertificateUnavailable = class extends Error {
197
+ url;
198
+ status;
199
+ constructor(url, status) {
200
+ super(`Fetching the SNS signing certificate at '${url}' answered ${status}. The message cannot be verified, so it is not handled; SNS will retry.`);
201
+ this.url = url;
202
+ this.status = status;
203
+ this.name = "SnsCertificateUnavailable";
204
+ }
205
+ };
206
+ var SnsSignatureVersionUnknown = class extends Error {
207
+ version;
208
+ constructor(version) {
209
+ super(`SNS signature version '${version}' is not one this endpoint verifies (it knows '1' and '2'). Reject the request.`);
210
+ this.version = version;
211
+ this.name = "SnsSignatureVersionUnknown";
212
+ }
213
+ };
214
+ var SnsSignatureInvalid = class extends Error {
215
+ messageId;
216
+ topicArn;
217
+ constructor(messageId, topicArn) {
218
+ super(`SNS message '${messageId}' from '${topicArn}' does not verify against its signing certificate. It was not sent by SNS, or was altered on the way; reject it.`);
219
+ this.messageId = messageId;
220
+ this.topicArn = topicArn;
221
+ this.name = "SnsSignatureInvalid";
222
+ }
223
+ };
224
+ var SnsMessageTypeUnknown = class extends Error {
225
+ type;
226
+ constructor(type) {
227
+ super(`'${type}' is not an SNS message type (Notification, SubscriptionConfirmation, UnsubscribeConfirmation). Reject it.`);
228
+ this.type = type;
229
+ this.name = "SnsMessageTypeUnknown";
230
+ }
231
+ };
232
+ var SnsNotAConfirmation = class extends Error {
233
+ type;
234
+ constructor(type) {
235
+ super(`Only a SubscriptionConfirmation carries a URL to confirm; this is a '${type}'.`);
236
+ this.type = type;
237
+ this.name = "SnsNotAConfirmation";
238
+ }
239
+ };
240
+ var SnsConfirmationFailed = class extends Error {
241
+ topicArn;
242
+ status;
243
+ constructor(topicArn, status) {
244
+ super(`Confirming the subscription to '${topicArn}' answered ${status}. SNS will deliver nothing until it is confirmed; it resends the confirmation, or subscribe the endpoint again.`);
245
+ this.topicArn = topicArn;
246
+ this.status = status;
247
+ this.name = "SnsConfirmationFailed";
248
+ }
249
+ };
250
+ var SnsEndpointNotHttp = class extends Error {
251
+ endpoint;
252
+ constructor(endpoint) {
253
+ super(`'${endpoint}' is not an http(s) URL. SNS pushes to an HTTP endpoint; subscribe a queue for anything else.`);
254
+ this.endpoint = endpoint;
255
+ this.name = "SnsEndpointNotHttp";
256
+ }
257
+ };
258
+ //#endregion
259
+ //#region src/sns/SNSConnection.ts
260
+ var SNSConnection = class SNSConnection {
261
+ type = "sns";
262
+ client;
263
+ connected = false;
264
+ config;
265
+ constructor(config) {
266
+ this.config = config;
267
+ const clientConfig = {
268
+ region: config.region,
269
+ endpoint: config.endpoint,
270
+ credentials: config.credentials
271
+ };
272
+ this.client = new _aws_sdk_client_sns.SNSClient(clientConfig);
273
+ }
274
+ /**
275
+ * Create an SNSConnection from a connection string
276
+ * Format: sns://?topicArn=arn:aws:sns:region:account:topic&region=us-east-1&endpoint=http://localhost:4566
277
+ */
278
+ static async fromConnectionString(connectionString) {
279
+ const { topicArn, region, endpoint } = require_sns_snsUrl.parse(connectionString);
280
+ const params = new URL(connectionString).searchParams;
281
+ const config = {
282
+ topicArn,
283
+ region,
284
+ endpoint
285
+ };
286
+ const accessKeyId = params.get("accessKeyId");
287
+ const secretAccessKey = params.get("secretAccessKey");
288
+ if (accessKeyId && secretAccessKey) config.credentials = {
289
+ accessKeyId,
290
+ secretAccessKey,
291
+ sessionToken: params.get("sessionToken") || void 0
292
+ };
293
+ const connection = new SNSConnection(config);
294
+ await connection.connect();
295
+ return connection;
296
+ }
297
+ async connect() {
298
+ this.connected = true;
299
+ }
300
+ async close() {
301
+ this.client.destroy();
302
+ this.connected = false;
303
+ }
304
+ isConnected() {
305
+ return this.connected;
306
+ }
307
+ /**
308
+ * Get the underlying SNS client
309
+ */
310
+ get snsClient() {
311
+ return this.client;
312
+ }
313
+ /**
314
+ * Get the topic ARN for this connection
315
+ */
316
+ get topicArn() {
317
+ return this.config.topicArn;
318
+ }
319
+ };
320
+ //#endregion
11
321
  //#region src/sns/SNSPublisher.ts
12
322
  var SNSPublisher = class SNSPublisher {
323
+ connection;
13
324
  constructor(connection, _options = {}) {
14
325
  this.connection = connection;
15
326
  }
@@ -18,8 +329,8 @@ var SNSPublisher = class SNSPublisher {
18
329
  * Format: sns://?topicArn=arn:aws:sns:region:account:topic&region=us-east-1&endpoint=http://localhost:4566
19
330
  */
20
331
  static async fromConnectionString(connectionString) {
21
- const { SNSConnection: SNSConnection$1 } = await Promise.resolve().then(() => require("../SNSConnection-DIHRjrQF.cjs"));
22
- const connection = await SNSConnection$1.fromConnectionString(connectionString);
332
+ const { SNSConnection } = await Promise.resolve().then(() => require("../SNSConnection-BWDC1wAF.cjs"));
333
+ const connection = await SNSConnection.fromConnectionString(connectionString);
23
334
  return new SNSPublisher(connection);
24
335
  }
25
336
  async publish(messages) {
@@ -28,7 +339,7 @@ var SNSPublisher = class SNSPublisher {
28
339
  await Promise.all(messages.map((message) => this.publishMessage(message)));
29
340
  }
30
341
  async publishMessage(message) {
31
- const command = new __aws_sdk_client_sns.PublishCommand({
342
+ const command = new _aws_sdk_client_sns.PublishCommand({
32
343
  TopicArn: this.connection.topicArn,
33
344
  Message: JSON.stringify({
34
345
  type: message.type,
@@ -43,7 +354,6 @@ var SNSPublisher = class SNSPublisher {
43
354
  }
44
355
  async close() {}
45
356
  };
46
-
47
357
  //#endregion
48
358
  //#region src/sns/SNSSubscriber.ts
49
359
  /**
@@ -64,6 +374,7 @@ var SNSPublisher = class SNSPublisher {
64
374
  * Messages are pushed from SNS to SQS, and we receive those pushed messages.
65
375
  */
66
376
  var SNSSubscriber = class SNSSubscriber {
377
+ connection;
67
378
  sqsClient;
68
379
  queueUrl;
69
380
  queueArn;
@@ -79,7 +390,7 @@ var SNSSubscriber = class SNSSubscriber {
79
390
  waitTimeSeconds: options.waitTimeSeconds ?? 20,
80
391
  maxMessages: options.maxMessages ?? 10
81
392
  };
82
- this.sqsClient = new __aws_sdk_client_sqs.SQSClient({
393
+ this.sqsClient = new _aws_sdk_client_sqs.SQSClient({
83
394
  region: this.connection.config.region,
84
395
  endpoint: this.connection.config.endpoint,
85
396
  credentials: this.connection.config.credentials
@@ -90,10 +401,9 @@ var SNSSubscriber = class SNSSubscriber {
90
401
  * Format: sns://?topicArn=...&queueName=myqueue&region=us-east-1
91
402
  */
92
403
  static async fromConnectionString(connectionString) {
93
- const url = new URL(connectionString);
94
- const params = url.searchParams;
95
- const { SNSConnection: SNSConnection$1 } = await Promise.resolve().then(() => require("../SNSConnection-DIHRjrQF.cjs"));
96
- const connection = await SNSConnection$1.fromConnectionString(connectionString);
404
+ const params = new URL(connectionString).searchParams;
405
+ const { SNSConnection } = await Promise.resolve().then(() => require("../SNSConnection-BWDC1wAF.cjs"));
406
+ const connection = await SNSConnection.fromConnectionString(connectionString);
97
407
  const options = {
98
408
  queueName: params.get("queueName") || void 0,
99
409
  createQueue: params.get("createQueue") !== "false",
@@ -105,7 +415,7 @@ var SNSSubscriber = class SNSSubscriber {
105
415
  async subscribe(messages, listener) {
106
416
  await this.setupQueue();
107
417
  await this.subscribeToTopic();
108
- const sqsConnection = new require_SQSConnection.SQSConnection({
418
+ const sqsConnection = new require_SQSSubscriber.SQSConnection({
109
419
  queueUrl: this.queueUrl,
110
420
  region: this.connection.config.region,
111
421
  endpoint: this.connection.config.endpoint,
@@ -125,22 +435,22 @@ var SNSSubscriber = class SNSSubscriber {
125
435
  async stop() {
126
436
  if (this.sqsSubscriber) this.sqsSubscriber.stop();
127
437
  if (this.subscriptionArn) try {
128
- const command = new __aws_sdk_client_sns.UnsubscribeCommand({ SubscriptionArn: this.subscriptionArn });
438
+ const command = new _aws_sdk_client_sns.UnsubscribeCommand({ SubscriptionArn: this.subscriptionArn });
129
439
  await this.connection.snsClient.send(command);
130
440
  } catch (_error) {}
131
441
  if (this.queueUrl && this.options.deleteQueueOnClose) try {
132
- const command = new __aws_sdk_client_sqs.DeleteQueueCommand({ QueueUrl: this.queueUrl });
442
+ const command = new _aws_sdk_client_sqs.DeleteQueueCommand({ QueueUrl: this.queueUrl });
133
443
  await this.sqsClient.send(command);
134
444
  } catch (_error) {}
135
445
  this.sqsClient.destroy();
136
446
  }
137
447
  async setupQueue() {
138
448
  if (this.options.createQueue) {
139
- const createCommand = new __aws_sdk_client_sqs.CreateQueueCommand({ QueueName: this.options.queueName });
449
+ const createCommand = new _aws_sdk_client_sqs.CreateQueueCommand({ QueueName: this.options.queueName });
140
450
  const response = await this.sqsClient.send(createCommand);
141
451
  this.queueUrl = response.QueueUrl;
142
- } else throw new Error("Queue must exist or createQueue option must be true");
143
- const attrsCommand = new __aws_sdk_client_sqs.GetQueueAttributesCommand({
452
+ } else throw new SnsQueueMissing(this.options.queueName);
453
+ const attrsCommand = new _aws_sdk_client_sqs.GetQueueAttributesCommand({
144
454
  QueueUrl: this.queueUrl,
145
455
  AttributeNames: ["QueueArn"]
146
456
  });
@@ -159,14 +469,14 @@ var SNSSubscriber = class SNSSubscriber {
159
469
  Condition: { ArnEquals: { "aws:SourceArn": this.connection.topicArn } }
160
470
  }]
161
471
  };
162
- const command = new __aws_sdk_client_sqs.SetQueueAttributesCommand({
472
+ const command = new _aws_sdk_client_sqs.SetQueueAttributesCommand({
163
473
  QueueUrl: this.queueUrl,
164
474
  Attributes: { Policy: JSON.stringify(policy) }
165
475
  });
166
476
  await this.sqsClient.send(command);
167
477
  }
168
478
  async subscribeToTopic() {
169
- const command = new __aws_sdk_client_sns.SubscribeCommand({
479
+ const command = new _aws_sdk_client_sns.SubscribeCommand({
170
480
  TopicArn: this.connection.topicArn,
171
481
  Protocol: "sqs",
172
482
  Endpoint: this.queueArn
@@ -175,15 +485,42 @@ var SNSSubscriber = class SNSSubscriber {
175
485
  this.subscriptionArn = response.SubscriptionArn;
176
486
  }
177
487
  };
178
-
488
+ /**
489
+ * A subscriber asked to use a queue it may not create. The SQS queue behind an
490
+ * SNS subscription has to exist first, or `createQueue` has to allow it.
491
+ */
492
+ var SnsQueueMissing = class extends Error {
493
+ queueName;
494
+ constructor(queueName) {
495
+ super(`The SNS subscriber's queue${queueName ? ` "${queueName}"` : ""} is not created here: pass createQueue: true, or create the queue first.`);
496
+ this.queueName = queueName;
497
+ this.name = "SnsQueueMissing";
498
+ }
499
+ };
179
500
  //#endregion
180
- exports.SNSConnection = require_SNSConnection.SNSConnection;
501
+ exports.SNSConnection = SNSConnection;
502
+ exports.SNSConnection$1 = SNSConnection;
181
503
  exports.SNSPublisher = SNSPublisher;
182
504
  exports.SNSSubscriber = SNSSubscriber;
183
- Object.defineProperty(exports, 'snsUrl', {
184
- enumerable: true,
185
- get: function () {
186
- return require_snsUrl.snsUrl_exports;
187
- }
505
+ exports.SnsCertificateUnavailable = SnsCertificateUnavailable;
506
+ exports.SnsCertificateUntrusted = SnsCertificateUntrusted;
507
+ exports.SnsConfirmationFailed = SnsConfirmationFailed;
508
+ exports.SnsEndpointNotHttp = SnsEndpointNotHttp;
509
+ exports.SnsMessageTypeUnknown = SnsMessageTypeUnknown;
510
+ exports.SnsNotAConfirmation = SnsNotAConfirmation;
511
+ exports.SnsQueueMissing = SnsQueueMissing;
512
+ exports.SnsSignatureInvalid = SnsSignatureInvalid;
513
+ exports.SnsSignatureVersionUnknown = SnsSignatureVersionUnknown;
514
+ exports.confirmSnsSubscription = confirmSnsSubscription;
515
+ exports.snsStringToSign = snsStringToSign;
516
+ Object.defineProperty(exports, "snsUrl", {
517
+ enumerable: true,
518
+ get: function() {
519
+ return require_sns_snsUrl.snsUrl_exports;
520
+ }
188
521
  });
522
+ exports.subscribeHttpEndpoint = subscribeHttpEndpoint;
523
+ exports.toSnsEvent = toSnsEvent;
524
+ exports.verifySnsMessage = verifySnsMessage;
525
+
189
526
  //# sourceMappingURL=index.cjs.map