htmx-extensions
htmx-extensions copied to clipboard
No way to proxy SSE data
It would be interesting if there was a way to update the server side event's data before they got swapped into the elements.
The main reason for this is that SSE specification uses \n\n as an event terminator and the way to send multiline data is to base64 encode it before. However, there is no way to decode it on the frontend before the swap. At least not to my knowledge.
Hey, doesn't the htmx:sseBeforeMessage match your usecase here? It should allow you to override event.data which is then passed to the swap method
https://github.com/bigskysoftware/htmx-extensions/blob/719538f486614ba48a16c7b53a4ca1f069132d97/src/sse/sse.js#L118-L123
Let me know!
Thanks for the response @Telroshan.
I'm afraid event.data is read-only. It seems to be protected by a getter.
The shortest workaround I've found is to fork the library and dispatch a custom event with a closure like so:
var decodedData = null
const setDecodedData = function(data) {
decodedData = data
}
const newEvent = new CustomEvent('htmx:sseBeforeSwap', {
detail: { setDecodedData, data: event.data },
})
// swap the response into the DOM and trigger a notification
if (!api.triggerEvent(elt, 'htmx:sseBeforeSwap', newEvent)) {
return
}
swap(elt, decodedData || event.data)
Which is no easier than just decoding directly in the extension side.
Oh, indeed the properties are read-only...
Maybe we should use a copy of event in the extension to pass down to htmx:sseBeforeMessage? Like { ... event } ? I don't know if that's a good idea, I would expect it to just copy the values and get rid of the properties protections, so that we don't add an extra event when we don't need one, and since it'd be an object reference, we wouldn't change the code that much.
Or, we could add an additional property to the event data like a dataOverride, that if is set would take precedence over event.data
Just some thoughts out loud, if you feel like it, feel free to explore solutions and submit a PR!
I would be happy to contribute.
Approaching this problem from another perspective, I think message encoding should be configured to a specific SSE connection/endpoint. I would go for an sse-encoding="base64" attribute, whereas sse-encoding="text" is the default. Such an example use case would read as:
<div hx-ext="sse" sse-connect="/api/events" sse-encoding="base64">
<!-- html fowarded to swaps is already decoded into text -->
</div>
Then the proper encoding process would occur inside a callback registered inside registerSSE. What do you think @Telroshan?
This would solve your immediate usecase, but I'im thinking an event-based solution would be more modular and let people handle more specific usecases as needed.
With this approach, you could easily use a custom attribute if you want to and say, in a global htmx:sseBeforeMessage listener, call atob to decode your base 64 and override the message data if your event's target has a closest element with that attribute. So 1 if + 1 instruction in your handler.
I would suppose that the sseBeforeMessage event was designed with this goal in mind, unfortunately it seems it hadn't been tested until now, considering that read-only constraint. It would've worked similarly to say htmx:configRequest, that's also an event that lets you override whatever you want to override before htmx sends its requests.
It's a nice and easy way to cover any future specific needs people might have, because we give them the keys to do so, while implementing just an additional attribute for the encoding here, would require adding even more things to the extension code later on if people have other specific usecases not covered by this change
Running into the same issues here. Both the encoding issue but also the lack of ability to modify the event data before it is swapped in.
The workaround sofar is to just use hx-trigger with sse and do a round trip to the server with hx-get to get the properly formatted data. This reduces the functionality of sse to mere triggers. Also, as the formatting is a client side concern, having to code this on the server for each case where it differs is not the most efficient way. The extension client-side-templates is interesting but is a limited use-case. It also requires another extra library for template handling.
My latest workaround is to listen for the event htmx:sseBeforeMessage, cancel it with preventDefault, json decode the data field and use it to modify the event target directly in javascript.
Any news on that? Is it possible to transform the event data before swap? Can't seem to get it working, either.