@geekmidas/events 0.0.1 → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{BasicConnection-CzfMubv_.cjs → BasicConnection-Bz3F2rXA.cjs} +1 -1
- package/dist/{BasicConnection-CzfMubv_.cjs.map → BasicConnection-Bz3F2rXA.cjs.map} +1 -1
- package/dist/{BasicConnection-BNi6_olg.mjs → BasicConnection-DOSOFSsW.mjs} +1 -1
- package/dist/{BasicConnection-BNi6_olg.mjs.map → BasicConnection-DOSOFSsW.mjs.map} +1 -1
- package/dist/{BasicPublisher-Cg3SxIVR.mjs → BasicPublisher-CaX8GCUi.mjs} +1 -1
- package/dist/{BasicPublisher-Cg3SxIVR.mjs.map → BasicPublisher-CaX8GCUi.mjs.map} +1 -1
- package/dist/{BasicPublisher-BzfzHIfb.cjs → BasicPublisher-afwuA4Xp.cjs} +1 -1
- package/dist/{BasicPublisher-BzfzHIfb.cjs.map → BasicPublisher-afwuA4Xp.cjs.map} +1 -1
- package/dist/{BasicSubscriber-DyTKOixe.cjs → BasicSubscriber-Ckf3BkxS.cjs} +1 -1
- package/dist/{BasicSubscriber-DyTKOixe.cjs.map → BasicSubscriber-Ckf3BkxS.cjs.map} +1 -1
- package/dist/{BasicSubscriber-BEv5N21f.mjs → BasicSubscriber-DPpJNUUY.mjs} +1 -1
- package/dist/{BasicSubscriber-BEv5N21f.mjs.map → BasicSubscriber-DPpJNUUY.mjs.map} +1 -1
- package/dist/{SNSConnection-DVsC_pbB.cjs → SNSConnection-CZz_U56V.cjs} +1 -1
- package/dist/{SNSConnection-DVsC_pbB.cjs.map → SNSConnection-CZz_U56V.cjs.map} +1 -1
- package/dist/{SNSConnection-D7wCf9Si.mjs → SNSConnection-DVEE3XoC.mjs} +1 -1
- package/dist/{SNSConnection-D7wCf9Si.mjs.map → SNSConnection-DVEE3XoC.mjs.map} +1 -1
- package/dist/{SNSPublisher-BZ6N3JI9.cjs → SNSPublisher-DbWVH-g_.cjs} +1 -1
- package/dist/{SNSPublisher-BZ6N3JI9.cjs.map → SNSPublisher-DbWVH-g_.cjs.map} +1 -1
- package/dist/{SNSPublisher-BfWjzNiB.mjs → SNSPublisher-Do-FMUyT.mjs} +1 -1
- package/dist/{SNSPublisher-BfWjzNiB.mjs.map → SNSPublisher-Do-FMUyT.mjs.map} +1 -1
- package/dist/{SNSSubscriber-UE0ia4XM.cjs → SNSSubscriber-DpEwQ8Tb.cjs} +3 -3
- package/dist/{SNSSubscriber-UE0ia4XM.cjs.map → SNSSubscriber-DpEwQ8Tb.cjs.map} +1 -1
- package/dist/{SNSSubscriber-C9YJWa7m.mjs → SNSSubscriber-e7eG48yJ.mjs} +3 -3
- package/dist/{SNSSubscriber-C9YJWa7m.mjs.map → SNSSubscriber-e7eG48yJ.mjs.map} +1 -1
- package/dist/{SQSConnection-Eleqqre3.mjs → SQSConnection-BBlDWZLU.mjs} +1 -1
- package/dist/{SQSConnection-Eleqqre3.mjs.map → SQSConnection-BBlDWZLU.mjs.map} +1 -1
- package/dist/{SQSConnection-BwNjUPr4.cjs → SQSConnection-DWYDtftQ.cjs} +1 -1
- package/dist/{SQSConnection-BwNjUPr4.cjs.map → SQSConnection-DWYDtftQ.cjs.map} +1 -1
- package/dist/{SQSPublisher-DG9o4ME3.mjs → SQSPublisher-B3lS2zbu.mjs} +1 -1
- package/dist/{SQSPublisher-DG9o4ME3.mjs.map → SQSPublisher-B3lS2zbu.mjs.map} +1 -1
- package/dist/{SQSPublisher-Dq5353oZ.cjs → SQSPublisher-nzbSaXSH.cjs} +1 -1
- package/dist/{SQSPublisher-Dq5353oZ.cjs.map → SQSPublisher-nzbSaXSH.cjs.map} +1 -1
- package/dist/{SQSSubscriber-CD66P7hH.mjs → SQSSubscriber-84pFnW1z.mjs} +1 -1
- package/dist/{SQSSubscriber-CD66P7hH.mjs.map → SQSSubscriber-84pFnW1z.mjs.map} +1 -1
- package/dist/{SQSSubscriber-DZDNgLE5.cjs → SQSSubscriber-DcL9BbG1.cjs} +1 -1
- package/dist/{SQSSubscriber-DZDNgLE5.cjs.map → SQSSubscriber-DcL9BbG1.cjs.map} +1 -1
- package/dist/basic/BasicConnection.cjs +1 -1
- package/dist/basic/BasicConnection.mjs +1 -1
- package/dist/basic/BasicPublisher.cjs +1 -1
- package/dist/basic/BasicPublisher.mjs +1 -1
- package/dist/basic/BasicSubscriber.cjs +1 -1
- package/dist/basic/BasicSubscriber.mjs +1 -1
- package/dist/basic/index.cjs +3 -3
- package/dist/basic/index.mjs +3 -3
- package/dist/sns/SNSConnection.cjs +1 -1
- package/dist/sns/SNSConnection.mjs +1 -1
- package/dist/sns/SNSPublisher.cjs +1 -1
- package/dist/sns/SNSPublisher.mjs +1 -1
- package/dist/sns/SNSSubscriber.cjs +3 -3
- package/dist/sns/SNSSubscriber.mjs +3 -3
- package/dist/sns/index.cjs +5 -5
- package/dist/sns/index.mjs +5 -5
- package/dist/sqs/SQSConnection.cjs +1 -1
- package/dist/sqs/SQSConnection.mjs +1 -1
- package/dist/sqs/SQSPublisher.cjs +1 -1
- package/dist/sqs/SQSPublisher.mjs +1 -1
- package/dist/sqs/SQSSubscriber.cjs +1 -1
- package/dist/sqs/SQSSubscriber.mjs +1 -1
- package/dist/sqs/index.cjs +3 -3
- package/dist/sqs/index.mjs +3 -3
- package/package.json +24 -5
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"BasicConnection-
|
|
1
|
+
{"version":3,"file":"BasicConnection-Bz3F2rXA.cjs","names":["EventPublisherType","EventEmitter","_connectionString: string"],"sources":["../src/basic/BasicConnection.ts"],"sourcesContent":["import { EventEmitter } from 'node:events';\nimport type { EventConnection } from '../types';\nimport { EventPublisherType } from '../types';\n\nexport class BasicConnection implements EventConnection {\n readonly type = EventPublisherType.Basic;\n private emitter: EventEmitter;\n private connected = false;\n\n constructor() {\n this.emitter = new EventEmitter();\n }\n\n /**\n * Create a BasicConnection from a connection string\n * Format: basic://\n */\n static async fromConnectionString(\n _connectionString: string,\n ): Promise<BasicConnection> {\n // Basic connection doesn't need any configuration\n const connection = new BasicConnection();\n await connection.connect();\n return connection;\n }\n\n async connect(): Promise<void> {\n this.connected = true;\n }\n\n async close(): Promise<void> {\n this.emitter.removeAllListeners();\n this.connected = false;\n }\n\n isConnected(): boolean {\n return this.connected;\n }\n\n /**\n * Get the underlying EventEmitter\n * Used by BasicPublisher and BasicSubscriber\n */\n get eventEmitter(): EventEmitter {\n return this.emitter;\n }\n}\n"],"mappings":";;;;;AAIA,IAAa,kBAAb,MAAa,gBAA2C;CACtD,AAAS,OAAOA,iCAAmB;CACnC,AAAQ;CACR,AAAQ,YAAY;CAEpB,cAAc;AACZ,OAAK,UAAU,IAAIC;CACpB;;;;;CAMD,aAAa,qBACXC,mBAC0B;EAE1B,MAAM,aAAa,IAAI;AACvB,QAAM,WAAW,SAAS;AAC1B,SAAO;CACR;CAED,MAAM,UAAyB;AAC7B,OAAK,YAAY;CAClB;CAED,MAAM,QAAuB;AAC3B,OAAK,QAAQ,oBAAoB;AACjC,OAAK,YAAY;CAClB;CAED,cAAuB;AACrB,SAAO,KAAK;CACb;;;;;CAMD,IAAI,eAA6B;AAC/B,SAAO,KAAK;CACb;AACF"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"BasicConnection-
|
|
1
|
+
{"version":3,"file":"BasicConnection-DOSOFSsW.mjs","names":["_connectionString: string"],"sources":["../src/basic/BasicConnection.ts"],"sourcesContent":["import { EventEmitter } from 'node:events';\nimport type { EventConnection } from '../types';\nimport { EventPublisherType } from '../types';\n\nexport class BasicConnection implements EventConnection {\n readonly type = EventPublisherType.Basic;\n private emitter: EventEmitter;\n private connected = false;\n\n constructor() {\n this.emitter = new EventEmitter();\n }\n\n /**\n * Create a BasicConnection from a connection string\n * Format: basic://\n */\n static async fromConnectionString(\n _connectionString: string,\n ): Promise<BasicConnection> {\n // Basic connection doesn't need any configuration\n const connection = new BasicConnection();\n await connection.connect();\n return connection;\n }\n\n async connect(): Promise<void> {\n this.connected = true;\n }\n\n async close(): Promise<void> {\n this.emitter.removeAllListeners();\n this.connected = false;\n }\n\n isConnected(): boolean {\n return this.connected;\n }\n\n /**\n * Get the underlying EventEmitter\n * Used by BasicPublisher and BasicSubscriber\n */\n get eventEmitter(): EventEmitter {\n return this.emitter;\n }\n}\n"],"mappings":";;;;AAIA,IAAa,kBAAb,MAAa,gBAA2C;CACtD,AAAS,OAAO,mBAAmB;CACnC,AAAQ;CACR,AAAQ,YAAY;CAEpB,cAAc;AACZ,OAAK,UAAU,IAAI;CACpB;;;;;CAMD,aAAa,qBACXA,mBAC0B;EAE1B,MAAM,aAAa,IAAI;AACvB,QAAM,WAAW,SAAS;AAC1B,SAAO;CACR;CAED,MAAM,UAAyB;AAC7B,OAAK,YAAY;CAClB;CAED,MAAM,QAAuB;AAC3B,OAAK,QAAQ,oBAAoB;AACjC,OAAK,YAAY;CAClB;CAED,cAAuB;AACrB,SAAO,KAAK;CACb;;;;;CAMD,IAAI,eAA6B;AAC/B,SAAO,KAAK;CACb;AACF"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"BasicPublisher-
|
|
1
|
+
{"version":3,"file":"BasicPublisher-CaX8GCUi.mjs","names":["connection: BasicConnection","messages: TMessage[]"],"sources":["../src/basic/BasicPublisher.ts"],"sourcesContent":["import type { EventPublisher, PublishableMessage } from '../types';\nimport type { BasicConnection } from './BasicConnection';\n\nexport class BasicPublisher<TMessage extends PublishableMessage<string, any>>\n implements EventPublisher<TMessage>\n{\n constructor(private connection: BasicConnection) {}\n\n async publish(messages: TMessage[]) {\n const emitter = this.connection.eventEmitter;\n for (const message of messages) {\n emitter.emit(message.type, message);\n }\n }\n}\n"],"mappings":";AAGA,IAAa,iBAAb,MAEA;CACE,YAAoBA,YAA6B;EAA7B;CAA+B;CAEnD,MAAM,QAAQC,UAAsB;EAClC,MAAM,UAAU,KAAK,WAAW;AAChC,OAAK,MAAM,WAAW,SACpB,SAAQ,KAAK,QAAQ,MAAM,QAAQ;CAEtC;AACF"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"BasicPublisher-
|
|
1
|
+
{"version":3,"file":"BasicPublisher-afwuA4Xp.cjs","names":["connection: BasicConnection","messages: TMessage[]"],"sources":["../src/basic/BasicPublisher.ts"],"sourcesContent":["import type { EventPublisher, PublishableMessage } from '../types';\nimport type { BasicConnection } from './BasicConnection';\n\nexport class BasicPublisher<TMessage extends PublishableMessage<string, any>>\n implements EventPublisher<TMessage>\n{\n constructor(private connection: BasicConnection) {}\n\n async publish(messages: TMessage[]) {\n const emitter = this.connection.eventEmitter;\n for (const message of messages) {\n emitter.emit(message.type, message);\n }\n }\n}\n"],"mappings":";;AAGA,IAAa,iBAAb,MAEA;CACE,YAAoBA,YAA6B;EAA7B;CAA+B;CAEnD,MAAM,QAAQC,UAAsB;EAClC,MAAM,UAAU,KAAK,WAAW;AAChC,OAAK,MAAM,WAAW,SACpB,SAAQ,KAAK,QAAQ,MAAM,QAAQ;CAEtC;AACF"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"BasicSubscriber-
|
|
1
|
+
{"version":3,"file":"BasicSubscriber-Ckf3BkxS.cjs","names":["connection: BasicConnection","messages: TMessage['type'][]","listener: (payload: TMessage) => Promise<void>","message: TMessage"],"sources":["../src/basic/BasicSubscriber.ts"],"sourcesContent":["import type { EventSubscriber, PublishableMessage } from '../types';\nimport type { BasicConnection } from './BasicConnection';\n\nexport class BasicSubscriber<TMessage extends PublishableMessage<string, any>>\n implements EventSubscriber<TMessage>\n{\n constructor(private connection: BasicConnection) {}\n\n async subscribe(\n messages: TMessage['type'][],\n listener: (payload: TMessage) => Promise<void>,\n ): Promise<void> {\n const emitter = this.connection.eventEmitter;\n\n for (const messageType of messages) {\n emitter.on(messageType, (message: TMessage) => {\n listener(message).catch((error) => {\n // Log error but don't throw to prevent unhandled promise rejections\n console.error(`Error processing message ${messageType}:`, error);\n });\n });\n }\n }\n}\n"],"mappings":";;AAGA,IAAa,kBAAb,MAEA;CACE,YAAoBA,YAA6B;EAA7B;CAA+B;CAEnD,MAAM,UACJC,UACAC,UACe;EACf,MAAM,UAAU,KAAK,WAAW;AAEhC,OAAK,MAAM,eAAe,SACxB,SAAQ,GAAG,aAAa,CAACC,YAAsB;AAC7C,YAAS,QAAQ,CAAC,MAAM,CAAC,UAAU;AAEjC,YAAQ,OAAO,2BAA2B,YAAY,IAAI,MAAM;GACjE,EAAC;EACH,EAAC;CAEL;AACF"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"BasicSubscriber-
|
|
1
|
+
{"version":3,"file":"BasicSubscriber-DPpJNUUY.mjs","names":["connection: BasicConnection","messages: TMessage['type'][]","listener: (payload: TMessage) => Promise<void>","message: TMessage"],"sources":["../src/basic/BasicSubscriber.ts"],"sourcesContent":["import type { EventSubscriber, PublishableMessage } from '../types';\nimport type { BasicConnection } from './BasicConnection';\n\nexport class BasicSubscriber<TMessage extends PublishableMessage<string, any>>\n implements EventSubscriber<TMessage>\n{\n constructor(private connection: BasicConnection) {}\n\n async subscribe(\n messages: TMessage['type'][],\n listener: (payload: TMessage) => Promise<void>,\n ): Promise<void> {\n const emitter = this.connection.eventEmitter;\n\n for (const messageType of messages) {\n emitter.on(messageType, (message: TMessage) => {\n listener(message).catch((error) => {\n // Log error but don't throw to prevent unhandled promise rejections\n console.error(`Error processing message ${messageType}:`, error);\n });\n });\n }\n }\n}\n"],"mappings":";AAGA,IAAa,kBAAb,MAEA;CACE,YAAoBA,YAA6B;EAA7B;CAA+B;CAEnD,MAAM,UACJC,UACAC,UACe;EACf,MAAM,UAAU,KAAK,WAAW;AAEhC,OAAK,MAAM,eAAe,SACxB,SAAQ,GAAG,aAAa,CAACC,YAAsB;AAC7C,YAAS,QAAQ,CAAC,MAAM,CAAC,UAAU;AAEjC,YAAQ,OAAO,2BAA2B,YAAY,IAAI,MAAM;GACjE,EAAC;EACH,EAAC;CAEL;AACF"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SNSConnection-
|
|
1
|
+
{"version":3,"file":"SNSConnection-CZz_U56V.cjs","names":["EventPublisherType","config: SNSConnectionConfig","clientConfig: SNSClientConfig","SNSClient","connectionString: string"],"sources":["../src/sns/SNSConnection.ts"],"sourcesContent":["import { SNSClient, type SNSClientConfig } from '@aws-sdk/client-sns';\nimport type { EventConnection } from '../types';\nimport { EventPublisherType } from '../types';\n\nexport interface SNSConnectionConfig {\n topicArn: string;\n region?: string;\n endpoint?: string; // Custom endpoint (e.g., for LocalStack)\n credentials?: {\n accessKeyId: string;\n secretAccessKey: string;\n sessionToken?: string;\n };\n}\n\nexport class SNSConnection implements EventConnection {\n readonly type = EventPublisherType.SNS;\n private client: SNSClient;\n private connected = false;\n private config: SNSConnectionConfig;\n\n constructor(config: SNSConnectionConfig) {\n this.config = config;\n\n const clientConfig: SNSClientConfig = {\n region: config.region,\n endpoint: config.endpoint,\n credentials: config.credentials,\n };\n\n this.client = new SNSClient(clientConfig);\n }\n\n /**\n * Create an SNSConnection from a connection string\n * Format: sns://?topicArn=arn:aws:sns:region:account:topic®ion=us-east-1&endpoint=http://localhost:4566\n */\n static async fromConnectionString(\n connectionString: string,\n ): Promise<SNSConnection> {\n const url = new URL(connectionString);\n const params = url.searchParams;\n\n const topicArn = params.get('topicArn');\n if (!topicArn) {\n throw new Error('topicArn parameter is required in connection string');\n }\n\n const config: SNSConnectionConfig = {\n topicArn,\n region: params.get('region') || undefined,\n endpoint: params.get('endpoint') || undefined,\n };\n\n // Parse credentials if provided\n const accessKeyId = params.get('accessKeyId');\n const secretAccessKey = params.get('secretAccessKey');\n if (accessKeyId && secretAccessKey) {\n config.credentials = {\n accessKeyId,\n secretAccessKey,\n sessionToken: params.get('sessionToken') || undefined,\n };\n }\n\n const connection = new SNSConnection(config);\n await connection.connect();\n return connection;\n }\n\n async connect(): Promise<void> {\n // SNS doesn't have a traditional connection, but we mark as connected\n this.connected = true;\n }\n\n async close(): Promise<void> {\n this.client.destroy();\n this.connected = false;\n }\n\n isConnected(): boolean {\n return this.connected;\n }\n\n /**\n * Get the underlying SNS client\n */\n get snsClient(): SNSClient {\n return this.client;\n }\n\n /**\n * Get the topic ARN for this connection\n */\n get topicArn(): string {\n return this.config.topicArn;\n }\n}\n"],"mappings":";;;;;AAeA,IAAa,gBAAb,MAAa,cAAyC;CACpD,AAAS,OAAOA,iCAAmB;CACnC,AAAQ;CACR,AAAQ,YAAY;CACpB,AAAQ;CAER,YAAYC,QAA6B;AACvC,OAAK,SAAS;EAEd,MAAMC,eAAgC;GACpC,QAAQ,OAAO;GACf,UAAU,OAAO;GACjB,aAAa,OAAO;EACrB;AAED,OAAK,SAAS,IAAIC,+BAAU;CAC7B;;;;;CAMD,aAAa,qBACXC,kBACwB;EACxB,MAAM,MAAM,IAAI,IAAI;EACpB,MAAM,SAAS,IAAI;EAEnB,MAAM,WAAW,OAAO,IAAI,WAAW;AACvC,OAAK,SACH,OAAM,IAAI,MAAM;EAGlB,MAAMH,SAA8B;GAClC;GACA,QAAQ,OAAO,IAAI,SAAS;GAC5B,UAAU,OAAO,IAAI,WAAW;EACjC;EAGD,MAAM,cAAc,OAAO,IAAI,cAAc;EAC7C,MAAM,kBAAkB,OAAO,IAAI,kBAAkB;AACrD,MAAI,eAAe,gBACjB,QAAO,cAAc;GACnB;GACA;GACA,cAAc,OAAO,IAAI,eAAe;EACzC;EAGH,MAAM,aAAa,IAAI,cAAc;AACrC,QAAM,WAAW,SAAS;AAC1B,SAAO;CACR;CAED,MAAM,UAAyB;AAE7B,OAAK,YAAY;CAClB;CAED,MAAM,QAAuB;AAC3B,OAAK,OAAO,SAAS;AACrB,OAAK,YAAY;CAClB;CAED,cAAuB;AACrB,SAAO,KAAK;CACb;;;;CAKD,IAAI,YAAuB;AACzB,SAAO,KAAK;CACb;;;;CAKD,IAAI,WAAmB;AACrB,SAAO,KAAK,OAAO;CACpB;AACF"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SNSConnection-
|
|
1
|
+
{"version":3,"file":"SNSConnection-DVEE3XoC.mjs","names":["config: SNSConnectionConfig","clientConfig: SNSClientConfig","connectionString: string"],"sources":["../src/sns/SNSConnection.ts"],"sourcesContent":["import { SNSClient, type SNSClientConfig } from '@aws-sdk/client-sns';\nimport type { EventConnection } from '../types';\nimport { EventPublisherType } from '../types';\n\nexport interface SNSConnectionConfig {\n topicArn: string;\n region?: string;\n endpoint?: string; // Custom endpoint (e.g., for LocalStack)\n credentials?: {\n accessKeyId: string;\n secretAccessKey: string;\n sessionToken?: string;\n };\n}\n\nexport class SNSConnection implements EventConnection {\n readonly type = EventPublisherType.SNS;\n private client: SNSClient;\n private connected = false;\n private config: SNSConnectionConfig;\n\n constructor(config: SNSConnectionConfig) {\n this.config = config;\n\n const clientConfig: SNSClientConfig = {\n region: config.region,\n endpoint: config.endpoint,\n credentials: config.credentials,\n };\n\n this.client = new SNSClient(clientConfig);\n }\n\n /**\n * Create an SNSConnection from a connection string\n * Format: sns://?topicArn=arn:aws:sns:region:account:topic®ion=us-east-1&endpoint=http://localhost:4566\n */\n static async fromConnectionString(\n connectionString: string,\n ): Promise<SNSConnection> {\n const url = new URL(connectionString);\n const params = url.searchParams;\n\n const topicArn = params.get('topicArn');\n if (!topicArn) {\n throw new Error('topicArn parameter is required in connection string');\n }\n\n const config: SNSConnectionConfig = {\n topicArn,\n region: params.get('region') || undefined,\n endpoint: params.get('endpoint') || undefined,\n };\n\n // Parse credentials if provided\n const accessKeyId = params.get('accessKeyId');\n const secretAccessKey = params.get('secretAccessKey');\n if (accessKeyId && secretAccessKey) {\n config.credentials = {\n accessKeyId,\n secretAccessKey,\n sessionToken: params.get('sessionToken') || undefined,\n };\n }\n\n const connection = new SNSConnection(config);\n await connection.connect();\n return connection;\n }\n\n async connect(): Promise<void> {\n // SNS doesn't have a traditional connection, but we mark as connected\n this.connected = true;\n }\n\n async close(): Promise<void> {\n this.client.destroy();\n this.connected = false;\n }\n\n isConnected(): boolean {\n return this.connected;\n }\n\n /**\n * Get the underlying SNS client\n */\n get snsClient(): SNSClient {\n return this.client;\n }\n\n /**\n * Get the topic ARN for this connection\n */\n get topicArn(): string {\n return this.config.topicArn;\n }\n}\n"],"mappings":";;;;AAeA,IAAa,gBAAb,MAAa,cAAyC;CACpD,AAAS,OAAO,mBAAmB;CACnC,AAAQ;CACR,AAAQ,YAAY;CACpB,AAAQ;CAER,YAAYA,QAA6B;AACvC,OAAK,SAAS;EAEd,MAAMC,eAAgC;GACpC,QAAQ,OAAO;GACf,UAAU,OAAO;GACjB,aAAa,OAAO;EACrB;AAED,OAAK,SAAS,IAAI,UAAU;CAC7B;;;;;CAMD,aAAa,qBACXC,kBACwB;EACxB,MAAM,MAAM,IAAI,IAAI;EACpB,MAAM,SAAS,IAAI;EAEnB,MAAM,WAAW,OAAO,IAAI,WAAW;AACvC,OAAK,SACH,OAAM,IAAI,MAAM;EAGlB,MAAMF,SAA8B;GAClC;GACA,QAAQ,OAAO,IAAI,SAAS;GAC5B,UAAU,OAAO,IAAI,WAAW;EACjC;EAGD,MAAM,cAAc,OAAO,IAAI,cAAc;EAC7C,MAAM,kBAAkB,OAAO,IAAI,kBAAkB;AACrD,MAAI,eAAe,gBACjB,QAAO,cAAc;GACnB;GACA;GACA,cAAc,OAAO,IAAI,eAAe;EACzC;EAGH,MAAM,aAAa,IAAI,cAAc;AACrC,QAAM,WAAW,SAAS;AAC1B,SAAO;CACR;CAED,MAAM,UAAyB;AAE7B,OAAK,YAAY;CAClB;CAED,MAAM,QAAuB;AAC3B,OAAK,OAAO,SAAS;AACrB,OAAK,YAAY;CAClB;CAED,cAAuB;AACrB,SAAO,KAAK;CACb;;;;CAKD,IAAI,YAAuB;AACzB,SAAO,KAAK;CACb;;;;CAKD,IAAI,WAAmB;AACrB,SAAO,KAAK,OAAO;CACpB;AACF"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SNSPublisher-
|
|
1
|
+
{"version":3,"file":"SNSPublisher-DbWVH-g_.cjs","names":["connection: SNSConnection","options: SNSPublisherOptions","connectionString: string","messages: TMessage[]","message: TMessage","PublishCommand"],"sources":["../src/sns/SNSPublisher.ts"],"sourcesContent":["import { PublishCommand } from '@aws-sdk/client-sns';\nimport type { EventPublisher, PublishableMessage } from '../types';\nimport type { SNSConnection } from './SNSConnection';\n\nexport interface SNSPublisherOptions {\n // Optional publisher-specific options\n}\n\nexport class SNSPublisher<TMessage extends PublishableMessage<string, any>>\n implements EventPublisher<TMessage>\n{\n constructor(\n private connection: SNSConnection,\n private options: SNSPublisherOptions = {},\n ) {}\n\n /**\n * Create an SNSPublisher from a connection string\n * Format: sns://?topicArn=arn:aws:sns:region:account:topic®ion=us-east-1&endpoint=http://localhost:4566\n */\n static async fromConnectionString<\n TMessage extends PublishableMessage<string, any>,\n >(connectionString: string): Promise<SNSPublisher<TMessage>> {\n const { SNSConnection } = await import('./SNSConnection');\n const connection =\n await SNSConnection.fromConnectionString(connectionString);\n\n return new SNSPublisher<TMessage>(connection);\n }\n\n async publish(messages: TMessage[]): Promise<void> {\n if (messages.length === 0) return;\n\n if (!this.connection.isConnected()) {\n await this.connection.connect();\n }\n\n // Publish all messages\n await Promise.all(messages.map((message) => this.publishMessage(message)));\n }\n\n private async publishMessage(message: TMessage): Promise<void> {\n const command = new PublishCommand({\n TopicArn: this.connection.topicArn,\n Message: JSON.stringify({\n type: message.type,\n payload: message.payload,\n }),\n MessageAttributes: {\n type: {\n DataType: 'String',\n StringValue: message.type,\n },\n },\n });\n\n await this.connection.snsClient.send(command);\n }\n\n async close(): Promise<void> {\n // Publisher doesn't own the connection\n // Connection should be closed by whoever created it\n }\n}\n"],"mappings":";;;;AAQA,IAAa,eAAb,MAAa,aAEb;CACE,YACUA,YACAC,UAA+B,CAAE,GACzC;EAFQ;EACA;CACN;;;;;CAMJ,aAAa,qBAEXC,kBAA2D;EAC3D,MAAM,EAAE,eAAe,GAAG,2CAAM;EAChC,MAAM,aACJ,MAAM,cAAc,qBAAqB,iBAAiB;AAE5D,SAAO,IAAI,aAAuB;CACnC;CAED,MAAM,QAAQC,UAAqC;AACjD,MAAI,SAAS,WAAW,EAAG;AAE3B,OAAK,KAAK,WAAW,aAAa,CAChC,OAAM,KAAK,WAAW,SAAS;AAIjC,QAAM,QAAQ,IAAI,SAAS,IAAI,CAAC,YAAY,KAAK,eAAe,QAAQ,CAAC,CAAC;CAC3E;CAED,MAAc,eAAeC,SAAkC;EAC7D,MAAM,UAAU,IAAIC,oCAAe;GACjC,UAAU,KAAK,WAAW;GAC1B,SAAS,KAAK,UAAU;IACtB,MAAM,QAAQ;IACd,SAAS,QAAQ;GAClB,EAAC;GACF,mBAAmB,EACjB,MAAM;IACJ,UAAU;IACV,aAAa,QAAQ;GACtB,EACF;EACF;AAED,QAAM,KAAK,WAAW,UAAU,KAAK,QAAQ;CAC9C;CAED,MAAM,QAAuB,CAG5B;AACF"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SNSPublisher-
|
|
1
|
+
{"version":3,"file":"SNSPublisher-Do-FMUyT.mjs","names":["connection: SNSConnection","options: SNSPublisherOptions","connectionString: string","messages: TMessage[]","message: TMessage"],"sources":["../src/sns/SNSPublisher.ts"],"sourcesContent":["import { PublishCommand } from '@aws-sdk/client-sns';\nimport type { EventPublisher, PublishableMessage } from '../types';\nimport type { SNSConnection } from './SNSConnection';\n\nexport interface SNSPublisherOptions {\n // Optional publisher-specific options\n}\n\nexport class SNSPublisher<TMessage extends PublishableMessage<string, any>>\n implements EventPublisher<TMessage>\n{\n constructor(\n private connection: SNSConnection,\n private options: SNSPublisherOptions = {},\n ) {}\n\n /**\n * Create an SNSPublisher from a connection string\n * Format: sns://?topicArn=arn:aws:sns:region:account:topic®ion=us-east-1&endpoint=http://localhost:4566\n */\n static async fromConnectionString<\n TMessage extends PublishableMessage<string, any>,\n >(connectionString: string): Promise<SNSPublisher<TMessage>> {\n const { SNSConnection } = await import('./SNSConnection');\n const connection =\n await SNSConnection.fromConnectionString(connectionString);\n\n return new SNSPublisher<TMessage>(connection);\n }\n\n async publish(messages: TMessage[]): Promise<void> {\n if (messages.length === 0) return;\n\n if (!this.connection.isConnected()) {\n await this.connection.connect();\n }\n\n // Publish all messages\n await Promise.all(messages.map((message) => this.publishMessage(message)));\n }\n\n private async publishMessage(message: TMessage): Promise<void> {\n const command = new PublishCommand({\n TopicArn: this.connection.topicArn,\n Message: JSON.stringify({\n type: message.type,\n payload: message.payload,\n }),\n MessageAttributes: {\n type: {\n DataType: 'String',\n StringValue: message.type,\n },\n },\n });\n\n await this.connection.snsClient.send(command);\n }\n\n async close(): Promise<void> {\n // Publisher doesn't own the connection\n // Connection should be closed by whoever created it\n }\n}\n"],"mappings":";;;AAQA,IAAa,eAAb,MAAa,aAEb;CACE,YACUA,YACAC,UAA+B,CAAE,GACzC;EAFQ;EACA;CACN;;;;;CAMJ,aAAa,qBAEXC,kBAA2D;EAC3D,MAAM,EAAE,eAAe,GAAG,MAAM,OAAO;EACvC,MAAM,aACJ,MAAM,cAAc,qBAAqB,iBAAiB;AAE5D,SAAO,IAAI,aAAuB;CACnC;CAED,MAAM,QAAQC,UAAqC;AACjD,MAAI,SAAS,WAAW,EAAG;AAE3B,OAAK,KAAK,WAAW,aAAa,CAChC,OAAM,KAAK,WAAW,SAAS;AAIjC,QAAM,QAAQ,IAAI,SAAS,IAAI,CAAC,YAAY,KAAK,eAAe,QAAQ,CAAC,CAAC;CAC3E;CAED,MAAc,eAAeC,SAAkC;EAC7D,MAAM,UAAU,IAAI,eAAe;GACjC,UAAU,KAAK,WAAW;GAC1B,SAAS,KAAK,UAAU;IACtB,MAAM,QAAQ;IACd,SAAS,QAAQ;GAClB,EAAC;GACF,mBAAmB,EACjB,MAAM;IACJ,UAAU;IACV,aAAa,QAAQ;GACtB,EACF;EACF;AAED,QAAM,KAAK,WAAW,UAAU,KAAK,QAAQ;CAC9C;CAED,MAAM,QAAuB,CAG5B;AACF"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
const require_chunk = require('./chunk-CUT6urMc.cjs');
|
|
2
|
-
const require_SQSConnection = require('./SQSConnection-
|
|
3
|
-
const require_SQSSubscriber = require('./SQSSubscriber-
|
|
2
|
+
const require_SQSConnection = require('./SQSConnection-DWYDtftQ.cjs');
|
|
3
|
+
const require_SQSSubscriber = require('./SQSSubscriber-DcL9BbG1.cjs');
|
|
4
4
|
const __aws_sdk_client_sns = require_chunk.__toESM(require("@aws-sdk/client-sns"));
|
|
5
5
|
const __aws_sdk_client_sqs = require_chunk.__toESM(require("@aws-sdk/client-sqs"));
|
|
6
6
|
|
|
@@ -146,4 +146,4 @@ Object.defineProperty(exports, 'SNSSubscriber', {
|
|
|
146
146
|
return SNSSubscriber;
|
|
147
147
|
}
|
|
148
148
|
});
|
|
149
|
-
//# sourceMappingURL=SNSSubscriber-
|
|
149
|
+
//# sourceMappingURL=SNSSubscriber-DpEwQ8Tb.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SNSSubscriber-UE0ia4XM.cjs","names":["connection: SNSConnection","options: SNSSubscriberOptions","SQSClient","connectionString: string","messages: TMessage['type'][]","listener: (payload: TMessage) => Promise<void>","SQSConnection","SQSSubscriber","UnsubscribeCommand","DeleteQueueCommand","CreateQueueCommand","GetQueueAttributesCommand","SetQueueAttributesCommand","SubscribeCommand"],"sources":["../src/sns/SNSSubscriber.ts"],"sourcesContent":["import { SubscribeCommand, UnsubscribeCommand } from '@aws-sdk/client-sns';\nimport {\n CreateQueueCommand,\n DeleteQueueCommand,\n GetQueueAttributesCommand,\n SQSClient,\n SetQueueAttributesCommand,\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 queueName?: string; // Optional queue name (defaults to auto-generated)\n createQueue?: boolean; // Auto-create SQS queue (default: true)\n deleteQueueOnClose?: boolean; // Delete queue when subscriber closes (default: false for named queues, true for auto-generated)\n waitTimeSeconds?: number; // SQS long polling (default: 20)\n maxMessages?: 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 implements EventSubscriber<TMessage>\n{\n private sqsClient: SQSClient;\n private queueUrl?: string;\n private queueArn?: string;\n private subscriptionArn?: string;\n private sqsSubscriber?: SQSSubscriber<TMessage>;\n private options: Required<SNSSubscriberOptions>;\n\n constructor(\n private connection: SNSConnection,\n options: SNSSubscriberOptions = {},\n ) {\n this.options = {\n queueName:\n options.queueName ||\n `sns-sub-${Date.now()}-${Math.random().toString(36).substring(7)}`,\n createQueue: options.createQueue ?? true,\n deleteQueueOnClose: options.deleteQueueOnClose ?? !options.queueName, // Delete auto-generated queues\n waitTimeSeconds: options.waitTimeSeconds ?? 20,\n maxMessages: options.maxMessages ?? 10,\n };\n\n // Create SQS client with same config as SNS\n this.sqsClient = new SQSClient({\n region: (this.connection as any).config.region,\n endpoint: (this.connection as any).config.endpoint,\n credentials: (this.connection as any).config.credentials,\n });\n }\n\n /**\n * Create an SNSSubscriber from a connection string\n * Format: sns://?topicArn=...&queueName=myqueue®ion=us-east-1\n */\n static async fromConnectionString<\n TMessage extends PublishableMessage<string, any>,\n >(connectionString: string): Promise<SNSSubscriber<TMessage>> {\n const url = new URL(connectionString);\n const params = url.searchParams;\n\n const { SNSConnection } = await import('./SNSConnection');\n const connection =\n await SNSConnection.fromConnectionString(connectionString);\n\n const options: SNSSubscriberOptions = {\n queueName: params.get('queueName') || undefined,\n createQueue: params.get('createQueue') !== 'false',\n deleteQueueOnClose: params.get('deleteQueueOnClose') === 'true',\n waitTimeSeconds: params.get('waitTimeSeconds')\n ? Number.parseInt(params.get('waitTimeSeconds')!, 10)\n : undefined,\n };\n\n return new SNSSubscriber<TMessage>(connection, options);\n }\n\n async subscribe(\n messages: TMessage['type'][],\n listener: (payload: TMessage) => Promise<void>,\n ): Promise<void> {\n // Setup SQS queue and SNS subscription\n await this.setupQueue();\n await this.subscribeToTopic();\n\n // Create SQS connection for the subscriber\n const sqsConnection = new SQSConnection({\n queueUrl: this.queueUrl!,\n region: (this.connection as any).config.region,\n endpoint: (this.connection as any).config.endpoint,\n credentials: (this.connection as any).config.credentials,\n });\n await sqsConnection.connect();\n\n // Use SQSSubscriber to receive messages, with topic filtering\n this.sqsSubscriber = new SQSSubscriber<TMessage>(sqsConnection, {\n waitTimeSeconds: this.options.waitTimeSeconds,\n maxMessages: this.options.maxMessages,\n expectedTopicArn: this.connection.topicArn,\n });\n\n await this.sqsSubscriber.subscribe(messages, listener);\n }\n\n /**\n * Stop receiving messages and clean up resources\n */\n async stop(): Promise<void> {\n // Stop SQS subscriber\n if (this.sqsSubscriber) {\n this.sqsSubscriber.stop();\n }\n\n // Unsubscribe from topic\n if (this.subscriptionArn) {\n try {\n const command = new UnsubscribeCommand({\n SubscriptionArn: this.subscriptionArn,\n });\n await this.connection.snsClient.send(command);\n } catch (error) {\n console.error('Error unsubscribing:', error);\n }\n }\n\n // Delete queue if configured\n if (this.queueUrl && this.options.deleteQueueOnClose) {\n try {\n const command = new DeleteQueueCommand({\n QueueUrl: this.queueUrl,\n });\n await this.sqsClient.send(command);\n } catch (error) {\n console.error('Error deleting queue:', error);\n }\n }\n\n this.sqsClient.destroy();\n }\n\n private async setupQueue(): Promise<void> {\n if (this.options.createQueue) {\n // Create queue\n const createCommand = new CreateQueueCommand({\n QueueName: this.options.queueName,\n });\n const response = await this.sqsClient.send(createCommand);\n this.queueUrl = response.QueueUrl!;\n } else {\n throw new Error('Queue must exist or createQueue option must be true');\n }\n\n // Get queue ARN\n const attrsCommand = new GetQueueAttributesCommand({\n QueueUrl: this.queueUrl,\n AttributeNames: ['QueueArn'],\n });\n const attrsResponse = await this.sqsClient.send(attrsCommand);\n this.queueArn = attrsResponse.Attributes?.QueueArn!;\n\n // Set queue policy to allow SNS\n await this.setQueuePolicy();\n }\n\n private async setQueuePolicy(): Promise<void> {\n const policy = {\n Version: '2012-10-17',\n Statement: [\n {\n Effect: 'Allow',\n Principal: '*',\n Action: 'SQS:SendMessage',\n Resource: this.queueArn,\n Condition: {\n ArnEquals: {\n 'aws:SourceArn': this.connection.topicArn,\n },\n },\n },\n ],\n };\n\n const command = new SetQueueAttributesCommand({\n QueueUrl: this.queueUrl,\n Attributes: {\n Policy: JSON.stringify(policy),\n },\n });\n\n await this.sqsClient.send(command);\n }\n\n private async subscribeToTopic(): Promise<void> {\n const command = new SubscribeCommand({\n TopicArn: this.connection.topicArn,\n Protocol: 'sqs',\n Endpoint: this.queueArn,\n });\n\n const response = await this.connection.snsClient.send(command);\n this.subscriptionArn = response.SubscriptionArn!;\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"SNSSubscriber-DpEwQ8Tb.cjs","names":["connection: SNSConnection","options: SNSSubscriberOptions","SQSClient","connectionString: string","messages: TMessage['type'][]","listener: (payload: TMessage) => Promise<void>","SQSConnection","SQSSubscriber","UnsubscribeCommand","DeleteQueueCommand","CreateQueueCommand","GetQueueAttributesCommand","SetQueueAttributesCommand","SubscribeCommand"],"sources":["../src/sns/SNSSubscriber.ts"],"sourcesContent":["import { SubscribeCommand, UnsubscribeCommand } from '@aws-sdk/client-sns';\nimport {\n CreateQueueCommand,\n DeleteQueueCommand,\n GetQueueAttributesCommand,\n SQSClient,\n SetQueueAttributesCommand,\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 queueName?: string; // Optional queue name (defaults to auto-generated)\n createQueue?: boolean; // Auto-create SQS queue (default: true)\n deleteQueueOnClose?: boolean; // Delete queue when subscriber closes (default: false for named queues, true for auto-generated)\n waitTimeSeconds?: number; // SQS long polling (default: 20)\n maxMessages?: 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 implements EventSubscriber<TMessage>\n{\n private sqsClient: SQSClient;\n private queueUrl?: string;\n private queueArn?: string;\n private subscriptionArn?: string;\n private sqsSubscriber?: SQSSubscriber<TMessage>;\n private options: Required<SNSSubscriberOptions>;\n\n constructor(\n private connection: SNSConnection,\n options: SNSSubscriberOptions = {},\n ) {\n this.options = {\n queueName:\n options.queueName ||\n `sns-sub-${Date.now()}-${Math.random().toString(36).substring(7)}`,\n createQueue: options.createQueue ?? true,\n deleteQueueOnClose: options.deleteQueueOnClose ?? !options.queueName, // Delete auto-generated queues\n waitTimeSeconds: options.waitTimeSeconds ?? 20,\n maxMessages: options.maxMessages ?? 10,\n };\n\n // Create SQS client with same config as SNS\n this.sqsClient = new SQSClient({\n region: (this.connection as any).config.region,\n endpoint: (this.connection as any).config.endpoint,\n credentials: (this.connection as any).config.credentials,\n });\n }\n\n /**\n * Create an SNSSubscriber from a connection string\n * Format: sns://?topicArn=...&queueName=myqueue®ion=us-east-1\n */\n static async fromConnectionString<\n TMessage extends PublishableMessage<string, any>,\n >(connectionString: string): Promise<SNSSubscriber<TMessage>> {\n const url = new URL(connectionString);\n const params = url.searchParams;\n\n const { SNSConnection } = await import('./SNSConnection');\n const connection =\n await SNSConnection.fromConnectionString(connectionString);\n\n const options: SNSSubscriberOptions = {\n queueName: params.get('queueName') || undefined,\n createQueue: params.get('createQueue') !== 'false',\n deleteQueueOnClose: params.get('deleteQueueOnClose') === 'true',\n waitTimeSeconds: params.get('waitTimeSeconds')\n ? Number.parseInt(params.get('waitTimeSeconds')!, 10)\n : undefined,\n };\n\n return new SNSSubscriber<TMessage>(connection, options);\n }\n\n async subscribe(\n messages: TMessage['type'][],\n listener: (payload: TMessage) => Promise<void>,\n ): Promise<void> {\n // Setup SQS queue and SNS subscription\n await this.setupQueue();\n await this.subscribeToTopic();\n\n // Create SQS connection for the subscriber\n const sqsConnection = new SQSConnection({\n queueUrl: this.queueUrl!,\n region: (this.connection as any).config.region,\n endpoint: (this.connection as any).config.endpoint,\n credentials: (this.connection as any).config.credentials,\n });\n await sqsConnection.connect();\n\n // Use SQSSubscriber to receive messages, with topic filtering\n this.sqsSubscriber = new SQSSubscriber<TMessage>(sqsConnection, {\n waitTimeSeconds: this.options.waitTimeSeconds,\n maxMessages: this.options.maxMessages,\n expectedTopicArn: this.connection.topicArn,\n });\n\n await this.sqsSubscriber.subscribe(messages, listener);\n }\n\n /**\n * Stop receiving messages and clean up resources\n */\n async stop(): Promise<void> {\n // Stop SQS subscriber\n if (this.sqsSubscriber) {\n this.sqsSubscriber.stop();\n }\n\n // Unsubscribe from topic\n if (this.subscriptionArn) {\n try {\n const command = new UnsubscribeCommand({\n SubscriptionArn: this.subscriptionArn,\n });\n await this.connection.snsClient.send(command);\n } catch (error) {\n console.error('Error unsubscribing:', error);\n }\n }\n\n // Delete queue if configured\n if (this.queueUrl && this.options.deleteQueueOnClose) {\n try {\n const command = new DeleteQueueCommand({\n QueueUrl: this.queueUrl,\n });\n await this.sqsClient.send(command);\n } catch (error) {\n console.error('Error deleting queue:', error);\n }\n }\n\n this.sqsClient.destroy();\n }\n\n private async setupQueue(): Promise<void> {\n if (this.options.createQueue) {\n // Create queue\n const createCommand = new CreateQueueCommand({\n QueueName: this.options.queueName,\n });\n const response = await this.sqsClient.send(createCommand);\n this.queueUrl = response.QueueUrl!;\n } else {\n throw new Error('Queue must exist or createQueue option must be true');\n }\n\n // Get queue ARN\n const attrsCommand = new GetQueueAttributesCommand({\n QueueUrl: this.queueUrl,\n AttributeNames: ['QueueArn'],\n });\n const attrsResponse = await this.sqsClient.send(attrsCommand);\n this.queueArn = attrsResponse.Attributes?.QueueArn!;\n\n // Set queue policy to allow SNS\n await this.setQueuePolicy();\n }\n\n private async setQueuePolicy(): Promise<void> {\n const policy = {\n Version: '2012-10-17',\n Statement: [\n {\n Effect: 'Allow',\n Principal: '*',\n Action: 'SQS:SendMessage',\n Resource: this.queueArn,\n Condition: {\n ArnEquals: {\n 'aws:SourceArn': this.connection.topicArn,\n },\n },\n },\n ],\n };\n\n const command = new SetQueueAttributesCommand({\n QueueUrl: this.queueUrl,\n Attributes: {\n Policy: JSON.stringify(policy),\n },\n });\n\n await this.sqsClient.send(command);\n }\n\n private async subscribeToTopic(): Promise<void> {\n const command = new SubscribeCommand({\n TopicArn: this.connection.topicArn,\n Protocol: 'sqs',\n Endpoint: this.queueArn,\n });\n\n const response = await this.connection.snsClient.send(command);\n this.subscriptionArn = response.SubscriptionArn!;\n }\n}\n"],"mappings":"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"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { SQSConnection } from "./SQSConnection-
|
|
2
|
-
import { SQSSubscriber } from "./SQSSubscriber-
|
|
1
|
+
import { SQSConnection } from "./SQSConnection-BBlDWZLU.mjs";
|
|
2
|
+
import { SQSSubscriber } from "./SQSSubscriber-84pFnW1z.mjs";
|
|
3
3
|
import { SubscribeCommand, UnsubscribeCommand } from "@aws-sdk/client-sns";
|
|
4
4
|
import { CreateQueueCommand, DeleteQueueCommand, GetQueueAttributesCommand, SQSClient, SetQueueAttributesCommand } from "@aws-sdk/client-sqs";
|
|
5
5
|
|
|
@@ -140,4 +140,4 @@ var SNSSubscriber = class SNSSubscriber {
|
|
|
140
140
|
|
|
141
141
|
//#endregion
|
|
142
142
|
export { SNSSubscriber };
|
|
143
|
-
//# sourceMappingURL=SNSSubscriber-
|
|
143
|
+
//# sourceMappingURL=SNSSubscriber-e7eG48yJ.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SNSSubscriber-C9YJWa7m.mjs","names":["connection: SNSConnection","options: SNSSubscriberOptions","connectionString: string","messages: TMessage['type'][]","listener: (payload: TMessage) => Promise<void>"],"sources":["../src/sns/SNSSubscriber.ts"],"sourcesContent":["import { SubscribeCommand, UnsubscribeCommand } from '@aws-sdk/client-sns';\nimport {\n CreateQueueCommand,\n DeleteQueueCommand,\n GetQueueAttributesCommand,\n SQSClient,\n SetQueueAttributesCommand,\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 queueName?: string; // Optional queue name (defaults to auto-generated)\n createQueue?: boolean; // Auto-create SQS queue (default: true)\n deleteQueueOnClose?: boolean; // Delete queue when subscriber closes (default: false for named queues, true for auto-generated)\n waitTimeSeconds?: number; // SQS long polling (default: 20)\n maxMessages?: 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 implements EventSubscriber<TMessage>\n{\n private sqsClient: SQSClient;\n private queueUrl?: string;\n private queueArn?: string;\n private subscriptionArn?: string;\n private sqsSubscriber?: SQSSubscriber<TMessage>;\n private options: Required<SNSSubscriberOptions>;\n\n constructor(\n private connection: SNSConnection,\n options: SNSSubscriberOptions = {},\n ) {\n this.options = {\n queueName:\n options.queueName ||\n `sns-sub-${Date.now()}-${Math.random().toString(36).substring(7)}`,\n createQueue: options.createQueue ?? true,\n deleteQueueOnClose: options.deleteQueueOnClose ?? !options.queueName, // Delete auto-generated queues\n waitTimeSeconds: options.waitTimeSeconds ?? 20,\n maxMessages: options.maxMessages ?? 10,\n };\n\n // Create SQS client with same config as SNS\n this.sqsClient = new SQSClient({\n region: (this.connection as any).config.region,\n endpoint: (this.connection as any).config.endpoint,\n credentials: (this.connection as any).config.credentials,\n });\n }\n\n /**\n * Create an SNSSubscriber from a connection string\n * Format: sns://?topicArn=...&queueName=myqueue®ion=us-east-1\n */\n static async fromConnectionString<\n TMessage extends PublishableMessage<string, any>,\n >(connectionString: string): Promise<SNSSubscriber<TMessage>> {\n const url = new URL(connectionString);\n const params = url.searchParams;\n\n const { SNSConnection } = await import('./SNSConnection');\n const connection =\n await SNSConnection.fromConnectionString(connectionString);\n\n const options: SNSSubscriberOptions = {\n queueName: params.get('queueName') || undefined,\n createQueue: params.get('createQueue') !== 'false',\n deleteQueueOnClose: params.get('deleteQueueOnClose') === 'true',\n waitTimeSeconds: params.get('waitTimeSeconds')\n ? Number.parseInt(params.get('waitTimeSeconds')!, 10)\n : undefined,\n };\n\n return new SNSSubscriber<TMessage>(connection, options);\n }\n\n async subscribe(\n messages: TMessage['type'][],\n listener: (payload: TMessage) => Promise<void>,\n ): Promise<void> {\n // Setup SQS queue and SNS subscription\n await this.setupQueue();\n await this.subscribeToTopic();\n\n // Create SQS connection for the subscriber\n const sqsConnection = new SQSConnection({\n queueUrl: this.queueUrl!,\n region: (this.connection as any).config.region,\n endpoint: (this.connection as any).config.endpoint,\n credentials: (this.connection as any).config.credentials,\n });\n await sqsConnection.connect();\n\n // Use SQSSubscriber to receive messages, with topic filtering\n this.sqsSubscriber = new SQSSubscriber<TMessage>(sqsConnection, {\n waitTimeSeconds: this.options.waitTimeSeconds,\n maxMessages: this.options.maxMessages,\n expectedTopicArn: this.connection.topicArn,\n });\n\n await this.sqsSubscriber.subscribe(messages, listener);\n }\n\n /**\n * Stop receiving messages and clean up resources\n */\n async stop(): Promise<void> {\n // Stop SQS subscriber\n if (this.sqsSubscriber) {\n this.sqsSubscriber.stop();\n }\n\n // Unsubscribe from topic\n if (this.subscriptionArn) {\n try {\n const command = new UnsubscribeCommand({\n SubscriptionArn: this.subscriptionArn,\n });\n await this.connection.snsClient.send(command);\n } catch (error) {\n console.error('Error unsubscribing:', error);\n }\n }\n\n // Delete queue if configured\n if (this.queueUrl && this.options.deleteQueueOnClose) {\n try {\n const command = new DeleteQueueCommand({\n QueueUrl: this.queueUrl,\n });\n await this.sqsClient.send(command);\n } catch (error) {\n console.error('Error deleting queue:', error);\n }\n }\n\n this.sqsClient.destroy();\n }\n\n private async setupQueue(): Promise<void> {\n if (this.options.createQueue) {\n // Create queue\n const createCommand = new CreateQueueCommand({\n QueueName: this.options.queueName,\n });\n const response = await this.sqsClient.send(createCommand);\n this.queueUrl = response.QueueUrl!;\n } else {\n throw new Error('Queue must exist or createQueue option must be true');\n }\n\n // Get queue ARN\n const attrsCommand = new GetQueueAttributesCommand({\n QueueUrl: this.queueUrl,\n AttributeNames: ['QueueArn'],\n });\n const attrsResponse = await this.sqsClient.send(attrsCommand);\n this.queueArn = attrsResponse.Attributes?.QueueArn!;\n\n // Set queue policy to allow SNS\n await this.setQueuePolicy();\n }\n\n private async setQueuePolicy(): Promise<void> {\n const policy = {\n Version: '2012-10-17',\n Statement: [\n {\n Effect: 'Allow',\n Principal: '*',\n Action: 'SQS:SendMessage',\n Resource: this.queueArn,\n Condition: {\n ArnEquals: {\n 'aws:SourceArn': this.connection.topicArn,\n },\n },\n },\n ],\n };\n\n const command = new SetQueueAttributesCommand({\n QueueUrl: this.queueUrl,\n Attributes: {\n Policy: JSON.stringify(policy),\n },\n });\n\n await this.sqsClient.send(command);\n }\n\n private async subscribeToTopic(): Promise<void> {\n const command = new SubscribeCommand({\n TopicArn: this.connection.topicArn,\n Protocol: 'sqs',\n Endpoint: this.queueArn,\n });\n\n const response = await this.connection.snsClient.send(command);\n this.subscriptionArn = response.SubscriptionArn!;\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;AAsCA,IAAa,gBAAb,MAAa,cAEb;CACE,AAAQ;CACR,AAAQ;CACR,AAAQ;CACR,AAAQ;CACR,AAAQ;CACR,AAAQ;CAER,YACUA,YACRC,UAAgC,CAAE,GAClC;EAFQ;AAGR,OAAK,UAAU;GACb,WACE,QAAQ,cACP,UAAU,KAAK,KAAK,CAAC,GAAG,KAAK,QAAQ,CAAC,SAAS,GAAG,CAAC,UAAU,EAAE,CAAC;GACnE,aAAa,QAAQ,eAAe;GACpC,oBAAoB,QAAQ,uBAAuB,QAAQ;GAC3D,iBAAiB,QAAQ,mBAAmB;GAC5C,aAAa,QAAQ,eAAe;EACrC;AAGD,OAAK,YAAY,IAAI,UAAU;GAC7B,QAAS,KAAK,WAAmB,OAAO;GACxC,UAAW,KAAK,WAAmB,OAAO;GAC1C,aAAc,KAAK,WAAmB,OAAO;EAC9C;CACF;;;;;CAMD,aAAa,qBAEXC,kBAA4D;EAC5D,MAAM,MAAM,IAAI,IAAI;EACpB,MAAM,SAAS,IAAI;EAEnB,MAAM,EAAE,eAAe,GAAG,MAAM,OAAO;EACvC,MAAM,aACJ,MAAM,cAAc,qBAAqB,iBAAiB;EAE5D,MAAMD,UAAgC;GACpC,WAAW,OAAO,IAAI,YAAY;GAClC,aAAa,OAAO,IAAI,cAAc,KAAK;GAC3C,oBAAoB,OAAO,IAAI,qBAAqB,KAAK;GACzD,iBAAiB,OAAO,IAAI,kBAAkB,GAC1C,OAAO,SAAS,OAAO,IAAI,kBAAkB,EAAG,GAAG;EAExD;AAED,SAAO,IAAI,cAAwB,YAAY;CAChD;CAED,MAAM,UACJE,UACAC,UACe;AAEf,QAAM,KAAK,YAAY;AACvB,QAAM,KAAK,kBAAkB;EAG7B,MAAM,gBAAgB,IAAI,cAAc;GACtC,UAAU,KAAK;GACf,QAAS,KAAK,WAAmB,OAAO;GACxC,UAAW,KAAK,WAAmB,OAAO;GAC1C,aAAc,KAAK,WAAmB,OAAO;EAC9C;AACD,QAAM,cAAc,SAAS;AAG7B,OAAK,gBAAgB,IAAI,cAAwB,eAAe;GAC9D,iBAAiB,KAAK,QAAQ;GAC9B,aAAa,KAAK,QAAQ;GAC1B,kBAAkB,KAAK,WAAW;EACnC;AAED,QAAM,KAAK,cAAc,UAAU,UAAU,SAAS;CACvD;;;;CAKD,MAAM,OAAsB;AAE1B,MAAI,KAAK,cACP,MAAK,cAAc,MAAM;AAI3B,MAAI,KAAK,gBACP,KAAI;GACF,MAAM,UAAU,IAAI,mBAAmB,EACrC,iBAAiB,KAAK,gBACvB;AACD,SAAM,KAAK,WAAW,UAAU,KAAK,QAAQ;EAC9C,SAAQ,OAAO;AACd,WAAQ,MAAM,wBAAwB,MAAM;EAC7C;AAIH,MAAI,KAAK,YAAY,KAAK,QAAQ,mBAChC,KAAI;GACF,MAAM,UAAU,IAAI,mBAAmB,EACrC,UAAU,KAAK,SAChB;AACD,SAAM,KAAK,UAAU,KAAK,QAAQ;EACnC,SAAQ,OAAO;AACd,WAAQ,MAAM,yBAAyB,MAAM;EAC9C;AAGH,OAAK,UAAU,SAAS;CACzB;CAED,MAAc,aAA4B;AACxC,MAAI,KAAK,QAAQ,aAAa;GAE5B,MAAM,gBAAgB,IAAI,mBAAmB,EAC3C,WAAW,KAAK,QAAQ,UACzB;GACD,MAAM,WAAW,MAAM,KAAK,UAAU,KAAK,cAAc;AACzD,QAAK,WAAW,SAAS;EAC1B,MACC,OAAM,IAAI,MAAM;EAIlB,MAAM,eAAe,IAAI,0BAA0B;GACjD,UAAU,KAAK;GACf,gBAAgB,CAAC,UAAW;EAC7B;EACD,MAAM,gBAAgB,MAAM,KAAK,UAAU,KAAK,aAAa;AAC7D,OAAK,WAAW,cAAc,YAAY;AAG1C,QAAM,KAAK,gBAAgB;CAC5B;CAED,MAAc,iBAAgC;EAC5C,MAAM,SAAS;GACb,SAAS;GACT,WAAW,CACT;IACE,QAAQ;IACR,WAAW;IACX,QAAQ;IACR,UAAU,KAAK;IACf,WAAW,EACT,WAAW,EACT,iBAAiB,KAAK,WAAW,SAClC,EACF;GACF,CACF;EACF;EAED,MAAM,UAAU,IAAI,0BAA0B;GAC5C,UAAU,KAAK;GACf,YAAY,EACV,QAAQ,KAAK,UAAU,OAAO,CAC/B;EACF;AAED,QAAM,KAAK,UAAU,KAAK,QAAQ;CACnC;CAED,MAAc,mBAAkC;EAC9C,MAAM,UAAU,IAAI,iBAAiB;GACnC,UAAU,KAAK,WAAW;GAC1B,UAAU;GACV,UAAU,KAAK;EAChB;EAED,MAAM,WAAW,MAAM,KAAK,WAAW,UAAU,KAAK,QAAQ;AAC9D,OAAK,kBAAkB,SAAS;CACjC;AACF"}
|
|
1
|
+
{"version":3,"file":"SNSSubscriber-e7eG48yJ.mjs","names":["connection: SNSConnection","options: SNSSubscriberOptions","connectionString: string","messages: TMessage['type'][]","listener: (payload: TMessage) => Promise<void>"],"sources":["../src/sns/SNSSubscriber.ts"],"sourcesContent":["import { SubscribeCommand, UnsubscribeCommand } from '@aws-sdk/client-sns';\nimport {\n CreateQueueCommand,\n DeleteQueueCommand,\n GetQueueAttributesCommand,\n SQSClient,\n SetQueueAttributesCommand,\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 queueName?: string; // Optional queue name (defaults to auto-generated)\n createQueue?: boolean; // Auto-create SQS queue (default: true)\n deleteQueueOnClose?: boolean; // Delete queue when subscriber closes (default: false for named queues, true for auto-generated)\n waitTimeSeconds?: number; // SQS long polling (default: 20)\n maxMessages?: 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 implements EventSubscriber<TMessage>\n{\n private sqsClient: SQSClient;\n private queueUrl?: string;\n private queueArn?: string;\n private subscriptionArn?: string;\n private sqsSubscriber?: SQSSubscriber<TMessage>;\n private options: Required<SNSSubscriberOptions>;\n\n constructor(\n private connection: SNSConnection,\n options: SNSSubscriberOptions = {},\n ) {\n this.options = {\n queueName:\n options.queueName ||\n `sns-sub-${Date.now()}-${Math.random().toString(36).substring(7)}`,\n createQueue: options.createQueue ?? true,\n deleteQueueOnClose: options.deleteQueueOnClose ?? !options.queueName, // Delete auto-generated queues\n waitTimeSeconds: options.waitTimeSeconds ?? 20,\n maxMessages: options.maxMessages ?? 10,\n };\n\n // Create SQS client with same config as SNS\n this.sqsClient = new SQSClient({\n region: (this.connection as any).config.region,\n endpoint: (this.connection as any).config.endpoint,\n credentials: (this.connection as any).config.credentials,\n });\n }\n\n /**\n * Create an SNSSubscriber from a connection string\n * Format: sns://?topicArn=...&queueName=myqueue®ion=us-east-1\n */\n static async fromConnectionString<\n TMessage extends PublishableMessage<string, any>,\n >(connectionString: string): Promise<SNSSubscriber<TMessage>> {\n const url = new URL(connectionString);\n const params = url.searchParams;\n\n const { SNSConnection } = await import('./SNSConnection');\n const connection =\n await SNSConnection.fromConnectionString(connectionString);\n\n const options: SNSSubscriberOptions = {\n queueName: params.get('queueName') || undefined,\n createQueue: params.get('createQueue') !== 'false',\n deleteQueueOnClose: params.get('deleteQueueOnClose') === 'true',\n waitTimeSeconds: params.get('waitTimeSeconds')\n ? Number.parseInt(params.get('waitTimeSeconds')!, 10)\n : undefined,\n };\n\n return new SNSSubscriber<TMessage>(connection, options);\n }\n\n async subscribe(\n messages: TMessage['type'][],\n listener: (payload: TMessage) => Promise<void>,\n ): Promise<void> {\n // Setup SQS queue and SNS subscription\n await this.setupQueue();\n await this.subscribeToTopic();\n\n // Create SQS connection for the subscriber\n const sqsConnection = new SQSConnection({\n queueUrl: this.queueUrl!,\n region: (this.connection as any).config.region,\n endpoint: (this.connection as any).config.endpoint,\n credentials: (this.connection as any).config.credentials,\n });\n await sqsConnection.connect();\n\n // Use SQSSubscriber to receive messages, with topic filtering\n this.sqsSubscriber = new SQSSubscriber<TMessage>(sqsConnection, {\n waitTimeSeconds: this.options.waitTimeSeconds,\n maxMessages: this.options.maxMessages,\n expectedTopicArn: this.connection.topicArn,\n });\n\n await this.sqsSubscriber.subscribe(messages, listener);\n }\n\n /**\n * Stop receiving messages and clean up resources\n */\n async stop(): Promise<void> {\n // Stop SQS subscriber\n if (this.sqsSubscriber) {\n this.sqsSubscriber.stop();\n }\n\n // Unsubscribe from topic\n if (this.subscriptionArn) {\n try {\n const command = new UnsubscribeCommand({\n SubscriptionArn: this.subscriptionArn,\n });\n await this.connection.snsClient.send(command);\n } catch (error) {\n console.error('Error unsubscribing:', error);\n }\n }\n\n // Delete queue if configured\n if (this.queueUrl && this.options.deleteQueueOnClose) {\n try {\n const command = new DeleteQueueCommand({\n QueueUrl: this.queueUrl,\n });\n await this.sqsClient.send(command);\n } catch (error) {\n console.error('Error deleting queue:', error);\n }\n }\n\n this.sqsClient.destroy();\n }\n\n private async setupQueue(): Promise<void> {\n if (this.options.createQueue) {\n // Create queue\n const createCommand = new CreateQueueCommand({\n QueueName: this.options.queueName,\n });\n const response = await this.sqsClient.send(createCommand);\n this.queueUrl = response.QueueUrl!;\n } else {\n throw new Error('Queue must exist or createQueue option must be true');\n }\n\n // Get queue ARN\n const attrsCommand = new GetQueueAttributesCommand({\n QueueUrl: this.queueUrl,\n AttributeNames: ['QueueArn'],\n });\n const attrsResponse = await this.sqsClient.send(attrsCommand);\n this.queueArn = attrsResponse.Attributes?.QueueArn!;\n\n // Set queue policy to allow SNS\n await this.setQueuePolicy();\n }\n\n private async setQueuePolicy(): Promise<void> {\n const policy = {\n Version: '2012-10-17',\n Statement: [\n {\n Effect: 'Allow',\n Principal: '*',\n Action: 'SQS:SendMessage',\n Resource: this.queueArn,\n Condition: {\n ArnEquals: {\n 'aws:SourceArn': this.connection.topicArn,\n },\n },\n },\n ],\n };\n\n const command = new SetQueueAttributesCommand({\n QueueUrl: this.queueUrl,\n Attributes: {\n Policy: JSON.stringify(policy),\n },\n });\n\n await this.sqsClient.send(command);\n }\n\n private async subscribeToTopic(): Promise<void> {\n const command = new SubscribeCommand({\n TopicArn: this.connection.topicArn,\n Protocol: 'sqs',\n Endpoint: this.queueArn,\n });\n\n const response = await this.connection.snsClient.send(command);\n this.subscriptionArn = response.SubscriptionArn!;\n }\n}\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SQSConnection-
|
|
1
|
+
{"version":3,"file":"SQSConnection-BBlDWZLU.mjs","names":["config: SQSConnectionConfig","clientConfig: SQSClientConfig","connectionString: string"],"sources":["../src/sqs/SQSConnection.ts"],"sourcesContent":["import { SQSClient, type SQSClientConfig } from '@aws-sdk/client-sqs';\nimport type { EventConnection } from '../types';\nimport { EventPublisherType } from '../types';\n\nexport interface SQSConnectionConfig {\n queueUrl: string;\n region?: string;\n endpoint?: string; // Custom endpoint (e.g., for LocalStack)\n credentials?: {\n accessKeyId: string;\n secretAccessKey: string;\n sessionToken?: string;\n };\n}\n\nexport class SQSConnection implements EventConnection {\n readonly type = EventPublisherType.SQS;\n private client: SQSClient;\n private connected = false;\n\n constructor(readonly config: SQSConnectionConfig) {\n this.config = config;\n\n const clientConfig: SQSClientConfig = {\n region: config.region,\n endpoint: config.endpoint,\n credentials: config.credentials,\n };\n\n this.client = new SQSClient(clientConfig);\n }\n\n /**\n * Create an SQSConnection from a connection string\n * Format: sqs://?queueUrl=https://sqs.region.amazonaws.com/accountId/queueName®ion=us-east-1&endpoint=http://localhost:4566\n */\n static async fromConnectionString(\n connectionString: string,\n ): Promise<SQSConnection> {\n const url = new URL(connectionString);\n const params = url.searchParams;\n\n const queueUrl = params.get('queueUrl');\n if (!queueUrl) {\n throw new Error('queueUrl parameter is required in connection string');\n }\n\n const config: SQSConnectionConfig = {\n queueUrl,\n region: params.get('region') || undefined,\n endpoint: params.get('endpoint') || undefined,\n };\n\n // Parse credentials if provided\n const accessKeyId = params.get('accessKeyId');\n const secretAccessKey = params.get('secretAccessKey');\n if (accessKeyId && secretAccessKey) {\n config.credentials = {\n accessKeyId,\n secretAccessKey,\n sessionToken: params.get('sessionToken') || undefined,\n };\n }\n\n const connection = new SQSConnection(config);\n await connection.connect();\n return connection;\n }\n\n async connect(): Promise<void> {\n // SQS doesn't have a traditional connection, but we mark as connected\n this.connected = true;\n }\n\n async close(): Promise<void> {\n this.client.destroy();\n this.connected = false;\n }\n\n isConnected(): boolean {\n return this.connected;\n }\n\n /**\n * Get the underlying SQS client\n */\n get sqsClient(): SQSClient {\n return this.client;\n }\n\n /**\n * Get the queue URL for this connection\n */\n get queueUrl(): string {\n return this.config.queueUrl;\n }\n}\n"],"mappings":";;;;AAeA,IAAa,gBAAb,MAAa,cAAyC;CACpD,AAAS,OAAO,mBAAmB;CACnC,AAAQ;CACR,AAAQ,YAAY;CAEpB,YAAqBA,QAA6B;EAA7B;AACnB,OAAK,SAAS;EAEd,MAAMC,eAAgC;GACpC,QAAQ,OAAO;GACf,UAAU,OAAO;GACjB,aAAa,OAAO;EACrB;AAED,OAAK,SAAS,IAAI,UAAU;CAC7B;;;;;CAMD,aAAa,qBACXC,kBACwB;EACxB,MAAM,MAAM,IAAI,IAAI;EACpB,MAAM,SAAS,IAAI;EAEnB,MAAM,WAAW,OAAO,IAAI,WAAW;AACvC,OAAK,SACH,OAAM,IAAI,MAAM;EAGlB,MAAMF,SAA8B;GAClC;GACA,QAAQ,OAAO,IAAI,SAAS;GAC5B,UAAU,OAAO,IAAI,WAAW;EACjC;EAGD,MAAM,cAAc,OAAO,IAAI,cAAc;EAC7C,MAAM,kBAAkB,OAAO,IAAI,kBAAkB;AACrD,MAAI,eAAe,gBACjB,QAAO,cAAc;GACnB;GACA;GACA,cAAc,OAAO,IAAI,eAAe;EACzC;EAGH,MAAM,aAAa,IAAI,cAAc;AACrC,QAAM,WAAW,SAAS;AAC1B,SAAO;CACR;CAED,MAAM,UAAyB;AAE7B,OAAK,YAAY;CAClB;CAED,MAAM,QAAuB;AAC3B,OAAK,OAAO,SAAS;AACrB,OAAK,YAAY;CAClB;CAED,cAAuB;AACrB,SAAO,KAAK;CACb;;;;CAKD,IAAI,YAAuB;AACzB,SAAO,KAAK;CACb;;;;CAKD,IAAI,WAAmB;AACrB,SAAO,KAAK,OAAO;CACpB;AACF"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SQSConnection-
|
|
1
|
+
{"version":3,"file":"SQSConnection-DWYDtftQ.cjs","names":["EventPublisherType","config: SQSConnectionConfig","clientConfig: SQSClientConfig","SQSClient","connectionString: string"],"sources":["../src/sqs/SQSConnection.ts"],"sourcesContent":["import { SQSClient, type SQSClientConfig } from '@aws-sdk/client-sqs';\nimport type { EventConnection } from '../types';\nimport { EventPublisherType } from '../types';\n\nexport interface SQSConnectionConfig {\n queueUrl: string;\n region?: string;\n endpoint?: string; // Custom endpoint (e.g., for LocalStack)\n credentials?: {\n accessKeyId: string;\n secretAccessKey: string;\n sessionToken?: string;\n };\n}\n\nexport class SQSConnection implements EventConnection {\n readonly type = EventPublisherType.SQS;\n private client: SQSClient;\n private connected = false;\n\n constructor(readonly config: SQSConnectionConfig) {\n this.config = config;\n\n const clientConfig: SQSClientConfig = {\n region: config.region,\n endpoint: config.endpoint,\n credentials: config.credentials,\n };\n\n this.client = new SQSClient(clientConfig);\n }\n\n /**\n * Create an SQSConnection from a connection string\n * Format: sqs://?queueUrl=https://sqs.region.amazonaws.com/accountId/queueName®ion=us-east-1&endpoint=http://localhost:4566\n */\n static async fromConnectionString(\n connectionString: string,\n ): Promise<SQSConnection> {\n const url = new URL(connectionString);\n const params = url.searchParams;\n\n const queueUrl = params.get('queueUrl');\n if (!queueUrl) {\n throw new Error('queueUrl parameter is required in connection string');\n }\n\n const config: SQSConnectionConfig = {\n queueUrl,\n region: params.get('region') || undefined,\n endpoint: params.get('endpoint') || undefined,\n };\n\n // Parse credentials if provided\n const accessKeyId = params.get('accessKeyId');\n const secretAccessKey = params.get('secretAccessKey');\n if (accessKeyId && secretAccessKey) {\n config.credentials = {\n accessKeyId,\n secretAccessKey,\n sessionToken: params.get('sessionToken') || undefined,\n };\n }\n\n const connection = new SQSConnection(config);\n await connection.connect();\n return connection;\n }\n\n async connect(): Promise<void> {\n // SQS doesn't have a traditional connection, but we mark as connected\n this.connected = true;\n }\n\n async close(): Promise<void> {\n this.client.destroy();\n this.connected = false;\n }\n\n isConnected(): boolean {\n return this.connected;\n }\n\n /**\n * Get the underlying SQS client\n */\n get sqsClient(): SQSClient {\n return this.client;\n }\n\n /**\n * Get the queue URL for this connection\n */\n get queueUrl(): string {\n return this.config.queueUrl;\n }\n}\n"],"mappings":";;;;;AAeA,IAAa,gBAAb,MAAa,cAAyC;CACpD,AAAS,OAAOA,iCAAmB;CACnC,AAAQ;CACR,AAAQ,YAAY;CAEpB,YAAqBC,QAA6B;EAA7B;AACnB,OAAK,SAAS;EAEd,MAAMC,eAAgC;GACpC,QAAQ,OAAO;GACf,UAAU,OAAO;GACjB,aAAa,OAAO;EACrB;AAED,OAAK,SAAS,IAAIC,+BAAU;CAC7B;;;;;CAMD,aAAa,qBACXC,kBACwB;EACxB,MAAM,MAAM,IAAI,IAAI;EACpB,MAAM,SAAS,IAAI;EAEnB,MAAM,WAAW,OAAO,IAAI,WAAW;AACvC,OAAK,SACH,OAAM,IAAI,MAAM;EAGlB,MAAMH,SAA8B;GAClC;GACA,QAAQ,OAAO,IAAI,SAAS;GAC5B,UAAU,OAAO,IAAI,WAAW;EACjC;EAGD,MAAM,cAAc,OAAO,IAAI,cAAc;EAC7C,MAAM,kBAAkB,OAAO,IAAI,kBAAkB;AACrD,MAAI,eAAe,gBACjB,QAAO,cAAc;GACnB;GACA;GACA,cAAc,OAAO,IAAI,eAAe;EACzC;EAGH,MAAM,aAAa,IAAI,cAAc;AACrC,QAAM,WAAW,SAAS;AAC1B,SAAO;CACR;CAED,MAAM,UAAyB;AAE7B,OAAK,YAAY;CAClB;CAED,MAAM,QAAuB;AAC3B,OAAK,OAAO,SAAS;AACrB,OAAK,YAAY;CAClB;CAED,cAAuB;AACrB,SAAO,KAAK;CACb;;;;CAKD,IAAI,YAAuB;AACzB,SAAO,KAAK;CACb;;;;CAKD,IAAI,WAAmB;AACrB,SAAO,KAAK,OAAO;CACpB;AACF"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SQSPublisher-
|
|
1
|
+
{"version":3,"file":"SQSPublisher-B3lS2zbu.mjs","names":["connection: SQSConnection","options: SQSPublisherOptions","connectionString: string","messages: TMessage[]","entries: SendMessageBatchRequestEntry[]","input: SendMessageBatchCommandInput"],"sources":["../src/sqs/SQSPublisher.ts"],"sourcesContent":["import {\n SendMessageBatchCommand,\n type SendMessageBatchCommandInput,\n type SendMessageBatchRequestEntry,\n} from '@aws-sdk/client-sqs';\nimport chunk from 'lodash.chunk';\nimport type { EventPublisher, PublishableMessage } from '../types';\nimport type { SQSConnection } from './SQSConnection';\n\nexport interface SQSPublisherOptions {\n maxBatchSize?: number; // Default: 10 (SQS limit)\n}\n\nexport class SQSPublisher<TMessage extends PublishableMessage<string, any>>\n implements EventPublisher<TMessage>\n{\n readonly options: Required<SQSPublisherOptions>;\n\n constructor(\n readonly connection: SQSConnection,\n options: SQSPublisherOptions = {},\n ) {\n this.options = {\n maxBatchSize: options.maxBatchSize ?? 10, // SQS limit\n };\n }\n\n /**\n * Create an SQSPublisher from a connection string\n * Format: sqs://?queueUrl=https://sqs.region.amazonaws.com/accountId/queueName®ion=us-east-1&endpoint=http://localhost:4566&maxBatchSize=10\n */\n static async fromConnectionString<\n TMessage extends PublishableMessage<string, any>,\n >(connectionString: string): Promise<SQSPublisher<TMessage>> {\n const url = new URL(connectionString);\n const params = url.searchParams;\n\n const { SQSConnection } = await import('./SQSConnection');\n const connection =\n await SQSConnection.fromConnectionString(connectionString);\n\n const options: SQSPublisherOptions = {\n maxBatchSize: params.get('maxBatchSize')\n ? Number.parseInt(params.get('maxBatchSize')!, 10)\n : undefined,\n };\n\n return new SQSPublisher<TMessage>(connection, options);\n }\n\n async publish(messages: TMessage[]): Promise<void> {\n if (messages.length === 0) return;\n\n if (!this.connection.isConnected()) {\n await this.connection.connect();\n }\n\n // Split messages into batches (SQS limit is 10 messages per batch)\n const batches = chunk(messages, this.options.maxBatchSize);\n\n // Send all batches\n await Promise.all(batches.map((batch) => this.sendBatch(batch)));\n }\n\n private async sendBatch(messages: TMessage[]): Promise<void> {\n const entries: SendMessageBatchRequestEntry[] = messages.map(\n (message, index) => ({\n Id: `${index}`,\n MessageBody: JSON.stringify({\n type: message.type,\n payload: message.payload,\n }),\n MessageAttributes: {\n type: {\n DataType: 'String',\n StringValue: message.type,\n },\n },\n }),\n );\n\n const input: SendMessageBatchCommandInput = {\n QueueUrl: this.connection.queueUrl,\n Entries: entries,\n };\n\n const command = new SendMessageBatchCommand(input);\n const response = await this.connection.sqsClient.send(command);\n\n // Check for failures\n if (response.Failed && response.Failed.length > 0) {\n const errors = response.Failed.map(\n (f) => `${f.Id}: ${f.Code} - ${f.Message}`,\n ).join(', ');\n throw new Error(\n `Failed to send ${response.Failed.length} messages: ${errors}`,\n );\n }\n }\n\n async close(): Promise<void> {\n // Publisher doesn't own the connection\n // Connection should be closed by whoever created it\n }\n}\n"],"mappings":";;;;AAaA,IAAa,eAAb,MAAa,aAEb;CACE,AAAS;CAET,YACWA,YACTC,UAA+B,CAAE,GACjC;EAFS;AAGT,OAAK,UAAU,EACb,cAAc,QAAQ,gBAAgB,GACvC;CACF;;;;;CAMD,aAAa,qBAEXC,kBAA2D;EAC3D,MAAM,MAAM,IAAI,IAAI;EACpB,MAAM,SAAS,IAAI;EAEnB,MAAM,EAAE,eAAe,GAAG,MAAM,OAAO;EACvC,MAAM,aACJ,MAAM,cAAc,qBAAqB,iBAAiB;EAE5D,MAAMD,UAA+B,EACnC,cAAc,OAAO,IAAI,eAAe,GACpC,OAAO,SAAS,OAAO,IAAI,eAAe,EAAG,GAAG,UAErD;AAED,SAAO,IAAI,aAAuB,YAAY;CAC/C;CAED,MAAM,QAAQE,UAAqC;AACjD,MAAI,SAAS,WAAW,EAAG;AAE3B,OAAK,KAAK,WAAW,aAAa,CAChC,OAAM,KAAK,WAAW,SAAS;EAIjC,MAAM,UAAU,MAAM,UAAU,KAAK,QAAQ,aAAa;AAG1D,QAAM,QAAQ,IAAI,QAAQ,IAAI,CAAC,UAAU,KAAK,UAAU,MAAM,CAAC,CAAC;CACjE;CAED,MAAc,UAAUA,UAAqC;EAC3D,MAAMC,UAA0C,SAAS,IACvD,CAAC,SAAS,WAAW;GACnB,KAAK,EAAE,MAAM;GACb,aAAa,KAAK,UAAU;IAC1B,MAAM,QAAQ;IACd,SAAS,QAAQ;GAClB,EAAC;GACF,mBAAmB,EACjB,MAAM;IACJ,UAAU;IACV,aAAa,QAAQ;GACtB,EACF;EACF,GACF;EAED,MAAMC,QAAsC;GAC1C,UAAU,KAAK,WAAW;GAC1B,SAAS;EACV;EAED,MAAM,UAAU,IAAI,wBAAwB;EAC5C,MAAM,WAAW,MAAM,KAAK,WAAW,UAAU,KAAK,QAAQ;AAG9D,MAAI,SAAS,UAAU,SAAS,OAAO,SAAS,GAAG;GACjD,MAAM,SAAS,SAAS,OAAO,IAC7B,CAAC,OAAO,EAAE,EAAE,GAAG,IAAI,EAAE,KAAK,KAAK,EAAE,QAAQ,EAC1C,CAAC,KAAK,KAAK;AACZ,SAAM,IAAI,OACP,iBAAiB,SAAS,OAAO,OAAO,aAAa,OAAO;EAEhE;CACF;CAED,MAAM,QAAuB,CAG5B;AACF"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SQSPublisher-
|
|
1
|
+
{"version":3,"file":"SQSPublisher-nzbSaXSH.cjs","names":["connection: SQSConnection","options: SQSPublisherOptions","connectionString: string","messages: TMessage[]","entries: SendMessageBatchRequestEntry[]","input: SendMessageBatchCommandInput","SendMessageBatchCommand"],"sources":["../src/sqs/SQSPublisher.ts"],"sourcesContent":["import {\n SendMessageBatchCommand,\n type SendMessageBatchCommandInput,\n type SendMessageBatchRequestEntry,\n} from '@aws-sdk/client-sqs';\nimport chunk from 'lodash.chunk';\nimport type { EventPublisher, PublishableMessage } from '../types';\nimport type { SQSConnection } from './SQSConnection';\n\nexport interface SQSPublisherOptions {\n maxBatchSize?: number; // Default: 10 (SQS limit)\n}\n\nexport class SQSPublisher<TMessage extends PublishableMessage<string, any>>\n implements EventPublisher<TMessage>\n{\n readonly options: Required<SQSPublisherOptions>;\n\n constructor(\n readonly connection: SQSConnection,\n options: SQSPublisherOptions = {},\n ) {\n this.options = {\n maxBatchSize: options.maxBatchSize ?? 10, // SQS limit\n };\n }\n\n /**\n * Create an SQSPublisher from a connection string\n * Format: sqs://?queueUrl=https://sqs.region.amazonaws.com/accountId/queueName®ion=us-east-1&endpoint=http://localhost:4566&maxBatchSize=10\n */\n static async fromConnectionString<\n TMessage extends PublishableMessage<string, any>,\n >(connectionString: string): Promise<SQSPublisher<TMessage>> {\n const url = new URL(connectionString);\n const params = url.searchParams;\n\n const { SQSConnection } = await import('./SQSConnection');\n const connection =\n await SQSConnection.fromConnectionString(connectionString);\n\n const options: SQSPublisherOptions = {\n maxBatchSize: params.get('maxBatchSize')\n ? Number.parseInt(params.get('maxBatchSize')!, 10)\n : undefined,\n };\n\n return new SQSPublisher<TMessage>(connection, options);\n }\n\n async publish(messages: TMessage[]): Promise<void> {\n if (messages.length === 0) return;\n\n if (!this.connection.isConnected()) {\n await this.connection.connect();\n }\n\n // Split messages into batches (SQS limit is 10 messages per batch)\n const batches = chunk(messages, this.options.maxBatchSize);\n\n // Send all batches\n await Promise.all(batches.map((batch) => this.sendBatch(batch)));\n }\n\n private async sendBatch(messages: TMessage[]): Promise<void> {\n const entries: SendMessageBatchRequestEntry[] = messages.map(\n (message, index) => ({\n Id: `${index}`,\n MessageBody: JSON.stringify({\n type: message.type,\n payload: message.payload,\n }),\n MessageAttributes: {\n type: {\n DataType: 'String',\n StringValue: message.type,\n },\n },\n }),\n );\n\n const input: SendMessageBatchCommandInput = {\n QueueUrl: this.connection.queueUrl,\n Entries: entries,\n };\n\n const command = new SendMessageBatchCommand(input);\n const response = await this.connection.sqsClient.send(command);\n\n // Check for failures\n if (response.Failed && response.Failed.length > 0) {\n const errors = response.Failed.map(\n (f) => `${f.Id}: ${f.Code} - ${f.Message}`,\n ).join(', ');\n throw new Error(\n `Failed to send ${response.Failed.length} messages: ${errors}`,\n );\n }\n }\n\n async close(): Promise<void> {\n // Publisher doesn't own the connection\n // Connection should be closed by whoever created it\n }\n}\n"],"mappings":";;;;;AAaA,IAAa,eAAb,MAAa,aAEb;CACE,AAAS;CAET,YACWA,YACTC,UAA+B,CAAE,GACjC;EAFS;AAGT,OAAK,UAAU,EACb,cAAc,QAAQ,gBAAgB,GACvC;CACF;;;;;CAMD,aAAa,qBAEXC,kBAA2D;EAC3D,MAAM,MAAM,IAAI,IAAI;EACpB,MAAM,SAAS,IAAI;EAEnB,MAAM,EAAE,eAAe,GAAG,2CAAM;EAChC,MAAM,aACJ,MAAM,cAAc,qBAAqB,iBAAiB;EAE5D,MAAMD,UAA+B,EACnC,cAAc,OAAO,IAAI,eAAe,GACpC,OAAO,SAAS,OAAO,IAAI,eAAe,EAAG,GAAG,UAErD;AAED,SAAO,IAAI,aAAuB,YAAY;CAC/C;CAED,MAAM,QAAQE,UAAqC;AACjD,MAAI,SAAS,WAAW,EAAG;AAE3B,OAAK,KAAK,WAAW,aAAa,CAChC,OAAM,KAAK,WAAW,SAAS;EAIjC,MAAM,UAAU,0BAAM,UAAU,KAAK,QAAQ,aAAa;AAG1D,QAAM,QAAQ,IAAI,QAAQ,IAAI,CAAC,UAAU,KAAK,UAAU,MAAM,CAAC,CAAC;CACjE;CAED,MAAc,UAAUA,UAAqC;EAC3D,MAAMC,UAA0C,SAAS,IACvD,CAAC,SAAS,WAAW;GACnB,KAAK,EAAE,MAAM;GACb,aAAa,KAAK,UAAU;IAC1B,MAAM,QAAQ;IACd,SAAS,QAAQ;GAClB,EAAC;GACF,mBAAmB,EACjB,MAAM;IACJ,UAAU;IACV,aAAa,QAAQ;GACtB,EACF;EACF,GACF;EAED,MAAMC,QAAsC;GAC1C,UAAU,KAAK,WAAW;GAC1B,SAAS;EACV;EAED,MAAM,UAAU,IAAIC,6CAAwB;EAC5C,MAAM,WAAW,MAAM,KAAK,WAAW,UAAU,KAAK,QAAQ;AAG9D,MAAI,SAAS,UAAU,SAAS,OAAO,SAAS,GAAG;GACjD,MAAM,SAAS,SAAS,OAAO,IAC7B,CAAC,OAAO,EAAE,EAAE,GAAG,IAAI,EAAE,KAAK,KAAK,EAAE,QAAQ,EAC1C,CAAC,KAAK,KAAK;AACZ,SAAM,IAAI,OACP,iBAAiB,SAAS,OAAO,OAAO,aAAa,OAAO;EAEhE;CACF;CAED,MAAM,QAAuB,CAG5B;AACF"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SQSSubscriber-
|
|
1
|
+
{"version":3,"file":"SQSSubscriber-84pFnW1z.mjs","names":["connection: SQSConnection","options: SQSSubscriberOptions","messages: TMessage['type'][]","listener: (payload: TMessage) => Promise<void>","messages: Message[]","body: string","receiptHandle: string","ms: number"],"sources":["../src/sqs/SQSSubscriber.ts"],"sourcesContent":["import {\n DeleteMessageCommand,\n type Message,\n ReceiveMessageCommand,\n} from '@aws-sdk/client-sqs';\nimport type { EventSubscriber, PublishableMessage } from '../types';\nimport type { SQSConnection } from './SQSConnection';\n\nexport interface SQSSubscriberOptions {\n waitTimeSeconds?: number; // SQS long polling (default: 20)\n maxMessages?: number; // Max messages per poll (default: 10)\n expectedTopicArn?: string; // Optional: verify messages are from specific SNS topic\n}\n\n/**\n * SQS Subscriber - receives messages from an SQS queue\n *\n * This subscriber can receive:\n * 1. Direct SQS messages\n * 2. SNS messages that were pushed to the queue (SNS → SQS pattern)\n *\n * When receiving SNS messages, it:\n * - Automatically detects and parses the SNS wrapper format\n * - Filters by TopicArn (if expectedTopicArn is provided)\n * - Filters by message type\n * - Processes the actual message payload\n */\nexport class SQSSubscriber<TMessage extends PublishableMessage<string, any>>\n implements EventSubscriber<TMessage>\n{\n private polling = false;\n private messageTypes: Set<TMessage['type']> = new Set();\n private listener?: (payload: TMessage) => Promise<void>;\n private options: Required<SQSSubscriberOptions>;\n\n constructor(\n private connection: SQSConnection,\n options: SQSSubscriberOptions = {},\n ) {\n this.options = {\n waitTimeSeconds: options.waitTimeSeconds ?? 20,\n maxMessages: options.maxMessages ?? 10,\n expectedTopicArn: options.expectedTopicArn ?? '',\n };\n }\n\n async subscribe(\n messages: TMessage['type'][],\n listener: (payload: TMessage) => Promise<void>,\n ): Promise<void> {\n this.messageTypes = new Set(messages);\n this.listener = listener;\n\n if (!this.connection.isConnected()) {\n await this.connection.connect();\n }\n\n this.polling = true;\n this.poll();\n }\n\n stop(): void {\n this.polling = false;\n }\n\n private async poll(): Promise<void> {\n while (this.polling) {\n try {\n const command = new ReceiveMessageCommand({\n QueueUrl: this.connection.queueUrl,\n MaxNumberOfMessages: this.options.maxMessages,\n WaitTimeSeconds: this.options.waitTimeSeconds,\n MessageAttributeNames: ['All'],\n });\n\n const response = await this.connection.sqsClient.send(command);\n\n if (response.Messages && response.Messages.length > 0) {\n await this.processMessages(response.Messages);\n }\n } catch (error) {\n console.error('Error receiving messages from SQS:', error);\n await this.sleep(1000);\n }\n }\n }\n\n private async processMessages(messages: Message[]): Promise<void> {\n for (const message of messages) {\n try {\n if (!message.Body) continue;\n\n // Try to parse as SNS message first\n const parsedMessage = this.parseMessage(message.Body);\n\n if (!parsedMessage) {\n // Not a valid message format\n await this.deleteMessage(message.ReceiptHandle!);\n continue;\n }\n\n const { payload, messageType, topicArn } = parsedMessage;\n\n // If expectedTopicArn is set, verify the message is from that topic\n if (this.options.expectedTopicArn && topicArn) {\n if (topicArn !== this.options.expectedTopicArn) {\n console.warn(\n `Received message from unexpected topic: ${topicArn}, expected: ${this.options.expectedTopicArn}`,\n );\n await this.deleteMessage(message.ReceiptHandle!);\n continue;\n }\n }\n\n // Check if we're subscribed to this message type\n if (\n messageType &&\n this.messageTypes.has(messageType as TMessage['type'])\n ) {\n // Call listener\n if (this.listener) {\n await this.listener(payload as TMessage);\n }\n\n // Delete message after successful processing\n await this.deleteMessage(message.ReceiptHandle!);\n } else {\n // Delete messages we're not subscribed to\n await this.deleteMessage(message.ReceiptHandle!);\n }\n } catch (error) {\n console.error('Error processing message:', error);\n // Message will become visible again\n }\n }\n }\n\n /**\n * Parse message body - handles both direct SQS messages and SNS-wrapped messages\n */\n private parseMessage(body: string): {\n payload: unknown;\n messageType: string | undefined;\n topicArn?: string;\n } | null {\n try {\n const parsed = JSON.parse(body);\n\n // Check if this is an SNS notification wrapper\n if (parsed.Type === 'Notification' && parsed.Message) {\n // This is an SNS message\n const snsMessage = JSON.parse(parsed.Message);\n const messageType =\n parsed.MessageAttributes?.type?.Value || snsMessage.type;\n\n return {\n payload: snsMessage,\n messageType,\n topicArn: parsed.TopicArn,\n };\n }\n\n // This is a direct SQS message\n return {\n payload: parsed,\n messageType: parsed.type,\n };\n } catch (error) {\n console.error('Failed to parse message:', error);\n return null;\n }\n }\n\n private async deleteMessage(receiptHandle: string): Promise<void> {\n try {\n const command = new DeleteMessageCommand({\n QueueUrl: this.connection.queueUrl,\n ReceiptHandle: receiptHandle,\n });\n await this.connection.sqsClient.send(command);\n } catch (error) {\n console.error('Error deleting message:', error);\n }\n }\n\n private sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;AA2BA,IAAa,gBAAb,MAEA;CACE,AAAQ,UAAU;CAClB,AAAQ,+BAAsC,IAAI;CAClD,AAAQ;CACR,AAAQ;CAER,YACUA,YACRC,UAAgC,CAAE,GAClC;EAFQ;AAGR,OAAK,UAAU;GACb,iBAAiB,QAAQ,mBAAmB;GAC5C,aAAa,QAAQ,eAAe;GACpC,kBAAkB,QAAQ,oBAAoB;EAC/C;CACF;CAED,MAAM,UACJC,UACAC,UACe;AACf,OAAK,eAAe,IAAI,IAAI;AAC5B,OAAK,WAAW;AAEhB,OAAK,KAAK,WAAW,aAAa,CAChC,OAAM,KAAK,WAAW,SAAS;AAGjC,OAAK,UAAU;AACf,OAAK,MAAM;CACZ;CAED,OAAa;AACX,OAAK,UAAU;CAChB;CAED,MAAc,OAAsB;AAClC,SAAO,KAAK,QACV,KAAI;GACF,MAAM,UAAU,IAAI,sBAAsB;IACxC,UAAU,KAAK,WAAW;IAC1B,qBAAqB,KAAK,QAAQ;IAClC,iBAAiB,KAAK,QAAQ;IAC9B,uBAAuB,CAAC,KAAM;GAC/B;GAED,MAAM,WAAW,MAAM,KAAK,WAAW,UAAU,KAAK,QAAQ;AAE9D,OAAI,SAAS,YAAY,SAAS,SAAS,SAAS,EAClD,OAAM,KAAK,gBAAgB,SAAS,SAAS;EAEhD,SAAQ,OAAO;AACd,WAAQ,MAAM,sCAAsC,MAAM;AAC1D,SAAM,KAAK,MAAM,IAAK;EACvB;CAEJ;CAED,MAAc,gBAAgBC,UAAoC;AAChE,OAAK,MAAM,WAAW,SACpB,KAAI;AACF,QAAK,QAAQ,KAAM;GAGnB,MAAM,gBAAgB,KAAK,aAAa,QAAQ,KAAK;AAErD,QAAK,eAAe;AAElB,UAAM,KAAK,cAAc,QAAQ,cAAe;AAChD;GACD;GAED,MAAM,EAAE,SAAS,aAAa,UAAU,GAAG;AAG3C,OAAI,KAAK,QAAQ,oBAAoB,UACnC;QAAI,aAAa,KAAK,QAAQ,kBAAkB;AAC9C,aAAQ,MACL,0CAA0C,SAAS,cAAc,KAAK,QAAQ,iBAAiB,EACjG;AACD,WAAM,KAAK,cAAc,QAAQ,cAAe;AAChD;IACD;;AAIH,OACE,eACA,KAAK,aAAa,IAAI,YAAgC,EACtD;AAEA,QAAI,KAAK,SACP,OAAM,KAAK,SAAS,QAAoB;AAI1C,UAAM,KAAK,cAAc,QAAQ,cAAe;GACjD,MAEC,OAAM,KAAK,cAAc,QAAQ,cAAe;EAEnD,SAAQ,OAAO;AACd,WAAQ,MAAM,6BAA6B,MAAM;EAElD;CAEJ;;;;CAKD,AAAQ,aAAaC,MAIZ;AACP,MAAI;GACF,MAAM,SAAS,KAAK,MAAM,KAAK;AAG/B,OAAI,OAAO,SAAS,kBAAkB,OAAO,SAAS;IAEpD,MAAM,aAAa,KAAK,MAAM,OAAO,QAAQ;IAC7C,MAAM,cACJ,OAAO,mBAAmB,MAAM,SAAS,WAAW;AAEtD,WAAO;KACL,SAAS;KACT;KACA,UAAU,OAAO;IAClB;GACF;AAGD,UAAO;IACL,SAAS;IACT,aAAa,OAAO;GACrB;EACF,SAAQ,OAAO;AACd,WAAQ,MAAM,4BAA4B,MAAM;AAChD,UAAO;EACR;CACF;CAED,MAAc,cAAcC,eAAsC;AAChE,MAAI;GACF,MAAM,UAAU,IAAI,qBAAqB;IACvC,UAAU,KAAK,WAAW;IAC1B,eAAe;GAChB;AACD,SAAM,KAAK,WAAW,UAAU,KAAK,QAAQ;EAC9C,SAAQ,OAAO;AACd,WAAQ,MAAM,2BAA2B,MAAM;EAChD;CACF;CAED,AAAQ,MAAMC,IAA2B;AACvC,SAAO,IAAI,QAAQ,CAAC,YAAY,WAAW,SAAS,GAAG;CACxD;AACF"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SQSSubscriber-
|
|
1
|
+
{"version":3,"file":"SQSSubscriber-DcL9BbG1.cjs","names":["connection: SQSConnection","options: SQSSubscriberOptions","messages: TMessage['type'][]","listener: (payload: TMessage) => Promise<void>","ReceiveMessageCommand","messages: Message[]","body: string","receiptHandle: string","DeleteMessageCommand","ms: number"],"sources":["../src/sqs/SQSSubscriber.ts"],"sourcesContent":["import {\n DeleteMessageCommand,\n type Message,\n ReceiveMessageCommand,\n} from '@aws-sdk/client-sqs';\nimport type { EventSubscriber, PublishableMessage } from '../types';\nimport type { SQSConnection } from './SQSConnection';\n\nexport interface SQSSubscriberOptions {\n waitTimeSeconds?: number; // SQS long polling (default: 20)\n maxMessages?: number; // Max messages per poll (default: 10)\n expectedTopicArn?: string; // Optional: verify messages are from specific SNS topic\n}\n\n/**\n * SQS Subscriber - receives messages from an SQS queue\n *\n * This subscriber can receive:\n * 1. Direct SQS messages\n * 2. SNS messages that were pushed to the queue (SNS → SQS pattern)\n *\n * When receiving SNS messages, it:\n * - Automatically detects and parses the SNS wrapper format\n * - Filters by TopicArn (if expectedTopicArn is provided)\n * - Filters by message type\n * - Processes the actual message payload\n */\nexport class SQSSubscriber<TMessage extends PublishableMessage<string, any>>\n implements EventSubscriber<TMessage>\n{\n private polling = false;\n private messageTypes: Set<TMessage['type']> = new Set();\n private listener?: (payload: TMessage) => Promise<void>;\n private options: Required<SQSSubscriberOptions>;\n\n constructor(\n private connection: SQSConnection,\n options: SQSSubscriberOptions = {},\n ) {\n this.options = {\n waitTimeSeconds: options.waitTimeSeconds ?? 20,\n maxMessages: options.maxMessages ?? 10,\n expectedTopicArn: options.expectedTopicArn ?? '',\n };\n }\n\n async subscribe(\n messages: TMessage['type'][],\n listener: (payload: TMessage) => Promise<void>,\n ): Promise<void> {\n this.messageTypes = new Set(messages);\n this.listener = listener;\n\n if (!this.connection.isConnected()) {\n await this.connection.connect();\n }\n\n this.polling = true;\n this.poll();\n }\n\n stop(): void {\n this.polling = false;\n }\n\n private async poll(): Promise<void> {\n while (this.polling) {\n try {\n const command = new ReceiveMessageCommand({\n QueueUrl: this.connection.queueUrl,\n MaxNumberOfMessages: this.options.maxMessages,\n WaitTimeSeconds: this.options.waitTimeSeconds,\n MessageAttributeNames: ['All'],\n });\n\n const response = await this.connection.sqsClient.send(command);\n\n if (response.Messages && response.Messages.length > 0) {\n await this.processMessages(response.Messages);\n }\n } catch (error) {\n console.error('Error receiving messages from SQS:', error);\n await this.sleep(1000);\n }\n }\n }\n\n private async processMessages(messages: Message[]): Promise<void> {\n for (const message of messages) {\n try {\n if (!message.Body) continue;\n\n // Try to parse as SNS message first\n const parsedMessage = this.parseMessage(message.Body);\n\n if (!parsedMessage) {\n // Not a valid message format\n await this.deleteMessage(message.ReceiptHandle!);\n continue;\n }\n\n const { payload, messageType, topicArn } = parsedMessage;\n\n // If expectedTopicArn is set, verify the message is from that topic\n if (this.options.expectedTopicArn && topicArn) {\n if (topicArn !== this.options.expectedTopicArn) {\n console.warn(\n `Received message from unexpected topic: ${topicArn}, expected: ${this.options.expectedTopicArn}`,\n );\n await this.deleteMessage(message.ReceiptHandle!);\n continue;\n }\n }\n\n // Check if we're subscribed to this message type\n if (\n messageType &&\n this.messageTypes.has(messageType as TMessage['type'])\n ) {\n // Call listener\n if (this.listener) {\n await this.listener(payload as TMessage);\n }\n\n // Delete message after successful processing\n await this.deleteMessage(message.ReceiptHandle!);\n } else {\n // Delete messages we're not subscribed to\n await this.deleteMessage(message.ReceiptHandle!);\n }\n } catch (error) {\n console.error('Error processing message:', error);\n // Message will become visible again\n }\n }\n }\n\n /**\n * Parse message body - handles both direct SQS messages and SNS-wrapped messages\n */\n private parseMessage(body: string): {\n payload: unknown;\n messageType: string | undefined;\n topicArn?: string;\n } | null {\n try {\n const parsed = JSON.parse(body);\n\n // Check if this is an SNS notification wrapper\n if (parsed.Type === 'Notification' && parsed.Message) {\n // This is an SNS message\n const snsMessage = JSON.parse(parsed.Message);\n const messageType =\n parsed.MessageAttributes?.type?.Value || snsMessage.type;\n\n return {\n payload: snsMessage,\n messageType,\n topicArn: parsed.TopicArn,\n };\n }\n\n // This is a direct SQS message\n return {\n payload: parsed,\n messageType: parsed.type,\n };\n } catch (error) {\n console.error('Failed to parse message:', error);\n return null;\n }\n }\n\n private async deleteMessage(receiptHandle: string): Promise<void> {\n try {\n const command = new DeleteMessageCommand({\n QueueUrl: this.connection.queueUrl,\n ReceiptHandle: receiptHandle,\n });\n await this.connection.sqsClient.send(command);\n } catch (error) {\n console.error('Error deleting message:', error);\n }\n }\n\n private sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;AA2BA,IAAa,gBAAb,MAEA;CACE,AAAQ,UAAU;CAClB,AAAQ,+BAAsC,IAAI;CAClD,AAAQ;CACR,AAAQ;CAER,YACUA,YACRC,UAAgC,CAAE,GAClC;EAFQ;AAGR,OAAK,UAAU;GACb,iBAAiB,QAAQ,mBAAmB;GAC5C,aAAa,QAAQ,eAAe;GACpC,kBAAkB,QAAQ,oBAAoB;EAC/C;CACF;CAED,MAAM,UACJC,UACAC,UACe;AACf,OAAK,eAAe,IAAI,IAAI;AAC5B,OAAK,WAAW;AAEhB,OAAK,KAAK,WAAW,aAAa,CAChC,OAAM,KAAK,WAAW,SAAS;AAGjC,OAAK,UAAU;AACf,OAAK,MAAM;CACZ;CAED,OAAa;AACX,OAAK,UAAU;CAChB;CAED,MAAc,OAAsB;AAClC,SAAO,KAAK,QACV,KAAI;GACF,MAAM,UAAU,IAAIC,2CAAsB;IACxC,UAAU,KAAK,WAAW;IAC1B,qBAAqB,KAAK,QAAQ;IAClC,iBAAiB,KAAK,QAAQ;IAC9B,uBAAuB,CAAC,KAAM;GAC/B;GAED,MAAM,WAAW,MAAM,KAAK,WAAW,UAAU,KAAK,QAAQ;AAE9D,OAAI,SAAS,YAAY,SAAS,SAAS,SAAS,EAClD,OAAM,KAAK,gBAAgB,SAAS,SAAS;EAEhD,SAAQ,OAAO;AACd,WAAQ,MAAM,sCAAsC,MAAM;AAC1D,SAAM,KAAK,MAAM,IAAK;EACvB;CAEJ;CAED,MAAc,gBAAgBC,UAAoC;AAChE,OAAK,MAAM,WAAW,SACpB,KAAI;AACF,QAAK,QAAQ,KAAM;GAGnB,MAAM,gBAAgB,KAAK,aAAa,QAAQ,KAAK;AAErD,QAAK,eAAe;AAElB,UAAM,KAAK,cAAc,QAAQ,cAAe;AAChD;GACD;GAED,MAAM,EAAE,SAAS,aAAa,UAAU,GAAG;AAG3C,OAAI,KAAK,QAAQ,oBAAoB,UACnC;QAAI,aAAa,KAAK,QAAQ,kBAAkB;AAC9C,aAAQ,MACL,0CAA0C,SAAS,cAAc,KAAK,QAAQ,iBAAiB,EACjG;AACD,WAAM,KAAK,cAAc,QAAQ,cAAe;AAChD;IACD;;AAIH,OACE,eACA,KAAK,aAAa,IAAI,YAAgC,EACtD;AAEA,QAAI,KAAK,SACP,OAAM,KAAK,SAAS,QAAoB;AAI1C,UAAM,KAAK,cAAc,QAAQ,cAAe;GACjD,MAEC,OAAM,KAAK,cAAc,QAAQ,cAAe;EAEnD,SAAQ,OAAO;AACd,WAAQ,MAAM,6BAA6B,MAAM;EAElD;CAEJ;;;;CAKD,AAAQ,aAAaC,MAIZ;AACP,MAAI;GACF,MAAM,SAAS,KAAK,MAAM,KAAK;AAG/B,OAAI,OAAO,SAAS,kBAAkB,OAAO,SAAS;IAEpD,MAAM,aAAa,KAAK,MAAM,OAAO,QAAQ;IAC7C,MAAM,cACJ,OAAO,mBAAmB,MAAM,SAAS,WAAW;AAEtD,WAAO;KACL,SAAS;KACT;KACA,UAAU,OAAO;IAClB;GACF;AAGD,UAAO;IACL,SAAS;IACT,aAAa,OAAO;GACrB;EACF,SAAQ,OAAO;AACd,WAAQ,MAAM,4BAA4B,MAAM;AAChD,UAAO;EACR;CACF;CAED,MAAc,cAAcC,eAAsC;AAChE,MAAI;GACF,MAAM,UAAU,IAAIC,0CAAqB;IACvC,UAAU,KAAK,WAAW;IAC1B,eAAe;GAChB;AACD,SAAM,KAAK,WAAW,UAAU,KAAK,QAAQ;EAC9C,SAAQ,OAAO;AACd,WAAQ,MAAM,2BAA2B,MAAM;EAChD;CACF;CAED,AAAQ,MAAMC,IAA2B;AACvC,SAAO,IAAI,QAAQ,CAAC,YAAY,WAAW,SAAS,GAAG;CACxD;AACF"}
|
package/dist/basic/index.cjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
require('../types-CvSMEO5z.cjs');
|
|
2
|
-
const require_BasicConnection = require('../BasicConnection-
|
|
3
|
-
const require_BasicPublisher = require('../BasicPublisher-
|
|
4
|
-
const require_BasicSubscriber = require('../BasicSubscriber-
|
|
2
|
+
const require_BasicConnection = require('../BasicConnection-Bz3F2rXA.cjs');
|
|
3
|
+
const require_BasicPublisher = require('../BasicPublisher-afwuA4Xp.cjs');
|
|
4
|
+
const require_BasicSubscriber = require('../BasicSubscriber-Ckf3BkxS.cjs');
|
|
5
5
|
|
|
6
6
|
exports.BasicConnection = require_BasicConnection.BasicConnection;
|
|
7
7
|
exports.BasicPublisher = require_BasicPublisher.BasicPublisher;
|
package/dist/basic/index.mjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import "../types-B63qHVOG.mjs";
|
|
2
|
-
import { BasicConnection } from "../BasicConnection-
|
|
3
|
-
import { BasicPublisher } from "../BasicPublisher-
|
|
4
|
-
import { BasicSubscriber } from "../BasicSubscriber-
|
|
2
|
+
import { BasicConnection } from "../BasicConnection-DOSOFSsW.mjs";
|
|
3
|
+
import { BasicPublisher } from "../BasicPublisher-CaX8GCUi.mjs";
|
|
4
|
+
import { BasicSubscriber } from "../BasicSubscriber-DPpJNUUY.mjs";
|
|
5
5
|
|
|
6
6
|
export { BasicConnection, BasicPublisher, BasicSubscriber };
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
require('../types-CvSMEO5z.cjs');
|
|
2
|
-
require('../SQSConnection-
|
|
3
|
-
require('../SQSSubscriber-
|
|
4
|
-
const require_SNSSubscriber = require('../SNSSubscriber-
|
|
2
|
+
require('../SQSConnection-DWYDtftQ.cjs');
|
|
3
|
+
require('../SQSSubscriber-DcL9BbG1.cjs');
|
|
4
|
+
const require_SNSSubscriber = require('../SNSSubscriber-DpEwQ8Tb.cjs');
|
|
5
5
|
|
|
6
6
|
exports.SNSSubscriber = require_SNSSubscriber.SNSSubscriber;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import "../types-B63qHVOG.mjs";
|
|
2
|
-
import "../SQSConnection-
|
|
3
|
-
import "../SQSSubscriber-
|
|
4
|
-
import { SNSSubscriber } from "../SNSSubscriber-
|
|
2
|
+
import "../SQSConnection-BBlDWZLU.mjs";
|
|
3
|
+
import "../SQSSubscriber-84pFnW1z.mjs";
|
|
4
|
+
import { SNSSubscriber } from "../SNSSubscriber-e7eG48yJ.mjs";
|
|
5
5
|
|
|
6
6
|
export { SNSSubscriber };
|
package/dist/sns/index.cjs
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
require('../types-CvSMEO5z.cjs');
|
|
2
|
-
const require_SNSConnection = require('../SNSConnection-
|
|
3
|
-
const require_SNSPublisher = require('../SNSPublisher-
|
|
4
|
-
require('../SQSConnection-
|
|
5
|
-
require('../SQSSubscriber-
|
|
6
|
-
const require_SNSSubscriber = require('../SNSSubscriber-
|
|
2
|
+
const require_SNSConnection = require('../SNSConnection-CZz_U56V.cjs');
|
|
3
|
+
const require_SNSPublisher = require('../SNSPublisher-DbWVH-g_.cjs');
|
|
4
|
+
require('../SQSConnection-DWYDtftQ.cjs');
|
|
5
|
+
require('../SQSSubscriber-DcL9BbG1.cjs');
|
|
6
|
+
const require_SNSSubscriber = require('../SNSSubscriber-DpEwQ8Tb.cjs');
|
|
7
7
|
|
|
8
8
|
exports.SNSConnection = require_SNSConnection.SNSConnection;
|
|
9
9
|
exports.SNSPublisher = require_SNSPublisher.SNSPublisher;
|
package/dist/sns/index.mjs
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import "../types-B63qHVOG.mjs";
|
|
2
|
-
import { SNSConnection } from "../SNSConnection-
|
|
3
|
-
import { SNSPublisher } from "../SNSPublisher-
|
|
4
|
-
import "../SQSConnection-
|
|
5
|
-
import "../SQSSubscriber-
|
|
6
|
-
import { SNSSubscriber } from "../SNSSubscriber-
|
|
2
|
+
import { SNSConnection } from "../SNSConnection-DVEE3XoC.mjs";
|
|
3
|
+
import { SNSPublisher } from "../SNSPublisher-Do-FMUyT.mjs";
|
|
4
|
+
import "../SQSConnection-BBlDWZLU.mjs";
|
|
5
|
+
import "../SQSSubscriber-84pFnW1z.mjs";
|
|
6
|
+
import { SNSSubscriber } from "../SNSSubscriber-e7eG48yJ.mjs";
|
|
7
7
|
|
|
8
8
|
export { SNSConnection, SNSPublisher, SNSSubscriber };
|
package/dist/sqs/index.cjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
require('../types-CvSMEO5z.cjs');
|
|
2
|
-
const require_SQSConnection = require('../SQSConnection-
|
|
3
|
-
const require_SQSSubscriber = require('../SQSSubscriber-
|
|
4
|
-
const require_SQSPublisher = require('../SQSPublisher-
|
|
2
|
+
const require_SQSConnection = require('../SQSConnection-DWYDtftQ.cjs');
|
|
3
|
+
const require_SQSSubscriber = require('../SQSSubscriber-DcL9BbG1.cjs');
|
|
4
|
+
const require_SQSPublisher = require('../SQSPublisher-nzbSaXSH.cjs');
|
|
5
5
|
|
|
6
6
|
exports.SQSConnection = require_SQSConnection.SQSConnection;
|
|
7
7
|
exports.SQSPublisher = require_SQSPublisher.SQSPublisher;
|
package/dist/sqs/index.mjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import "../types-B63qHVOG.mjs";
|
|
2
|
-
import { SQSConnection } from "../SQSConnection-
|
|
3
|
-
import { SQSSubscriber } from "../SQSSubscriber-
|
|
4
|
-
import { SQSPublisher } from "../SQSPublisher-
|
|
2
|
+
import { SQSConnection } from "../SQSConnection-BBlDWZLU.mjs";
|
|
3
|
+
import { SQSSubscriber } from "../SQSSubscriber-84pFnW1z.mjs";
|
|
4
|
+
import { SQSPublisher } from "../SQSPublisher-B3lS2zbu.mjs";
|
|
5
5
|
|
|
6
6
|
export { SQSConnection, SQSPublisher, SQSSubscriber };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@geekmidas/events",
|
|
3
|
-
"version": "0.0
|
|
3
|
+
"version": "0.1.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"exports": {
|
|
6
6
|
".": {
|
|
@@ -29,21 +29,40 @@
|
|
|
29
29
|
"require": "./dist/sns/index.cjs"
|
|
30
30
|
}
|
|
31
31
|
},
|
|
32
|
+
"repository": {
|
|
33
|
+
"type": "git",
|
|
34
|
+
"url": "https://github.com/geekmidas/toolbox"
|
|
35
|
+
},
|
|
32
36
|
"publishConfig": {
|
|
33
37
|
"registry": "https://registry.npmjs.org/",
|
|
34
38
|
"access": "public"
|
|
35
39
|
},
|
|
36
40
|
"dependencies": {
|
|
37
|
-
"lodash.chunk": "~4.2.0"
|
|
38
|
-
"@geekmidas/schema": "0.0.1"
|
|
41
|
+
"lodash.chunk": "~4.2.0"
|
|
39
42
|
},
|
|
40
43
|
"peerDependencies": {
|
|
41
44
|
"amqplib": "^0.10.3",
|
|
42
45
|
"@aws-sdk/client-sqs": "~3.908.0",
|
|
43
|
-
"@aws-sdk/client-sns": "~3.908.0"
|
|
46
|
+
"@aws-sdk/client-sns": "~3.908.0",
|
|
47
|
+
"@geekmidas/schema": "^0.1.0"
|
|
48
|
+
},
|
|
49
|
+
"peerDependenciesMeta": {
|
|
50
|
+
"@geekmidas/schema": {
|
|
51
|
+
"optional": true
|
|
52
|
+
},
|
|
53
|
+
"amqplib": {
|
|
54
|
+
"optional": true
|
|
55
|
+
},
|
|
56
|
+
"@aws-sdk/client-sqs": {
|
|
57
|
+
"optional": true
|
|
58
|
+
},
|
|
59
|
+
"@aws-sdk/client-sns": {
|
|
60
|
+
"optional": true
|
|
61
|
+
}
|
|
44
62
|
},
|
|
45
63
|
"devDependencies": {
|
|
46
64
|
"@types/amqplib": "~0.10.7",
|
|
47
|
-
"@types/lodash.chunk": "~4.2.0"
|
|
65
|
+
"@types/lodash.chunk": "~4.2.0",
|
|
66
|
+
"@geekmidas/schema": "^0.1.0"
|
|
48
67
|
}
|
|
49
68
|
}
|