@xapp/chat-widget 1.110.0 → 1.111.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.css +1 -1
- package/dist/index.es.js +195 -17
- package/dist/index.es.js.map +1 -1
- package/dist/index.js +195 -17
- package/dist/index.js.map +1 -1
- package/dist/xapp/StentorRouterChat.d.ts +0 -0
- package/dist/xapp-chat-widget.css +1 -1
- package/dist/xapp-chat-widget.js +4 -4
- package/dist/xapp-chat-widget.js.map +1 -1
- package/package.json +8 -8
package/dist/index.es.js
CHANGED
|
@@ -3615,6 +3615,115 @@ function stripQueryString(url) {
|
|
|
3615
3615
|
return undefined;
|
|
3616
3616
|
}
|
|
3617
3617
|
}
|
|
3618
|
+
|
|
3619
|
+
/**
|
|
3620
|
+
* Google Analytics 4 ids for chat leads.
|
|
3621
|
+
*
|
|
3622
|
+
* A form lead's `generate_lead` can go out from the browser, but a chat lead is only
|
|
3623
|
+
* ever sent by the backend's Measurement Protocol integration. Without the visitor's
|
|
3624
|
+
* GA client and session ids that event matches no browser session, and GA4 credits
|
|
3625
|
+
* it to Direct / Unassigned. These are sent as chat attributes so the backend can
|
|
3626
|
+
* credit the lead to the traffic source that brought the visitor.
|
|
3627
|
+
*
|
|
3628
|
+
* Only the ids GA itself set are read; nothing here is PII.
|
|
3629
|
+
*/
|
|
3630
|
+
var SESSION_COOKIE_PREFIX = "_ga_";
|
|
3631
|
+
/**
|
|
3632
|
+
* Extracts the GA4 client id from a `_ga` cookie value: `GA1.1.<rand>.<ts>` ->
|
|
3633
|
+
* `<rand>.<ts>`.
|
|
3634
|
+
*/
|
|
3635
|
+
function parseGaClientId(cookie) {
|
|
3636
|
+
var parts = (cookie === null || cookie === void 0 ? void 0 : cookie.split(".")) || [];
|
|
3637
|
+
if (parts.length < 4) {
|
|
3638
|
+
return undefined;
|
|
3639
|
+
}
|
|
3640
|
+
var clientId = parts.slice(-2).join(".");
|
|
3641
|
+
return /^\d+\.\d+$/.test(clientId) ? clientId : undefined;
|
|
3642
|
+
}
|
|
3643
|
+
/**
|
|
3644
|
+
* Extracts the session id from a `_ga_<stream>` cookie value. Handles both the legacy
|
|
3645
|
+
* `GS1.1.<sid>.<n>...` format and the 2025 `GS2.1.s<sid>$o3$...` format.
|
|
3646
|
+
*/
|
|
3647
|
+
function parseGaSessionId(cookie) {
|
|
3648
|
+
var parts = (cookie === null || cookie === void 0 ? void 0 : cookie.split(".")) || [];
|
|
3649
|
+
if (parts.length < 3) {
|
|
3650
|
+
return undefined;
|
|
3651
|
+
}
|
|
3652
|
+
var sessionId;
|
|
3653
|
+
if (parts[0] === "GS2") {
|
|
3654
|
+
var token = parts[2].split("$").find(function (t) { return t.startsWith("s"); });
|
|
3655
|
+
sessionId = token === null || token === void 0 ? void 0 : token.slice(1);
|
|
3656
|
+
}
|
|
3657
|
+
else {
|
|
3658
|
+
sessionId = parts[2];
|
|
3659
|
+
}
|
|
3660
|
+
return sessionId && /^\d+$/.test(sessionId) ? sessionId : undefined;
|
|
3661
|
+
}
|
|
3662
|
+
function hasGlobalPrivacyControlOptOut() {
|
|
3663
|
+
if (typeof navigator === "undefined") {
|
|
3664
|
+
return false;
|
|
3665
|
+
}
|
|
3666
|
+
return (navigator.globalPrivacyControl === true);
|
|
3667
|
+
}
|
|
3668
|
+
function readCookies() {
|
|
3669
|
+
var cookies = {};
|
|
3670
|
+
if (typeof document === "undefined" || !document.cookie) {
|
|
3671
|
+
return cookies;
|
|
3672
|
+
}
|
|
3673
|
+
for (var _i = 0, _a = document.cookie.split(";"); _i < _a.length; _i++) {
|
|
3674
|
+
var pair = _a[_i];
|
|
3675
|
+
var index = pair.indexOf("=");
|
|
3676
|
+
if (index < 0) {
|
|
3677
|
+
continue;
|
|
3678
|
+
}
|
|
3679
|
+
var name_1 = pair.slice(0, index).trim();
|
|
3680
|
+
var raw = pair.slice(index + 1).trim();
|
|
3681
|
+
try {
|
|
3682
|
+
cookies[name_1] = decodeURIComponent(raw);
|
|
3683
|
+
}
|
|
3684
|
+
catch (_b) {
|
|
3685
|
+
// A stray "%" from another script must not break the chat.
|
|
3686
|
+
cookies[name_1] = raw;
|
|
3687
|
+
}
|
|
3688
|
+
}
|
|
3689
|
+
return cookies;
|
|
3690
|
+
}
|
|
3691
|
+
/**
|
|
3692
|
+
* The GA4 attributes to send with a chat message. Under Global Privacy Control only
|
|
3693
|
+
* the opt-out flag is sent. Never throws.
|
|
3694
|
+
*/
|
|
3695
|
+
function getGa4Attributes() {
|
|
3696
|
+
try {
|
|
3697
|
+
if (hasGlobalPrivacyControlOptOut()) {
|
|
3698
|
+
return { ga4OptOut: true };
|
|
3699
|
+
}
|
|
3700
|
+
var cookies = readCookies();
|
|
3701
|
+
var attributes = {};
|
|
3702
|
+
var gaClientId = parseGaClientId(cookies["_ga"]);
|
|
3703
|
+
if (gaClientId) {
|
|
3704
|
+
attributes.gaClientId = gaClientId;
|
|
3705
|
+
}
|
|
3706
|
+
var gaSessionIds = {};
|
|
3707
|
+
for (var _i = 0, _a = Object.entries(cookies); _i < _a.length; _i++) {
|
|
3708
|
+
var _b = _a[_i], name_2 = _b[0], value = _b[1];
|
|
3709
|
+
if (!name_2.startsWith(SESSION_COOKIE_PREFIX)) {
|
|
3710
|
+
continue;
|
|
3711
|
+
}
|
|
3712
|
+
var stream = name_2.slice(SESSION_COOKIE_PREFIX.length);
|
|
3713
|
+
var sessionId = parseGaSessionId(value);
|
|
3714
|
+
if (/^[A-Z0-9]+$/i.test(stream) && sessionId) {
|
|
3715
|
+
gaSessionIds[stream] = sessionId;
|
|
3716
|
+
}
|
|
3717
|
+
}
|
|
3718
|
+
if (Object.keys(gaSessionIds).length > 0) {
|
|
3719
|
+
attributes.gaSessionIds = gaSessionIds;
|
|
3720
|
+
}
|
|
3721
|
+
return attributes;
|
|
3722
|
+
}
|
|
3723
|
+
catch (_c) {
|
|
3724
|
+
return {};
|
|
3725
|
+
}
|
|
3726
|
+
}
|
|
3618
3727
|
/**
|
|
3619
3728
|
* A minimal client for the tenant's homeowner MCP endpoint: one `tools/call` per request, no
|
|
3620
3729
|
* session. The endpoint is public and the server keeps no state we need, so the MCP
|
|
@@ -4000,6 +4109,7 @@ var SuggestionsList_1 = SuggestionsList;
|
|
|
4000
4109
|
var addTestLeadAttribute_1 = addTestLeadAttribute;
|
|
4001
4110
|
var findItemByIndex_1 = findItemByIndex;
|
|
4002
4111
|
var getAttribution_1 = getAttribution;
|
|
4112
|
+
var getGa4Attributes_1 = getGa4Attributes;
|
|
4003
4113
|
var getItemsLength_1 = getItemsLength;
|
|
4004
4114
|
var parseSuggestionsResponse_1 = parseSuggestionsResponse;
|
|
4005
4115
|
var stripQueryString_1 = stripQueryString;
|
|
@@ -6578,16 +6688,25 @@ var StentorDirectChat = /** @class */ (function () {
|
|
|
6578
6688
|
StentorDirectChat.prototype.wakeup = function () { };
|
|
6579
6689
|
StentorDirectChat.prototype.postMessage = function (message) {
|
|
6580
6690
|
return __awaiter$1(this, void 0, void 0, function () {
|
|
6581
|
-
var request, userId, sessionId, accessToken, attributes, rwgToken, merchantId, environment, enablePreferredTime, service, origin, now, permissionRequest, expired, text, granted, userProfile, isEmail, configurableMessages, botResponse, successResult, success, fail, i, timeout, responseMessage;
|
|
6691
|
+
var request, userId, sessionId, accessToken, attributes, _i, _a, _b, key, value, rwgToken, merchantId, environment, enablePreferredTime, service, origin, now, permissionRequest, expired, text, granted, userProfile, isEmail, configurableMessages, botResponse, successResult, success, fail, i, timeout, responseMessage;
|
|
6582
6692
|
var _this = this;
|
|
6583
|
-
var
|
|
6584
|
-
return __generator$1(this, function (
|
|
6585
|
-
switch (
|
|
6693
|
+
var _c, _d, _e, _f, _g, _h;
|
|
6694
|
+
return __generator$1(this, function (_j) {
|
|
6695
|
+
switch (_j.label) {
|
|
6586
6696
|
case 0:
|
|
6587
6697
|
userId = this._userId;
|
|
6588
6698
|
sessionId = this._sessionId;
|
|
6589
6699
|
accessToken = this._accessToken;
|
|
6590
6700
|
attributes = mergeAttribution(this._attributes, getAttribution_1());
|
|
6701
|
+
// The visitor's GA client and session ids, so the backend can credit a chat lead
|
|
6702
|
+
// to the traffic source that brought them rather than Direct / Unassigned. Like the
|
|
6703
|
+
// rest of the attribution, a value the host page set itself wins.
|
|
6704
|
+
for (_i = 0, _a = Object.entries(getGa4Attributes_1()); _i < _a.length; _i++) {
|
|
6705
|
+
_b = _a[_i], key = _b[0], value = _b[1];
|
|
6706
|
+
if (((_c = this._attributes) === null || _c === void 0 ? void 0 : _c[key]) === undefined) {
|
|
6707
|
+
attributes[key] = value;
|
|
6708
|
+
}
|
|
6709
|
+
}
|
|
6591
6710
|
rwgToken = localStorage.getItem("xa_rwg_token");
|
|
6592
6711
|
if (rwgToken) {
|
|
6593
6712
|
attributes["rwg_token"] = rwgToken;
|
|
@@ -6617,10 +6736,10 @@ var StentorDirectChat = /** @class */ (function () {
|
|
|
6617
6736
|
// Add current time for accurate date/time responses
|
|
6618
6737
|
attributes["currentTime"] = new Date().toISOString();
|
|
6619
6738
|
now = new Date().getTime();
|
|
6620
|
-
if (this.isNewSession && !((
|
|
6739
|
+
if (this.isNewSession && !((_d = message === null || message === void 0 ? void 0 : message.msg) === null || _d === void 0 ? void 0 : _d.text)) {
|
|
6621
6740
|
request = {
|
|
6622
6741
|
type: "LAUNCH_REQUEST",
|
|
6623
|
-
rawQuery: (
|
|
6742
|
+
rawQuery: (_e = message === null || message === void 0 ? void 0 : message.msg) === null || _e === void 0 ? void 0 : _e.text,
|
|
6624
6743
|
sessionId: sessionId,
|
|
6625
6744
|
userId: userId,
|
|
6626
6745
|
isNewSession: true,
|
|
@@ -6632,11 +6751,11 @@ var StentorDirectChat = /** @class */ (function () {
|
|
|
6632
6751
|
};
|
|
6633
6752
|
}
|
|
6634
6753
|
else {
|
|
6635
|
-
permissionRequest = (
|
|
6754
|
+
permissionRequest = (_f = message.msg) === null || _f === void 0 ? void 0 : _f.permissionRequest;
|
|
6636
6755
|
if (permissionRequest) {
|
|
6637
6756
|
message = __assign$1(__assign$1({}, message), { msg: __assign$1(__assign$1({}, message.msg), { permissionRequest: undefined }) });
|
|
6638
6757
|
expired = now - permissionRequest.time > PERMISSION_QUESTION_EXPIRATION_MS$1;
|
|
6639
|
-
text = (
|
|
6758
|
+
text = (_g = message.msg.text) === null || _g === void 0 ? void 0 : _g.toLowerCase();
|
|
6640
6759
|
granted = !expired;
|
|
6641
6760
|
userProfile = { id: this._userId };
|
|
6642
6761
|
if (!expired) {
|
|
@@ -6734,7 +6853,7 @@ var StentorDirectChat = /** @class */ (function () {
|
|
|
6734
6853
|
});
|
|
6735
6854
|
}); };
|
|
6736
6855
|
i = 1;
|
|
6737
|
-
|
|
6856
|
+
_j.label = 1;
|
|
6738
6857
|
case 1:
|
|
6739
6858
|
if (!(i < configurableMessages.length)) return [3 /*break*/, 4];
|
|
6740
6859
|
timeout = this.config.timeout || 22000;
|
|
@@ -6750,12 +6869,12 @@ var StentorDirectChat = /** @class */ (function () {
|
|
|
6750
6869
|
.catch(fail.bind(this, i))];
|
|
6751
6870
|
case 2:
|
|
6752
6871
|
// We don't start typing IF we have a failure message displayed and we are waiting on the timeout
|
|
6753
|
-
|
|
6872
|
+
_j.sent();
|
|
6754
6873
|
// If we have what we want, break the for loop
|
|
6755
6874
|
if (successResult) {
|
|
6756
6875
|
return [3 /*break*/, 4];
|
|
6757
6876
|
}
|
|
6758
|
-
|
|
6877
|
+
_j.label = 3;
|
|
6759
6878
|
case 3:
|
|
6760
6879
|
i++;
|
|
6761
6880
|
return [3 /*break*/, 1];
|
|
@@ -6781,7 +6900,7 @@ var StentorDirectChat = /** @class */ (function () {
|
|
|
6781
6900
|
: "The assistant is not responding right now. Please close the widget and open it again.",
|
|
6782
6901
|
};
|
|
6783
6902
|
}
|
|
6784
|
-
if (typeof ((
|
|
6903
|
+
if (typeof ((_h = this.hooks) === null || _h === void 0 ? void 0 : _h.onResponse) === "function") {
|
|
6785
6904
|
this.hooks.onResponse(botResponse);
|
|
6786
6905
|
}
|
|
6787
6906
|
this.stopTyping();
|
|
@@ -7018,10 +7137,28 @@ function requestToMessage(botRequest, now) {
|
|
|
7018
7137
|
}
|
|
7019
7138
|
|
|
7020
7139
|
var ROUTER_USER = "router";
|
|
7140
|
+
/**
|
|
7141
|
+
* Shown when the server reports an error outside the router protocol and no failure
|
|
7142
|
+
* messages are configured.
|
|
7143
|
+
*/
|
|
7144
|
+
var DEFAULT_GATEWAY_ERROR_TEXT = "Sorry, we cannot communicate with the assistant right now. Please check your connection or try again later.";
|
|
7145
|
+
function isGatewayErrorFrame(frame) {
|
|
7146
|
+
if (!frame || typeof frame !== "object") {
|
|
7147
|
+
return false;
|
|
7148
|
+
}
|
|
7149
|
+
var f = frame;
|
|
7150
|
+
return f.event === undefined && typeof f.message === "string";
|
|
7151
|
+
}
|
|
7021
7152
|
var serverInfo = {
|
|
7022
7153
|
deviceId: "Router",
|
|
7023
7154
|
userId: ROUTER_USER,
|
|
7024
|
-
|
|
7155
|
+
isAdmin: false,
|
|
7156
|
+
displayName: "XAPP Server",
|
|
7157
|
+
urlAttributes: {
|
|
7158
|
+
path: [],
|
|
7159
|
+
query: {}
|
|
7160
|
+
}
|
|
7161
|
+
};
|
|
7025
7162
|
var StentorRouterChat = /** @class */ (function () {
|
|
7026
7163
|
function StentorRouterChat(config, options) {
|
|
7027
7164
|
var _a, _b, _c, _d, _e;
|
|
@@ -7034,6 +7171,8 @@ var StentorRouterChat = /** @class */ (function () {
|
|
|
7034
7171
|
this.isAdmin = false;
|
|
7035
7172
|
this.urlAttributes = {};
|
|
7036
7173
|
this.handlers = {};
|
|
7174
|
+
// Senders whose typing indicator is currently on, so it can be cleared when a request fails
|
|
7175
|
+
this.typingSenders = new Map();
|
|
7037
7176
|
// Sleep delay management
|
|
7038
7177
|
// Use number | NodeJS.Timeout for cross-environment compatibility (browser vs Node.js)
|
|
7039
7178
|
this.sleepTimeoutId = null;
|
|
@@ -7217,6 +7356,7 @@ var StentorRouterChat = /** @class */ (function () {
|
|
|
7217
7356
|
});
|
|
7218
7357
|
};
|
|
7219
7358
|
this.handlers["typing"] = function (_data, sender, ts, _attributes) {
|
|
7359
|
+
_this.typingSenders.set(senderToNick(sender), sender);
|
|
7220
7360
|
_this.dispatch({
|
|
7221
7361
|
type: "chat",
|
|
7222
7362
|
detail: {
|
|
@@ -7230,6 +7370,7 @@ var StentorRouterChat = /** @class */ (function () {
|
|
|
7230
7370
|
});
|
|
7231
7371
|
};
|
|
7232
7372
|
this.handlers["stop typing"] = function (_data, sender, ts, _attributes) {
|
|
7373
|
+
_this.typingSenders.delete(senderToNick(sender));
|
|
7233
7374
|
_this.dispatch({
|
|
7234
7375
|
type: "chat",
|
|
7235
7376
|
detail: {
|
|
@@ -7470,7 +7611,18 @@ var StentorRouterChat = /** @class */ (function () {
|
|
|
7470
7611
|
};
|
|
7471
7612
|
this.ws.onmessage = function (me) {
|
|
7472
7613
|
log("ROUTER says: ".concat(me.data));
|
|
7473
|
-
var message
|
|
7614
|
+
var message;
|
|
7615
|
+
try {
|
|
7616
|
+
message = JSON.parse(me.data);
|
|
7617
|
+
}
|
|
7618
|
+
catch (e) {
|
|
7619
|
+
err("Unparseable router message: ".concat(me.data));
|
|
7620
|
+
return;
|
|
7621
|
+
}
|
|
7622
|
+
if (isGatewayErrorFrame(message)) {
|
|
7623
|
+
_this.handleGatewayError(message);
|
|
7624
|
+
return;
|
|
7625
|
+
}
|
|
7474
7626
|
var handler = _this.handlers[message.event];
|
|
7475
7627
|
if (handler) {
|
|
7476
7628
|
_this.handlers[message.event](message.data, message.sender, message.timeMs, message.attributes, message.messageId);
|
|
@@ -7579,6 +7731,32 @@ var StentorRouterChat = /** @class */ (function () {
|
|
|
7579
7731
|
}
|
|
7580
7732
|
});
|
|
7581
7733
|
};
|
|
7734
|
+
/**
|
|
7735
|
+
* The gateway answered instead of the router (typically a Lambda timeout). The request is
|
|
7736
|
+
* lost and nothing else will arrive for it, so stop any typing indicator and tell the visitor.
|
|
7737
|
+
*/
|
|
7738
|
+
StentorRouterChat.prototype.handleGatewayError = function (frame) {
|
|
7739
|
+
var _this = this;
|
|
7740
|
+
err("SERVER: gateway error: \"".concat(frame.message, "\" (requestId: ").concat(frame.requestId || "?", ")"));
|
|
7741
|
+
this.typingSenders.forEach(function (sender) {
|
|
7742
|
+
_this.dispatch({
|
|
7743
|
+
type: "chat",
|
|
7744
|
+
detail: {
|
|
7745
|
+
type: "chat.typing",
|
|
7746
|
+
user: {
|
|
7747
|
+
nick: senderToNick(sender),
|
|
7748
|
+
},
|
|
7749
|
+
typing: false,
|
|
7750
|
+
timestamp: +new Date(),
|
|
7751
|
+
},
|
|
7752
|
+
});
|
|
7753
|
+
});
|
|
7754
|
+
this.typingSenders.clear();
|
|
7755
|
+
// The last configured message is the final, no-retry one
|
|
7756
|
+
var last = this.configurableMessages[this.configurableMessages.length - 1];
|
|
7757
|
+
var configured = last && last.text ? last : undefined;
|
|
7758
|
+
this.sendFailureMessage(configured ? configured.retry : 1, 0, configured ? configured.text : DEFAULT_GATEWAY_ERROR_TEXT, serverInfo);
|
|
7759
|
+
};
|
|
7582
7760
|
StentorRouterChat.prototype.sendDisconnectMessage = function () {
|
|
7583
7761
|
this.dispatch({
|
|
7584
7762
|
type: "chat",
|
|
@@ -16088,7 +16266,7 @@ const createQueueWithTimer = (timeout) => {
|
|
|
16088
16266
|
setTimeout(notify, timeout);
|
|
16089
16267
|
};
|
|
16090
16268
|
};
|
|
16091
|
-
const createRafWithFallbackTimer = (raf, timeout) => {
|
|
16269
|
+
const createRafWithFallbackTimer = (raf, caf, timeout) => {
|
|
16092
16270
|
return (notify) => {
|
|
16093
16271
|
let called = false;
|
|
16094
16272
|
let rafId;
|
|
@@ -16096,7 +16274,7 @@ const createRafWithFallbackTimer = (raf, timeout) => {
|
|
|
16096
16274
|
const callback = () => {
|
|
16097
16275
|
if (called) return;
|
|
16098
16276
|
called = true;
|
|
16099
|
-
|
|
16277
|
+
caf?.(rafId);
|
|
16100
16278
|
clearTimeout(timerId);
|
|
16101
16279
|
notify();
|
|
16102
16280
|
};
|
|
@@ -16110,7 +16288,7 @@ const autoBatchEnhancer = (options = { type: "raf" }) => (next) => (...args) =>
|
|
|
16110
16288
|
let shouldNotifyAtEndOfTick = false;
|
|
16111
16289
|
let notificationQueued = false;
|
|
16112
16290
|
const listeners = /* @__PURE__ */ new Set();
|
|
16113
|
-
const queueCallback = options.type === "tick" ? queueMicrotask : options.type === "raf" ? typeof window !== "undefined" && window.requestAnimationFrame ? createRafWithFallbackTimer(window.requestAnimationFrame, 100) : createQueueWithTimer(10) : options.type === "callback" ? options.queueNotification : createQueueWithTimer(options.timeout);
|
|
16291
|
+
const queueCallback = options.type === "tick" ? queueMicrotask : options.type === "raf" ? typeof window !== "undefined" && window.requestAnimationFrame ? createRafWithFallbackTimer(window.requestAnimationFrame, window.cancelAnimationFrame, 100) : createQueueWithTimer(10) : options.type === "callback" ? options.queueNotification : createQueueWithTimer(options.timeout);
|
|
16114
16292
|
const notifyListeners = () => {
|
|
16115
16293
|
notificationQueued = false;
|
|
16116
16294
|
if (shouldNotifyAtEndOfTick) {
|