@digisglobal/omnivox-sdk 0.5.0 → 0.6.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.
package/dist/index.cjs CHANGED
@@ -24,14 +24,19 @@ __export(index_exports, {
24
24
  buildPageParams: () => buildPageParams,
25
25
  createAgentsModule: () => createAgentsModule,
26
26
  createApiKeyTokenProvider: () => createApiKeyTokenProvider,
27
+ createAttachmentsModule: () => createAttachmentsModule,
27
28
  createAuthMeModule: () => createAuthMeModule,
29
+ createCannedResponsesModule: () => createCannedResponsesModule,
28
30
  createChannelsModule: () => createChannelsModule,
29
31
  createContactsModule: () => createContactsModule,
30
32
  createConversationsModule: () => createConversationsModule,
31
33
  createMessagesModule: () => createMessagesModule,
32
34
  createOmnivoxClient: () => createOmnivoxClient,
33
35
  createSessionTokenProvider: () => createSessionTokenProvider,
36
+ createTemplatesModule: () => createTemplatesModule,
34
37
  createTenantsModule: () => createTenantsModule,
38
+ createWhatsAppBusinessAccountsModule: () => createWhatsAppBusinessAccountsModule,
39
+ createWsClient: () => createWsClient,
35
40
  iteratePages: () => iteratePages
36
41
  });
37
42
  module.exports = __toCommonJS(index_exports);
@@ -64,6 +69,11 @@ async function parseResponse(res) {
64
69
  }
65
70
  return text ? JSON.parse(text) : void 0;
66
71
  }
72
+ async function parseEnvelopedResponse(res) {
73
+ if (NO_BODY_STATUSES.includes(res.status)) return void 0;
74
+ const envelope = await parseResponse(res);
75
+ return envelope.data;
76
+ }
67
77
 
68
78
  // src/auth/auth-me.ts
69
79
  function createAuthMeModule(options) {
@@ -163,6 +173,9 @@ var getConversationsControllerListLobbyUrl = (params) => {
163
173
  var getConversationsControllerPickupLobbyEntryUrl = (entryId) => {
164
174
  return `/api/v1/conversations/lobby/${entryId}/pickup`;
165
175
  };
176
+ var getConversationsControllerGetWhatsAppRecoveryOptionsUrl = (id) => {
177
+ return `/api/v1/conversations/${id}/whatsapp-recovery-options`;
178
+ };
166
179
  var getConversationsControllerFindOneUrl = (id) => {
167
180
  return `/api/v1/conversations/${id}`;
168
181
  };
@@ -179,6 +192,9 @@ var getMessagesControllerListUrl = (params) => {
179
192
  const stringifiedParams = normalizedParams.toString();
180
193
  return stringifiedParams.length > 0 ? `/api/v1/messages?${stringifiedParams}` : `/api/v1/messages`;
181
194
  };
195
+ var getMessagesControllerSendTemplateUrl = () => {
196
+ return `/api/v1/messages/send-template`;
197
+ };
182
198
  var getMessagesControllerGetByIdUrl = (id, params) => {
183
199
  const normalizedParams = new URLSearchParams();
184
200
  Object.entries(params || {}).forEach(([key, value]) => {
@@ -207,6 +223,12 @@ var getChannelsControllerUpdateUrl = (id, channelId) => {
207
223
  var getChannelsControllerVerifyUrl = (id, channelId) => {
208
224
  return `/api/v1/tenants/${id}/channels/${channelId}/verify`;
209
225
  };
226
+ var getWhatsAppBusinessAccountsControllerListUrl = (id) => {
227
+ return `/api/v1/tenants/${id}/whatsapp-business-accounts`;
228
+ };
229
+ var getWhatsAppBusinessAccountsControllerFindOneUrl = (id, wabaRowId) => {
230
+ return `/api/v1/tenants/${id}/whatsapp-business-accounts/${wabaRowId}`;
231
+ };
210
232
  var getAgentsControllerCreateUrl = () => {
211
233
  return `/api/v1/agents`;
212
234
  };
@@ -223,6 +245,68 @@ var getAgentsControllerListUrl = (params) => {
223
245
  var getAgentsControllerFindOneUrl = (id) => {
224
246
  return `/api/v1/agents/${id}`;
225
247
  };
248
+ var getTemplatesControllerAuthorDraftUrl = (id, wabaId) => {
249
+ return `/api/v1/tenants/${id}/whatsapp-business-accounts/${wabaId}/templates`;
250
+ };
251
+ var getTemplatesControllerImportApprovedUrl = (id, wabaId) => {
252
+ return `/api/v1/tenants/${id}/whatsapp-business-accounts/${wabaId}/templates/import`;
253
+ };
254
+ var getTemplatesControllerForkDraftUrl = (id, wabaId, templateId) => {
255
+ return `/api/v1/tenants/${id}/whatsapp-business-accounts/${wabaId}/templates/${templateId}`;
256
+ };
257
+ var getTemplatesControllerGetHistoryUrl = (id, wabaId, templateId) => {
258
+ return `/api/v1/tenants/${id}/whatsapp-business-accounts/${wabaId}/templates/${templateId}/history`;
259
+ };
260
+ var getTemplatesControllerSubmitUrl = (id, wabaId, templateId) => {
261
+ return `/api/v1/tenants/${id}/whatsapp-business-accounts/${wabaId}/templates/${templateId}/submit`;
262
+ };
263
+ var getEmailTemplatesControllerCreateUrl = () => {
264
+ return `/api/v1/templates/email`;
265
+ };
266
+ var getEmailTemplatesControllerListUrl = (params) => {
267
+ const normalizedParams = new URLSearchParams();
268
+ Object.entries(params || {}).forEach(([key, value]) => {
269
+ if (value !== void 0) {
270
+ normalizedParams.append(key, value === null ? "null" : String(value));
271
+ }
272
+ });
273
+ const stringifiedParams = normalizedParams.toString();
274
+ return stringifiedParams.length > 0 ? `/api/v1/templates/email?${stringifiedParams}` : `/api/v1/templates/email`;
275
+ };
276
+ var getEmailTemplatesControllerGetByIdUrl = (id) => {
277
+ return `/api/v1/templates/email/${id}`;
278
+ };
279
+ var getEmailTemplatesControllerPatchUrl = (id) => {
280
+ return `/api/v1/templates/email/${id}`;
281
+ };
282
+ var getEmailTemplatesControllerDeleteUrl = (id) => {
283
+ return `/api/v1/templates/email/${id}`;
284
+ };
285
+ var getCannedResponsesControllerCreateUrl = () => {
286
+ return `/api/v1/canned-responses`;
287
+ };
288
+ var getCannedResponsesControllerListUrl = (params) => {
289
+ const normalizedParams = new URLSearchParams();
290
+ Object.entries(params || {}).forEach(([key, value]) => {
291
+ if (value !== void 0) {
292
+ normalizedParams.append(key, value === null ? "null" : String(value));
293
+ }
294
+ });
295
+ const stringifiedParams = normalizedParams.toString();
296
+ return stringifiedParams.length > 0 ? `/api/v1/canned-responses?${stringifiedParams}` : `/api/v1/canned-responses`;
297
+ };
298
+ var getCannedResponsesControllerPatchUrl = (id) => {
299
+ return `/api/v1/canned-responses/${id}`;
300
+ };
301
+ var getCannedResponsesControllerDeleteUrl = (id) => {
302
+ return `/api/v1/canned-responses/${id}`;
303
+ };
304
+ var getAttachmentsControllerUploadUrl = () => {
305
+ return `/api/v1/attachments`;
306
+ };
307
+ var getAttachmentsControllerGetDownloadUrlUrl = (id) => {
308
+ return `/api/v1/attachments/${id}/url`;
309
+ };
226
310
 
227
311
  // src/pagination/pagination.ts
228
312
  var MAX_PAGE_SIZE = 100;
@@ -623,6 +707,18 @@ function createConversationsModule(options) {
623
707
  const url = `${baseUrl}${path}`;
624
708
  const res = await fetch(url, buildInit("POST", input));
625
709
  return parseResponse(res);
710
+ },
711
+ /**
712
+ * List APPROVED + SENDABLE WhatsApp templates eligible to re-open an
713
+ * expired 24-hour customer-service window for the given conversation.
714
+ * Returns an empty array when the channel is not WhatsApp or has no linked WABA.
715
+ * Calls GET /api/v1/conversations/:id/whatsapp-recovery-options.
716
+ */
717
+ async whatsappRecoveryOptions(id) {
718
+ const path = getConversationsControllerGetWhatsAppRecoveryOptionsUrl(id);
719
+ const url = `${baseUrl}${path}`;
720
+ const res = await fetch(url, buildInit("GET"));
721
+ return parseResponse(res);
626
722
  }
627
723
  };
628
724
  }
@@ -689,6 +785,415 @@ function createMessagesModule(options) {
689
785
  const url = `${baseUrl}${path}`;
690
786
  const res = await fetch(url, buildInit("DELETE"));
691
787
  await parseResponse(res);
788
+ },
789
+ /**
790
+ * Send a template message in a conversation (WhatsApp or Email).
791
+ * Branches on the conversation channel type — WhatsApp uses positional
792
+ * `variables`; Email uses named `emailVariables`.
793
+ * Calls POST /api/v1/messages/send-template.
794
+ */
795
+ async sendTemplate(input) {
796
+ const path = getMessagesControllerSendTemplateUrl();
797
+ const url = `${baseUrl}${path}`;
798
+ const res = await fetch(url, buildInit("POST", input));
799
+ return parseResponse(res);
800
+ }
801
+ };
802
+ }
803
+
804
+ // src/templates/templates.ts
805
+ function createTemplatesModule(options) {
806
+ const baseUrl = options.baseUrl;
807
+ function buildInit(method, body) {
808
+ return {
809
+ method,
810
+ credentials: options.getCredentials(),
811
+ headers: {
812
+ "Content-Type": "application/json",
813
+ ...options.getHeaders()
814
+ },
815
+ ...body !== void 0 ? { body: JSON.stringify(body) } : {}
816
+ };
817
+ }
818
+ return {
819
+ /**
820
+ * WhatsApp template submodule — WABA-scoped operations.
821
+ */
822
+ whatsapp: {
823
+ /**
824
+ * Author a new DRAFT template (version 1) under a WhatsApp Business Account.
825
+ * Calls POST /api/v1/tenants/:id/whatsapp-business-accounts/:wabaId/templates.
826
+ */
827
+ async authorDraft(tenantId, wabaId, input) {
828
+ const path = getTemplatesControllerAuthorDraftUrl(tenantId, wabaId);
829
+ const url = `${baseUrl}${path}`;
830
+ const res = await fetch(url, buildInit("POST", input));
831
+ return parseEnvelopedResponse(res);
832
+ },
833
+ /**
834
+ * Import all APPROVED templates from the WABA into the system (immediately sendable).
835
+ * Calls POST /api/v1/tenants/:id/whatsapp-business-accounts/:wabaId/templates/import.
836
+ */
837
+ async importApproved(tenantId, wabaId) {
838
+ const path = getTemplatesControllerImportApprovedUrl(tenantId, wabaId);
839
+ const url = `${baseUrl}${path}`;
840
+ const res = await fetch(url, buildInit("POST"));
841
+ return parseEnvelopedResponse(res);
842
+ },
843
+ /**
844
+ * Fork a new DRAFT version from the latest version of a template (update).
845
+ * Calls PATCH /api/v1/tenants/:id/whatsapp-business-accounts/:wabaId/templates/:templateId.
846
+ */
847
+ async update(tenantId, wabaId, templateId, input) {
848
+ const path = getTemplatesControllerForkDraftUrl(
849
+ tenantId,
850
+ wabaId,
851
+ templateId
852
+ );
853
+ const url = `${baseUrl}${path}`;
854
+ const res = await fetch(url, buildInit("PATCH", input));
855
+ return parseEnvelopedResponse(res);
856
+ },
857
+ /**
858
+ * List all versions and status events for a template in chronological order.
859
+ * Calls GET /api/v1/tenants/:id/whatsapp-business-accounts/:wabaId/templates/:templateId/history.
860
+ */
861
+ async history(tenantId, wabaId, templateId) {
862
+ const path = getTemplatesControllerGetHistoryUrl(
863
+ tenantId,
864
+ wabaId,
865
+ templateId
866
+ );
867
+ const url = `${baseUrl}${path}`;
868
+ const res = await fetch(url, buildInit("GET"));
869
+ return parseEnvelopedResponse(res);
870
+ },
871
+ /**
872
+ * Submit a DRAFT template version to Meta for review and approval (DRAFT → PENDING_APPROVAL).
873
+ * Calls POST /api/v1/tenants/:id/whatsapp-business-accounts/:wabaId/templates/:templateId/submit.
874
+ */
875
+ async submit(tenantId, wabaId, templateId) {
876
+ const path = getTemplatesControllerSubmitUrl(
877
+ tenantId,
878
+ wabaId,
879
+ templateId
880
+ );
881
+ const url = `${baseUrl}${path}`;
882
+ const res = await fetch(url, buildInit("POST"));
883
+ return parseEnvelopedResponse(res);
884
+ }
885
+ },
886
+ /**
887
+ * Email template submodule — tenant-scoped operations.
888
+ */
889
+ email: {
890
+ /**
891
+ * Create an Email template for the authenticated tenant.
892
+ * Calls POST /api/v1/templates/email.
893
+ */
894
+ async create(input) {
895
+ const path = getEmailTemplatesControllerCreateUrl();
896
+ const url = `${baseUrl}${path}`;
897
+ const res = await fetch(url, buildInit("POST", input));
898
+ return parseEnvelopedResponse(res);
899
+ },
900
+ /**
901
+ * List Email templates for the authenticated tenant (paginated).
902
+ * Calls GET /api/v1/templates/email.
903
+ */
904
+ async list(opts) {
905
+ const { page, pageSize } = buildPageParams(opts);
906
+ const path = getEmailTemplatesControllerListUrl({ page, pageSize });
907
+ const url = `${baseUrl}${path}`;
908
+ const res = await fetch(url, buildInit("GET"));
909
+ return parseResponse(res);
910
+ },
911
+ /**
912
+ * Get an Email template by id.
913
+ * Calls GET /api/v1/templates/email/:id.
914
+ */
915
+ async getById(id) {
916
+ const path = getEmailTemplatesControllerGetByIdUrl(id);
917
+ const url = `${baseUrl}${path}`;
918
+ const res = await fetch(url, buildInit("GET"));
919
+ return parseEnvelopedResponse(res);
920
+ },
921
+ /**
922
+ * Partially update an Email template — name, subject, htmlBody, textBody, and/or variableSchema; omitted fields unchanged.
923
+ * Calls PATCH /api/v1/templates/email/:id.
924
+ */
925
+ async patch(id, input) {
926
+ const path = getEmailTemplatesControllerPatchUrl(id);
927
+ const url = `${baseUrl}${path}`;
928
+ const res = await fetch(url, buildInit("PATCH", input));
929
+ return parseEnvelopedResponse(res);
930
+ },
931
+ /**
932
+ * Delete an Email template.
933
+ * Calls DELETE /api/v1/templates/email/:id.
934
+ */
935
+ async delete(id) {
936
+ const path = getEmailTemplatesControllerDeleteUrl(id);
937
+ const url = `${baseUrl}${path}`;
938
+ const res = await fetch(url, buildInit("DELETE"));
939
+ if (!res.ok) {
940
+ return parseResponse(res);
941
+ }
942
+ }
943
+ }
944
+ };
945
+ }
946
+
947
+ // src/canned-responses/canned-responses.ts
948
+ function createCannedResponsesModule(options) {
949
+ const baseUrl = options.baseUrl;
950
+ function buildInit(method, body) {
951
+ return {
952
+ method,
953
+ credentials: options.getCredentials(),
954
+ headers: {
955
+ "Content-Type": "application/json",
956
+ ...options.getHeaders()
957
+ },
958
+ ...body !== void 0 ? { body: JSON.stringify(body) } : {}
959
+ };
960
+ }
961
+ return {
962
+ /**
963
+ * List canned responses (paginated, no search filter).
964
+ * pageSize is clamped to 100 per the API contract.
965
+ * Calls GET /api/v1/canned-responses.
966
+ */
967
+ async list(opts) {
968
+ const { page, pageSize } = buildPageParams(opts);
969
+ const path = getCannedResponsesControllerListUrl({ page, pageSize });
970
+ const url = `${baseUrl}${path}`;
971
+ const res = await fetch(url, buildInit("GET"));
972
+ return parseResponse(res);
973
+ },
974
+ /**
975
+ * Search canned responses by title substring or keyword prefix.
976
+ * pageSize is clamped to 100 per the API contract.
977
+ * Calls GET /api/v1/canned-responses?q=<term>.
978
+ */
979
+ async search(opts) {
980
+ const { page, pageSize } = buildPageParams(opts);
981
+ const path = getCannedResponsesControllerListUrl({
982
+ page,
983
+ pageSize,
984
+ q: opts.q
985
+ });
986
+ const url = `${baseUrl}${path}`;
987
+ const res = await fetch(url, buildInit("GET"));
988
+ return parseResponse(res);
989
+ },
990
+ /**
991
+ * Create a canned response.
992
+ * Calls POST /api/v1/canned-responses.
993
+ */
994
+ async create(input) {
995
+ const path = getCannedResponsesControllerCreateUrl();
996
+ const url = `${baseUrl}${path}`;
997
+ const res = await fetch(url, buildInit("POST", input));
998
+ return parseResponse(res);
999
+ },
1000
+ /**
1001
+ * Partially update a canned response — title, body, and/or keywords.
1002
+ * Calls PATCH /api/v1/canned-responses/:id.
1003
+ */
1004
+ async update(id, input) {
1005
+ const path = getCannedResponsesControllerPatchUrl(id);
1006
+ const url = `${baseUrl}${path}`;
1007
+ const res = await fetch(url, buildInit("PATCH", input));
1008
+ return parseResponse(res);
1009
+ },
1010
+ /**
1011
+ * Delete a canned response by id.
1012
+ * Calls DELETE /api/v1/canned-responses/:id.
1013
+ */
1014
+ async delete(id) {
1015
+ const path = getCannedResponsesControllerDeleteUrl(id);
1016
+ const url = `${baseUrl}${path}`;
1017
+ const res = await fetch(url, buildInit("DELETE"));
1018
+ if (!res.ok) {
1019
+ return parseResponse(res);
1020
+ }
1021
+ }
1022
+ };
1023
+ }
1024
+
1025
+ // src/attachments/attachments.ts
1026
+ function createAttachmentsModule(options) {
1027
+ const baseUrl = options.baseUrl;
1028
+ return {
1029
+ /**
1030
+ * Upload a file as a draft attachment.
1031
+ *
1032
+ * Accepts a Blob or File (browser) or a Uint8Array wrapped in a Blob (Node 22+).
1033
+ * Content-Type is intentionally omitted from headers so that fetch (both
1034
+ * Node 22 built-in and browser) can set the multipart/form-data boundary.
1035
+ *
1036
+ * Calls POST /api/v1/attachments.
1037
+ */
1038
+ async upload(file, _opts) {
1039
+ const form = new FormData();
1040
+ const blob = file instanceof Uint8Array ? new Blob([file]) : file;
1041
+ form.append("file", blob);
1042
+ const path = getAttachmentsControllerUploadUrl();
1043
+ const url = `${baseUrl}${path}`;
1044
+ const res = await fetch(url, {
1045
+ method: "POST",
1046
+ credentials: options.getCredentials(),
1047
+ headers: {
1048
+ // No Content-Type — let fetch set multipart boundary.
1049
+ ...options.getHeaders()
1050
+ },
1051
+ body: form
1052
+ });
1053
+ return parseResponse(res);
1054
+ },
1055
+ /**
1056
+ * Get a short-lived presigned download URL for a stored attachment.
1057
+ * The URL is valid for ≤ 15 minutes (NF-09).
1058
+ *
1059
+ * Calls GET /api/v1/attachments/:id/url.
1060
+ */
1061
+ async url(id) {
1062
+ const path = getAttachmentsControllerGetDownloadUrlUrl(id);
1063
+ const fullUrl = `${baseUrl}${path}`;
1064
+ const res = await fetch(fullUrl, {
1065
+ method: "GET",
1066
+ credentials: options.getCredentials(),
1067
+ headers: {
1068
+ ...options.getHeaders()
1069
+ }
1070
+ });
1071
+ return parseResponse(res);
1072
+ }
1073
+ };
1074
+ }
1075
+
1076
+ // src/whatsapp-business-accounts/whatsapp-business-accounts.ts
1077
+ function createWhatsAppBusinessAccountsModule(options) {
1078
+ const baseUrl = options.baseUrl;
1079
+ function buildInit(method) {
1080
+ return {
1081
+ method,
1082
+ credentials: options.getCredentials(),
1083
+ headers: {
1084
+ "Content-Type": "application/json",
1085
+ ...options.getHeaders()
1086
+ }
1087
+ };
1088
+ }
1089
+ return {
1090
+ /**
1091
+ * List all WhatsApp Business Accounts for the tenant.
1092
+ * Returns a non-secret view — no secretRef is exposed.
1093
+ * Calls GET /api/v1/tenants/:id/whatsapp-business-accounts.
1094
+ *
1095
+ * @example
1096
+ * const wabas = await sdk.whatsappBusinessAccounts.list('ten_acmecorp')
1097
+ * // pick wabas[0].id as the wabaId for sdk.templates.whatsapp.*
1098
+ */
1099
+ async list(tenantId) {
1100
+ const path = getWhatsAppBusinessAccountsControllerListUrl(tenantId);
1101
+ const url = `${baseUrl}${path}`;
1102
+ const res = await fetch(url, buildInit("GET"));
1103
+ return parseEnvelopedResponse(res);
1104
+ },
1105
+ /**
1106
+ * Get a single WhatsApp Business Account by its row id.
1107
+ * Returns a non-secret view — no secretRef is exposed.
1108
+ * Calls GET /api/v1/tenants/:id/whatsapp-business-accounts/:wabaRowId.
1109
+ *
1110
+ * @example
1111
+ * const waba = await sdk.whatsappBusinessAccounts.get('ten_acmecorp', 'waba_row_001')
1112
+ */
1113
+ async get(tenantId, wabaRowId) {
1114
+ const path = getWhatsAppBusinessAccountsControllerFindOneUrl(
1115
+ tenantId,
1116
+ wabaRowId
1117
+ );
1118
+ const url = `${baseUrl}${path}`;
1119
+ const res = await fetch(url, buildInit("GET"));
1120
+ return parseEnvelopedResponse(res);
1121
+ }
1122
+ };
1123
+ }
1124
+
1125
+ // src/ws/ws-client.ts
1126
+ var import_socket = require("socket.io-client");
1127
+ function createWsClient(options) {
1128
+ const handshakeAuth = options.auth === void 0 ? void 0 : "apiKey" in options.auth ? { apiKey: options.auth.apiKey } : { token: options.auth.token };
1129
+ const ioOptions = {
1130
+ reconnection: true,
1131
+ reconnectionAttempts: Infinity,
1132
+ reconnectionDelay: 1e3,
1133
+ reconnectionDelayMax: 3e4,
1134
+ randomizationFactor: 0.5,
1135
+ withCredentials: options.withCredentials ?? false
1136
+ };
1137
+ if (handshakeAuth !== void 0) {
1138
+ ioOptions.auth = handshakeAuth;
1139
+ }
1140
+ const socket = (0, import_socket.io)(`${options.baseUrl}/ws/chat`, ioOptions);
1141
+ let currentState = "connecting";
1142
+ const stateListeners = /* @__PURE__ */ new Set();
1143
+ function setState(next) {
1144
+ if (next === currentState) return;
1145
+ currentState = next;
1146
+ for (const listener of stateListeners) {
1147
+ listener(next);
1148
+ }
1149
+ }
1150
+ socket.on("connect", () => {
1151
+ setState("connected");
1152
+ });
1153
+ socket.on("disconnect", () => {
1154
+ setState("disconnected");
1155
+ });
1156
+ socket.on("reconnect_attempt", () => {
1157
+ setState("reconnecting");
1158
+ });
1159
+ socket.on("reconnect", () => {
1160
+ setState("connected");
1161
+ });
1162
+ return {
1163
+ get state() {
1164
+ return currentState;
1165
+ },
1166
+ onStateChange(listener) {
1167
+ stateListeners.add(listener);
1168
+ return () => {
1169
+ stateListeners.delete(listener);
1170
+ };
1171
+ },
1172
+ on(event, handler) {
1173
+ const rawHandler = handler;
1174
+ socket.on(event, rawHandler);
1175
+ return () => {
1176
+ ;
1177
+ socket.off(event, rawHandler);
1178
+ };
1179
+ },
1180
+ joinConversation(conversationId) {
1181
+ socket.emit("conversation:join", { conversationId });
1182
+ },
1183
+ leaveConversation(conversationId) {
1184
+ socket.emit("conversation:leave", { conversationId });
1185
+ },
1186
+ typingStart(conversationId) {
1187
+ socket.emit("typing:start", { conversationId });
1188
+ },
1189
+ typingStop(conversationId) {
1190
+ socket.emit("typing:stop", { conversationId });
1191
+ },
1192
+ markRead(conversationId, lastReadMessageId) {
1193
+ socket.emit("message:read", { conversationId, lastReadMessageId });
1194
+ },
1195
+ disconnect() {
1196
+ socket.disconnect();
692
1197
  }
693
1198
  };
694
1199
  }
@@ -707,7 +1212,35 @@ function createOmnivoxClient(options) {
707
1212
  channels: createChannelsModule(moduleOptions),
708
1213
  contacts: createContactsModule(moduleOptions),
709
1214
  conversations: createConversationsModule(moduleOptions),
710
- messages: createMessagesModule(moduleOptions)
1215
+ messages: createMessagesModule(moduleOptions),
1216
+ templates: createTemplatesModule(moduleOptions),
1217
+ cannedResponses: createCannedResponsesModule(moduleOptions),
1218
+ attachments: createAttachmentsModule(moduleOptions),
1219
+ whatsappBusinessAccounts: createWhatsAppBusinessAccountsModule(moduleOptions),
1220
+ /**
1221
+ * Creates a WebSocket client connected to the `/ws/chat` namespace.
1222
+ *
1223
+ * Call this factory once and reuse the returned client. Reconnection with
1224
+ * exponential backoff + jitter (cap 30 s) is fully automatic.
1225
+ *
1226
+ * For API-key or token auth, pass the credential explicitly:
1227
+ * @example
1228
+ * const ws = client.connect({ apiKey: 'ovx_live_abc' })
1229
+ * ws.on('message:new', (msg) => console.log(msg))
1230
+ * ws.joinConversation('conv_001')
1231
+ *
1232
+ * For session/cookie auth (Console), call with no argument — the token
1233
+ * provider's `getCredentials() === 'include'` signals cookie mode and
1234
+ * `withCredentials` is set automatically:
1235
+ * @example
1236
+ * const ws = client.connect()
1237
+ * ws.on('message:new', (msg) => console.log(msg))
1238
+ */
1239
+ connect: (auth) => createWsClient({
1240
+ baseUrl: options.baseUrl,
1241
+ ...auth !== void 0 ? { auth } : {},
1242
+ withCredentials: options.tokenProvider.getCredentials() === "include"
1243
+ })
711
1244
  };
712
1245
  }
713
1246
 
@@ -738,13 +1271,18 @@ function createSessionTokenProvider() {
738
1271
  buildPageParams,
739
1272
  createAgentsModule,
740
1273
  createApiKeyTokenProvider,
1274
+ createAttachmentsModule,
741
1275
  createAuthMeModule,
1276
+ createCannedResponsesModule,
742
1277
  createChannelsModule,
743
1278
  createContactsModule,
744
1279
  createConversationsModule,
745
1280
  createMessagesModule,
746
1281
  createOmnivoxClient,
747
1282
  createSessionTokenProvider,
1283
+ createTemplatesModule,
748
1284
  createTenantsModule,
1285
+ createWhatsAppBusinessAccountsModule,
1286
+ createWsClient,
749
1287
  iteratePages
750
1288
  });