{"version":3,"file":"ajax-result.cjs","sources":["../../../../src/core/http/ajax-result.ts"],"sourcesContent":["import type { IResult } from '../result'\nimport type { Payload, SuccessPayload } from '../../types/payloads'\nimport type { ValidationDetail } from './parse-error-payload'\nimport { parseErrorPayload } from './parse-error-payload'\nimport type { TypeCallParams, TypeHttp } from '../../types/http'\nimport { Type } from '../../tools/type'\nimport { Text } from '../../tools/text'\nimport { Result } from '../result'\nimport { AjaxError } from './ajax-error'\nimport { ApiVersion } from '../../types/b24'\nimport { SdkError } from '../sdk-error'\n\nexport type AjaxQuery = Readonly<{\n  method: string\n  params: TypeCallParams\n  requestId: string\n}>\n\ntype AjaxResultOptions<T> = Readonly<{\n  answer: Payload<T>\n  query: AjaxQuery\n  status: number\n  /**\n   * An error the caller has already built, used instead of deriving one from\n   * `answer`.\n   *\n   * For the soft-error path in `AbstractHttp`, which has parsed the portal's\n   * body, built an `AjaxError` from it, and then needs a `Result` to hand back.\n   * Re-deriving there parses the same body twice — and the second pass folds the\n   * validation messages onto a description that already contains them, so the\n   * text came out doubled (#423). Carrying the error is also simply more honest:\n   * it is the error, not a reconstruction of one.\n   */\n  error?: AjaxError\n}>\n\ntype ErrorData = {\n  code: string\n  description: string\n  status: number\n  validation?: readonly ValidationDetail[]\n}\n\n/**\n * Typed result wrapper for a single Bitrix24 REST API response.\n *\n * Extends {@link Result} with the raw HTTP status, the originating query\n * (method, params, requestId), and the deserialized payload. On construction\n * it inspects the payload for API-level error fields and populates the\n * inherited error collection, so callers can branch on {@link isSuccess}\n * without inspecting raw HTTP status codes.\n */\nexport class AjaxResult<T = unknown> extends Result<Payload<T>> implements IResult<Payload<T>> {\n  private readonly _status: number\n  private readonly _query: AjaxQuery\n  protected override _data: Payload<T> | null | undefined\n\n  constructor(options: AjaxResultOptions<T>) {\n    super()\n\n    this._data = options.answer ? Object.freeze(options.answer) : undefined\n    this._query = Object.freeze(structuredClone(options.query))\n    this._status = options.status\n\n    if (options.error) {\n      this.addError(options.error, 'base-error')\n    } else {\n      this.#processErrors()\n    }\n  }\n\n  override get isSuccess(): boolean {\n    return this.#getIsSuccess()\n  }\n\n  /**\n   * @todo test this predicate\n   */\n  #getIsSuccess(): this is { getData: () => SuccessPayload<T> } {\n    return this._errors.size === 0\n  }\n\n  override getData(): undefined | SuccessPayload<T> {\n    if (!this.isSuccess) {\n      return undefined\n    }\n\n    const payload = this._data as SuccessPayload<T>\n\n    return Object.freeze({\n      result: payload.result,\n      time: payload.time\n    }) as SuccessPayload<T>\n  }\n\n  /**\n   * If the response contains error data, we'll restore it to an error.\n   *\n   * The parsing lives in {@link parseErrorPayload}, shared with\n   * `AbstractHttp._convertAxiosErrorToAjaxError()`. It used to be written out\n   * here as well, and the two copies had drifted into agreeing on everything\n   * except that neither read `validation[].field` (#423).\n   */\n  #processErrors(): void {\n    const parsed = parseErrorPayload(this._data, 'JSSDK_RESPONSE_ERROR', 'Some error in response')\n    if (parsed === undefined) {\n      return\n    }\n\n    this.addError(this.#createAjaxError({\n      code: parsed.code,\n      description: parsed.description,\n      status: this._status,\n      validation: parsed.validation\n    }), 'base-error')\n  }\n\n  #createAjaxError(errorData: ErrorData): AjaxError {\n    return new AjaxError({\n      code: errorData.code,\n      description: errorData.description,\n      status: errorData.status,\n      validation: errorData.validation,\n      requestInfo: {\n        method: this._query.method,\n        params: this._query.params,\n        requestId: this._query.requestId\n      }\n    })\n  }\n\n  /**\n   * Alias for {@link AjaxResult.isMore}.\n   *\n   * `restApi:v2` only — see {@link AjaxResult.isMore} for what this returns on\n   * a `restApi:v3` response.\n   */\n  hasMore(): boolean {\n    return this.isMore()\n  }\n\n  /**\n   * Whether the `restApi:v2` envelope carries a `next` offset — i.e. the portal\n   * has more rows for this query.\n   *\n   * **`restApi:v2` only.** `restApi:v3` returns no `next` field, so this returns\n   * `false` on a v3 response — which is not the same statement as \"there are no\n   * more rows\". Do not branch on it for v3; there is nothing to read.\n   *\n   * This is a reader for a protocol field, not a deprecated API: it stays for as\n   * long as `restApi:v2` does, and so does its counterpart\n   * {@link AjaxResult.getNext} — the two together are the manual `restApi:v2`\n   * paging loop, and neither is going away. For new code prefer the list\n   * helpers, which hide the offset bookkeeping and work under both protocol\n   * versions:\n   *   - `restApi:v2`: `b24.actions.v2.callList.make` or `b24.actions.v2.fetchList.make`\n   *   - `restApi:v3`: `b24.actions.v3.callList.make` or `b24.actions.v3.fetchList.make`\n   */\n  isMore(): boolean {\n    if (!this.isSuccess) {\n      return false\n    }\n    const payload = this._data as { next?: number }\n    const nextValue = 'next' in payload ? payload.next : undefined\n\n    return Type.isNumber(nextValue)\n  }\n\n  /**\n   * The row count the `restApi:v2` envelope reports in its `total` field.\n   *\n   * **`restApi:v2` only.** `restApi:v3` returns no `total`, so this returns `0`\n   * on a v3 response — which is not the same statement as \"no rows matched\".\n   * Do not read it for v3; use\n   * `b24.actions.v3.aggregate.make` with `count` / `countDistinct` instead,\n   * bearing in mind that action is `@experimental` and unverified against a live\n   * portal.\n   *\n   * This is a reader for a protocol field, not a deprecated API. It is the only\n   * way to obtain a count under `restApi:v2` — the list helpers iterate without\n   * exposing `total`, {@link SuccessPayload} deliberately omits it, and the\n   * `aggregate` action exists for `restApi:v3` only. It therefore stays for as\n   * long as `restApi:v2` does, and is not part of the `3.0.0` removal set.\n   *\n   * That is a decision with a trigger, not an open-ended promise. Revisit it\n   * when either holds: `b24.actions.v3.aggregate` is verified against a live\n   * portal and loses its `@experimental` tag across the common modules (a v3\n   * count then exists, and `getTotal()` has a replacement for the first time),\n   * or Bitrix24 announces a `restApi:v2` sunset date (the field it reads goes\n   * away regardless). Until one of those happens there is nothing to migrate\n   * callers to, which is the whole reason it is still here.\n   *\n   * Note this trigger is specific to the readers. {@link AjaxResult.getNext} and\n   * {@link AjaxResult.fetchNext} already have a working replacement, so nothing\n   * about `aggregate` maturing changes anything for them — a `restApi:v2` sunset\n   * is their only exit condition.\n   */\n  getTotal(): number {\n    if (!this.isSuccess) {\n      return 0\n    }\n    const payload = this._data as { total?: number }\n    const totalValue = 'total' in payload ? payload.total : undefined\n\n    return Text.toInteger(totalValue)\n  }\n\n  getStatus(): number {\n    return this._status\n  }\n\n  getQuery(): Readonly<AjaxQuery> {\n    return this._query\n  }\n\n  /**\n   * Alias for {@link AjaxResult.getNext}, returning `null` where that returns\n   * `false`.\n   *\n   * **`restApi:v2` only** — see {@link AjaxResult.getNext}, including the throw\n   * on a `restApi:v3` client, which this inherits.\n   */\n  async fetchNext(http: TypeHttp): Promise<AjaxResult<T> | null> {\n    const data = await this.getNext(http)\n    if (data === false) {\n      return null\n    }\n\n    return data\n  }\n\n  /**\n   * Re-runs this result's own query with `params.start` set to the `next` offset\n   * the `restApi:v2` envelope reported, and resolves to the following page.\n   * Returns `false` when this result is unsuccessful or has no `next`.\n   *\n   * `restApi:v2` only, and permanently so. Unlike the readers above, this one\n   * acts on the envelope, and `restApi:v3` has no `next` to act on — so it\n   * throws rather than silently returning `false`, which would be\n   * indistinguishable from \"last page\". That throw is not a transitional\n   * measure; it is the honest answer for a protocol that does not have this\n   * operation.\n   *\n   * For new code prefer `b24.actions.v{2,3}.callList.make` (collect everything)\n   * or `b24.actions.v{2,3}.fetchList.make` (async generator, one page per\n   * iteration — the same page-by-page control this gives, without the manual\n   * offset bookkeeping, and it works under both protocol versions). This method\n   * is kept because it works under `restApi:v2` and deleting it would break\n   * running code for no gain, not because it is the better tool.\n   *\n   * @throws {SdkError} `JSSDK_CORE_METHOD_NOT_SUPPORT_IN_API_V3` when called against a `restApi:v3` HTTP client.\n   */\n  async getNext(http: TypeHttp): Promise<AjaxResult<T> | false> {\n    if (http.apiVersion === ApiVersion.v3) {\n      throw new SdkError({\n        code: 'JSSDK_CORE_METHOD_NOT_SUPPORT_IN_API_V3',\n        description: `restApi:v3 not support method getNext`,\n        status: 500\n      })\n    }\n    if (\n      !this.isSuccess\n      || !this.isMore()\n    ) {\n      return false\n    }\n\n    const nextPageQuery = this.#buildNextPageQuery()\n    return http.call<T>(\n      nextPageQuery.method,\n      nextPageQuery.params\n    )\n  }\n\n  #buildNextPageQuery(): AjaxQuery {\n    const payload = this._data as { next?: number }\n    const nextValue = 'next' in payload ? payload.next : undefined\n\n    // Fresh params object — the previous shallow `{ ...this._query }` shared the\n    // params reference and wrote `start` back into the frozen _query, so the\n    // previous result's getQuery().params silently changed after getNext() (#144).\n    //\n    // `requestId` comes along with the spread but is never used: getNext() reads\n    // only `.method` and `.params`, and the http client mints a fresh id per\n    // request. Reusing this page's id for the next page would be wrong anyway.\n    return {\n      ...this._query,\n      params: { ...this._query.params, start: Text.toInteger(nextValue) }\n    }\n  }\n\n  // Immutable API\n  override setData(): never {\n    throw new ReferenceError('AjaxResult does not allow data modification')\n  }\n}\n"],"names":["Result","parseErrorPayload","AjaxError","Type","Text","ApiVersion","SdkError"],"mappings":"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"}