{"version":3,"file":"fetch-tail.mjs","sources":["../../../../../src/core/actions/v3/fetch-tail.ts"],"sourcesContent":["import type { ActionOptions } from '../abstract-action'\nimport type { TypeCallParams } from '../../../types/http'\nimport { AbstractAction } from '../abstract-action'\nimport { SdkError } from '../../sdk-error'\nimport { keysetPaginate, KeysetPaginationError } from './_keyset-paginate'\n\nexport type ActionFetchTailV3 = ActionOptions & {\n  method: string\n  params?: Omit<TypeCallParams, 'pagination' | 'order' | 'cursor'>\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 * Calls a REST API `tail` method (native keyset cursor) and returns an async\n * generator for efficient large data retrieval. `restApi:v3`\n *\n * Unlike `fetchList`, which emulates keyset pagination on top of the `list`\n * action by injecting a `[field, '>', n]` filter, this helper drives the server\n * `tail` action with its native `cursor: { field, value, order, limit }`\n * parameter (see the v3 reference §6.2). The server itself adds `field > value`\n * (asc) / `field < value` (desc) and sorts by `field`, so the cursor field\n * MUST NOT appear in `filter` (the server rejects it with\n * `INVALIDFILTEREXCEPTION`).\n */\nexport class FetchTailV3 extends AbstractAction {\n  /**\n   * Streams every record of a `tail` method as chunks, advancing the keyset\n   * cursor between requests.\n   *\n   * @template T - The type of items in the returned arrays (default is `unknown`).\n   *\n   * @param {ActionFetchTailV3} options - parameters for executing the request.\n   *     - `method: string` - A REST API `tail` method name (for example: `main.eventlog.tail`).\n   *     - `params?: Omit<TypeCallParams, 'pagination' | 'order' | 'cursor'>` - Request parameters.\n   *         Use `filter` and `select` to control the selection. `pagination`, `order` and `cursor`\n   *         are managed by this helper and must not be passed. The cursor field must NOT be used in `filter`.\n   *     - `cursorField?: string` - The DTO field that drives the cursor. Must be monotonic and\n   *         preferably unique, and present in `select`. 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   *     - `limit?: number` - How many records to retrieve at a time. Default is `50`. Maximum is `1000`.\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 {AsyncGenerator<T[]>} An async generator that yields chunks of data as arrays of type `T`.\n   *\n   * @example\n   * const generator = b24.actions.v3.fetchTail.make<{ id: string }>({\n   *   method: 'main.eventlog.tail',\n   *   params: { select: ['id', 'auditType'] },\n   *   cursorField: 'id',\n   *   customKeyForResult: 'items'\n   * })\n   * for await (const chunk of generator) {\n   *   console.log(`Processing ${chunk.length} items`)\n   * }\n   */\n  public override async* make<T = unknown>(options: ActionFetchTailV3): AsyncGenerator<T[]> {\n    const batchSize = options?.limit ?? 50\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    // 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/i.test(order) && options?.initialValue === undefined) {\n      throw new SdkError({\n        code: 'JSSDK_CORE_B24_FETCH_TAIL_DESC_REQUIRES_INITIAL_VALUE',\n        description: 'fetchTail.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    // The cursor field cannot also live in `filter` — the server forces ordering\n    // and the `> value` condition on it and rejects a duplicate with\n    // INVALIDFILTEREXCEPTION. Warn instead of letting the server 400. (Detection\n    // covers only the short-form `[field, op, value]` triples the SDK emits.)\n    if (Array.isArray(params['filter']) && params['filter'].some((c: any) => Array.isArray(c) && c[0] === cursorField)) {\n      this._logger.warning(`fetchTail.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\\`.`)\n    }\n\n    // The cursor field must be readable in the response to advance. Append it to\n    // an explicit `select`; when `select` is omitted we rely on the server's\n    // default field set — warn for a non-default cursorField, which may not be\n    // in those defaults (the per-page guard below would otherwise stop silently).\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(`fetchTail.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.`)\n    }\n\n    const { select: _ignoredSelect, ...restParams } = params as TypeCallParams\n\n    try {\n      yield* 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: `fetchTail.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: 'fetchTailMethod'\n      })\n    } catch (error) {\n      if (error instanceof KeysetPaginationError) {\n        throw new SdkError({\n          code: 'JSSDK_CORE_B24_FETCH_TAIL_METHOD_API_V3',\n          description: `API Error: ${error.messages.join('; ')}`,\n          status: 500\n        })\n      }\n      throw error\n    }\n  }\n}\n"],"names":[],"mappings":";;;;;;;;;;;;;;AA6BO,MAAM,oBAAoB,cAAA,CAAe;AAAA,EA7BhD;AA6BgD,IAAA,MAAA,CAAA,IAAA,EAAA,aAAA,CAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAmC9C,OAAuB,KAAkB,OAAA,EAAiD;AACxF,IAAA,MAAM,SAAA,GAAY,SAAS,KAAA,IAAS,EAAA;AACpC,IAAA,MAAM,WAAA,GAAc,SAAS,WAAA,IAAe,IAAA;AAC5C,IAAA,MAAM,KAAA,GAAQ,SAAS,KAAA,IAAS,KAAA;AAChC,IAAA,MAAM,kBAAA,GAAqB,SAAS,kBAAA,IAAsB,OAAA;AAC1D,IAAA,MAAM,MAAA,GAAS,OAAA,EAAS,MAAA,IAAU,EAAC;AAKnC,IAAA,IAAI,QAAQ,IAAA,CAAK,KAAK,CAAA,IAAK,OAAA,EAAS,iBAAiB,MAAA,EAAW;AAC9D,MAAA,MAAM,IAAI,QAAA,CAAS;AAAA,QACjB,IAAA,EAAM,uDAAA;AAAA,QACN,WAAA,EAAa,uMAAA;AAAA,QACb,MAAA,EAAQ;AAAA,OACT,CAAA;AAAA,IACH;AAMA,IAAA,IAAI,KAAA,CAAM,QAAQ,MAAA,CAAO,QAAQ,CAAC,CAAA,IAAK,MAAA,CAAO,QAAQ,CAAA,CAAE,IAAA,CAAK,CAAC,CAAA,KAAW,KAAA,CAAM,QAAQ,CAAC,CAAA,IAAK,EAAE,CAAC,CAAA,KAAM,WAAW,CAAA,EAAG;AAClH,MAAA,IAAA,CAAK,OAAA,CAAQ,OAAA,CAAQ,CAAA,kCAAA,EAAqC,WAAW,CAAA,wKAAA,CAAqK,CAAA;AAAA,IAC5O;AAMA,IAAA,IAAI,MAAA,GAAS,OAAO,QAAQ,CAAA;AAC5B,IAAA,IAAI,KAAA,CAAM,OAAA,CAAQ,MAAM,CAAA,EAAG;AACzB,MAAA,IAAI,CAAC,MAAA,CAAO,QAAA,CAAS,WAAW,CAAA,EAAG;AACjC,QAAA,MAAA,GAAS,CAAC,GAAG,MAAA,EAAQ,WAAW,CAAA;AAAA,MAClC;AAAA,IACF,CAAA,MAAA,IAAW,gBAAgB,IAAA,EAAM;AAC/B,MAAA,IAAA,CAAK,QAAQ,OAAA,CAAQ,CAAA,uEAAA,EAA0E,WAAW,CAAA,iGAAA,EAA+F,WAAW,CAAA,4BAAA,CAA8B,CAAA;AAAA,IACpP;AAEA,IAAA,MAAM,EAAE,MAAA,EAAQ,cAAA,EAAgB,GAAG,YAAW,GAAI,MAAA;AAElD,IAAA,IAAI;AACF,MAAA,OAAO,cAAA,CAAkB,IAAA,CAAK,IAAA,EAAM,IAAA,CAAK,OAAA,EAAS;AAAA,QAChD,QAAQ,OAAA,CAAQ,MAAA;AAAA,QAChB,WAAW,OAAA,CAAQ,SAAA;AAAA,QACnB,kBAAA;AAAA,QACA,aAAA,EAAe,SAAS,YAAA,IAAgB,CAAA;AAAA;AAAA,QAExC,6BAAa,MAAA,CAAA,CAAA,MAAA,MAAW;AAAA,UACtB,GAAG,UAAA;AAAA,UACH,GAAI,MAAA,GAAS,EAAE,MAAA,KAAW,EAAC;AAAA,UAC3B,MAAA,EAAQ,EAAE,KAAA,EAAO,WAAA,EAAa,OAAO,MAAA,EAAQ,KAAA,EAAO,OAAO,SAAA;AAAU,SACvE,CAAA,EAJa,aAAA,CAAA;AAAA;AAAA;AAAA,QAOb,cAAA,kBAAgB,MAAA,CAAA,CAAA,QAAA,KAAY,QAAA,CAAS,WAAW,KAAK,IAAA,EAArC,gBAAA,CAAA;AAAA,QAChB,eAAA,EAAiB,gHAA2G,WAAW,CAAA,qFAAA,CAAA;AAAA,QACvI,UAAA,EAAY;AAAA,OACb,CAAA;AAAA,IACH,SAAS,KAAA,EAAO;AACd,MAAA,IAAI,iBAAiB,qBAAA,EAAuB;AAC1C,QAAA,MAAM,IAAI,QAAA,CAAS;AAAA,UACjB,IAAA,EAAM,yCAAA;AAAA,UACN,aAAa,CAAA,WAAA,EAAc,KAAA,CAAM,QAAA,CAAS,IAAA,CAAK,IAAI,CAAC,CAAA,CAAA;AAAA,UACpD,MAAA,EAAQ;AAAA,SACT,CAAA;AAAA,MACH;AACA,MAAA,MAAM,KAAA;AAAA,IACR;AAAA,EACF;AACF;;;;"}