Site / foldkit-site
Common workflows
Navigation rendering. Derive the nav from the tree instead of keeping
parallel tables: sections in tree order, hrefs from Site.landing, titles
from Site.titleOf, key bindings from Site.shortcuts, and the current
section by comparing tags.
Route changes. On a URL change, resolve the route value once, then
Site.chainOf for the active chain and Site.historyOf for the history
step — or hand both branches to Site.routing (with pages for the routed
placements) and keep only the guard in update. What each route does with
the change beyond informing its page is still the application's update.
Who may be shown a page. A route carries no authorization: the server
guards what a page reads, whatever the route says. What a route may decide is
presentation, sending a visitor to sign in rather than drawing an empty
page. access on a node says who may be shown it and the nodes below it,
from what the application knows of the reader (its Me row), and where
anyone else goes (from access.test.ts):
const Projects = Site.node(pipe(literal('projects'), mapTo(AppRoute.Projects)), AppRoute.Projects)
.access<Me>({ allow: me => me.signedIn, otherwise: toSignIn })
.nodeSite.accessOf(site, route, who) decides it, root first: Allowed;
Pending while who is Option.none() (the reader's Me still loading),
for a loading state rather than a sign-in page flashed at someone signed in;
Denied with the URL to go to (otherwise gets the address asked for, so a
sign-in page can return to it). Read it with Access.match. The view draws
from it, update replaces the URL on Denied after a route change or when
Me arrives, and a server answers Denied with a 303 before rendering. An
otherwise whose target the same rule covers throws, at any address: a
sign-in page mounted under the rule it serves would otherwise send each
refused reader to a longer returnTo for ever. A rule given in a node's
options names the reader it reads (allow: (me: Me) => …); accessOf
cannot check that its who is that reader, so pass the one the rules read.
Prefetch and SSR. Site.target plus Site.chainOf say which Surfaces a
destination will activate; Site.sourcesFor(site, owner, place, target.route)
is those as Sources — Data.satisfy over them, with the route set to the
target's, prepares the Model before a synchronous render. Preparation stays
caller-composed — there is no prepare in any plan.
Limits / when not to use it
- A single-screen application has one node; the tree buys nothing.
- Bundles with OutMessages, and children that are not Bundles, stay on
Bundle.atandLink.child:Site.placementtakes neitheronOutnor a non-Bundle child. - A node's
historyfunction sees only its own route values; cross-node rules more subtle than "another node is a step" do not exist yet. - Locales are route params like any other when they arrive; themes are preferences, not routes.