@qrvey/sms 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (85) hide show
  1. package/README.md +74 -0
  2. package/__tests__/__mocks__/SMSSender.mock.ts +10 -0
  3. package/coverageReader.sh +50 -0
  4. package/dist/cjs/errors/index.js +19 -0
  5. package/dist/cjs/errors/index.js.map +1 -0
  6. package/dist/cjs/errors/notProvidedCredentials.error.js +11 -0
  7. package/dist/cjs/errors/notProvidedCredentials.error.js.map +1 -0
  8. package/dist/cjs/errors/unknownProvider.error.js +11 -0
  9. package/dist/cjs/errors/unknownProvider.error.js.map +1 -0
  10. package/dist/cjs/index.js +6 -0
  11. package/dist/cjs/index.js.map +1 -0
  12. package/dist/cjs/interfaces/index.js +18 -0
  13. package/dist/cjs/interfaces/index.js.map +1 -0
  14. package/dist/cjs/interfaces/smsSender.interface.js +3 -0
  15. package/dist/cjs/interfaces/smsSender.interface.js.map +1 -0
  16. package/dist/cjs/services/clients/awsSmsSender.client.js +37 -0
  17. package/dist/cjs/services/clients/awsSmsSender.client.js.map +1 -0
  18. package/dist/cjs/services/clients/azureSmsSender.client.js +36 -0
  19. package/dist/cjs/services/clients/azureSmsSender.client.js.map +1 -0
  20. package/dist/cjs/services/clients/twilioSmsSender.client.js +41 -0
  21. package/dist/cjs/services/clients/twilioSmsSender.client.js.map +1 -0
  22. package/dist/cjs/services/clients/vonageSmsSender.client.js +34 -0
  23. package/dist/cjs/services/clients/vonageSmsSender.client.js.map +1 -0
  24. package/dist/cjs/services/index.js +18 -0
  25. package/dist/cjs/services/index.js.map +1 -0
  26. package/dist/cjs/services/smsSender.service.js +17 -0
  27. package/dist/cjs/services/smsSender.service.js.map +1 -0
  28. package/dist/cjs/services/smsSenderFactory.service.js +45 -0
  29. package/dist/cjs/services/smsSenderFactory.service.js.map +1 -0
  30. package/dist/cjs/types/index.js +19 -0
  31. package/dist/cjs/types/index.js.map +1 -0
  32. package/dist/cjs/types/sendResult.type.js +3 -0
  33. package/dist/cjs/types/sendResult.type.js.map +1 -0
  34. package/dist/cjs/types/smsProviderModule.type.js +3 -0
  35. package/dist/cjs/types/smsProviderModule.type.js.map +1 -0
  36. package/dist/cjs/utils/constants.js +9 -0
  37. package/dist/cjs/utils/constants.js.map +1 -0
  38. package/dist/esm/awsSmsSender.client-DGZ2JYIG.mjs +41 -0
  39. package/dist/esm/awsSmsSender.client-DGZ2JYIG.mjs.map +1 -0
  40. package/dist/esm/azureSmsSender.client-K6MPTIU7.mjs +36 -0
  41. package/dist/esm/azureSmsSender.client-K6MPTIU7.mjs.map +1 -0
  42. package/dist/esm/chunk-NCQDKWNI.mjs +26 -0
  43. package/dist/esm/chunk-NCQDKWNI.mjs.map +1 -0
  44. package/dist/esm/index.d.mts +20 -0
  45. package/dist/esm/index.mjs +36 -0
  46. package/dist/esm/index.mjs.map +1 -0
  47. package/dist/esm/twilioSmsSender.client-2Z4R25US.mjs +42 -0
  48. package/dist/esm/twilioSmsSender.client-2Z4R25US.mjs.map +1 -0
  49. package/dist/esm/vonageSmsSender.client-23U65FBQ.mjs +38 -0
  50. package/dist/esm/vonageSmsSender.client-23U65FBQ.mjs.map +1 -0
  51. package/dist/types/index.d.ts +20 -0
  52. package/jest.config.ts +15 -0
  53. package/package.json +80 -0
  54. package/src/errors/index.test.ts +13 -0
  55. package/src/errors/index.ts +2 -0
  56. package/src/errors/notProvidedCredentials.error.ts +6 -0
  57. package/src/errors/unknownProvider.error.ts +6 -0
  58. package/src/index.test.ts +7 -0
  59. package/src/index.ts +2 -0
  60. package/src/interfaces/index.ts +1 -0
  61. package/src/interfaces/smsSender.interface.ts +5 -0
  62. package/src/services/clients/awsSmsSender.client.test.ts +39 -0
  63. package/src/services/clients/awsSmsSender.client.ts +57 -0
  64. package/src/services/clients/azureSmsSender.client.test.ts +46 -0
  65. package/src/services/clients/azureSmsSender.client.ts +38 -0
  66. package/src/services/clients/index.test.ts +50 -0
  67. package/src/services/clients/index.ts +4 -0
  68. package/src/services/clients/twilioSmsSender.client.test.ts +48 -0
  69. package/src/services/clients/twilioSmsSender.client.ts +52 -0
  70. package/src/services/clients/voangeSmsSender.client.test.ts +48 -0
  71. package/src/services/clients/vonageSmsSender.client.ts +49 -0
  72. package/src/services/index.test.ts +12 -0
  73. package/src/services/index.ts +1 -0
  74. package/src/services/smsSender.service.ts +16 -0
  75. package/src/services/smsSenderFactory.service.test.ts +16 -0
  76. package/src/services/smsSenderFactory.service.ts +24 -0
  77. package/src/types/index.ts +2 -0
  78. package/src/types/sendResult.type.ts +9 -0
  79. package/src/types/smsProviderModule.type.ts +5 -0
  80. package/src/utils/constants.ts +6 -0
  81. package/tsconfig.cjs.json +7 -0
  82. package/tsconfig.json +17 -0
  83. package/tsup.config.cjs.ts +9 -0
  84. package/tsup.config.esm.ts +10 -0
  85. package/tsup.config.types.ts +9 -0
@@ -0,0 +1,36 @@
1
+ import { USERNAME, PASSWORD, FROM, NotProvidedCredentialsError } from './chunk-NCQDKWNI.mjs';
2
+ import { SmsClient } from '@azure/communication-sms';
3
+
4
+ var AzureSMSSenderClient = class {
5
+ constructor() {
6
+ if (!(USERNAME && PASSWORD && FROM))
7
+ throw new NotProvidedCredentialsError();
8
+ const connectionString = `endpoint=https://${USERNAME}.communication.azure.com/;accesskey=${PASSWORD}`;
9
+ this.client = new SmsClient(connectionString);
10
+ }
11
+ async send(phones, message) {
12
+ const result = {
13
+ sent: [],
14
+ failed: []
15
+ };
16
+ const smsResults = await this.client.send({
17
+ from: FROM,
18
+ to: phones,
19
+ message
20
+ });
21
+ smsResults.forEach((smsResult) => {
22
+ if (smsResult.errorMessage)
23
+ result.failed.push({
24
+ phone: smsResult.to,
25
+ error: smsResult.errorMessage
26
+ });
27
+ else
28
+ result.sent.push({ phone: smsResult.to });
29
+ });
30
+ return result;
31
+ }
32
+ };
33
+
34
+ export { AzureSMSSenderClient as default };
35
+ //# sourceMappingURL=out.js.map
36
+ //# sourceMappingURL=azureSmsSender.client-K6MPTIU7.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/services/clients/azureSmsSender.client.ts"],"names":[],"mappings":";;;;;;;;AAIA,SAAS,iBAAiB;AAE1B,IAAqB,uBAArB,MAAgE;AAAA,EAE5D,cAAc;AACV,QAAI,EAAE,YAAY,YAAY;AAC1B,YAAM,IAAI,4BAA4B;AAC1C,UAAM,mBAAmB,oBAAoB,QAAQ,uCAAuC,QAAQ;AACpG,SAAK,SAAS,IAAI,UAAU,gBAAgB;AAAA,EAChD;AAAA,EAEA,MAAa,KAAK,QAAkB,SAAsC;AACtE,UAAM,SAAqB;AAAA,MACvB,MAAM,CAAC;AAAA,MACP,QAAQ,CAAC;AAAA,IACb;AACA,UAAM,aAAa,MAAM,KAAK,OAAO,KAAK;AAAA,MACtC,MAAM;AAAA,MACN,IAAI;AAAA,MACJ;AAAA,IACJ,CAAC;AAED,eAAW,QAAQ,CAAC,cAAc;AAC9B,UAAI,UAAU;AACV,eAAO,OAAO,KAAK;AAAA,UACf,OAAO,UAAU;AAAA,UACjB,OAAO,UAAU;AAAA,QACrB,CAAC;AAAA;AACA,eAAO,KAAK,KAAK,EAAE,OAAO,UAAU,GAAG,CAAC;AAAA,IACjD,CAAC;AAED,WAAO;AAAA,EACX;AACJ","sourcesContent":["import { NotProvidedCredentialsError } from '../../errors';\nimport { ISMSSender } from '../../interfaces';\nimport { SendResult } from '../../types';\nimport { USERNAME, PASSWORD, FROM } from '../../utils/constants';\nimport { SmsClient } from '@azure/communication-sms';\n\nexport default class AzureSMSSenderClient implements ISMSSender {\n client: SmsClient;\n constructor() {\n if (!(USERNAME && PASSWORD && FROM))\n throw new NotProvidedCredentialsError();\n const connectionString = `endpoint=https://${USERNAME}.communication.azure.com/;accesskey=${PASSWORD}`;\n this.client = new SmsClient(connectionString);\n }\n\n public async send(phones: string[], message: string): Promise<SendResult> {\n const result: SendResult = {\n sent: [],\n failed: [],\n };\n const smsResults = await this.client.send({\n from: FROM as string,\n to: phones,\n message: message,\n });\n\n smsResults.forEach((smsResult) => {\n if (smsResult.errorMessage)\n result.failed.push({\n phone: smsResult.to,\n error: smsResult.errorMessage,\n });\n else result.sent.push({ phone: smsResult.to });\n });\n\n return result;\n }\n}\n"]}
@@ -0,0 +1,26 @@
1
+ // src/errors/notProvidedCredentials.error.ts
2
+ var NotProvidedCredentialsError = class extends Error {
3
+ constructor() {
4
+ super("SMS sender credentials not provided.");
5
+ this.name = "NotProvidedCredentialsError";
6
+ }
7
+ };
8
+
9
+ // src/errors/unknownProvider.error.ts
10
+ var UnknownProviderError = class extends Error {
11
+ constructor() {
12
+ super("Unknown provider. Set PROVIDER env variable");
13
+ this.name = "UnknownProviderError";
14
+ }
15
+ };
16
+
17
+ // src/utils/constants.ts
18
+ var SMS_PROVIDER = process.env.SMS_PROVIDER;
19
+ var USERNAME = process.env.SMS_PROVIDER_USERNAME;
20
+ var PASSWORD = process.env.SMS_PROVIDER_PASSWORD;
21
+ var FROM = process.env.SMS_PROVIDER_FROM_NUMBER;
22
+ var REGION = process.env.SMS_PROVIDER_REGION;
23
+
24
+ export { FROM, NotProvidedCredentialsError, PASSWORD, REGION, SMS_PROVIDER, USERNAME, UnknownProviderError };
25
+ //# sourceMappingURL=out.js.map
26
+ //# sourceMappingURL=chunk-NCQDKWNI.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/errors/notProvidedCredentials.error.ts","../../src/errors/unknownProvider.error.ts","../../src/utils/constants.ts"],"names":[],"mappings":";AAAO,IAAM,8BAAN,cAA0C,MAAM;AAAA,EACnD,cAAc;AACV,UAAM,sCAAsC;AAC5C,SAAK,OAAO;AAAA,EAChB;AACJ;;;ACLO,IAAM,uBAAN,cAAmC,MAAM;AAAA,EAC5C,cAAc;AACV,UAAM,6CAA6C;AACnD,SAAK,OAAO;AAAA,EAChB;AACJ;;;ACLO,IAAM,eAAe,QAAQ,IAAI;AAEjC,IAAM,WAAW,QAAQ,IAAI;AAC7B,IAAM,WAAW,QAAQ,IAAI;AAC7B,IAAM,OAAO,QAAQ,IAAI;AACzB,IAAM,SAAS,QAAQ,IAAI","sourcesContent":["export class NotProvidedCredentialsError extends Error {\n constructor() {\n super('SMS sender credentials not provided.');\n this.name = 'NotProvidedCredentialsError';\n }\n}\n","export class UnknownProviderError extends Error {\n constructor() {\n super('Unknown provider. Set PROVIDER env variable');\n this.name = 'UnknownProviderError';\n }\n}\n","export const SMS_PROVIDER = process.env.SMS_PROVIDER;\n\nexport const USERNAME = process.env.SMS_PROVIDER_USERNAME;\nexport const PASSWORD = process.env.SMS_PROVIDER_PASSWORD;\nexport const FROM = process.env.SMS_PROVIDER_FROM_NUMBER;\nexport const REGION = process.env.SMS_PROVIDER_REGION;\n"]}
@@ -0,0 +1,20 @@
1
+ type SendResult = {
2
+ sent: SendMessageResult[];
3
+ failed: SendMessageResult[];
4
+ };
5
+ type SendMessageResult = {
6
+ phone: string;
7
+ error?: string;
8
+ };
9
+
10
+ declare class SMSSenderService {
11
+ /**
12
+ * Send a message to one or more phones.
13
+ * @param phones - phone numbers to send to
14
+ * @param message - message to send
15
+ * @returns {Promise<SendResult>} Send result.
16
+ */
17
+ send: (phones: string[], message: string) => Promise<SendResult>;
18
+ }
19
+
20
+ export { SMSSenderService, SendResult };
@@ -0,0 +1,36 @@
1
+ import { SMS_PROVIDER, UnknownProviderError } from './chunk-NCQDKWNI.mjs';
2
+
3
+ // src/services/smsSenderFactory.service.ts
4
+ var SMSSenderFactory = class {
5
+ static async getSMSSender() {
6
+ var _a;
7
+ const provider = (_a = SMS_PROVIDER) == null ? void 0 : _a.toLowerCase();
8
+ if (!provider)
9
+ throw new UnknownProviderError();
10
+ const { default: SMSSender } = await this.providerModules[provider]();
11
+ return new SMSSender();
12
+ }
13
+ };
14
+ SMSSenderFactory.providerModules = {
15
+ twilio: () => import('./twilioSmsSender.client-2Z4R25US.mjs'),
16
+ aws: () => import('./awsSmsSender.client-DGZ2JYIG.mjs'),
17
+ azure: () => import('./azureSmsSender.client-K6MPTIU7.mjs'),
18
+ vonage: () => import('./vonageSmsSender.client-23U65FBQ.mjs')
19
+ };
20
+
21
+ // src/services/smsSender.service.ts
22
+ var SMSSenderService = class {
23
+ constructor() {
24
+ /**
25
+ * Send a message to one or more phones.
26
+ * @param phones - phone numbers to send to
27
+ * @param message - message to send
28
+ * @returns {Promise<SendResult>} Send result.
29
+ */
30
+ this.send = async (phones, message) => (await SMSSenderFactory.getSMSSender()).send(phones, message);
31
+ }
32
+ };
33
+
34
+ export { SMSSenderService };
35
+ //# sourceMappingURL=out.js.map
36
+ //# sourceMappingURL=index.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/services/smsSenderFactory.service.ts","../../src/services/smsSender.service.ts"],"names":[],"mappings":";;;;;;AAKO,IAAM,mBAAN,MAAuB;AAAA,EAW1B,aAAa,eAAoC;AAhBrD;AAiBQ,UAAM,YAAW,yCAAc;AAC/B,QAAI,CAAC;AAAU,YAAM,IAAI,qBAAqB;AAE9C,UAAM,EAAE,SAAS,UAAU,IAAI,MAAM,KAAK,gBAAgB,QAAQ,EAAE;AACpE,WAAO,IAAI,UAAU;AAAA,EACzB;AACJ;AAlBa,iBACM,kBAGX;AAAA,EACA,QAAQ,MAAM,OAAO,uCAAkC;AAAA,EACvD,KAAK,MAAM,OAAO,oCAA+B;AAAA,EACjD,OAAO,MAAM,OAAO,sCAAiC;AAAA,EACrD,QAAQ,MAAM,OAAO,uCAAkC;AAC3D;;;ACXG,IAAM,mBAAN,MAAuB;AAAA,EAAvB;AAOH;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,SAAO,OAAO,OACV,QACA,aAEC,MAAM,iBAAiB,aAAa,GAAG,KAAK,QAAQ,OAAO;AAAA;AACpE","sourcesContent":["import { UnknownProviderError } from '../errors';\nimport { ISMSSender } from '../interfaces';\nimport { SMSProviderModule } from '../types';\nimport { SMS_PROVIDER } from '../utils/constants';\n\nexport class SMSSenderFactory {\n private static providerModules: Record<\n string,\n () => Promise<SMSProviderModule>\n > = {\n twilio: () => import('./clients/twilioSmsSender.client'),\n aws: () => import('./clients/awsSmsSender.client'),\n azure: () => import('./clients/azureSmsSender.client'),\n vonage: () => import('./clients/vonageSmsSender.client'),\n };\n\n static async getSMSSender(): Promise<ISMSSender> {\n const provider = SMS_PROVIDER?.toLowerCase();\n if (!provider) throw new UnknownProviderError();\n\n const { default: SMSSender } = await this.providerModules[provider]();\n return new SMSSender();\n }\n}\n","import { SendResult } from '../types';\nimport { SMSSenderFactory } from './smsSenderFactory.service';\n\nexport class SMSSenderService {\n /**\n * Send a message to one or more phones.\n * @param phones - phone numbers to send to\n * @param message - message to send\n * @returns {Promise<SendResult>} Send result.\n */\n public send = async (\n phones: string[],\n message: string,\n ): Promise<SendResult> =>\n (await SMSSenderFactory.getSMSSender()).send(phones, message);\n}\n"]}
@@ -0,0 +1,42 @@
1
+ import { FROM, USERNAME, PASSWORD, NotProvidedCredentialsError } from './chunk-NCQDKWNI.mjs';
2
+ import { Twilio } from 'twilio';
3
+
4
+ var TwilioSMSSenderClient = class {
5
+ constructor() {
6
+ this._send = (phone, message) => this.client.messages.create({
7
+ from: FROM,
8
+ to: phone,
9
+ body: message
10
+ });
11
+ if (USERNAME && PASSWORD)
12
+ this.client = new Twilio(USERNAME, PASSWORD);
13
+ else
14
+ throw new NotProvidedCredentialsError();
15
+ }
16
+ async send(phones = [], message) {
17
+ const sentMessages = await Promise.all(
18
+ phones.map((phone) => this._send(phone, message))
19
+ );
20
+ const result = {
21
+ sent: [],
22
+ failed: []
23
+ };
24
+ sentMessages.forEach((message2) => {
25
+ if (message2.errorMessage) {
26
+ result.failed.push({
27
+ phone: message2.to,
28
+ error: message2.errorMessage
29
+ });
30
+ } else {
31
+ result.sent.push({
32
+ phone: message2.to
33
+ });
34
+ }
35
+ });
36
+ return result;
37
+ }
38
+ };
39
+
40
+ export { TwilioSMSSenderClient as default };
41
+ //# sourceMappingURL=out.js.map
42
+ //# sourceMappingURL=twilioSmsSender.client-2Z4R25US.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/services/clients/twilioSmsSender.client.ts"],"names":["message"],"mappings":";;;;;;;;AACA,SAAS,cAAc;AAMvB,IAAqB,wBAArB,MAAiE;AAAA,EAE7D,cAAc;AAiCd,SAAQ,QAAQ,CACZ,OACA,YAEA,KAAK,OAAO,SAAS,OAAO;AAAA,MACxB,MAAM;AAAA,MACN,IAAI;AAAA,MACJ,MAAM;AAAA,IACV,CAAC;AAxCD,QAAI,YAAY;AAAU,WAAK,SAAS,IAAI,OAAO,UAAU,QAAQ;AAAA;AAChE,YAAM,IAAI,4BAA4B;AAAA,EAC/C;AAAA,EAEA,MAAa,KACT,SAAmB,CAAC,GACpB,SACmB;AACnB,UAAM,eAAe,MAAM,QAAQ;AAAA,MAC/B,OAAO,IAAI,CAAC,UAAU,KAAK,MAAM,OAAO,OAAO,CAAC;AAAA,IACpD;AAEA,UAAM,SAAqB;AAAA,MACvB,MAAM,CAAC;AAAA,MACP,QAAQ,CAAC;AAAA,IACb;AACA,iBAAa,QAAQ,CAACA,aAAY;AAC9B,UAAIA,SAAQ,cAAc;AACtB,eAAO,OAAO,KAAK;AAAA,UACf,OAAOA,SAAQ;AAAA,UACf,OAAOA,SAAQ;AAAA,QACnB,CAAC;AAAA,MACL,OAAO;AACH,eAAO,KAAK,KAAK;AAAA,UACb,OAAOA,SAAQ;AAAA,QACnB,CAAC;AAAA,MACL;AAAA,IACJ,CAAC;AAED,WAAO;AAAA,EACX;AAWJ","sourcesContent":["import { ISMSSender } from '../../interfaces/smsSender.interface';\nimport { Twilio } from 'twilio';\nimport { USERNAME, PASSWORD, FROM } from '../../utils/constants';\nimport { MessageInstance } from 'twilio/lib/rest/api/v2010/account/message';\nimport { SendResult } from '../../types/sendResult.type';\nimport { NotProvidedCredentialsError } from '../../errors';\n\nexport default class TwilioSMSSenderClient implements ISMSSender {\n private client: Twilio;\n constructor() {\n if (USERNAME && PASSWORD) this.client = new Twilio(USERNAME, PASSWORD);\n else throw new NotProvidedCredentialsError();\n }\n\n public async send(\n phones: string[] = [],\n message: string,\n ): Promise<SendResult> {\n const sentMessages = await Promise.all(\n phones.map((phone) => this._send(phone, message)),\n );\n\n const result: SendResult = {\n sent: [],\n failed: [],\n };\n sentMessages.forEach((message) => {\n if (message.errorMessage) {\n result.failed.push({\n phone: message.to,\n error: message.errorMessage,\n });\n } else {\n result.sent.push({\n phone: message.to,\n });\n }\n });\n\n return result;\n }\n\n private _send = (\n phone: string,\n message: string,\n ): Promise<MessageInstance> =>\n this.client.messages.create({\n from: FROM,\n to: phone,\n body: message,\n });\n}\n"]}
@@ -0,0 +1,38 @@
1
+ import { FROM, USERNAME, PASSWORD, NotProvidedCredentialsError } from './chunk-NCQDKWNI.mjs';
2
+ import { Vonage } from '@vonage/server-sdk';
3
+ import { Auth } from '@vonage/auth';
4
+
5
+ var VonageSMSSenderClient = class {
6
+ constructor() {
7
+ this.send = async (phones, message) => {
8
+ const result = {
9
+ sent: [],
10
+ failed: []
11
+ };
12
+ await Promise.all(
13
+ phones.map(
14
+ (phone) => this._send(phone, message).then(() => result.sent.push({ phone })).catch(
15
+ (error) => result.failed.push({ phone, error: error.message })
16
+ )
17
+ )
18
+ );
19
+ return result;
20
+ };
21
+ this._send = (phone, message) => this.client.sms.send({
22
+ to: phone,
23
+ text: message,
24
+ from: FROM
25
+ });
26
+ if (!(USERNAME && PASSWORD && FROM))
27
+ throw new NotProvidedCredentialsError();
28
+ const vonageAuth = new Auth({
29
+ apiKey: USERNAME,
30
+ apiSecret: PASSWORD
31
+ });
32
+ this.client = new Vonage(vonageAuth);
33
+ }
34
+ };
35
+
36
+ export { VonageSMSSenderClient as default };
37
+ //# sourceMappingURL=out.js.map
38
+ //# sourceMappingURL=vonageSmsSender.client-23U65FBQ.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/services/clients/vonageSmsSender.client.ts"],"names":[],"mappings":";;;;;;;;AAEA,SAAS,cAAc;AACvB,SAAS,YAAY;AAKrB,IAAqB,wBAArB,MAAiE;AAAA,EAE7D,cAAc;AAUd,SAAO,OAAO,OACV,QACA,YACsB;AACtB,YAAM,SAAqB;AAAA,QACvB,MAAM,CAAC;AAAA,QACP,QAAQ,CAAC;AAAA,MACb;AAEA,YAAM,QAAQ;AAAA,QACV,OAAO;AAAA,UAAI,CAAC,UACR,KAAK,MAAM,OAAO,OAAO,EACpB,KAAK,MAAM,OAAO,KAAK,KAAK,EAAE,MAAM,CAAC,CAAC,EACtC;AAAA,YAAM,CAAC,UACJ,OAAO,OAAO,KAAK,EAAE,OAAO,OAAO,MAAM,QAAQ,CAAC;AAAA,UACtD;AAAA,QACR;AAAA,MACJ;AAEA,aAAO;AAAA,IACX;AAEA,SAAQ,QAAQ,CAAC,OAAe,YAC5B,KAAK,OAAO,IAAI,KAAK;AAAA,MACjB,IAAI;AAAA,MACJ,MAAM;AAAA,MACN,MAAM;AAAA,IACV,CAAC;AApCD,QAAI,EAAE,YAAY,YAAY;AAC1B,YAAM,IAAI,4BAA4B;AAC1C,UAAM,aAAa,IAAI,KAAK;AAAA,MACxB,QAAQ;AAAA,MACR,WAAW;AAAA,IACf,CAAC;AACD,SAAK,SAAS,IAAI,OAAO,UAAU;AAAA,EACvC;AA8BJ","sourcesContent":["import { ISMSSender } from '../../interfaces';\nimport { SendResult } from '../../types';\nimport { Vonage } from '@vonage/server-sdk';\nimport { Auth } from '@vonage/auth';\nimport { USERNAME, PASSWORD, FROM } from '../../utils/constants';\nimport { NotProvidedCredentialsError } from '../../errors';\nimport { SMSMessages } from '@vonage/sms/dist/lib/types/SMSMessages';\n\nexport default class VonageSMSSenderClient implements ISMSSender {\n private client: Vonage;\n constructor() {\n if (!(USERNAME && PASSWORD && FROM))\n throw new NotProvidedCredentialsError();\n const vonageAuth = new Auth({\n apiKey: USERNAME,\n apiSecret: PASSWORD,\n });\n this.client = new Vonage(vonageAuth);\n }\n\n public send = async (\n phones: string[],\n message: string,\n ): Promise<SendResult> => {\n const result: SendResult = {\n sent: [],\n failed: [],\n };\n\n await Promise.all(\n phones.map((phone) =>\n this._send(phone, message)\n .then(() => result.sent.push({ phone }))\n .catch((error) =>\n result.failed.push({ phone, error: error.message }),\n ),\n ),\n );\n\n return result;\n };\n\n private _send = (phone: string, message: string): Promise<SMSMessages> =>\n this.client.sms.send({\n to: phone,\n text: message,\n from: FROM as string,\n });\n}\n"]}
@@ -0,0 +1,20 @@
1
+ type SendResult = {
2
+ sent: SendMessageResult[];
3
+ failed: SendMessageResult[];
4
+ };
5
+ type SendMessageResult = {
6
+ phone: string;
7
+ error?: string;
8
+ };
9
+
10
+ declare class SMSSenderService {
11
+ /**
12
+ * Send a message to one or more phones.
13
+ * @param phones - phone numbers to send to
14
+ * @param message - message to send
15
+ * @returns {Promise<SendResult>} Send result.
16
+ */
17
+ send: (phones: string[], message: string) => Promise<SendResult>;
18
+ }
19
+
20
+ export { SMSSenderService, SendResult };
package/jest.config.ts ADDED
@@ -0,0 +1,15 @@
1
+ export default {
2
+ preset: 'ts-jest',
3
+ testEnvironment: 'node',
4
+ collectCoverageFrom: [
5
+ 'src/**/*.ts',
6
+ '!src/**/*.test.ts',
7
+ '!src/**/*.spec.ts',
8
+ '!src/types/**/*',
9
+ '!src/interfaces/**/*',
10
+ ],
11
+ testPathIgnorePatterns: [
12
+ '<rootDir>/node_modules/',
13
+ '<rootDir>/__tests__/__mocks__/',
14
+ ],
15
+ };
package/package.json ADDED
@@ -0,0 +1,80 @@
1
+ {
2
+ "name": "@qrvey/sms",
3
+ "version": "0.0.1",
4
+ "types": "dist/types/index.d.ts",
5
+ "main": "dist/cjs/index.js",
6
+ "exports": {
7
+ ".": {
8
+ "import": "./dist/esm/index.mjs",
9
+ "require": "./dist/cjs/index.js",
10
+ "default": "./dist/cjs/index.js"
11
+ }
12
+ },
13
+ "scripts": {
14
+ "test": "jest",
15
+ "test:cov": "jest --coverage && chmod +x ./coverageReader.sh && sh ./coverageReader.sh",
16
+ "build:clean": "rm -rf dist",
17
+ "build:compile:original": "tsc -b ./tsconfig.cjs.json ./tsconfig.esm.json ./tsconfig.types.json",
18
+ "build:compile:cjs:original": "tsup --config ./tsup.config.cjs.ts",
19
+ "build:compile:cjs": "tsc -b ./tsconfig.cjs.json",
20
+ "build:compile:esm": "tsup --config ./tsup.config.esm.ts",
21
+ "build:compile:types": "tsup --config ./tsup.config.types.ts",
22
+ "build:compile": "yarn build:compile:cjs && yarn build:compile:esm && yarn build:compile:types",
23
+ "build": "yarn test:cov && yarn build:clean && yarn build:compile",
24
+ "prepare-publish": "yarn install && yarn build",
25
+ "publish-package": "yarn prepare-publish && npm publish"
26
+ },
27
+ "keywords": [
28
+ "sms",
29
+ "client",
30
+ "qrvey"
31
+ ],
32
+ "author": "Qrvey",
33
+ "license": "MIT",
34
+ "description": "",
35
+ "devDependencies": {
36
+ "@aws-sdk/client-sns": "3.682.0",
37
+ "@azure/communication-sms": "1.2.0-beta.1",
38
+ "@types/jest": "29.5.5",
39
+ "@types/twilio": "3.19.3",
40
+ "@vonage/auth": "1.12.0",
41
+ "@vonage/server-sdk": "3.19.1",
42
+ "auto-version-js": "0.3.10",
43
+ "eslint-config-custom": "*",
44
+ "jest": "29.7.0",
45
+ "ts-jest": "29.1.1",
46
+ "tsc": "2.0.4",
47
+ "tsconfig": "*",
48
+ "tsup": "7.2.0",
49
+ "twilio": "5.3.5",
50
+ "typescript": "5.2.2"
51
+ },
52
+ "peerDependencies": {
53
+ "@aws-sdk/client-sns": "3.682.0",
54
+ "@azure/communication-sms": "1.2.0-beta.1",
55
+ "@vonage/auth": "1.12.0",
56
+ "@vonage/server-sdk": "3.19.1",
57
+ "twilio": "5.3.5"
58
+ },
59
+ "peerDependenciesMeta": {
60
+ "@aws-sdk/client-sns": {
61
+ "optional": true
62
+ },
63
+ "@azure/communication-sms": {
64
+ "optional": true
65
+ },
66
+ "@vonage/auth": {
67
+ "optional": true
68
+ },
69
+ "@vonage/server-sdk": {
70
+ "optional": true
71
+ },
72
+ "twilio": {
73
+ "optional": true
74
+ }
75
+ },
76
+ "publishConfig": {
77
+ "access": "public",
78
+ "registry": "https://registry.npmjs.org"
79
+ }
80
+ }
@@ -0,0 +1,13 @@
1
+ import { UnknownProviderError, NotProvidedCredentialsError } from './index';
2
+
3
+ describe('Error tests.', () => {
4
+ test('UnknownProviderError should be defined.', () => {
5
+ expect(UnknownProviderError).toBeDefined();
6
+ expect(new UnknownProviderError()).toBeInstanceOf(Error);
7
+ });
8
+
9
+ test('NotProvidedCredentialsError should be defined.', () => {
10
+ expect(NotProvidedCredentialsError).toBeDefined();
11
+ expect(new NotProvidedCredentialsError()).toBeInstanceOf(Error);
12
+ });
13
+ });
@@ -0,0 +1,2 @@
1
+ export * from './unknownProvider.error';
2
+ export * from './notProvidedCredentials.error';
@@ -0,0 +1,6 @@
1
+ export class NotProvidedCredentialsError extends Error {
2
+ constructor() {
3
+ super('SMS sender credentials not provided.');
4
+ this.name = 'NotProvidedCredentialsError';
5
+ }
6
+ }
@@ -0,0 +1,6 @@
1
+ export class UnknownProviderError extends Error {
2
+ constructor() {
3
+ super('Unknown provider. Set PROVIDER env variable');
4
+ this.name = 'UnknownProviderError';
5
+ }
6
+ }
@@ -0,0 +1,7 @@
1
+ import { SMSSenderService } from './index';
2
+
3
+ describe('index.ts', () => {
4
+ test('SMSSender should be defined.', () => {
5
+ expect(SMSSenderService).toBeDefined();
6
+ });
7
+ });
package/src/index.ts ADDED
@@ -0,0 +1,2 @@
1
+ export { SMSSenderService } from './services';
2
+ export type { SendResult } from './types';
@@ -0,0 +1 @@
1
+ export * from './smsSender.interface';
@@ -0,0 +1,5 @@
1
+ import { SendResult } from '../types';
2
+
3
+ export interface ISMSSender {
4
+ send: (phones: string[], message: string) => Promise<SendResult>;
5
+ }
@@ -0,0 +1,39 @@
1
+ /* eslint-disable @typescript-eslint/no-unused-vars */
2
+ import { SNSClient, PublishCommandOutput } from '@aws-sdk/client-sns';
3
+ import AwsSMSSenderClient from './awsSmsSender.client';
4
+
5
+ jest.mock('@aws-sdk/client-sns', () => {
6
+ const actualModule = jest.requireActual('@aws-sdk/client-sns');
7
+ class SNSClient {
8
+ send(
9
+ phones: string[] = [],
10
+ message: string,
11
+ ): Promise<PublishCommandOutput> {
12
+ return Promise.resolve({} as PublishCommandOutput);
13
+ }
14
+ }
15
+ return {
16
+ ...actualModule,
17
+ SNSClient,
18
+ };
19
+ });
20
+
21
+ jest.mock('../../utils/constants', () => ({
22
+ REGION: 'us-east-1',
23
+ USERNAME: 'qrvey',
24
+ PASSWORD: '123456',
25
+ }));
26
+
27
+ describe('AwsSMSSender tests.', () => {
28
+ beforeEach(() => {
29
+ jest.clearAllMocks();
30
+ jest.resetAllMocks();
31
+ });
32
+
33
+ test('Should call send method.', async () => {
34
+ const smsSender = new AwsSMSSenderClient();
35
+ const clientSend = jest.spyOn(SNSClient.prototype, 'send');
36
+ await smsSender.send(['+1234567890', '+9876543210'], 'Hello world');
37
+ expect(clientSend).toHaveBeenCalledTimes(2);
38
+ });
39
+ });
@@ -0,0 +1,57 @@
1
+ import { NotProvidedCredentialsError } from '../../errors';
2
+ import { ISMSSender } from '../../interfaces';
3
+ import { SendResult } from '../../types';
4
+ import { PASSWORD, REGION, USERNAME } from '../../utils/constants';
5
+ import {
6
+ PublishCommand,
7
+ PublishCommandOutput,
8
+ SNSClient,
9
+ } from '@aws-sdk/client-sns';
10
+
11
+ export default class AwsSMSSenderClient implements ISMSSender {
12
+ private client: SNSClient;
13
+ constructor() {
14
+ if (!(USERNAME && PASSWORD)) throw new NotProvidedCredentialsError();
15
+ this.client = new SNSClient({
16
+ region: REGION,
17
+ credentials: {
18
+ accessKeyId: USERNAME,
19
+ secretAccessKey: PASSWORD,
20
+ },
21
+ });
22
+ }
23
+
24
+ public async send(
25
+ phones: string[] = [],
26
+ message: string,
27
+ ): Promise<SendResult> {
28
+ const result: SendResult = {
29
+ sent: [],
30
+ failed: [],
31
+ };
32
+
33
+ await Promise.all(
34
+ phones.map((phone) =>
35
+ this._send(phone, message)
36
+ .then(() => result.sent.push({ phone }))
37
+ .catch((error) =>
38
+ result.failed.push({ phone, error: error.message }),
39
+ ),
40
+ ),
41
+ );
42
+
43
+ return result;
44
+ }
45
+
46
+ private _send = async (
47
+ phone: string,
48
+ message: string,
49
+ ): Promise<PublishCommandOutput> => {
50
+ const command = new PublishCommand({
51
+ Message: message,
52
+ PhoneNumber: phone,
53
+ });
54
+
55
+ return await this.client.send(command);
56
+ };
57
+ }
@@ -0,0 +1,46 @@
1
+ /* eslint-disable @typescript-eslint/no-unused-vars */
2
+ import { SmsClient, SmsSendResult } from '@azure/communication-sms';
3
+ import AzureSMSSenderClient from './azureSmsSender.client';
4
+
5
+ jest.mock('@azure/communication-sms', () => {
6
+ const actualModule = jest.requireActual('@azure/communication-sms');
7
+ class SmsClient {
8
+ send({
9
+ from,
10
+ to,
11
+ message,
12
+ }: {
13
+ from: string;
14
+ to: string[];
15
+ message: string;
16
+ }): Promise<SmsSendResult[]> {
17
+ return Promise.resolve(
18
+ to.map((phone) => ({ to: phone })) as SmsSendResult[],
19
+ );
20
+ }
21
+ }
22
+ return {
23
+ ...actualModule,
24
+ SmsClient,
25
+ };
26
+ });
27
+
28
+ jest.mock('../../utils/constants', () => ({
29
+ FROM: '+123456789',
30
+ USERNAME: 'qrvey',
31
+ PASSWORD: '123456',
32
+ }));
33
+
34
+ describe('AzureSMSSender tests.', () => {
35
+ beforeEach(() => {
36
+ jest.clearAllMocks();
37
+ jest.resetAllMocks();
38
+ });
39
+
40
+ test('Should call send method.', async () => {
41
+ const smsSender = new AzureSMSSenderClient();
42
+ const clientSend = jest.spyOn(SmsClient.prototype, 'send');
43
+ await smsSender.send(['+1234567890', '+9876543210'], 'Hello world');
44
+ expect(clientSend).toHaveBeenCalledTimes(1);
45
+ });
46
+ });
@@ -0,0 +1,38 @@
1
+ import { NotProvidedCredentialsError } from '../../errors';
2
+ import { ISMSSender } from '../../interfaces';
3
+ import { SendResult } from '../../types';
4
+ import { USERNAME, PASSWORD, FROM } from '../../utils/constants';
5
+ import { SmsClient } from '@azure/communication-sms';
6
+
7
+ export default class AzureSMSSenderClient implements ISMSSender {
8
+ client: SmsClient;
9
+ constructor() {
10
+ if (!(USERNAME && PASSWORD && FROM))
11
+ throw new NotProvidedCredentialsError();
12
+ const connectionString = `endpoint=https://${USERNAME}.communication.azure.com/;accesskey=${PASSWORD}`;
13
+ this.client = new SmsClient(connectionString);
14
+ }
15
+
16
+ public async send(phones: string[], message: string): Promise<SendResult> {
17
+ const result: SendResult = {
18
+ sent: [],
19
+ failed: [],
20
+ };
21
+ const smsResults = await this.client.send({
22
+ from: FROM as string,
23
+ to: phones,
24
+ message: message,
25
+ });
26
+
27
+ smsResults.forEach((smsResult) => {
28
+ if (smsResult.errorMessage)
29
+ result.failed.push({
30
+ phone: smsResult.to,
31
+ error: smsResult.errorMessage,
32
+ });
33
+ else result.sent.push({ phone: smsResult.to });
34
+ });
35
+
36
+ return result;
37
+ }
38
+ }
@@ -0,0 +1,50 @@
1
+ import { NotProvidedCredentialsError } from '../../errors';
2
+ import {
3
+ AwsSMSSenderClient,
4
+ AzureSMSSenderClient,
5
+ TwilioSMSSenderClient,
6
+ VonageSMSSenderClient,
7
+ } from './index';
8
+ import { SMSSender } from '../../../__tests__/__mocks__/SMSSender.mock';
9
+
10
+ describe('Client tests.', () => {
11
+ test('AwsSMSSender should be defined.', () => {
12
+ expect(AwsSMSSenderClient.default).toBeDefined();
13
+ });
14
+
15
+ test('AzureSMSSender should be defined.', () => {
16
+ expect(AzureSMSSenderClient.default).toBeDefined();
17
+ });
18
+
19
+ test('TwilioSMSSender should be defined.', () => {
20
+ expect(TwilioSMSSenderClient.default).toBeDefined();
21
+ });
22
+
23
+ test('VonageSMSSender should be defined.', () => {
24
+ expect(VonageSMSSenderClient.default).toBeDefined();
25
+ });
26
+
27
+ test('SMSSender must implement ISMSSender interface.', async () => {
28
+ const smsSender = new SMSSender();
29
+ const results = await smsSender.send(['+1234567890'], 'Hello world');
30
+ expect('send' in smsSender).toBe(true);
31
+ expect(smsSender.send).toBeInstanceOf(Function);
32
+
33
+ expect(results.sent).toBeInstanceOf(Array);
34
+ });
35
+
36
+ test('Should return NotProvidedCredentialsError.', () => {
37
+ expect(() => new AwsSMSSenderClient.default()).toThrow(
38
+ new NotProvidedCredentialsError(),
39
+ );
40
+ expect(() => new TwilioSMSSenderClient.default()).toThrow(
41
+ new NotProvidedCredentialsError(),
42
+ );
43
+ expect(() => new VonageSMSSenderClient.default()).toThrow(
44
+ new NotProvidedCredentialsError(),
45
+ );
46
+ expect(() => new AzureSMSSenderClient.default()).toThrow(
47
+ new NotProvidedCredentialsError(),
48
+ );
49
+ });
50
+ });
@@ -0,0 +1,4 @@
1
+ export * as AwsSMSSenderClient from './awsSmsSender.client';
2
+ export * as TwilioSMSSenderClient from './twilioSmsSender.client';
3
+ export * as AzureSMSSenderClient from './azureSmsSender.client';
4
+ export * as VonageSMSSenderClient from './vonageSmsSender.client';