{"version":3,"file":"cancellable.mjs","names":[],"sources":["../../../../../../http/src/cancellable.ts"],"sourcesContent":["import type { HttpError } from \"./HttpError\";\n\n/**\n * CancellableAsyncIterable — an AsyncIterable augmented with `.cancel()`, `.signal`,\n * and `.error`. Returned by Http.stream(). Use with `for await...of`.\n *\n * @example\n * const stream = http.stream<ChatChunk>('/chat', { method: 'POST', data: { messages } });\n * for await (const chunk of stream) {\n *   process(chunk);\n * }\n * // No try/catch needed — check .error after the loop:\n * if (stream.error) console.error('Stream failed:', stream.error.message);\n *\n * // Cancel from outside:\n * stream.cancel('component unmounted');\n */\nexport type CancellableAsyncIterable<T> = AsyncIterable<T> & {\n  /** Abort the in-flight stream. Ends iteration silently. */\n  cancel(reason?: string): void;\n  /** The AbortSignal wired into this stream. */\n  readonly signal: AbortSignal;\n  /**\n   * Set after iteration ends if the stream terminated due to an error.\n   * `null` on clean completion or cancellation.\n   *\n   * The generator never throws — errors are caught, stored here, and the\n   * iteration ends. Check this value after your `for await` loop.\n   */\n  readonly error: HttpError | null;\n};\n\n/**\n * CancellablePromise — a Promise augmented with `.cancel()` and `.signal`.\n *\n * Usage:\n *   const req = http.get<User[]>('/users');\n *   req.cancel('leaving page');\n *   const { data, error } = await req;\n *   // error.isAborted === true\n */\nexport type CancellablePromise<T> = Promise<T> & {\n  /** Abort the in-flight request. */\n  cancel(reason?: string): void;\n  /** The AbortSignal wired into this request. */\n  readonly signal: AbortSignal;\n};\n\n/**\n * Wraps a factory that accepts an AbortSignal and returns a Promise<T>.\n * Returns a CancellablePromise<T> whose .cancel() triggers that signal.\n *\n * If an external signal is provided (e.g. from React Query or useEffect),\n * aborting *either* signal cancels the request.\n */\nexport function makeCancellable<T>(\n  factory: (signal: AbortSignal) => Promise<T>,\n  externalSignal?: AbortSignal,\n): CancellablePromise<T> {\n  const controller = new AbortController();\n\n  // Forward external abort into our internal controller.\n  if (externalSignal) {\n    if (externalSignal.aborted) {\n      controller.abort(externalSignal.reason);\n    } else {\n      externalSignal.addEventListener(\"abort\", () => {\n        controller.abort(externalSignal.reason);\n      }, { once: true }); // { once: true } prevents a permanent listener when the signal is long-lived\n    }\n  }\n\n  const promise = factory(controller.signal) as CancellablePromise<T>;\n\n  promise.cancel = (reason?: string) => {\n    controller.abort(reason ?? \"cancelled\");\n  };\n\n  Object.defineProperty(promise, \"signal\", {\n    get: () => controller.signal,\n    enumerable: false,\n    configurable: false,\n  });\n\n  return promise;\n}\n"],"mappings":";;;;;;;;AAuDA,SAAgB,gBACd,SACA,gBACuB;CACvB,MAAM,aAAa,IAAI,gBAAgB;CAGvC,IAAI,gBACF,IAAI,eAAe,SACjB,WAAW,MAAM,eAAe,MAAM;MAEtC,eAAe,iBAAiB,eAAe;EAC7C,WAAW,MAAM,eAAe,MAAM;CACxC,GAAG,EAAE,MAAM,KAAK,CAAC;CAIrB,MAAM,UAAU,QAAQ,WAAW,MAAM;CAEzC,QAAQ,UAAU,WAAoB;EACpC,WAAW,MAAM,UAAU,WAAW;CACxC;CAEA,OAAO,eAAe,SAAS,UAAU;EACvC,WAAW,WAAW;EACtB,YAAY;EACZ,cAAc;CAChB,CAAC;CAED,OAAO;AACT"}