@xapp/chat-widget 1.105.5 → 1.105.6

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.js CHANGED
@@ -4177,6 +4177,85 @@ function isTransportFailure(error) {
4177
4177
  return !(Number.isFinite(status) && status >= 100 && status <= 599);
4178
4178
  }
4179
4179
 
4180
+ /**
4181
+ * Rate limiting, as agent-server reports it.
4182
+ *
4183
+ * On 2026-10-07 the XAPP API's WAF rate-limited agent-server and every chat failed. The widget told
4184
+ * visitors the chat was offline and to check their connection, and retried each message twice more —
4185
+ * adding load to the thing that was already over its limit. Rate limiting is the server answering:
4186
+ * it is not offline, an immediate retry will not help, and the visitor should be told what is going on.
4187
+ *
4188
+ * agent-server signals it two ways:
4189
+ * - before the stream: HTTP 429, `Retry-After`, body `{ error, code: "RATE_LIMITED", retryAfter }`
4190
+ * - after the stream starts: SSE `error` event `{ error, code: "RATE_LIMITED", retryAfter }`
4191
+ */
4192
+ var RATE_LIMITED_CODE = "RATE_LIMITED";
4193
+ var RATE_LIMITED_STATUS = 429;
4194
+ /** Shown when the server did not send a message of its own (e.g. a bare 429 from a WAF). */
4195
+ var RATE_LIMITED_FALLBACK_MESSAGE = "We're getting more requests than usual right now. Please try again in a few minutes.";
4196
+ function isRateLimitError(error) {
4197
+ return (error === null || error === void 0 ? void 0 : error.code) === RATE_LIMITED_CODE;
4198
+ }
4199
+ function createRateLimitError(serverMessage, retryAfter) {
4200
+ var message = serverMessage || RATE_LIMITED_FALLBACK_MESSAGE;
4201
+ var error = new Error(message);
4202
+ error.statusCode = RATE_LIMITED_STATUS;
4203
+ error.code = RATE_LIMITED_CODE;
4204
+ if (retryAfter !== undefined) {
4205
+ error.retryAfter = retryAfter;
4206
+ }
4207
+ if (serverMessage) {
4208
+ error.serverMessage = serverMessage;
4209
+ }
4210
+ return error;
4211
+ }
4212
+ /** Seconds from a `retryAfter` body field or a `Retry-After` header. HTTP-date values are ignored. */
4213
+ function parseRetryAfter(value) {
4214
+ if (value === null || value === undefined || value === "") {
4215
+ return undefined;
4216
+ }
4217
+ var seconds = Number(value);
4218
+ return Number.isFinite(seconds) && seconds >= 0 ? seconds : undefined;
4219
+ }
4220
+ /**
4221
+ * Builds the error for a 429 response, reading the agent-server JSON body when there is one. The body
4222
+ * is optional: a 429 from a WAF or proxy in front of agent-server will not carry it.
4223
+ */
4224
+ function rateLimitErrorFromResponse(response) {
4225
+ return __awaiter$1(this, void 0, void 0, function () {
4226
+ var body, text, _a, parsed, serverMessage, retryAfter;
4227
+ var _c, _d;
4228
+ return __generator$1(this, function (_e) {
4229
+ switch (_e.label) {
4230
+ case 0:
4231
+ _e.trys.push([0, 4, , 5]);
4232
+ if (!response.text) return [3 /*break*/, 2];
4233
+ return [4 /*yield*/, response.text()];
4234
+ case 1:
4235
+ _a = _e.sent();
4236
+ return [3 /*break*/, 3];
4237
+ case 2:
4238
+ _a = "";
4239
+ _e.label = 3;
4240
+ case 3:
4241
+ text = _a;
4242
+ parsed = text ? JSON.parse(text) : undefined;
4243
+ if (parsed && typeof parsed === "object") {
4244
+ body = parsed;
4245
+ }
4246
+ return [3 /*break*/, 5];
4247
+ case 4:
4248
+ _e.sent();
4249
+ return [3 /*break*/, 5];
4250
+ case 5:
4251
+ serverMessage = typeof (body === null || body === void 0 ? void 0 : body.error) === "string" && body.error.trim() ? body.error : undefined;
4252
+ 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"));
4253
+ return [2 /*return*/, createRateLimitError(serverMessage, retryAfter)];
4254
+ }
4255
+ });
4256
+ });
4257
+ }
4258
+
4180
4259
  /**
4181
4260
  * Default configuration messages
4182
4261
  * @returns
@@ -4575,6 +4654,13 @@ var MCPChat = /** @class */ (function () {
4575
4654
  case 3:
4576
4655
  error_1 = _a.sent();
4577
4656
  err("Failed to validate session: ".concat(error_1));
4657
+ if (isRateLimitError(error_1)) {
4658
+ // Starting a new session would be a second request into the same limit, and would
4659
+ // throw away a session that is still good. Keep it and tell the visitor.
4660
+ this.stopTyping();
4661
+ this.sendFailureMessage(0, 0, this.getErrorMessage(error_1, RATE_LIMITED_STATUS));
4662
+ return [2 /*return*/];
4663
+ }
4578
4664
  // If validation fails (network error, etc.), clear session and start fresh
4579
4665
  clearStoredSession(this._sessionId);
4580
4666
  this._sessionId = "";
@@ -4801,6 +4887,13 @@ var MCPChat = /** @class */ (function () {
4801
4887
  * Determines a user-friendly error message based on the error type.
4802
4888
  */
4803
4889
  MCPChat.prototype.getErrorMessage = function (error, statusCode) {
4890
+ // Rate limited: the server's own message when it sent one
4891
+ if (isRateLimitError(error)) {
4892
+ return error.serverMessage || RATE_LIMITED_FALLBACK_MESSAGE;
4893
+ }
4894
+ if (statusCode === RATE_LIMITED_STATUS) {
4895
+ return RATE_LIMITED_FALLBACK_MESSAGE;
4896
+ }
4804
4897
  // Auth errors
4805
4898
  if (statusCode === 401) {
4806
4899
  return "Authentication failed. Please check your credentials.";
@@ -5300,16 +5393,26 @@ var MCPChat = /** @class */ (function () {
5300
5393
  onopen: function (response) { return __awaiter$1(_this, void 0, void 0, function () {
5301
5394
  var error;
5302
5395
  return __generator$1(this, function (_a) {
5303
- httpStatusCode = response.status;
5304
- if (!response.ok) {
5305
- error = new Error("HTTP error! status: ".concat(response.status));
5306
- error.statusCode = response.status;
5307
- throw error;
5396
+ switch (_a.label) {
5397
+ case 0:
5398
+ httpStatusCode = response.status;
5399
+ if (!(response.status === RATE_LIMITED_STATUS)) return [3 /*break*/, 2];
5400
+ return [4 /*yield*/, rateLimitErrorFromResponse(response)];
5401
+ case 1:
5402
+ // Rate limited: carry the server's message and Retry-After. Not retried (4xx)
5403
+ // and not offline (it has a status code) -- see rateLimit.ts.
5404
+ throw _a.sent();
5405
+ case 2:
5406
+ if (!response.ok) {
5407
+ error = new Error("HTTP error! status: ".concat(response.status));
5408
+ error.statusCode = response.status;
5409
+ throw error;
5410
+ }
5411
+ log("SSE connection opened");
5412
+ // Connection successful - ensure we're marked as online
5413
+ this.setConnectionStatus("online");
5414
+ return [2 /*return*/];
5308
5415
  }
5309
- log("SSE connection opened");
5310
- // Connection successful - ensure we're marked as online
5311
- this.setConnectionStatus("online");
5312
- return [2 /*return*/];
5313
5416
  });
5314
5417
  }); },
5315
5418
  onmessage: function (event) {
@@ -5586,6 +5689,12 @@ var MCPChat = /** @class */ (function () {
5586
5689
  _this.stopTyping();
5587
5690
  _this.stopStreamThrottle();
5588
5691
  err("SSE error: ".concat(data.error));
5692
+ if (data.code === RATE_LIMITED_CODE) {
5693
+ // Carries statusCode 429, so postMessage neither retries it nor
5694
+ // marks the connection offline.
5695
+ reject(createRateLimitError(data.error, parseRetryAfter(data.retryAfter)));
5696
+ break;
5697
+ }
5589
5698
  // Server-side errors should not retry - reject immediately with the error
5590
5699
  reject(new Error(data.error || "Server error during processing"));
5591
5700
  break;