@jadedm/nestjs-verify-twilio 0.1.0 → 0.2.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/README.md +48 -0
- package/dist/index.js +20 -3
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +22 -3
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/README.md
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
# @jadedm/nestjs-verify-twilio
|
|
2
|
+
|
|
3
|
+
Twilio SMS provider adapter for [`@jadedm/nestjs-verify`](https://www.npmjs.com/package/@jadedm/nestjs-verify).
|
|
4
|
+
|
|
5
|
+
```bash
|
|
6
|
+
pnpm add @jadedm/nestjs-verify-twilio twilio
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
## Usage
|
|
10
|
+
|
|
11
|
+
```ts
|
|
12
|
+
import { VerifyModule } from '@jadedm/nestjs-verify';
|
|
13
|
+
import { TwilioSmsProvider } from '@jadedm/nestjs-verify-twilio';
|
|
14
|
+
|
|
15
|
+
VerifyModule.forRoot({
|
|
16
|
+
sms: {
|
|
17
|
+
provider: new TwilioSmsProvider({
|
|
18
|
+
accountSid: process.env.TWILIO_ACCOUNT_SID!,
|
|
19
|
+
authToken: process.env.TWILIO_AUTH_TOKEN!,
|
|
20
|
+
from: process.env.TWILIO_FROM!, // E.164 number OR Messaging Service SID (starts with "MG")
|
|
21
|
+
maxRetries: 2, // optional, default 2
|
|
22
|
+
retryBaseMs: 250, // optional, default 250
|
|
23
|
+
}),
|
|
24
|
+
},
|
|
25
|
+
stores: { /* ... */ },
|
|
26
|
+
});
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
If `from` starts with `MG`, the adapter calls Twilio with `messagingServiceSid` instead of `from`.
|
|
30
|
+
|
|
31
|
+
## Retry behavior
|
|
32
|
+
|
|
33
|
+
Transient errors (HTTP 429 and 5xx) are retried with exponential backoff: `retryBaseMs * 2^attempt`. Default budget is two retries.
|
|
34
|
+
|
|
35
|
+
Terminal errors (invalid number, blocked recipient, geo permission, anything that is not 429/5xx) are not retried and are surfaced to the caller.
|
|
36
|
+
|
|
37
|
+
## Peers
|
|
38
|
+
|
|
39
|
+
- `@jadedm/nestjs-verify` 0.x
|
|
40
|
+
- `twilio` 4.x or 5.x
|
|
41
|
+
|
|
42
|
+
## Consulting
|
|
43
|
+
|
|
44
|
+
If you need a custom SMS provider adapter or fractional CTO support shipping this into production, see [manishj.com](https://manishj.com).
|
|
45
|
+
|
|
46
|
+
## License
|
|
47
|
+
|
|
48
|
+
MIT. Manish Jadhav ([@jadedm](https://github.com/jadedm)).
|
package/dist/index.js
CHANGED
|
@@ -3,6 +3,7 @@ var __defProp = Object.defineProperty;
|
|
|
3
3
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
4
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
5
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
|
|
6
7
|
var __export = (target, all) => {
|
|
7
8
|
for (var name in all)
|
|
8
9
|
__defProp(target, name, { get: all[name], enumerable: true });
|
|
@@ -26,8 +27,17 @@ module.exports = __toCommonJS(src_exports);
|
|
|
26
27
|
|
|
27
28
|
// src/twilio-sms.provider.ts
|
|
28
29
|
var import_twilio = require("twilio");
|
|
29
|
-
var TRANSIENT_STATUS_CODES = /* @__PURE__ */ new Set([
|
|
30
|
+
var TRANSIENT_STATUS_CODES = /* @__PURE__ */ new Set([
|
|
31
|
+
429,
|
|
32
|
+
500,
|
|
33
|
+
502,
|
|
34
|
+
503,
|
|
35
|
+
504
|
|
36
|
+
]);
|
|
30
37
|
var TwilioSmsProvider = class {
|
|
38
|
+
static {
|
|
39
|
+
__name(this, "TwilioSmsProvider");
|
|
40
|
+
}
|
|
31
41
|
name = "twilio";
|
|
32
42
|
client;
|
|
33
43
|
from;
|
|
@@ -48,9 +58,16 @@ var TwilioSmsProvider = class {
|
|
|
48
58
|
const message = await this.client.messages.create({
|
|
49
59
|
to: params.to,
|
|
50
60
|
body: params.body,
|
|
51
|
-
...useMessagingService ? {
|
|
61
|
+
...useMessagingService ? {
|
|
62
|
+
messagingServiceSid: this.from
|
|
63
|
+
} : {
|
|
64
|
+
from: this.from
|
|
65
|
+
}
|
|
52
66
|
});
|
|
53
|
-
return {
|
|
67
|
+
return {
|
|
68
|
+
providerMessageId: message.sid,
|
|
69
|
+
provider: this.name
|
|
70
|
+
};
|
|
54
71
|
} catch (err) {
|
|
55
72
|
lastErr = err;
|
|
56
73
|
const status = err.status;
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/twilio-sms.provider.ts"],"sourcesContent":["export * from './twilio-sms.provider.js';\n","import type {\n SmsProvider,\n SmsSendParams,\n SmsSendResult,\n} from '@jadedm/nestjs-verify';\nimport { Twilio } from 'twilio';\n\nexport interface TwilioSmsProviderOptions {\n accountSid: string;\n authToken: string;\n /** E.164 sender number, or Messaging Service SID (starts with MG…). */\n from: string;\n /** Retry budget for transient (5xx, network) failures. Default 2. */\n maxRetries?: number;\n /** Base delay in ms for exponential backoff. Default 250. */\n retryBaseMs?: number;\n}\n\n/**\n * Errors Twilio considers transient — worth retrying. Everything else\n * (invalid number, blocked recipient, geo-permission) is terminal.\n */\nconst TRANSIENT_STATUS_CODES = new Set([429, 500, 502, 503, 504]);\n\nexport class TwilioSmsProvider implements SmsProvider {\n readonly name = 'twilio';\n private readonly client: Twilio;\n private readonly from: string;\n private readonly maxRetries: number;\n private readonly retryBaseMs: number;\n\n constructor(opts: TwilioSmsProviderOptions) {\n this.client = new Twilio(opts.accountSid, opts.authToken);\n this.from = opts.from;\n this.maxRetries = opts.maxRetries ?? 2;\n this.retryBaseMs = opts.retryBaseMs ?? 250;\n }\n\n async send(params: SmsSendParams): Promise<SmsSendResult> {\n const useMessagingService = this.from.startsWith('MG');\n let attempt = 0;\n let lastErr: unknown;\n while (attempt <= this.maxRetries) {\n try {\n const message = await this.client.messages.create({\n to: params.to,\n body: params.body,\n ...(useMessagingService\n ? { messagingServiceSid: this.from }\n : { from: this.from }),\n });\n return { providerMessageId: message.sid, provider: this.name };\n } catch (err) {\n lastErr = err;\n const status = (err as { status?: number }).status;\n if (status && !TRANSIENT_STATUS_CODES.has(status)) throw err;\n if (attempt === this.maxRetries) break;\n await this.sleep(this.retryBaseMs * 2 ** attempt);\n attempt++;\n }\n }\n throw lastErr;\n }\n\n private sleep(ms: number): Promise<void> {\n return new Promise((r) => setTimeout(r, ms));\n }\n}\n"],"mappings":"
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/twilio-sms.provider.ts"],"sourcesContent":["export * from './twilio-sms.provider.js';\n","import type {\n SmsProvider,\n SmsSendParams,\n SmsSendResult,\n} from '@jadedm/nestjs-verify';\nimport { Twilio } from 'twilio';\n\nexport interface TwilioSmsProviderOptions {\n accountSid: string;\n authToken: string;\n /** E.164 sender number, or Messaging Service SID (starts with MG…). */\n from: string;\n /** Retry budget for transient (5xx, network) failures. Default 2. */\n maxRetries?: number;\n /** Base delay in ms for exponential backoff. Default 250. */\n retryBaseMs?: number;\n}\n\n/**\n * Errors Twilio considers transient — worth retrying. Everything else\n * (invalid number, blocked recipient, geo-permission) is terminal.\n */\nconst TRANSIENT_STATUS_CODES = new Set([429, 500, 502, 503, 504]);\n\nexport class TwilioSmsProvider implements SmsProvider {\n readonly name = 'twilio';\n private readonly client: Twilio;\n private readonly from: string;\n private readonly maxRetries: number;\n private readonly retryBaseMs: number;\n\n constructor(opts: TwilioSmsProviderOptions) {\n this.client = new Twilio(opts.accountSid, opts.authToken);\n this.from = opts.from;\n this.maxRetries = opts.maxRetries ?? 2;\n this.retryBaseMs = opts.retryBaseMs ?? 250;\n }\n\n async send(params: SmsSendParams): Promise<SmsSendResult> {\n const useMessagingService = this.from.startsWith('MG');\n let attempt = 0;\n let lastErr: unknown;\n while (attempt <= this.maxRetries) {\n try {\n const message = await this.client.messages.create({\n to: params.to,\n body: params.body,\n ...(useMessagingService\n ? { messagingServiceSid: this.from }\n : { from: this.from }),\n });\n return { providerMessageId: message.sid, provider: this.name };\n } catch (err) {\n lastErr = err;\n const status = (err as { status?: number }).status;\n if (status && !TRANSIENT_STATUS_CODES.has(status)) throw err;\n if (attempt === this.maxRetries) break;\n await this.sleep(this.retryBaseMs * 2 ** attempt);\n attempt++;\n }\n }\n throw lastErr;\n }\n\n private sleep(ms: number): Promise<void> {\n return new Promise((r) => setTimeout(r, ms));\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;AAAA;;;;;;;ACKA,oBAAuB;AAiBvB,IAAMA,yBAAyB,oBAAIC,IAAI;EAAC;EAAK;EAAK;EAAK;EAAK;CAAI;AAEzD,IAAMC,oBAAN,MAAMA;EAnBb,OAmBaA;;;EACFC,OAAO;EACCC;EACAC;EACAC;EACAC;EAEjB,YAAYC,MAAgC;AAC1C,SAAKJ,SAAS,IAAIK,qBAAOD,KAAKE,YAAYF,KAAKG,SAAS;AACxD,SAAKN,OAAOG,KAAKH;AACjB,SAAKC,aAAaE,KAAKF,cAAc;AACrC,SAAKC,cAAcC,KAAKD,eAAe;EACzC;EAEA,MAAMK,KAAKC,QAA+C;AACxD,UAAMC,sBAAsB,KAAKT,KAAKU,WAAW,IAAA;AACjD,QAAIC,UAAU;AACd,QAAIC;AACJ,WAAOD,WAAW,KAAKV,YAAY;AACjC,UAAI;AACF,cAAMY,UAAU,MAAM,KAAKd,OAAOe,SAASC,OAAO;UAChDC,IAAIR,OAAOQ;UACXC,MAAMT,OAAOS;UACb,GAAIR,sBACA;YAAES,qBAAqB,KAAKlB;UAAK,IACjC;YAAEA,MAAM,KAAKA;UAAK;QACxB,CAAA;AACA,eAAO;UAAEmB,mBAAmBN,QAAQO;UAAKC,UAAU,KAAKvB;QAAK;MAC/D,SAASwB,KAAK;AACZV,kBAAUU;AACV,cAAMC,SAAUD,IAA4BC;AAC5C,YAAIA,UAAU,CAAC5B,uBAAuB6B,IAAID,MAAAA,EAAS,OAAMD;AACzD,YAAIX,YAAY,KAAKV,WAAY;AACjC,cAAM,KAAKwB,MAAM,KAAKvB,cAAc,KAAKS,OAAAA;AACzCA;MACF;IACF;AACA,UAAMC;EACR;EAEQa,MAAMC,IAA2B;AACvC,WAAO,IAAIC,QAAQ,CAACC,MAAMC,WAAWD,GAAGF,EAAAA,CAAAA;EAC1C;AACF;","names":["TRANSIENT_STATUS_CODES","Set","TwilioSmsProvider","name","client","from","maxRetries","retryBaseMs","opts","Twilio","accountSid","authToken","send","params","useMessagingService","startsWith","attempt","lastErr","message","messages","create","to","body","messagingServiceSid","providerMessageId","sid","provider","err","status","has","sleep","ms","Promise","r","setTimeout"]}
|
package/dist/index.mjs
CHANGED
|
@@ -1,7 +1,19 @@
|
|
|
1
|
+
var __defProp = Object.defineProperty;
|
|
2
|
+
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
|
|
3
|
+
|
|
1
4
|
// src/twilio-sms.provider.ts
|
|
2
5
|
import { Twilio } from "twilio";
|
|
3
|
-
var TRANSIENT_STATUS_CODES = /* @__PURE__ */ new Set([
|
|
6
|
+
var TRANSIENT_STATUS_CODES = /* @__PURE__ */ new Set([
|
|
7
|
+
429,
|
|
8
|
+
500,
|
|
9
|
+
502,
|
|
10
|
+
503,
|
|
11
|
+
504
|
|
12
|
+
]);
|
|
4
13
|
var TwilioSmsProvider = class {
|
|
14
|
+
static {
|
|
15
|
+
__name(this, "TwilioSmsProvider");
|
|
16
|
+
}
|
|
5
17
|
name = "twilio";
|
|
6
18
|
client;
|
|
7
19
|
from;
|
|
@@ -22,9 +34,16 @@ var TwilioSmsProvider = class {
|
|
|
22
34
|
const message = await this.client.messages.create({
|
|
23
35
|
to: params.to,
|
|
24
36
|
body: params.body,
|
|
25
|
-
...useMessagingService ? {
|
|
37
|
+
...useMessagingService ? {
|
|
38
|
+
messagingServiceSid: this.from
|
|
39
|
+
} : {
|
|
40
|
+
from: this.from
|
|
41
|
+
}
|
|
26
42
|
});
|
|
27
|
-
return {
|
|
43
|
+
return {
|
|
44
|
+
providerMessageId: message.sid,
|
|
45
|
+
provider: this.name
|
|
46
|
+
};
|
|
28
47
|
} catch (err) {
|
|
29
48
|
lastErr = err;
|
|
30
49
|
const status = err.status;
|
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/twilio-sms.provider.ts"],"sourcesContent":["import type {\n SmsProvider,\n SmsSendParams,\n SmsSendResult,\n} from '@jadedm/nestjs-verify';\nimport { Twilio } from 'twilio';\n\nexport interface TwilioSmsProviderOptions {\n accountSid: string;\n authToken: string;\n /** E.164 sender number, or Messaging Service SID (starts with MG…). */\n from: string;\n /** Retry budget for transient (5xx, network) failures. Default 2. */\n maxRetries?: number;\n /** Base delay in ms for exponential backoff. Default 250. */\n retryBaseMs?: number;\n}\n\n/**\n * Errors Twilio considers transient — worth retrying. Everything else\n * (invalid number, blocked recipient, geo-permission) is terminal.\n */\nconst TRANSIENT_STATUS_CODES = new Set([429, 500, 502, 503, 504]);\n\nexport class TwilioSmsProvider implements SmsProvider {\n readonly name = 'twilio';\n private readonly client: Twilio;\n private readonly from: string;\n private readonly maxRetries: number;\n private readonly retryBaseMs: number;\n\n constructor(opts: TwilioSmsProviderOptions) {\n this.client = new Twilio(opts.accountSid, opts.authToken);\n this.from = opts.from;\n this.maxRetries = opts.maxRetries ?? 2;\n this.retryBaseMs = opts.retryBaseMs ?? 250;\n }\n\n async send(params: SmsSendParams): Promise<SmsSendResult> {\n const useMessagingService = this.from.startsWith('MG');\n let attempt = 0;\n let lastErr: unknown;\n while (attempt <= this.maxRetries) {\n try {\n const message = await this.client.messages.create({\n to: params.to,\n body: params.body,\n ...(useMessagingService\n ? { messagingServiceSid: this.from }\n : { from: this.from }),\n });\n return { providerMessageId: message.sid, provider: this.name };\n } catch (err) {\n lastErr = err;\n const status = (err as { status?: number }).status;\n if (status && !TRANSIENT_STATUS_CODES.has(status)) throw err;\n if (attempt === this.maxRetries) break;\n await this.sleep(this.retryBaseMs * 2 ** attempt);\n attempt++;\n }\n }\n throw lastErr;\n }\n\n private sleep(ms: number): Promise<void> {\n return new Promise((r) => setTimeout(r, ms));\n }\n}\n"],"mappings":"
|
|
1
|
+
{"version":3,"sources":["../src/twilio-sms.provider.ts"],"sourcesContent":["import type {\n SmsProvider,\n SmsSendParams,\n SmsSendResult,\n} from '@jadedm/nestjs-verify';\nimport { Twilio } from 'twilio';\n\nexport interface TwilioSmsProviderOptions {\n accountSid: string;\n authToken: string;\n /** E.164 sender number, or Messaging Service SID (starts with MG…). */\n from: string;\n /** Retry budget for transient (5xx, network) failures. Default 2. */\n maxRetries?: number;\n /** Base delay in ms for exponential backoff. Default 250. */\n retryBaseMs?: number;\n}\n\n/**\n * Errors Twilio considers transient — worth retrying. Everything else\n * (invalid number, blocked recipient, geo-permission) is terminal.\n */\nconst TRANSIENT_STATUS_CODES = new Set([429, 500, 502, 503, 504]);\n\nexport class TwilioSmsProvider implements SmsProvider {\n readonly name = 'twilio';\n private readonly client: Twilio;\n private readonly from: string;\n private readonly maxRetries: number;\n private readonly retryBaseMs: number;\n\n constructor(opts: TwilioSmsProviderOptions) {\n this.client = new Twilio(opts.accountSid, opts.authToken);\n this.from = opts.from;\n this.maxRetries = opts.maxRetries ?? 2;\n this.retryBaseMs = opts.retryBaseMs ?? 250;\n }\n\n async send(params: SmsSendParams): Promise<SmsSendResult> {\n const useMessagingService = this.from.startsWith('MG');\n let attempt = 0;\n let lastErr: unknown;\n while (attempt <= this.maxRetries) {\n try {\n const message = await this.client.messages.create({\n to: params.to,\n body: params.body,\n ...(useMessagingService\n ? { messagingServiceSid: this.from }\n : { from: this.from }),\n });\n return { providerMessageId: message.sid, provider: this.name };\n } catch (err) {\n lastErr = err;\n const status = (err as { status?: number }).status;\n if (status && !TRANSIENT_STATUS_CODES.has(status)) throw err;\n if (attempt === this.maxRetries) break;\n await this.sleep(this.retryBaseMs * 2 ** attempt);\n attempt++;\n }\n }\n throw lastErr;\n }\n\n private sleep(ms: number): Promise<void> {\n return new Promise((r) => setTimeout(r, ms));\n }\n}\n"],"mappings":";;;;AAKA,SAASA,cAAc;AAiBvB,IAAMC,yBAAyB,oBAAIC,IAAI;EAAC;EAAK;EAAK;EAAK;EAAK;CAAI;AAEzD,IAAMC,oBAAN,MAAMA;EAnBb,OAmBaA;;;EACFC,OAAO;EACCC;EACAC;EACAC;EACAC;EAEjB,YAAYC,MAAgC;AAC1C,SAAKJ,SAAS,IAAIK,OAAOD,KAAKE,YAAYF,KAAKG,SAAS;AACxD,SAAKN,OAAOG,KAAKH;AACjB,SAAKC,aAAaE,KAAKF,cAAc;AACrC,SAAKC,cAAcC,KAAKD,eAAe;EACzC;EAEA,MAAMK,KAAKC,QAA+C;AACxD,UAAMC,sBAAsB,KAAKT,KAAKU,WAAW,IAAA;AACjD,QAAIC,UAAU;AACd,QAAIC;AACJ,WAAOD,WAAW,KAAKV,YAAY;AACjC,UAAI;AACF,cAAMY,UAAU,MAAM,KAAKd,OAAOe,SAASC,OAAO;UAChDC,IAAIR,OAAOQ;UACXC,MAAMT,OAAOS;UACb,GAAIR,sBACA;YAAES,qBAAqB,KAAKlB;UAAK,IACjC;YAAEA,MAAM,KAAKA;UAAK;QACxB,CAAA;AACA,eAAO;UAAEmB,mBAAmBN,QAAQO;UAAKC,UAAU,KAAKvB;QAAK;MAC/D,SAASwB,KAAK;AACZV,kBAAUU;AACV,cAAMC,SAAUD,IAA4BC;AAC5C,YAAIA,UAAU,CAAC5B,uBAAuB6B,IAAID,MAAAA,EAAS,OAAMD;AACzD,YAAIX,YAAY,KAAKV,WAAY;AACjC,cAAM,KAAKwB,MAAM,KAAKvB,cAAc,KAAKS,OAAAA;AACzCA;MACF;IACF;AACA,UAAMC;EACR;EAEQa,MAAMC,IAA2B;AACvC,WAAO,IAAIC,QAAQ,CAACC,MAAMC,WAAWD,GAAGF,EAAAA,CAAAA;EAC1C;AACF;","names":["Twilio","TRANSIENT_STATUS_CODES","Set","TwilioSmsProvider","name","client","from","maxRetries","retryBaseMs","opts","Twilio","accountSid","authToken","send","params","useMessagingService","startsWith","attempt","lastErr","message","messages","create","to","body","messagingServiceSid","providerMessageId","sid","provider","err","status","has","sleep","ms","Promise","r","setTimeout"]}
|