From 41d783abf0b4efbc71cbe7fa507569dc3659b9bc Mon Sep 17 00:00:00 2001 From: Madraka <8898640+Madraka@users.noreply.github.com> Date: Thu, 12 Nov 2026 12:42:17 +0300 Subject: [PATCH 1/2] feat(astro-renderer,core-ts): resolve reference and repeater fields (RFC-0045 step 3a) - entryFields: reference cards (ReferencedEntry) by pickMember among the members the view shows, depth 1 by default and 2 at most, breadth-first over a visited set of groups (one groupMembers call per level), within the request's 200-target budget (PlaceholderBudget.references, one console.warn); repeater items rendered like fields; unresolved targets leave no id, title or empty item - GraphSource.groupMembers / referencedBy / conversionEntries on both sources: the snapshot answers from gate-independent indexes filtered per view (no store reads); the live source reads entry_refs by (site_id, target_id) expanded by the shared rule and resolves each candidate like the snapshot index - core-ts: getGroupMembers and referrerCandidates (shared suite on SQLite, D1 and DO SQLite), the conversion-map builder, resolveFields' optional conversion context - EntryFields.astro: links for references, a nested dl per repeater item - site Worker features: { gates: 2, fields: 2 } - tests: live vs snapshot cards and referencedBy (editions, translations' own localized values), gate instants, depth, cycles and budget, the route sweep extension, and the frozen theme-v0.8.0 copy in old-theme-compat Co-Authored-By: Claude Opus 5.5 Signed-off-by: Madraka <8898640+Madraka@users.noreply.github.com> --- .changeset/rfc45-step3a-renderer.md | 16 + packages/astro-renderer/README.md | 14 + .../astro-renderer/src/cloudflare-entry.ts | 9 +- .../src/components/EntryFields.astro | 24 +- packages/astro-renderer/src/fields.ts | 263 ++++++++-- packages/astro-renderer/src/live-source.ts | 47 +- packages/astro-renderer/src/media.ts | 17 +- .../src/reference-conversions.ts | 71 +++ packages/astro-renderer/src/references.ts | 305 +++++++++++ packages/astro-renderer/src/source.ts | 64 ++- .../astro-renderer/test/references-fixture.ts | 219 ++++++++ .../test/references-sweep.test.ts | 165 ++++++ .../astro-renderer/test/references.test.ts | 278 ++++++++++ packages/astro-renderer/test/sweep.ts | 15 +- packages/cli/test/cloudflare-gates.test.ts | 7 +- .../cli/test/fixtures/theme-v0.8.0/README.md | 7 + .../test/fixtures/theme-v0.8.0/src/DESIGN.md | 42 ++ .../test/fixtures/theme-v0.8.0/src/blocks.ts | 45 ++ .../theme-v0.8.0/src/components/Archive.astro | 108 ++++ .../theme-v0.8.0/src/components/Article.astro | 110 ++++ .../src/components/Breadcrumbs.astro | 25 + .../src/components/EntryList.astro | 54 ++ .../src/components/LatestPosts.astro | 30 ++ .../src/components/MenuPanel.astro | 46 ++ .../theme-v0.8.0/src/components/NavLink.astro | 27 + .../theme-v0.8.0/src/components/NavMenu.astro | 95 ++++ .../src/components/NotFound.astro | 27 + .../src/components/PostTerms.astro | 33 ++ .../src/components/RelatedPosts.astro | 20 + .../theme-v0.8.0/src/components/Search.astro | 57 +++ .../src/components/SearchDialog.astro | 46 ++ .../src/components/blocks/Testimonial.astro | 18 + .../test/fixtures/theme-v0.8.0/src/env.d.ts | 5 + .../test/fixtures/theme-v0.8.0/src/fonts.ts | 31 ++ .../test/fixtures/theme-v0.8.0/src/icons.ts | 12 + .../theme-v0.8.0/src/layouts/Base.astro | 426 +++++++++++++++ .../fixtures/theme-v0.8.0/src/middleware.ts | 2 + .../theme-v0.8.0/src/pages/[...path].astro | 484 ++++++++++++++++++ .../src/pages/[...path]/llms.txt.ts | 41 ++ .../src/pages/[...path]/rss.xml.ts | 96 ++++ .../src/pages/[...path]/search.json.ts | 23 + .../theme-v0.8.0/src/pages/llms.txt.ts | 22 + .../theme-v0.8.0/src/pages/robots.txt.ts | 13 + .../theme-v0.8.0/src/pages/rss.xml.ts | 26 + .../theme-v0.8.0/src/pages/search.json.ts | 21 + .../theme-v0.8.0/src/pages/sitemap.xml.ts | 24 + .../fixtures/theme-v0.8.0/src/settings.ts | 312 +++++++++++ .../test/fixtures/theme-v0.8.0/src/strings.ts | 279 ++++++++++ .../src/styles/features/ambient.css | 185 +++++++ .../src/styles/features/article.css | 215 ++++++++ .../src/styles/features/blocks.css | 93 ++++ .../src/styles/features/carousel.css | 266 ++++++++++ .../src/styles/features/chrome-menus.css | 264 ++++++++++ .../src/styles/features/chrome.css | 193 +++++++ .../src/styles/features/comments.css | 75 +++ .../src/styles/features/icons.css | 70 +++ .../src/styles/features/languages.css | 34 ++ .../src/styles/features/live-surfaces.css | 238 +++++++++ .../src/styles/features/motion-effects.css | 217 ++++++++ .../src/styles/features/motion-scroll.css | 309 +++++++++++ .../src/styles/features/motion.css | 254 +++++++++ .../src/styles/features/overlays.css | 324 ++++++++++++ .../src/styles/features/prose.css | 140 +++++ .../src/styles/features/search-dialog.css | 144 ++++++ .../src/styles/features/search.css | 59 +++ .../src/styles/features/surface-media.css | 123 +++++ .../src/styles/features/surfaces.css | 310 +++++++++++ .../src/styles/features/tokens.css | 113 ++++ .../src/styles/font-fallbacks.css | 53 ++ .../theme-v0.8.0/src/styles/fonts.css | 113 ++++ .../theme-v0.8.0/src/styles/global.css | 31 ++ .../test/fixtures/theme-v0.8.0/src/theme.ts | 49 ++ packages/cli/test/old-theme-compat.test.ts | 79 ++- packages/core-ts/src/fields/conversions.ts | 6 + packages/core-ts/src/fields/index.ts | 7 + packages/core-ts/src/fields/resolve.ts | 11 +- packages/core-ts/src/index.ts | 5 + .../core-ts/src/references/conversion-maps.ts | 86 ++++ packages/core-ts/src/references/reads.ts | 97 ++++ .../core-ts/src/testing-reference-reads.ts | 155 ++++++ packages/core-ts/src/testing.ts | 2 + 81 files changed, 8354 insertions(+), 87 deletions(-) create mode 100644 .changeset/rfc45-step3a-renderer.md create mode 100644 packages/astro-renderer/src/reference-conversions.ts create mode 100644 packages/astro-renderer/src/references.ts create mode 100644 packages/astro-renderer/test/references-fixture.ts create mode 100644 packages/astro-renderer/test/references-sweep.test.ts create mode 100644 packages/astro-renderer/test/references.test.ts create mode 100644 packages/cli/test/fixtures/theme-v0.8.0/README.md create mode 100644 packages/cli/test/fixtures/theme-v0.8.0/src/DESIGN.md create mode 100644 packages/cli/test/fixtures/theme-v0.8.0/src/blocks.ts create mode 100644 packages/cli/test/fixtures/theme-v0.8.0/src/components/Archive.astro create mode 100644 packages/cli/test/fixtures/theme-v0.8.0/src/components/Article.astro create mode 100644 packages/cli/test/fixtures/theme-v0.8.0/src/components/Breadcrumbs.astro create mode 100644 packages/cli/test/fixtures/theme-v0.8.0/src/components/EntryList.astro create mode 100644 packages/cli/test/fixtures/theme-v0.8.0/src/components/LatestPosts.astro create mode 100644 packages/cli/test/fixtures/theme-v0.8.0/src/components/MenuPanel.astro create mode 100644 packages/cli/test/fixtures/theme-v0.8.0/src/components/NavLink.astro create mode 100644 packages/cli/test/fixtures/theme-v0.8.0/src/components/NavMenu.astro create mode 100644 packages/cli/test/fixtures/theme-v0.8.0/src/components/NotFound.astro create mode 100644 packages/cli/test/fixtures/theme-v0.8.0/src/components/PostTerms.astro create mode 100644 packages/cli/test/fixtures/theme-v0.8.0/src/components/RelatedPosts.astro create mode 100644 packages/cli/test/fixtures/theme-v0.8.0/src/components/Search.astro create mode 100644 packages/cli/test/fixtures/theme-v0.8.0/src/components/SearchDialog.astro create mode 100644 packages/cli/test/fixtures/theme-v0.8.0/src/components/blocks/Testimonial.astro create mode 100644 packages/cli/test/fixtures/theme-v0.8.0/src/env.d.ts create mode 100644 packages/cli/test/fixtures/theme-v0.8.0/src/fonts.ts create mode 100644 packages/cli/test/fixtures/theme-v0.8.0/src/icons.ts create mode 100644 packages/cli/test/fixtures/theme-v0.8.0/src/layouts/Base.astro create mode 100644 packages/cli/test/fixtures/theme-v0.8.0/src/middleware.ts create mode 100644 packages/cli/test/fixtures/theme-v0.8.0/src/pages/[...path].astro create mode 100644 packages/cli/test/fixtures/theme-v0.8.0/src/pages/[...path]/llms.txt.ts create mode 100644 packages/cli/test/fixtures/theme-v0.8.0/src/pages/[...path]/rss.xml.ts create mode 100644 packages/cli/test/fixtures/theme-v0.8.0/src/pages/[...path]/search.json.ts create mode 100644 packages/cli/test/fixtures/theme-v0.8.0/src/pages/llms.txt.ts create mode 100644 packages/cli/test/fixtures/theme-v0.8.0/src/pages/robots.txt.ts create mode 100644 packages/cli/test/fixtures/theme-v0.8.0/src/pages/rss.xml.ts create mode 100644 packages/cli/test/fixtures/theme-v0.8.0/src/pages/search.json.ts create mode 100644 packages/cli/test/fixtures/theme-v0.8.0/src/pages/sitemap.xml.ts create mode 100644 packages/cli/test/fixtures/theme-v0.8.0/src/settings.ts create mode 100644 packages/cli/test/fixtures/theme-v0.8.0/src/strings.ts create mode 100644 packages/cli/test/fixtures/theme-v0.8.0/src/styles/features/ambient.css create mode 100644 packages/cli/test/fixtures/theme-v0.8.0/src/styles/features/article.css create mode 100644 packages/cli/test/fixtures/theme-v0.8.0/src/styles/features/blocks.css create mode 100644 packages/cli/test/fixtures/theme-v0.8.0/src/styles/features/carousel.css create mode 100644 packages/cli/test/fixtures/theme-v0.8.0/src/styles/features/chrome-menus.css create mode 100644 packages/cli/test/fixtures/theme-v0.8.0/src/styles/features/chrome.css create mode 100644 packages/cli/test/fixtures/theme-v0.8.0/src/styles/features/comments.css create mode 100644 packages/cli/test/fixtures/theme-v0.8.0/src/styles/features/icons.css create mode 100644 packages/cli/test/fixtures/theme-v0.8.0/src/styles/features/languages.css create mode 100644 packages/cli/test/fixtures/theme-v0.8.0/src/styles/features/live-surfaces.css create mode 100644 packages/cli/test/fixtures/theme-v0.8.0/src/styles/features/motion-effects.css create mode 100644 packages/cli/test/fixtures/theme-v0.8.0/src/styles/features/motion-scroll.css create mode 100644 packages/cli/test/fixtures/theme-v0.8.0/src/styles/features/motion.css create mode 100644 packages/cli/test/fixtures/theme-v0.8.0/src/styles/features/overlays.css create mode 100644 packages/cli/test/fixtures/theme-v0.8.0/src/styles/features/prose.css create mode 100644 packages/cli/test/fixtures/theme-v0.8.0/src/styles/features/search-dialog.css create mode 100644 packages/cli/test/fixtures/theme-v0.8.0/src/styles/features/search.css create mode 100644 packages/cli/test/fixtures/theme-v0.8.0/src/styles/features/surface-media.css create mode 100644 packages/cli/test/fixtures/theme-v0.8.0/src/styles/features/surfaces.css create mode 100644 packages/cli/test/fixtures/theme-v0.8.0/src/styles/features/tokens.css create mode 100644 packages/cli/test/fixtures/theme-v0.8.0/src/styles/font-fallbacks.css create mode 100644 packages/cli/test/fixtures/theme-v0.8.0/src/styles/fonts.css create mode 100644 packages/cli/test/fixtures/theme-v0.8.0/src/styles/global.css create mode 100644 packages/cli/test/fixtures/theme-v0.8.0/src/theme.ts create mode 100644 packages/core-ts/src/references/conversion-maps.ts create mode 100644 packages/core-ts/src/references/reads.ts create mode 100644 packages/core-ts/src/testing-reference-reads.ts diff --git a/.changeset/rfc45-step3a-renderer.md b/.changeset/rfc45-step3a-renderer.md new file mode 100644 index 00000000..0fb2c28f --- /dev/null +++ b/.changeset/rfc45-step3a-renderer.md @@ -0,0 +1,16 @@ +--- +"@cloudhal/astro-renderer": minor +"@cloudhal/core-ts": patch +--- + +RFC-0045 rollout step 3a: the renderer resolves reference and repeater fields. Both types stay refused on every write path until rollout step 7, so nothing changes for existing sites. + +- **`@cloudhal/astro-renderer`.** + - `entryFields` resolves a reference to cards (`RenderedField.references`: `ReferencedEntry` with group id, id, collection, locale, slug, title, summary, URL, cover image and `fromFallbackLocale`), in stored order, by `pickMember`'s locale rule among the members the source's view shows, gates included. `depth: 2` adds each card's own fields, with their references as cards without fields. Resolution is breadth-first over a visited set of groups, one `groupMembers` call per level, and spends the request's 200 targets (`PlaceholderBudget.references`; `placeholderBudget()` includes it); past the budget the rest resolve to nothing, with one `console.warn` per request. + - A repeater renders its items' sub-fields (`RenderedField.items`), like fields; empty items are left out. + - An unresolved target leaves nothing behind: no id, no title, no empty item. `value` keeps only the resolved group ids. + - `GraphSource.groupMembers?` and `GraphSource.referencedBy?` on `SnapshotSource` (in memory: gate-independent indexes built once per snapshot, filtered per view) and `LiveSource` (one `entry_refs` read expanded by the `shared` rule, each candidate resolved like the snapshot's index). `referencedBy` lists entries whose resolved values name the group, newest first, at most 100. + - `GraphSource.conversionEntries?` builds the entry maps of a text ↔ reference conversion while one runs (rollout step 4 adds the conversions). + - `EntryFields.astro` draws references as links (`lang` on a fallback-locale card, a `ul.ch-references` for several) and repeaters as an `ol.ch-items` of nested `dl`s. Old theme copies get both with no change of their own. + - The site Worker's `GET /__cloudhal/features` answers `{ "gates": 2, "fields": 2 }`. +- **`@cloudhal/core-ts`.** `getGroupMembers` and `referrerCandidates` (the live renderer's reads, by `site_id`); `conversionEntries`, `conversionTargets`, `conversionCollections` and `foldText` in `./fields`; `resolveFields` takes an optional fourth argument with the conversion maps (`ConvertContext.entries`). diff --git a/packages/astro-renderer/README.md b/packages/astro-renderer/README.md index bc22f5c7..d8b572d8 100644 --- a/packages/astro-renderer/README.md +++ b/packages/astro-renderer/README.md @@ -69,6 +69,20 @@ Guide: [`docs/guides/structured-data.md`](../../docs/guides/structured-data.md). Derived gates: `unlist` keeps an entry's page (200), comments and menu links but leaves `entries()`, `allEntries()`, `entriesByFilter`/`entriesByTerm` (archives, related), feeds, the sitemap and search; `hide` is a 404 like an expired `availability`. Both sources apply them, the gate index caps the edge cache at every derived instant and at every value instant of date/datetime list fields (for `now` conditions), and previews ignore them with an "Ended on …" notice. The site Worker's `GET /__cloudhal/features` answers `{ "gates": 2 }`; writers refuse `expiry` until it does. +### References and repeaters (RFC-0045, rollout step 3) +Additive, and dormant until rollout step 7 lets sites define the two types. `entryFields` (and `source.entryFields`) resolve a `reference` to cards of the targets the page's *view* shows (gates applied at its instant; draft, archived, embargoed and hidden targets resolve to nothing and leave no id, title or empty item) and a `repeater` to its items. `EntryFields.astro` draws both, so old theme copies show them with no change of their own (`theme-v0.8.0` in the compat test). + +| Export | Signature | +|---|---| +| `RenderedField.references?` | `ReferencedEntry[]`: `{ groupId, id, collection, locale, slug, title, summary?, url, image?, fromFallbackLocale, fields? }` in stored order; the member in the page's locale, else the default locale's under `localization.fallback: default-with-notice` (flagged: set `lang`) | +| `RenderedField.items?` | `RenderedFields[]`: each repeater item's sub-fields, rendered like fields; empty items left out | +| `EntryFieldsOptions.depth?` | `1` (default): cards; `2`: each card's own `fields`, with *their* references as cards without fields | +| `GraphSource.groupMembers?` | `(groupIds) => Promise>`: the members this view shows, by group (snapshot: in memory; live: one store read per call) | +| `GraphSource.referencedBy?` | `(groupId, { collection?, field?, locale?, limit? }) => Promise`: entries this view lists whose resolved values name the group (a translation inheriting its original's value included), newest first, at most 100 | +| `PlaceholderBudget.references?` | the request's resolved-target allowance (200, in `placeholderBudget()`); past it targets resolve to nothing, with one `console.warn` | + +Resolution is breadth-first over a visited set of groups, one `groupMembers` call per level, so cycles cost nothing. A "used by" list drawn from `referencedBy` is purged with the target's page when a referrer appears, goes or changes (the purge planner reads snapshot bodies, rollout step 3b); drawing *those* entries' fields is outside that guarantee. The site Worker's `GET /__cloudhal/features` answers `{ "gates": 2, "fields": 2 }`. + ## Site analytics (RFC-0006) The renderer carries the site side of Cloudhal's cookie-less analytics (`@cloudhal/analytics`): diff --git a/packages/astro-renderer/src/cloudflare-entry.ts b/packages/astro-renderer/src/cloudflare-entry.ts index fb7a66bc..741c53fb 100644 --- a/packages/astro-renderer/src/cloudflare-entry.ts +++ b/packages/astro-renderer/src/cloudflare-entry.ts @@ -60,6 +60,7 @@ import { } from "@cloudhal/astro-renderer/runtime"; import { SqlCommentStore } from "@cloudhal/comments/sql"; import { GATES_FEATURE_VERSION } from "@cloudhal/core-ts/availability"; +import { FIELDS_FEATURE_VERSION } from "@cloudhal/core-ts/fields"; import { D1Driver } from "@cloudhal/storage/d1"; import { ANALYTICS_BEACON_PATH, @@ -277,7 +278,8 @@ function fromAdmin(request: Request, env: Env): boolean { /** * What this site Worker enforces (RFC-0008 §7.6–§7.7): the admin Worker asks before it lets anyone - * write time gates. Authenticated like the purge (it is not a public endpoint), never cached. + * write time gates, and (RFC-0045 §1, `fields: 2`) reference and repeater fields, which this + * renderer resolves. Authenticated like the purge (it is not a public endpoint), never cached. */ function features(request: Request, env: Env): Response { const headers = { "cache-control": "no-store", "cloudflare-cdn-cache-control": "no-store" }; @@ -288,7 +290,10 @@ function features(request: Request, env: Env): Response { }); if (!fromAdmin(request, env)) return Response.json({ error: "unauthorized" }, { status: 401, headers }); - return Response.json({ gates: GATES_FEATURE_VERSION }, { headers }); + return Response.json( + { gates: GATES_FEATURE_VERSION, fields: FIELDS_FEATURE_VERSION }, + { headers }, + ); } /** diff --git a/packages/astro-renderer/src/components/EntryFields.astro b/packages/astro-renderer/src/components/EntryFields.astro index fce66ffd..0535e932 100644 --- a/packages/astro-renderer/src/components/EntryFields.astro +++ b/packages/astro-renderer/src/components/EntryFields.astro @@ -5,10 +5,14 @@ * `source.entryFields(entry, { locale, media })` (or `entryFields(source, entry, …)`) returns. * * Values are escaped text, except rich text (the body's markdown pipeline: raw HTML escaped, links - * through `safeHref`) and images (`imageAttributes`, the asset's alt in the page's language). No - * styles of its own: themes style `.ch-fields`, `.ch-field[data-type]` and `[data-field=""]`. + * through `safeHref`) and images (`imageAttributes`, the asset's alt in the page's language). + * RFC-0045: a reference is a link to each target the page's view shows (`lang` on a card from the + * fallback locale; several in a list), a repeater a nested `dl` per item with something to show. + * Unresolved targets leave nothing: no id, no attribute, no empty link. No styles of its own: + * themes style `.ch-fields`, `.ch-field[data-type]`, `[data-field=""]`, `.ch-references` and + * `.ch-items`. */ -import type { RenderedFields } from "../fields.ts"; +import type { RenderedField, RenderedFields } from "../fields.ts"; interface Props { fields: RenderedFields; @@ -19,14 +23,24 @@ interface Props { } const { fields, label, class: className } = Astro.props; const shown = Object.values(fields).filter((f) => !f.empty); -const multiline = (f: (typeof shown)[number]) => f.def.type === "text" && f.def.multiline === true; +const multiline = (f: RenderedField) => f.def.type === "text" && f.def.multiline === true; +const langOf = (c: { fromFallbackLocale: boolean; locale: string }) => + c.fromFallbackLocale ? c.locale : undefined; --- {shown.length > 0 && (
{shown.map((f) => (
{f.label}
- {f.html !== undefined ? ( + {f.references !== undefined ? ( + f.references.length === 1 && !Array.isArray(f.value) ? ( +
{f.references[0]?.title}
+ ) : ( +
+ ) + ) : f.items !== undefined ? ( +
    {f.items.map((item) =>
  1. )}
+ ) : f.html !== undefined ? (
) : f.href !== undefined ? (
{f.text}
diff --git a/packages/astro-renderer/src/fields.ts b/packages/astro-renderer/src/fields.ts index 41d44dbb..477fdab9 100644 --- a/packages/astro-renderer/src/fields.ts +++ b/packages/astro-renderer/src/fields.ts @@ -8,12 +8,16 @@ * `asset:` references resolved), never stored HTML; * - URLs: through `safeHref`, dropped when unsafe; * - images: `` attributes from `imageAttributes`, with the asset's alt in the page's language; - * - everything else: `formatFieldValue` text, which themes print escaped. + * - everything else: `formatFieldValue` text, which themes print escaped; + * - RFC-0045: references as cards of the targets this view shows (`references`, depth 1 by + * default, 2 at most, within a per-request budget), repeaters as their items' sub-fields + * (`items`). An unresolved target leaves nothing behind: no id, no title, no empty item. * * Worker-safe; the same function serves live and snapshot sources. */ -import type { Asset, ContentEntry, Site } from "@cloudhal/core-contracts"; +import type { Asset, ContentEntry, FieldDefinition, Site } from "@cloudhal/core-contracts"; import { + type ConversionEntries, type FormatFieldOptions, fieldHelp, fieldLabel, @@ -21,10 +25,19 @@ import { hasFieldValue, type ResolvedField, type ResolvedFields, + referenceUses, resolveFields, } from "@cloudhal/core-ts/fields"; import { renderMarkdown } from "./markdown.ts"; -import { type ImageAttributes, imageAttributes, type MediaContext } from "./media.ts"; +import { + type ImageAttributes, + imageAttributes, + type MediaContext, + type PlaceholderBudget, + REFERENCE_BUDGET, +} from "./media.ts"; +import { conversionsFor } from "./reference-conversions.ts"; +import { type ReferencedEntry, referenceCards } from "./references.ts"; import { safeHref } from "./safe-urls.ts"; export { @@ -37,6 +50,7 @@ export { type ResolvedFields, resolveFields, } from "@cloudhal/core-ts/fields"; +export type { ReferencedEntry } from "./references.ts"; /** An image field's picture: `` attributes plus the asset it shows. */ export interface FieldImage extends ImageAttributes { @@ -49,7 +63,7 @@ export interface RenderedField extends ResolvedField { /** The label in the page's language (then the site's default, then any, then the key). */ label: string; help?: string; - /** Nothing to show: no value, an unreadable one, an unsafe URL or a missing image. */ + /** Nothing to show: no value, an unreadable one, an unsafe URL, a missing image or target. */ empty: boolean; /** The value as plain text in the page's language ("" when empty). Print it escaped. */ text: string; @@ -61,6 +75,10 @@ export interface RenderedField extends ResolvedField { image?: FieldImage; /** `date` (`YYYY-MM-DD`) and `datetime` (the UTC instant): for `