UNPKG

@bitrix24/b24jssdk

Version:

Bitrix24 REST API JavaScript SDK

172 lines (169 loc) 5.98 kB
/** * @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