Repository modes#
| mode | behaviour |
|---|---|
local |
cache only; miss → undefined |
remote |
always core; write-through into cache |
preferLocal (default) |
cache hit wins; miss → core (then cache) |
preferRemote |
core first; on network/error → cache fallback |
list() can't be answered from a per-id cache: every mode except local (which
returns []) queries core and writes each result through.
Reference fields#
Data documents don't embed referenced content — a schema kind:'reference'
field stores a pointer (DocReference / DocListReference /
CollectionReference / SiblingReference). resolveReferences(entity, repo, {locale}) deep-walks an entity and replaces each pointer with the fetched
content: document → the doc, list → the docs in stored order,
collection → the live query result, sibling → the ONE doc immediately
before/after the carrying doc in the value's order_by ({type:'sibling', direction:'prev'|'next'}; the carrying doc's identity comes from
ResolveReferencesOptions.self, reset per fetched doc — no self, a
collection mismatch, or the sequence's edge resolve to undefined; prev
needs an order_by, reversed manual order has no backend form).
A CollectionReference without an explicit order_by follows the collection's
manual admin order when one is set (setup/data reorder + order — applied
server-side by the core /public/content listing).
A CollectionReference whose query filters one field and whose order_by
(or a range) names another runs on a query key of the collection — keys
live on the collection, never on the page. Most listings need nothing: the
common shapes are built in (status + newest, status + a date field, the
first enum field + date / newest; products by status + price / name /
newest, and the category / tag pages on platform composites). Only a shape
beyond them needs a declared key on the setup/data row
(keys: {name: [fields]}); without it the core sorts up to 500 matching
documents in memory and above that answers
400 query_key_required {collection, suggest} — declare suggest as the key
(drop-core-sdk README, "Query keys").
It also returns the dependencies of the resolution (deps.keys — resolved
doc cache keys; deps.collections — locale/collection of collection AND
sibling references, since any write in the collection can change who the
sibling is), which WebSite uses to drop rendered pages when a hook push
arrives for a referenced doc or for any doc in a queried collection.
A document (or sibling) reference whose value carries render:true is
rendered through the target's OWN schema template (renderDoc, supplied by
WebSite) and the fragment lands on the resolved value as html. Such a
target is resolved like a root first, whatever depth the walk has left
(renderDepth): its element placements render and its own references
resolve one hop, so a component that is just {{{ cta.html }}} over a
library element embeds at the default depth 1 — before this, renderDoc
received the raw placement pointer and the component rendered empty. The hop
is cycle-guarded (a component embedding itself renders once, then falls back
to data), and renderDoc also receives the doc as fetched (stored) as the
parallel twin for datetime localization and edit-mode provenance.
The resolver also descends into blocks TextValue envelopes (kind:'editor'
content): each element block's ElementReference resolves through the
same renderElement path as a standalone placement — the rendered markup
lands on the block as html (what flatten's blocksToHtml emits in
place), its CSS/JS hoist as fragments, and its library doc registers as a
dependency, so editing a placed element re-renders the pages that carry it.
Link fields (kind:'link') resolve too, but to a URL rather than to
content. A link value is either a plain string used verbatim (#kontakt,
tel:…, https://…) or a LinkReference envelope {type:'link', collection, document?, hash?}; the resolver replaces the envelope with a
LinkUrlValue — a String hybrid whose primitive is the target's public URL
in the serve locale (href="{{cta.href}}") and whose .title is the
target's title ({{cta.href.title}}), toJSON() giving the plain URL for
DROP_DATA. The URL comes from core-lib's linkPathOf: the homepage doc →
/, else the target's seo.slug under its seo.scope and the collection
route (/{segment}/{slug} when routed, the flat /{slug} otherwise), else
/ref/{collection}/{document}; a missing target resolves to undefined
(guard with {{#if}} — a hidden button beats a dead link), a
document-less link points at a routed collection's own /{segment}.
Pass ResolveReferencesOptions.site ({dataSetup, index} — setup/data +
setup/web.index) so routes and the homepage rule apply; without it links
fall back to flat slugs. The named target joins deps.keys exactly like a
document reference, so a slug rename evicts the linking page; the target is
never walked or rendered. WebSite applies the same derivation to
setup/web.shell.menu items whose page is a document pointer (a tagged
link or the legacy {collection, document}; a missing label takes the
target's localized title; the targets join the shell deps), and
stringifyLinks(entity, repo, {locale, site}) — the same rule producing
plain URL strings, everything else untouched — runs on the documents the
load-more and variant routes hand to the browser.