open-chat-studio-widget 0.8.0 → 0.9.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 (35) hide show
  1. package/README.md +1 -0
  2. package/dist/cjs/{index-Cf6K60f1.js → index-fFSp-Z_h.js} +3 -3
  3. package/dist/cjs/{index-Cf6K60f1.js.map → index-fFSp-Z_h.js.map} +1 -1
  4. package/dist/cjs/loader.cjs.js +2 -2
  5. package/dist/cjs/open-chat-studio-widget.cjs.entry.js +179 -24
  6. package/dist/cjs/open-chat-studio-widget.cjs.entry.js.map +1 -1
  7. package/dist/cjs/open-chat-studio-widget.cjs.js +2 -2
  8. package/dist/cjs/open-chat-studio-widget.entry.cjs.js.map +1 -1
  9. package/dist/collection/components/ocs-chat/ocs-chat.js +82 -4
  10. package/dist/collection/components/ocs-chat/ocs-chat.js.map +1 -1
  11. package/dist/collection/services/chat-session-service.js +95 -20
  12. package/dist/collection/services/chat-session-service.js.map +1 -1
  13. package/dist/collection/services/file-attachment-manager.js +3 -0
  14. package/dist/collection/services/file-attachment-manager.js.map +1 -1
  15. package/dist/components/open-chat-studio-widget.js +179 -23
  16. package/dist/components/open-chat-studio-widget.js.map +1 -1
  17. package/dist/esm/{index-DXf2dIht.js → index-ythTKHg-.js} +3 -3
  18. package/dist/esm/{index-DXf2dIht.js.map → index-ythTKHg-.js.map} +1 -1
  19. package/dist/esm/loader.js +3 -3
  20. package/dist/esm/open-chat-studio-widget.entry.js +179 -24
  21. package/dist/esm/open-chat-studio-widget.entry.js.map +1 -1
  22. package/dist/esm/open-chat-studio-widget.js +3 -3
  23. package/dist/open-chat-studio-widget/open-chat-studio-widget.entry.esm.js.map +1 -1
  24. package/dist/open-chat-studio-widget/open-chat-studio-widget.esm.js +1 -1
  25. package/dist/open-chat-studio-widget/p-2d31a15c.entry.js +4 -0
  26. package/dist/open-chat-studio-widget/p-2d31a15c.entry.js.map +1 -0
  27. package/dist/open-chat-studio-widget/{p-DXf2dIht.js → p-ythTKHg-.js} +2 -2
  28. package/dist/open-chat-studio-widget/{p-DXf2dIht.js.map → p-ythTKHg-.js.map} +1 -1
  29. package/dist/types/components/ocs-chat/ocs-chat.d.ts +14 -0
  30. package/dist/types/components.d.ts +8 -0
  31. package/dist/types/services/chat-session-service.d.ts +23 -0
  32. package/dist/types/services/file-attachment-manager.d.ts +3 -0
  33. package/package.json +1 -1
  34. package/dist/open-chat-studio-widget/p-ff47dabf.entry.js +0 -4
  35. package/dist/open-chat-studio-widget/p-ff47dabf.entry.js.map +0 -1
@@ -4,7 +4,7 @@ import { XMarkIcon, GripDotsVerticalIcon, PlusWithCircleIcon, ArrowsPointingOutI
4
4
  import { renderMarkdownSync as renderMarkdownComplete } from "../../utils/markdown";
5
5
  import { varToPixels } from "../../utils/utils";
6
6
  import { TranslationManager, defaultTranslations } from "../../utils/translations";
7
- import { ChatSessionService } from "../../services/chat-session-service";
7
+ import { ChatSessionService, SessionAccessError } from "../../services/chat-session-service";
8
8
  import { FileAttachmentManager } from "../../services/file-attachment-manager";
9
9
  export class OcsChat {
10
10
  constructor() {
@@ -237,13 +237,15 @@ export class OcsChat {
237
237
  if (this.isSessionBound()) {
238
238
  // Bound to an externally-managed session: the host page is the source of truth.
239
239
  this.activeSessionId = this.sessionId;
240
+ this.applySessionToken(this.sessionToken);
240
241
  }
241
242
  else if (this.persistentSession && this.isLocalStorageAvailable()) {
242
243
  // Always try to load existing session if localStorage is available
243
- const { sessionId, messages } = this.loadSessionFromStorage();
244
+ const { sessionId, messages, sessionToken } = this.loadSessionFromStorage();
244
245
  if (sessionId && messages) {
245
246
  this.activeSessionId = sessionId;
246
247
  this.messages = messages;
248
+ this.applySessionToken(sessionToken);
247
249
  }
248
250
  }
249
251
  this.parseWelcomeMessages();
@@ -292,6 +294,11 @@ export class OcsChat {
292
294
  this.removeButtonEventListeners();
293
295
  window.removeEventListener('resize', this.handleWindowResize);
294
296
  }
297
+ applySessionToken(token) {
298
+ var _a;
299
+ this.currentSessionToken = token;
300
+ (_a = this.chatService) === null || _a === void 0 ? void 0 : _a.setSessionToken(token);
301
+ }
295
302
  getChatService() {
296
303
  if (!this.chatService) {
297
304
  this.chatService = new ChatSessionService({
@@ -301,6 +308,7 @@ export class OcsChat {
301
308
  taskPollingIntervalMs: OcsChat.TASK_POLLING_INTERVAL_MS,
302
309
  taskPollingMaxAttempts: OcsChat.TASK_POLLING_MAX_ATTEMPTS,
303
310
  messagePollingIntervalMs: OcsChat.MESSAGE_POLLING_INTERVAL_MS,
311
+ sessionToken: this.currentSessionToken,
304
312
  });
305
313
  }
306
314
  return this.chatService;
@@ -316,6 +324,27 @@ export class OcsChat {
316
324
  this.saveSessionToStorage();
317
325
  this.scrollToBottom();
318
326
  }
327
+ /**
328
+ * Recover from a rejected session token (403). Unbound widgets discard the
329
+ * dead session/token, show a notice, and start fresh on the next send; bound
330
+ * widgets cannot restart a host-owned session, so they surface an error.
331
+ */
332
+ handleSessionAccessError() {
333
+ this.cleanup();
334
+ this.isLoading = false;
335
+ this.isTyping = false;
336
+ this.isUploadingFiles = false;
337
+ this.typingProgressMessage = '';
338
+ if (this.isSessionBound()) {
339
+ this.addErrorMessage(this.translationManager.get('status.sessionError', 'This chat session is no longer available.'));
340
+ return;
341
+ }
342
+ this.sessionEpoch += 1;
343
+ this.activeSessionId = undefined;
344
+ this.applySessionToken(undefined);
345
+ this.clearSessionStorage();
346
+ this.addErrorMessage(this.translationManager.get('status.sessionExpired', 'Your chat session expired. Starting a new chat — please resend your message.'));
347
+ }
319
348
  handleError(errorText) {
320
349
  // show as system message
321
350
  this.addErrorMessage(errorText);
@@ -388,12 +417,14 @@ export class OcsChat {
388
417
  this.currentPollTaskId = '';
389
418
  }
390
419
  async startSession() {
420
+ var _a;
391
421
  const epoch = this.sessionEpoch;
392
422
  try {
393
423
  this.isLoading = true;
394
424
  const userId = this.getOrGenerateUserId();
395
425
  const requestBody = {
396
426
  chatbot_id: this.chatbotId,
427
+ use_session_token: true,
397
428
  session_data: {
398
429
  source: 'widget',
399
430
  page_url: window.location.href,
@@ -410,6 +441,7 @@ export class OcsChat {
410
441
  if (epoch !== this.sessionEpoch)
411
442
  return;
412
443
  this.activeSessionId = data.session_id;
444
+ this.applySessionToken((_a = data.session_token) !== null && _a !== void 0 ? _a : undefined);
413
445
  this.saveSessionToStorage();
414
446
  this.startMessagePolling();
415
447
  }
@@ -442,6 +474,10 @@ export class OcsChat {
442
474
  catch (error) {
443
475
  if (epoch !== this.sessionEpoch)
444
476
  return;
477
+ if (error instanceof SessionAccessError) {
478
+ this.handleSessionAccessError();
479
+ return;
480
+ }
445
481
  console.warn('Failed to load chat history:', error);
446
482
  }
447
483
  this.startMessagePolling();
@@ -457,8 +493,12 @@ export class OcsChat {
457
493
  sessionId: this.activeSessionId,
458
494
  participantId: this.getOrGenerateUserId(),
459
495
  participantName: this.userName,
496
+ headers: this.getChatService().getUploadHeaders(),
460
497
  });
461
498
  this.selectedFiles = uploadResult.selectedFiles;
499
+ if (uploadResult.tokenRejected) {
500
+ throw new SessionAccessError(403, 'session_token_required', uploadResult.errorMessage || 'Session token rejected');
501
+ }
462
502
  return uploadResult.uploadedIds;
463
503
  }
464
504
  finally {
@@ -550,6 +590,10 @@ export class OcsChat {
550
590
  catch (error) {
551
591
  if (epoch !== this.sessionEpoch)
552
592
  return;
593
+ if (error instanceof SessionAccessError) {
594
+ this.handleSessionAccessError();
595
+ return;
596
+ }
553
597
  const errorText = error instanceof Error ? error.message : 'Failed to send message';
554
598
  this.handleError(errorText);
555
599
  }
@@ -724,8 +768,12 @@ export class OcsChat {
724
768
  },
725
769
  onError: error => {
726
770
  this.typingProgressMessage = '';
727
- this.handleError(error.message);
728
771
  this.taskPollingHandle = undefined;
772
+ if (error instanceof SessionAccessError) {
773
+ this.handleSessionAccessError();
774
+ return;
775
+ }
776
+ this.handleError(error.message);
729
777
  this.startMessagePolling();
730
778
  },
731
779
  });
@@ -1087,6 +1135,7 @@ export class OcsChat {
1087
1135
  messages: `ocs-chat-messages-${this.chatbotId}`,
1088
1136
  lastActivity: `ocs-chat-activity-${this.chatbotId}`,
1089
1137
  visible: `ocs-chat-visible-${this.chatbotId}`,
1138
+ sessionToken: `ocs-chat-token-${this.chatbotId}`,
1090
1139
  };
1091
1140
  }
1092
1141
  saveSessionToStorage() {
@@ -1098,6 +1147,12 @@ export class OcsChat {
1098
1147
  if (this.activeSessionId) {
1099
1148
  localStorage.setItem(keys.sessionId, this.activeSessionId);
1100
1149
  localStorage.setItem(keys.lastActivity, new Date().toISOString());
1150
+ if (this.currentSessionToken) {
1151
+ localStorage.setItem(keys.sessionToken, this.currentSessionToken);
1152
+ }
1153
+ else {
1154
+ localStorage.removeItem(keys.sessionToken);
1155
+ }
1101
1156
  }
1102
1157
  localStorage.setItem(keys.messages, JSON.stringify(this.messages));
1103
1158
  }
@@ -1106,6 +1161,7 @@ export class OcsChat {
1106
1161
  }
1107
1162
  }
1108
1163
  loadSessionFromStorage() {
1164
+ var _a;
1109
1165
  const keys = this.getStorageKeys();
1110
1166
  try {
1111
1167
  if (this.persistentSessionExpire > 0) {
@@ -1133,7 +1189,8 @@ export class OcsChat {
1133
1189
  messages = [];
1134
1190
  }
1135
1191
  }
1136
- return { sessionId, messages };
1192
+ const sessionToken = (_a = localStorage.getItem(keys.sessionToken)) !== null && _a !== void 0 ? _a : undefined;
1193
+ return { sessionId, messages, sessionToken };
1137
1194
  }
1138
1195
  catch (error) {
1139
1196
  // fall back to starting a new session
@@ -1205,6 +1262,7 @@ export class OcsChat {
1205
1262
  localStorage.removeItem(keys.messages);
1206
1263
  localStorage.removeItem(keys.lastActivity);
1207
1264
  localStorage.removeItem(keys.visible);
1265
+ localStorage.removeItem(keys.sessionToken);
1208
1266
  }
1209
1267
  catch (error) {
1210
1268
  console.warn('Failed to clear chat session from localStorage:', error);
@@ -1246,6 +1304,7 @@ export class OcsChat {
1246
1304
  // A session provided by the host page (session-id prop) cannot be cleared;
1247
1305
  // stay bound to it. Unbound widgets start a new session on the next message.
1248
1306
  this.activeSessionId = this.sessionId;
1307
+ this.applySessionToken(this.isSessionBound() ? this.sessionToken : undefined);
1249
1308
  this.messages = [];
1250
1309
  this.isTyping = false;
1251
1310
  this.currentPollTaskId = '';
@@ -1800,6 +1859,25 @@ export class OcsChat {
1800
1859
  "getter": false,
1801
1860
  "setter": false,
1802
1861
  "reflect": false
1862
+ },
1863
+ "sessionToken": {
1864
+ "type": "string",
1865
+ "attribute": "session-token",
1866
+ "mutable": false,
1867
+ "complexType": {
1868
+ "original": "string",
1869
+ "resolved": "string",
1870
+ "references": {}
1871
+ },
1872
+ "required": false,
1873
+ "optional": true,
1874
+ "docs": {
1875
+ "tags": [],
1876
+ "text": "A session token proving access to the session named by `session-id`. Host\npages that create the session server-side pass a server-minted token here so\nthe widget can authenticate its requests. Only meaningful with `session-id`."
1877
+ },
1878
+ "getter": false,
1879
+ "setter": false,
1880
+ "reflect": false
1803
1881
  }
1804
1882
  };
1805
1883
  }