@opencx/widget-core 4.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 (40) hide show
  1. package/README.md +5 -0
  2. package/dist/__tests__/api-caller.mock.d.ts +1 -0
  3. package/dist/__tests__/context/contact/auth/auto-create-unverified-anonymous.spec.d.ts +0 -0
  4. package/dist/__tests__/context/contact/auth/auto-create-unverified-with-user-data-provided-by-org.spec.d.ts +0 -0
  5. package/dist/__tests__/context/contact/auth/manually-create-unverified-with-user-data-provided-by-user.spec.d.ts +0 -0
  6. package/dist/__tests__/context/contact/auth/with-secure-token.spec.d.ts +0 -0
  7. package/dist/__tests__/context/contact/should-collect-data.spec.d.ts +0 -0
  8. package/dist/__tests__/test-utils.d.ts +419 -0
  9. package/dist/__tests__/utils/Poller.spec.d.ts +1 -0
  10. package/dist/__tests__/utils/PrimitiveState.spec.d.ts +1 -0
  11. package/dist/api/api-caller.d.ts +437 -0
  12. package/dist/api/client.d.ts +12 -0
  13. package/dist/api/schema.d.ts +928 -0
  14. package/dist/context/active-session-polling.ctx.d.ts +25 -0
  15. package/dist/context/contact.ctx.d.ts +30 -0
  16. package/dist/context/message.ctx.d.ts +38 -0
  17. package/dist/context/router.ctx.d.ts +33 -0
  18. package/dist/context/session.ctx.d.ts +119 -0
  19. package/dist/context/storage.ctx.d.ts +15 -0
  20. package/dist/context/widget.ctx.d.ts +27 -0
  21. package/dist/index.cjs +5 -0
  22. package/dist/index.cjs.map +1 -0
  23. package/dist/index.d.ts +15 -0
  24. package/dist/index.js +699 -0
  25. package/dist/index.js.map +1 -0
  26. package/dist/types/agent-or-bot.d.ts +6 -0
  27. package/dist/types/component-name.d.ts +1 -0
  28. package/dist/types/dtos.d.ts +12 -0
  29. package/dist/types/external-storage.d.ts +5 -0
  30. package/dist/types/helpers.d.ts +2 -0
  31. package/dist/types/icons.d.ts +3 -0
  32. package/dist/types/json-value.d.ts +7 -0
  33. package/dist/types/messages.d.ts +55 -0
  34. package/dist/types/widget-config.d.ts +400 -0
  35. package/dist/utils/Poller.d.ts +12 -0
  36. package/dist/utils/PrimitiveState.d.ts +13 -0
  37. package/dist/utils/is-exhaustive.d.ts +1 -0
  38. package/dist/utils/run-catching.d.ts +13 -0
  39. package/dist/utils/uuid.d.ts +1 -0
  40. package/package.json +49 -0
package/dist/index.js ADDED
@@ -0,0 +1,699 @@
1
+ import M from "openapi-fetch";
2
+ import O from "lodash.isequal";
3
+ import { v4 as U } from "uuid";
4
+ const F = (d) => {
5
+ console.log(d.error);
6
+ }, D = (d) => {
7
+ const n = M({
8
+ baseUrl: d.baseUrl
9
+ }), o = {
10
+ onRequest: d.onRequest,
11
+ onResponse: d.onResponse,
12
+ onError: d.onError || F
13
+ };
14
+ return n.use(o), n;
15
+ };
16
+ class T {
17
+ constructor({ config: n }) {
18
+ var g, e;
19
+ this.userToken = null, this.constructClientOptions = (t) => {
20
+ const s = this.config.apiUrl || "https://api.open.cx", i = {
21
+ "X-Bot-Token": this.config.token,
22
+ "Content-Type": "application/json",
23
+ Accept: "application/json",
24
+ Authorization: t ? `Bearer ${t}` : void 0
25
+ };
26
+ return { baseUrl: s, headers: i };
27
+ }, this.createOpenAPIClient = ({
28
+ baseUrl: t,
29
+ headers: s
30
+ }) => D({
31
+ baseUrl: t,
32
+ onRequest: ({ request: i }) => {
33
+ Object.entries(s).forEach(([r, a]) => {
34
+ a && i.headers.set(r, a);
35
+ });
36
+ }
37
+ }), this.setAuthToken = (t) => {
38
+ this.userToken = t;
39
+ const { baseUrl: s, headers: i } = this.constructClientOptions(t);
40
+ this.client = this.createOpenAPIClient({ baseUrl: s, headers: i });
41
+ }, this.getExternalWidgetConfig = async () => await this.client.GET("/backend/widget/v2/config", {
42
+ params: { header: { "X-Bot-Token": this.config.token } }
43
+ }), this.widgetPrelude = async () => await this.client.GET("/backend/widget/v2/prelude", {
44
+ params: { header: { "X-Bot-Token": this.config.token } }
45
+ }), this.sendMessage = async (t, s) => await this.client.POST("/backend/widget/v2/chat/send", {
46
+ body: t,
47
+ signal: s
48
+ }), this.createUnverifiedContact = async (t) => await this.client.POST(
49
+ "/backend/widget/v2/contact/create-unverified",
50
+ {
51
+ params: { header: { "x-bot-token": this.config.token } },
52
+ body: t
53
+ }
54
+ ), this.createSession = async (t) => await this.client.POST("/backend/widget/v2/create-session", {
55
+ body: t
56
+ }), this.pollSessionAndHistory = async ({
57
+ sessionId: t,
58
+ lastMessageTimestamp: s,
59
+ abortSignal: i
60
+ }) => {
61
+ const r = s ? { lastMessageTimestamp: s } : void 0;
62
+ return await this.client.GET("/backend/widget/v2/poll/{sessionId}", {
63
+ params: { path: { sessionId: t }, query: r },
64
+ signal: i
65
+ });
66
+ }, this.getSessions = async ({
67
+ cursor: t,
68
+ filters: s,
69
+ abortSignal: i
70
+ }) => await this.client.GET("/backend/widget/v2/sessions", {
71
+ params: { query: { cursor: t, filters: JSON.stringify(s) } },
72
+ signal: i
73
+ }), this.uploadFile = async ({
74
+ file: t,
75
+ abortSignal: s,
76
+ onProgress: i
77
+ }) => new Promise((r, a) => {
78
+ var m;
79
+ const h = new FormData();
80
+ h.append("file", t);
81
+ const l = new XMLHttpRequest();
82
+ if (s && (s.addEventListener("abort", () => {
83
+ l.abort(), a(new DOMException("Aborted", "AbortError"));
84
+ }), s.aborted)) {
85
+ a(new DOMException("Aborted", "AbortError"));
86
+ return;
87
+ }
88
+ l.upload.addEventListener("progress", (u) => {
89
+ if (u.lengthComputable && i) {
90
+ const x = Math.round(u.loaded / u.total * 100);
91
+ i(x);
92
+ }
93
+ }), l.addEventListener("load", () => {
94
+ if (l.status >= 200 && l.status < 300)
95
+ try {
96
+ const u = JSON.parse(l.responseText);
97
+ r(u);
98
+ } catch (u) {
99
+ a(new Error(`Failed to parse response: ${u}`));
100
+ }
101
+ else
102
+ a(new Error(`Upload failed with status: ${l.status}`));
103
+ }), l.addEventListener("error", () => {
104
+ a(new Error("Network error occurred"));
105
+ }), l.addEventListener("timeout", () => {
106
+ a(new Error("Upload timed out"));
107
+ });
108
+ const { baseUrl: C } = this.constructClientOptions(this.userToken), S = `${C}/backend/widget/v2/upload`;
109
+ l.open("POST", S), l.setRequestHeader("X-Bot-Token", this.config.token), this.userToken ?? ((m = this.config.user) == null ? void 0 : m.token) ? l.setRequestHeader("Authorization", `Bearer ${this.userToken}`) : console.error("User token not set"), l.send(h);
110
+ }), this.vote = async (t) => await this.client.POST("/backend/widget/v2/chat/vote", { body: t }), this.resolveSession = async (t, s) => await this.client.POST("/backend/widget/v2/session/resolve", {
111
+ body: t,
112
+ signal: s
113
+ }), this.createStateCheckpoint = async (t) => await this.client.POST("/backend/widget/v2/checkpoint", {
114
+ body: t
115
+ }), this.config = n, this.userToken = ((g = n.user) == null ? void 0 : g.token) || null;
116
+ const { baseUrl: o, headers: c } = this.constructClientOptions(
117
+ (e = n.user) == null ? void 0 : e.token
118
+ );
119
+ this.client = this.createOpenAPIClient({ baseUrl: o, headers: c });
120
+ }
121
+ }
122
+ class P {
123
+ constructor(n) {
124
+ this.subscribers = /* @__PURE__ */ new Set(), this.get = () => this.state, this.set = (o) => {
125
+ O(this.state, o) || (this.state = o, this.notifySubscribers(o));
126
+ }, this.setPartial = (o) => {
127
+ if (o == null) return;
128
+ const c = { ...this.state, ...o };
129
+ this.set(c);
130
+ }, this.reset = () => {
131
+ this.set(this.initialState);
132
+ }, this.notifySubscribers = (o) => {
133
+ Array.from(this.subscribers).forEach((g) => {
134
+ try {
135
+ g(o);
136
+ } catch (e) {
137
+ console.error(e);
138
+ }
139
+ });
140
+ }, this.subscribe = (o) => (this.subscribers.add(o), () => {
141
+ this.subscribers.delete(o);
142
+ }), this.state = n, this.initialState = n;
143
+ }
144
+ }
145
+ class R {
146
+ constructor() {
147
+ this.state = new P({
148
+ isPolling: !1,
149
+ isError: !1
150
+ }), this.abortController = new AbortController(), this.reset = () => {
151
+ var n;
152
+ this.abortController.abort("Resetting poller"), (n = this.stopPolling) == null || n.call(this), this.stopPolling = null;
153
+ }, this.stopPolling = null, this.startPolling = (n, o) => {
154
+ if (this.stopPolling) return;
155
+ const c = [], g = async () => {
156
+ this.abortController = new AbortController(), this.state.setPartial({ isPolling: !0 });
157
+ try {
158
+ await n(this.abortController.signal);
159
+ } catch (e) {
160
+ if (this.abortController.signal.aborted)
161
+ return;
162
+ console.error("Failed to poll:", e), this.state.setPartial({ isError: !0 });
163
+ } finally {
164
+ this.state.setPartial({ isPolling: !1 });
165
+ }
166
+ this.abortController.signal.aborted ? console.log("Poller aborted, not scheduling anymore") : c.push(setTimeout(g, o));
167
+ };
168
+ g(), this.stopPolling = () => {
169
+ c.forEach(clearTimeout), this.state.reset();
170
+ };
171
+ };
172
+ }
173
+ }
174
+ function _(d) {
175
+ try {
176
+ const n = d();
177
+ return n instanceof Promise ? n.then((o) => ({ data: o })).catch((o) => ({ error: o })) : { data: n };
178
+ } catch (n) {
179
+ return { error: n };
180
+ }
181
+ }
182
+ class L {
183
+ constructor({
184
+ api: n,
185
+ config: o,
186
+ sessionCtx: c,
187
+ messageCtx: g,
188
+ sessionPollingIntervalSeconds: e
189
+ }) {
190
+ this.poller = new R(), this.registerPolling = () => {
191
+ this.sessionCtx.sessionState.subscribe(({ session: t }) => {
192
+ t != null && t.id ? this.poller.startPolling(async (s) => {
193
+ this.hackAndSlash(t.id, s);
194
+ }, this.sessionPollingIntervalSeconds * 1e3) : this.poller.reset();
195
+ });
196
+ }, this.hackAndSlash = async (t, s) => {
197
+ var h;
198
+ this.messageCtx.state.get().messages.length === 0 && this.messageCtx.state.setPartial({ isInitialFetchLoading: !0 });
199
+ const i = this.messageCtx.state.get().messages, r = i.length > 0 ? (h = i[i.length - 1]) == null ? void 0 : h.timestamp : void 0, { data: a } = await this.api.pollSessionAndHistory({
200
+ sessionId: t,
201
+ abortSignal: s,
202
+ lastMessageTimestamp: r
203
+ });
204
+ if (a != null && a.session && (this.sessionCtx.sessionState.setPartial({ session: a.session }), this.sessionCtx.setSessions([a.session])), a != null && a.history && a.history.length > 0) {
205
+ const l = this.messageCtx.state.get().messages, C = a.history.map(this.mapHistoryToMessage).filter(
206
+ (p) => !l.some((S) => S.id === p.id)
207
+ );
208
+ this.messageCtx.state.setPartial({
209
+ messages: [...l, ...C]
210
+ });
211
+ }
212
+ this.messageCtx.state.get().isInitialFetchLoading && this.messageCtx.state.setPartial({ isInitialFetchLoading: !1 });
213
+ }, this.mapHistoryToMessage = (t) => {
214
+ var r, a;
215
+ const s = {
216
+ id: t.publicId,
217
+ timestamp: t.sentAt || "",
218
+ attachments: t.attachments || void 0
219
+ };
220
+ if (t.sender.kind === "user")
221
+ return {
222
+ ...s,
223
+ type: "FROM_USER",
224
+ content: t.content.text || "",
225
+ deliveredAt: t.sentAt || ""
226
+ };
227
+ if (t.sender.kind === "agent")
228
+ return {
229
+ ...s,
230
+ type: "FROM_AGENT",
231
+ component: "agent_message",
232
+ data: {
233
+ message: t.content.text || ""
234
+ },
235
+ agent: {
236
+ name: t.sender.name || "",
237
+ avatar: t.sender.avatar || "",
238
+ id: null,
239
+ isAi: !1
240
+ }
241
+ };
242
+ const i = t.actionCalls && t.actionCalls.length > 0 ? t.actionCalls[t.actionCalls.length - 1] : void 0;
243
+ return {
244
+ ...s,
245
+ type: "FROM_BOT",
246
+ component: "bot_message",
247
+ agent: {
248
+ id: null,
249
+ name: ((r = this.config.bot) == null ? void 0 : r.name) || "",
250
+ isAi: !0,
251
+ avatar: ((a = this.config.bot) == null ? void 0 : a.avatar) || ""
252
+ },
253
+ data: {
254
+ message: t.content.text || "",
255
+ action: i ? {
256
+ name: i.actionName,
257
+ data: this.extractActionResult(i)
258
+ } : void 0
259
+ }
260
+ };
261
+ }, this.extractActionResult = (t) => {
262
+ const s = t.result;
263
+ if (s === null || typeof s != "object") return s;
264
+ if ("responseBodyText" in s && typeof s.responseBodyText == "string") {
265
+ const i = s.responseBodyText, r = _(() => JSON.parse(i)).data;
266
+ if (r) return r;
267
+ }
268
+ return t.result;
269
+ }, this.api = n, this.config = o, this.sessionCtx = c, this.messageCtx = g, this.sessionPollingIntervalSeconds = e, this.registerPolling();
270
+ }
271
+ }
272
+ class B {
273
+ constructor({
274
+ config: n,
275
+ api: o,
276
+ storageCtx: c
277
+ }) {
278
+ var g;
279
+ this.shouldCollectData = () => {
280
+ var e;
281
+ return !!(!((e = this.state.get().contact) != null && e.token) && this.config.collectUserData);
282
+ }, this.autoCreateUnverifiedUserIfNotExists = async () => {
283
+ var e, t, s, i, r, a, h, l, C, p, S, b, m, u;
284
+ if (!((e = this.config.user) != null && e.token)) {
285
+ if (this.config.collectUserData && !((s = (t = this.config.user) == null ? void 0 : t.data) != null && s.email)) {
286
+ if ((i = this.config.extraDataCollectionFields) != null && i.length)
287
+ return;
288
+ const x = await ((r = this.storageCtx) == null ? void 0 : r.getContactToken());
289
+ x && await this.setUnverifiedContact(x);
290
+ return;
291
+ }
292
+ if (!((h = (a = this.config.user) == null ? void 0 : a.data) != null && h.email)) {
293
+ const x = await ((l = this.storageCtx) == null ? void 0 : l.getContactToken());
294
+ if (x) {
295
+ await this.setUnverifiedContact(x);
296
+ return;
297
+ }
298
+ }
299
+ await this.createUnverifiedContact({
300
+ email: (p = (C = this.config.user) == null ? void 0 : C.data) == null ? void 0 : p.email,
301
+ non_verified_name: ((b = (S = this.config.user) == null ? void 0 : S.data) == null ? void 0 : b.name) || "Anonymous",
302
+ non_verified_custom_data: (u = (m = this.config.user) == null ? void 0 : m.data) == null ? void 0 : u.customData
303
+ });
304
+ }
305
+ }, this.createUnverifiedContact = async (e, t) => {
306
+ this.state.setPartial({ extraCollectedData: t });
307
+ try {
308
+ this.state.setPartial({
309
+ isCreatingUnverifiedContact: !0,
310
+ isErrorCreatingUnverifiedContact: !1
311
+ });
312
+ const { data: s } = await this.api.createUnverifiedContact(e);
313
+ s != null && s.token ? await this.setUnverifiedContact(s.token) : this.state.setPartial({ isErrorCreatingUnverifiedContact: !0 });
314
+ } finally {
315
+ this.state.setPartial({ isCreatingUnverifiedContact: !1 });
316
+ }
317
+ }, this.setUnverifiedContact = async (e) => {
318
+ var i, r, a, h;
319
+ const t = await ((i = this.storageCtx) == null ? void 0 : i.getExternalContactId()), s = ((r = this.config.user) == null ? void 0 : r.externalId) || t || U();
320
+ this.api.setAuthToken(e), await ((a = this.storageCtx) == null ? void 0 : a.setContactToken(e)), await ((h = this.storageCtx) == null ? void 0 : h.setExternalContactId(s)), this.state.setPartial({ contact: { token: e, externalId: s } });
321
+ }, this.config = n, this.storageCtx = c, this.api = o, this.state = new P({
322
+ contact: (g = n.user) != null && g.token ? {
323
+ token: n.user.token,
324
+ // Set optional externalId from config... not local storage
325
+ externalId: n.user.externalId
326
+ } : null,
327
+ extraCollectedData: void 0,
328
+ isCreatingUnverifiedContact: !1,
329
+ isErrorCreatingUnverifiedContact: !1
330
+ }), this.autoCreateUnverifiedUserIfNotExists();
331
+ }
332
+ }
333
+ function I() {
334
+ return U();
335
+ }
336
+ class $ {
337
+ constructor({
338
+ config: n,
339
+ api: o,
340
+ contactCtx: c,
341
+ sessionsPollingIntervalSeconds: g
342
+ }) {
343
+ this.sessionsRefresher = new R(), this.sessionState = new P({
344
+ session: null,
345
+ isCreatingSession: !1,
346
+ isResolvingSession: !1
347
+ }), this.sessionsState = new P({
348
+ data: [],
349
+ cursor: void 0,
350
+ isLastPage: !1,
351
+ didStartInitialFetch: !1,
352
+ /**
353
+ * Initialize this as `true` so it always starts loading until the first fetch is done
354
+ */
355
+ isInitialFetchLoading: !0
356
+ }), this.reset = async () => {
357
+ this.sessionState.reset();
358
+ }, this.registerSessionsRefresherWrapper = () => {
359
+ var e;
360
+ // If the widget config was initially provided with a contact token, no state change would be triggered, so we just fetch
361
+ (e = this.contactCtx.state.get().contact) != null && e.token && !this.sessionsState.get().didStartInitialFetch ? this.registerSessionsRefresher() : this.contactCtx.state.subscribe(({ contact: t }) => {
362
+ t != null && t.token && !this.sessionsState.get().didStartInitialFetch && this.registerSessionsRefresher();
363
+ });
364
+ }, this.registerSessionsRefresher = () => {
365
+ this.sessionsRefresher.startPolling(async () => {
366
+ this.sessionsState.get().didStartInitialFetch === !1 && this.sessionsState.setPartial({ didStartInitialFetch: !0 }), await this.refreshSessions(), this.sessionsState.get().isInitialFetchLoading === !0 && this.sessionsState.setPartial({ isInitialFetchLoading: !1 });
367
+ }, this.sessionsPollingIntervalSeconds * 1e3);
368
+ }, this.createSession = async () => {
369
+ var r;
370
+ this.sessionState.setPartial({ session: null, isCreatingSession: !0 });
371
+ const e = (r = this.contactCtx.state.get().contact) == null ? void 0 : r.externalId, t = {
372
+ ...this.config.sessionCustomData,
373
+ ...e ? { external_id: e } : {}
374
+ }, { data: s, error: i } = await this.api.createSession({
375
+ customData: Object.keys(t).length > 0 ? t : void 0
376
+ });
377
+ return s ? (this.sessionState.setPartial({ session: s, isCreatingSession: !1 }), s) : (this.sessionState.setPartial({ isCreatingSession: !1 }), console.error("Failed to create session:", i), null);
378
+ }, this.loadMoreSessions = async () => {
379
+ if (this.sessionsState.get().isLastPage) return;
380
+ const { data: e } = await this.getSessions({
381
+ cursor: this.sessionsState.get().cursor
382
+ });
383
+ if (e) {
384
+ const s = [...this.sessionsState.get().data, ...e.items].filter(
385
+ (i, r, a) => r === a.findIndex((h) => i.id === h.id)
386
+ );
387
+ this.sessionsState.setPartial({
388
+ data: s,
389
+ cursor: e.next || void 0,
390
+ isLastPage: e.next === null
391
+ });
392
+ }
393
+ }, this.getSessions = async ({ cursor: e }) => {
394
+ var s, i;
395
+ if (!((s = this.contactCtx.state.get().contact) != null && s.token)) return { data: null };
396
+ const t = (i = this.contactCtx.state.get().contact) == null ? void 0 : i.externalId;
397
+ return await this.api.getSessions({
398
+ cursor: e,
399
+ filters: t ? {
400
+ external_id: t
401
+ } : {}
402
+ });
403
+ }, this.setSessions = (e) => {
404
+ const t = [...e, ...this.sessionsState.get().data].filter(
405
+ (s, i, r) => i === r.findIndex((a) => s.id === a.id)
406
+ );
407
+ this.sessionsState.setPartial({ data: t });
408
+ }, this.refreshSessions = async () => {
409
+ const { data: e } = await this.getSessions({ cursor: void 0 });
410
+ e && this.setSessions(e.items);
411
+ }, this.resolveSession = async () => {
412
+ const e = this.sessionState.get().session;
413
+ if (!e || !e.isOpened)
414
+ return { success: !1, error: "Session is not opened" };
415
+ this.sessionState.setPartial({ isResolvingSession: !0 });
416
+ const { data: t, error: s } = await this.api.resolveSession({
417
+ session_id: e.id
418
+ });
419
+ return t ? (this.sessionState.setPartial({ session: t, isResolvingSession: !1 }), { success: !0, data: t }) : (this.sessionState.setPartial({ isResolvingSession: !1 }), { success: !1, error: s });
420
+ }, this.createStateCheckpoint = async (e) => {
421
+ var r;
422
+ const t = (r = this.sessionState.get().session) == null ? void 0 : r.id;
423
+ if (!t) return;
424
+ const { data: s, error: i } = await this.api.createStateCheckpoint({
425
+ session_id: t,
426
+ payload: e
427
+ });
428
+ return s ? { data: s } : { success: !1, error: i };
429
+ }, this.config = n, this.api = o, this.contactCtx = c, this.sessionsPollingIntervalSeconds = g, this.registerSessionsRefresherWrapper();
430
+ }
431
+ }
432
+ class q {
433
+ constructor({
434
+ config: n,
435
+ api: o,
436
+ sessionCtx: c,
437
+ contactCtx: g
438
+ }) {
439
+ this.state = new P({
440
+ messages: [],
441
+ isSendingMessage: !1,
442
+ lastAIResMightSolveUserIssue: !1,
443
+ isInitialFetchLoading: !1
444
+ }), this.sendMessageAbortController = new AbortController(), this.reset = () => {
445
+ this.sendMessageAbortController.abort("Resetting chat"), this.state.reset();
446
+ }, this.sendMessage = async (e) => {
447
+ var t, s, i, r, a, h, l, C;
448
+ try {
449
+ if (!e.content.trim() && (!e.attachments || e.attachments.length === 0)) {
450
+ console.warn(
451
+ "Cannot send an empty message of no content or attachments"
452
+ );
453
+ return;
454
+ }
455
+ const p = this.state.get().isSendingMessage, S = ((t = this.sessionCtx.sessionState.get().session) == null ? void 0 : t.assignee.kind) === "ai", b = this.state.get().messages, m = b.length > 0 ? b[b.length - 1] : void 0;
456
+ if (S && p || // If last message is from user, then bot response did not arrive yet
457
+ S && (m == null ? void 0 : m.type) === "FROM_USER") {
458
+ console.warn("Cannot send messages while awaiting AI response");
459
+ return;
460
+ }
461
+ this.sendMessageAbortController = new AbortController(), this.state.setPartial({ lastAIResMightSolveUserIssue: !1 }), this.state.setPartial({ isSendingMessage: !0 });
462
+ const u = this.toUserMessage(
463
+ e.content.trim(),
464
+ e.attachments || void 0
465
+ ), x = this.state.get().messages;
466
+ if (this.state.setPartial({
467
+ messages: [...x, u]
468
+ }), !((s = this.sessionCtx.sessionState.get().session) != null && s.id)) {
469
+ if (!await this.sessionCtx.createSession()) {
470
+ console.error("Failed to create session");
471
+ return;
472
+ }
473
+ this.sessionCtx.refreshSessions();
474
+ }
475
+ const y = (i = this.sessionCtx.sessionState.get().session) == null ? void 0 : i.id;
476
+ if (!y) return;
477
+ const { data: f } = await this.api.sendMessage(
478
+ {
479
+ uuid: u.id,
480
+ bot_token: this.config.token,
481
+ headers: this.config.headers,
482
+ query_params: this.config.queryParams,
483
+ body_properties: this.config.bodyProperties,
484
+ session_id: y,
485
+ content: u.content,
486
+ attachments: e.attachments,
487
+ clientContext: this.config.context,
488
+ custom_data: {
489
+ ...this.config.messageCustomData || {},
490
+ ...e.customData || {}
491
+ },
492
+ language: this.config.language,
493
+ exit_mode_prompt: e.exitModePrompt
494
+ },
495
+ this.sendMessageAbortController.signal
496
+ );
497
+ if (f != null && f.success) {
498
+ const w = this.toBotMessage(f);
499
+ if (w) {
500
+ const k = this.state.get().messages;
501
+ if (!!k.some(
502
+ (A) => A.id === w.id
503
+ )) {
504
+ this.state.setPartial({
505
+ lastAIResMightSolveUserIssue: ((r = f.autopilotResponse) == null ? void 0 : r.mightSolveUserIssue) || ((a = f.uiResponse) == null ? void 0 : a.mightSolveUserIssue)
506
+ });
507
+ return;
508
+ }
509
+ this.state.setPartial({
510
+ messages: [...k, w],
511
+ lastAIResMightSolveUserIssue: ((h = f.autopilotResponse) == null ? void 0 : h.mightSolveUserIssue) || ((l = f.uiResponse) == null ? void 0 : l.mightSolveUserIssue)
512
+ });
513
+ }
514
+ f.session && this.sessionCtx.sessionState.setPartial({ session: f.session });
515
+ } else {
516
+ const w = this.toBotErrorMessage(
517
+ ((C = f == null ? void 0 : f.error) == null ? void 0 : C.message) || "Unknown error occurred"
518
+ ), k = this.state.get().messages;
519
+ this.state.setPartial({
520
+ messages: [...k, w]
521
+ });
522
+ }
523
+ } catch (p) {
524
+ this.sendMessageAbortController.signal.aborted || console.error("Failed to send message:", p);
525
+ } finally {
526
+ this.state.setPartial({ isSendingMessage: !1 });
527
+ }
528
+ }, this.toUserMessage = (e, t) => {
529
+ const s = (() => {
530
+ const i = this.contactCtx.state.get().extraCollectedData;
531
+ return this.state.get().messages.length === 0 && i && Object.keys(i).length > 0 ? `${Object.entries(i).filter(([a, h]) => !!h).map(([a, h]) => `${a}: ${h}`).join(`
532
+ `)}
533
+
534
+ ${e}` : e;
535
+ })();
536
+ return {
537
+ id: I(),
538
+ type: "FROM_USER",
539
+ content: s,
540
+ deliveredAt: (/* @__PURE__ */ new Date()).toISOString(),
541
+ attachments: t,
542
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
543
+ };
544
+ }, this.toBotMessage = (e) => {
545
+ var t;
546
+ return e.success && e.autopilotResponse ? {
547
+ type: "FROM_BOT",
548
+ id: e.autopilotResponse.id || I(),
549
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
550
+ component: "bot_message",
551
+ agent: this.config.bot ? {
552
+ name: this.config.bot.name || "",
553
+ isAi: !0,
554
+ avatar: this.config.bot.avatar || "",
555
+ id: null
556
+ } : void 0,
557
+ data: {
558
+ message: e.autopilotResponse.value.content,
559
+ action: (t = e.uiResponse) != null && t.value.name ? {
560
+ name: e.uiResponse.value.name,
561
+ data: e.uiResponse.value.request_response
562
+ } : void 0
563
+ }
564
+ } : null;
565
+ }, this.toBotErrorMessage = (e) => ({
566
+ type: "FROM_BOT",
567
+ id: I(),
568
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
569
+ component: "TEXT",
570
+ data: {
571
+ message: e,
572
+ variant: "error",
573
+ action: void 0
574
+ }
575
+ }), this.config = n, this.api = o, this.sessionCtx = c, this.contactCtx = g;
576
+ }
577
+ }
578
+ class N {
579
+ constructor({
580
+ config: n,
581
+ contactCtx: o,
582
+ sessionCtx: c,
583
+ resetChat: g
584
+ }) {
585
+ var e;
586
+ this.registerRoutingListener = () => {
587
+ this.contactCtx.state.subscribe(({ contact: t }) => {
588
+ var s;
589
+ t != null && t.token && this.state.get().screen === "welcome" && this.state.setPartial({
590
+ screen: (s = this.config.router) != null && s.chatScreenOnly ? "chat" : "sessions"
591
+ });
592
+ }), this.sessionCtx.sessionsState.subscribe(
593
+ ({ isInitialFetchLoading: t, data: s }) => {
594
+ var i, r, a, h;
595
+ if ((i = this.config.router) != null && i.chatScreenOnly && // Do not route to a chat if we are currently inside one already
596
+ // This also applies to newly created sessions; the new session will be in `sessionState` before it is refreshed and included in `sessionsState`
597
+ !((r = this.sessionCtx.sessionState.get().session) != null && r.id)) {
598
+ const l = (a = s.find((C) => C.isOpened)) == null ? void 0 : a.id;
599
+ return l ? this.toChatScreen(l) : void 0;
600
+ }
601
+ s.length || ((h = this.config.router) == null ? void 0 : h.goToChatIfNoSessions) !== !1 && !t && this.state.get().screen !== "chat" && this.toChatScreen();
602
+ }
603
+ );
604
+ }, this.toSessionsScreen = () => {
605
+ this.resetChat(), this.state.setPartial({ screen: "sessions" });
606
+ }, this.toChatScreen = (t) => {
607
+ if (this.resetChat(), t) {
608
+ const s = this.sessionCtx.sessionsState.get().data.find((i) => i.id === t);
609
+ if (!s) return;
610
+ this.sessionCtx.sessionState.setPartial({ session: s });
611
+ }
612
+ this.state.setPartial({ screen: "chat" });
613
+ }, this.config = n, this.contactCtx = o, this.sessionCtx = c, this.resetChat = g, this.state = new P({
614
+ screen: this.contactCtx.shouldCollectData() ? "welcome" : (e = this.config.router) != null && e.chatScreenOnly ? "chat" : "sessions"
615
+ }), this.registerRoutingListener();
616
+ }
617
+ }
618
+ class H {
619
+ constructor({
620
+ storage: n,
621
+ config: o
622
+ }) {
623
+ this.KEYS = {
624
+ contactToken: (c) => `opencx-widget:org-token-${c}:contact-token`,
625
+ externalContactId: (c) => `opencx-widget:org-token-${c}:external-contact-id`
626
+ }, this.setContactToken = async (c) => {
627
+ await this.storage.set(this.KEYS.contactToken(this.config.token), c);
628
+ }, this.getContactToken = async () => this.storage.get(this.KEYS.contactToken(this.config.token)), this.setExternalContactId = async (c) => {
629
+ await this.storage.set(this.KEYS.externalContactId(this.config.token), c);
630
+ }, this.getExternalContactId = async () => this.storage.get(this.KEYS.externalContactId(this.config.token)), this.storage = n, this.config = o;
631
+ }
632
+ }
633
+ const v = class v {
634
+ constructor({
635
+ config: n,
636
+ storage: o,
637
+ modes: c
638
+ }) {
639
+ if (this.modes = [], this.resetChat = () => {
640
+ this.sessionCtx.reset(), this.messageCtx.reset();
641
+ }, !v.pollingIntervalsSeconds)
642
+ throw Error(
643
+ "Widget polling values are not defined, did you call WidgetCtx.initialize()"
644
+ );
645
+ this.config = n, this.api = new T({ config: n }), this.storageCtx = o ? new H({ storage: o, config: n }) : void 0, this.modes = c, this.contactCtx = new B({
646
+ api: this.api,
647
+ config: this.config,
648
+ storageCtx: this.storageCtx
649
+ }), this.sessionCtx = new $({
650
+ config: this.config,
651
+ api: this.api,
652
+ contactCtx: this.contactCtx,
653
+ sessionsPollingIntervalSeconds: v.pollingIntervalsSeconds.sessions
654
+ }), this.messageCtx = new q({
655
+ config: this.config,
656
+ api: this.api,
657
+ sessionCtx: this.sessionCtx,
658
+ contactCtx: this.contactCtx
659
+ }), this.activeSessionPollingCtx = new L({
660
+ api: this.api,
661
+ config: this.config,
662
+ sessionCtx: this.sessionCtx,
663
+ messageCtx: this.messageCtx,
664
+ sessionPollingIntervalSeconds: v.pollingIntervalsSeconds.session
665
+ }), this.routerCtx = new N({
666
+ config: this.config,
667
+ contactCtx: this.contactCtx,
668
+ sessionCtx: this.sessionCtx,
669
+ resetChat: this.resetChat
670
+ });
671
+ }
672
+ };
673
+ v.pollingIntervalsSeconds = null, v.initialize = async ({
674
+ config: n,
675
+ storage: o
676
+ }) => {
677
+ var g, e, t;
678
+ const c = await new T({
679
+ config: n
680
+ }).getExternalWidgetConfig();
681
+ return v.pollingIntervalsSeconds = {
682
+ session: ((g = c.data) == null ? void 0 : g.sessionPollingIntervalSeconds) || 10,
683
+ sessions: ((e = c.data) == null ? void 0 : e.sessionsPollingIntervalSeconds) || 60
684
+ }, new v({
685
+ config: n,
686
+ storage: o,
687
+ modes: ((t = c.data) == null ? void 0 : t.modes) || []
688
+ });
689
+ };
690
+ let E = v;
691
+ function z(d, n) {
692
+ console.error(`Missing case for ${d} in ${n}`);
693
+ }
694
+ export {
695
+ P as PrimitiveState,
696
+ E as WidgetCtx,
697
+ z as isExhaustive
698
+ };
699
+ //# sourceMappingURL=index.js.map