EventSource / eventsource

EventSource client for Node.js and Browser (polyfill)
MIT License
911 stars 252 forks source link

eventsource@v3: looking for testers #329

Open rexxars opened 2 days ago

rexxars commented 2 days ago

eventsource v3

TL;DR

I want to modernize this module, making it both easier to maintain and more cross-platform friendly.

You can help test the next version of this module by installing eventsource@next from npm, and reading the migration guide to see how to update your code.

Any issues you find can be reported on this issue. The code for the new version is available in the v3 branch.

Background

This package is soon to be 13 years old. When Aslak and Einar created the first version of this module, node.js wasn't even at 0.10 yet. A lot has changed since those days:

Proposal

Use ES Modules (ESM), with CommonJS fallback

We'll build the module using @sanity/pkg-utils (because we use it where I work, and I am familiar with it), and use conditional exports to provide both ESM and CommonJS variants. Will also switch to named exports, since it has a better cross-module system compatibility.

Split the parser and the client

I've made a parser module for EventSource that is minimal, well tested and works in "any" JavaScript environment. Splitting the concerns makes sense from a testing and maintenance perspective.

Use TypeScript for type safety

I find it easier to maintain and think about code when it's typed. Not having to keep the @types/eventsource module in sync with the main module is a big plus.

Use fetch, ReadableStream, EventTarget

Allows us to reuse the same code across Node.js, browsers and other environments that support the fetch API. The polyfill for browsers becomes very slim, and there are few behavioral differences between the environments.

Remove (most) request-based extensions

In v2, there are some extensions to the WhatWG spec - allowing users to pass custom headers, adding support for proxies, specifying HTTPS options etc. I propose that all of these are removed, and instead left up to the user to implement: they are trivially implemented by passing a custom fetch function. This creates a single extension point, making the module easier to maintain.

Drop support for Node.js < 18

In order to use fetch, web streams and other modern APIs, we need to drop support for Node.js versions < 18. This is a tradeoff I'm willing to make, as Node 18 is going out of LTS in April 2024 and older versions are already out of LTS. Users who are on older versions can still rely on v2 of this module.

Repo: Use conventional commits, automate releases

In order to make the module easier to maintain, I propose we switch to using conventional commits, and automate releases using GitHub Actions. This will make it easier to maintain the module, and make it easier for contributors to understand the release process. Automatic generation of changelogs is a nice bonus.

Repo: Move to main branch

I propose we move the default branch from master to main. This better aligns with the new default branch naming in GitHub.

Disclaimer

While these changes may seem drastic, they are necessary for me to continue supporting this module long term. It also falls within the bounds of what Aslak said when he passed on the maintainership of the module to me:

Be conservative - if you allow non-standard extensions to the W3C API, only do so if it cannot be done easily with a wrapper around the library.

This is exactly what I am proposing, by encapsulating all/most non-standard extensions in a single extension point (the fetch property).

Dependency free

While not 100% true, it only has a single dependency - eventsource-parser, which has zero dependencies of its own.

Keep it working in browsers

This is probably the biggest change - in a positive way. Instead of having to rely on a webpacked file that sets some browser globals, we can now use the module directly in the browser, and with no node.js-polyfills bundled.

Keep it open

I will continue to welcome contributions, but also want to ensure that we keep an eye on security. 19+ million downloads per month means we have a responsibility to our users. By making releases built and signed by GitHub actions, the risk of malicious code being published by someone accidently gaining access to an npm account is reduced.

i18nsite commented 8 hours ago

I use eventsource3, and the program will exit inexplicably after running for a few hours without any alarm (I open and close a different eventsource every minute). With eventsource2, there is no such problem.

my code is like below

gen = (prompt, style, width, height) =>
  seed = Math.floor( Math.random() * 2147483647 )
  session_hash = Math.floor(Math.random()*1e13).toString(36)

  bodyData = {
    data: [
      prompt, # 正面词
      "", # 负面词
      style, # 风格
      false,
      1,
      seed,
      height,
      width,
      1,
      1,
      STEP,
      true
    ]
    event_data: null
    fn_index: 4
    trigger_id: 8
    session_hash
  }

  r = await fetch API+"queue/join?",
    headers:
      accept: "*/*"
      "accept-language": "zh-CN,zh;q=0.9,zh-TW;q=0.8,zh-HK;q=0.7,en;q=0.6"
      "content-type": "application/json"
      priority: "u=1, i"
      "sec-ch-ua": "\"Google Chrome\";v=\"131\", \"Chromium\";v=\"131\", \"Not_A Brand\";v=\"24\""
      "sec-ch-ua-mobile": "?0"
      "sec-ch-ua-platform": "\"macOS\""
      "sec-fetch-dest": "empty"
      "sec-fetch-mode": "cors"
      "sec-fetch-site": "same-origin"
    referrer: "https://nv-sana.mit.edu/"
    referrerPolicy: "strict-origin"
    body: JSON.stringify bodyData
    method: "POST"
    mode: "cors"
    credentials: "omit"

  console.log await r.text()

  esurl = API+"queue/data?session_hash="+session_hash
  es = new EventSource(esurl)
  espromise = new Promise (resolve, reject) =>
    es.onmessage = ({data}) =>
      data = JSON.parse data
      {msg} = data
      switch msg
        when 'estimation'
          console.log '预期', Math.round(data.rank_eta), '秒'
        when 'progress'
          for i from data.progress_data
            console.log i.desc
        when 'process_completed'
          for {image} from data.output.data[0]
            {url} = image
            hash = Buffer.from(
              new Uint8Array await crypto.subtle.digest(
                'SHA-256'
                utf8e url
              )
            ).toString('base64url')
            if style == '(No style)'
              style = 'default'
            fname = style+'.'+hash+'.jpg'
            console.log fname
            await wget url, join(JPG, fname)
            es.onerror = es.onclose = =>
            resolve()
            es.close()
        when 'unexpected_error'
          console.log msg
          reject data.message
        else
          if ! ['close_stream', 'heartbeat', 'process_starts'].includes msg
            console.log '>',data
      return
    es.onerror = (err)=>
      if err?.message
        reject err.message
        # if not (err.defaultPrevented == false and err.cancelable == false)
        #   console.log 'EventSource error:', err
        #   reject err
      return
    es.onclose = =>
      resolve()
      return
    return
  # await r.text() # 必须等一下

  try
    return await espromise
  finally
    es.close()
  return

WH_LI_LEN = WH_LI.length

do =>
  {prompt:prompt_li}= Yml ROOT
  loop
    for [en,zh] from prompt_li
      [w,h] = randWh()
      console.log '\n\n'+zh+'\n\n'+en+'\n'
      loop
        try
          await gen(en, randStyle(), w, h)
          break
        catch e
          console.error 'gen error', e
  console.log 'exit'
  return
rexxars commented 3 hours ago

I use eventsource3, and the program will exit inexplicably after running for a few hours without any alarm (I open and close a different eventsource every minute). With eventsource2, there is no such problem.

Under which environment is this? Node.js? Which version? I'm not familiar with the language above, but could you add a catch handler to your promise that awaits the espromise that logs any errors?