-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
add module for HTMX SSE extension (#5)
* wip commit [vapor example works|hummingbird example does not work] * removed .devontainer and .vscode and added do .gitignore * changed API from .hx.sse.ext() to .hx.ext(.sse) * HummingbirdSSEDemo is working [AsyncStream task is not cancelled on ctrl+c] * fixed no graceful shutdown * implemented Hummingbird Response.stream() * moved .cancelOnGracefulShutdown() to Response.stream() * removed duplicate Examples and defined Response.stream() for HTML protocol * moved definition of sse extension into ElementaryHxSSE * renamed library from 'ElementaryHxSSE' to 'ElementaryHTMXSSE' * removed unstructured Task in Vapor Example * reworked sse-trigger and implemented tests * removed dead code and renamed 'ElementaryHTMXSSE+Hummingbird' to 'Response+Stream'
- Loading branch information
Showing
19 changed files
with
5,771 additions
and
6 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
2,684 changes: 2,683 additions & 1 deletion
2,684
Examples/HummingbirdDemo/Sources/App/Public/htmx.min.js
Large diffs are not rendered by default.
Oops, something went wrong.
121 changes: 121 additions & 0 deletions
121
Examples/HummingbirdDemo/Sources/App/Public/htmxsse.min.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,121 @@ | ||
!(function () { | ||
var e; | ||
function t(e) { | ||
return new EventSource(e, { withCredentials: !0 }); | ||
} | ||
function n(t) { | ||
if (!e.bodyContains(t)) { | ||
var n = e.getInternalData(t).sseEventSource; | ||
if (null != n) return n.close(), !0; | ||
} | ||
return !1; | ||
} | ||
function r(t, n) { | ||
var r = []; | ||
return ( | ||
e.hasAttribute(t, n) && r.push(t), | ||
t.querySelectorAll('[' + n + '], [data-' + n + ']').forEach(function (e) { | ||
r.push(e); | ||
}), | ||
r | ||
); | ||
} | ||
function s(t, n) { | ||
e.withExtensions(t, function (e) { | ||
n = e.transformResponse(n, null, t); | ||
}); | ||
var r = e.getSwapSpecification(t), | ||
s = e.getTarget(t); | ||
e.swap(s, n, r); | ||
} | ||
function a(t) { | ||
return null != e.getInternalData(t).sseEventSource; | ||
} | ||
htmx.defineExtension('sse', { | ||
init: function (n) { | ||
(e = n), null == htmx.createEventSource && (htmx.createEventSource = t); | ||
}, | ||
onEvent: function (t, o) { | ||
var i = o.target || o.detail.elt; | ||
switch (t) { | ||
case 'htmx:beforeCleanupElement': | ||
var u = e.getInternalData(i); | ||
return void (u.sseEventSource && u.sseEventSource.close()); | ||
case 'htmx:afterProcessNode': | ||
!(function t(o, i) { | ||
var u; | ||
if (null == o) return null; | ||
r(o, 'sse-connect').forEach(function (r) { | ||
var s, | ||
a, | ||
o, | ||
u, | ||
c = e.getAttributeValue(r, 'sse-connect'); | ||
null != c && | ||
((s = r), | ||
(a = c), | ||
(o = i), | ||
((u = htmx.createEventSource(a)).onerror = function (r) { | ||
if ( | ||
(e.triggerErrorEvent(s, 'htmx:sseError', { | ||
error: r, | ||
source: u, | ||
}), | ||
!n(s) && u.readyState === EventSource.CLOSED) | ||
) { | ||
var a = Math.random() * (2 ^ (o = o || 0)) * 500; | ||
window.setTimeout(function () { | ||
t(s, Math.min(7, o + 1)); | ||
}, a); | ||
} | ||
}), | ||
(u.onopen = function (t) { | ||
e.triggerEvent(s, 'htmx:sseOpen', { source: u }); | ||
}), | ||
(e.getInternalData(s).sseEventSource = u)); | ||
}), | ||
r((u = o), 'sse-swap').forEach(function (t) { | ||
var r = e.getClosestMatch(t, a); | ||
if (null == r) return null; | ||
for ( | ||
var o = e.getInternalData(r).sseEventSource, | ||
i = e.getAttributeValue(t, 'sse-swap').split(','), | ||
c = 0; | ||
c < i.length; | ||
c++ | ||
) { | ||
var l = i[c].trim(), | ||
v = function (a) { | ||
if (!n(r)) { | ||
if (!e.bodyContains(t)) | ||
return void o.removeEventListener(l, v); | ||
e.triggerEvent(u, 'htmx:sseBeforeMessage', a) && | ||
(s(t, a.data), | ||
e.triggerEvent(u, 'htmx:sseMessage', a)); | ||
} | ||
}; | ||
(e.getInternalData(t).sseEventListener = v), | ||
o.addEventListener(l, v); | ||
} | ||
}), | ||
r(u, 'hx-trigger').forEach(function (t) { | ||
var r = e.getClosestMatch(t, a); | ||
if (null == r) return null; | ||
var s = e.getInternalData(r).sseEventSource, | ||
o = e.getAttributeValue(t, 'hx-trigger'); | ||
if (null != o && 'sse:' == o.slice(0, 4)) { | ||
var i = function (a) { | ||
!n(r) && | ||
(e.bodyContains(t) || s.removeEventListener(o, i), | ||
htmx.trigger(t, o, a), | ||
htmx.trigger(t, 'htmx:sseMessage', a)); | ||
}; | ||
(e.getInternalData(u).sseEventListener = i), | ||
s.addEventListener(o.slice(4), i); | ||
} | ||
}); | ||
})(i); | ||
} | ||
}, | ||
}); | ||
})(); |
17 changes: 17 additions & 0 deletions
17
Examples/HummingbirdDemo/Sources/App/Response+Stream.swift
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
import Elementary | ||
import Hummingbird | ||
import HummingbirdElementary | ||
import ServiceLifecycle | ||
|
||
public extension Response { | ||
static func stream<BufferSequence: AsyncSequence & Sendable>(_ asyncSequence: BufferSequence) -> Response where BufferSequence.Element == ByteBuffer { | ||
.init(status: .ok, headers: [.contentType: "text/event-stream"], body: .init(asyncSequence: asyncSequence.cancelOnGracefulShutdown())) | ||
} | ||
|
||
static func stream<BufferSequence: AsyncSequence & Sendable>(_ asyncSequence: BufferSequence, eventName: String? = nil) -> Response where BufferSequence.Element: HTML { | ||
let eventStream = asyncSequence.map { html in | ||
ByteBuffer(bytes: "\(eventName != nil ? "event:\(eventName!)\n" : "")data:\(html.render())\n\n".utf8) | ||
} | ||
return .stream(eventStream) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,121 @@ | ||
!(function () { | ||
var e; | ||
function t(e) { | ||
return new EventSource(e, { withCredentials: !0 }); | ||
} | ||
function n(t) { | ||
if (!e.bodyContains(t)) { | ||
var n = e.getInternalData(t).sseEventSource; | ||
if (null != n) return n.close(), !0; | ||
} | ||
return !1; | ||
} | ||
function r(t, n) { | ||
var r = []; | ||
return ( | ||
e.hasAttribute(t, n) && r.push(t), | ||
t.querySelectorAll('[' + n + '], [data-' + n + ']').forEach(function (e) { | ||
r.push(e); | ||
}), | ||
r | ||
); | ||
} | ||
function s(t, n) { | ||
e.withExtensions(t, function (e) { | ||
n = e.transformResponse(n, null, t); | ||
}); | ||
var r = e.getSwapSpecification(t), | ||
s = e.getTarget(t); | ||
e.swap(s, n, r); | ||
} | ||
function a(t) { | ||
return null != e.getInternalData(t).sseEventSource; | ||
} | ||
htmx.defineExtension('sse', { | ||
init: function (n) { | ||
(e = n), null == htmx.createEventSource && (htmx.createEventSource = t); | ||
}, | ||
onEvent: function (t, o) { | ||
var i = o.target || o.detail.elt; | ||
switch (t) { | ||
case 'htmx:beforeCleanupElement': | ||
var u = e.getInternalData(i); | ||
return void (u.sseEventSource && u.sseEventSource.close()); | ||
case 'htmx:afterProcessNode': | ||
!(function t(o, i) { | ||
var u; | ||
if (null == o) return null; | ||
r(o, 'sse-connect').forEach(function (r) { | ||
var s, | ||
a, | ||
o, | ||
u, | ||
c = e.getAttributeValue(r, 'sse-connect'); | ||
null != c && | ||
((s = r), | ||
(a = c), | ||
(o = i), | ||
((u = htmx.createEventSource(a)).onerror = function (r) { | ||
if ( | ||
(e.triggerErrorEvent(s, 'htmx:sseError', { | ||
error: r, | ||
source: u, | ||
}), | ||
!n(s) && u.readyState === EventSource.CLOSED) | ||
) { | ||
var a = Math.random() * (2 ^ (o = o || 0)) * 500; | ||
window.setTimeout(function () { | ||
t(s, Math.min(7, o + 1)); | ||
}, a); | ||
} | ||
}), | ||
(u.onopen = function (t) { | ||
e.triggerEvent(s, 'htmx:sseOpen', { source: u }); | ||
}), | ||
(e.getInternalData(s).sseEventSource = u)); | ||
}), | ||
r((u = o), 'sse-swap').forEach(function (t) { | ||
var r = e.getClosestMatch(t, a); | ||
if (null == r) return null; | ||
for ( | ||
var o = e.getInternalData(r).sseEventSource, | ||
i = e.getAttributeValue(t, 'sse-swap').split(','), | ||
c = 0; | ||
c < i.length; | ||
c++ | ||
) { | ||
var l = i[c].trim(), | ||
v = function (a) { | ||
if (!n(r)) { | ||
if (!e.bodyContains(t)) | ||
return void o.removeEventListener(l, v); | ||
e.triggerEvent(u, 'htmx:sseBeforeMessage', a) && | ||
(s(t, a.data), | ||
e.triggerEvent(u, 'htmx:sseMessage', a)); | ||
} | ||
}; | ||
(e.getInternalData(t).sseEventListener = v), | ||
o.addEventListener(l, v); | ||
} | ||
}), | ||
r(u, 'hx-trigger').forEach(function (t) { | ||
var r = e.getClosestMatch(t, a); | ||
if (null == r) return null; | ||
var s = e.getInternalData(r).sseEventSource, | ||
o = e.getAttributeValue(t, 'hx-trigger'); | ||
if (null != o && 'sse:' == o.slice(0, 4)) { | ||
var i = function (a) { | ||
!n(r) && | ||
(e.bodyContains(t) || s.removeEventListener(o, i), | ||
htmx.trigger(t, o, a), | ||
htmx.trigger(t, 'htmx:sseMessage', a)); | ||
}; | ||
(e.getInternalData(u).sseEventListener = i), | ||
s.addEventListener(o.slice(4), i); | ||
} | ||
}); | ||
})(i); | ||
} | ||
}, | ||
}); | ||
})(); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.