{"version":3,"file":"streaming-fetch-BTESzLKX.cjs","names":[],"sources":["../src/streaming-fetch.ts"],"sourcesContent":["/**\n * Streaming fetch implementation for React Native.\n *\n * React Native's built-in fetch doesn't support response.body.getReader()\n * (ReadableStream). This replaces global.fetch with an XHR-based\n * implementation that streams chunks via ReadableStream, enabling\n * CopilotKit's SSE-based agent communication.\n *\n * If native fetch already supports ReadableStream bodies (newer RN / Hermes),\n * the replacement is skipped entirely.\n *\n * THREADING NOTE: In React Native, XHR callbacks (onprogress, onload, etc.)\n * may fire on a native networking thread. Pushing data into the ReadableStream\n * from that thread can trigger downstream React setState calls on the wrong\n * thread, causing iOS to kill the process with \"deleted thread with uncommitted\n * CATransaction\". All stream-mutating operations are therefore deferred via\n * setTimeout(fn, 0) to bounce back to the JS thread (main thread in Hermes).\n *\n * Call `installStreamingFetch()` once at app startup after polyfills.\n */\n\ndeclare const global: typeof globalThis;\n\n/** Subset of the Response interface implemented by the streaming fetch polyfill. */\ninterface StreamingFetchResponse {\n  readonly ok: boolean;\n  readonly status: number;\n  readonly statusText: string;\n  readonly url: string;\n  readonly type: string;\n  readonly redirected: boolean;\n  readonly bodyUsed: boolean;\n  readonly headers: Headers;\n  readonly body: ReadableStream<Uint8Array>;\n  json(): Promise<unknown>;\n  text(): Promise<string>;\n  arrayBuffer(): Promise<ArrayBuffer>;\n  blob(): Promise<Blob>;\n  clone(): never;\n  formData(): Promise<never>;\n}\n\nfunction createAbortError(): DOMException {\n  return new (global as any).DOMException(\n    \"The operation was aborted.\",\n    \"AbortError\",\n  );\n}\n\nexport function installStreamingFetch(): void {\n  // Skip if native fetch already supports ReadableStream body.\n  // Newer React Native versions (Hermes) may support this natively.\n  try {\n    const testResponse = new Response(\"\");\n    if (\n      testResponse.body != null &&\n      typeof testResponse.body.getReader === \"function\"\n    ) {\n      return;\n    }\n  } catch (e) {\n    // Response constructor unavailable — expected in older RN environments.\n    if (\n      __DEV__ &&\n      e instanceof Error &&\n      !(e instanceof ReferenceError) &&\n      !(e instanceof TypeError)\n    ) {\n      console.warn(\n        \"[CopilotKit] Unexpected error during streaming fetch feature detection, \" +\n          \"installing XHR-based polyfill:\",\n        e,\n      );\n    }\n  }\n\n  const originalFetch = global.fetch;\n  const TextEncoder = global.TextEncoder;\n\n  const streamingFetch = function streamingFetch(\n    input: RequestInfo | URL,\n    init?: RequestInit,\n  ): Promise<Response> {\n    // Extract defaults from Request object when input is a Request\n    const request =\n      typeof input !== \"string\" && !(input instanceof URL) ? input : null;\n    let url: string;\n    if (typeof input === \"string\") {\n      url = input;\n    } else if (input instanceof URL) {\n      url = input.href;\n    } else {\n      url = (input as Request).url;\n    }\n    const method = init?.method || request?.method || \"GET\";\n    const headers = init?.headers || (request ? request.headers : {});\n    const body = (init?.body ?? request?.body) as string | null | undefined;\n    const signal = init?.signal || request?.signal;\n\n    return new Promise((resolve, reject) => {\n      // Reject immediately if signal is already aborted (per fetch spec)\n      if (signal?.aborted) {\n        reject(createAbortError());\n        return;\n      }\n\n      const xhr = new XMLHttpRequest();\n      xhr.open(method, url);\n\n      // Default 60s timeout to prevent hanging on stalled mobile connections\n      // (WiFi→cellular transitions, tunnels, serverless cold starts).\n      // Callers can still use AbortSignal.timeout() for finer control.\n      xhr.timeout = 60_000;\n\n      let headerEntries: [string, string][];\n      if (headers instanceof Headers) {\n        headerEntries = Array.from(headers.entries());\n      } else if (Array.isArray(headers)) {\n        headerEntries = headers as [string, string][];\n      } else {\n        headerEntries = Object.entries(headers as Record<string, string>);\n      }\n      for (const [key, value] of headerEntries) {\n        xhr.setRequestHeader(key, value as string);\n      }\n\n      xhr.responseType = \"text\";\n\n      let streamController: ReadableStreamDefaultController<Uint8Array> | null =\n        null;\n      let lastIndex = 0;\n      let streamClosed = false;\n      let settled = false;\n      const encoder = new TextEncoder();\n\n      // Promise that resolves/rejects when XHR completes or fails\n      let resolveFullText: (text: string) => void;\n      let rejectFullText: (error: Error) => void;\n      const fullTextPromise = new Promise<string>((res, rej) => {\n        resolveFullText = res;\n        rejectFullText = rej;\n      });\n      // Prevent unhandled rejection when error occurs but .text()/.json() is never called\n      fullTextPromise.catch(() => {});\n\n      function closeStream() {\n        if (streamController && !streamClosed) {\n          streamClosed = true;\n          streamController.close();\n        }\n      }\n\n      function errorStream(err: Error) {\n        if (streamController && !streamClosed) {\n          streamClosed = true;\n          streamController.error(err);\n        }\n      }\n\n      function flushChunks() {\n        if (\n          streamController &&\n          !streamClosed &&\n          xhr.responseText.length > lastIndex\n        ) {\n          const newData = xhr.responseText.slice(lastIndex);\n          lastIndex = xhr.responseText.length;\n          streamController.enqueue(encoder.encode(newData));\n        }\n      }\n\n      /** Centralized error handler — errors the stream, rejects fullTextPromise,\n       *  and rejects the outer fetch promise if not yet settled. */\n      function fail(err: Error) {\n        cleanupAbortListener();\n        errorStream(err);\n        rejectFullText(err);\n        if (!settled) {\n          settled = true;\n          reject(err);\n        }\n      }\n\n      const onAbort = () => {\n        fail(createAbortError());\n        xhr.abort();\n      };\n\n      if (signal) {\n        signal.addEventListener(\"abort\", onAbort);\n      }\n\n      function cleanupAbortListener() {\n        if (signal) {\n          signal.removeEventListener(\"abort\", onAbort);\n        }\n      }\n\n      const stream = new ReadableStream<Uint8Array>({\n        start(controller) {\n          streamController = controller;\n        },\n        cancel() {\n          xhr.abort();\n          rejectFullText(createAbortError());\n        },\n      });\n\n      // All XHR callbacks are wrapped with setTimeout(fn, 0) to ensure they\n      // run on the JS thread. In React Native, XHR callbacks may fire on a\n      // native networking thread; calling streamController.enqueue() there\n      // triggers downstream React setState on the wrong thread, which causes\n      // iOS to kill the process (\"deleted thread with uncommitted CATransaction\").\n      // setTimeout(fn, 0) defers execution to the JS event loop (main thread\n      // in Hermes) with negligible latency — streaming still feels real-time.\n\n      xhr.onprogress = function () {\n        setTimeout(() => {\n          try {\n            flushChunks();\n          } catch (err) {\n            fail(err instanceof Error ? err : new Error(String(err)));\n            xhr.abort();\n          }\n        }, 0);\n      };\n\n      xhr.onload = function () {\n        setTimeout(() => {\n          cleanupAbortListener();\n          try {\n            flushChunks();\n          } catch (err) {\n            fail(err instanceof Error ? err : new Error(String(err)));\n            return;\n          }\n          closeStream();\n          resolveFullText(xhr.responseText);\n        }, 0);\n      };\n\n      xhr.onerror = function () {\n        setTimeout(() => {\n          fail(new TypeError(\"Network request failed\"));\n        }, 0);\n      };\n\n      xhr.ontimeout = function () {\n        setTimeout(() => {\n          fail(new TypeError(\"Network request timed out\"));\n        }, 0);\n      };\n\n      // Resolve with Response once headers arrive.\n      // Guard against status === 0 which XHR produces for CORS failures,\n      // DNS errors, and mixed-content blocks — let onerror handle those.\n      let resp: StreamingFetchResponse | null = null;\n      xhr.onreadystatechange = function () {\n        // Capture XHR state synchronously before deferring — XHR properties\n        // may change between now and when setTimeout fires.\n        const readyState = xhr.readyState;\n        const xhrStatus = xhr.status;\n        const xhrStatusText = xhr.statusText;\n        const rawHeaders = xhr.getAllResponseHeaders() || \"\";\n\n        setTimeout(() => {\n          // Safety net: if XHR completed but we never resolved/rejected, fail explicitly.\n          // This can happen when status === 0 and onerror doesn't fire (some RN networking impls).\n          if (readyState === 4 && !settled && !resp) {\n            fail(\n              new TypeError(\n                `Network request to ${url} completed with status ${xhrStatus} but no response was produced. ` +\n                  `This may indicate a CORS failure, DNS error, or React Native networking issue.`,\n              ),\n            );\n            return;\n          }\n\n          if (readyState >= 2 && !resp && xhrStatus !== 0) {\n            const respHeaders: Record<string, string> = {};\n            for (const line of rawHeaders.trim().split(\"\\r\\n\")) {\n              const idx = line.indexOf(\": \");\n              if (idx > 0) {\n                respHeaders[line.slice(0, idx).toLowerCase()] = line.slice(\n                  idx + 2,\n                );\n              }\n            }\n\n            const responseHeaders = new Headers(respHeaders);\n\n            let bodyUsed = false;\n\n            resp = {\n              // Duck-typed Response object (not a native Response instance)\n              ok: xhrStatus >= 200 && xhrStatus < 300,\n              status: xhrStatus,\n              statusText: xhrStatusText,\n              url: url,\n              type: \"basic\",\n              redirected: false,\n              get bodyUsed() {\n                return bodyUsed;\n              },\n              headers: responseHeaders,\n              body: stream,\n              json: async () => {\n                bodyUsed = true;\n                const text = await fullTextPromise;\n                try {\n                  return JSON.parse(text);\n                } catch (e) {\n                  throw new TypeError(\n                    `Failed to parse JSON from ${method} ${url} (status ${xhrStatus}): ${\n                      text.length > 200 ? text.slice(0, 200) + \"...\" : text\n                    }`,\n                    { cause: e },\n                  );\n                }\n              },\n              text: async () => {\n                bodyUsed = true;\n                return fullTextPromise;\n              },\n              arrayBuffer: async () => {\n                bodyUsed = true;\n                return encoder.encode(await fullTextPromise).buffer;\n              },\n              blob: async () => {\n                bodyUsed = true;\n                const buf = encoder.encode(await fullTextPromise);\n                if (typeof Blob !== \"undefined\") {\n                  return new Blob([buf], {\n                    type: responseHeaders.get(\"content-type\") || \"\",\n                  });\n                }\n                throw new Error(\n                  \"Blob is not available in this React Native environment.\",\n                );\n              },\n              clone: () => {\n                throw new Error(\n                  \"Response.clone() is not supported by the React Native streaming fetch polyfill.\",\n                );\n              },\n              formData: async () => {\n                throw new Error(\n                  \"Response.formData() is not supported by the React Native streaming fetch polyfill.\",\n                );\n              },\n            };\n            settled = true;\n            // NOTE: abort listener is NOT removed here — the signal must remain\n            // wired to xhr.abort() for mid-stream cancellation. Cleanup happens\n            // in terminal handlers (onload, onerror, ontimeout) or onAbort itself.\n            resolve(resp as unknown as Response);\n          }\n        }, 0);\n      };\n\n      xhr.send(body ?? null);\n    });\n  };\n\n  // Expose original fetch for opt-out (e.g., third-party libs that need native behavior)\n  (streamingFetch as any).__originalFetch = originalFetch;\n  global.fetch = streamingFetch as typeof fetch;\n}\n"],"mappings":"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"}