@bitrix24/b24jssdk
Version:
Bitrix24 REST API JavaScript SDK
1 lines • 6.59 kB
Source Map (JSON)
{"version":3,"file":"_keyset-paginate.mjs","sources":["../../../../../src/core/actions/v3/_keyset-paginate.ts"],"sourcesContent":["import type { TypeB24 } from '../../../types/b24'\nimport type { LoggerInterface } from '../../../types/logger'\nimport type { TypeCallParams } from '../../../types/http'\nimport type { AjaxResult } from '../../http/ajax-result'\n\n/**\n * Thrown by {@link keysetPaginate} when the underlying v3 `call` reports a soft\n * error part-way through a walk. It carries both the raw `[key, Error]` entries\n * (so the eager `call*` helpers can fold them into a `Result` via `addError`)\n * and the flat messages (so the streaming `fetch*` helpers can rethrow as their\n * own action-specific `SdkError`).\n */\nexport class KeysetPaginationError extends Error {\n public readonly errors: Iterable<[string, Error]>\n public readonly messages: string[]\n\n constructor(errors: Iterable<[string, Error]>, messages: string[]) {\n super(messages.join('; '))\n this.name = 'KeysetPaginationError'\n this.errors = errors\n this.messages = messages\n }\n}\n\n/**\n * Per-helper plug-ins that adapt the shared keyset loop to either the emulated\n * `list` cursor (a `[field, '>', n]` filter) or the native `tail` cursor.\n */\nexport type KeysetPaginateStrategy = {\n /** REST API method name (e.g. `tasks.task.list`, `main.eventlog.tail`). */\n method: string\n /** Optional request id forwarded to `call`. */\n requestId?: string\n /** Key under `result` that holds the row array (e.g. `items`). */\n customKeyForResult: string | null\n /** Cursor value used for the very first page. */\n initialCursor: number | string\n /** Build the per-page request params for a given cursor value. */\n buildParams: (cursor: number | string) => TypeCallParams\n /**\n * Read the next cursor value from the last item of a full page. Return `null`\n * to stop pagination — used when the cursor field cannot be read from the\n * response (the loop logs {@link KeysetPaginateStrategy.noCursorWarning}).\n */\n readNextCursor: (lastItem: Record<string, any>) => number | string | null\n /** Logged at `warning` level when `readNextCursor` returns `null`. */\n noCursorWarning: string\n /** Label logged at `error` level when the underlying `call` fails. */\n errorLabel: string\n}\n\n/**\n * Shared keyset-pagination driver for the v3 list/tail helpers\n * (`callList`/`fetchList` and `callTail`/`fetchTail`).\n *\n * It repeatedly calls `actions.v3.call`, yields each page, and advances the\n * cursor until the data runs out. End-of-data is detected by the size of the\n * page the **server actually returns**, not the requested `limit`: the loop\n * tracks the largest page seen so far (`maxPageSize`) and stops only when a page\n * is shorter than that (or empty). This matters because some v3 methods (e.g.\n * `tasks.task.list`) silently cap the page below the requested `limit`; keying\n * the stop on `limit` would end the walk right after the first capped page.\n *\n * On a soft error it throws {@link KeysetPaginationError}; the calling helper\n * decides whether to fold it into a `Result` (eager) or rethrow as an\n * `SdkError` (streaming).\n */\nexport async function* keysetPaginate<T = unknown>(\n b24: TypeB24,\n logger: LoggerInterface,\n strategy: KeysetPaginateStrategy\n): AsyncGenerator<T[]> {\n let cursor = strategy.initialCursor\n let maxPageSize = 0\n\n while (true) {\n const response: AjaxResult<T> = await b24.actions.v3.call.make<T>({\n method: strategy.method,\n params: strategy.buildParams(cursor),\n requestId: strategy.requestId\n })\n\n if (!response.isSuccess) {\n logger.error(strategy.errorLabel, {\n method: strategy.method,\n requestId: strategy.requestId,\n messages: response.getErrorMessages()\n })\n throw new KeysetPaginationError(response.errors, response.getErrorMessages())\n }\n\n const responseData = response.getData()\n if (!responseData) {\n break\n }\n\n const resultData: T[] = (responseData.result as any)[strategy.customKeyForResult as any] as T[]\n // Guard against a wrong `customKeyForResult` (key absent → undefined): treat\n // a missing/non-array bucket as \"no data\" instead of throwing on `.length`.\n if (!Array.isArray(resultData) || resultData.length === 0) {\n break\n }\n\n yield resultData\n\n maxPageSize = Math.max(maxPageSize, resultData.length)\n if (resultData.length < maxPageSize) {\n break\n }\n\n const lastItem = resultData[resultData.length - 1] as Record<string, any>\n const next = lastItem ? strategy.readNextCursor(lastItem) : null\n if (next === null || next === undefined) {\n logger.warning(strategy.noCursorWarning)\n break\n }\n cursor = next\n }\n}\n"],"names":[],"mappings":";;;;;;;;;;AAYO,MAAM,8BAA8B,KAAA,CAAM;AAAA,EAZjD;AAYiD,IAAA,MAAA,CAAA,IAAA,EAAA,uBAAA,CAAA;AAAA;AAAA,EAC/B,MAAA;AAAA,EACA,QAAA;AAAA,EAEhB,WAAA,CAAY,QAAmC,QAAA,EAAoB;AACjE,IAAA,KAAA,CAAM,QAAA,CAAS,IAAA,CAAK,IAAI,CAAC,CAAA;AACzB,IAAA,IAAA,CAAK,IAAA,GAAO,uBAAA;AACZ,IAAA,IAAA,CAAK,MAAA,GAAS,MAAA;AACd,IAAA,IAAA,CAAK,QAAA,GAAW,QAAA;AAAA,EAClB;AACF;AA6CA,gBAAuB,cAAA,CACrB,GAAA,EACA,MAAA,EACA,QAAA,EACqB;AACrB,EAAA,IAAI,SAAS,QAAA,CAAS,aAAA;AACtB,EAAA,IAAI,WAAA,GAAc,CAAA;AAElB,EAAA,OAAO,IAAA,EAAM;AACX,IAAA,MAAM,WAA0B,MAAM,GAAA,CAAI,OAAA,CAAQ,EAAA,CAAG,KAAK,IAAA,CAAQ;AAAA,MAChE,QAAQ,QAAA,CAAS,MAAA;AAAA,MACjB,MAAA,EAAQ,QAAA,CAAS,WAAA,CAAY,MAAM,CAAA;AAAA,MACnC,WAAW,QAAA,CAAS;AAAA,KACrB,CAAA;AAED,IAAA,IAAI,CAAC,SAAS,SAAA,EAAW;AACvB,MAAA,MAAA,CAAO,KAAA,CAAM,SAAS,UAAA,EAAY;AAAA,QAChC,QAAQ,QAAA,CAAS,MAAA;AAAA,QACjB,WAAW,QAAA,CAAS,SAAA;AAAA,QACpB,QAAA,EAAU,SAAS,gBAAA;AAAiB,OACrC,CAAA;AACD,MAAA,MAAM,IAAI,qBAAA,CAAsB,QAAA,CAAS,MAAA,EAAQ,QAAA,CAAS,kBAAkB,CAAA;AAAA,IAC9E;AAEA,IAAA,MAAM,YAAA,GAAe,SAAS,OAAA,EAAQ;AACtC,IAAA,IAAI,CAAC,YAAA,EAAc;AACjB,MAAA;AAAA,IACF;AAEA,IAAA,MAAM,UAAA,GAAmB,YAAA,CAAa,MAAA,CAAe,QAAA,CAAS,kBAAyB,CAAA;AAGvF,IAAA,IAAI,CAAC,KAAA,CAAM,OAAA,CAAQ,UAAU,CAAA,IAAK,UAAA,CAAW,WAAW,CAAA,EAAG;AACzD,MAAA;AAAA,IACF;AAEA,IAAA,MAAM,UAAA;AAEN,IAAA,WAAA,GAAc,IAAA,CAAK,GAAA,CAAI,WAAA,EAAa,UAAA,CAAW,MAAM,CAAA;AACrD,IAAA,IAAI,UAAA,CAAW,SAAS,WAAA,EAAa;AACnC,MAAA;AAAA,IACF;AAEA,IAAA,MAAM,QAAA,GAAW,UAAA,CAAW,UAAA,CAAW,MAAA,GAAS,CAAC,CAAA;AACjD,IAAA,MAAM,IAAA,GAAO,QAAA,GAAW,QAAA,CAAS,cAAA,CAAe,QAAQ,CAAA,GAAI,IAAA;AAC5D,IAAA,IAAI,IAAA,KAAS,IAAA,IAAQ,IAAA,KAAS,MAAA,EAAW;AACvC,MAAA,MAAA,CAAO,OAAA,CAAQ,SAAS,eAAe,CAAA;AACvC,MAAA;AAAA,IACF;AACA,IAAA,MAAA,GAAS,IAAA;AAAA,EACX;AACF;AAnDuB,MAAA,CAAA,cAAA,EAAA,gBAAA,CAAA;;;;"}