@bitrix24/b24jssdk
Version:
Bitrix24 REST API JavaScript SDK
172 lines (169 loc) • 5.98 kB
JavaScript
/**
* @package @bitrix24/b24jssdk
* @version 2.2.0
* @copyright (c) 2026 Bitrix24
* @license MIT
* @see https://github.com/bitrix24/b24jssdk
* @see https://bitrix24.github.io/b24jssdk/
*/
import { SdkError } from '../sdk-error.mjs';
import { redactSensitiveParams } from './redact.mjs';
var __defProp = Object.defineProperty;
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
class AjaxError extends SdkError {
static {
__name(this, "AjaxError");
}
/**
* Redaction contract: `requestInfo.params` has already been run through
* {@link redactSensitiveParams} in the constructor, so credential-bearing
* keys are stored as `***REDACTED***` and are safe to surface via
* `toJSON()` / `toString()`. (#39, #73)
*/
requestInfo;
/**
* The `restApi:v3` `validation` array, when the portal sent one.
*
* `description` folds the validation messages into one string for display;
* this keeps them apart, **with the `field` each belongs to** — which the
* message alone does not carry, and which is what a form needs in order to
* mark the offending input rather than show a banner (#423).
*
* ```ts
* if (!response.isSuccess) {
* for (const error of response.getErrors()) {
* if (error instanceof AjaxError) {
* for (const detail of error.validation ?? []) {
* markInvalid(detail.field, detail.message)
* }
* }
* }
* }
* ```
*
* Absent under `restApi:v2`, which has no equivalent, and absent when v3
* reported an error without one. `field` is optional inside each entry
* because the portal's own shape says so.
*
* **Included in `toJSON()`**, but only when present — an error carrying no
* validation serializes exactly as it did before. It belongs there because
* `toJSON()` is what reaches a log or an error tracker, and that is where the
* field name matters most: `message` folds the validation *messages* in, but
* not the `field` each came from. A portal often names the field inside its
* own wording, as in `` Обязательное поле `id` не указано `` — but that is the
* portal's phrasing, not a guarantee, and nothing structured survives without
* this.
*
* **Redaction contract:** each entry has been run through
* {@link redactSensitiveParams} in the constructor, on the same terms as
* `requestInfo.params`. That matters because the portal's own shape permits
* extra keys beyond `field` and `message`, and only `message` is folded into
* `description` — so an extra key reaches a serializer without ever passing
* through the text. Before this was added, a row carrying `token: '…'` was
* masked inside `requestInfo.params` and printed verbatim here, from the same
* error object.
*
* What redaction does *not* cover is portal prose: a message that quotes a
* submitted value stays as the portal wrote it, exactly as it already does in
* `message`. Contrast `originalError`, which is genuinely hidden: it holds the
* raw transport error and its credentials.
*/
validation;
constructor(params) {
if (params.code === "AUTHORIZE_ERROR" || params.code === "WRONG_AUTH_TYPE") {
params.status = 403;
}
params.description = AjaxError.formatErrorMessage(params);
super(params);
this.name = "AjaxError";
this.validation = params.validation?.length ? Object.freeze(params.validation.map(
(row) => redactSensitiveParams(row)
)) : void 0;
this.requestInfo = params.requestInfo ? {
...params.requestInfo,
...params.requestInfo.params !== void 0 ? { params: redactSensitiveParams(params.requestInfo.params) } : {}
} : void 0;
this.cleanErrorStack();
}
/**
* Creates AjaxError from HTTP response
* @todo add support v3
*/
static fromResponse(response) {
return new AjaxError({
code: response.data?.error || "JSSDK_INTERNAL_AJAX_ERROR",
description: response.data?.error_description,
status: response.status,
requestInfo: response.config
});
}
/**
* @inheritDoc
*/
static fromException(error, context) {
if (error instanceof AjaxError) return error;
return new AjaxError({
code: context?.code || "JSSDK_INTERNAL_AJAX_ERROR",
status: context?.status || 500,
description: error instanceof Error ? error.message : String(error),
requestInfo: context?.requestInfo,
originalError: error
});
}
/**
* @inheritDoc
*/
toJSON() {
return {
name: this.name,
code: this.code,
message: this.message,
status: this._status,
timestamp: this.timestamp.toISOString(),
requestInfo: this.requestInfo,
// Only when present, so an error without validation serializes exactly as
// it did before this field existed (#423).
...this.validation ? { validation: this.validation } : {},
stack: this.stack
};
}
/**
* @inheritDoc
*/
toString() {
let output = `[${this.name}] ${this.code} (${this._status}): ${this.message}`;
if (this.requestInfo) {
output += `
Request: ${this.requestInfo?.requestId ? `[${this.requestInfo.requestId}] ` : ""}${this.requestInfo.method}`;
}
if (this.stack) {
output += `
Stack trace:
${this.stack}`;
}
return output;
}
/**
* @inheritDoc
*/
static formatErrorMessage(params) {
if (!params?.description) {
if (params.requestInfo?.method) {
return `${params.code} (on ${params.requestInfo.method})`;
} else {
return `Internal ajax error`;
}
}
return `${params.description}`;
}
/**
* @inheritDoc
*/
cleanErrorStack() {
if (typeof this.stack === "string") {
this.stack = this.stack.split("\n").filter((line) => !line.includes("AjaxError.constructor")).join("\n");
}
}
}
export { AjaxError };
//# sourceMappingURL=ajax-error.mjs.map