open-chat-studio-widget 0.8.0 → 0.9.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 +1 -0
- package/dist/cjs/{index-Cf6K60f1.js → index-DDod9Zyw.js} +3 -3
- package/dist/cjs/{index-Cf6K60f1.js.map → index-DDod9Zyw.js.map} +1 -1
- package/dist/cjs/loader.cjs.js +2 -2
- package/dist/cjs/open-chat-studio-widget.cjs.entry.js +175 -22
- package/dist/cjs/open-chat-studio-widget.cjs.entry.js.map +1 -1
- package/dist/cjs/open-chat-studio-widget.cjs.js +2 -2
- package/dist/cjs/open-chat-studio-widget.entry.cjs.js.map +1 -1
- package/dist/collection/components/ocs-chat/ocs-chat.js +82 -4
- package/dist/collection/components/ocs-chat/ocs-chat.js.map +1 -1
- package/dist/collection/services/chat-session-service.js +88 -18
- package/dist/collection/services/chat-session-service.js.map +1 -1
- package/dist/collection/services/file-attachment-manager.js +6 -0
- package/dist/collection/services/file-attachment-manager.js.map +1 -1
- package/dist/components/open-chat-studio-widget.js +175 -21
- package/dist/components/open-chat-studio-widget.js.map +1 -1
- package/dist/esm/{index-DXf2dIht.js → index-iUBQH9om.js} +3 -3
- package/dist/esm/{index-DXf2dIht.js.map → index-iUBQH9om.js.map} +1 -1
- package/dist/esm/loader.js +3 -3
- package/dist/esm/open-chat-studio-widget.entry.js +175 -22
- package/dist/esm/open-chat-studio-widget.entry.js.map +1 -1
- package/dist/esm/open-chat-studio-widget.js +3 -3
- package/dist/open-chat-studio-widget/open-chat-studio-widget.entry.esm.js.map +1 -1
- package/dist/open-chat-studio-widget/open-chat-studio-widget.esm.js +1 -1
- package/dist/open-chat-studio-widget/p-9c925476.entry.js +4 -0
- package/dist/open-chat-studio-widget/p-9c925476.entry.js.map +1 -0
- package/dist/open-chat-studio-widget/{p-DXf2dIht.js → p-iUBQH9om.js} +2 -2
- package/dist/open-chat-studio-widget/{p-DXf2dIht.js.map → p-iUBQH9om.js.map} +1 -1
- package/dist/types/components/ocs-chat/ocs-chat.d.ts +14 -0
- package/dist/types/components.d.ts +8 -0
- package/dist/types/services/chat-session-service.d.ts +21 -0
- package/dist/types/services/file-attachment-manager.d.ts +2 -0
- package/package.json +1 -1
- package/dist/open-chat-studio-widget/p-ff47dabf.entry.js +0 -4
- 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
|
+
sessionToken: this.currentSessionToken,
|
|
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
|
-
|
|
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
|
}
|