{"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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"}