// This file is auto-generated by @hey-api/openapi-ts
export const createSseClient = ({ onRequest, onSseError, onSseEvent, responseTransformer, responseValidator, sseDefaultRetryDelay, sseMaxRetryAttempts, sseMaxRetryDelay, sseSleepFn, url, ...options }) => {
    let lastEventId;
    const sleep = sseSleepFn ?? ((ms) => new Promise((resolve) => setTimeout(resolve, ms)));
    const createStream = async function* () {
        let retryDelay = sseDefaultRetryDelay ?? 3000;
        let attempt = 0;
        const signal = options.signal ?? new AbortController().signal;
        while (true) {
            if (signal.aborted)
                break;
            attempt++;
            const headers = options.headers instanceof Headers
                ? options.headers
                : new Headers(options.headers);
            if (lastEventId !== undefined) {
                headers.set("Last-Event-ID", lastEventId);
            }
            try {
                const requestInit = {
                    redirect: "follow",
                    ...options,
                    body: options.serializedBody,
                    headers,
                    signal,
                };
                let request = new Request(url, requestInit);
                if (onRequest) {
                    request = await onRequest(url, requestInit);
                }
                // fetch must be assigned here, otherwise it would throw the error:
                // TypeError: Failed to execute 'fetch' on 'Window': Illegal invocation
                const _fetch = options.fetch ?? globalThis.fetch;
                const response = await _fetch(request);
                if (!response.ok)
                    throw new Error(`SSE failed: ${response.status} ${response.statusText}`);
                if (!response.body)
                    throw new Error("No body in SSE response");
                const reader = response.body.pipeThrough(new TextDecoderStream()).getReader();
                let buffer = "";
                const abortHandler = () => {
                    try {
                        reader.cancel();
                    }
                    catch {
                        // noop
                    }
                };
                signal.addEventListener("abort", abortHandler);
                try {
                    while (true) {
                        const { done, value } = await reader.read();
                        if (done)
                            break;
                        buffer += value;
                        // Normalize line endings: CRLF -> LF, then CR -> LF
                        buffer = buffer.replace(/\r\n/g, "\n").replace(/\r/g, "\n");
                        const chunks = buffer.split("\n\n");
                        buffer = chunks.pop() ?? "";
                        for (const chunk of chunks) {
                            const lines = chunk.split("\n");
                            const dataLines = [];
                            let eventName;
                            for (const line of lines) {
                                if (line.startsWith("data:")) {
                                    dataLines.push(line.replace(/^data:\s*/, ""));
                                }
                                else if (line.startsWith("event:")) {
                                    eventName = line.replace(/^event:\s*/, "");
                                }
                                else if (line.startsWith("id:")) {
                                    lastEventId = line.replace(/^id:\s*/, "");
                                }
                                else if (line.startsWith("retry:")) {
                                    const parsed = Number.parseInt(line.replace(/^retry:\s*/, ""), 10);
                                    if (!Number.isNaN(parsed)) {
                                        retryDelay = parsed;
                                    }
                                }
                            }
                            let data;
                            let parsedJson = false;
                            if (dataLines.length) {
                                const rawData = dataLines.join("\n");
                                try {
                                    data = JSON.parse(rawData);
                                    parsedJson = true;
                                }
                                catch {
                                    data = rawData;
                                }
                            }
                            if (parsedJson) {
                                if (responseValidator) {
                                    await responseValidator(data);
                                }
                                if (responseTransformer) {
                                    data = await responseTransformer(data);
                                }
                            }
                            onSseEvent?.({
                                data,
                                event: eventName,
                                id: lastEventId,
                                retry: retryDelay,
                            });
                            if (dataLines.length) {
                                yield data;
                            }
                        }
                    }
                }
                finally {
                    signal.removeEventListener("abort", abortHandler);
                    reader.releaseLock();
                }
                break; // exit loop on normal completion
            }
            catch (error) {
                // connection failed or aborted; retry after delay
                onSseError?.(error);
                if (sseMaxRetryAttempts !== undefined && attempt >= sseMaxRetryAttempts) {
                    break; // stop after firing error
                }
                // exponential backoff: double retry each attempt, cap at 30s
                const backoff = Math.min(retryDelay * 2 ** (attempt - 1), sseMaxRetryDelay ?? 30000);
                await sleep(backoff);
            }
        }
    };
    const stream = createStream();
    return { stream };
};
