{"version":3,"file":"fetch-list.cjs","sources":["../../../../../src/core/actions/v3/fetch-list.ts"],"sourcesContent":["import type { WalkBoundsOptions } from '../_walk-bounds'\nimport type { TypeCallParams, TypeCallParamsV3, TypeFilterV3 } from '../../../types/http'\nimport { AbstractAction } from '../abstract-action'\nimport { SdkError } from '../../sdk-error'\nimport { assertArrayFilter, keysetPaginate, KeysetPaginationError } from './_keyset-paginate'\nimport { CURSOR_STALLED_HINT_LIST } from '../_cursor-stalled'\n\nexport type ActionFetchListV3 = WalkBoundsOptions & {\n  method: string\n  /**\n   * `filter` is narrowed to the v3 array form here, unlike {@link TypeCallParamsV3},\n   * which also accepts the v2 object dialect for backward compatibility.\n   *\n   * Keyset pagination is emulated by appending `[cursorIdKey, '>', cursor]` to\n   * this filter on every page, so an array is not a preference — it is the only\n   * shape the mechanism can extend. The object form used to be accepted here and\n   * then threw `filter is not iterable` at runtime, one page into the walk.\n   */\n  params?: Omit<TypeCallParamsV3, 'pagination' | 'order' | 'filter'> & { filter?: TypeFilterV3 }\n  /**\n   * Name of the id field **as it appears in each response item**; its value\n   * drives the cursor. Default `'id'`.\n   */\n  idKey?: string\n  /**\n   * Field name used in the **request**, for `order` and the `[field, '>', n]`\n   * page filter. Defaults to `idKey`, which is usually right on `restApi:v3`\n   * where names are camelCase in both directions.\n   */\n  cursorIdKey?: string\n  /** Key the rows are nested under in the response, e.g. `items` for CRM items. */\n  customKeyForResult: string\n  /**\n   * Sent as the `bx24_request_id` query parameter, for tracing. It does **not**\n   * deduplicate anything — for that see `idempotencyKey`.\n   */\n  requestId?: string\n  /**\n   * Rows per page. Default `50`, and **a request rather than a guarantee**:\n   * every method applies its own maximum, so a page shorter than `limit` is not\n   * the end of the data. The walk allows for that; hand-rolled paging on\n   * `call.make` does not.\n   */\n  limit?: number\n}\n\n/**\n * Calls a REST API list method and returns an async generator for efficient large data retrieval. `restApi:v3`\n *\n * Iterates through all pages of a v3 list method using keyset (cursor) pagination and yields\n * each page as an array, allowing callers to process records incrementally without holding the\n * entire dataset in memory. Unlike `CallListV3`, which accumulates all pages before returning,\n * this class exposes an `AsyncGenerator` so processing can begin as soon as the first page\n * arrives. Compared to `FetchListV2`, it uses v3-style array filter syntax and supports the\n * `limit` option (a requested page size; the server applies its own per-method\n * maximum).\n */\nexport class FetchListV3 extends AbstractAction {\n  /**\n   * Calls a REST API list method and returns an async generator, for walking a\n   * large dataset without holding all of it in memory.\n   *\n   * **Every option is documented on the [fetchList v3 page](https://bitrix24.github.io/b24jssdk/docs/working-with-the-rest-api/fetch-list-rest-api-ver3/),\n   * and each one on {@link ActionFetchListV3}.** Not repeated here: nothing\n   * watches a sentence in a comment, while the page is link-checked and its\n   * code compiled on every CI run.\n   *\n   * What matters while editing this file:\n   *\n   * - The cursor only advances if rows arrive sorted by `cursorIdKey` ascending,\n   *   so the walk writes its own `order` and strips a caller's with a `warning`.\n   * - `idKey` reads the RESPONSE, `cursorIdKey` writes the REQUEST, and the two\n   *   fail differently. A wrong `cursorIdKey` means the page condition never\n   *   matches, the same page keeps arriving, and the walk stops with\n   *   `JSSDK_ACTION_CURSOR_STALLED`\n   *   ({@link CURSOR_STALLED_HINT_LIST} names the usual causes). A wrong `idKey` is quieter: if the value\n   *   cannot be read as a number the walk warns and stops short, and if it\n   *   names a *different numeric* field it advances a cursor the request never\n   *   sorts by — which skips rows rather than reporting anything.\n   * - End of data is decided against the largest page seen, never against\n   *   `limit`, which methods are free to cap below the ask. The rule lives in\n   *   {@link keysetPaginate}, which this delegates to.\n   * - `maxPages` never ends a walk silently. Every page up to the ceiling has\n   *   already been yielded and is the consumer's; the throw is what stops a\n   *   truncated walk from reading as a finished one.\n   *\n   * @template T - The type of items in the returned arrays (default is `unknown`).\n   * @param {ActionFetchListV3} options - every field is documented on the type.\n   * @returns {AsyncGenerator<T[]>} An async generator yielding one page of rows\n   *     at a time until the dataset is exhausted.\n   *\n   * @example\n   * import { Text } from '@bitrix24/b24jssdk'\n   *\n   * interface MainEventLogItem { id: number, userId: number }\n   * const sixMonthAgo = new Date()\n   * sixMonthAgo.setMonth((new Date()).getMonth() - 6)\n   * sixMonthAgo.setHours(0, 0, 0)\n   * const generator = b24.actions.v3.fetchList.make<MainEventLogItem>({\n   *   method: 'main.eventlog.list',\n   *   params: {\n   *     filter: [\n   *      ['timestampX', '>=', Text.toB24Format(sixMonthAgo)] // created at least 6 months ago\n   *     ],\n   *     select: ['id', 'userId']\n   *   },\n   *   idKey: 'id',\n   *   customKeyForResult: 'items',\n   *   requestId: 'eventlog-123',\n   *   limit: 60\n   * })\n   *\n   * for await (const chunk of generator) {\n   *   // Process chunk (e.g., save to database, analyze, etc.)\n   *   console.log(`Processing ${chunk.length} items`)\n   * }\n   */\n  public override async* make<T = unknown>(options: ActionFetchListV3): AsyncGenerator<T[]> {\n    const batchSize = options?.limit ?? 50\n\n    const idKey = options?.idKey ?? 'id'\n    const cursorIdKey = options?.cursorIdKey ?? idKey\n    const customKeyForResult = options?.customKeyForResult ?? null\n    const params = options?.params ?? {}\n\n    // Warn and strip user-provided `order` — cursor pagination requires ordering by cursorIdKey only\n    if ('order' in params && params['order']) {\n      this._logger.warning('fetchList.make: user-provided `order` parameter is ignored because cursor-based pagination requires ordering by cursorIdKey. Use `filter` to narrow results instead.').catch(() => {})\n    }\n\n    assertArrayFilter(params['filter'], 'fetchList.make')\n\n    const { order: _ignoredOrder, ...restParams } = params as TypeCallParams\n    const requestParams: TypeCallParamsV3 & { filter: TypeFilterV3 } = {\n      ...restParams,\n      order: { [cursorIdKey]: 'ASC' },\n      filter: [...(params['filter'] ?? [])],\n      pagination: { page: 0, limit: batchSize }\n    }\n\n    try {\n      yield* keysetPaginate<T>(this._b24, this._logger, {\n        method: options.method,\n        requestId: options.requestId,\n        customKeyForResult,\n        initialCursor: 0,\n        // Emulated keyset: append the `[cursorIdKey, '>', cursor]` page filter.\n        buildParams: cursor => ({ ...requestParams, filter: [...requestParams.filter, [cursorIdKey, '>', cursor]] }),\n        // Advance by the numeric id read from the last item via `idKey`. A\n        // non-numeric value (almost always an `idKey` that doesn't match the\n        // response field — e.g. sorting by `ID` while the response carries a\n        // lowercase `id`) stops the walk instead of silently truncating.\n        readNextCursor: (lastItem) => {\n          const value = Number.parseInt(lastItem[idKey], 10)\n          return Number.isFinite(value) ? value : null\n        },\n        noCursorWarning: `fetchList.make: pagination stops here — no numeric id could be read from the returned items via idKey \"${idKey}\". Make sure idKey matches the id field in the response; if the sortable field name differs from it, also set cursorIdKey (e.g. idKey: 'id', cursorIdKey: 'ID').`,\n        errorLabel: 'fetchList.make',\n        actionLabel: 'fetchList.make',\n        stalledCursorHint: CURSOR_STALLED_HINT_LIST,\n        // Always ascending: the page condition is `[cursorIdKey, '>', cursor]`\n        // and the request sorts by the same field, so the cursor read off each\n        // page is strictly greater than the one it was requested with.\n        cursorDirection: 'ASC',\n        maxPages: options?.maxPages,\n        signal: options?.signal\n      })\n    } catch (error) {\n      if (error instanceof KeysetPaginationError) {\n        throw new SdkError({\n          code: 'JSSDK_CORE_B24_FETCH_LIST_METHOD_API_V3',\n          description: `API Error: ${error.messages.join('; ')}`,\n          status: 500\n        })\n      }\n      throw error\n    }\n  }\n}\n"],"names":["AbstractAction","assertArrayFilter","keysetPaginate","CURSOR_STALLED_HINT_LIST","KeysetPaginationError","SdkError"],"mappings":"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"}