{"version":3,"file":"run-batch-item.mjs","names":[],"sources":["../../../../../../../ai/src/batch/run-batch-item.ts"],"sourcesContent":["import type { BaseResult } from \"../contracts/result/base-result.type\";\nimport type { ExecutableContract } from \"../contracts/executable.contract\";\nimport type { RetryConfig } from \"../contracts/workflow/retry-config.type\";\nimport { isAbortError, resolveBackoff, resolveRetryConfig } from \"../workflow/retry\";\nimport { toAIError } from \"../workflow/step-runner\";\nimport type { BatchItemResult } from \"./batch.type\";\n\n/**\n * Parameters for {@link runBatchItem}. Kept as a plain bag so the\n * concurrency pool can build it once per index without a long\n * positional argument list.\n */\nexport type RunBatchItemParams<TInput, TOptions, TResult extends BaseResult> = {\n  index: number;\n  input: TInput;\n  executable: ExecutableContract<TInput, TOptions, TResult>;\n  retry: RetryConfig | undefined;\n  signal: AbortSignal | undefined;\n};\n\n/**\n * Sleep for `ms`, settling early (without throwing) if `signal`\n * aborts during the wait. The caller re-checks `signal.aborted` after\n * this resolves, so a silent early return is enough — we never want a\n * pending timer to keep the batch alive past cancellation.\n */\nfunction sleep(ms: number, signal: AbortSignal | undefined): Promise<void> {\n  return new Promise((resolve) => {\n    const timer = setTimeout(() => {\n      signal?.removeEventListener(\"abort\", onAbort);\n      resolve();\n    }, ms);\n\n    const onAbort = (): void => {\n      clearTimeout(timer);\n      resolve();\n    };\n\n    signal?.addEventListener(\"abort\", onAbort, { once: true });\n  });\n}\n\n/**\n * Execute one item with per-item retry, isolated so a failure can\n * neither throw nor disturb sibling items running in the same pool.\n *\n * Reuses the workflow retry vocabulary verbatim\n * ({@link resolveRetryConfig} / {@link resolveBackoff}) so batch and\n * workflow steps retry identically. An item is `\"completed\"` when the\n * primitive's own result carries no `error`; a primitive that returns\n * `result.error` (rather than throwing) is treated as a failed attempt\n * and re-run under the same policy.\n *\n * Cancellation short-circuits: if the signal is already aborted on\n * entry the item is reported `\"cancelled\"` without executing; an abort\n * observed mid-flight surfaces as `\"cancelled\"` too.\n */\nexport async function runBatchItem<TInput, TOptions, TResult extends BaseResult>(\n  params: RunBatchItemParams<TInput, TOptions, TResult>,\n): Promise<BatchItemResult<TResult>> {\n  const { index, input, executable, signal } = params;\n\n  if (signal?.aborted) {\n    return { index, status: \"cancelled\", attempts: 0 };\n  }\n\n  const retryConfig = resolveRetryConfig({ retry: params.retry }, undefined);\n  const totalAttempts = Math.max(1, retryConfig.attempts ?? 1);\n\n  let lastResult: TResult | undefined;\n  let lastError: unknown;\n  let attemptsMade = 0;\n\n  for (let attempt = 1; attempt <= totalAttempts; attempt++) {\n    if (signal?.aborted) {\n      return { index, status: \"cancelled\", result: lastResult, attempts: attemptsMade };\n    }\n\n    attemptsMade = attempt;\n\n    try {\n      const result = await executable.execute(input, { signal } as TOptions);\n      lastResult = result;\n\n      if (!result.error) {\n        return { index, status: \"completed\", result, attempts: attempt };\n      }\n\n      lastError = result.error;\n    } catch (error) {\n      if (isAbortError(error)) {\n        return { index, status: \"cancelled\", result: lastResult, attempts: attempt };\n      }\n\n      lastError = error;\n    }\n\n    const canRetry =\n      attempt < totalAttempts &&\n      (retryConfig.retryOn ? retryConfig.retryOn(lastError, attempt) !== false : true);\n\n    if (!canRetry) {\n      break;\n    }\n\n    retryConfig.onRetry?.(attempt + 1, lastError);\n\n    const delay = resolveBackoff(attempt, retryConfig.backoff);\n    if (delay > 0) {\n      await sleep(delay, signal);\n    }\n  }\n\n  if (signal?.aborted) {\n    return { index, status: \"cancelled\", result: lastResult, attempts: attemptsMade };\n  }\n\n  return {\n    index,\n    status: \"failed\",\n    result: lastResult,\n    error: toAIError(lastError),\n    attempts: attemptsMade,\n  };\n}\n"],"mappings":";;;;;;;;;;AA0BA,SAAS,MAAM,IAAY,QAAgD;CACzE,OAAO,IAAI,SAAS,YAAY;EAC9B,MAAM,QAAQ,iBAAiB;GAC7B,QAAQ,oBAAoB,SAAS,OAAO;GAC5C,QAAQ;EACV,GAAG,EAAE;EAEL,MAAM,gBAAsB;GAC1B,aAAa,KAAK;GAClB,QAAQ;EACV;EAEA,QAAQ,iBAAiB,SAAS,SAAS,EAAE,MAAM,KAAK,CAAC;CAC3D,CAAC;AACH;;;;;;;;;;;;;;;;AAiBA,eAAsB,aACpB,QACmC;CACnC,MAAM,EAAE,OAAO,OAAO,YAAY,WAAW;CAE7C,IAAI,QAAQ,SACV,OAAO;EAAE;EAAO,QAAQ;EAAa,UAAU;CAAE;CAGnD,MAAM,cAAc,mBAAmB,EAAE,OAAO,OAAO,MAAM,GAAG,MAAS;CACzE,MAAM,gBAAgB,KAAK,IAAI,GAAG,YAAY,YAAY,CAAC;CAE3D,IAAI;CACJ,IAAI;CACJ,IAAI,eAAe;CAEnB,KAAK,IAAI,UAAU,GAAG,WAAW,eAAe,WAAW;EACzD,IAAI,QAAQ,SACV,OAAO;GAAE;GAAO,QAAQ;GAAa,QAAQ;GAAY,UAAU;EAAa;EAGlF,eAAe;EAEf,IAAI;GACF,MAAM,SAAS,MAAM,WAAW,QAAQ,OAAO,EAAE,OAAO,CAAa;GACrE,aAAa;GAEb,IAAI,CAAC,OAAO,OACV,OAAO;IAAE;IAAO,QAAQ;IAAa;IAAQ,UAAU;GAAQ;GAGjE,YAAY,OAAO;EACrB,SAAS,OAAO;GACd,IAAI,aAAa,KAAK,GACpB,OAAO;IAAE;IAAO,QAAQ;IAAa,QAAQ;IAAY,UAAU;GAAQ;GAG7E,YAAY;EACd;EAMA,IAAI,EAHF,UAAU,kBACT,YAAY,UAAU,YAAY,QAAQ,WAAW,OAAO,MAAM,QAAQ,QAG3E;EAGF,YAAY,UAAU,UAAU,GAAG,SAAS;EAE5C,MAAM,QAAQ,eAAe,SAAS,YAAY,OAAO;EACzD,IAAI,QAAQ,GACV,MAAM,MAAM,OAAO,MAAM;CAE7B;CAEA,IAAI,QAAQ,SACV,OAAO;EAAE;EAAO,QAAQ;EAAa,QAAQ;EAAY,UAAU;CAAa;CAGlF,OAAO;EACL;EACA,QAAQ;EACR,QAAQ;EACR,OAAO,UAAU,SAAS;EAC1B,UAAU;CACZ;AACF"}