@xapp/chat-widget 1.105.5 → 1.106.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.css +1 -1
- package/dist/index.es.js +148 -14
- package/dist/index.es.js.map +1 -1
- package/dist/index.js +148 -14
- package/dist/index.js.map +1 -1
- package/dist/utils/chatAgentDiscovery.d.ts +0 -0
- package/dist/utils/fingerprint.d.ts +0 -0
- package/dist/xapp/MCPChat.d.ts +0 -0
- package/dist/xapp/rateLimit.d.ts +0 -0
- package/dist/xapp-chat-widget.css +1 -1
- package/dist/xapp-chat-widget.js +5 -5
- package/dist/xapp-chat-widget.js.map +1 -1
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -3490,17 +3490,24 @@ var CLICK_ID_PARAMETERS = [
|
|
|
3490
3490
|
"fbclid"
|
|
3491
3491
|
];
|
|
3492
3492
|
var ATTRIBUTION_PARAMETERS = __spreadArray(__spreadArray([], UTM_PARAMETERS, true), CLICK_ID_PARAMETERS);
|
|
3493
|
+
/**
|
|
3494
|
+
* Params AI surfaces put on the links they send people to: Google AI Overviews
|
|
3495
|
+
* (`st_source=ai_overview`), Google's `srsltid` click id, and the generic `ref` / `source`
|
|
3496
|
+
* that ChatGPT, Perplexity and others use. Read only by the widget `load` event, never stored and
|
|
3497
|
+
* never sent with chat messages.
|
|
3498
|
+
*/
|
|
3499
|
+
var AI_REFERRAL_PARAMETERS = ["st_source", "srsltid", "ref", "source"];
|
|
3493
3500
|
var STORAGE_KEY_PREFIX = "xa_tracking_";
|
|
3494
3501
|
/** Matches the 30 day window the form widget already uses. */
|
|
3495
3502
|
var DEFAULT_PERSISTENCE_DAYS = 30;
|
|
3496
|
-
var MAX_VALUE_LENGTH = 255;
|
|
3503
|
+
var MAX_VALUE_LENGTH$1 = 255;
|
|
3497
3504
|
/**
|
|
3498
3505
|
* Values land in a request body and, downstream, in a CRM. Cap the length and
|
|
3499
3506
|
* reject anything carrying markup or control characters.
|
|
3500
3507
|
*/
|
|
3501
|
-
function isValidValue(value) {
|
|
3508
|
+
function isValidValue$1(value) {
|
|
3502
3509
|
// eslint-disable-next-line no-control-regex
|
|
3503
|
-
return value.length <= MAX_VALUE_LENGTH && !/[<>"'\x00-\x1f]/.test(value);
|
|
3510
|
+
return value.length <= MAX_VALUE_LENGTH$1 && !/[<>"'\x00-\x1f]/.test(value);
|
|
3504
3511
|
}
|
|
3505
3512
|
/**
|
|
3506
3513
|
* Reads the attribution parameters present on a URL. Never throws — a URL we
|
|
@@ -3517,7 +3524,7 @@ function extractAttributionFromUrl(url) {
|
|
|
3517
3524
|
var attribution = {};
|
|
3518
3525
|
ATTRIBUTION_PARAMETERS.forEach(function (parameter) {
|
|
3519
3526
|
var value = parsed.searchParams.get(parameter);
|
|
3520
|
-
if (value && isValidValue(value)) {
|
|
3527
|
+
if (value && isValidValue$1(value)) {
|
|
3521
3528
|
attribution[parameter] = value;
|
|
3522
3529
|
}
|
|
3523
3530
|
});
|
|
@@ -3610,6 +3617,9 @@ function stripQueryString(url) {
|
|
|
3610
3617
|
}
|
|
3611
3618
|
}
|
|
3612
3619
|
|
|
3620
|
+
/** Page params whose VALUES the load event may send, each as its own metadata field. */
|
|
3621
|
+
__spreadArray(__spreadArray([], ATTRIBUTION_PARAMETERS, true), AI_REFERRAL_PARAMETERS);
|
|
3622
|
+
|
|
3613
3623
|
/**
|
|
3614
3624
|
* Stop the host page scrolling behind an open overlay.
|
|
3615
3625
|
*
|
|
@@ -4177,6 +4187,85 @@ function isTransportFailure(error) {
|
|
|
4177
4187
|
return !(Number.isFinite(status) && status >= 100 && status <= 599);
|
|
4178
4188
|
}
|
|
4179
4189
|
|
|
4190
|
+
/**
|
|
4191
|
+
* Rate limiting, as agent-server reports it.
|
|
4192
|
+
*
|
|
4193
|
+
* On 2026-10-07 the XAPP API's WAF rate-limited agent-server and every chat failed. The widget told
|
|
4194
|
+
* visitors the chat was offline and to check their connection, and retried each message twice more —
|
|
4195
|
+
* adding load to the thing that was already over its limit. Rate limiting is the server answering:
|
|
4196
|
+
* it is not offline, an immediate retry will not help, and the visitor should be told what is going on.
|
|
4197
|
+
*
|
|
4198
|
+
* agent-server signals it two ways:
|
|
4199
|
+
* - before the stream: HTTP 429, `Retry-After`, body `{ error, code: "RATE_LIMITED", retryAfter }`
|
|
4200
|
+
* - after the stream starts: SSE `error` event `{ error, code: "RATE_LIMITED", retryAfter }`
|
|
4201
|
+
*/
|
|
4202
|
+
var RATE_LIMITED_CODE = "RATE_LIMITED";
|
|
4203
|
+
var RATE_LIMITED_STATUS = 429;
|
|
4204
|
+
/** Shown when the server did not send a message of its own (e.g. a bare 429 from a WAF). */
|
|
4205
|
+
var RATE_LIMITED_FALLBACK_MESSAGE = "We're getting more requests than usual right now. Please try again in a few minutes.";
|
|
4206
|
+
function isRateLimitError(error) {
|
|
4207
|
+
return (error === null || error === void 0 ? void 0 : error.code) === RATE_LIMITED_CODE;
|
|
4208
|
+
}
|
|
4209
|
+
function createRateLimitError(serverMessage, retryAfter) {
|
|
4210
|
+
var message = serverMessage || RATE_LIMITED_FALLBACK_MESSAGE;
|
|
4211
|
+
var error = new Error(message);
|
|
4212
|
+
error.statusCode = RATE_LIMITED_STATUS;
|
|
4213
|
+
error.code = RATE_LIMITED_CODE;
|
|
4214
|
+
if (retryAfter !== undefined) {
|
|
4215
|
+
error.retryAfter = retryAfter;
|
|
4216
|
+
}
|
|
4217
|
+
if (serverMessage) {
|
|
4218
|
+
error.serverMessage = serverMessage;
|
|
4219
|
+
}
|
|
4220
|
+
return error;
|
|
4221
|
+
}
|
|
4222
|
+
/** Seconds from a `retryAfter` body field or a `Retry-After` header. HTTP-date values are ignored. */
|
|
4223
|
+
function parseRetryAfter(value) {
|
|
4224
|
+
if (value === null || value === undefined || value === "") {
|
|
4225
|
+
return undefined;
|
|
4226
|
+
}
|
|
4227
|
+
var seconds = Number(value);
|
|
4228
|
+
return Number.isFinite(seconds) && seconds >= 0 ? seconds : undefined;
|
|
4229
|
+
}
|
|
4230
|
+
/**
|
|
4231
|
+
* Builds the error for a 429 response, reading the agent-server JSON body when there is one. The body
|
|
4232
|
+
* is optional: a 429 from a WAF or proxy in front of agent-server will not carry it.
|
|
4233
|
+
*/
|
|
4234
|
+
function rateLimitErrorFromResponse(response) {
|
|
4235
|
+
return __awaiter$1(this, void 0, void 0, function () {
|
|
4236
|
+
var body, text, _a, parsed, serverMessage, retryAfter;
|
|
4237
|
+
var _c, _d;
|
|
4238
|
+
return __generator$1(this, function (_e) {
|
|
4239
|
+
switch (_e.label) {
|
|
4240
|
+
case 0:
|
|
4241
|
+
_e.trys.push([0, 4, , 5]);
|
|
4242
|
+
if (!response.text) return [3 /*break*/, 2];
|
|
4243
|
+
return [4 /*yield*/, response.text()];
|
|
4244
|
+
case 1:
|
|
4245
|
+
_a = _e.sent();
|
|
4246
|
+
return [3 /*break*/, 3];
|
|
4247
|
+
case 2:
|
|
4248
|
+
_a = "";
|
|
4249
|
+
_e.label = 3;
|
|
4250
|
+
case 3:
|
|
4251
|
+
text = _a;
|
|
4252
|
+
parsed = text ? JSON.parse(text) : undefined;
|
|
4253
|
+
if (parsed && typeof parsed === "object") {
|
|
4254
|
+
body = parsed;
|
|
4255
|
+
}
|
|
4256
|
+
return [3 /*break*/, 5];
|
|
4257
|
+
case 4:
|
|
4258
|
+
_e.sent();
|
|
4259
|
+
return [3 /*break*/, 5];
|
|
4260
|
+
case 5:
|
|
4261
|
+
serverMessage = typeof (body === null || body === void 0 ? void 0 : body.error) === "string" && body.error.trim() ? body.error : undefined;
|
|
4262
|
+
retryAfter = (_c = parseRetryAfter(body === null || body === void 0 ? void 0 : body.retryAfter)) !== null && _c !== void 0 ? _c : parseRetryAfter((_d = response.headers) === null || _d === void 0 ? void 0 : _d.get("Retry-After"));
|
|
4263
|
+
return [2 /*return*/, createRateLimitError(serverMessage, retryAfter)];
|
|
4264
|
+
}
|
|
4265
|
+
});
|
|
4266
|
+
});
|
|
4267
|
+
}
|
|
4268
|
+
|
|
4180
4269
|
/**
|
|
4181
4270
|
* Default configuration messages
|
|
4182
4271
|
* @returns
|
|
@@ -4575,6 +4664,13 @@ var MCPChat = /** @class */ (function () {
|
|
|
4575
4664
|
case 3:
|
|
4576
4665
|
error_1 = _a.sent();
|
|
4577
4666
|
err("Failed to validate session: ".concat(error_1));
|
|
4667
|
+
if (isRateLimitError(error_1)) {
|
|
4668
|
+
// Starting a new session would be a second request into the same limit, and would
|
|
4669
|
+
// throw away a session that is still good. Keep it and tell the visitor.
|
|
4670
|
+
this.stopTyping();
|
|
4671
|
+
this.sendFailureMessage(0, 0, this.getErrorMessage(error_1, RATE_LIMITED_STATUS));
|
|
4672
|
+
return [2 /*return*/];
|
|
4673
|
+
}
|
|
4578
4674
|
// If validation fails (network error, etc.), clear session and start fresh
|
|
4579
4675
|
clearStoredSession(this._sessionId);
|
|
4580
4676
|
this._sessionId = "";
|
|
@@ -4801,6 +4897,13 @@ var MCPChat = /** @class */ (function () {
|
|
|
4801
4897
|
* Determines a user-friendly error message based on the error type.
|
|
4802
4898
|
*/
|
|
4803
4899
|
MCPChat.prototype.getErrorMessage = function (error, statusCode) {
|
|
4900
|
+
// Rate limited: the server's own message when it sent one
|
|
4901
|
+
if (isRateLimitError(error)) {
|
|
4902
|
+
return error.serverMessage || RATE_LIMITED_FALLBACK_MESSAGE;
|
|
4903
|
+
}
|
|
4904
|
+
if (statusCode === RATE_LIMITED_STATUS) {
|
|
4905
|
+
return RATE_LIMITED_FALLBACK_MESSAGE;
|
|
4906
|
+
}
|
|
4804
4907
|
// Auth errors
|
|
4805
4908
|
if (statusCode === 401) {
|
|
4806
4909
|
return "Authentication failed. Please check your credentials.";
|
|
@@ -5300,16 +5403,26 @@ var MCPChat = /** @class */ (function () {
|
|
|
5300
5403
|
onopen: function (response) { return __awaiter$1(_this, void 0, void 0, function () {
|
|
5301
5404
|
var error;
|
|
5302
5405
|
return __generator$1(this, function (_a) {
|
|
5303
|
-
|
|
5304
|
-
|
|
5305
|
-
|
|
5306
|
-
|
|
5307
|
-
|
|
5406
|
+
switch (_a.label) {
|
|
5407
|
+
case 0:
|
|
5408
|
+
httpStatusCode = response.status;
|
|
5409
|
+
if (!(response.status === RATE_LIMITED_STATUS)) return [3 /*break*/, 2];
|
|
5410
|
+
return [4 /*yield*/, rateLimitErrorFromResponse(response)];
|
|
5411
|
+
case 1:
|
|
5412
|
+
// Rate limited: carry the server's message and Retry-After. Not retried (4xx)
|
|
5413
|
+
// and not offline (it has a status code) -- see rateLimit.ts.
|
|
5414
|
+
throw _a.sent();
|
|
5415
|
+
case 2:
|
|
5416
|
+
if (!response.ok) {
|
|
5417
|
+
error = new Error("HTTP error! status: ".concat(response.status));
|
|
5418
|
+
error.statusCode = response.status;
|
|
5419
|
+
throw error;
|
|
5420
|
+
}
|
|
5421
|
+
log("SSE connection opened");
|
|
5422
|
+
// Connection successful - ensure we're marked as online
|
|
5423
|
+
this.setConnectionStatus("online");
|
|
5424
|
+
return [2 /*return*/];
|
|
5308
5425
|
}
|
|
5309
|
-
log("SSE connection opened");
|
|
5310
|
-
// Connection successful - ensure we're marked as online
|
|
5311
|
-
this.setConnectionStatus("online");
|
|
5312
|
-
return [2 /*return*/];
|
|
5313
5426
|
});
|
|
5314
5427
|
}); },
|
|
5315
5428
|
onmessage: function (event) {
|
|
@@ -5586,6 +5699,12 @@ var MCPChat = /** @class */ (function () {
|
|
|
5586
5699
|
_this.stopTyping();
|
|
5587
5700
|
_this.stopStreamThrottle();
|
|
5588
5701
|
err("SSE error: ".concat(data.error));
|
|
5702
|
+
if (data.code === RATE_LIMITED_CODE) {
|
|
5703
|
+
// Carries statusCode 429, so postMessage neither retries it nor
|
|
5704
|
+
// marks the connection offline.
|
|
5705
|
+
reject(createRateLimitError(data.error, parseRetryAfter(data.retryAfter)));
|
|
5706
|
+
break;
|
|
5707
|
+
}
|
|
5589
5708
|
// Server-side errors should not retry - reject immediately with the error
|
|
5590
5709
|
reject(new Error(data.error || "Server error during processing"));
|
|
5591
5710
|
break;
|
|
@@ -15514,11 +15633,26 @@ var ModalContent = function (_a) {
|
|
|
15514
15633
|
/**
|
|
15515
15634
|
* Id the visitor (cookies, browser fingerprint, etc)
|
|
15516
15635
|
*
|
|
15636
|
+
* Kept in localStorage so the widget's load event, which fires before the chat store exists,
|
|
15637
|
+
* reports the same id the store goes on to adopt for a first-time visitor.
|
|
15638
|
+
*
|
|
15517
15639
|
* @export
|
|
15518
15640
|
* @returns {string}
|
|
15519
15641
|
*/
|
|
15642
|
+
var VISITOR_ID_KEY = "xapp_chat_visitorId";
|
|
15520
15643
|
function visitorFingerprint() {
|
|
15521
|
-
|
|
15644
|
+
try {
|
|
15645
|
+
var stored = localStorage.getItem(VISITOR_ID_KEY);
|
|
15646
|
+
if (stored) {
|
|
15647
|
+
return stored;
|
|
15648
|
+
}
|
|
15649
|
+
var id = uuid_1();
|
|
15650
|
+
localStorage.setItem(VISITOR_ID_KEY, id);
|
|
15651
|
+
return id;
|
|
15652
|
+
}
|
|
15653
|
+
catch (_a) {
|
|
15654
|
+
return uuid_1();
|
|
15655
|
+
}
|
|
15522
15656
|
}
|
|
15523
15657
|
|
|
15524
15658
|
var DEFAULT_VISITOR = {
|