async function* streamAsyncIterable(stream) {
    const reader = stream.getReader();
    try {
      while (true) {
        const { done, value } = await reader.read();
        if (done) {
          return;
        }
        yield value;
      }
    } finally {
      reader.releaseLock();
    }
  }

// add timeout to fetchSSE
async function fetchSSE(url, options, fetch2 = fetch) {
    const  { createParser } = await import("eventsource-parser");
    const { onMessage, timeout , ...fetchOptions } = options;
    const controller = new AbortController();
    const timeoutId = setTimeout(() => controller.abort(), timeout||30000)
    
    const res = await fetch2(url, {...fetchOptions,signal:controller.signal});
    clearTimeout(timeoutId);
    
    if (!res.ok) {
      let reason;
      try {
        reason = await res.text();
      } catch (err) {
        reason = res.statusText;
      }
      const msg = `ChatGPT error ${res.status}: ${reason}`;
      const error = new ChatGPTError(msg, { cause: res });
      error.statusCode = res.status;
      error.statusText = res.statusText;
      error.context = { url, options };
      throw error;
    }
    const parser = createParser((event) => {
      if (event.type === "event") {
        onMessage(event.data);
      }
    });
    if (!res.body.getReader) {
      const body = res.body;
      if (!body.on || !body.read) {
        throw new ChatGPTError('unsupported "fetch" implementation');
      }
      body.on("readable", () => {
        let chunk;
        while (null !== (chunk = body.read())) {
          parser.feed(chunk.toString());
        }
      });
    } else {
      for await (const chunk of streamAsyncIterable(res.body)) {
        const str = new TextDecoder().decode(chunk);
        parser.feed(str);
      }
    }
}

module.exports = fetchSSE;
