Head and history interfaces, and interceptors over every host import - #25
Merged
Conversation
Two more platform interfaces, both unconditional imports of `world app`: - `head`: `set-title` and `create-element`, the back end of dioxus's `document::Title`/`Meta`/`Link`/`Style`/`Script`. Until now those routed through `eval`, which apps do not have; now they are plain DOM writes the host performs. The default host refuses `script` unless the mount granted eval. - `history`: dioxus-history's `History` trait over abstract routes, with host-initiated moves delivered as a `stream<string>` from `changes()`. The host chooses the encoding (`MountOptions.history`: `"memory"` by default, `"fragment"` for a host that does not own the path); the guest never sees it, so dioxus-router works unchanged. And one mechanism over all of them: `MountOptions.intercept` lets the embedder wrap any operation of any host-implemented import table with `(next, ...args) => result` — observe, rewrite arguments, refuse, or replace. Policy stays out of the WIT, which remains one fixed protocol per capability. Rules in host/src/intercept.ts: typed per operation (a sync import cannot be given a Promise-returning interceptor), one denial spelling per return type (`false`, `undefined`, or `eval`'s new `denied` case, which the guest maps to `EvalError::Unsupported`), the dispatch-gate bracket sits outside the interceptor, throwing is a host bug, and interceptors never grant a capability the mount did not. The eval-probe fixture becomes platform-probe and also exercises head elements and a two-route router, including a host-driven navigation.
lannbot
enabled auto-merge
September 5, 2026 13:00
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.
Follow-up to #23/#24, covering the remaining platform gaps against dioxus-web and the customization hook polyvisor needs.
New WIT (
wit/world.wit), both unconditional imports:head:set-title,create-element(tag, attrs, contents) -> bool. Retires the "head elements need eval" gap. Default host refusesscriptunlessevalis granted.history: dioxus-history's trait over abstract routes;changes() -> stream<string>carries host-initiated moves (back button, visor navigation). Encoding is host-side:MountOptions.history: "memory" | "fragment". Hydration note in the doc: a hydrating host must answer the prerendered route first.eval.error.denied→ guest maps toEvalError::Unsupported(the graceful path apps already handle).Interceptors (
host/src/intercept.ts, normative header):MountOptions.intercept.{dom,eval,head,history}— per-operation(next, ...args) => result. Typed against each fragment's import table; denial has one spelling per return type; gate bracket is outside the interceptor (fragments wrap their unbracketed impls then bracket); throwing = host bug (trap);intercept.evalwithouteval: truethrows at mount.Example polyvisor policy:
Guest:
WitDocumentgainsset_title/create_head_element(queue_effect'd as dioxus-web does;dioxus-documentis now a plain dependency,evalstill feature-gated); newsrc/history.rsWitHistorywithupdaterreadingchanges. Both provided as root context.Tests: unit tests for
wrap, head, history (memory provider,changes, interceptor refusal);platform_component_test.tsdrives the real fixture through eval (incl. denial), head (incl. script refused via interceptor), and router (click + host-drivenforward()). 142 deno tests; fulljust check/fixtures/example/ssg-example/serve-test/testpass.Not covered here (documented gaps remaining): clipboard/file payloads, suspense in hydration.
Automerge armed (merge commit).