@forklaunch/implementation-messaging-twilio 1.0.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.
Files changed (42) hide show
  1. package/LICENSE +21 -0
  2. package/lib/domain/enum/index.d.ts +3 -0
  3. package/lib/domain/enum/index.d.ts.map +1 -0
  4. package/lib/domain/enum/index.js +50 -0
  5. package/lib/domain/enum/index.mjs +21 -0
  6. package/lib/domain/enum/twilioMessageStatus.enum.d.ts +16 -0
  7. package/lib/domain/enum/twilioMessageStatus.enum.d.ts.map +1 -0
  8. package/lib/domain/schemas/index.d.ts +2 -0
  9. package/lib/domain/schemas/index.d.ts.map +1 -0
  10. package/lib/domain/schemas/index.js +86 -0
  11. package/lib/domain/schemas/index.mjs +75 -0
  12. package/lib/domain/schemas/sms.schema.d.ts +44 -0
  13. package/lib/domain/schemas/sms.schema.d.ts.map +1 -0
  14. package/lib/domain/schemas/typebox/sms.schema.d.ts +39 -0
  15. package/lib/domain/schemas/typebox/sms.schema.d.ts.map +1 -0
  16. package/lib/domain/schemas/zod/sms.schema.d.ts +39 -0
  17. package/lib/domain/schemas/zod/sms.schema.d.ts.map +1 -0
  18. package/lib/domain/types/index.d.ts +2 -0
  19. package/lib/domain/types/index.d.ts.map +1 -0
  20. package/lib/domain/types/index.js +18 -0
  21. package/lib/domain/types/index.mjs +0 -0
  22. package/lib/domain/types/twilio.types.d.ts +15 -0
  23. package/lib/domain/types/twilio.types.d.ts.map +1 -0
  24. package/lib/eject/domain/enum/index.ts +3 -0
  25. package/lib/eject/domain/enum/twilioMessageStatus.enum.ts +18 -0
  26. package/lib/eject/domain/schemas/index.ts +1 -0
  27. package/lib/eject/domain/schemas/sms.schema.ts +33 -0
  28. package/lib/eject/domain/types/index.ts +1 -0
  29. package/lib/eject/domain/types/twilio.types.ts +24 -0
  30. package/lib/eject/services/index.ts +4 -0
  31. package/lib/eject/services/sms.service.ts +216 -0
  32. package/lib/persistence/entities/index.d.ts +92 -0
  33. package/lib/persistence/entities/index.d.ts.map +1 -0
  34. package/lib/persistence/entities/index.js +42 -0
  35. package/lib/persistence/entities/index.mjs +17 -0
  36. package/lib/services/index.d.ts +4 -0
  37. package/lib/services/index.d.ts.map +1 -0
  38. package/lib/services/index.js +193 -0
  39. package/lib/services/index.mjs +164 -0
  40. package/lib/services/sms.service.d.ts +38 -0
  41. package/lib/services/sms.service.d.ts.map +1 -0
  42. package/package.json +99 -0
@@ -0,0 +1,164 @@
1
+ // services/sms.service.ts
2
+ import {
3
+ evaluateTelemetryOptions
4
+ } from "@forklaunch/core/http";
5
+ import { SmsStatusEnum } from "@forklaunch/implementation-messaging-base/enum";
6
+ import { EntityManager } from "@mikro-orm/core";
7
+
8
+ // domain/enum/twilioMessageStatus.enum.ts
9
+ var TwilioMessageStatusEnum = {
10
+ QUEUED: "queued",
11
+ ACCEPTED: "accepted",
12
+ SCHEDULED: "scheduled",
13
+ CANCELED: "canceled",
14
+ SENDING: "sending",
15
+ SENT: "sent",
16
+ DELIVERED: "delivered",
17
+ UNDELIVERED: "undelivered",
18
+ FAILED: "failed",
19
+ RECEIVING: "receiving",
20
+ RECEIVED: "received",
21
+ READ: "read"
22
+ };
23
+
24
+ // services/sms.service.ts
25
+ var mapTwilioMessageStatus = (messageStatus) => {
26
+ switch (messageStatus) {
27
+ case TwilioMessageStatusEnum.SENT:
28
+ return SmsStatusEnum.SENT;
29
+ case TwilioMessageStatusEnum.DELIVERED:
30
+ case TwilioMessageStatusEnum.READ:
31
+ return SmsStatusEnum.DELIVERED;
32
+ case TwilioMessageStatusEnum.UNDELIVERED:
33
+ return SmsStatusEnum.UNDELIVERED;
34
+ case TwilioMessageStatusEnum.FAILED:
35
+ case TwilioMessageStatusEnum.CANCELED:
36
+ return SmsStatusEnum.FAILED;
37
+ case TwilioMessageStatusEnum.QUEUED:
38
+ case TwilioMessageStatusEnum.ACCEPTED:
39
+ case TwilioMessageStatusEnum.SCHEDULED:
40
+ case TwilioMessageStatusEnum.SENDING:
41
+ default:
42
+ return SmsStatusEnum.QUEUED;
43
+ }
44
+ };
45
+ var TWILIO_API_BASE_URL = "https://api.twilio.com/2010-04-01";
46
+ var TwilioSmsService = class {
47
+ constructor(accountSid, authToken, fromNumber, em, openTelemetryCollector, schemaValidator, mappers, options) {
48
+ this.options = options;
49
+ this.accountSid = accountSid;
50
+ this.authToken = authToken;
51
+ this.fromNumber = fromNumber;
52
+ this.em = em;
53
+ this.openTelemetryCollector = openTelemetryCollector;
54
+ this.schemaValidator = schemaValidator;
55
+ this.mappers = mappers;
56
+ this.evaluatedTelemetryOptions = options?.telemetry ? evaluateTelemetryOptions(options.telemetry).enabled : {
57
+ logging: false,
58
+ metrics: false,
59
+ tracing: false
60
+ };
61
+ }
62
+ options;
63
+ evaluatedTelemetryOptions;
64
+ accountSid;
65
+ authToken;
66
+ fromNumber;
67
+ em;
68
+ openTelemetryCollector;
69
+ schemaValidator;
70
+ mappers;
71
+ async sendSms(smsDto, em, ...args) {
72
+ if (this.evaluatedTelemetryOptions.logging) {
73
+ this.openTelemetryCollector.info("Sending sms via twilio", {
74
+ to: smsDto.to
75
+ });
76
+ }
77
+ const smsRecord = await this.mappers.SendSmsMapper.toEntity(
78
+ smsDto,
79
+ em ?? this.em,
80
+ ...args[0] instanceof EntityManager ? args.slice(1) : args
81
+ );
82
+ const record = smsRecord;
83
+ const response = await fetch(
84
+ `${TWILIO_API_BASE_URL}/Accounts/${this.accountSid}/Messages.json`,
85
+ {
86
+ method: "POST",
87
+ headers: {
88
+ Authorization: `Basic ${Buffer.from(
89
+ `${this.accountSid}:${this.authToken}`
90
+ ).toString("base64")}`,
91
+ "Content-Type": "application/x-www-form-urlencoded"
92
+ },
93
+ body: new URLSearchParams({
94
+ To: smsDto.to,
95
+ From: this.fromNumber,
96
+ Body: smsDto.body
97
+ }).toString()
98
+ }
99
+ );
100
+ if (response.ok) {
101
+ const message = await response.json();
102
+ record.providerMessageId = message.sid;
103
+ record.status = mapTwilioMessageStatus(message.status);
104
+ record.error = message.error_message ?? null;
105
+ } else {
106
+ const errorBody = await response.text();
107
+ record.status = SmsStatusEnum.FAILED;
108
+ record.error = `Twilio API error (${response.status}): ${errorBody}`;
109
+ this.openTelemetryCollector.error("Twilio message dispatch failed", {
110
+ status: response.status,
111
+ error: errorBody
112
+ });
113
+ }
114
+ await (em ?? this.em).transactional(async (innerEm) => {
115
+ await innerEm.persist(smsRecord);
116
+ });
117
+ return this.mappers.SmsRecordMapper.toDto(
118
+ smsRecord
119
+ );
120
+ }
121
+ async getSmsRecord({ id }, em) {
122
+ if (this.evaluatedTelemetryOptions.logging) {
123
+ this.openTelemetryCollector.info("Getting sms record", { id });
124
+ }
125
+ const smsRecord = await (em ?? this.em).findOneOrFail(
126
+ this.mappers.SmsRecordMapper.entity,
127
+ { id }
128
+ );
129
+ return this.mappers.SmsRecordMapper.toDto(
130
+ smsRecord
131
+ );
132
+ }
133
+ /**
134
+ * Maps a Twilio delivery-status callback (MessageStatus, MessageSid,
135
+ * ErrorMessage) onto the persisted record's status.
136
+ */
137
+ async processStatusCallback(payload, em) {
138
+ if (this.evaluatedTelemetryOptions.logging) {
139
+ this.openTelemetryCollector.info("Processing twilio status callback", {
140
+ messageSid: payload.MessageSid,
141
+ messageStatus: payload.MessageStatus
142
+ });
143
+ }
144
+ const smsRecord = await (em ?? this.em).findOneOrFail(
145
+ this.mappers.SmsRecordMapper.entity,
146
+ { providerMessageId: payload.MessageSid }
147
+ );
148
+ const record = smsRecord;
149
+ record.status = mapTwilioMessageStatus(payload.MessageStatus);
150
+ record.error = payload.ErrorMessage ?? null;
151
+ await (em ?? this.em).flush();
152
+ return this.mappers.SmsRecordMapper.toDto(
153
+ smsRecord
154
+ );
155
+ }
156
+ };
157
+
158
+ // services/index.ts
159
+ export * from "@forklaunch/interfaces-messaging/interfaces";
160
+ export * from "@forklaunch/interfaces-messaging/types";
161
+ export {
162
+ TwilioSmsService,
163
+ mapTwilioMessageStatus
164
+ };
@@ -0,0 +1,38 @@
1
+ import { IdDto } from '@forklaunch/common';
2
+ import { MetricsDefinition, OpenTelemetryCollector, TelemetryOptions } from '@forklaunch/core/http';
3
+ import { SmsStatusEnum } from '@forklaunch/implementation-messaging-base/enum';
4
+ import { SmsMappers } from '@forklaunch/implementation-messaging-base/types';
5
+ import { SmsService } from '@forklaunch/interfaces-messaging/interfaces';
6
+ import { SendSmsDto } from '@forklaunch/interfaces-messaging/types';
7
+ import { AnySchemaValidator } from '@forklaunch/validator';
8
+ import { EntityManager } from '@mikro-orm/core';
9
+ import { TwilioSmsDtos, TwilioSmsEntities, TwilioStatusCallbackDto } from '../domain/types/twilio.types';
10
+ /**
11
+ * Maps a Twilio message status (from the Messages REST API or a delivery
12
+ * status callback) onto the canonical SmsStatusEnum.
13
+ */
14
+ export declare const mapTwilioMessageStatus: (messageStatus: string) => SmsStatusEnum;
15
+ export declare class TwilioSmsService<SchemaValidator extends AnySchemaValidator, Entities extends TwilioSmsEntities = TwilioSmsEntities, Dto extends TwilioSmsDtos = TwilioSmsDtos> implements SmsService<typeof SmsStatusEnum> {
16
+ readonly options?: {
17
+ telemetry?: TelemetryOptions;
18
+ } | undefined;
19
+ private evaluatedTelemetryOptions;
20
+ protected readonly accountSid: string;
21
+ protected readonly authToken: string;
22
+ protected readonly fromNumber: string;
23
+ em: EntityManager;
24
+ protected readonly openTelemetryCollector: OpenTelemetryCollector<MetricsDefinition>;
25
+ protected readonly schemaValidator: SchemaValidator;
26
+ protected readonly mappers: SmsMappers<typeof SmsStatusEnum, Entities, Dto>;
27
+ constructor(accountSid: string, authToken: string, fromNumber: string, em: EntityManager, openTelemetryCollector: OpenTelemetryCollector<MetricsDefinition>, schemaValidator: SchemaValidator, mappers: SmsMappers<typeof SmsStatusEnum, Entities, Dto>, options?: {
28
+ telemetry?: TelemetryOptions;
29
+ } | undefined);
30
+ sendSms(smsDto: SendSmsDto, em?: EntityManager, ...args: unknown[]): Promise<Dto['SmsRecordMapper']>;
31
+ getSmsRecord({ id }: IdDto, em?: EntityManager): Promise<Dto['SmsRecordMapper']>;
32
+ /**
33
+ * Maps a Twilio delivery-status callback (MessageStatus, MessageSid,
34
+ * ErrorMessage) onto the persisted record's status.
35
+ */
36
+ processStatusCallback(payload: TwilioStatusCallbackDto, em?: EntityManager): Promise<Dto['SmsRecordMapper']>;
37
+ }
38
+ //# sourceMappingURL=sms.service.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sms.service.d.ts","sourceRoot":"","sources":["../../services/sms.service.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAC;AAC3C,OAAO,EAEL,iBAAiB,EACjB,sBAAsB,EACtB,gBAAgB,EACjB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EAAE,aAAa,EAAE,MAAM,gDAAgD,CAAC;AAC/E,OAAO,EAAE,UAAU,EAAE,MAAM,iDAAiD,CAAC;AAC7E,OAAO,EAAE,UAAU,EAAE,MAAM,6CAA6C,CAAC;AACzE,OAAO,EAAE,UAAU,EAAE,MAAM,wCAAwC,CAAC;AACpE,OAAO,EAAE,kBAAkB,EAAE,MAAM,uBAAuB,CAAC;AAC3D,OAAO,EAAE,aAAa,EAAe,MAAM,iBAAiB,CAAC;AAE7D,OAAO,EAEL,aAAa,EACb,iBAAiB,EACjB,uBAAuB,EACxB,MAAM,8BAA8B,CAAC;AAGtC;;;GAGG;AACH,eAAO,MAAM,sBAAsB,kBAClB,MAAM,KACpB,aAmBF,CAAC;AAIF,qBAAa,gBAAgB,CAC3B,eAAe,SAAS,kBAAkB,EAC1C,QAAQ,SAAS,iBAAiB,GAAG,iBAAiB,EACtD,GAAG,SAAS,aAAa,GAAG,aAAa,CACzC,YAAW,UAAU,CAAC,OAAO,aAAa,CAAC;IAuBzC,QAAQ,CAAC,OAAO,CAAC,EAAE;QACjB,SAAS,CAAC,EAAE,gBAAgB,CAAC;KAC9B;IAvBH,OAAO,CAAC,yBAAyB,CAI/B;IACF,SAAS,CAAC,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IACtC,SAAS,CAAC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IACrC,SAAS,CAAC,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC/B,EAAE,EAAE,aAAa,CAAC;IACzB,SAAS,CAAC,QAAQ,CAAC,sBAAsB,EAAE,sBAAsB,CAAC,iBAAiB,CAAC,CAAC;IACrF,SAAS,CAAC,QAAQ,CAAC,eAAe,EAAE,eAAe,CAAC;IACpD,SAAS,CAAC,QAAQ,CAAC,OAAO,EAAE,UAAU,CAAC,OAAO,aAAa,EAAE,QAAQ,EAAE,GAAG,CAAC,CAAC;IAE5E,YACE,UAAU,EAAE,MAAM,EAClB,SAAS,EAAE,MAAM,EACjB,UAAU,EAAE,MAAM,EAClB,EAAE,EAAE,aAAa,EACjB,sBAAsB,EAAE,sBAAsB,CAAC,iBAAiB,CAAC,EACjE,eAAe,EAAE,eAAe,EAChC,OAAO,EAAE,UAAU,CAAC,OAAO,aAAa,EAAE,QAAQ,EAAE,GAAG,CAAC,EAC/C,OAAO,CAAC,EAAE;QACjB,SAAS,CAAC,EAAE,gBAAgB,CAAC;KAC9B,YAAA,EAgBF;IAEK,OAAO,CACX,MAAM,EAAE,UAAU,EAClB,EAAE,CAAC,EAAE,aAAa,EAClB,GAAG,IAAI,EAAE,OAAO,EAAE,GACjB,OAAO,CAAC,GAAG,CAAC,iBAAiB,CAAC,CAAC,CA4DjC;IAEK,YAAY,CAChB,EAAE,EAAE,EAAE,EAAE,KAAK,EACb,EAAE,CAAC,EAAE,aAAa,GACjB,OAAO,CAAC,GAAG,CAAC,iBAAiB,CAAC,CAAC,CAajC;IAED;;;OAGG;IACG,qBAAqB,CACzB,OAAO,EAAE,uBAAuB,EAChC,EAAE,CAAC,EAAE,aAAa,GACjB,OAAO,CAAC,GAAG,CAAC,iBAAiB,CAAC,CAAC,CAyBjC;CACF"}
package/package.json ADDED
@@ -0,0 +1,99 @@
1
+ {
2
+ "name": "@forklaunch/implementation-messaging-twilio",
3
+ "version": "1.0.0",
4
+ "description": "Twilio implementation for forklaunch messaging",
5
+ "homepage": "https://github.com/forklaunch/forklaunch-js#readme",
6
+ "bugs": {
7
+ "url": "https://github.com/forklaunch/forklaunch-js/issues"
8
+ },
9
+ "repository": {
10
+ "type": "git",
11
+ "url": "git+https://github.com/forklaunch/forklaunch-js.git"
12
+ },
13
+ "license": "MIT",
14
+ "author": "Forklift Technologies, Inc.",
15
+ "exports": {
16
+ "./enum": {
17
+ "types": "./lib/domain/enum/index.d.ts",
18
+ "import": "./lib/domain/enum/index.mjs",
19
+ "require": "./lib/domain/enum/index.js",
20
+ "default": "./lib/domain/enum/index.js"
21
+ },
22
+ "./schemas": {
23
+ "types": "./lib/domain/schemas/index.d.ts",
24
+ "import": "./lib/domain/schemas/index.mjs",
25
+ "require": "./lib/domain/schemas/index.js",
26
+ "default": "./lib/domain/schemas/index.js"
27
+ },
28
+ "./services": {
29
+ "types": "./lib/services/index.d.ts",
30
+ "import": "./lib/services/index.mjs",
31
+ "require": "./lib/services/index.js",
32
+ "default": "./lib/services/index.js"
33
+ },
34
+ "./types": {
35
+ "types": "./lib/domain/types/index.d.ts",
36
+ "import": "./lib/domain/types/index.mjs",
37
+ "require": "./lib/domain/types/index.js",
38
+ "default": "./lib/domain/types/index.js"
39
+ },
40
+ "./enum/*": {
41
+ "types": "./lib/domain/enum/*.d.ts"
42
+ },
43
+ "./schemas/*": {
44
+ "types": "./lib/domain/schemas/*.d.ts"
45
+ },
46
+ "./services/*": {
47
+ "types": "./lib/services/*.d.ts"
48
+ },
49
+ "./types/*": {
50
+ "types": "./lib/domain/types/*.d.ts"
51
+ },
52
+ "./persistence": {
53
+ "types": "./lib/persistence/entities/index.d.ts",
54
+ "import": "./lib/persistence/entities/index.mjs",
55
+ "require": "./lib/persistence/entities/index.js",
56
+ "default": "./lib/persistence/entities/index.js"
57
+ },
58
+ "./persistence/*": {
59
+ "types": "./lib/persistence/entities/*.d.ts"
60
+ }
61
+ },
62
+ "files": [
63
+ "lib/**"
64
+ ],
65
+ "dependencies": {
66
+ "@forklaunch/common": "^1.2.23",
67
+ "@forklaunch/core": "^1.5.15",
68
+ "@forklaunch/internal": "^1.2.24",
69
+ "@forklaunch/validator": "^1.2.24",
70
+ "@mikro-orm/core": "^7.1.11",
71
+ "@sinclair/typebox": "^0.34.52",
72
+ "ajv": "^8.20.0",
73
+ "zod": "^4.4.3",
74
+ "@forklaunch/implementation-messaging-base": "^1.0.0",
75
+ "@forklaunch/interfaces-messaging": "^1.0.0"
76
+ },
77
+ "devDependencies": {
78
+ "@typescript/native-preview": "7.0.0-dev.20260707.2",
79
+ "depcheck": "^1.4.7",
80
+ "prettier": "^3.9.6",
81
+ "typedoc": "^0.28.20",
82
+ "@mikro-orm/sqlite": "^7.1.11",
83
+ "uuid": "^14.0.1"
84
+ },
85
+ "publishConfig": {
86
+ "access": "public"
87
+ },
88
+ "scripts": {
89
+ "build": "tsgo --noEmit && tsup persistence/entities/index.ts domain/schemas/index.ts services/index.ts domain/enum/index.ts domain/types/index.ts --format cjs,esm --no-splitting --tsconfig tsconfig.json --out-dir lib --clean && tsgo -p tsconfig.build.json --emitDeclarationOnly && if [ -f eject-package.bash ]; then pnpm package:eject; fi",
90
+ "clean": "rm -rf lib pnpm.lock.yaml node_modules",
91
+ "docs": "typedoc --out docs *",
92
+ "format": "prettier --ignore-path=.prettierignore --config .prettierrc '**/*.{ts,tsx,json}' --write",
93
+ "lint": "eslint . -c eslint.config.mjs",
94
+ "lint:fix": "eslint . -c eslint.config.mjs --fix",
95
+ "package:eject": "./eject-package.bash",
96
+ "publish:package": "./publish-package.bash",
97
+ "test": "vitest --passWithNoTests"
98
+ }
99
+ }