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.
- package/README.md +1 -0
- package/dist/cjs/{index-Cf6K60f1.js → index-fFSp-Z_h.js} +3 -3
- package/dist/cjs/{index-Cf6K60f1.js.map → index-fFSp-Z_h.js.map} +1 -1
- package/dist/cjs/loader.cjs.js +2 -2
- package/dist/cjs/open-chat-studio-widget.cjs.entry.js +179 -24
- 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 +95 -20
- package/dist/collection/services/chat-session-service.js.map +1 -1
- package/dist/collection/services/file-attachment-manager.js +3 -0
- package/dist/collection/services/file-attachment-manager.js.map +1 -1
- package/dist/components/open-chat-studio-widget.js +179 -23
- package/dist/components/open-chat-studio-widget.js.map +1 -1
- package/dist/esm/{index-DXf2dIht.js → index-ythTKHg-.js} +3 -3
- package/dist/esm/{index-DXf2dIht.js.map → index-ythTKHg-.js.map} +1 -1
- package/dist/esm/loader.js +3 -3
- package/dist/esm/open-chat-studio-widget.entry.js +179 -24
- 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-2d31a15c.entry.js +4 -0
- package/dist/open-chat-studio-widget/p-2d31a15c.entry.js.map +1 -0
- package/dist/open-chat-studio-widget/{p-DXf2dIht.js → p-ythTKHg-.js} +2 -2
- package/dist/open-chat-studio-widget/{p-DXf2dIht.js.map → p-ythTKHg-.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 +23 -0
- package/dist/types/services/file-attachment-manager.d.ts +3 -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
package/dist/esm/loader.js
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import { b as bootstrapLazy } from './index-
|
|
2
|
-
export { s as setNonce } from './index-
|
|
1
|
+
import { b as bootstrapLazy } from './index-ythTKHg-.js';
|
|
2
|
+
export { s as setNonce } from './index-ythTKHg-.js';
|
|
3
3
|
import { g as globalScripts } from './app-globals-DQuL1Twl.js';
|
|
4
4
|
|
|
5
5
|
const defineCustomElements = async (win, options) => {
|
|
6
6
|
if (typeof window === 'undefined') return undefined;
|
|
7
7
|
await globalScripts();
|
|
8
|
-
return bootstrapLazy([["open-chat-studio-widget",[[257,"open-chat-studio-widget",{"chatbotId":[1,"chatbot-id"],"apiBaseUrl":[1,"api-base-url"],"buttonText":[1,"button-text"],"iconUrl":[1,"icon-url"],"embedKey":[1,"embed-key"],"buttonShape":[1,"button-shape"],"showButton":[4,"show-button"],"mode":[1],"headerText":[1,"header-text"],"newChatConfirmationMessage":[1,"new-chat-confirmation-message"],"visible":[1028],"position":[1025],"welcomeMessages":[1,"welcome-messages"],"starterQuestions":[1,"starter-questions"],"userId":[1,"user-id"],"userName":[1,"user-name"],"persistentSession":[4,"persistent-session"],"persistentSessionExpire":[2,"persistent-session-expire"],"allowFullScreen":[4,"allow-full-screen"],"allowAttachments":[4,"allow-attachments"],"typingIndicatorText":[1,"typing-indicator-text"],"language":[1],"translationsUrl":[1,"translations-url"],"pageContext":[1040,"page-context"],"versionNumber":[2,"version-number"],"sessionId":[1,"session-id"],"error":[32],"messages":[32],"activeSessionId":[32],"isLoading":[32],"isTyping":[32],"typingProgressMessage":[32],"messageInput":[32],"currentPollTaskId":[32],"isDragging":[32],"dragOffset":[32],"windowPosition":[32],"fullscreenPosition":[32],"parsedWelcomeMessages":[32],"parsedStarterQuestions":[32],"generatedUserId":[32],"isFullscreen":[32],"showNewChatConfirmation":[32],"selectedFiles":[32],"isUploadingFiles":[32],"isButtonDragging":[32],"buttonWasDragged":[32]},null,{"pageContext":["pageContextHandler"],"chatbotId":["chatbotConfigHandler"],"versionNumber":["chatbotConfigHandler"],"visible":["visibilityHandler"]}]]]], options);
|
|
8
|
+
return bootstrapLazy([["open-chat-studio-widget",[[257,"open-chat-studio-widget",{"chatbotId":[1,"chatbot-id"],"apiBaseUrl":[1,"api-base-url"],"buttonText":[1,"button-text"],"iconUrl":[1,"icon-url"],"embedKey":[1,"embed-key"],"buttonShape":[1,"button-shape"],"showButton":[4,"show-button"],"mode":[1],"headerText":[1,"header-text"],"newChatConfirmationMessage":[1,"new-chat-confirmation-message"],"visible":[1028],"position":[1025],"welcomeMessages":[1,"welcome-messages"],"starterQuestions":[1,"starter-questions"],"userId":[1,"user-id"],"userName":[1,"user-name"],"persistentSession":[4,"persistent-session"],"persistentSessionExpire":[2,"persistent-session-expire"],"allowFullScreen":[4,"allow-full-screen"],"allowAttachments":[4,"allow-attachments"],"typingIndicatorText":[1,"typing-indicator-text"],"language":[1],"translationsUrl":[1,"translations-url"],"pageContext":[1040,"page-context"],"versionNumber":[2,"version-number"],"sessionId":[1,"session-id"],"sessionToken":[1,"session-token"],"error":[32],"messages":[32],"activeSessionId":[32],"isLoading":[32],"isTyping":[32],"typingProgressMessage":[32],"messageInput":[32],"currentPollTaskId":[32],"isDragging":[32],"dragOffset":[32],"windowPosition":[32],"fullscreenPosition":[32],"parsedWelcomeMessages":[32],"parsedStarterQuestions":[32],"generatedUserId":[32],"isFullscreen":[32],"showNewChatConfirmation":[32],"selectedFiles":[32],"isUploadingFiles":[32],"isButtonDragging":[32],"buttonWasDragged":[32]},null,{"pageContext":["pageContextHandler"],"chatbotId":["chatbotConfigHandler"],"versionNumber":["chatbotConfigHandler"],"visible":["visibilityHandler"]}]]]], options);
|
|
9
9
|
};
|
|
10
10
|
|
|
11
11
|
export { defineCustomElements };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { h, r as registerInstance, E as Env, H as Host, g as getElement } from './index-
|
|
1
|
+
import { h, r as registerInstance, E as Env, H as Host, g as getElement } from './index-ythTKHg-.js';
|
|
2
2
|
|
|
3
3
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
4
4
|
const OcsWidgetAvatar = () => {
|
|
@@ -4468,6 +4468,8 @@ var ar = {
|
|
|
4468
4468
|
"status.starting": "جارٍ بدء المحادثة...",
|
|
4469
4469
|
"status.typing": "جارٍ تحضير الرد",
|
|
4470
4470
|
"status.uploading": "جارٍ التحميل",
|
|
4471
|
+
"status.sessionExpired": "انتهت صلاحية جلسة الدردشة. سيتم بدء محادثة جديدة — يُرجى إعادة إرسال رسالتك.",
|
|
4472
|
+
"status.sessionError": "لم تعد جلسة الدردشة هذه متاحة.",
|
|
4471
4473
|
"modal.newChatTitle": "بدء محادثة جديدة",
|
|
4472
4474
|
"modal.newChatBody": "بدء محادثة جديدة سيؤدي إلى مسح المحادثة الحالية. هل ترغب بالمتابعة؟",
|
|
4473
4475
|
"modal.cancel": "إلغاء",
|
|
@@ -4500,6 +4502,8 @@ var en = {
|
|
|
4500
4502
|
"status.starting": "Starting chat...",
|
|
4501
4503
|
"status.typing": "Preparing response",
|
|
4502
4504
|
"status.uploading": "Uploading",
|
|
4505
|
+
"status.sessionExpired": "Your chat session expired. Starting a new chat — please resend your message.",
|
|
4506
|
+
"status.sessionError": "This chat session is no longer available.",
|
|
4503
4507
|
"modal.newChatTitle": "Start New Chat",
|
|
4504
4508
|
"modal.newChatBody": "Starting a new chat will clear your current conversation. Continue?",
|
|
4505
4509
|
"modal.cancel": "Cancel",
|
|
@@ -4532,6 +4536,8 @@ var es = {
|
|
|
4532
4536
|
"status.starting": "Iniciando chat...",
|
|
4533
4537
|
"status.typing": "Preparando respuesta",
|
|
4534
4538
|
"status.uploading": "Subiendo",
|
|
4539
|
+
"status.sessionExpired": "Tu sesión de chat ha expirado. Se iniciará un nuevo chat: vuelve a enviar tu mensaje.",
|
|
4540
|
+
"status.sessionError": "Esta sesión de chat ya no está disponible.",
|
|
4535
4541
|
"modal.newChatTitle": "Iniciar Nuevo Chat",
|
|
4536
4542
|
"modal.newChatBody": "Iniciar un nuevo chat borrará tu conversación actual. ¿Continuar?",
|
|
4537
4543
|
"modal.cancel": "Cancelar",
|
|
@@ -4564,6 +4570,8 @@ var fr = {
|
|
|
4564
4570
|
"status.starting": "Démarrage du chat...",
|
|
4565
4571
|
"status.typing": "Préparation de la réponse",
|
|
4566
4572
|
"status.uploading": "Téléversement",
|
|
4573
|
+
"status.sessionExpired": "Votre session de chat a expiré. Un nouveau chat va démarrer — veuillez renvoyer votre message.",
|
|
4574
|
+
"status.sessionError": "Cette session de chat n'est plus disponible.",
|
|
4567
4575
|
"modal.newChatTitle": "Nouveau Chat",
|
|
4568
4576
|
"modal.newChatBody": "Démarrer un nouveau chat effacera votre conversation actuelle. Continuer?",
|
|
4569
4577
|
"modal.cancel": "Annuler",
|
|
@@ -4596,6 +4604,8 @@ var hi = {
|
|
|
4596
4604
|
"status.starting": "चैट शुरू हो रही है...",
|
|
4597
4605
|
"status.typing": "जवाब तैयार किया जा रहा है",
|
|
4598
4606
|
"status.uploading": "अपलोड हो रहा है",
|
|
4607
|
+
"status.sessionExpired": "आपका चैट सत्र समाप्त हो गया है। एक नई चैट शुरू हो रही है — कृपया अपना संदेश फिर से भेजें।",
|
|
4608
|
+
"status.sessionError": "यह चैट सत्र अब उपलब्ध नहीं है।",
|
|
4599
4609
|
"modal.newChatTitle": "नई चैट शुरू करें",
|
|
4600
4610
|
"modal.newChatBody": "नई चैट शुरू करने से आपकी मौजूदा बातचीत हट जाएगी। क्या आप जारी रखना चाहते हैं?",
|
|
4601
4611
|
"modal.cancel": "रद्द करें",
|
|
@@ -4628,6 +4638,8 @@ var it = {
|
|
|
4628
4638
|
"status.starting": "Avvio della chat...",
|
|
4629
4639
|
"status.typing": "Preparazione della risposta",
|
|
4630
4640
|
"status.uploading": "Caricamento",
|
|
4641
|
+
"status.sessionExpired": "La tua sessione di chat è scaduta. Verrà avviata una nuova chat: invia di nuovo il tuo messaggio.",
|
|
4642
|
+
"status.sessionError": "Questa sessione di chat non è più disponibile.",
|
|
4631
4643
|
"modal.newChatTitle": "Avvia nuova chat",
|
|
4632
4644
|
"modal.newChatBody": "Avviare una nuova chat cancellerà la conversazione attuale. Continuare?",
|
|
4633
4645
|
"modal.cancel": "Annulla",
|
|
@@ -4660,6 +4672,8 @@ var pt = {
|
|
|
4660
4672
|
"status.starting": "Iniciando chat...",
|
|
4661
4673
|
"status.typing": "Preparando resposta",
|
|
4662
4674
|
"status.uploading": "Carregando",
|
|
4675
|
+
"status.sessionExpired": "Sua sessão de chat expirou. Iniciando um novo chat — reenvie sua mensagem.",
|
|
4676
|
+
"status.sessionError": "Esta sessão de chat não está mais disponível.",
|
|
4663
4677
|
"modal.newChatTitle": "Iniciar novo chat",
|
|
4664
4678
|
"modal.newChatBody": "Iniciar um novo chat apagará sua conversa atual. Deseja continuar?",
|
|
4665
4679
|
"modal.cancel": "Cancelar",
|
|
@@ -4692,6 +4706,8 @@ var sw = {
|
|
|
4692
4706
|
"status.starting": "Inaanzisha gumzo...",
|
|
4693
4707
|
"status.typing": "Inatayarisha jibu",
|
|
4694
4708
|
"status.uploading": "Inapakia",
|
|
4709
|
+
"status.sessionExpired": "Kipindi chako cha gumzo kimeisha. Gumzo jipya linaanza — tafadhali tuma tena ujumbe wako.",
|
|
4710
|
+
"status.sessionError": "Kipindi hiki cha gumzo hakipatikani tena.",
|
|
4695
4711
|
"modal.newChatTitle": "Anza gumzo jipya",
|
|
4696
4712
|
"modal.newChatBody": "Kuanza gumzo jipya kutafuta gumzo lako la sasa. Je, ungependa kuendelea?",
|
|
4697
4713
|
"modal.cancel": "Ghairi",
|
|
@@ -4724,6 +4740,8 @@ var uk = {
|
|
|
4724
4740
|
"status.starting": "Запуск чату...",
|
|
4725
4741
|
"status.typing": "Готується відповідь",
|
|
4726
4742
|
"status.uploading": "Завантаження",
|
|
4743
|
+
"status.sessionExpired": "Ваш сеанс чату завершився. Розпочинається новий чат — будь ласка, надішліть повідомлення ще раз.",
|
|
4744
|
+
"status.sessionError": "Цей сеанс чату більше недоступний.",
|
|
4727
4745
|
"modal.newChatTitle": "Почати новий чат",
|
|
4728
4746
|
"modal.newChatBody": "Початок нового чату видалить вашу поточну розмову. Продовжити?",
|
|
4729
4747
|
"modal.cancel": "Скасувати",
|
|
@@ -4980,55 +4998,54 @@ function currentDomainMatchesApiBaseUrl(apiBaseUrl) {
|
|
|
4980
4998
|
return window.location.origin === apiBase.origin;
|
|
4981
4999
|
}
|
|
4982
5000
|
|
|
5001
|
+
class SessionAccessError extends Error {
|
|
5002
|
+
constructor(status, code, message) {
|
|
5003
|
+
super(message);
|
|
5004
|
+
this.name = 'SessionAccessError';
|
|
5005
|
+
this.status = status;
|
|
5006
|
+
this.code = code;
|
|
5007
|
+
}
|
|
5008
|
+
}
|
|
4983
5009
|
class ChatSessionService {
|
|
4984
5010
|
constructor(options) {
|
|
4985
5011
|
var _a, _b, _c, _d;
|
|
4986
5012
|
this.apiBaseUrl = options.apiBaseUrl;
|
|
4987
5013
|
this.embedKey = options.embedKey;
|
|
4988
5014
|
this.widgetVersion = options.widgetVersion;
|
|
5015
|
+
this.sessionToken = options.sessionToken;
|
|
4989
5016
|
this.csrfTokenProvider = (_a = options.csrfTokenProvider) !== null && _a !== void 0 ? _a : getCSRFToken;
|
|
4990
5017
|
this.taskPollingIntervalMs = (_b = options.taskPollingIntervalMs) !== null && _b !== void 0 ? _b : 1000;
|
|
4991
5018
|
this.taskPollingMaxAttempts = (_c = options.taskPollingMaxAttempts) !== null && _c !== void 0 ? _c : 120;
|
|
4992
5019
|
this.messagePollingIntervalMs = (_d = options.messagePollingIntervalMs) !== null && _d !== void 0 ? _d : 30000;
|
|
4993
5020
|
}
|
|
4994
5021
|
async startSession(requestBody) {
|
|
4995
|
-
const response = await
|
|
5022
|
+
const response = await this.request(`${this.apiBaseUrl}/api/chat/start/`, {
|
|
4996
5023
|
method: 'POST',
|
|
4997
5024
|
headers: this.getJsonHeaders(),
|
|
4998
5025
|
body: JSON.stringify(requestBody),
|
|
4999
5026
|
});
|
|
5000
5027
|
if (!response.ok) {
|
|
5001
|
-
|
|
5028
|
+
await this.raiseForStatus(response, 'Failed to start session');
|
|
5002
5029
|
}
|
|
5003
5030
|
return response.json();
|
|
5004
5031
|
}
|
|
5005
5032
|
async sendMessage(sessionId, payload) {
|
|
5006
|
-
const response = await
|
|
5033
|
+
const response = await this.request(`${this.apiBaseUrl}/api/chat/${sessionId}/message/`, {
|
|
5007
5034
|
method: 'POST',
|
|
5008
5035
|
headers: this.getJsonHeaders(),
|
|
5009
5036
|
body: JSON.stringify(payload),
|
|
5010
5037
|
});
|
|
5011
5038
|
if (!response.ok) {
|
|
5012
|
-
|
|
5039
|
+
await this.raiseForStatus(response, 'Failed to send message');
|
|
5013
5040
|
}
|
|
5014
5041
|
return response.json();
|
|
5015
5042
|
}
|
|
5016
5043
|
async pollTaskOnce(sessionId, taskId) {
|
|
5017
|
-
const response = await
|
|
5044
|
+
const response = await this.request(`${this.apiBaseUrl}/api/chat/${sessionId}/${taskId}/poll/`, {
|
|
5018
5045
|
headers: this.getCommonHeaders(),
|
|
5019
5046
|
});
|
|
5020
5047
|
if (!response.ok) {
|
|
5021
|
-
|
|
5022
|
-
try {
|
|
5023
|
-
const data = (await response.json());
|
|
5024
|
-
if (data === null || data === void 0 ? void 0 : data.error) {
|
|
5025
|
-
errorMessage = data.error;
|
|
5026
|
-
}
|
|
5027
|
-
}
|
|
5028
|
-
catch (_a) {
|
|
5029
|
-
// non-JSON body; keep statusText fallback
|
|
5030
|
-
}
|
|
5031
|
-
throw new Error(errorMessage);
|
|
5048
|
+
await this.raiseForStatus(response, 'Failed to poll task');
|
|
5032
5049
|
}
|
|
5033
5050
|
return response.json();
|
|
5034
5051
|
}
|
|
@@ -5088,11 +5105,11 @@ class ChatSessionService {
|
|
|
5088
5105
|
if (since) {
|
|
5089
5106
|
url.searchParams.set('since', since);
|
|
5090
5107
|
}
|
|
5091
|
-
const response = await
|
|
5108
|
+
const response = await this.request(url.toString(), {
|
|
5092
5109
|
headers: this.getCommonHeaders(),
|
|
5093
5110
|
});
|
|
5094
5111
|
if (!response.ok) {
|
|
5095
|
-
|
|
5112
|
+
await this.raiseForStatus(response, 'Failed to poll messages');
|
|
5096
5113
|
}
|
|
5097
5114
|
return response.json();
|
|
5098
5115
|
}
|
|
@@ -5149,15 +5166,88 @@ class ChatSessionService {
|
|
|
5149
5166
|
this.messagePollingTimer = undefined;
|
|
5150
5167
|
}
|
|
5151
5168
|
}
|
|
5152
|
-
|
|
5169
|
+
setSessionToken(token) {
|
|
5170
|
+
this.sessionToken = token;
|
|
5171
|
+
}
|
|
5172
|
+
async request(input, init) {
|
|
5173
|
+
const response = await fetch(input, init);
|
|
5174
|
+
this.checkSunsetHeaders(response);
|
|
5175
|
+
return response;
|
|
5176
|
+
}
|
|
5177
|
+
/**
|
|
5178
|
+
* Log a deprecation warning (RFC 8594 `Deprecation`/`Sunset`/`Link` headers)
|
|
5179
|
+
* when the server reports that this widget version is deprecated. Warns
|
|
5180
|
+
* during the deprecation window and errors once the sunset date has passed.
|
|
5181
|
+
* Logs at most once per level so polling does not flood the console.
|
|
5182
|
+
*/
|
|
5183
|
+
checkSunsetHeaders(response) {
|
|
5184
|
+
const headers = response === null || response === void 0 ? void 0 : response.headers;
|
|
5185
|
+
if (!headers || typeof headers.get !== 'function') {
|
|
5186
|
+
return;
|
|
5187
|
+
}
|
|
5188
|
+
if (headers.get('Deprecation') !== 'true') {
|
|
5189
|
+
return;
|
|
5190
|
+
}
|
|
5191
|
+
const sunsetAt = this.parseSunsetDate(headers.get('Sunset'));
|
|
5192
|
+
const pastSunset = sunsetAt !== null && Date.now() >= sunsetAt.getTime();
|
|
5193
|
+
const level = pastSunset ? 'error' : 'warn';
|
|
5194
|
+
if (this.loggedSunsetLevel === level) {
|
|
5195
|
+
return;
|
|
5196
|
+
}
|
|
5197
|
+
this.loggedSunsetLevel = level;
|
|
5198
|
+
const upgradeUrl = this.parseSuccessorUrl(headers.get('Link'));
|
|
5199
|
+
const upgradeSuffix = upgradeUrl ? ` Upgrade: ${upgradeUrl}` : '';
|
|
5200
|
+
const sunsetText = sunsetAt ? sunsetAt.toUTCString() : 'an upcoming date';
|
|
5201
|
+
if (level === 'error') {
|
|
5202
|
+
console.error(`[open-chat-studio-widget] Widget version ${this.widgetVersion} is past its sunset date ` + `(${sunsetText}) and may stop working.${upgradeSuffix}`);
|
|
5203
|
+
}
|
|
5204
|
+
else {
|
|
5205
|
+
console.warn(`[open-chat-studio-widget] Widget version ${this.widgetVersion} is deprecated and will stop ` + `working after ${sunsetText}.${upgradeSuffix}`);
|
|
5206
|
+
}
|
|
5207
|
+
}
|
|
5208
|
+
parseSunsetDate(sunset) {
|
|
5209
|
+
if (!sunset) {
|
|
5210
|
+
return null;
|
|
5211
|
+
}
|
|
5212
|
+
const parsed = new Date(sunset);
|
|
5213
|
+
return Number.isNaN(parsed.getTime()) ? null : parsed;
|
|
5214
|
+
}
|
|
5215
|
+
parseSuccessorUrl(link) {
|
|
5216
|
+
const match = link === null || link === void 0 ? void 0 : link.match(/<([^>]+)>\s*;\s*rel="?successor-version"?/);
|
|
5217
|
+
return match === null || match === void 0 ? void 0 : match[1];
|
|
5218
|
+
}
|
|
5219
|
+
async raiseForStatus(response, fallbackPrefix) {
|
|
5220
|
+
let message = `${fallbackPrefix}: ${response.statusText}`;
|
|
5221
|
+
let code;
|
|
5222
|
+
try {
|
|
5223
|
+
const data = (await response.json());
|
|
5224
|
+
if (data === null || data === void 0 ? void 0 : data.error) {
|
|
5225
|
+
message = data.error;
|
|
5226
|
+
}
|
|
5227
|
+
code = data === null || data === void 0 ? void 0 : data.code;
|
|
5228
|
+
}
|
|
5229
|
+
catch (_a) {
|
|
5230
|
+
// non-JSON body; keep statusText fallback
|
|
5231
|
+
}
|
|
5232
|
+
if (response.status === 403) {
|
|
5233
|
+
throw new SessionAccessError(response.status, code, message);
|
|
5234
|
+
}
|
|
5235
|
+
throw new Error(message);
|
|
5236
|
+
}
|
|
5237
|
+
/** Headers for multipart requests (no Content-Type — fetch sets the boundary). */
|
|
5238
|
+
getUploadHeaders() {
|
|
5153
5239
|
const headers = this.getCommonHeaders();
|
|
5154
|
-
headers['Content-Type'] = 'application/json';
|
|
5155
5240
|
const csrfToken = this.csrfTokenProvider(this.apiBaseUrl);
|
|
5156
5241
|
if (csrfToken) {
|
|
5157
5242
|
headers['X-CSRFToken'] = csrfToken;
|
|
5158
5243
|
}
|
|
5159
5244
|
return headers;
|
|
5160
5245
|
}
|
|
5246
|
+
getJsonHeaders() {
|
|
5247
|
+
const headers = this.getUploadHeaders();
|
|
5248
|
+
headers['Content-Type'] = 'application/json';
|
|
5249
|
+
return headers;
|
|
5250
|
+
}
|
|
5161
5251
|
getCommonHeaders() {
|
|
5162
5252
|
const headers = {
|
|
5163
5253
|
'x-ocs-widget-version': this.widgetVersion,
|
|
@@ -5165,6 +5255,9 @@ class ChatSessionService {
|
|
|
5165
5255
|
if (this.embedKey) {
|
|
5166
5256
|
headers['X-Embed-Key'] = this.embedKey;
|
|
5167
5257
|
}
|
|
5258
|
+
if (this.sessionToken) {
|
|
5259
|
+
headers['X-Session-Token'] = this.sessionToken;
|
|
5260
|
+
}
|
|
5168
5261
|
return headers;
|
|
5169
5262
|
}
|
|
5170
5263
|
}
|
|
@@ -5214,6 +5307,7 @@ class FileAttachmentManager {
|
|
|
5214
5307
|
});
|
|
5215
5308
|
}
|
|
5216
5309
|
async uploadPendingFiles(existingFiles, context) {
|
|
5310
|
+
var _a;
|
|
5217
5311
|
if (existingFiles.length === 0) {
|
|
5218
5312
|
return { selectedFiles: existingFiles, uploadedIds: [] };
|
|
5219
5313
|
}
|
|
@@ -5233,6 +5327,7 @@ class FileAttachmentManager {
|
|
|
5233
5327
|
try {
|
|
5234
5328
|
const response = await fetch(`${context.apiBaseUrl}/api/chat/${context.sessionId}/upload/`, {
|
|
5235
5329
|
method: 'POST',
|
|
5330
|
+
headers: (_a = context.headers) !== null && _a !== void 0 ? _a : {},
|
|
5236
5331
|
body: formData,
|
|
5237
5332
|
});
|
|
5238
5333
|
if (!response.ok) {
|
|
@@ -5242,6 +5337,7 @@ class FileAttachmentManager {
|
|
|
5242
5337
|
selectedFiles: this.markPendingFilesWithError(existingFiles, errorMessage),
|
|
5243
5338
|
uploadedIds,
|
|
5244
5339
|
errorMessage,
|
|
5340
|
+
tokenRejected: response.status === 403,
|
|
5245
5341
|
};
|
|
5246
5342
|
}
|
|
5247
5343
|
const data = await this.safeJson(response);
|
|
@@ -5527,13 +5623,15 @@ const OcsChat = class {
|
|
|
5527
5623
|
if (this.isSessionBound()) {
|
|
5528
5624
|
// Bound to an externally-managed session: the host page is the source of truth.
|
|
5529
5625
|
this.activeSessionId = this.sessionId;
|
|
5626
|
+
this.applySessionToken(this.sessionToken);
|
|
5530
5627
|
}
|
|
5531
5628
|
else if (this.persistentSession && this.isLocalStorageAvailable()) {
|
|
5532
5629
|
// Always try to load existing session if localStorage is available
|
|
5533
|
-
const { sessionId, messages } = this.loadSessionFromStorage();
|
|
5630
|
+
const { sessionId, messages, sessionToken } = this.loadSessionFromStorage();
|
|
5534
5631
|
if (sessionId && messages) {
|
|
5535
5632
|
this.activeSessionId = sessionId;
|
|
5536
5633
|
this.messages = messages;
|
|
5634
|
+
this.applySessionToken(sessionToken);
|
|
5537
5635
|
}
|
|
5538
5636
|
}
|
|
5539
5637
|
this.parseWelcomeMessages();
|
|
@@ -5582,6 +5680,11 @@ const OcsChat = class {
|
|
|
5582
5680
|
this.removeButtonEventListeners();
|
|
5583
5681
|
window.removeEventListener('resize', this.handleWindowResize);
|
|
5584
5682
|
}
|
|
5683
|
+
applySessionToken(token) {
|
|
5684
|
+
var _a;
|
|
5685
|
+
this.currentSessionToken = token;
|
|
5686
|
+
(_a = this.chatService) === null || _a === void 0 ? void 0 : _a.setSessionToken(token);
|
|
5687
|
+
}
|
|
5585
5688
|
getChatService() {
|
|
5586
5689
|
if (!this.chatService) {
|
|
5587
5690
|
this.chatService = new ChatSessionService({
|
|
@@ -5591,6 +5694,7 @@ const OcsChat = class {
|
|
|
5591
5694
|
taskPollingIntervalMs: OcsChat.TASK_POLLING_INTERVAL_MS,
|
|
5592
5695
|
taskPollingMaxAttempts: OcsChat.TASK_POLLING_MAX_ATTEMPTS,
|
|
5593
5696
|
messagePollingIntervalMs: OcsChat.MESSAGE_POLLING_INTERVAL_MS,
|
|
5697
|
+
sessionToken: this.currentSessionToken,
|
|
5594
5698
|
});
|
|
5595
5699
|
}
|
|
5596
5700
|
return this.chatService;
|
|
@@ -5606,6 +5710,27 @@ const OcsChat = class {
|
|
|
5606
5710
|
this.saveSessionToStorage();
|
|
5607
5711
|
this.scrollToBottom();
|
|
5608
5712
|
}
|
|
5713
|
+
/**
|
|
5714
|
+
* Recover from a rejected session token (403). Unbound widgets discard the
|
|
5715
|
+
* dead session/token, show a notice, and start fresh on the next send; bound
|
|
5716
|
+
* widgets cannot restart a host-owned session, so they surface an error.
|
|
5717
|
+
*/
|
|
5718
|
+
handleSessionAccessError() {
|
|
5719
|
+
this.cleanup();
|
|
5720
|
+
this.isLoading = false;
|
|
5721
|
+
this.isTyping = false;
|
|
5722
|
+
this.isUploadingFiles = false;
|
|
5723
|
+
this.typingProgressMessage = '';
|
|
5724
|
+
if (this.isSessionBound()) {
|
|
5725
|
+
this.addErrorMessage(this.translationManager.get('status.sessionError', 'This chat session is no longer available.'));
|
|
5726
|
+
return;
|
|
5727
|
+
}
|
|
5728
|
+
this.sessionEpoch += 1;
|
|
5729
|
+
this.activeSessionId = undefined;
|
|
5730
|
+
this.applySessionToken(undefined);
|
|
5731
|
+
this.clearSessionStorage();
|
|
5732
|
+
this.addErrorMessage(this.translationManager.get('status.sessionExpired', 'Your chat session expired. Starting a new chat — please resend your message.'));
|
|
5733
|
+
}
|
|
5609
5734
|
handleError(errorText) {
|
|
5610
5735
|
// show as system message
|
|
5611
5736
|
this.addErrorMessage(errorText);
|
|
@@ -5678,12 +5803,14 @@ const OcsChat = class {
|
|
|
5678
5803
|
this.currentPollTaskId = '';
|
|
5679
5804
|
}
|
|
5680
5805
|
async startSession() {
|
|
5806
|
+
var _a;
|
|
5681
5807
|
const epoch = this.sessionEpoch;
|
|
5682
5808
|
try {
|
|
5683
5809
|
this.isLoading = true;
|
|
5684
5810
|
const userId = this.getOrGenerateUserId();
|
|
5685
5811
|
const requestBody = {
|
|
5686
5812
|
chatbot_id: this.chatbotId,
|
|
5813
|
+
use_session_token: true,
|
|
5687
5814
|
session_data: {
|
|
5688
5815
|
source: 'widget',
|
|
5689
5816
|
page_url: window.location.href,
|
|
@@ -5700,6 +5827,7 @@ const OcsChat = class {
|
|
|
5700
5827
|
if (epoch !== this.sessionEpoch)
|
|
5701
5828
|
return;
|
|
5702
5829
|
this.activeSessionId = data.session_id;
|
|
5830
|
+
this.applySessionToken((_a = data.session_token) !== null && _a !== void 0 ? _a : undefined);
|
|
5703
5831
|
this.saveSessionToStorage();
|
|
5704
5832
|
this.startMessagePolling();
|
|
5705
5833
|
}
|
|
@@ -5732,6 +5860,10 @@ const OcsChat = class {
|
|
|
5732
5860
|
catch (error) {
|
|
5733
5861
|
if (epoch !== this.sessionEpoch)
|
|
5734
5862
|
return;
|
|
5863
|
+
if (error instanceof SessionAccessError) {
|
|
5864
|
+
this.handleSessionAccessError();
|
|
5865
|
+
return;
|
|
5866
|
+
}
|
|
5735
5867
|
console.warn('Failed to load chat history:', error);
|
|
5736
5868
|
}
|
|
5737
5869
|
this.startMessagePolling();
|
|
@@ -5747,8 +5879,12 @@ const OcsChat = class {
|
|
|
5747
5879
|
sessionId: this.activeSessionId,
|
|
5748
5880
|
participantId: this.getOrGenerateUserId(),
|
|
5749
5881
|
participantName: this.userName,
|
|
5882
|
+
headers: this.getChatService().getUploadHeaders(),
|
|
5750
5883
|
});
|
|
5751
5884
|
this.selectedFiles = uploadResult.selectedFiles;
|
|
5885
|
+
if (uploadResult.tokenRejected) {
|
|
5886
|
+
throw new SessionAccessError(403, 'session_token_required', uploadResult.errorMessage || 'Session token rejected');
|
|
5887
|
+
}
|
|
5752
5888
|
return uploadResult.uploadedIds;
|
|
5753
5889
|
}
|
|
5754
5890
|
finally {
|
|
@@ -5840,6 +5976,10 @@ const OcsChat = class {
|
|
|
5840
5976
|
catch (error) {
|
|
5841
5977
|
if (epoch !== this.sessionEpoch)
|
|
5842
5978
|
return;
|
|
5979
|
+
if (error instanceof SessionAccessError) {
|
|
5980
|
+
this.handleSessionAccessError();
|
|
5981
|
+
return;
|
|
5982
|
+
}
|
|
5843
5983
|
const errorText = error instanceof Error ? error.message : 'Failed to send message';
|
|
5844
5984
|
this.handleError(errorText);
|
|
5845
5985
|
}
|
|
@@ -6014,8 +6154,12 @@ const OcsChat = class {
|
|
|
6014
6154
|
},
|
|
6015
6155
|
onError: error => {
|
|
6016
6156
|
this.typingProgressMessage = '';
|
|
6017
|
-
this.handleError(error.message);
|
|
6018
6157
|
this.taskPollingHandle = undefined;
|
|
6158
|
+
if (error instanceof SessionAccessError) {
|
|
6159
|
+
this.handleSessionAccessError();
|
|
6160
|
+
return;
|
|
6161
|
+
}
|
|
6162
|
+
this.handleError(error.message);
|
|
6019
6163
|
this.startMessagePolling();
|
|
6020
6164
|
},
|
|
6021
6165
|
});
|
|
@@ -6377,6 +6521,7 @@ const OcsChat = class {
|
|
|
6377
6521
|
messages: `ocs-chat-messages-${this.chatbotId}`,
|
|
6378
6522
|
lastActivity: `ocs-chat-activity-${this.chatbotId}`,
|
|
6379
6523
|
visible: `ocs-chat-visible-${this.chatbotId}`,
|
|
6524
|
+
sessionToken: `ocs-chat-token-${this.chatbotId}`,
|
|
6380
6525
|
};
|
|
6381
6526
|
}
|
|
6382
6527
|
saveSessionToStorage() {
|
|
@@ -6388,6 +6533,12 @@ const OcsChat = class {
|
|
|
6388
6533
|
if (this.activeSessionId) {
|
|
6389
6534
|
localStorage.setItem(keys.sessionId, this.activeSessionId);
|
|
6390
6535
|
localStorage.setItem(keys.lastActivity, new Date().toISOString());
|
|
6536
|
+
if (this.currentSessionToken) {
|
|
6537
|
+
localStorage.setItem(keys.sessionToken, this.currentSessionToken);
|
|
6538
|
+
}
|
|
6539
|
+
else {
|
|
6540
|
+
localStorage.removeItem(keys.sessionToken);
|
|
6541
|
+
}
|
|
6391
6542
|
}
|
|
6392
6543
|
localStorage.setItem(keys.messages, JSON.stringify(this.messages));
|
|
6393
6544
|
}
|
|
@@ -6396,6 +6547,7 @@ const OcsChat = class {
|
|
|
6396
6547
|
}
|
|
6397
6548
|
}
|
|
6398
6549
|
loadSessionFromStorage() {
|
|
6550
|
+
var _a;
|
|
6399
6551
|
const keys = this.getStorageKeys();
|
|
6400
6552
|
try {
|
|
6401
6553
|
if (this.persistentSessionExpire > 0) {
|
|
@@ -6423,7 +6575,8 @@ const OcsChat = class {
|
|
|
6423
6575
|
messages = [];
|
|
6424
6576
|
}
|
|
6425
6577
|
}
|
|
6426
|
-
|
|
6578
|
+
const sessionToken = (_a = localStorage.getItem(keys.sessionToken)) !== null && _a !== void 0 ? _a : undefined;
|
|
6579
|
+
return { sessionId, messages, sessionToken };
|
|
6427
6580
|
}
|
|
6428
6581
|
catch (error) {
|
|
6429
6582
|
// fall back to starting a new session
|
|
@@ -6495,6 +6648,7 @@ const OcsChat = class {
|
|
|
6495
6648
|
localStorage.removeItem(keys.messages);
|
|
6496
6649
|
localStorage.removeItem(keys.lastActivity);
|
|
6497
6650
|
localStorage.removeItem(keys.visible);
|
|
6651
|
+
localStorage.removeItem(keys.sessionToken);
|
|
6498
6652
|
}
|
|
6499
6653
|
catch (error) {
|
|
6500
6654
|
console.warn('Failed to clear chat session from localStorage:', error);
|
|
@@ -6536,6 +6690,7 @@ const OcsChat = class {
|
|
|
6536
6690
|
// A session provided by the host page (session-id prop) cannot be cleared;
|
|
6537
6691
|
// stay bound to it. Unbound widgets start a new session on the next message.
|
|
6538
6692
|
this.activeSessionId = this.sessionId;
|
|
6693
|
+
this.applySessionToken(this.isSessionBound() ? this.sessionToken : undefined);
|
|
6539
6694
|
this.messages = [];
|
|
6540
6695
|
this.isTyping = false;
|
|
6541
6696
|
this.currentPollTaskId = '';
|