SSR / foldkit-ssr
When a page is refused
A refusal never shows a user a half-working page. On the server it is an error before anything is served; in the browser the page is contained, as Foldkit contains a page from another build, and the reason is logged.
On the server, SSR.render fails with ResumeUnsafe, whose reason is one
of:
reason | What it means | The fix |
|---|---|---|
ViewDependsOnUnsentState | The view, or its head, renders differently from the browser's Model | Add the field the message names to state, or stop the view reading it |
UndeclaredStartup | init returned Commands and the plan has no boot | Name them in boot |
Uncovered | An active Surface reads or is activated by an unsent field, or a binding sends a Message no active Surface lists | Send the field, name it local, or list the Message on the Surface |
UnrestorablePart | A part cannot restore its own capture | A bug in that part |
DuplicateStaticRegion | Two SSR.static regions share an id | Give each its own id |
BindingInStaticRegion | A handler inside an SSR.static region | Move the element out, into a Surface |
UndeclaredSurfaces | The page has bindings and the plan no surfaces | Name the Surfaces the page may activate |
UnencodableBinding | The plan has no Message Schema, or a binding's Message does not encode through it | Make the plan from the application; build the Message with its constructor |
EagerStartRequired | A deferred plan would start a Subscription or Managed Resource late | Declare it in deferrable, or start 'now' |
VoidRootWithStyles | styles were given for a view whose root is void and holds nothing | Put the styles in the template's head with SSR.generate, or return an element root |
UngeneratablePath | SSR.generate was given a path no file can be served at | Leave out the query and fragment; keep paths to distinct files |
In the browser, SSR.hydrate checks in Foldkit's order: the build id first,
then the envelope. SSR.resume reads the envelope off the stamped root and
returns its refusal as ResumeRefused: a page with no stamped root or more
than one, a root with no envelope (Missing, also for a second root as
Duplicate), Unreadable JSON, another Protocol version or Plan, state,
parts or bindings that are Invalid, or a Route other than the one the page
was rendered for. A page is resumed whole or not at all.
A page the browser does not take over
A page can be declined before anything is adopted: when(document) says
whether to take it over (a page rendered for a visitor, opened by an author),
and fresh(plan.version) whether its data is still current. A declined or
stale page is contained, as a refused one is, unless otherwise: 'render'
runs the application afresh in its place.
Drawn afresh at once, the page would show the fresh application's first draw,
usually its loading state, until its reads answer: the page blinks to "Loading"
and back. until(model) keeps the served page in view meanwhile. The fresh
application draws in a hidden element beside it, and when a Model it has drawn
satisfies until, it takes the page's place in one step, with the served
page's id. It never waits longer than UNTIL_LIMIT_MS (3 s). The CMS site
(examples/cms/src/ssr/sitePlan.ts) declines a page opened by an author, and
waits for the route's read and its Blocks' reads, which its Site.answered
checks with RemoteData.answered and QueryBlock.active(...).answered:
SSR.hydrate(config, plan, { buildId, when, otherwise: 'render', until: Site.answered })The served page stays as it was served while it waits: its links are plain links, and nothing it shows becomes live.