@microsoft/omnichannel-chat-widget 1.8.4-main.2e1ef38 → 1.8.4-main.2e8be66
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 +13 -3
- package/lib/cjs/common/Constants.js +4 -0
- package/lib/cjs/common/facades/FacadeChatSDK.js +235 -9
- package/lib/cjs/common/telemetry/TelemetryConstants.js +14 -0
- package/lib/cjs/common/utils.js +92 -16
- package/lib/cjs/components/citationpanestateful/CitationPaneStateful.js +2 -1
- package/lib/cjs/components/confirmationpanestateful/ConfirmationPaneStateful.js +2 -1
- package/lib/cjs/components/emailtranscriptpanestateful/EmailTranscriptPaneStateful.js +32 -13
- package/lib/cjs/components/livechatwidget/LiveChatWidget.js +9 -1
- package/lib/cjs/components/livechatwidget/common/ChatWidgetEvents.js +1 -0
- package/lib/cjs/components/livechatwidget/common/PersistentConversationHandler.js +9 -0
- package/lib/cjs/components/livechatwidget/common/authHelper.js +44 -4
- package/lib/cjs/components/livechatwidget/common/createMarkdown.js +96 -5
- package/lib/cjs/components/livechatwidget/common/endChat.js +6 -0
- package/lib/cjs/components/livechatwidget/common/initWebChatComposer.js +182 -1
- package/lib/cjs/components/livechatwidget/common/startChat.js +31 -14
- package/lib/cjs/components/livechatwidget/livechatwidgetstateful/LiveChatWidgetStateful.js +122 -4
- package/lib/cjs/components/postchatloadingpanestateful/PostChatLoadingPaneStateful.js +15 -3
- package/lib/cjs/components/postchatsurveypanestateful/PostChatSurveyPaneStateful.js +1 -0
- package/lib/cjs/components/prechatsurveypanestateful/PreChatSurveyPaneStateful.js +64 -2
- package/lib/cjs/components/prechatsurveypanestateful/common/defaultStyles/defaultGeneralPreChatSurveyPaneStyleProps.js +2 -1
- package/lib/cjs/components/webchatcontainerstateful/WebChatContainerStateful.js +98 -12
- package/lib/cjs/components/webchatcontainerstateful/common/activityConverters/convertPersistentChatHistoryMessageToActivity.js +92 -19
- package/lib/cjs/components/webchatcontainerstateful/common/defaultProps/defaultMiddlewareLocalizedTexts.js +3 -0
- package/lib/cjs/components/webchatcontainerstateful/common/defaultStyles/defaultAdaptiveCardStyles.js +3 -1
- package/lib/cjs/components/webchatcontainerstateful/common/defaultStyles/defaultWebChatStyles.js +2 -2
- package/lib/cjs/components/webchatcontainerstateful/common/utils/citationA11y.js +195 -0
- package/lib/cjs/components/webchatcontainerstateful/webchatcontroller/WebChatEventSubscribers.js +13 -2
- package/lib/cjs/components/webchatcontainerstateful/webchatcontroller/middlewares/renderingmiddlewares/activities/LazyLoadActivity.js +160 -167
- package/lib/cjs/components/webchatcontainerstateful/webchatcontroller/middlewares/renderingmiddlewares/activityMiddleware.js +27 -8
- package/lib/cjs/components/webchatcontainerstateful/webchatcontroller/middlewares/renderingmiddlewares/attachmentMiddleware.js +2 -1
- package/lib/cjs/components/webchatcontainerstateful/webchatcontroller/middlewares/renderingmiddlewares/attachments/AdaptiveCardAccessibilityWrapper.js +153 -0
- package/lib/cjs/components/webchatcontainerstateful/webchatcontroller/middlewares/renderingmiddlewares/defaultStyles/defaultAvatarTextStyles.js +1 -1
- package/lib/cjs/components/webchatcontainerstateful/webchatcontroller/middlewares/renderingmiddlewares/defaultStyles/defaultSystemMessageStyles.js +8 -3
- package/lib/cjs/components/webchatcontainerstateful/webchatcontroller/middlewares/renderingmiddlewares/defaultStyles/defaultTimestampRetryStyles.js +8 -1
- package/lib/cjs/components/webchatcontainerstateful/webchatcontroller/middlewares/renderingmiddlewares/timestamps/NotDeliveredTimestamp.js +3 -13
- package/lib/cjs/components/webchatcontainerstateful/webchatcontroller/middlewares/storemiddlewares/attachmentSentAnnouncementMiddleware.js +81 -0
- package/lib/cjs/components/webchatcontainerstateful/webchatcontroller/middlewares/storemiddlewares/attachmentUploadValidatorMiddleware.js +33 -1
- package/lib/cjs/components/webchatcontainerstateful/webchatcontroller/middlewares/storemiddlewares/localizedStringsBotInitialsMiddleware.js +52 -8
- package/lib/cjs/components/webchatcontainerstateful/webchatcontroller/notification/NotificationHandler.js +55 -1
- package/lib/cjs/contexts/common/LiveChatWidgetActionType.js +1 -0
- package/lib/cjs/contexts/common/LiveChatWidgetContextInitialState.js +6 -1
- package/lib/cjs/contexts/createReducer.js +15 -0
- package/lib/cjs/controller/componentController.js +13 -1
- package/lib/cjs/plugins/newMessageEventHandler.js +20 -3
- package/lib/esm/common/Constants.js +4 -0
- package/lib/esm/common/facades/FacadeChatSDK.js +236 -10
- package/lib/esm/common/telemetry/TelemetryConstants.js +14 -0
- package/lib/esm/common/utils.js +87 -13
- package/lib/esm/components/citationpanestateful/CitationPaneStateful.js +2 -1
- package/lib/esm/components/confirmationpanestateful/ConfirmationPaneStateful.js +2 -1
- package/lib/esm/components/emailtranscriptpanestateful/EmailTranscriptPaneStateful.js +33 -14
- package/lib/esm/components/livechatwidget/LiveChatWidget.js +9 -1
- package/lib/esm/components/livechatwidget/common/ChatWidgetEvents.js +1 -0
- package/lib/esm/components/livechatwidget/common/PersistentConversationHandler.js +9 -0
- package/lib/esm/components/livechatwidget/common/authHelper.js +44 -4
- package/lib/esm/components/livechatwidget/common/createMarkdown.js +96 -5
- package/lib/esm/components/livechatwidget/common/endChat.js +6 -0
- package/lib/esm/components/livechatwidget/common/initWebChatComposer.js +182 -1
- package/lib/esm/components/livechatwidget/common/startChat.js +31 -14
- package/lib/esm/components/livechatwidget/livechatwidgetstateful/LiveChatWidgetStateful.js +123 -5
- package/lib/esm/components/postchatloadingpanestateful/PostChatLoadingPaneStateful.js +16 -4
- package/lib/esm/components/postchatsurveypanestateful/PostChatSurveyPaneStateful.js +1 -0
- package/lib/esm/components/prechatsurveypanestateful/PreChatSurveyPaneStateful.js +65 -3
- package/lib/esm/components/prechatsurveypanestateful/common/defaultStyles/defaultGeneralPreChatSurveyPaneStyleProps.js +2 -1
- package/lib/esm/components/webchatcontainerstateful/WebChatContainerStateful.js +99 -13
- package/lib/esm/components/webchatcontainerstateful/common/activityConverters/convertPersistentChatHistoryMessageToActivity.js +92 -19
- package/lib/esm/components/webchatcontainerstateful/common/defaultProps/defaultMiddlewareLocalizedTexts.js +3 -0
- package/lib/esm/components/webchatcontainerstateful/common/defaultStyles/defaultAdaptiveCardStyles.js +3 -1
- package/lib/esm/components/webchatcontainerstateful/common/defaultStyles/defaultWebChatStyles.js +2 -2
- package/lib/esm/components/webchatcontainerstateful/common/utils/citationA11y.js +188 -0
- package/lib/esm/components/webchatcontainerstateful/webchatcontroller/WebChatEventSubscribers.js +13 -2
- package/lib/esm/components/webchatcontainerstateful/webchatcontroller/middlewares/renderingmiddlewares/activities/LazyLoadActivity.js +160 -171
- package/lib/esm/components/webchatcontainerstateful/webchatcontroller/middlewares/renderingmiddlewares/activityMiddleware.js +24 -6
- package/lib/esm/components/webchatcontainerstateful/webchatcontroller/middlewares/renderingmiddlewares/attachmentMiddleware.js +2 -1
- package/lib/esm/components/webchatcontainerstateful/webchatcontroller/middlewares/renderingmiddlewares/attachments/AdaptiveCardAccessibilityWrapper.js +145 -0
- package/lib/esm/components/webchatcontainerstateful/webchatcontroller/middlewares/renderingmiddlewares/defaultStyles/defaultAvatarTextStyles.js +1 -1
- package/lib/esm/components/webchatcontainerstateful/webchatcontroller/middlewares/renderingmiddlewares/defaultStyles/defaultSystemMessageStyles.js +8 -3
- package/lib/esm/components/webchatcontainerstateful/webchatcontroller/middlewares/renderingmiddlewares/defaultStyles/defaultTimestampRetryStyles.js +8 -1
- package/lib/esm/components/webchatcontainerstateful/webchatcontroller/middlewares/renderingmiddlewares/timestamps/NotDeliveredTimestamp.js +3 -13
- package/lib/esm/components/webchatcontainerstateful/webchatcontroller/middlewares/storemiddlewares/attachmentSentAnnouncementMiddleware.js +74 -0
- package/lib/esm/components/webchatcontainerstateful/webchatcontroller/middlewares/storemiddlewares/attachmentUploadValidatorMiddleware.js +34 -2
- package/lib/esm/components/webchatcontainerstateful/webchatcontroller/middlewares/storemiddlewares/localizedStringsBotInitialsMiddleware.js +52 -8
- package/lib/esm/components/webchatcontainerstateful/webchatcontroller/notification/NotificationHandler.js +55 -1
- package/lib/esm/contexts/common/LiveChatWidgetActionType.js +1 -0
- package/lib/esm/contexts/common/LiveChatWidgetContextInitialState.js +6 -1
- package/lib/esm/contexts/createReducer.js +15 -0
- package/lib/esm/controller/componentController.js +13 -1
- package/lib/esm/plugins/newMessageEventHandler.js +20 -3
- package/lib/types/common/Constants.d.ts +3 -0
- package/lib/types/common/facades/FacadeChatSDK.d.ts +29 -0
- package/lib/types/common/facades/types/IFacadeChatSDKInput.d.ts +3 -1
- package/lib/types/common/telemetry/TelemetryConstants.d.ts +14 -2
- package/lib/types/common/utils.d.ts +3 -1
- package/lib/types/components/livechatwidget/common/ChatWidgetEvents.d.ts +2 -1
- package/lib/types/components/livechatwidget/common/authHelper.d.ts +9 -2
- package/lib/types/components/webchatcontainerstateful/common/utils/citationA11y.d.ts +1 -0
- package/lib/types/components/webchatcontainerstateful/interfaces/IAdaptiveCardStyles.d.ts +1 -0
- package/lib/types/components/webchatcontainerstateful/webchatcontroller/middlewares/renderingmiddlewares/activities/LazyLoadActivity.d.ts +14 -38
- package/lib/types/components/webchatcontainerstateful/webchatcontroller/middlewares/renderingmiddlewares/activityMiddleware.d.ts +2 -0
- package/lib/types/components/webchatcontainerstateful/webchatcontroller/middlewares/renderingmiddlewares/attachments/AdaptiveCardAccessibilityWrapper.d.ts +18 -0
- package/lib/types/components/webchatcontainerstateful/webchatcontroller/middlewares/renderingmiddlewares/defaultStyles/defaultSystemMessageStyles.d.ts +3 -0
- package/lib/types/components/webchatcontainerstateful/webchatcontroller/middlewares/storemiddlewares/attachmentSentAnnouncementMiddleware.d.ts +12 -0
- package/lib/types/components/webchatcontainerstateful/webchatcontroller/notification/NotificationHandler.d.ts +7 -0
- package/lib/types/contexts/common/ILiveChatWidgetContext.d.ts +1 -0
- package/lib/types/contexts/common/ILiveChatWidgetLocalizedTexts.d.ts +17 -0
- package/lib/types/contexts/common/LiveChatWidgetActionType.d.ts +2 -1
- package/package.json +29 -10
|
@@ -35,16 +35,23 @@ const EmailTranscriptPaneStateful = props => {
|
|
|
35
35
|
const [state, dispatch] = (0, _useChatContextStore.default)();
|
|
36
36
|
const [facadeChatSDK] = (0, _useFacadeChatSDKStore.default)();
|
|
37
37
|
const [initialEmail, setInitialEmail] = (0, _react.useState)("");
|
|
38
|
-
|
|
38
|
+
// restoreFocus=false is used on the submit path: the notification banner
|
|
39
|
+
// (success or error) takes focus via NotificationHandler.setFocusOnNotificationCloseButton,
|
|
40
|
+
// so an intermediate restore to the chat-widget shell would otherwise cause SRs to
|
|
41
|
+
// announce "Enter <widget>" in between the dialog and the banner.
|
|
42
|
+
const closeEmailTranscriptPane = function () {
|
|
43
|
+
let restoreFocus = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : true;
|
|
39
44
|
dispatch({
|
|
40
45
|
type: _LiveChatWidgetActionType.LiveChatWidgetActionType.SET_SHOW_EMAIL_TRANSCRIPT_PANE,
|
|
41
46
|
payload: false
|
|
42
47
|
});
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
+
if (restoreFocus) {
|
|
49
|
+
const previousFocusedElementId = state.appStates.previousElementIdOnFocusBeforeModalOpen;
|
|
50
|
+
if (previousFocusedElementId) {
|
|
51
|
+
(0, _utils.setFocusOnElement)("#" + previousFocusedElementId);
|
|
52
|
+
} else {
|
|
53
|
+
(0, _utils.setFocusOnSendBox)();
|
|
54
|
+
}
|
|
48
55
|
}
|
|
49
56
|
dispatch({
|
|
50
57
|
type: _LiveChatWidgetActionType.LiveChatWidgetActionType.SET_PREVIOUS_FOCUSED_ELEMENT_ID,
|
|
@@ -55,23 +62,31 @@ const EmailTranscriptPaneStateful = props => {
|
|
|
55
62
|
const onSend = (0, _react.useCallback)(async email => {
|
|
56
63
|
var _state$domainStates;
|
|
57
64
|
const liveChatContext = state === null || state === void 0 ? void 0 : (_state$domainStates = state.domainStates) === null || _state$domainStates === void 0 ? void 0 : _state$domainStates.liveChatContext;
|
|
58
|
-
closeEmailTranscriptPane();
|
|
65
|
+
closeEmailTranscriptPane(false);
|
|
59
66
|
const chatTranscriptBody = {
|
|
60
67
|
emailAddress: email,
|
|
61
68
|
attachmentMessage: (props === null || props === void 0 ? void 0 : props.attachmentMessage) ?? "The following attachment was uploaded during the conversation:"
|
|
62
69
|
};
|
|
63
70
|
try {
|
|
64
|
-
var _state$domainStates2, _state$domainStates2$;
|
|
71
|
+
var _state$domainStates2, _state$domainStates2$, _state$domainStates3, _state$domainStates3$;
|
|
65
72
|
await (facadeChatSDK === null || facadeChatSDK === void 0 ? void 0 : facadeChatSDK.emailLiveChatTranscript(chatTranscriptBody, {
|
|
66
73
|
liveChatContext
|
|
67
74
|
}));
|
|
68
|
-
|
|
75
|
+
const successMessage = (state === null || state === void 0 ? void 0 : (_state$domainStates2 = state.domainStates) === null || _state$domainStates2 === void 0 ? void 0 : (_state$domainStates2$ = _state$domainStates2.middlewareLocalizedTexts) === null || _state$domainStates2$ === void 0 ? void 0 : _state$domainStates2$.MIDDLEWARE_BANNER_FILE_EMAIL_ADDRESS_RECORDED_SUCCESS) ?? (_defaultMiddlewareLocalizedTexts.defaultMiddlewareLocalizedTexts === null || _defaultMiddlewareLocalizedTexts.defaultMiddlewareLocalizedTexts === void 0 ? void 0 : _defaultMiddlewareLocalizedTexts.defaultMiddlewareLocalizedTexts.MIDDLEWARE_BANNER_FILE_EMAIL_ADDRESS_RECORDED_SUCCESS);
|
|
76
|
+
// Announce from a live region at document.body level so the
|
|
77
|
+
// screen reader speaks the confirmation immediately, without
|
|
78
|
+
// traversing the chat transcript on the way to the banner.
|
|
79
|
+
// Prefix with the explicit state word so SR users hear the outcome
|
|
80
|
+
// (visual users already see a success icon on the banner).
|
|
81
|
+
const successSrPrefix = (state === null || state === void 0 ? void 0 : (_state$domainStates3 = state.domainStates) === null || _state$domainStates3 === void 0 ? void 0 : (_state$domainStates3$ = _state$domainStates3.middlewareLocalizedTexts) === null || _state$domainStates3$ === void 0 ? void 0 : _state$domainStates3$.MIDDLEWARE_SR_PREFIX_SUCCESS) ?? _defaultMiddlewareLocalizedTexts.defaultMiddlewareLocalizedTexts.MIDDLEWARE_SR_PREFIX_SUCCESS;
|
|
82
|
+
(0, _utils.announceMessageImmediately)(`${successSrPrefix}${successMessage}`);
|
|
83
|
+
_NotificationHandler.NotificationHandler.notifySuccess(_NotificationScenarios.NotificationScenarios.EmailAddressSaved, successMessage);
|
|
69
84
|
_TelemetryHelper.TelemetryHelper.logActionEventToAllTelemetry(_TelemetryConstants.LogLevel.INFO, {
|
|
70
85
|
Event: _TelemetryConstants.TelemetryEvent.EmailTranscriptSent,
|
|
71
86
|
Description: "Transcript sent to email successfully."
|
|
72
87
|
});
|
|
73
88
|
} catch (ex) {
|
|
74
|
-
var _state$
|
|
89
|
+
var _state$domainStates4, _state$domainStates4$, _state$domainStates5, _state$domainStates5$;
|
|
75
90
|
_TelemetryHelper.TelemetryHelper.logActionEventToAllTelemetry(_TelemetryConstants.LogLevel.ERROR, {
|
|
76
91
|
Event: _TelemetryConstants.TelemetryEvent.EmailTranscriptFailed,
|
|
77
92
|
Description: "Email transcript failed.",
|
|
@@ -79,8 +94,11 @@ const EmailTranscriptPaneStateful = props => {
|
|
|
79
94
|
exception: ex
|
|
80
95
|
}
|
|
81
96
|
});
|
|
82
|
-
const message = (0, _utils.formatTemplateString)((state === null || state === void 0 ? void 0 : (_state$
|
|
83
|
-
|
|
97
|
+
const message = (0, _utils.formatTemplateString)((state === null || state === void 0 ? void 0 : (_state$domainStates4 = state.domainStates) === null || _state$domainStates4 === void 0 ? void 0 : (_state$domainStates4$ = _state$domainStates4.middlewareLocalizedTexts) === null || _state$domainStates4$ === void 0 ? void 0 : _state$domainStates4$.MIDDLEWARE_BANNER_FILE_EMAIL_ADDRESS_RECORDED_ERROR) ?? _defaultMiddlewareLocalizedTexts.defaultMiddlewareLocalizedTexts.MIDDLEWARE_BANNER_FILE_EMAIL_ADDRESS_RECORDED_ERROR, [email]);
|
|
98
|
+
const bannerMessage = (props === null || props === void 0 ? void 0 : props.bannerMessageOnError) ?? message;
|
|
99
|
+
const errorSrPrefix = (state === null || state === void 0 ? void 0 : (_state$domainStates5 = state.domainStates) === null || _state$domainStates5 === void 0 ? void 0 : (_state$domainStates5$ = _state$domainStates5.middlewareLocalizedTexts) === null || _state$domainStates5$ === void 0 ? void 0 : _state$domainStates5$.MIDDLEWARE_SR_PREFIX_ERROR) ?? _defaultMiddlewareLocalizedTexts.defaultMiddlewareLocalizedTexts.MIDDLEWARE_SR_PREFIX_ERROR;
|
|
100
|
+
(0, _utils.announceMessageImmediately)(`${errorSrPrefix}${bannerMessage}`);
|
|
101
|
+
_NotificationHandler.NotificationHandler.notifyError(_NotificationScenarios.NotificationScenarios.EmailTranscriptError, bannerMessage);
|
|
84
102
|
}
|
|
85
103
|
}, [props.attachmentMessage, props.bannerMessageOnError, facadeChatSDK, state.domainStates.liveChatContext]);
|
|
86
104
|
const controlProps = {
|
|
@@ -103,7 +121,7 @@ const EmailTranscriptPaneStateful = props => {
|
|
|
103
121
|
|
|
104
122
|
// Move focus to the first button
|
|
105
123
|
(0, _react.useEffect)(() => {
|
|
106
|
-
(0, _utils.preventFocusToMoveOutOfElement)(controlProps.id);
|
|
124
|
+
const cleanup = (0, _utils.preventFocusToMoveOutOfElement)(controlProps.id);
|
|
107
125
|
const focusableElements = (0, _utils.findAllFocusableElement)(`#${controlProps.id}`);
|
|
108
126
|
if (focusableElements) {
|
|
109
127
|
focusableElements[0].focus();
|
|
@@ -118,6 +136,7 @@ const EmailTranscriptPaneStateful = props => {
|
|
|
118
136
|
Event: _TelemetryConstants.TelemetryEvent.UXEmailTranscriptPaneCompleted,
|
|
119
137
|
ElapsedTimeInMilliseconds: uiTimer.milliSecondsElapsed
|
|
120
138
|
});
|
|
139
|
+
return cleanup;
|
|
121
140
|
}, [initialEmail]);
|
|
122
141
|
return /*#__PURE__*/_react.default.createElement(_react.default.Fragment, null, /*#__PURE__*/_react.default.createElement(_DimLayer.DimLayer, {
|
|
123
142
|
brightness: (controlProps === null || controlProps === void 0 ? void 0 : controlProps.brightnessValueOnDim) ?? "0.2"
|
|
@@ -35,8 +35,16 @@ const LiveChatWidget = props => {
|
|
|
35
35
|
throw new Error("chatConfig is required");
|
|
36
36
|
}
|
|
37
37
|
|
|
38
|
+
// Check configuration flags
|
|
38
39
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
39
|
-
const
|
|
40
|
+
const hasAuthClientFn = !!((_props$chatConfig = props.chatConfig) !== null && _props$chatConfig !== void 0 && (_props$chatConfig$Liv = _props$chatConfig.LiveChatConfigAuthSettings) !== null && _props$chatConfig$Liv !== void 0 && _props$chatConfig$Liv.msdyn_javascriptclientfunction);
|
|
41
|
+
const persistentChatEnabled = (0, _liveChatConfigUtils.isPersistentChatEnabled)((_props$chatConfig2 = props.chatConfig) === null || _props$chatConfig2 === void 0 ? void 0 : (_props$chatConfig2$Li = _props$chatConfig2.LiveWSAndLiveChatEngJoin) === null || _props$chatConfig2$Li === void 0 ? void 0 : _props$chatConfig2$Li.msdyn_conversationmode);
|
|
42
|
+
|
|
43
|
+
// isAuthenticatedChat determines if FacadeChatSDK should require authentication:
|
|
44
|
+
// REGULAR AUTH FLOW (config-based):
|
|
45
|
+
// - Persistent chat enabled ? always authenticated
|
|
46
|
+
// - Auth settings exist ? authenticated from start
|
|
47
|
+
const isAuthenticatedChat = persistentChatEnabled || hasAuthClientFn;
|
|
40
48
|
if (!facadeChatSDK) {
|
|
41
49
|
var _props$mock;
|
|
42
50
|
setFacadeChatSDK(new _FacadeChatSDK.FacadeChatSDK({
|
|
@@ -10,6 +10,7 @@ var ChatWidgetEvents;
|
|
|
10
10
|
ChatWidgetEvents["FETCH_PERSISTENT_CHAT_HISTORY"] = "CHAT_WIDGET/FETCH_PERSISTENT_CHAT_HISTORY";
|
|
11
11
|
ChatWidgetEvents["NO_MORE_HISTORY_AVAILABLE"] = "CHAT_WIDGET/NO_MORE_HISTORY_AVAILABLE";
|
|
12
12
|
ChatWidgetEvents["HISTORY_LOAD_ERROR"] = "CHAT_WIDGET/HISTORY_LOAD_ERROR";
|
|
13
|
+
ChatWidgetEvents["HISTORY_BATCH_LOADED"] = "CHAT_WIDGET/HISTORY_BATCH_LOADED";
|
|
13
14
|
})(ChatWidgetEvents || (ChatWidgetEvents = {}));
|
|
14
15
|
var _default = ChatWidgetEvents;
|
|
15
16
|
exports.default = _default;
|
|
@@ -133,6 +133,12 @@ let PersistentConversationHandler = /*#__PURE__*/function () {
|
|
|
133
133
|
}
|
|
134
134
|
const messagesDescOrder = (_ref = [...messages]) === null || _ref === void 0 ? void 0 : _ref.reverse();
|
|
135
135
|
this.processHistoryMessages(messagesDescOrder);
|
|
136
|
+
|
|
137
|
+
// Signal that a batch of history messages has been added to the store.
|
|
138
|
+
// LazyLoadActivity subscribes to this to apply scroll anchoring after render.
|
|
139
|
+
(0, _dispatchCustomEvent.default)(_ChatWidgetEvents.default.HISTORY_BATCH_LOADED, {
|
|
140
|
+
messageCount: messages.length
|
|
141
|
+
});
|
|
136
142
|
_TelemetryHelper.TelemetryHelper.logActionEvent(_TelemetryConstants.LogLevel.INFO, {
|
|
137
143
|
Event: _TelemetryConstants.TelemetryEvent.LCWPersistentHistoryPullCompleted,
|
|
138
144
|
Description: "History pull completed successfully",
|
|
@@ -241,6 +247,9 @@ let PersistentConversationHandler = /*#__PURE__*/function () {
|
|
|
241
247
|
value: function processMessageToActivity(message) {
|
|
242
248
|
try {
|
|
243
249
|
const activity = (0, _convertPersistentChatHistoryMessageToActivity.default)(message);
|
|
250
|
+
if (!activity) {
|
|
251
|
+
return null;
|
|
252
|
+
}
|
|
244
253
|
activity.id = activity.id || `activity-${this.count}`;
|
|
245
254
|
activity.channelData = {
|
|
246
255
|
...activity.channelData,
|
|
@@ -3,11 +3,22 @@
|
|
|
3
3
|
Object.defineProperty(exports, "__esModule", {
|
|
4
4
|
value: true
|
|
5
5
|
});
|
|
6
|
-
exports.removeAuthTokenProvider = exports.handleAuthentication = exports.getAuthClientFunction = void 0;
|
|
6
|
+
exports.removeAuthTokenProvider = exports.isMidAuthEnabled = exports.handleAuthentication = exports.getAuthClientFunction = void 0;
|
|
7
7
|
var _TelemetryConstants = require("../../../common/telemetry/TelemetryConstants");
|
|
8
8
|
var _TelemetryHelper = require("../../../common/telemetry/TelemetryHelper");
|
|
9
9
|
var _Constants = require("../../../common/Constants");
|
|
10
10
|
var _utils = require("../../../common/utils");
|
|
11
|
+
/**
|
|
12
|
+
* Check if mid-auth is enabled based on chatConfig.
|
|
13
|
+
* Mid-auth flag lives under LiveWSAndLiveChatEngJoin.msdyn_authenticatedsigninoptional.
|
|
14
|
+
*/
|
|
15
|
+
const isMidAuthEnabled = chatConfig => {
|
|
16
|
+
var _chatConfig$LiveWSAnd, _value$toString, _value$toString$call$, _value$toString$call;
|
|
17
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
18
|
+
const value = chatConfig === null || chatConfig === void 0 ? void 0 : (_chatConfig$LiveWSAnd = chatConfig.LiveWSAndLiveChatEngJoin) === null || _chatConfig$LiveWSAnd === void 0 ? void 0 : _chatConfig$LiveWSAnd.msdyn_authenticatedsigninoptional;
|
|
19
|
+
return (value === null || value === void 0 ? void 0 : (_value$toString = value.toString) === null || _value$toString === void 0 ? void 0 : (_value$toString$call$ = (_value$toString$call = _value$toString.call(value)).toLowerCase) === null || _value$toString$call$ === void 0 ? void 0 : _value$toString$call$.call(_value$toString$call)) === "true";
|
|
20
|
+
};
|
|
21
|
+
exports.isMidAuthEnabled = isMidAuthEnabled;
|
|
11
22
|
const getAuthClientFunction = chatConfig => {
|
|
12
23
|
let authClientFunction = undefined;
|
|
13
24
|
if (chatConfig !== null && chatConfig !== void 0 && chatConfig.LiveChatConfigAuthSettings) {
|
|
@@ -19,12 +30,17 @@ const getAuthClientFunction = chatConfig => {
|
|
|
19
30
|
exports.getAuthClientFunction = getAuthClientFunction;
|
|
20
31
|
const handleAuthentication = async (chatSDK, chatConfig, getAuthToken) => {
|
|
21
32
|
var _chatSDK$chatSDKConfi;
|
|
33
|
+
const midAuthEnabled = isMidAuthEnabled(chatConfig);
|
|
22
34
|
const authClientFunction = getAuthClientFunction(chatConfig);
|
|
23
35
|
if (getAuthToken && authClientFunction) {
|
|
24
36
|
_TelemetryHelper.TelemetryHelper.logActionEvent(_TelemetryConstants.LogLevel.INFO, {
|
|
25
37
|
Event: _TelemetryConstants.TelemetryEvent.GetAuthTokenCalled
|
|
26
38
|
});
|
|
27
|
-
|
|
39
|
+
|
|
40
|
+
// Only pass isMidAuthEnabled option when mid-auth is enabled.
|
|
41
|
+
const token = midAuthEnabled ? await getAuthToken(authClientFunction, {
|
|
42
|
+
isMidAuthEnabled: midAuthEnabled
|
|
43
|
+
}) : await getAuthToken(authClientFunction);
|
|
28
44
|
if (!(0, _utils.isNullOrEmptyString)(token)) {
|
|
29
45
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
30
46
|
chatSDK.setAuthTokenProvider(async () => {
|
|
@@ -35,8 +51,21 @@ const handleAuthentication = async (chatSDK, chatConfig, getAuthToken) => {
|
|
|
35
51
|
"token": token
|
|
36
52
|
};
|
|
37
53
|
} else {
|
|
38
|
-
//
|
|
39
|
-
//
|
|
54
|
+
// For mid-auth scenarios, empty token means "user not signed in" - this is expected behavior.
|
|
55
|
+
// Return result: true with empty token so caller can decide to proceed unauthenticated.
|
|
56
|
+
if (midAuthEnabled) {
|
|
57
|
+
// Expected behavior for mid-auth: user not signed in
|
|
58
|
+
_TelemetryHelper.TelemetryHelper.logActionEvent(_TelemetryConstants.LogLevel.INFO, {
|
|
59
|
+
Event: _TelemetryConstants.TelemetryEvent.GetAuthTokenCalled,
|
|
60
|
+
Description: "Mid-auth: token provider returned empty; user not signed in"
|
|
61
|
+
});
|
|
62
|
+
return {
|
|
63
|
+
"result": true,
|
|
64
|
+
"token": null
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// For non-mid-auth scenarios, empty token is an error
|
|
40
69
|
_TelemetryHelper.TelemetryHelper.logActionEvent(_TelemetryConstants.LogLevel.ERROR, {
|
|
41
70
|
Event: _TelemetryConstants.TelemetryEvent.ReceivedNullOrEmptyToken
|
|
42
71
|
});
|
|
@@ -46,6 +75,17 @@ const handleAuthentication = async (chatSDK, chatConfig, getAuthToken) => {
|
|
|
46
75
|
var _chatSDK$chatSDKConfi2;
|
|
47
76
|
const token = await ((_chatSDK$chatSDKConfi2 = chatSDK.chatSDKConfig) === null || _chatSDK$chatSDKConfi2 === void 0 ? void 0 : _chatSDK$chatSDKConfi2.getAuthToken());
|
|
48
77
|
if ((0, _utils.isNullOrEmptyString)(token)) {
|
|
78
|
+
// For mid-auth scenarios, empty token from SDK's getAuthToken is also expected
|
|
79
|
+
if (midAuthEnabled) {
|
|
80
|
+
_TelemetryHelper.TelemetryHelper.logActionEvent(_TelemetryConstants.LogLevel.INFO, {
|
|
81
|
+
Event: _TelemetryConstants.TelemetryEvent.GetAuthTokenCalled,
|
|
82
|
+
Description: "Mid-auth: SDK getAuthToken returned empty; user not signed in"
|
|
83
|
+
});
|
|
84
|
+
return {
|
|
85
|
+
"result": true,
|
|
86
|
+
"token": null
|
|
87
|
+
};
|
|
88
|
+
}
|
|
49
89
|
_TelemetryHelper.TelemetryHelper.logActionEvent(_TelemetryConstants.LogLevel.ERROR, {
|
|
50
90
|
Event: _TelemetryConstants.TelemetryEvent.ReceivedNullOrEmptyToken,
|
|
51
91
|
Description: "getAuthToken in chat SDK returns empty string"
|
|
@@ -42,10 +42,8 @@ const createMarkdown = (disableMarkdownMessageFormatting, disableNewLineMarkdown
|
|
|
42
42
|
|
|
43
43
|
markdown.disable(["strikethrough"]);
|
|
44
44
|
|
|
45
|
-
// Custom plugin to fix numbered list continuity
|
|
45
|
+
// Custom plugin to fix numbered list continuity and merge adjacent markdown links with the same href.
|
|
46
46
|
markdown.use(function (md) {
|
|
47
|
-
const originalRender = md.render.bind(md);
|
|
48
|
-
const originalRenderInline = md.renderInline.bind(md);
|
|
49
47
|
function preprocessText(text) {
|
|
50
48
|
// Handle numbered lists that come with double line breaks (knowledge article format)
|
|
51
49
|
// This ensures proper continuous numbering instead of separate lists
|
|
@@ -80,16 +78,109 @@ const createMarkdown = (disableMarkdownMessageFormatting, disableNewLineMarkdown
|
|
|
80
78
|
return result;
|
|
81
79
|
}
|
|
82
80
|
|
|
81
|
+
// Accessibility fix: when a bot emits content like "[1.](url) [View details](url)",
|
|
82
|
+
// markdown-it renders two sibling links that screen readers announce as two separate
|
|
83
|
+
// focusable links. Merge consecutive markdown link tokens with identical attributes so
|
|
84
|
+
// the number and label form one combined focusable link without discarding metadata.
|
|
85
|
+
md.core.ruler.after("inline", "merge_adjacent_same_href_links", function (state) {
|
|
86
|
+
const sortedAttrs = token => (token.attrs || []).map(attr => `${attr[0]}=${attr[1]}`).sort();
|
|
87
|
+
const hasSameAttributes = (first, second) => {
|
|
88
|
+
const firstAttrs = sortedAttrs(first);
|
|
89
|
+
const secondAttrs = sortedAttrs(second);
|
|
90
|
+
return firstAttrs.length === secondAttrs.length && firstAttrs.every((attr, index) => attr === secondAttrs[index]);
|
|
91
|
+
};
|
|
92
|
+
const collectLink = (children, index) => {
|
|
93
|
+
var _open$attrGet;
|
|
94
|
+
const open = children[index];
|
|
95
|
+
if (!open || open.type !== "link_open") {
|
|
96
|
+
return undefined;
|
|
97
|
+
}
|
|
98
|
+
const href = (_open$attrGet = open.attrGet) === null || _open$attrGet === void 0 ? void 0 : _open$attrGet.call(open, "href");
|
|
99
|
+
if (!href) {
|
|
100
|
+
return undefined;
|
|
101
|
+
}
|
|
102
|
+
let depth = 0;
|
|
103
|
+
for (let currentIndex = index; currentIndex < children.length; currentIndex++) {
|
|
104
|
+
const token = children[currentIndex];
|
|
105
|
+
if (token.type === "link_open") {
|
|
106
|
+
depth++;
|
|
107
|
+
} else if (token.type === "link_close") {
|
|
108
|
+
depth--;
|
|
109
|
+
if (depth === 0) {
|
|
110
|
+
return {
|
|
111
|
+
closeIndex: currentIndex,
|
|
112
|
+
href,
|
|
113
|
+
open
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
return undefined;
|
|
119
|
+
};
|
|
120
|
+
const getNextAdjacentLink = (children, startIndex) => {
|
|
121
|
+
const separatorTokens = [];
|
|
122
|
+
let index = startIndex;
|
|
123
|
+
while (index < children.length && children[index].type === "text" && /^\s*$/.test(children[index].content || "")) {
|
|
124
|
+
separatorTokens.push(children[index]);
|
|
125
|
+
index++;
|
|
126
|
+
}
|
|
127
|
+
const link = collectLink(children, index);
|
|
128
|
+
if (!link) {
|
|
129
|
+
return undefined;
|
|
130
|
+
}
|
|
131
|
+
return {
|
|
132
|
+
...link,
|
|
133
|
+
openIndex: index,
|
|
134
|
+
separatorTokens
|
|
135
|
+
};
|
|
136
|
+
};
|
|
137
|
+
state.tokens.forEach(blockToken => {
|
|
138
|
+
if (blockToken.type !== "inline" || !blockToken.children) {
|
|
139
|
+
return;
|
|
140
|
+
}
|
|
141
|
+
const mergedChildren = [];
|
|
142
|
+
const children = blockToken.children;
|
|
143
|
+
let index = 0;
|
|
144
|
+
while (index < children.length) {
|
|
145
|
+
const firstLink = collectLink(children, index);
|
|
146
|
+
if (!firstLink) {
|
|
147
|
+
mergedChildren.push(children[index]);
|
|
148
|
+
index++;
|
|
149
|
+
continue;
|
|
150
|
+
}
|
|
151
|
+
const linkTokens = [firstLink.open, ...children.slice(index + 1, firstLink.closeIndex)];
|
|
152
|
+
let nextIndex = firstLink.closeIndex + 1;
|
|
153
|
+
let mergedAnyLink = false;
|
|
154
|
+
let nextLink = getNextAdjacentLink(children, nextIndex);
|
|
155
|
+
while (nextLink && nextLink.href === firstLink.href && hasSameAttributes(firstLink.open, nextLink.open)) {
|
|
156
|
+
linkTokens.push(...nextLink.separatorTokens);
|
|
157
|
+
linkTokens.push(...children.slice(nextLink.openIndex + 1, nextLink.closeIndex));
|
|
158
|
+
nextIndex = nextLink.closeIndex + 1;
|
|
159
|
+
mergedAnyLink = true;
|
|
160
|
+
nextLink = getNextAdjacentLink(children, nextIndex);
|
|
161
|
+
}
|
|
162
|
+
if (mergedAnyLink) {
|
|
163
|
+
mergedChildren.push(...linkTokens, children[firstLink.closeIndex]);
|
|
164
|
+
index = nextIndex;
|
|
165
|
+
} else {
|
|
166
|
+
mergedChildren.push(children[index]);
|
|
167
|
+
index++;
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
blockToken.children = mergedChildren;
|
|
171
|
+
});
|
|
172
|
+
});
|
|
173
|
+
|
|
83
174
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
84
175
|
md.render = function (text, env) {
|
|
85
176
|
const processedText = preprocessText(text);
|
|
86
|
-
return
|
|
177
|
+
return md.renderer.render(md.parse(processedText, env), md.options, env);
|
|
87
178
|
};
|
|
88
179
|
|
|
89
180
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
90
181
|
md.renderInline = function (text, env) {
|
|
91
182
|
const processedText = preprocessText(text);
|
|
92
|
-
return
|
|
183
|
+
return md.renderer.render(md.parseInline(processedText, env), md.options, env);
|
|
93
184
|
};
|
|
94
185
|
});
|
|
95
186
|
|
|
@@ -11,6 +11,7 @@ var _renderSurveyHelpers = require("./renderSurveyHelpers");
|
|
|
11
11
|
var _omnichannelChatComponents = require("@microsoft/omnichannel-chat-components");
|
|
12
12
|
var _ConversationState = require("../../../contexts/common/ConversationState");
|
|
13
13
|
var _LazyLoadActivity = require("../../webchatcontainerstateful/webchatcontroller/middlewares/renderingmiddlewares/activities/LazyLoadActivity");
|
|
14
|
+
var _activityMiddleware = require("../../webchatcontainerstateful/webchatcontroller/middlewares/renderingmiddlewares/activityMiddleware");
|
|
14
15
|
var _LiveChatWidgetActionType = require("../../../contexts/common/LiveChatWidgetActionType");
|
|
15
16
|
var _NotificationHandler = require("../../webchatcontainerstateful/webchatcontroller/notification/NotificationHandler");
|
|
16
17
|
var _NotificationScenarios = require("../../webchatcontainerstateful/webchatcontroller/enums/NotificationScenarios");
|
|
@@ -249,6 +250,7 @@ const endChat = async (props, facadeChatSDK, state, dispatch, setAdapter, setWeb
|
|
|
249
250
|
|
|
250
251
|
// Call direct reset to ensure LazyLoadHandler gets reset regardless of broadcast timing
|
|
251
252
|
_LazyLoadActivity.LazyLoadHandler.directReset();
|
|
253
|
+
(0, _activityMiddleware.resetActivityMiddlewareCache)();
|
|
252
254
|
_omnichannelChatComponents.BroadcastService.postMessage({
|
|
253
255
|
eventName: _TelemetryConstants.BroadcastEvent.PersistentConversationReset
|
|
254
256
|
});
|
|
@@ -344,6 +346,10 @@ const closeChatStateCleanUp = dispatch => {
|
|
|
344
346
|
type: _LiveChatWidgetActionType.LiveChatWidgetActionType.SET_CITATIONS,
|
|
345
347
|
payload: {}
|
|
346
348
|
});
|
|
349
|
+
dispatch({
|
|
350
|
+
type: _LiveChatWidgetActionType.LiveChatWidgetActionType.SET_SHOW_EMAIL_TRANSCRIPT_PANE,
|
|
351
|
+
payload: false
|
|
352
|
+
});
|
|
347
353
|
|
|
348
354
|
// Dismiss the chat disconnect notification banner if it was shown
|
|
349
355
|
_NotificationHandler.NotificationHandler.dismissNotification(_NotificationScenarios.NotificationScenarios.ChatDisconnect);
|
|
@@ -16,6 +16,7 @@ var _LiveChatWidgetActionType = require("../../../contexts/common/LiveChatWidget
|
|
|
16
16
|
var _TelemetryHelper = require("../../../common/telemetry/TelemetryHelper");
|
|
17
17
|
var _WebChatStoreLoader = require("../../webchatcontainerstateful/webchatcontroller/WebChatStoreLoader");
|
|
18
18
|
var _attachmentProcessingMiddleware = _interopRequireDefault(require("../../webchatcontainerstateful/webchatcontroller/middlewares/storemiddlewares/attachmentProcessingMiddleware"));
|
|
19
|
+
var _attachmentSentAnnouncementMiddleware = _interopRequireDefault(require("../../webchatcontainerstateful/webchatcontroller/middlewares/storemiddlewares/attachmentSentAnnouncementMiddleware"));
|
|
19
20
|
var _channelDataMiddleware = _interopRequireDefault(require("../../webchatcontainerstateful/webchatcontroller/middlewares/storemiddlewares/channelDataMiddleware"));
|
|
20
21
|
var _activityMiddleware = require("../../webchatcontainerstateful/webchatcontroller/middlewares/renderingmiddlewares/activityMiddleware");
|
|
21
22
|
var _activityStatusMiddleware = require("../../webchatcontainerstateful/webchatcontroller/middlewares/renderingmiddlewares/activityStatusMiddleware");
|
|
@@ -125,7 +126,7 @@ const initWebChatComposer = (props, state, dispatch, facadeChatSDK, endChat) =>
|
|
|
125
126
|
};
|
|
126
127
|
webChatStore = (0, _botframeworkWebchat.createStore)({},
|
|
127
128
|
//initial state
|
|
128
|
-
_preProcessingMiddleware.default, _attachmentProcessingMiddleware.default, (0, _attachmentUploadValidatorMiddleware.default)((_state$domainStates$l = state.domainStates.liveChatConfig) === null || _state$domainStates$l === void 0 ? void 0 : _state$domainStates$l.allowedFileExtensions, (_state$domainStates$l2 = state.domainStates.liveChatConfig) === null || _state$domainStates$l2 === void 0 ? void 0 : _state$domainStates$l2.maxUploadFileSize, localizedTexts), (0, _customEventMiddleware.default)(_omnichannelChatComponents.BroadcastService), (0, _queueOverflowHandlerMiddleware.createQueueOverflowMiddleware)(state, dispatch), (0, _channelDataMiddleware.default)(addConversationalSurveyTagsCallback), (0, _conversationEndMiddleware.default)(conversationEndCallback, startConversationalSurveyCallback, endConversationalSurveyCallback), (0, _dataMaskingMiddleware.default)((_state$domainStates$l3 = state.domainStates.liveChatConfig) === null || _state$domainStates$l3 === void 0 ? void 0 : _state$domainStates$l3.DataMaskingInfo), _messageTimestampMiddleware.createMessageTimeStampMiddleware, _messageSequenceIdOverrideMiddleware.createMessageSequenceIdOverrideMiddleware, (0, _citationsMiddleware.createCitationsMiddleware)(state, dispatch), _gifUploadMiddleware.default, _htmlPlayerMiddleware.default, (0, _htmlTextMiddleware.default)(honorsTargetInHTMLLinks), (0, _maxMessageSizeValidator.default)(localizedTexts), _sanitizationMiddleware.default, (0, _callActionMiddleware.default)(),
|
|
129
|
+
_preProcessingMiddleware.default, _attachmentProcessingMiddleware.default, (0, _attachmentUploadValidatorMiddleware.default)((_state$domainStates$l = state.domainStates.liveChatConfig) === null || _state$domainStates$l === void 0 ? void 0 : _state$domainStates$l.allowedFileExtensions, (_state$domainStates$l2 = state.domainStates.liveChatConfig) === null || _state$domainStates$l2 === void 0 ? void 0 : _state$domainStates$l2.maxUploadFileSize, localizedTexts), _attachmentSentAnnouncementMiddleware.default, (0, _customEventMiddleware.default)(_omnichannelChatComponents.BroadcastService), (0, _queueOverflowHandlerMiddleware.createQueueOverflowMiddleware)(state, dispatch), (0, _channelDataMiddleware.default)(addConversationalSurveyTagsCallback), (0, _conversationEndMiddleware.default)(conversationEndCallback, startConversationalSurveyCallback, endConversationalSurveyCallback), (0, _dataMaskingMiddleware.default)((_state$domainStates$l3 = state.domainStates.liveChatConfig) === null || _state$domainStates$l3 === void 0 ? void 0 : _state$domainStates$l3.DataMaskingInfo), _messageTimestampMiddleware.createMessageTimeStampMiddleware, _messageSequenceIdOverrideMiddleware.createMessageSequenceIdOverrideMiddleware, (0, _citationsMiddleware.createCitationsMiddleware)(state, dispatch), _gifUploadMiddleware.default, _htmlPlayerMiddleware.default, (0, _htmlTextMiddleware.default)(honorsTargetInHTMLLinks), (0, _maxMessageSizeValidator.default)(localizedTexts), _sanitizationMiddleware.default, (0, _callActionMiddleware.default)(),
|
|
129
130
|
// Pass a callback so middleware can push initials into React context for reactivity
|
|
130
131
|
(0, _localizedStringsBotInitialsMiddleware.localizedStringsBotInitialsMiddleware)(initials => {
|
|
131
132
|
dispatch({
|
|
@@ -151,14 +152,194 @@ const initWebChatComposer = (props, state, dispatch, facadeChatSDK, endChat) =>
|
|
|
151
152
|
markdownRenderers.forEach(renderer => {
|
|
152
153
|
text = renderer.render(text);
|
|
153
154
|
});
|
|
155
|
+
|
|
156
|
+
// EXISTING sanitization (continues to work as before)
|
|
154
157
|
const config = {
|
|
155
158
|
FORBID_TAGS: ["form", "button", "script", "div", "input"],
|
|
156
159
|
FORBID_ATTR: ["action"],
|
|
157
160
|
ADD_ATTR: ["target"]
|
|
158
161
|
};
|
|
159
162
|
text = _dompurify.default.sanitize(text, config);
|
|
163
|
+
|
|
164
|
+
// MONITOR-ONLY: Test what the stricter allowlist would remove (Phase 1)
|
|
165
|
+
// This does NOT modify the text, only logs telemetry
|
|
166
|
+
// Run during browser idle time to avoid blocking message flow and adding latency
|
|
167
|
+
const textToMonitor = text; // Capture current text value
|
|
168
|
+
|
|
169
|
+
// Schedule monitoring to run during browser idle time
|
|
170
|
+
const scheduleMonitoring = () => {
|
|
171
|
+
try {
|
|
172
|
+
monitorStrictSanitization(textToMonitor, state);
|
|
173
|
+
} catch (error) {
|
|
174
|
+
// Silently catch errors to prevent blocking message flow
|
|
175
|
+
if (process.env.NODE_ENV === "development") {
|
|
176
|
+
console.error("[Monitor] HTML sanitization monitoring failed:", error);
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
};
|
|
180
|
+
|
|
181
|
+
// Use requestIdleCallback for truly idle execution, fallback to setTimeout for older browsers
|
|
182
|
+
if (typeof window !== "undefined" && "requestIdleCallback" in window) {
|
|
183
|
+
window.requestIdleCallback(scheduleMonitoring);
|
|
184
|
+
} else {
|
|
185
|
+
setTimeout(scheduleMonitoring, 0);
|
|
186
|
+
}
|
|
160
187
|
return text;
|
|
161
188
|
};
|
|
189
|
+
|
|
190
|
+
/**
|
|
191
|
+
* Monitor-only sanitization (Phase 1: Gather telemetry)
|
|
192
|
+
* Tests what a stricter allowlist-based sanitization would remove
|
|
193
|
+
* WITHOUT actually removing it. Logs telemetry for analysis.
|
|
194
|
+
*
|
|
195
|
+
* IMPORTANT: This function is wrapped in try-catch and runs asynchronously
|
|
196
|
+
* to ensure failures don't block message flow or add latency.
|
|
197
|
+
*
|
|
198
|
+
* ISOLATION: Uses a separate DOMPurify instance to completely isolate
|
|
199
|
+
* monitoring hooks from other sanitization paths (e.g., postDomPurifyActivities).
|
|
200
|
+
* The instance is garbage collected after use, no cleanup needed.
|
|
201
|
+
*
|
|
202
|
+
* @param html - The HTML text that was already sanitized with existing config
|
|
203
|
+
* @param state - Widget state containing orgId and chatId
|
|
204
|
+
*/
|
|
205
|
+
const monitorStrictSanitization = (html, state) => {
|
|
206
|
+
// Early exit for empty content
|
|
207
|
+
if (!html) return;
|
|
208
|
+
|
|
209
|
+
// Track execution time for performance monitoring
|
|
210
|
+
const startTime = performance.now();
|
|
211
|
+
try {
|
|
212
|
+
// Create a separate DOMPurify instance for monitoring
|
|
213
|
+
// This completely isolates monitoring from other sanitization paths
|
|
214
|
+
const monitorDOMPurify = (0, _dompurify.default)(window);
|
|
215
|
+
|
|
216
|
+
// Strict allowlist configuration (proposed new rules)
|
|
217
|
+
// Note: DOMPurify blocks event handlers (onclick, onerror, etc.) by default
|
|
218
|
+
const strictConfig = {
|
|
219
|
+
ALLOWED_TAGS: ["b", "strong",
|
|
220
|
+
// Bold text
|
|
221
|
+
"i", "em", "u",
|
|
222
|
+
// Italic, emphasis, underline
|
|
223
|
+
"br", "p",
|
|
224
|
+
// Line breaks and paragraphs
|
|
225
|
+
"ul", "ol", "li",
|
|
226
|
+
// Lists
|
|
227
|
+
"a" // Links (with restricted attributes)
|
|
228
|
+
],
|
|
229
|
+
|
|
230
|
+
ALLOWED_ATTR: ["href",
|
|
231
|
+
// For links (will be restricted to http/https)
|
|
232
|
+
"target",
|
|
233
|
+
// For link behavior
|
|
234
|
+
"rel" // For security (noopener, noreferrer)
|
|
235
|
+
],
|
|
236
|
+
|
|
237
|
+
FORBID_TAGS: ["img", "video", "audio",
|
|
238
|
+
// Media (tracking beacons)
|
|
239
|
+
"iframe", "object", "embed",
|
|
240
|
+
// Embedded content
|
|
241
|
+
"script", "style",
|
|
242
|
+
// Script and styling
|
|
243
|
+
"form", "input", "textarea", "button",
|
|
244
|
+
// Form elements
|
|
245
|
+
"link", "meta", "base",
|
|
246
|
+
// Document metadata
|
|
247
|
+
"div", "span" // Layout elements
|
|
248
|
+
],
|
|
249
|
+
|
|
250
|
+
FORBID_ATTR: ["style",
|
|
251
|
+
// Inline CSS
|
|
252
|
+
"action" // Form action attribute (event handlers blocked by default)
|
|
253
|
+
],
|
|
254
|
+
|
|
255
|
+
ALLOWED_URI_REGEXP: /^https?:/i,
|
|
256
|
+
ALLOW_DATA_ATTR: false,
|
|
257
|
+
ALLOW_UNKNOWN_PROTOCOLS: false
|
|
258
|
+
};
|
|
259
|
+
|
|
260
|
+
// Track what would be removed
|
|
261
|
+
const removedTags = [];
|
|
262
|
+
const removedAttributes = [];
|
|
263
|
+
|
|
264
|
+
// Add hooks to the isolated monitoring instance
|
|
265
|
+
monitorDOMPurify.addHook("uponSanitizeElement", (node, data) => {
|
|
266
|
+
try {
|
|
267
|
+
const tagName = data.tagName.toLowerCase();
|
|
268
|
+
// Filter out "body" tag which is DOMPurify's internal wrapper
|
|
269
|
+
if (node.nodeType === 1 && !strictConfig.ALLOWED_TAGS.includes(tagName) && tagName !== "body") {
|
|
270
|
+
removedTags.push(tagName);
|
|
271
|
+
}
|
|
272
|
+
} catch (hookError) {
|
|
273
|
+
// Silently ignore hook errors
|
|
274
|
+
}
|
|
275
|
+
});
|
|
276
|
+
monitorDOMPurify.addHook("uponSanitizeAttribute", (node, data) => {
|
|
277
|
+
try {
|
|
278
|
+
const attrName = data.attrName.toLowerCase();
|
|
279
|
+
if (!strictConfig.ALLOWED_ATTR.includes(attrName) && attrName !== "class" && attrName !== "id") {
|
|
280
|
+
removedAttributes.push(attrName);
|
|
281
|
+
}
|
|
282
|
+
} catch (hookError) {
|
|
283
|
+
// Silently ignore hook errors
|
|
284
|
+
}
|
|
285
|
+
});
|
|
286
|
+
|
|
287
|
+
// Run sanitization on the isolated instance (we discard the result)
|
|
288
|
+
// No cleanup needed - the instance will be garbage collected with its hooks
|
|
289
|
+
monitorDOMPurify.sanitize(html, strictConfig);
|
|
290
|
+
|
|
291
|
+
// Log telemetry if content would be affected by strict rules
|
|
292
|
+
if (removedTags.length > 0 || removedAttributes.length > 0) {
|
|
293
|
+
try {
|
|
294
|
+
var _state$domainStates, _state$domainStates$t, _state$domainStates2, _state$domainStates2$;
|
|
295
|
+
const uniqueTags = [...new Set(removedTags)];
|
|
296
|
+
const uniqueAttrs = [...new Set(removedAttributes)];
|
|
297
|
+
|
|
298
|
+
// Calculate execution time
|
|
299
|
+
const endTime = performance.now();
|
|
300
|
+
const executionTimeMs = Math.round((endTime - startTime) * 100) / 100; // Round to 2 decimal places
|
|
301
|
+
|
|
302
|
+
// Get context for telemetry (with safe fallbacks)
|
|
303
|
+
const orgId = (state === null || state === void 0 ? void 0 : (_state$domainStates = state.domainStates) === null || _state$domainStates === void 0 ? void 0 : (_state$domainStates$t = _state$domainStates.telemetryInternalData) === null || _state$domainStates$t === void 0 ? void 0 : _state$domainStates$t.orgId) || "unknown";
|
|
304
|
+
const conversationId = (state === null || state === void 0 ? void 0 : (_state$domainStates2 = state.domainStates) === null || _state$domainStates2 === void 0 ? void 0 : (_state$domainStates2$ = _state$domainStates2.chatToken) === null || _state$domainStates2$ === void 0 ? void 0 : _state$domainStates2$.chatId) || "unknown";
|
|
305
|
+
_TelemetryHelper.TelemetryHelper.logActionEvent(_TelemetryConstants.LogLevel.INFO, {
|
|
306
|
+
Event: _TelemetryConstants.TelemetryEvent.HTMLSanitized,
|
|
307
|
+
Description: "HTML content would be sanitized by stricter allowlist (monitor-only)",
|
|
308
|
+
ElapsedTimeInMilliseconds: executionTimeMs,
|
|
309
|
+
CustomProperties: {
|
|
310
|
+
OrganizationId: orgId,
|
|
311
|
+
ConversationId: conversationId,
|
|
312
|
+
RemovedTags: uniqueTags.join(", "),
|
|
313
|
+
RemovedAttributes: uniqueAttrs.join(", "),
|
|
314
|
+
Phase: "Monitor"
|
|
315
|
+
}
|
|
316
|
+
});
|
|
317
|
+
|
|
318
|
+
// Log to console in development for debugging
|
|
319
|
+
if (process.env.NODE_ENV === "development") {
|
|
320
|
+
console.warn("[Monitor] Stricter HTML sanitization would remove:", {
|
|
321
|
+
orgId,
|
|
322
|
+
conversationId,
|
|
323
|
+
removedTags: uniqueTags,
|
|
324
|
+
removedAttributes: uniqueAttrs,
|
|
325
|
+
executionTimeMs
|
|
326
|
+
});
|
|
327
|
+
}
|
|
328
|
+
} catch (telemetryError) {
|
|
329
|
+
// Silently ignore telemetry errors to prevent blocking
|
|
330
|
+
if (process.env.NODE_ENV === "development") {
|
|
331
|
+
console.error("[Monitor] Telemetry logging failed:", telemetryError);
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
} catch (error) {
|
|
336
|
+
// Catch-all for any unexpected errors
|
|
337
|
+
// Silently fail to ensure monitoring never blocks message flow
|
|
338
|
+
if (process.env.NODE_ENV === "development") {
|
|
339
|
+
console.error("[Monitor] Monitoring failed:", error);
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
};
|
|
162
343
|
function postDomPurifyActivities() {
|
|
163
344
|
_dompurify.default.addHook("afterSanitizeAttributes", function (node) {
|
|
164
345
|
const target = node.getAttribute(_Constants.Constants.Target);
|