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:

reasonWhat it meansThe fix
ViewDependsOnUnsentStateThe view, or its head, renders differently from the browser's ModelAdd the field the message names to state, or stop the view reading it
UndeclaredStartupinit returned Commands and the plan has no bootName them in boot
UncoveredAn active Surface reads or is activated by an unsent field, or a binding sends a Message no active Surface listsSend the field, name it local, or list the Message on the Surface
UnrestorablePartA part cannot restore its own captureA bug in that part
DuplicateStaticRegionTwo SSR.static regions share an idGive each its own id
BindingInStaticRegionA handler inside an SSR.static regionMove the element out, into a Surface
UndeclaredSurfacesThe page has bindings and the plan no surfacesName the Surfaces the page may activate
UnencodableBindingThe plan has no Message Schema, or a binding's Message does not encode through itMake the plan from the application; build the Message with its constructor
EagerStartRequiredA deferred plan would start a Subscription or Managed Resource lateDeclare it in deferrable, or start 'now'
VoidRootWithStylesstyles were given for a view whose root is void and holds nothingPut the styles in the template's head with SSR.generate, or return an element root
UngeneratablePathSSR.generate was given a path no file can be served atLeave 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:

ts
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.