feat(sse): Server-Sent Events directive — release v1.20.0#292
Merged
Conversation
NOJS-270 / NOJS-271
New directive `sse` enables declarative real-time data binding via the
browser-native EventSource API. Registered at priority 1 (same tier as
get/post), gated for if-conditional support.
Attribute API: sse (url), as (var name), sse-event (named event),
sse-insert (replace|append|prepend), sse-limit (array cap),
sse-credentials (withCredentials), into (store dual-write),
error (template on terminal close), then (per-message expression).
Reactive $sse state object ({connecting, open, error}) exposed on the
element context via a new switch case in context.js. Per-origin
connection tracking warns at the HTTP/1.1 6-connection limit. Reactive
URL interpolation follows the ancestor-watch pattern from http.js.
All mandatory safety rules enforced: disposal via _onDispose,
el.isConnected guards, watcher cleanup, dispose-before-innerHTML.
Bundle delta: +2,814 bytes (2.75 KB), within 3 KB budget.
NOJS-271: implement sse directive
…date sse-limit S1: Move _onDispose(_closeConnection) from inside _openConnection (where _currentEl may be null during reactive watcher callbacks) to the synchronous directive init path. The closure-based _closeConnection always closes whatever the current EventSource is, so a single registration covers all reconnect scenarios. N1: Add _log() debug output for SSE lifecycle events (connect, open, message, named-event, close, error, reconnect, URL change) consistent with the project's logging conventions. N2: Warn via _warn() when sse-limit is negative or non-numeric instead of silently ignoring the invalid value.
fix(sse): register disposal once during init, add debug logging, validate sse-limit
Add 4 real SSE streaming endpoints to test-server.js (/sse/messages, /sse/error, /sse/infinite, /sse/credentials) and 8 cross-browser Playwright specs covering live binding, append with limit, named events, $sse state transitions, error template on terminal close, store integration via into, disposal on if-toggle, and axe accessibility.
test(sse): add unit tests with MockEventSource (NOJS-272)
- New docs/md/sse.md with full attribute reference, connection state lifecycle, insert modes, authentication limitations, HTTP/1.1 connection limit guidance, and SSE vs polling comparison - README.md: add SSE to feature list and documentation table - docs/md/data-fetching.md: add cross-reference to SSE guide
Address QA review findings on PR #286: - HIGH: add /sse/stats endpoint (global + per-channel via ?chan=) tracking open SSE connections server-side; Test 7 now polls the count to 0 after disposal and asserts the stream restarts at Tick 1 on re-toggle, so a leaked EventSource (the #285 bug class) can no longer pass - MEDIUM: cover /sse/credentials with a fixture section + spec asserting cookie auth observable server-side (401 without cookie, 200 with) - MEDIUM: add Test 9 regression for the #285 path — interpolated sse URL, state change forces reconnect, dispose section, poll /sse/stats to 0 - LOW: Test 4 now asserts the connecting state deterministically via a server-side ?start=800 header delay before asserting open - LOW: /sse/error sends retry: 100 so terminal-close reconnect timing no longer depends on the browser default retry delay Also fix a Firefox flake: fixture sections get a fixed height so streaming content cannot shift the toggle buttons between Playwright's actionability check and the click dispatch. Channels use a per-page-load random id so parallel workers never share /sse/stats counters.
Add complete documentation for the sse directive: - New docs/templates/docs/sse.tpl with 13 sections covering all attributes, insert modes, named events, $sse state, reactive URLs, store integration, error handling, then callback, auth limitations, and connection limits - Sidebar navigation entry in docs/templates/docs/sidebar.tpl - i18n keys in all 5 locales (en, pt, es, fr, it): 47 keys in docs.sse section, 5 keys in cheatsheet.data section, 1 sidebar key in shell.json - LLM summary in docs/llms.txt and full section in docs/llms-full.txt with cheatsheet entries
- Fix invalid computed syntax: computed="name" expr="..." (F1)
- Fix bare hide attribute: hide="true" (F2)
- Remove false connection-sharing claim; connections are never coalesced (F3)
- Wire SSE into prev/next nav chain across data-fetching, sse, data-binding (F4)
- Add sse-limit to canonical append/prepend examples (F5)
- Add encodeURIComponent note to reactive URL section (F6)
- Correct overstated replace-mode warning: only non-zero limit warns (F7)
- Add sse-limit constraint details to attributes table (F8)
- Initialize store ticker to {} instead of null to avoid null-deref (F9)
test(e2e): add SSE directive specs and streaming endpoints
- N1: Replace localStorage.getItem() in computed expr (blocked by
_BLOCKED_WINDOW_PROPS) with <script>-seeded store pattern using
NoJS.config({ stores }) and {$store.auth.token} interpolation
- N2: Remove location.reload() retry buttons (location resolves to
_safeLocation with reload = _locationNoop); add note explaining
the evaluator constraint and app-level JS escape hatch
- N3: Add # prefix to error template IDs (error="#sseFailed",
error="#feedError") matching convention in all other docs
docs(sse): add SSE directive markdown documentation
…et rows
- Fix blocker: standalone t-html="key" -> t="key" t-html (sse.tpl:201)
- Fix reactiveUrlText: as variable reset only in append/prepend mode,
not in replace mode (5 locales + llms-full.txt)
- Document both _warn calls: append/prepend without sse-limit warns
about unbounded memory; sse-limit in replace mode warns no effect
(insertModesText + sseLimit in 5 locales + llms-full.txt)
- Fix errorHandlingText: template receives synthetic { message: "SSE
connection closed" }, not the browser error object (5 locales +
llms-full.txt)
- Fix connectionLimitText: connections are never shared/coalesced;
remove misleading named-events consolidation advice, recommend
HTTP/2, fewer elements, or conditional rendering (5 locales +
llms-full.txt)
- Wire 5 dead cheatsheet.data.sse* keys into cheatsheet.tpl after
retryDelay row
- Fix Data Binding Prev/Next footer in llms-full.txt (Previous now
points to SSE instead of Data Fetching)
- Fix header comment convention in sse.tpl (add "from sse.md" suffix)
docs(sse): HTML docs, i18n, and LLM docs for SSE directive
chore(release): bump version to 1.20.0
docs(changelog): add v1.20.0 entry
This file contains hidden or 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
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
New
ssedirective: declarative Server-Sent Events / EventSource support — the streaming counterpart toget. Full initiative (EPIC NOJS-270): implementation, unit + E2E suites, markdown/HTML/i18n (5 locales)/LLM docs, ecosystem sync, version bump to 1.20.0, CHANGELOG.Directive surface
sse="url"({expr} interpolation, reactive reconnect),as(default "data"),sse-event,sse-insert="replace|append|prepend",sse-limit,sse-credentials,into(store dual-write),error="#tplId"(terminal CLOSED only),then(per-message),$ssestatus context, per-origin connection warning.Task PRs merged into this branch (each QA-gated with /code-review)
Verification
grep -c "1.20.0" dist/iife/no.js= 2Release plan (after this PR)
Merge held satellite PRs (nojs-lsp#80+#81 — second resolves out/ by rebuild; nojs-elements#74; nojs-skill#69+#70), then tag v1.20.0 on all repos. CDN updates via jsDelivr from the tag; LSP publishes via GitHub Actions.