@microsoft/omnichannel-chat-widget 1.7.8-main.bd4de53 → 1.7.8-main.d71f599

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.
@@ -148,9 +148,20 @@ class FacadeChatSDK {
148
148
  }
149
149
  }
150
150
  }
151
+ async corroborateTokenIsSet(chatSDK) {
152
+ var _chatSDK$chatSDKConfi;
153
+ // if getAuthToken is not set, it's because handleAuthentication hasnt being called
154
+ // so we need to call it
155
+ if (this.isAuthenticated && (chatSDK === null || chatSDK === void 0 ? void 0 : (_chatSDK$chatSDKConfi = chatSDK.chatSDKConfig) === null || _chatSDK$chatSDKConfi === void 0 ? void 0 : _chatSDK$chatSDKConfi.getAuthToken) === undefined) {
156
+ (0, _authHelper.handleAuthentication)(this.chatSDK, this.chatConfig, this.getAuthToken);
157
+ }
158
+ }
151
159
  async tokenRing() {
152
160
  var _this$chatSDK$chatSDK;
153
161
  if (this.disableReauthentication === true) {
162
+ // Since we are not validating the token anymore, we at least need to check if the token is set
163
+ // no need to validate anything other that the token is set
164
+ await this.corroborateTokenIsSet(this.chatSDK);
154
165
  // facade feature is disabled, so we are bypassing the re authentication and let it fail.
155
166
  return {
156
167
  result: true,
@@ -194,7 +205,7 @@ class FacadeChatSDK {
194
205
  this.expiration = 0;
195
206
  try {
196
207
  const ring = await (0, _authHelper.handleAuthentication)(this.chatSDK, this.chatConfig, this.getAuthToken);
197
- if (ring.result === true && ring.token) {
208
+ if ((ring === null || ring === void 0 ? void 0 : ring.result) === true && ring !== null && ring !== void 0 && ring.token) {
198
209
  await this.setToken(ring.token);
199
210
  _TelemetryHelper.TelemetryHelper.logFacadeChatSDKEvent(_TelemetryConstants.LogLevel.INFO, {
200
211
  Event: _TelemetryConstants.TelemetryEvent.NewTokenSuccess,
@@ -211,12 +222,12 @@ class FacadeChatSDK {
211
222
  var _ring$error, _ring$error2;
212
223
  _TelemetryHelper.TelemetryHelper.logFacadeChatSDKEvent(_TelemetryConstants.LogLevel.ERROR, {
213
224
  Event: _TelemetryConstants.TelemetryEvent.NewTokenFailed,
214
- Description: (_ring$error = ring.error) === null || _ring$error === void 0 ? void 0 : _ring$error.message,
215
- ExceptionDetails: ring.error
225
+ Description: ring === null || ring === void 0 ? void 0 : (_ring$error = ring.error) === null || _ring$error === void 0 ? void 0 : _ring$error.message,
226
+ ExceptionDetails: ring === null || ring === void 0 ? void 0 : ring.error
216
227
  });
217
228
  return {
218
229
  result: false,
219
- message: ((_ring$error2 = ring.error) === null || _ring$error2 === void 0 ? void 0 : _ring$error2.message) || "Failed to get token"
230
+ message: (ring === null || ring === void 0 ? void 0 : (_ring$error2 = ring.error) === null || _ring$error2 === void 0 ? void 0 : _ring$error2.message) || "Failed to get token"
220
231
  };
221
232
  }
222
233
  } catch (e) {
@@ -66,6 +66,8 @@ exports.BroadcastEvent = BroadcastEvent;
66
66
  BroadcastEvent["ContactIdNotFound"] = "ContactIdNotFound";
67
67
  BroadcastEvent["SyncMinimize"] = "SyncMinimize";
68
68
  BroadcastEvent["OnWidgetError"] = "OnWidgetError";
69
+ BroadcastEvent["FMLTrackingCompletedAck"] = "FMLTrackingCompletedAck";
70
+ BroadcastEvent["FMLTrackingCompleted"] = "FMLTrackingCompleted";
69
71
  })(BroadcastEvent || (exports.BroadcastEvent = BroadcastEvent = {}));
70
72
  let TelemetryEvent;
71
73
  exports.TelemetryEvent = TelemetryEvent;
@@ -206,6 +208,8 @@ exports.TelemetryEvent = TelemetryEvent;
206
208
  TelemetryEvent["MessageSent"] = "MessageSent";
207
209
  TelemetryEvent["MessageReceived"] = "MessageReceived";
208
210
  TelemetryEvent["MessageLapTrack"] = "MessageLapTrack";
211
+ TelemetryEvent["BotFirstMessageLapTrack"] = "BotFirstMessageLapTrack";
212
+ TelemetryEvent["BotFirstMessageLapTrackError"] = "BotFirstMessageLapTrackError";
209
213
  TelemetryEvent["MessageStartLapTrackError"] = "MessageStartLapTrackError";
210
214
  TelemetryEvent["MessageStopLapTrackError"] = "MessageStopLapTrackError";
211
215
  TelemetryEvent["SystemMessageReceived"] = "SystemMessageReceived";
@@ -67,11 +67,6 @@ const getChatReconnectContext = async (facadeChatSDK, chatConfig, props, isAuthe
67
67
  const chatReconnectOptionalParams = {
68
68
  reconnectId: (_props$reconnectChatP4 = props.reconnectChatPaneProps) === null || _props$reconnectChatP4 === void 0 ? void 0 : _props$reconnectChatP4.reconnectId
69
69
  };
70
- // Get auth token for getting chat reconnect context
71
- if (isAuthenticatedChat) {
72
- // handle authentication will throw error if auth token is not available, so no need to check for response
73
- await (0, _authHelper.handleAuthentication)(facadeChatSDK.getChatSDK(), chatConfig, props.getAuthToken);
74
- }
75
70
  const reconnectChatContext = await (facadeChatSDK === null || facadeChatSDK === void 0 ? void 0 : facadeChatSDK.getChatReconnectContext(chatReconnectOptionalParams));
76
71
  if (isAuthenticatedChat) {
77
72
  // remove auth token after reconnectId is fetched
@@ -7,7 +7,6 @@ exports.setPreChatAndInitiateChat = exports.prepareStartChat = exports.initStart
7
7
  var _TelemetryConstants = require("../../../common/telemetry/TelemetryConstants");
8
8
  var _Constants = require("../../../common/Constants");
9
9
  var _utils = require("../../../common/utils");
10
- var _authHelper = require("./authHelper");
11
10
  var _reconnectChatHelper = require("./reconnectChatHelper");
12
11
  var _startChatErrorHandler = require("./startChatErrorHandler");
13
12
  var _ActivityStreamHandler = require("./ActivityStreamHandler");
@@ -19,6 +18,7 @@ var _TelemetryManager = require("../../../common/telemetry/TelemetryManager");
19
18
  var _endChat = require("./endChat");
20
19
  var _createAdapter = require("./createAdapter");
21
20
  var _newMessageEventHandler = require("../../../plugins/newMessageEventHandler");
21
+ var _FirstMessageTrackerFromBot = require("../../../firstresponselatency/FirstMessageTrackerFromBot");
22
22
  var _liveChatConfigUtils = require("./liveChatConfigUtils");
23
23
  var _setPostChatContextAndLoadSurvey = require("./setPostChatContextAndLoadSurvey");
24
24
  var _persistentChatHelper = require("./persistentChatHelper");
@@ -30,20 +30,6 @@ let widgetInstanceId;
30
30
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
31
31
  let popoutWidgetInstanceId;
32
32
 
33
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
34
- const setAuthenticationIfApplicable = async (props, facadeChatSDK) => {
35
- const chatConfig = props === null || props === void 0 ? void 0 : props.chatConfig;
36
- const getAuthToken = props === null || props === void 0 ? void 0 : props.getAuthToken;
37
- const authClientFunction = (0, _authHelper.getAuthClientFunction)(chatConfig);
38
- if (getAuthToken && authClientFunction) {
39
- // set auth token to chat sdk before start chat
40
- const authSuccess = await (0, _authHelper.handleAuthentication)(facadeChatSDK.getChatSDK(), chatConfig, getAuthToken);
41
- if (!authSuccess.result) {
42
- throw new Error(_Constants.WidgetLoadCustomErrorString.AuthenticationFailedErrorString);
43
- }
44
- }
45
- };
46
-
47
33
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
48
34
  const prepareStartChat = async (props, facadeChatSDK, state, dispatch, setAdapter) => {
49
35
  optionalParams = {}; //Resetting to ensure no stale values
@@ -71,11 +57,6 @@ const prepareStartChat = async (props, facadeChatSDK, state, dispatch, setAdapte
71
57
  const isProactiveChat = state.appStates.conversationState === _ConversationState.ConversationState.ProactiveChat;
72
58
  const isPreChatEnabledInProactiveChat = state.appStates.proactiveChatStates.proactiveChatEnablePrechat;
73
59
 
74
- // Setting auth settings to OC API to retrieve existing persistent chat session before start chat if any
75
- if ((0, _reconnectChatHelper.isPersistentEnabled)(props.chatConfig)) {
76
- await setAuthenticationIfApplicable(props, facadeChatSDK);
77
- }
78
-
79
60
  //Setting PreChat and intiate chat
80
61
  await setPreChatAndInitiateChat(facadeChatSDK, dispatch, setAdapter, isProactiveChat, isPreChatEnabledInProactiveChat, state, props);
81
62
  };
@@ -151,6 +132,7 @@ const setPreChatAndInitiateChat = async (facadeChatSDK, dispatch, setAdapter, is
151
132
  const optionalParams = {
152
133
  isProactiveChat
153
134
  };
135
+ (0, _FirstMessageTrackerFromBot.createTrackingForFirstMessage)();
154
136
  await initStartChat(facadeChatSDK, dispatch, setAdapter, state, props, optionalParams);
155
137
  };
156
138
 
@@ -179,9 +161,6 @@ const initStartChat = async (facadeChatSDK, dispatch, setAdapter, state, props,
179
161
  Description: "Widget loading started"
180
162
  });
181
163
 
182
- // Auth token retrieval needs to happen during start chat to support pop-out chat
183
- await setAuthenticationIfApplicable(props, facadeChatSDK);
184
-
185
164
  //Check if chat retrieved from cache
186
165
  if (persistedState || params !== null && params !== void 0 && params.liveChatContext) {
187
166
  var _persistedState$domai, _persistedState$domai2, _persistedState$domai3, _persistedState$domai4, _persistedState$domai5;
@@ -0,0 +1,118 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.createTrackingForFirstMessage = void 0;
7
+ var _TelemetryConstants = require("../common/telemetry/TelemetryConstants");
8
+ var _omnichannelChatComponents = require("@microsoft/omnichannel-chat-components");
9
+ var _TelemetryHelper = require("../common/telemetry/TelemetryHelper");
10
+ var _util = require("./util");
11
+ // This tracker is event based, this is since we are tracking events coming from different sources
12
+ // with different timeline, therefore this is a functional approach to track the events, instead of a class based approach
13
+ const createTrackingForFirstMessage = () => {
14
+ // Reset the tracking variables
15
+ let startTracking = false;
16
+ let stopTracking = false;
17
+ let startTime = 0;
18
+ let stopTime = 0;
19
+ let stopTrackingMessage;
20
+ let flag = false;
21
+ const isMessageFromValidSender = payload => {
22
+ var _payload$tags;
23
+ // agent scenario
24
+ if (payload !== null && payload !== void 0 && (_payload$tags = payload.tags) !== null && _payload$tags !== void 0 && _payload$tags.includes("public")) {
25
+ return false;
26
+ }
27
+ return true;
28
+ };
29
+ const widgetLoadListener = _omnichannelChatComponents.BroadcastService.getMessageByEventName(_TelemetryConstants.TelemetryEvent.WidgetLoadComplete).subscribe(() => {
30
+ if (startTracking) return;
31
+ startTracking = true;
32
+ startTime = new Date().getTime();
33
+ });
34
+ const newMessageListener = _omnichannelChatComponents.BroadcastService.getMessageByEventName(_TelemetryConstants.BroadcastEvent.NewMessageReceived).subscribe(message => {
35
+ const payload = message.payload;
36
+
37
+ // we only care for bot, so we need to check if the message is from the bot
38
+ // pending to add typing message indicator signal detection
39
+
40
+ if (isMessageFromValidSender(payload)) {
41
+ if (startTracking && !stopTracking) {
42
+ stopTime = new Date().getTime();
43
+ const elapsedTime = stopTime - startTime;
44
+ stopTracking = true;
45
+ stopTrackingMessage = (0, _util.createTrackingMessage)(payload, "botMessage");
46
+ notifyFMLTrackingCompleted();
47
+ _TelemetryHelper.TelemetryHelper.logActionEvent(_TelemetryConstants.LogLevel.INFO, {
48
+ Event: _TelemetryConstants.TelemetryEvent.BotFirstMessageLapTrack,
49
+ Description: "First Message from Bot latency tracking",
50
+ CustomProperties: {
51
+ elapsedTime,
52
+ widgetLoadedAt: startTime,
53
+ botMessage: stopTrackingMessage
54
+ }
55
+ });
56
+ }
57
+ }
58
+
59
+ // this track only first message, if coming from the bot or not
60
+ // the only difference is that it logs only those from bot
61
+ disconnectListener();
62
+ });
63
+ const notifyFMLTrackingCompleted = () => {
64
+ ackListener();
65
+ // Retry sending until flag is true, but do not block the main thread
66
+ const interval = setInterval(() => {
67
+ if (flag) {
68
+ clearInterval(interval);
69
+ } else {
70
+ _omnichannelChatComponents.BroadcastService.postMessage({
71
+ eventName: _TelemetryConstants.BroadcastEvent.FMLTrackingCompleted,
72
+ payload: null
73
+ });
74
+ }
75
+ }, 100);
76
+ };
77
+ const ackListener = () => {
78
+ const listen = _omnichannelChatComponents.BroadcastService.getMessageByEventName(_TelemetryConstants.BroadcastEvent.FMLTrackingCompletedAck).subscribe(() => {
79
+ flag = true;
80
+ listen.unsubscribe();
81
+ });
82
+ };
83
+
84
+ // Rehydrate message is received when the widget is reloaded, this is to ensure that we are not tracking messages that are not part of the current conversation
85
+ // No need to keep listerning for tracking, enforcing disconnection for the listners
86
+ const rehydrateListener = _omnichannelChatComponents.BroadcastService.getMessageByEventName(_TelemetryConstants.TelemetryEvent.RehydrateMessageReceived).subscribe(() => {
87
+ startTracking = false;
88
+ stopTracking = false;
89
+ disconnectListener();
90
+ });
91
+
92
+ // Rehydrate message is received when the widget is reloaded, this is to ensure that we are not tracking messages that are not part of the current conversation
93
+ // No need to keep listerning for tracking, enforcing disconnection for the listners
94
+ const historyListener = _omnichannelChatComponents.BroadcastService.getMessageByEventName(_TelemetryConstants.BroadcastEvent.HistoryMessageReceived).subscribe(() => {
95
+ startTracking = false;
96
+ stopTracking = false;
97
+ disconnectListener();
98
+ });
99
+ const offlineNetworkListener = _omnichannelChatComponents.BroadcastService.getMessageByEventName(_TelemetryConstants.TelemetryEvent.NetworkDisconnected).subscribe(() => {
100
+ startTracking = false;
101
+ stopTracking = false;
102
+ disconnectListener();
103
+ _TelemetryHelper.TelemetryHelper.logActionEvent(_TelemetryConstants.LogLevel.INFO, {
104
+ Event: _TelemetryConstants.TelemetryEvent.BotFirstMessageLapTrackError,
105
+ Description: "Tracker Stopped due to network disconnection"
106
+ });
107
+ });
108
+
109
+ // this is to ensure that we are not tracking messages that are not part of the current conversation
110
+ const disconnectListener = () => {
111
+ historyListener.unsubscribe();
112
+ rehydrateListener.unsubscribe();
113
+ newMessageListener.unsubscribe();
114
+ widgetLoadListener.unsubscribe();
115
+ offlineNetworkListener.unsubscribe();
116
+ };
117
+ };
118
+ exports.createTrackingForFirstMessage = createTrackingForFirstMessage;
@@ -5,6 +5,7 @@ Object.defineProperty(exports, "__esModule", {
5
5
  });
6
6
  exports.FirstResponseLatencyTracker = void 0;
7
7
  var _TelemetryConstants = require("../common/telemetry/TelemetryConstants");
8
+ var _omnichannelChatComponents = require("@microsoft/omnichannel-chat-components");
8
9
  var _TelemetryHelper = require("../common/telemetry/TelemetryHelper");
9
10
  function _defineProperty(obj, key, value) { key = _toPropertyKey(key); if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
10
11
  function _toPropertyKey(arg) { var key = _toPrimitive(arg, "string"); return typeof key === "symbol" ? key : String(key); }
@@ -16,6 +17,32 @@ class FirstResponseLatencyTracker {
16
17
  _defineProperty(this, "isEnded", false);
17
18
  _defineProperty(this, "startTrackingMessage", void 0);
18
19
  _defineProperty(this, "stopTrackingMessage", void 0);
20
+ _defineProperty(this, "isReady", false);
21
+ _defineProperty(this, "offlineNetworkListener", _omnichannelChatComponents.BroadcastService.getMessageByEventName(_TelemetryConstants.TelemetryEvent.NetworkDisconnected).subscribe(() => {
22
+ this.isStarted = false;
23
+ this.isEnded = false;
24
+ _TelemetryHelper.TelemetryHelper.logActionEvent(_TelemetryConstants.LogLevel.INFO, {
25
+ Event: _TelemetryConstants.TelemetryEvent.MessageStopLapTrackError,
26
+ Description: "Tracker Stopped due to network disconnection"
27
+ });
28
+ }));
29
+ _defineProperty(this, "fmltrackingListener", _omnichannelChatComponents.BroadcastService.getMessageByEventName(_TelemetryConstants.BroadcastEvent.FMLTrackingCompleted).subscribe(() => {
30
+ this.isReady = true;
31
+ _omnichannelChatComponents.BroadcastService.postMessage({
32
+ eventName: _TelemetryConstants.BroadcastEvent.FMLTrackingCompletedAck,
33
+ payload: null
34
+ });
35
+ }));
36
+ // Rehydrate message is received when the widget is reloaded, this is to ensure that we are not tracking messages that are not part of the current conversation
37
+ // No need to keep listerning for tracking, enforcing disconnection for the listners
38
+ _defineProperty(this, "rehydrateListener", _omnichannelChatComponents.BroadcastService.getMessageByEventName(_TelemetryConstants.TelemetryEvent.RehydrateMessageReceived).subscribe(() => {
39
+ this.isReady = true;
40
+ }));
41
+ // Rehydrate message is received when the widget is reloaded, this is to ensure that we are not tracking messages that are not part of the current conversation
42
+ // No need to keep listerning for tracking, enforcing disconnection for the listners
43
+ _defineProperty(this, "historyListener", _omnichannelChatComponents.BroadcastService.getMessageByEventName(_TelemetryConstants.BroadcastEvent.HistoryMessageReceived).subscribe(() => {
44
+ this.isReady = true;
45
+ }));
19
46
  // this is a workaround to ensure in reload we track effectively the messages
20
47
  // we do have a mechanism in place to prevent log agent messages.
21
48
  this.isABotConversation = true;
@@ -35,6 +62,7 @@ class FirstResponseLatencyTracker {
35
62
 
36
63
  // Tracking Functions
37
64
  startTracking(payload) {
65
+ if (!this.isReady) return;
38
66
  // this prevents to initiate tracking for multiple incoming messages
39
67
  if (this.isStarted) {
40
68
  return;
@@ -43,11 +71,9 @@ class FirstResponseLatencyTracker {
43
71
  if (!this.isABotConversation) {
44
72
  return;
45
73
  }
46
-
47
74
  // control of states to prevent clashing of messages
48
75
  this.isStarted = true;
49
76
  this.isEnded = false;
50
-
51
77
  // The idea of using types is to enrich telemetry data
52
78
  this.startTrackingMessage = this.createTrackingMessage(payload, "userMessage");
53
79
  }
@@ -99,9 +125,6 @@ class FirstResponseLatencyTracker {
99
125
  if (!payload || !payload.Id) {
100
126
  throw new Error("Invalid payload");
101
127
  }
102
- // in the case of a reload, tracker will be paused, until last history message is received
103
- // this is because we dont have a way to identidy send messages as part of the history
104
- //if (this.inPause) return;
105
128
  this.startTracking(payload);
106
129
  } catch (e) {
107
130
  _TelemetryHelper.TelemetryHelper.logActionEvent(_TelemetryConstants.LogLevel.ERROR, {
@@ -147,6 +170,10 @@ class FirstResponseLatencyTracker {
147
170
  this.isEnded = false;
148
171
  this.startTrackingMessage = undefined;
149
172
  this.stopTrackingMessage = undefined;
173
+ this.offlineNetworkListener.unsubscribe();
174
+ this.fmltrackingListener.unsubscribe();
175
+ this.rehydrateListener.unsubscribe();
176
+ this.historyListener.unsubscribe();
150
177
  }
151
178
  }
152
179
  exports.FirstResponseLatencyTracker = FirstResponseLatencyTracker;
@@ -3,7 +3,7 @@
3
3
  Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
- exports.polyfillMessagePayloadForEvent = exports.isHistoryMessage = exports.getScenarioType = exports.buildMessagePayload = void 0;
6
+ exports.polyfillMessagePayloadForEvent = exports.isHistoryMessage = exports.getScenarioType = exports.createTrackingMessage = exports.buildMessagePayload = void 0;
7
7
  var _Constants = require("./Constants");
8
8
  var _Constants2 = require("../common/Constants");
9
9
  const isHistoryMessage = (activity, startTime) => {
@@ -82,4 +82,17 @@ const getScenarioType = activity => {
82
82
  }
83
83
  return _Constants.ScenarioType.ReceivedMessageStrategy;
84
84
  };
85
- exports.getScenarioType = getScenarioType;
85
+ exports.getScenarioType = getScenarioType;
86
+ const createTrackingMessage = (payload, type) => {
87
+ return {
88
+ Id: payload.Id,
89
+ role: payload.role,
90
+ timestamp: payload === null || payload === void 0 ? void 0 : payload.timestamp,
91
+ tags: payload.tags,
92
+ messageType: payload.messageType,
93
+ text: payload.text,
94
+ type: type,
95
+ checkTime: new Date().getTime()
96
+ };
97
+ };
98
+ exports.createTrackingMessage = createTrackingMessage;
@@ -142,9 +142,20 @@ export class FacadeChatSDK {
142
142
  }
143
143
  }
144
144
  }
145
+ async corroborateTokenIsSet(chatSDK) {
146
+ var _chatSDK$chatSDKConfi;
147
+ // if getAuthToken is not set, it's because handleAuthentication hasnt being called
148
+ // so we need to call it
149
+ if (this.isAuthenticated && (chatSDK === null || chatSDK === void 0 ? void 0 : (_chatSDK$chatSDKConfi = chatSDK.chatSDKConfig) === null || _chatSDK$chatSDKConfi === void 0 ? void 0 : _chatSDK$chatSDKConfi.getAuthToken) === undefined) {
150
+ handleAuthentication(this.chatSDK, this.chatConfig, this.getAuthToken);
151
+ }
152
+ }
145
153
  async tokenRing() {
146
154
  var _this$chatSDK$chatSDK;
147
155
  if (this.disableReauthentication === true) {
156
+ // Since we are not validating the token anymore, we at least need to check if the token is set
157
+ // no need to validate anything other that the token is set
158
+ await this.corroborateTokenIsSet(this.chatSDK);
148
159
  // facade feature is disabled, so we are bypassing the re authentication and let it fail.
149
160
  return {
150
161
  result: true,
@@ -188,7 +199,7 @@ export class FacadeChatSDK {
188
199
  this.expiration = 0;
189
200
  try {
190
201
  const ring = await handleAuthentication(this.chatSDK, this.chatConfig, this.getAuthToken);
191
- if (ring.result === true && ring.token) {
202
+ if ((ring === null || ring === void 0 ? void 0 : ring.result) === true && ring !== null && ring !== void 0 && ring.token) {
192
203
  await this.setToken(ring.token);
193
204
  TelemetryHelper.logFacadeChatSDKEvent(LogLevel.INFO, {
194
205
  Event: TelemetryEvent.NewTokenSuccess,
@@ -205,12 +216,12 @@ export class FacadeChatSDK {
205
216
  var _ring$error, _ring$error2;
206
217
  TelemetryHelper.logFacadeChatSDKEvent(LogLevel.ERROR, {
207
218
  Event: TelemetryEvent.NewTokenFailed,
208
- Description: (_ring$error = ring.error) === null || _ring$error === void 0 ? void 0 : _ring$error.message,
209
- ExceptionDetails: ring.error
219
+ Description: ring === null || ring === void 0 ? void 0 : (_ring$error = ring.error) === null || _ring$error === void 0 ? void 0 : _ring$error.message,
220
+ ExceptionDetails: ring === null || ring === void 0 ? void 0 : ring.error
210
221
  });
211
222
  return {
212
223
  result: false,
213
- message: ((_ring$error2 = ring.error) === null || _ring$error2 === void 0 ? void 0 : _ring$error2.message) || "Failed to get token"
224
+ message: (ring === null || ring === void 0 ? void 0 : (_ring$error2 = ring.error) === null || _ring$error2 === void 0 ? void 0 : _ring$error2.message) || "Failed to get token"
214
225
  };
215
226
  }
216
227
  } catch (e) {
@@ -61,6 +61,8 @@ export let BroadcastEvent;
61
61
  BroadcastEvent["ContactIdNotFound"] = "ContactIdNotFound";
62
62
  BroadcastEvent["SyncMinimize"] = "SyncMinimize";
63
63
  BroadcastEvent["OnWidgetError"] = "OnWidgetError";
64
+ BroadcastEvent["FMLTrackingCompletedAck"] = "FMLTrackingCompletedAck";
65
+ BroadcastEvent["FMLTrackingCompleted"] = "FMLTrackingCompleted";
64
66
  })(BroadcastEvent || (BroadcastEvent = {}));
65
67
  export let TelemetryEvent;
66
68
  (function (TelemetryEvent) {
@@ -200,6 +202,8 @@ export let TelemetryEvent;
200
202
  TelemetryEvent["MessageSent"] = "MessageSent";
201
203
  TelemetryEvent["MessageReceived"] = "MessageReceived";
202
204
  TelemetryEvent["MessageLapTrack"] = "MessageLapTrack";
205
+ TelemetryEvent["BotFirstMessageLapTrack"] = "BotFirstMessageLapTrack";
206
+ TelemetryEvent["BotFirstMessageLapTrackError"] = "BotFirstMessageLapTrackError";
203
207
  TelemetryEvent["MessageStartLapTrackError"] = "MessageStartLapTrackError";
204
208
  TelemetryEvent["MessageStopLapTrackError"] = "MessageStopLapTrackError";
205
209
  TelemetryEvent["SystemMessageReceived"] = "SystemMessageReceived";
@@ -2,7 +2,7 @@ import "regenerator-runtime/runtime";
2
2
  import { BroadcastEvent, LogLevel, TelemetryEvent } from "../../../common/telemetry/TelemetryConstants";
3
3
  import { ConversationMode, WidgetLoadCustomErrorString } from "../../../common/Constants";
4
4
  import { checkContactIdError, isNullOrEmptyString, isNullOrUndefined } from "../../../common/utils";
5
- import { handleAuthentication, removeAuthTokenProvider } from "./authHelper";
5
+ import { removeAuthTokenProvider } from "./authHelper";
6
6
  import { BroadcastService } from "@microsoft/omnichannel-chat-components";
7
7
  import { ConversationState } from "../../../contexts/common/ConversationState";
8
8
  import { LiveChatWidgetActionType } from "../../../contexts/common/LiveChatWidgetActionType";
@@ -61,11 +61,6 @@ const getChatReconnectContext = async (facadeChatSDK, chatConfig, props, isAuthe
61
61
  const chatReconnectOptionalParams = {
62
62
  reconnectId: (_props$reconnectChatP4 = props.reconnectChatPaneProps) === null || _props$reconnectChatP4 === void 0 ? void 0 : _props$reconnectChatP4.reconnectId
63
63
  };
64
- // Get auth token for getting chat reconnect context
65
- if (isAuthenticatedChat) {
66
- // handle authentication will throw error if auth token is not available, so no need to check for response
67
- await handleAuthentication(facadeChatSDK.getChatSDK(), chatConfig, props.getAuthToken);
68
- }
69
64
  const reconnectChatContext = await (facadeChatSDK === null || facadeChatSDK === void 0 ? void 0 : facadeChatSDK.getChatReconnectContext(chatReconnectOptionalParams));
70
65
  if (isAuthenticatedChat) {
71
66
  // remove auth token after reconnectId is fetched
@@ -1,7 +1,6 @@
1
1
  import { BroadcastEvent, LogLevel, TelemetryEvent } from "../../../common/telemetry/TelemetryConstants";
2
- import { Constants, LiveWorkItemState, WidgetLoadCustomErrorString, WidgetLoadTelemetryMessage } from "../../../common/Constants";
2
+ import { Constants, LiveWorkItemState, WidgetLoadTelemetryMessage } from "../../../common/Constants";
3
3
  import { checkContactIdError, createTimer, getConversationDetailsCall, getStateFromCache, getWidgetCacheIdfromProps, isNullOrEmptyString, isNullOrUndefined, isUndefinedOrEmpty } from "../../../common/utils";
4
- import { getAuthClientFunction, handleAuthentication } from "./authHelper";
5
4
  import { handleChatReconnect, isPersistentEnabled, isReconnectEnabled } from "./reconnectChatHelper";
6
5
  import { handleStartChatError, logWidgetLoadComplete } from "./startChatErrorHandler";
7
6
  import { ActivityStreamHandler } from "./ActivityStreamHandler";
@@ -13,6 +12,7 @@ import { TelemetryTimers } from "../../../common/telemetry/TelemetryManager";
13
12
  import { chatSDKStateCleanUp } from "./endChat";
14
13
  import { createAdapter } from "./createAdapter";
15
14
  import { createOnNewAdapterActivityHandler } from "../../../plugins/newMessageEventHandler";
15
+ import { createTrackingForFirstMessage } from "../../../firstresponselatency/FirstMessageTrackerFromBot";
16
16
  import { isPersistentChatEnabled } from "./liveChatConfigUtils";
17
17
  import { setPostChatContextAndLoadSurvey } from "./setPostChatContextAndLoadSurvey";
18
18
  import { shouldSetPreChatIfPersistentChat } from "./persistentChatHelper";
@@ -25,20 +25,6 @@ let widgetInstanceId;
25
25
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
26
26
  let popoutWidgetInstanceId;
27
27
 
28
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
29
- const setAuthenticationIfApplicable = async (props, facadeChatSDK) => {
30
- const chatConfig = props === null || props === void 0 ? void 0 : props.chatConfig;
31
- const getAuthToken = props === null || props === void 0 ? void 0 : props.getAuthToken;
32
- const authClientFunction = getAuthClientFunction(chatConfig);
33
- if (getAuthToken && authClientFunction) {
34
- // set auth token to chat sdk before start chat
35
- const authSuccess = await handleAuthentication(facadeChatSDK.getChatSDK(), chatConfig, getAuthToken);
36
- if (!authSuccess.result) {
37
- throw new Error(WidgetLoadCustomErrorString.AuthenticationFailedErrorString);
38
- }
39
- }
40
- };
41
-
42
28
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
43
29
  const prepareStartChat = async (props, facadeChatSDK, state, dispatch, setAdapter) => {
44
30
  optionalParams = {}; //Resetting to ensure no stale values
@@ -66,11 +52,6 @@ const prepareStartChat = async (props, facadeChatSDK, state, dispatch, setAdapte
66
52
  const isProactiveChat = state.appStates.conversationState === ConversationState.ProactiveChat;
67
53
  const isPreChatEnabledInProactiveChat = state.appStates.proactiveChatStates.proactiveChatEnablePrechat;
68
54
 
69
- // Setting auth settings to OC API to retrieve existing persistent chat session before start chat if any
70
- if (isPersistentEnabled(props.chatConfig)) {
71
- await setAuthenticationIfApplicable(props, facadeChatSDK);
72
- }
73
-
74
55
  //Setting PreChat and intiate chat
75
56
  await setPreChatAndInitiateChat(facadeChatSDK, dispatch, setAdapter, isProactiveChat, isPreChatEnabledInProactiveChat, state, props);
76
57
  };
@@ -145,6 +126,7 @@ const setPreChatAndInitiateChat = async (facadeChatSDK, dispatch, setAdapter, is
145
126
  const optionalParams = {
146
127
  isProactiveChat
147
128
  };
129
+ createTrackingForFirstMessage();
148
130
  await initStartChat(facadeChatSDK, dispatch, setAdapter, state, props, optionalParams);
149
131
  };
150
132
 
@@ -172,9 +154,6 @@ const initStartChat = async (facadeChatSDK, dispatch, setAdapter, state, props,
172
154
  Description: "Widget loading started"
173
155
  });
174
156
 
175
- // Auth token retrieval needs to happen during start chat to support pop-out chat
176
- await setAuthenticationIfApplicable(props, facadeChatSDK);
177
-
178
157
  //Check if chat retrieved from cache
179
158
  if (persistedState || params !== null && params !== void 0 && params.liveChatContext) {
180
159
  var _persistedState$domai, _persistedState$domai2, _persistedState$domai3, _persistedState$domai4, _persistedState$domai5;
@@ -0,0 +1,112 @@
1
+ import { BroadcastEvent, LogLevel, TelemetryEvent } from "../common/telemetry/TelemetryConstants";
2
+ import { BroadcastService } from "@microsoft/omnichannel-chat-components";
3
+ import { TelemetryHelper } from "../common/telemetry/TelemetryHelper";
4
+ import { createTrackingMessage } from "./util";
5
+
6
+ // This tracker is event based, this is since we are tracking events coming from different sources
7
+ // with different timeline, therefore this is a functional approach to track the events, instead of a class based approach
8
+ export const createTrackingForFirstMessage = () => {
9
+ // Reset the tracking variables
10
+ let startTracking = false;
11
+ let stopTracking = false;
12
+ let startTime = 0;
13
+ let stopTime = 0;
14
+ let stopTrackingMessage;
15
+ let flag = false;
16
+ const isMessageFromValidSender = payload => {
17
+ var _payload$tags;
18
+ // agent scenario
19
+ if (payload !== null && payload !== void 0 && (_payload$tags = payload.tags) !== null && _payload$tags !== void 0 && _payload$tags.includes("public")) {
20
+ return false;
21
+ }
22
+ return true;
23
+ };
24
+ const widgetLoadListener = BroadcastService.getMessageByEventName(TelemetryEvent.WidgetLoadComplete).subscribe(() => {
25
+ if (startTracking) return;
26
+ startTracking = true;
27
+ startTime = new Date().getTime();
28
+ });
29
+ const newMessageListener = BroadcastService.getMessageByEventName(BroadcastEvent.NewMessageReceived).subscribe(message => {
30
+ const payload = message.payload;
31
+
32
+ // we only care for bot, so we need to check if the message is from the bot
33
+ // pending to add typing message indicator signal detection
34
+
35
+ if (isMessageFromValidSender(payload)) {
36
+ if (startTracking && !stopTracking) {
37
+ stopTime = new Date().getTime();
38
+ const elapsedTime = stopTime - startTime;
39
+ stopTracking = true;
40
+ stopTrackingMessage = createTrackingMessage(payload, "botMessage");
41
+ notifyFMLTrackingCompleted();
42
+ TelemetryHelper.logActionEvent(LogLevel.INFO, {
43
+ Event: TelemetryEvent.BotFirstMessageLapTrack,
44
+ Description: "First Message from Bot latency tracking",
45
+ CustomProperties: {
46
+ elapsedTime,
47
+ widgetLoadedAt: startTime,
48
+ botMessage: stopTrackingMessage
49
+ }
50
+ });
51
+ }
52
+ }
53
+
54
+ // this track only first message, if coming from the bot or not
55
+ // the only difference is that it logs only those from bot
56
+ disconnectListener();
57
+ });
58
+ const notifyFMLTrackingCompleted = () => {
59
+ ackListener();
60
+ // Retry sending until flag is true, but do not block the main thread
61
+ const interval = setInterval(() => {
62
+ if (flag) {
63
+ clearInterval(interval);
64
+ } else {
65
+ BroadcastService.postMessage({
66
+ eventName: BroadcastEvent.FMLTrackingCompleted,
67
+ payload: null
68
+ });
69
+ }
70
+ }, 100);
71
+ };
72
+ const ackListener = () => {
73
+ const listen = BroadcastService.getMessageByEventName(BroadcastEvent.FMLTrackingCompletedAck).subscribe(() => {
74
+ flag = true;
75
+ listen.unsubscribe();
76
+ });
77
+ };
78
+
79
+ // Rehydrate message is received when the widget is reloaded, this is to ensure that we are not tracking messages that are not part of the current conversation
80
+ // No need to keep listerning for tracking, enforcing disconnection for the listners
81
+ const rehydrateListener = BroadcastService.getMessageByEventName(TelemetryEvent.RehydrateMessageReceived).subscribe(() => {
82
+ startTracking = false;
83
+ stopTracking = false;
84
+ disconnectListener();
85
+ });
86
+
87
+ // Rehydrate message is received when the widget is reloaded, this is to ensure that we are not tracking messages that are not part of the current conversation
88
+ // No need to keep listerning for tracking, enforcing disconnection for the listners
89
+ const historyListener = BroadcastService.getMessageByEventName(BroadcastEvent.HistoryMessageReceived).subscribe(() => {
90
+ startTracking = false;
91
+ stopTracking = false;
92
+ disconnectListener();
93
+ });
94
+ const offlineNetworkListener = BroadcastService.getMessageByEventName(TelemetryEvent.NetworkDisconnected).subscribe(() => {
95
+ startTracking = false;
96
+ stopTracking = false;
97
+ disconnectListener();
98
+ TelemetryHelper.logActionEvent(LogLevel.INFO, {
99
+ Event: TelemetryEvent.BotFirstMessageLapTrackError,
100
+ Description: "Tracker Stopped due to network disconnection"
101
+ });
102
+ });
103
+
104
+ // this is to ensure that we are not tracking messages that are not part of the current conversation
105
+ const disconnectListener = () => {
106
+ historyListener.unsubscribe();
107
+ rehydrateListener.unsubscribe();
108
+ newMessageListener.unsubscribe();
109
+ widgetLoadListener.unsubscribe();
110
+ offlineNetworkListener.unsubscribe();
111
+ };
112
+ };
@@ -1,7 +1,8 @@
1
1
  function _defineProperty(obj, key, value) { key = _toPropertyKey(key); if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
2
2
  function _toPropertyKey(arg) { var key = _toPrimitive(arg, "string"); return typeof key === "symbol" ? key : String(key); }
3
3
  function _toPrimitive(input, hint) { if (typeof input !== "object" || input === null) return input; var prim = input[Symbol.toPrimitive]; if (prim !== undefined) { var res = prim.call(input, hint || "default"); if (typeof res !== "object") return res; throw new TypeError("@@toPrimitive must return a primitive value."); } return (hint === "string" ? String : Number)(input); }
4
- import { LogLevel, TelemetryEvent } from "../common/telemetry/TelemetryConstants";
4
+ import { BroadcastEvent, LogLevel, TelemetryEvent } from "../common/telemetry/TelemetryConstants";
5
+ import { BroadcastService } from "@microsoft/omnichannel-chat-components";
5
6
  import { TelemetryHelper } from "../common/telemetry/TelemetryHelper";
6
7
  export class FirstResponseLatencyTracker {
7
8
  constructor() {
@@ -10,6 +11,32 @@ export class FirstResponseLatencyTracker {
10
11
  _defineProperty(this, "isEnded", false);
11
12
  _defineProperty(this, "startTrackingMessage", void 0);
12
13
  _defineProperty(this, "stopTrackingMessage", void 0);
14
+ _defineProperty(this, "isReady", false);
15
+ _defineProperty(this, "offlineNetworkListener", BroadcastService.getMessageByEventName(TelemetryEvent.NetworkDisconnected).subscribe(() => {
16
+ this.isStarted = false;
17
+ this.isEnded = false;
18
+ TelemetryHelper.logActionEvent(LogLevel.INFO, {
19
+ Event: TelemetryEvent.MessageStopLapTrackError,
20
+ Description: "Tracker Stopped due to network disconnection"
21
+ });
22
+ }));
23
+ _defineProperty(this, "fmltrackingListener", BroadcastService.getMessageByEventName(BroadcastEvent.FMLTrackingCompleted).subscribe(() => {
24
+ this.isReady = true;
25
+ BroadcastService.postMessage({
26
+ eventName: BroadcastEvent.FMLTrackingCompletedAck,
27
+ payload: null
28
+ });
29
+ }));
30
+ // Rehydrate message is received when the widget is reloaded, this is to ensure that we are not tracking messages that are not part of the current conversation
31
+ // No need to keep listerning for tracking, enforcing disconnection for the listners
32
+ _defineProperty(this, "rehydrateListener", BroadcastService.getMessageByEventName(TelemetryEvent.RehydrateMessageReceived).subscribe(() => {
33
+ this.isReady = true;
34
+ }));
35
+ // Rehydrate message is received when the widget is reloaded, this is to ensure that we are not tracking messages that are not part of the current conversation
36
+ // No need to keep listerning for tracking, enforcing disconnection for the listners
37
+ _defineProperty(this, "historyListener", BroadcastService.getMessageByEventName(BroadcastEvent.HistoryMessageReceived).subscribe(() => {
38
+ this.isReady = true;
39
+ }));
13
40
  // this is a workaround to ensure in reload we track effectively the messages
14
41
  // we do have a mechanism in place to prevent log agent messages.
15
42
  this.isABotConversation = true;
@@ -29,6 +56,7 @@ export class FirstResponseLatencyTracker {
29
56
 
30
57
  // Tracking Functions
31
58
  startTracking(payload) {
59
+ if (!this.isReady) return;
32
60
  // this prevents to initiate tracking for multiple incoming messages
33
61
  if (this.isStarted) {
34
62
  return;
@@ -37,11 +65,9 @@ export class FirstResponseLatencyTracker {
37
65
  if (!this.isABotConversation) {
38
66
  return;
39
67
  }
40
-
41
68
  // control of states to prevent clashing of messages
42
69
  this.isStarted = true;
43
70
  this.isEnded = false;
44
-
45
71
  // The idea of using types is to enrich telemetry data
46
72
  this.startTrackingMessage = this.createTrackingMessage(payload, "userMessage");
47
73
  }
@@ -93,9 +119,6 @@ export class FirstResponseLatencyTracker {
93
119
  if (!payload || !payload.Id) {
94
120
  throw new Error("Invalid payload");
95
121
  }
96
- // in the case of a reload, tracker will be paused, until last history message is received
97
- // this is because we dont have a way to identidy send messages as part of the history
98
- //if (this.inPause) return;
99
122
  this.startTracking(payload);
100
123
  } catch (e) {
101
124
  TelemetryHelper.logActionEvent(LogLevel.ERROR, {
@@ -141,5 +164,9 @@ export class FirstResponseLatencyTracker {
141
164
  this.isEnded = false;
142
165
  this.startTrackingMessage = undefined;
143
166
  this.stopTrackingMessage = undefined;
167
+ this.offlineNetworkListener.unsubscribe();
168
+ this.fmltrackingListener.unsubscribe();
169
+ this.rehydrateListener.unsubscribe();
170
+ this.historyListener.unsubscribe();
144
171
  }
145
172
  }
@@ -72,4 +72,16 @@ export const getScenarioType = activity => {
72
72
  return ScenarioType.SystemMessageStrategy;
73
73
  }
74
74
  return ScenarioType.ReceivedMessageStrategy;
75
+ };
76
+ export const createTrackingMessage = (payload, type) => {
77
+ return {
78
+ Id: payload.Id,
79
+ role: payload.role,
80
+ timestamp: payload === null || payload === void 0 ? void 0 : payload.timestamp,
81
+ tags: payload.tags,
82
+ messageType: payload.messageType,
83
+ text: payload.text,
84
+ type: type,
85
+ checkTime: new Date().getTime()
86
+ };
75
87
  };
@@ -43,6 +43,7 @@ export declare class FacadeChatSDK {
43
43
  private enforceBase64Encoding;
44
44
  private extractExpFromToken;
45
45
  private setToken;
46
+ private corroborateTokenIsSet;
46
47
  private tokenRing;
47
48
  private validateAndExecuteCall;
48
49
  initialize(optionalParams?: InitializeOptionalParams): Promise<ChatConfig>;
@@ -54,7 +54,9 @@ export declare enum BroadcastEvent {
54
54
  UpdateConversationDataForTelemetry = "UpdateConversationDataForTelemetry",
55
55
  ContactIdNotFound = "ContactIdNotFound",
56
56
  SyncMinimize = "SyncMinimize",
57
- OnWidgetError = "OnWidgetError"
57
+ OnWidgetError = "OnWidgetError",
58
+ FMLTrackingCompletedAck = "FMLTrackingCompletedAck",
59
+ FMLTrackingCompleted = "FMLTrackingCompleted"
58
60
  }
59
61
  export declare enum TelemetryEvent {
60
62
  CallAdded = "CallAdded",
@@ -193,6 +195,8 @@ export declare enum TelemetryEvent {
193
195
  MessageSent = "MessageSent",
194
196
  MessageReceived = "MessageReceived",
195
197
  MessageLapTrack = "MessageLapTrack",
198
+ BotFirstMessageLapTrack = "BotFirstMessageLapTrack",
199
+ BotFirstMessageLapTrackError = "BotFirstMessageLapTrackError",
196
200
  MessageStartLapTrackError = "MessageStartLapTrackError",
197
201
  MessageStopLapTrackError = "MessageStopLapTrackError",
198
202
  SystemMessageReceived = "SystemMessageReceived",
@@ -0,0 +1 @@
1
+ export declare const createTrackingForFirstMessage: () => void;
@@ -5,6 +5,7 @@ export declare class FirstResponseLatencyTracker {
5
5
  private isEnded;
6
6
  private startTrackingMessage?;
7
7
  private stopTrackingMessage?;
8
+ private isReady;
8
9
  constructor();
9
10
  private createTrackingMessage;
10
11
  private startTracking;
@@ -13,5 +14,9 @@ export declare class FirstResponseLatencyTracker {
13
14
  private isMessageFromValidSender;
14
15
  startClock(payload: MessagePayload): void;
15
16
  stopClock(payload: MessagePayload): void;
17
+ private offlineNetworkListener;
18
+ private fmltrackingListener;
19
+ private rehydrateListener;
20
+ private historyListener;
16
21
  private deregister;
17
22
  }
@@ -1,6 +1,7 @@
1
- import { MessagePayload, ScenarioType } from "./Constants";
1
+ import { MessagePayload, ScenarioType, TrackingMessage } from "./Constants";
2
2
  import { IActivity } from "botframework-directlinejs";
3
3
  export declare const isHistoryMessage: (activity: IActivity, startTime: number) => boolean;
4
4
  export declare const buildMessagePayload: (activity: IActivity, userId: string) => MessagePayload;
5
5
  export declare const polyfillMessagePayloadForEvent: (activity: IActivity, payload: MessagePayload, conversationId?: string) => MessagePayload;
6
6
  export declare const getScenarioType: (activity: IActivity) => ScenarioType;
7
+ export declare const createTrackingMessage: (payload: MessagePayload, type: string) => TrackingMessage;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@microsoft/omnichannel-chat-widget",
3
- "version": "1.7.8-main.bd4de53",
3
+ "version": "1.7.8-main.d71f599",
4
4
  "description": "Microsoft Omnichannel Chat Widget",
5
5
  "main": "lib/cjs/index.js",
6
6
  "types": "lib/types/index.d.ts",
@@ -47,6 +47,7 @@
47
47
  "babel-jest": "^27.3.1",
48
48
  "babel-loader": "^8.2.3",
49
49
  "botframework-directlinejs": "^0.15.0",
50
+ "concurrently": "^9.1.2",
50
51
  "copyfiles": "^2.4.1",
51
52
  "eslint": "^7.32.0",
52
53
  "eslint-config-standard": "^16.0.3",
@@ -56,10 +57,12 @@
56
57
  "eslint-plugin-react": "^7.26.1",
57
58
  "eslint-plugin-storybook": "^0.5.3",
58
59
  "file-loader": "^6.2.0",
60
+ "husky": "^9.1.7",
59
61
  "jest": "^27.3.1",
60
62
  "jest-dom": "^4.0.0",
61
63
  "jest-image-snapshot": "^4.5.1",
62
64
  "json": "^11.0.0",
65
+ "lint-staged": "^15.5.2",
63
66
  "p-defer": "^4.0.1",
64
67
  "playwright": "^1.20.0",
65
68
  "postcss": "^8.3.9",
@@ -67,9 +70,11 @@
67
70
  "react-docgen-typescript-plugin": "^1.0.8",
68
71
  "react-dom": "^17.0.2",
69
72
  "react-test-renderer": "^17.0.2",
73
+ "rimraf": "^6.0.1",
70
74
  "storybook-addon-playwright": "^4.9.2",
71
75
  "swiper": "^9.0.5",
72
76
  "terser-webpack-plugin": "^4.2.3",
77
+ "thread-loader": "^2.1.3",
73
78
  "ts-loader": "^9.2.6",
74
79
  "typescript": "4.9.5",
75
80
  "webpack": "^4.44.2",
@@ -97,7 +102,7 @@
97
102
  "compose-storybook": "start-storybook -c stories/.storybook -p 9009",
98
103
  "build-composite-storybook": "build-storybook -c stories/.storybook -o storybook-build",
99
104
  "build-storybook": "build-storybook",
100
- "build": "yarn lint && yarn build:esm && yarn build:cjs && tsc",
105
+ "build": "yarn clean yarn lint && yarn build:esm && yarn build:cjs && tsc",
101
106
  "test:unit": "jest -c jest.config.unit.cjs --env=jsdom --runInBand --force-exit",
102
107
  "test:e2e": "cd automation_tests && yarn test",
103
108
  "test:e2e:build": "yarn build-sample && cd automation_tests && yarn test",
@@ -105,6 +110,8 @@
105
110
  "test:all": "yarn test:unit && yarn test:visual",
106
111
  "build:esm": "babel ./src --config-file ./babel.esm.config.json --out-dir lib/esm --extensions .ts,.js,.tsx --ignore **/*.test.ts,**/*.stories.tsx,**/*.test.tsx,**/*.spec.ts,**/*.spec.tsx",
107
112
  "build:cjs": "babel ./src --config-file ./babel.config.json --out-dir lib/cjs --extensions .ts,.js,.tsx --ignore **/*.test.ts,**/*.stories.tsx,**/*.test.tsx,**/*.spec.ts,**/*.spec.tsx",
113
+ "build:esm:watch": "babel ./src --config-file ./babel.esm.config.json --out-dir lib/esm --extensions .ts,.js,.tsx --watch --ignore **/*.test.ts,**/*.stories.tsx,**/*.test.tsx,**/*.spec.ts,**/*.spec.tsx",
114
+ "build:cjs:watch": "babel ./src --config-file ./babel.config.json --out-dir lib/cjs --extensions .ts,.js,.tsx --watch --ignore **/*.test.ts,**/*.stories.tsx,**/*.test.tsx,**/*.spec.ts,**/*.spec.tsx",
108
115
  "build:umd": "webpack --config ./webpack.umd.config.cjs",
109
116
  "verify": "yarn install && yarn build-storybook && yarn test:all && yarn build && yarn storybook",
110
117
  "testpack": "yarn build && yarn pack",
@@ -112,7 +119,10 @@
112
119
  "build-sample:dev": "yarn build && webpack --config ./webpack.dev.config.cjs",
113
120
  "test:visual:build": "yarn build-storybook && yarn test:visual",
114
121
  "lint": "yarn eslint . --max-warnings=0",
115
- "pretest:visual": "yarn playwright install"
122
+ "pretest:visual": "yarn playwright install",
123
+ "prepare": "husky install",
124
+ "clean": "rimraf lib",
125
+ "dev": "concurrently \"yarn build:esm:watch\" \"webpack --config ./webpack.config.cjs --watch\""
116
126
  },
117
127
  "resolutions": {
118
128
  "**/url-parse": "1.5.9",