{"version":3,"file":"call-tail.cjs","sources":["../../../../../src/core/actions/v3/call-tail.ts"],"sourcesContent":["import type { WalkBoundsOptions, WalkProgress } from '../_walk-bounds'\nimport { isWalkBoundsError } from '../_walk-bounds'\nimport type { TypeCallParams, TypeCallParamsV3, TypeFilterV3 } from '../../../types/http'\nimport { AbstractAction } from '../abstract-action'\nimport { Result } from '../../result'\nimport { SdkError } from '../../sdk-error'\nimport type { FilterV3Group } from '../../../tools/filter-v3'\nimport { assertTailFilter, filterMentionsField, keysetPaginate, KeysetPaginationError } from './_keyset-paginate'\nimport { CURSOR_STALLED_HINT_TAIL } from '../_cursor-stalled'\nimport { DESC_ORDER, resolveCursorDirection } from '../_cursor-progress'\n\nexport type ActionCallTailV3 = WalkBoundsOptions & {\n  /** Called after each collected page; see `WalkProgress`. */\n  progress?: WalkProgress\n  method: string\n  /**\n   * `filter` is narrowed away from the `restApi:v2` object dialect, which the\n   * portal rejects everywhere in v3. A **logic group** stays allowed: this\n   * walker forwards `filter` untouched, and the portal accepts a bare group as\n   * the whole filter — measured.\n   */\n  params?: Omit<TypeCallParamsV3, 'pagination' | 'order' | 'cursor' | 'filter'> & { filter?: TypeFilterV3 | FilterV3Group }\n  cursorField?: string\n  order?: 'ASC' | 'DESC' | 'asc' | 'desc' | string\n  customKeyForResult?: string\n  requestId?: string\n  limit?: number\n  initialValue?: number | string\n}\n\n/**\n * Fast data retrieval via the native `tail` (keyset cursor) action, without\n * counting the total number of records. `restApi:v3`\n *\n * The eager counterpart of `fetchTail`: it walks the same native\n * `cursor: { field, value, order, limit }` pagination and returns every record\n * as a single array. See the v3 reference §6.2. The cursor field MUST NOT appear\n * in `filter`.\n */\nexport class CallTailV3 extends AbstractAction {\n  /**\n   * Returns every record of a `tail` method as one array.\n   *\n   * @template T - The type of the elements of the returned array (default is `unknown`).\n   *\n   * @param {ActionCallTailV3} options - parameters for executing the request.\n   *     - `method: string` - A REST API `tail` method name (for example: `main.eventlog.tail`).\n   *     - `params?: Omit<TypeCallParamsV3, 'pagination' | 'order' | 'cursor'>` - Request parameters\n   *         (`filter`, `select`). `pagination`, `order` and `cursor` are managed by this helper.\n   *         The cursor field must NOT be used in `filter`.\n   *     - `cursorField?: string` - The DTO field that drives the cursor. Default is `id`.\n   *     - `order?: 'ASC' | 'DESC'` - Cursor direction. Default is `ASC`. For `DESC` you MUST pass\n   *         `initialValue` (the server pages by `field < value`, so the default `0` returns nothing).\n   *     - `customKeyForResult?: string` - The key the response groups rows under. Default is `items`.\n   *     - `requestId?: string` - Unique request identifier for tracking.\n   *     - `maxPages?: number` - Stop after this many pages and throw\n   *         `JSSDK_ACTION_MAX_PAGES_EXCEEDED` naming the method. Defaults to 10 000 — a\n   *         backstop, not a policy. The rows already collected come back with the\n   *         error attached rather than discarded — check `isSuccess`.\n   *     - `signal?: AbortSignal` - Stop the walk. Checked at the top of each iteration, so an\n   *         already-aborted signal costs no request. Throws `JSSDK_ACTION_ABORTED`.\n   *     - `progress?: (p: { pages: number, rows: number }) => void` - Called after each\n   *         collected page. Counts, not a percentage — cursor paging reads no total.\n   *     - `limit?: number` - How many records to retrieve at a time. Default is `50`.\n   *         **A request, not a guarantee.** Each method applies its own maximum and a page\n   *         shorter than `limit` is not the end of the data — `tasks.task.list` answers 50\n   *         however much you ask for, measured with 60 rows available. This walker is\n   *         cap-tolerant; hand-rolled paging on `call.make` is not. On the build measured, a\n   *         `limit` of `0` or a non-numeric one was refused with\n   *         `INVALIDPAGINATIONEXCEPTION` and a negative one answered a bare 500 — one\n   *         method on one on-premise build, so treat the codes as what to expect rather\n   *         than a contract.\n   *     - `initialValue?: number | string` - Cursor start value for the first page. Default is `0`\n   *         (valid for ascending numeric fields); required for `DESC` and for non-numeric fields.\n   *\n   * @returns {Promise<Result<T[]>>} A promise that resolves to the result of an REST API call.\n   *\n   * @example\n   * const response = await b24.actions.v3.callTail.make<{ id: string }>({\n   *   method: 'main.eventlog.tail',\n   *   params: { select: ['id', 'auditTypeId'] },\n   *   cursorField: 'id',\n   *   customKeyForResult: 'items'\n   * })\n   * if (!response.isSuccess) {\n   *   throw new Error(`Problem: ${response.getErrorMessages().join('; ')}`)\n   * }\n   * console.log(`Result: ${response.getData()?.length}`)\n   */\n  public override async make<T = unknown>(options: ActionCallTailV3): Promise<Result<T[]>> {\n    const batchSize = options?.limit ?? 50\n    const result: Result<T[]> = new Result()\n\n    const cursorField = options?.cursorField ?? 'id'\n    const order = options?.order ?? 'ASC'\n    const customKeyForResult = options?.customKeyForResult ?? 'items'\n    const params = options?.params ?? {}\n\n    assertTailFilter(params['filter'], 'callTail.make')\n\n    // DESC keyset needs an explicit start: the server pages by `field < value`,\n    // so the default first-page value 0 would match nothing for a non-negative\n    // field. Require `initialValue` (the type maximum / newest value) for DESC.\n    if (DESC_ORDER.test(order) && options?.initialValue === undefined) {\n      throw new SdkError({\n        code: 'JSSDK_CORE_B24_CALL_TAIL_DESC_REQUIRES_INITIAL_VALUE',\n        description: 'callTail.make: order \"DESC\" requires an explicit `initialValue` (the server pages by `field < value`, so the default 0 returns nothing). Pass `initialValue` set to the type maximum / newest value.',\n        status: 500\n      })\n    }\n\n    // Cursor field must not also live in `filter` (server rejects with\n    // INVALIDFILTEREXCEPTION). The scan descends into logic groups: it used to\n    // look only at a top-level array of triples, which stopped seeing anything\n    // at all once a bare group became a legal filter here, and had never seen a\n    // group nested inside the array form.\n    if (filterMentionsField(params['filter'], cursorField)) {\n      this._logger.warning(`callTail.make: the cursor field \"${cursorField}\" must not appear in \\`filter\\` — the server orders and pages by it and will reject a filter on the same field (INVALIDFILTEREXCEPTION). Remove it from \\`filter\\`.`).catch(() => {})\n    }\n\n    // Cursor field must be readable to advance. Append it to an explicit\n    // `select`; warn for a non-default cursorField when `select` is omitted.\n    let select = params['select'] as string[] | undefined\n    if (Array.isArray(select)) {\n      if (!select.includes(cursorField)) {\n        select = [...select, cursorField]\n      }\n    } else if (cursorField !== 'id') {\n      this._logger.warning(`callTail.make: no \\`select\\` provided with a non-default cursorField \"${cursorField}\" — make sure it is in the server's default field set, otherwise pass \\`select\\` including \"${cursorField}\" so the cursor can advance.`).catch(() => {})\n    }\n\n    const { select: _ignoredSelect, ...restParams } = params as TypeCallParams\n\n    const allItems: T[] = []\n    let pages = 0\n    try {\n      for await (const page of keysetPaginate<T>(this._b24, this._logger, {\n        method: options.method,\n        requestId: options.requestId,\n        customKeyForResult,\n        initialCursor: options?.initialValue ?? 0,\n        // Native keyset: drive the server's `cursor: { field, value, order, limit }`.\n        buildParams: cursor => ({\n          ...restParams,\n          ...(select ? { select } : {}),\n          cursor: { field: cursorField, value: cursor, order, limit: batchSize }\n        }),\n        // Advance by the raw cursor-field value from the last item; a missing\n        // value (cursorField not selected / wrong name) stops the walk.\n        readNextCursor: lastItem => lastItem[cursorField] ?? null,\n        noCursorWarning: `callTail.make: pagination stops here — no value could be read from the returned items via cursorField \"${cursorField}\". Make sure cursorField matches a field present in the response (and in \\`select\\`).`,\n        errorLabel: 'callTailMethod',\n        actionLabel: 'callTail.make',\n        stalledCursorHint: CURSOR_STALLED_HINT_TAIL,\n        // The caller's own `order`, normalised: the server pages by\n        // `field > value` for ASC and `field < value` for DESC, so which way the\n        // cursor must move is their choice rather than a constant here.\n        cursorDirection: resolveCursorDirection(order),\n        maxPages: options?.maxPages,\n        signal: options?.signal\n      })) {\n        for (const item of page) {\n          allItems.push(item)\n        }\n        pages += 1\n        options.progress?.({ pages, rows: allItems.length })\n      }\n    } catch (error) {\n      if (error instanceof KeysetPaginationError) {\n        for (const [index, err] of error.errors) {\n          result.addError(err, index)\n        }\n      } else if (isWalkBoundsError(error)) {\n        // A bound the caller set, not a fault in the data: the pages already\n        // collected are correct, so they are returned with the error attached\n        // rather than discarded — the same shape this walker already produces\n        // for a soft error from the portal.\n        result.addError(error)\n      } else {\n        throw error\n      }\n    }\n\n    return result.setData(allItems)\n  }\n}\n"],"names":["AbstractAction","result","Result","assertTailFilter","DESC_ORDER","SdkError","filterMentionsField","keysetPaginate","CURSOR_STALLED_HINT_TAIL","resolveCursorDirection","KeysetPaginationError","isWalkBoundsError"],"mappings":"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"}