feat: add @taujs/html - framework-free HTML SSR and streaming renderer - #169
Merged
Merged
Conversation
New package: no component framework, no compiler, no templating or escaping helper - render()
returns raw { headContent, appHtml } written to the response verbatim, so the application owns
its own HTML safety. Streaming carries the same abort handling, pre-observed done, exactly-once
onError and terminal-guard discipline as the framework renderers; deferred route data is
delivered as data only (never projected into HTML), bounded by a mandatory finite
deferredTimeoutMs since service calls carry no automatic deadline of their own. The client reads
that envelope through the single onDataReady export in @taujs/html/client.
Modelled on @taujs/vue's streaming machinery and render-contract brand; htmlRenderer() from
@taujs/html/renderer supplies an empty environment-plugin pack (no compiler to own).
Diagnostic strings and comments only, no behavioural change. The required-renderer message and the other runtime strings that used the two-name idiom (reactRenderer()/vueRenderer()) now name all four first-party factories in the same order, following the addition of @taujs/html; comments that named specific factories now say "a renderer factory" generically. One new cell asserts the required-renderer message names every first-party factory.
Modelled on fixtures/playground-vue: one bootable app that exercises @taujs/html end to end against the workspace package. Four routes cover ssr, streaming, deferred-with-hydrate and deferred-without-hydrate; the entry-server escapes its own interpolations locally (@taujs/html provides no escaping helper) and sets a 500ms deferredTimeoutMs so the deadline is exercised within the test suite's own timeout. test/routes.test.ts boots the real built prod and dev servers as child processes and asserts over plain fetch; test/browser.test.ts drives the deferred route in a real Chromium (skips visibly when the pinned browser is absent).
New renderers/html.mdx page (raw-HTML callout first, before any example), listed beside the Solid page in the sidebar. The config reference's renderer row and the getting-started guide's renderer sentence now name htmlRenderer(). Root README gains the npm badge, the package table row and the repository-tree entries for @taujs/html and its fixture.
@taujs/html minor (first publication, 0.1.0); @taujs/server patch (renderer diagnostics name all four factories).
…act lists RendererContract.ts:9,99 named reactRenderer()/vueRenderer() specifically; the implementation contract's section 5 lists both as comment sites that should say "a renderer factory" generically, alongside the diagnostic-string sweep in Config.ts/OwnershipPrepass.ts/ManagedPlugins.ts. No behavioural change.
Contract section 4.3 names four race outcomes the terminal guards make mechanically true; none
of them existed as separate cells. Added, titled as the claims they assert:
(a) abort while the critical-data thunk (not render) is pending: onHead never called, nothing
written, done resolves.
(b) a shell-timer expiry while render is pending: onError exactly once, done rejects; a LATER
render resolution produces no write and no callback.
(c) abort during the deferred wait (a never-settling entry, abort after onHead lands on a
macrotask so it lands inside the Promise.race, not synchronously inside onShellReady): no
onAllReady, no bootstrap tag, end() never called, done resolves.
(d) onShellReady aborting the signal synchronously: no appHtml write, no bootstrap tag, no
end(), done resolves.
All four passed against the existing implementation unmodified - the terminal guards already
described in SSRRender.ts were correct; only the pinning cells were missing.
… after every write Three empty catch blocks around writable.write(appHtml), the bootstrap-tag write and writable.end() swallowed synchronous sink exceptions instead of letting them reach the run() task's outer catch and fail() - a throwing write silently lost content with done resolved clean, and a throwing end() left done pending forever with no 'finish' ever able to fire. Removed all three; a synchronous throw now goes through fail() exactly once, same as any other step failure. Also added `if (controller.isAborted) return;` immediately after the appHtml write and again after the bootstrap-tag write: a write that synchronously emits a fatal sink 'error' (handled by the writable guards, which already call fail()) was previously falling through to onAllReady and the bootstrap tag even though the controller had already claimed a fatal outcome. Three regression cells added to Streaming.test.ts, titled as the claims they assert.
The README's quick-start example interpolated meta.title and data.message with String(...) right under a warning that the application must escape - String() escapes nothing. Replaced it with the same three-line escaper fixtures/playground-html/src/client/entry-server.ts uses, applied to both interpolations, with a comment stating @taujs/html provides no escaper. Also added a one-line comment directly on the website's createRenderer example (html.mdx) making explicit that `escape` there is the application's own function, not a @taujs/html export.
Merged
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.
Adds @taujs/html, a fourth first-party renderer for apps with no component framework.
Changesets: @taujs/html minor (first publish, 0.1.0), @taujs/server patch.