UNPKG

@bitrix24/b24jssdk

Version:

Bitrix24 REST API JavaScript SDK

1 lines 10.8 kB
{"version":3,"file":"ajax-error.mjs","sources":["../../../../src/core/http/ajax-error.ts"],"sourcesContent":["import type { AjaxQuery } from './ajax-result'\nimport type { ValidationDetail } from './parse-error-payload'\nimport type { SdkErrorDetails } from '../sdk-error'\nimport { SdkError } from '../sdk-error'\nimport { redactSensitiveParams } from './redact'\n\nexport type AnswerError = {\n error: string\n errorDescription: string\n}\n\nexport type AjaxErrorParams = {\n status: number\n answerError: AnswerError\n cause?: Error\n}\n\ntype AjaxErrorDetails = SdkErrorDetails & {\n requestInfo?: Partial<AjaxQuery>\n validation?: readonly ValidationDetail[]\n}\n\n/**\n * Error requesting RestApi\n */\nexport class AjaxError extends SdkError {\n /**\n * Redaction contract: `requestInfo.params` has already been run through\n * {@link redactSensitiveParams} in the constructor, so credential-bearing\n * keys are stored as `***REDACTED***` and are safe to surface via\n * `toJSON()` / `toString()`. (#39, #73)\n */\n public readonly requestInfo?: AjaxErrorDetails['requestInfo']\n\n /**\n * The `restApi:v3` `validation` array, when the portal sent one.\n *\n * `description` folds the validation messages into one string for display;\n * this keeps them apart, **with the `field` each belongs to** — which the\n * message alone does not carry, and which is what a form needs in order to\n * mark the offending input rather than show a banner (#423).\n *\n * ```ts\n * if (!response.isSuccess) {\n * for (const error of response.getErrors()) {\n * if (error instanceof AjaxError) {\n * for (const detail of error.validation ?? []) {\n * markInvalid(detail.field, detail.message)\n * }\n * }\n * }\n * }\n * ```\n *\n * Absent under `restApi:v2`, which has no equivalent, and absent when v3\n * reported an error without one. `field` is optional inside each entry\n * because the portal's own shape says so.\n *\n * **Included in `toJSON()`**, but only when present — an error carrying no\n * validation serializes exactly as it did before. It belongs there because\n * `toJSON()` is what reaches a log or an error tracker, and that is where the\n * field name matters most: `message` folds the validation *messages* in, but\n * not the `field` each came from. A portal often names the field inside its\n * own wording, as in `` Обязательное поле `id` не указано `` — but that is the\n * portal's phrasing, not a guarantee, and nothing structured survives without\n * this.\n *\n * **Redaction contract:** each entry has been run through\n * {@link redactSensitiveParams} in the constructor, on the same terms as\n * `requestInfo.params`. That matters because the portal's own shape permits\n * extra keys beyond `field` and `message`, and only `message` is folded into\n * `description` — so an extra key reaches a serializer without ever passing\n * through the text. Before this was added, a row carrying `token: '…'` was\n * masked inside `requestInfo.params` and printed verbatim here, from the same\n * error object.\n *\n * What redaction does *not* cover is portal prose: a message that quotes a\n * submitted value stays as the portal wrote it, exactly as it already does in\n * `message`. Contrast `originalError`, which is genuinely hidden: it holds the\n * raw transport error and its credentials.\n */\n public readonly validation?: readonly ValidationDetail[]\n\n constructor(params: AjaxErrorDetails) {\n // @todo test this\n // @memo get from PullClient.loadConfig\n if (params.code === 'AUTHORIZE_ERROR' || params.code === 'WRONG_AUTH_TYPE') {\n params.status = 403\n }\n\n params.description = AjaxError.formatErrorMessage(params)\n super(params)\n\n this.name = 'AjaxError' as const\n // Redacted on the same terms as `requestInfo.params` below: the portal's\n // shape permits keys the SDK has not seen, and this field is serialized.\n // An empty array is treated as no validation at all, so a caller\n // constructing one directly cannot produce a `validation: []` key that the\n // parser never emits (it guards on `length > 0`).\n this.validation = params.validation?.length\n ? Object.freeze(params.validation.map(\n row => redactSensitiveParams(row) as ValidationDetail\n ))\n : undefined\n // Redact credential-bearing keys from caller-supplied params so they never\n // leak via `toJSON()` / `toString()` consumers (#39).\n this.requestInfo = params.requestInfo\n ? {\n ...params.requestInfo,\n ...(params.requestInfo.params !== undefined\n ? { params: redactSensitiveParams(params.requestInfo.params) }\n : {})\n }\n : undefined\n\n this.cleanErrorStack()\n }\n\n /**\n * Creates AjaxError from HTTP response\n * @todo add support v3\n */\n static fromResponse(response: {\n status: number\n data?: { error?: string, error_description?: string }\n config?: AjaxErrorDetails['requestInfo']\n }): AjaxError {\n return new AjaxError({\n code: response.data?.error || 'JSSDK_INTERNAL_AJAX_ERROR',\n description: response.data?.error_description,\n status: response.status,\n requestInfo: response.config\n })\n }\n\n /**\n * @inheritDoc\n */\n static override fromException(error: unknown, context?: {\n code?: string\n status?: number\n requestInfo?: AjaxErrorDetails['requestInfo']\n }): AjaxError {\n if (error instanceof AjaxError) return error\n\n return new AjaxError({\n code: context?.code || 'JSSDK_INTERNAL_AJAX_ERROR',\n status: context?.status || 500,\n description: error instanceof Error ? error.message : String(error),\n requestInfo: context?.requestInfo,\n originalError: error\n })\n }\n\n /**\n * @inheritDoc\n */\n override toJSON() {\n return {\n name: this.name,\n code: this.code,\n message: this.message,\n status: this._status,\n timestamp: this.timestamp.toISOString(),\n requestInfo: this.requestInfo,\n // Only when present, so an error without validation serializes exactly as\n // it did before this field existed (#423).\n ...(this.validation ? { validation: this.validation } : {}),\n stack: this.stack\n }\n }\n\n /**\n * @inheritDoc\n */\n override toString(): string {\n let output = `[${this.name}] ${this.code} (${this._status}): ${this.message}`\n\n if (this.requestInfo) {\n output += `\\nRequest: ${this.requestInfo?.requestId ? `[${this.requestInfo.requestId}] ` : ''}${this.requestInfo.method}`\n }\n\n if (this.stack) {\n output += `\\nStack trace:\\n${this.stack}`\n }\n\n return output\n }\n\n /**\n * @inheritDoc\n */\n protected static override formatErrorMessage(params: AjaxErrorDetails): string {\n if (!params?.description) {\n if (params.requestInfo?.method) {\n return `${params.code} (on ${params.requestInfo.method})`\n } else {\n return `Internal ajax error`\n }\n }\n\n return `${params.description}`\n }\n\n /**\n * @inheritDoc\n */\n protected override cleanErrorStack() {\n if (typeof this.stack === 'string') {\n this.stack = this.stack\n .split('\\n')\n .filter(line => !line.includes('AjaxError.constructor'))\n .join('\\n')\n }\n }\n}\n"],"names":[],"mappings":"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"}