mpetazzoni/sse.js

Make importable as an ES module?

Closed this issue · 6 comments

I see that this module currently exports in CommonJS format for Node.js, but I'd like to import it as an ES module in browser JavaScript. Is it possible to make this library compatible with both import/export styles?

On my own fork, I was able to simply add export { SSE }; to the end of sse.js and everything works great for me, but I don't know if that would impact usage in Node at all.

Hello, any news on this? where exactly are you importing it? I REALLY need a code sample of your implementation on the browser to understand what's going on. How am I supposed to import it? it says SSE undefined if I do it the way it's done in the readMe

Just to clarify, all of this was to use the native ES module type rather than the CommonJS module type used by Node. If you're using Node, you don't need any of this:

It was a while ago and I wound up not using this module at all. Looking back through my project's history, I added export { SSE }; to the end of sse.js, and then in my main.js file I had:

import { SSE } from "sse.js";

...

function sendData() {
    const FD = new FormData(mainform);
    var overpassCountdown;
    evsource = new SSE("/result", {
        payload: FD,
    });
    evsource.addEventListener("error", () => {
        console.log("error");
    });
    evsource.addEventListener("open", () => {
        console.log("SSE connection open");
    });
    evsource.addEventListener("message", (e) => {
        // Fallback handler for undefined event types
        console.log("message " + e.data);
    });
    evsource.addEventListener("event_type_1", (e) => {
        // Code to handle different event types
    });
    evsource.stream();
}

You're a lifesaver my friend! May I ask why you didn't use it? how did you send your initial payload to the server? Also, how exactly am I supposed to access the payload from the server side? cause I can't find it in the request

My original goal in using SSE.js was to avoid needing to manage state on the server; I was hoping to just send a form, return an EventSource, and send the client progress and then the result through that stream. Eventually I ran into problems with results taking a long time, if the user leaves or refreshes the page the job is lost, maybe some other things. It wound up being easier to send the form through a regular POST request, receive a UUID for the job, and use that UUID to access the EventSource for the progress and the results once available. That meant that I had to use Celery to manage different jobs from different users, which introduced some complications, but it was worth it. Once I did that, I didn't need the special version of EventSource that this library provides.

I believe the payload gets sent either when you create the SSE object:

evsource = new SSE("/result", {
        payload: FD,
    });

or when you stream it with evsource.stream(); How you access it will depend on your server; my server code was written in Python, using the Flask framework. The code that parses the payload looks like this:

@app.route("/result", methods=["POST"])
def result():
    country = request.form.get("location", "", str.upper)

    startdate = request.form.get("startdate", type=datetime.fromisoformat)
    enddate = request.form.get("enddate")
    if enddate:
        enddate = datetime.fromisoformat(enddate)

    filter_list = filter_processing(request.form.getlist("filters"))
    # Do more stuff    
    ...
    def process_data() -> Generator:
        oldfile = request.files.get("old")
        newfile = request.files.get("new")

        # Analyze the input
        ...

        # Send how many items will be done
        yield message("event_type_1", len(tasks_to_do))
        # Send the start time so the client can figure out 
        # when a job has timed out
        yield message("event_type_2", start_time)

        # Do the main work
        ...

        # Sends the URL where the client can download the results
        yield str(message("file", the_path))


    return Response(
        stream_with_context(process_data()), mimetype="text/event-stream",
    )

def message(message_type: str, value: int) -> str:
    return f"event: {message_type}\ndata: {value}\n\n"

Thanks a lot for your help, there's a way to avoid losing the job by having the client send the previous job id if exists and retrieve the missed updates to display them for the client. I'm using expressJS but the payload doesn't seem to be in the request for some reason.

@mpetazzoni Have you thought about adding this to the package? It's enough to add export { SSE }; at the end of the sse.js file. It can be released as version 2.0.0 because older Node versions prior to 15.3.0 might have problems with this, as they don't recognize export.