59 lines
3.3 KiB
Markdown
59 lines
3.3 KiB
Markdown
# Provisioned pages use stable keys and render results carry safe response data
|
||
|
||
Provisioned pages were identified only by slug. Slugs are customer-facing
|
||
content and can change when an administrator renames a page, changes its
|
||
hierarchy, or mounts a plugin below a customer-specific route. A later plugin
|
||
load could therefore fail to find its page, recreate it, or attach children
|
||
and menu items to a stale location.
|
||
|
||
Decision: a page declaration may carry a plugin-scoped `Key`. `ParentKey`
|
||
references another declaration from the same plugin, while menu items use the
|
||
pair `PagePlugin` and `PageKey` so they can target a provisioned page across
|
||
plugin boundaries. The existing `ParentSlug` field remains an explicit legacy
|
||
input, but keyed references are the stable identity for new declarations.
|
||
|
||
`MountPath` is create-only. The host applies it while creating the page but
|
||
does not relocate an existing customer page during later reconciliation.
|
||
`MergeMissingBlockContent` enables a shallow default merge for matching
|
||
plugin-owned block assignments: only absent top-level keys are added, and
|
||
present values such as an empty string, zero, false, or null are preserved.
|
||
|
||
The render ABI exposes both `DerivedSlug` and `MountPath` on page and block
|
||
contexts. `DerivedSlug` is computed from the resolved hierarchy. `MountPath`
|
||
is the effective, normalized value after host inheritance, rather than the raw
|
||
stored declaration. Guests consume these values and do not reimplement host
|
||
routing rules.
|
||
|
||
Powered blocks and template documents may also request an HTTP status and a
|
||
shared `DocumentMetadata` value. Metadata is deliberately limited to title,
|
||
description, canonical URL, image URL, and robots directives. The host
|
||
validates status codes and metadata, escapes values, and accumulates status
|
||
before writing headers. Only 200–599 is accepted; the highest status class
|
||
wins, with the first result retained within a class. For metadata, the first
|
||
non-empty value for each field wins in deterministic render order, after which
|
||
page/document defaults fill gaps. Arbitrary meta/link maps were rejected
|
||
because they would create a second unvalidated head-injection path; advanced
|
||
trusted templates already have the explicit `HeadExtra` surface.
|
||
|
||
Appending fields to the v1 protobuf messages was chosen over renumbering or a
|
||
Go-only helper because `abi/proto/v1` is the language-neutral contract. Slug
|
||
fallbacks remain data in that contract; the SDK does not silently synthesize
|
||
keys from mutable slugs.
|
||
|
||
Consequences:
|
||
|
||
- Plugin updates can find the same provisioned page after customer route
|
||
changes without taking ownership of the customer's chosen slug.
|
||
- Parent and menu references survive page moves and renames.
|
||
- Changing a declaration's mount path does not unexpectedly move a live page.
|
||
- Plugins can add newly introduced block defaults without overwriting customer
|
||
configuration.
|
||
- Native and WASM render code receive the same host-resolved routing values.
|
||
- Wiki-like powered blocks can return correct not-found status and structured
|
||
metadata without rendering or injecting the document envelope themselves.
|
||
|
||
Keywords: PageConfig.Key, ParentKey, ParentSlug, MountPath,
|
||
MergeMissingBlockContent, MenuItemConfig.PagePlugin, PageKey, DerivedSlug,
|
||
PageSeed, RenderContext, PoweredBlockWithOptions, DocumentMetadata,
|
||
TemplateDocument, stable identity, page provisioning
|