@marktiderman/genesis-sites 0.2.0 → 0.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (39) hide show
  1. package/README.md +89 -5
  2. package/dist/assets/form-island.6a75e310e3.js +51 -0
  3. package/dist/assets/{islands.77d11f5697.js → islands.825682bd57.js} +1 -1
  4. package/dist/assets/sites-forms.732cc7e0c1.css +10 -0
  5. package/dist/assets/sites-forms.css +1 -1
  6. package/dist/assets/sites.716b4ac1a5.css +2 -0
  7. package/dist/assets/sites.css +1 -1
  8. package/dist/assets.generated-V3HPVNXQ.js +3 -0
  9. package/dist/assets.generated-V3HPVNXQ.js.map +1 -0
  10. package/dist/{chunk-IXMY4B56.js → chunk-FBQF2L7O.js} +90 -89
  11. package/dist/chunk-FBQF2L7O.js.map +1 -0
  12. package/dist/chunk-HCICSNBH.js +465 -0
  13. package/dist/chunk-HCICSNBH.js.map +1 -0
  14. package/dist/chunk-KPOX7D4S.js +250 -0
  15. package/dist/chunk-KPOX7D4S.js.map +1 -0
  16. package/dist/chunk-TL2ZKVML.js +18 -0
  17. package/dist/chunk-TL2ZKVML.js.map +1 -0
  18. package/dist/editor.d.ts +526 -0
  19. package/dist/editor.js +2480 -0
  20. package/dist/editor.js.map +1 -0
  21. package/dist/handler-3VGM5WKF.js +9 -0
  22. package/dist/handler-3VGM5WKF.js.map +1 -0
  23. package/dist/import.d.ts +2 -2
  24. package/dist/index.d.ts +11 -5
  25. package/dist/index.js +2 -249
  26. package/dist/index.js.map +1 -1
  27. package/dist/{registry-D_DCWRTc.d.ts → registry-HBn1X60x.d.ts} +1 -1
  28. package/dist/sections.d.ts +1 -1
  29. package/dist/{store-DkSycDhq.d.ts → store-BXiZeZzH.d.ts} +2 -2
  30. package/dist/supabase.d.ts +3 -3
  31. package/dist/supabase.js +2 -2
  32. package/dist/web.d.ts +5 -5
  33. package/dist/web.js +6 -476
  34. package/dist/web.js.map +1 -1
  35. package/package.json +18 -8
  36. package/dist/assets/form-island.c0f6fac859.js +0 -51
  37. package/dist/assets/sites-forms.4e6aa42497.css +0 -10
  38. package/dist/assets/sites.e1ffd2708b.css +0 -2
  39. package/dist/chunk-IXMY4B56.js.map +0 -1
@@ -0,0 +1,465 @@
1
+ import { formDrawMode } from './chunk-C375ZPU2.js';
2
+ import { themeCss, siteCacheTag, siteTheme, formCacheTag, migrateNode, brandDisplayName, migratePage, migrateSite } from './chunk-FBQF2L7O.js';
3
+ import { getSection, pageMeta, organizationJsonLd, websiteJsonLd, articleJsonLd, absoluteUrl, breadcrumbJsonLd, safeJsonForHtml, parseSnapshot, robotsTxt, sitemapXml, rssXml, matchRedirect, PostSlug, ReleaseSnapshot, postSummary, postDescription } from './chunk-ZGWGQGT2.js';
4
+ import { DEFAULT_RESERVED_PATHS, normalizeRequestPath, isReservedPath } from './chunk-3VKTBE7S.js';
5
+ import { ASSETS } from './chunk-TL2ZKVML.js';
6
+ import * as React from 'react';
7
+ import { createElement } from 'react';
8
+ import { renderToReadableStream } from 'react-dom/server';
9
+ import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
10
+
11
+ var googleFontHref = (font) => font.google ? `https://fonts.googleapis.com/css2?family=${font.google}&display=swap` : null;
12
+ var FORM_SLUG = /^[a-z0-9][a-z0-9-]{0,79}$/;
13
+ function collectFormSlugs(_snapshot, page) {
14
+ const slugs = /* @__PURE__ */ new Set();
15
+ for (const n of page?.doc.content ?? []) {
16
+ if (n.type === "form-embed" && !n.hidden && typeof n.props.form === "string" && FORM_SLUG.test(n.props.form)) slugs.add(n.props.form);
17
+ }
18
+ return [...slugs];
19
+ }
20
+ function renderNodes(nodes, base, h1, where) {
21
+ const out = [];
22
+ for (const raw of nodes) {
23
+ if (raw.hidden) continue;
24
+ if (raw.hideOn?.includes("mobile") && raw.hideOn.includes("desktop")) continue;
25
+ const { node, issues } = migrateNode(raw);
26
+ if (!node) {
27
+ for (const issue of issues) console.error(`[genesis-sites] ${base.pagePath}: ${issue.message}`);
28
+ continue;
29
+ }
30
+ const def = getSection(node.type);
31
+ if ((def.placement ?? "body") !== where) {
32
+ console.error(`[genesis-sites] ${base.pagePath}: section ${node.id} (${node.type}) belongs in the ${def.placement ?? "body"}, not the ${where}; left out.`);
33
+ continue;
34
+ }
35
+ const headingLevel = def.providesH1 && !h1.given ? 1 : 2;
36
+ if (headingLevel === 1) h1.given = true;
37
+ const ctx = { ...base, headingLevel, idPrefix: `gs-${node.id}` };
38
+ try {
39
+ const element = def.render({ props: node.props, variant: node.variant, node, ctx });
40
+ if (element === null || element === void 0 || element === false) {
41
+ if (headingLevel === 1) h1.given = false;
42
+ continue;
43
+ }
44
+ out.push({ key: node.id, type: node.type, element });
45
+ } catch (err) {
46
+ if (headingLevel === 1) h1.given = false;
47
+ console.error(`[genesis-sites] ${base.pagePath}: section ${node.id} (${node.type}) failed to render and was left out.`, err);
48
+ }
49
+ }
50
+ return out;
51
+ }
52
+ function hrefFor(basePath) {
53
+ const base = basePath.replace(/\/+$/, "");
54
+ return (href) => {
55
+ if (!href.startsWith("/") || href.startsWith("//")) return href;
56
+ if (!base) return href;
57
+ return href === "/" ? `${base}/` : `${base}${href}`;
58
+ };
59
+ }
60
+ function SiteRenderer({ snapshot, page, theme, origin, basePath = "", mode = "live", logo, forms = /* @__PURE__ */ new Map(), assets, fontHref = googleFontHref, posts }) {
61
+ const site = snapshot.site;
62
+ const preview = mode === "preview";
63
+ const path = page?.path ?? "/404";
64
+ const base = {
65
+ site: { name: site.name, lang: site.lang, nav: site.nav, logo },
66
+ pagePath: path,
67
+ href: hrefFor(basePath),
68
+ forms,
69
+ mode,
70
+ ...posts ? { posts } : {}
71
+ };
72
+ const chrome = !page?.doc.root.props.hideSiteChrome;
73
+ const h1 = { given: false };
74
+ const header = chrome ? renderNodes(site.header, base, { given: true }, "header") : [];
75
+ const body = page ? renderNodes(page.doc.content, base, h1, "body") : [];
76
+ const footer = chrome ? renderNodes(site.footer, base, { given: true }, "footer") : [];
77
+ const formModes = body.some((r) => r.type === "form-embed") ? [...forms.values()].map(formDrawMode) : [];
78
+ const hasForm = formModes.includes("light") || formModes.includes("island");
79
+ const hasFormBundle = formModes.includes("island");
80
+ const hasMenu = header.some((r) => r.type === "nav") && site.nav.length > 0;
81
+ const hasIsland = body.some((r) => getSection(r.type)?.islands === true);
82
+ const post = posts?.current ?? null;
83
+ const meta = page ? pageMeta({
84
+ site,
85
+ page,
86
+ origin,
87
+ basePath,
88
+ preview,
89
+ ...post ? { article: { publishedTime: post.publishedAt, modifiedTime: post.updatedAt, ...post.doc.author ? { author: post.doc.author.name } : {}, ...post.doc.category ? { section: post.doc.category } : {} } } : {}
90
+ }) : { title: `Page not found \xB7 ${site.name}`, canonical: "", robots: "noindex, nofollow", openGraph: {}, twitter: {} };
91
+ const jsonLd = [];
92
+ if (page && !preview) {
93
+ if (page.path === "/") jsonLd.push(organizationJsonLd(site, origin, basePath), websiteJsonLd(site, origin, basePath));
94
+ if (post) jsonLd.push(articleJsonLd(site, post, meta.canonical || absoluteUrl(origin, basePath, page.path), origin, basePath));
95
+ const crumbs = breadcrumbJsonLd(post ? [...snapshot.pages, page] : snapshot.pages, page, origin, basePath);
96
+ if (crumbs) jsonLd.push(crumbs);
97
+ }
98
+ const fontLinks = fontHref ? theme.fonts.map(fontHref).filter((h) => Boolean(h)) : [];
99
+ return /* @__PURE__ */ jsxs("html", { lang: site.lang, "data-gs-site": "", ...theme.colorScheme === "dark" ? { className: "dark" } : {}, children: [
100
+ /* @__PURE__ */ jsxs("head", { children: [
101
+ /* @__PURE__ */ jsx("meta", { charSet: "utf-8" }),
102
+ /* @__PURE__ */ jsx("meta", { name: "viewport", content: "width=device-width, initial-scale=1" }),
103
+ /* @__PURE__ */ jsx("title", { children: meta.title }),
104
+ meta.description ? /* @__PURE__ */ jsx("meta", { name: "description", content: meta.description }) : null,
105
+ /* @__PURE__ */ jsx("meta", { name: "robots", content: meta.robots }),
106
+ meta.canonical ? /* @__PURE__ */ jsx("link", { rel: "canonical", href: meta.canonical }) : null,
107
+ Object.entries(meta.openGraph).map(([property, content]) => /* @__PURE__ */ jsx("meta", { property, content }, property)),
108
+ Object.entries(meta.twitter).map(([name, content]) => /* @__PURE__ */ jsx("meta", { name, content }, name)),
109
+ fontLinks.length ? /* @__PURE__ */ jsxs(Fragment, { children: [
110
+ /* @__PURE__ */ jsx("link", { rel: "preconnect", href: "https://fonts.googleapis.com" }),
111
+ /* @__PURE__ */ jsx("link", { rel: "preconnect", href: "https://fonts.gstatic.com", crossOrigin: "anonymous" }),
112
+ fontLinks.map((href) => /* @__PURE__ */ jsx("link", { rel: "stylesheet", href }, href))
113
+ ] }) : null,
114
+ /* @__PURE__ */ jsx("link", { rel: "stylesheet", href: assets.css }),
115
+ hasFormBundle ? /* @__PURE__ */ jsx("link", { rel: "stylesheet", href: assets.formsCss }) : null,
116
+ /* @__PURE__ */ jsx("style", { "data-gs-theme": "", dangerouslySetInnerHTML: { __html: themeCss(theme) } }),
117
+ jsonLd.map((data, i) => /* @__PURE__ */ jsx("script", { type: "application/ld+json", dangerouslySetInnerHTML: { __html: safeJsonForHtml(data) } }, i)),
118
+ posts ? /* @__PURE__ */ jsx("link", { rel: "alternate", type: "application/rss+xml", title: site.name, href: base.href("/rss.xml") }) : null,
119
+ hasMenu || hasForm || hasIsland ? /* @__PURE__ */ jsx("script", { type: "module", src: assets.islands }) : null
120
+ ] }),
121
+ /* @__PURE__ */ jsxs("body", { className: "min-h-screen bg-background font-sans text-foreground antialiased", children: [
122
+ /* @__PURE__ */ jsx(
123
+ "a",
124
+ {
125
+ href: "#main",
126
+ className: "sr-only rounded-md bg-background px-4 py-3 font-medium text-foreground focus:not-sr-only focus:fixed focus:left-4 focus:top-4 focus:z-[100] focus:outline-2 focus:outline-ring",
127
+ children: "Skip to content"
128
+ }
129
+ ),
130
+ header.map((r) => /* @__PURE__ */ jsx(React.Fragment, { children: r.element }, r.key)),
131
+ /* @__PURE__ */ jsxs("main", { id: "main", tabIndex: -1, className: "outline-none", children: [
132
+ page && !h1.given ? /* @__PURE__ */ jsx("h1", { className: "sr-only", children: page.doc.root.props.title }) : null,
133
+ page ? body.map((r) => /* @__PURE__ */ jsx(React.Fragment, { children: r.element }, r.key)) : /* @__PURE__ */ jsxs("div", { className: "mx-auto flex max-w-3xl flex-col items-center gap-6 px-4 py-24 text-center", children: [
134
+ /* @__PURE__ */ jsx("h1", { className: "font-display text-4xl font-bold tracking-tight", children: "Page not found" }),
135
+ /* @__PURE__ */ jsx("p", { className: "text-lg text-muted-foreground", children: "There's no page at this address. It may have moved." }),
136
+ /* @__PURE__ */ jsx("a", { href: base.href("/"), className: "inline-flex min-h-11 items-center rounded-md bg-primary px-6 font-medium text-primary-foreground focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ring", children: "Go to the home page" })
137
+ ] })
138
+ ] }),
139
+ footer.map((r) => /* @__PURE__ */ jsx(React.Fragment, { children: r.element }, r.key))
140
+ ] })
141
+ ] });
142
+ }
143
+
144
+ // src/web/handler.ts
145
+ var ASSETS_PREFIX = "/_gs";
146
+ var LIVE_CACHE = "public, max-age=0, s-maxage=31536000, stale-while-revalidate=86400";
147
+ var MISS_CACHE = "public, max-age=0, s-maxage=300, stale-while-revalidate=86400";
148
+ var PREVIEW_CACHE = "private, no-store";
149
+ var ASSET_CACHE = "public, max-age=31536000, immutable";
150
+ var enc = new TextEncoder();
151
+ async function sha256Base64(text) {
152
+ const digest = await crypto.subtle.digest("SHA-256", enc.encode(text));
153
+ let binary = "";
154
+ for (const byte of new Uint8Array(digest)) binary += String.fromCharCode(byte);
155
+ return btoa(binary);
156
+ }
157
+ function originOf(url) {
158
+ try {
159
+ const u = new URL(url);
160
+ return u.protocol === "https:" ? u.origin : null;
161
+ } catch {
162
+ return null;
163
+ }
164
+ }
165
+ async function contentSecurityPolicy(styleText, forms, fonts, sectionFrames = []) {
166
+ const connect = /* @__PURE__ */ new Set(["'self'"]);
167
+ const formTargets = /* @__PURE__ */ new Set(["'self'"]);
168
+ const frames = /* @__PURE__ */ new Set();
169
+ for (const f of sectionFrames) {
170
+ const o = originOf(f);
171
+ if (o && o === f) frames.add(o);
172
+ }
173
+ for (const form of forms.values()) {
174
+ if (form?.kind === "definition") {
175
+ const o = originOf(form.submitUrl);
176
+ if (o) {
177
+ connect.add(o);
178
+ formTargets.add(o);
179
+ }
180
+ } else if (form?.kind === "embed") {
181
+ const o = originOf(form.url);
182
+ if (o) frames.add(o);
183
+ }
184
+ }
185
+ return [
186
+ "default-src 'self'",
187
+ "script-src 'self'",
188
+ `style-src 'self' 'sha256-${await sha256Base64(styleText)}'${fonts ? " https://fonts.googleapis.com" : ""}`,
189
+ // Inline style attributes: an image's focal point, a frame's height, the form's own.
190
+ "style-src-attr 'unsafe-inline'",
191
+ "img-src 'self' https: data:",
192
+ `font-src 'self'${fonts ? " https://fonts.gstatic.com" : ""}`,
193
+ `connect-src ${[...connect].join(" ")}`,
194
+ `frame-src ${frames.size ? [...frames].join(" ") : "'none'"}`,
195
+ `form-action ${[...formTargets].join(" ")}`,
196
+ "object-src 'none'",
197
+ "base-uri 'none'",
198
+ "frame-ancestors 'self'"
199
+ ].join("; ");
200
+ }
201
+ function textResponse(body, status, type, headers, head) {
202
+ return new Response(head ? null : body, { status, headers: { "content-type": type, "x-content-type-options": "nosniff", ...headers } });
203
+ }
204
+ async function draftSnapshot(store, siteId) {
205
+ if (!store.getDraft || !store.listPages || !store.getPageDraft) {
206
+ throw new Error("handleSiteRequest: preview needs a store with getDraft, listPages and getPageDraft.");
207
+ }
208
+ const draft = await store.getDraft(siteId);
209
+ if (!draft) return null;
210
+ const metas = await store.listPages(siteId);
211
+ const pages = [];
212
+ for (const meta of metas) {
213
+ const p = await store.getPageDraft(meta.id);
214
+ if (p) pages.push({ id: meta.id, path: p.path, kind: p.kind, doc: migratePage(p.doc).doc });
215
+ }
216
+ const parsed = ReleaseSnapshot.safeParse({ schemaVersion: 1, site: migrateSite(draft.site).site, pages });
217
+ if (!parsed.success) {
218
+ console.error(`[genesis-sites] the drafts of site ${siteId} don't form a valid site: ${parsed.error.message}`);
219
+ return null;
220
+ }
221
+ return parsed.data;
222
+ }
223
+ function listPage(url) {
224
+ const n = Number(url.searchParams.get("page") ?? "1");
225
+ return Number.isInteger(n) && n >= 1 && n <= 1e4 ? n : 1;
226
+ }
227
+ var PREVIEW_POSTS = 60;
228
+ async function blogPosts(store, siteId, preview) {
229
+ if (!preview) {
230
+ if (!store.listLivePosts) {
231
+ console.error(`[genesis-sites] site ${siteId} has a post page, but the store can't list posts (listLivePosts); the blog shows none.`);
232
+ return null;
233
+ }
234
+ try {
235
+ return await store.listLivePosts(siteId);
236
+ } catch (err) {
237
+ console.error(`[genesis-sites] reading the published posts of site ${siteId} failed; the blog shows none.`, err);
238
+ return null;
239
+ }
240
+ }
241
+ if (!store.listPosts || !store.getPost) {
242
+ console.error(`[genesis-sites] site ${siteId} has a post page, but the store can't read post drafts (listPosts, getPost) for the preview.`);
243
+ return null;
244
+ }
245
+ const out = [];
246
+ for (const meta of (await store.listPosts(siteId)).slice(0, PREVIEW_POSTS)) {
247
+ const draft = await store.getPost(meta.id);
248
+ if (draft) out.push(postSummary({ slug: draft.slug, publishedAt: draft.publishedAt ?? draft.updatedAt, updatedAt: draft.updatedAt, doc: draft.doc }));
249
+ }
250
+ return out;
251
+ }
252
+ async function blogPost(store, siteId, slug, preview) {
253
+ if (!preview) {
254
+ if (!store.getLivePost) return null;
255
+ try {
256
+ return await store.getLivePost(siteId, slug);
257
+ } catch (err) {
258
+ console.error(`[genesis-sites] reading the published post ${JSON.stringify(slug)} of site ${siteId} failed; answering not found.`, err);
259
+ return null;
260
+ }
261
+ }
262
+ if (!store.listPosts || !store.getPost) return null;
263
+ const meta = (await store.listPosts(siteId)).find((p) => p.slug === slug);
264
+ const draft = meta ? await store.getPost(meta.id) : null;
265
+ return draft ? { slug: draft.slug, publishedAt: draft.publishedAt ?? draft.updatedAt, updatedAt: draft.updatedAt, doc: draft.doc } : null;
266
+ }
267
+ function postAsPage(template, post, path) {
268
+ const d = post.doc;
269
+ const description = (d.seo?.description ?? postDescription(d))?.slice(0, 200);
270
+ const ogImage = d.seo?.ogImage ?? d.cover;
271
+ const seo = {
272
+ ...d.seo?.title ? { title: d.seo.title } : {},
273
+ ...description ? { description } : {},
274
+ ...ogImage ? { ogImage } : {},
275
+ ...d.seo?.canonical ? { canonical: d.seo.canonical } : {},
276
+ ...d.seo?.noindex || template.doc.seo?.noindex ? { noindex: true } : {}
277
+ };
278
+ return { ...template, path, doc: { ...template.doc, root: { props: { ...template.doc.root.props, title: d.title.slice(0, 120) } }, seo } };
279
+ }
280
+ var headerSafe = (value) => value.replace(/[^\x21-\x7e]/gu, (c) => encodeURIComponent(c));
281
+ function redirectLocation(to, basePath) {
282
+ if (to.startsWith("#")) return headerSafe(`${basePath}/${to}`);
283
+ if (to.startsWith("/")) return headerSafe(to === "/" ? `${basePath}/` : `${basePath}${to}`);
284
+ try {
285
+ return new URL(to).href;
286
+ } catch {
287
+ return headerSafe(`${basePath}/`);
288
+ }
289
+ }
290
+ async function handleSiteRequest(request, options) {
291
+ const method = request.method.toUpperCase();
292
+ if (method !== "GET" && method !== "HEAD") return null;
293
+ const head = method === "HEAD";
294
+ const url = new URL(request.url);
295
+ const basePath = (options.basePath ?? "").replace(/\/+$/, "");
296
+ const reserved = options.reserved ?? DEFAULT_RESERVED_PATHS;
297
+ const mode = options.mode ?? "live";
298
+ const preview = mode === "preview";
299
+ if (basePath && url.pathname !== basePath && !url.pathname.startsWith(`${basePath}/`)) return null;
300
+ const rawPath = url.pathname.slice(basePath.length) || "/";
301
+ if (rawPath.startsWith(`${ASSETS_PREFIX}/`)) {
302
+ const name = rawPath.slice(ASSETS_PREFIX.length + 1);
303
+ const asset = Object.values(ASSETS).find((a) => a.name === name);
304
+ if (!asset) return textResponse("Not found\n", 404, "text/plain; charset=utf-8", { "cache-control": "no-store" }, head);
305
+ return textResponse(asset.body, 200, asset.type, { "cache-control": ASSET_CACHE, "access-control-allow-origin": "*" }, head);
306
+ }
307
+ const path = normalizeRequestPath(rawPath);
308
+ if (path === null) return null;
309
+ if (isReservedPath(path, reserved)) return null;
310
+ if (preview && !options.viewer) return null;
311
+ let snapshot;
312
+ let release = null;
313
+ if (preview) {
314
+ snapshot = await draftSnapshot(options.store, options.siteId);
315
+ } else {
316
+ release = await options.store.getLive(options.siteId);
317
+ const id = release?.id;
318
+ snapshot = release ? parseSnapshot(release.snapshot, (m) => console.error(`[genesis-sites] release ${id} of site ${options.siteId}: ${m}`)) : null;
319
+ }
320
+ if (!snapshot) return null;
321
+ const origin = (options.origin ?? url.origin).replace(/\/+$/, "");
322
+ const tags = [siteCacheTag(options.siteId)];
323
+ const cacheHeaders = (status2, cache = LIVE_CACHE) => {
324
+ if (preview) return { "cache-control": PREVIEW_CACHE, "x-robots-tag": "noindex, nofollow" };
325
+ options.onPublishedTags?.(tags, { siteId: options.siteId, releaseId: release?.id ?? null, path, status: status2 });
326
+ return {
327
+ "cache-control": cache,
328
+ "vercel-cache-tag": tags.join(","),
329
+ ...release ? { "x-genesis-release": String(release.number) } : {}
330
+ };
331
+ };
332
+ if (path === "/robots.txt") {
333
+ const disallowAll = preview || snapshot.site.seo.noindex === true;
334
+ return textResponse(robotsTxt({ origin, basePath, disallowAll }), 200, "text/plain; charset=utf-8", cacheHeaders(200), head);
335
+ }
336
+ const postTemplate = snapshot.pages.find((p) => p.kind === "post") ?? null;
337
+ if (path === "/sitemap.xml") {
338
+ if (preview) return textResponse("Not found\n", 404, "text/plain; charset=utf-8", cacheHeaders(404), head);
339
+ const items = postTemplate ? await blogPosts(options.store, options.siteId, false) ?? [] : [];
340
+ return textResponse(
341
+ sitemapXml({ site: snapshot.site, pages: snapshot.pages, origin, basePath, lastModified: release?.publishedAt ?? null, ...postTemplate ? { posts: { path: postTemplate.path, items } } : {} }),
342
+ 200,
343
+ "application/xml; charset=utf-8",
344
+ cacheHeaders(200),
345
+ head
346
+ );
347
+ }
348
+ if (path === "/rss.xml") {
349
+ if (!postTemplate) return null;
350
+ if (preview) return textResponse("Not found\n", 404, "text/plain; charset=utf-8", cacheHeaders(404), head);
351
+ const items = snapshot.site.seo.noindex || postTemplate.doc.seo?.noindex ? [] : await blogPosts(options.store, options.siteId, false) ?? [];
352
+ return textResponse(rssXml({ site: snapshot.site, posts: items, postsPath: postTemplate.path, title: postTemplate.doc.root.props.title === snapshot.site.name ? void 0 : `${postTemplate.doc.root.props.title} \xB7 ${snapshot.site.name}`, origin, basePath }), 200, "application/rss+xml; charset=utf-8", cacheHeaders(200), head);
353
+ }
354
+ const redirect = matchRedirect(snapshot.site.redirects, path);
355
+ if (redirect) {
356
+ const location = redirectLocation(redirect.to, basePath) + (redirect.to.startsWith("/") ? url.search : "");
357
+ return new Response(null, { status: redirect.status, headers: { location, ...cacheHeaders(redirect.status) } });
358
+ }
359
+ let page = snapshot.pages.find((p) => p.path === path) ?? null;
360
+ let currentPost = null;
361
+ if (!page && postTemplate && path.startsWith(`${postTemplate.path === "/" ? "" : postTemplate.path}/`)) {
362
+ const slug = path.slice(postTemplate.path.length + 1);
363
+ if (PostSlug.safeParse(slug).success) {
364
+ currentPost = await blogPost(options.store, options.siteId, slug, preview);
365
+ if (currentPost) page = postAsPage(postTemplate, currentPost, path);
366
+ }
367
+ }
368
+ const bareBase = Boolean(basePath) && url.pathname === basePath;
369
+ if (page && (bareBase || rawPath !== path && !(path === "/" && rawPath === "/"))) {
370
+ const location = `${headerSafe(`${basePath}${path === "/" ? "/" : path}`)}${url.search}`;
371
+ return new Response(null, { status: 308, headers: { location, ...cacheHeaders(308) } });
372
+ }
373
+ if (!page && (options.unknownPath ?? "fall-through") === "fall-through") return null;
374
+ const brand = await options.brand.getBrand(options.siteId);
375
+ if (!brand) {
376
+ console.error(`[genesis-sites] the BrandSource has no brand for site ${options.siteId}; rendering with Genesis's default theme.`);
377
+ }
378
+ const fallbackBrand = { name: snapshot.site.name, colors: { primary: "#4f46e5" } };
379
+ let theme;
380
+ try {
381
+ theme = siteTheme(brand ?? fallbackBrand, snapshot.site.theme);
382
+ } catch (err) {
383
+ console.error(`[genesis-sites] the brand for site ${options.siteId} can't make a theme (a colour that isn't #rrggbb?); rendering with Genesis's default theme.`, err);
384
+ theme = siteTheme(fallbackBrand, snapshot.site.theme);
385
+ }
386
+ const brandChanged = Boolean(release?.brandHash) && release.brandHash !== theme.brandHash;
387
+ if (brandChanged) {
388
+ console.warn(`[genesis-sites] site ${options.siteId}: the brand changed since release ${release.number} was checked; republish to check its colours. Caching this page for 5 minutes only.`);
389
+ }
390
+ const forms = /* @__PURE__ */ new Map();
391
+ const slugs = collectFormSlugs(snapshot, page);
392
+ if (slugs.length && !options.forms) console.error(`[genesis-sites] ${path} embeds forms (${slugs.join(", ")}) but handleSiteRequest has no forms resolver.`);
393
+ for (const slug of slugs) {
394
+ tags.push(formCacheTag(slug));
395
+ try {
396
+ forms.set(slug, options.forms ? await options.forms(slug, { siteId: options.siteId, pagePath: path, mode }) : null);
397
+ } catch (err) {
398
+ console.error(`[genesis-sites] resolving the form "${slug}" failed.`, err);
399
+ forms.set(slug, null);
400
+ }
401
+ }
402
+ let posts;
403
+ if (postTemplate && page) {
404
+ const lists = page.doc.content.some((n) => !n.hidden && n.type === "blog-list");
405
+ const list = lists ? await blogPosts(options.store, options.siteId, preview) : [];
406
+ posts = { path: postTemplate.path, list: list ?? [], current: currentPost, page: listPage(url) };
407
+ }
408
+ const sectionFrames = [];
409
+ for (const node of page?.doc.content ?? []) {
410
+ if (node.hidden) continue;
411
+ const def = getSection(node.type);
412
+ if (!def?.frameOrigins) continue;
413
+ const migrated = migrateNode(node).node;
414
+ if (migrated) {
415
+ try {
416
+ sectionFrames.push(...def.frameOrigins(migrated.props));
417
+ } catch (err) {
418
+ console.error(`[genesis-sites] section ${node.id} (${node.type}) couldn't say what it frames.`, err);
419
+ }
420
+ }
421
+ }
422
+ const logo = brand && "logo" in brand && brand.logo ? { src: brand.logo.src, alt: brand.logo.alt?.trim() || brandDisplayName(brand) } : void 0;
423
+ const assetUrl = (name) => `${basePath}${ASSETS_PREFIX}/${name}`;
424
+ const fontHref = options.fonts === false ? false : options.fonts ?? googleFontHref;
425
+ const element = createElement(SiteRenderer, {
426
+ snapshot,
427
+ page,
428
+ theme,
429
+ origin,
430
+ basePath,
431
+ mode,
432
+ ...logo ? { logo } : {},
433
+ forms,
434
+ assets: { css: assetUrl(ASSETS.css.name), formsCss: assetUrl(ASSETS.formsCss.name), islands: assetUrl(ASSETS.islands.name) },
435
+ fontHref,
436
+ ...posts ? { posts } : {}
437
+ });
438
+ let failed = null;
439
+ const stream = await renderToReadableStream(element, {
440
+ onError(err) {
441
+ failed = err;
442
+ console.error(`[genesis-sites] rendering ${path} of site ${options.siteId} failed.`, err);
443
+ }
444
+ });
445
+ await stream.allReady;
446
+ if (failed) return textResponse("This page couldn't be shown.\n", 500, "text/plain; charset=utf-8", { "cache-control": "no-store" }, head);
447
+ const status = page ? 200 : 404;
448
+ const headers = {
449
+ "content-type": "text/html; charset=utf-8",
450
+ "x-content-type-options": "nosniff",
451
+ "referrer-policy": "strict-origin-when-cross-origin",
452
+ ...cacheHeaders(status, page && !brandChanged ? LIVE_CACHE : MISS_CACHE)
453
+ };
454
+ if (options.csp !== false) headers["content-security-policy"] = await contentSecurityPolicy(themeCss(theme), forms, fontHref !== false && theme.fonts.some((f) => f.google), sectionFrames);
455
+ return new Response(head ? null : stream, { status, headers });
456
+ }
457
+ async function renderSitePageToString(request, options) {
458
+ const res = await handleSiteRequest(request, options);
459
+ if (!res) return null;
460
+ return { status: res.status, html: await res.text(), headers: res.headers };
461
+ }
462
+
463
+ export { ASSETS_PREFIX, SiteRenderer, collectFormSlugs, googleFontHref, handleSiteRequest, renderSitePageToString };
464
+ //# sourceMappingURL=chunk-HCICSNBH.js.map
465
+ //# sourceMappingURL=chunk-HCICSNBH.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/web/document.tsx","../src/web/handler.ts"],"names":["Fragment","status"],"mappings":";;;;;;;;;;AAmDO,IAAM,cAAA,GAAiB,CAAC,IAAA,KAC7B,IAAA,CAAK,SAAS,CAAA,yCAAA,EAA4C,IAAA,CAAK,MAAM,CAAA,aAAA,CAAA,GAAkB;AAEzF,IAAM,SAAA,GAAY,2BAAA;AAGX,SAAS,gBAAA,CAAiB,WAA4B,IAAA,EAAoC;AAC/F,EAAA,MAAM,KAAA,uBAAY,GAAA,EAAY;AAC9B,EAAA,KAAA,MAAW,CAAA,IAAK,IAAA,EAAM,GAAA,CAAI,OAAA,IAAW,EAAC,EAAG;AACvC,IAAA,IAAI,CAAA,CAAE,SAAS,YAAA,IAAgB,CAAC,EAAE,MAAA,IAAU,OAAO,EAAE,KAAA,CAAM,IAAA,KAAS,YAAY,SAAA,CAAU,IAAA,CAAK,EAAE,KAAA,CAAM,IAAI,GAAG,KAAA,CAAM,GAAA,CAAI,CAAA,CAAE,KAAA,CAAM,IAAI,CAAA;AAAA,EACtI;AACA,EAAA,OAAO,CAAC,GAAG,KAAK,CAAA;AAClB;AAQA,SAAS,WAAA,CAAY,KAAA,EAA+B,IAAA,EAAwD,EAAA,EAAwB,KAAA,EAAiD;AACnL,EAAA,MAAM,MAAkB,EAAC;AACzB,EAAA,KAAA,MAAW,OAAO,KAAA,EAAO;AACvB,IAAA,IAAI,IAAI,MAAA,EAAQ;AAChB,IAAA,IAAI,GAAA,CAAI,QAAQ,QAAA,CAAS,QAAQ,KAAK,GAAA,CAAI,MAAA,CAAO,QAAA,CAAS,SAAS,CAAA,EAAG;AACtE,IAAA,MAAM,EAAE,IAAA,EAAM,MAAA,EAAO,GAAI,YAAY,GAAG,CAAA;AACxC,IAAA,IAAI,CAAC,IAAA,EAAM;AACT,MAAA,KAAA,MAAW,KAAA,IAAS,MAAA,EAAQ,OAAA,CAAQ,KAAA,CAAM,CAAA,gBAAA,EAAmB,KAAK,QAAQ,CAAA,EAAA,EAAK,KAAA,CAAM,OAAO,CAAA,CAAE,CAAA;AAC9F,MAAA;AAAA,IACF;AACA,IAAA,MAAM,GAAA,GAAM,UAAA,CAAW,IAAA,CAAK,IAAI,CAAA;AAEhC,IAAA,IAAA,CAAK,GAAA,CAAI,SAAA,IAAa,MAAA,MAAY,KAAA,EAAO;AACvC,MAAA,OAAA,CAAQ,MAAM,CAAA,gBAAA,EAAmB,IAAA,CAAK,QAAQ,CAAA,UAAA,EAAa,KAAK,EAAE,CAAA,EAAA,EAAK,IAAA,CAAK,IAAI,oBAAoB,GAAA,CAAI,SAAA,IAAa,MAAM,CAAA,UAAA,EAAa,KAAK,CAAA,WAAA,CAAa,CAAA;AAC1J,MAAA;AAAA,IACF;AACA,IAAA,MAAM,eAAsB,GAAA,CAAI,UAAA,IAAc,CAAC,EAAA,CAAG,QAAQ,CAAA,GAAI,CAAA;AAC9D,IAAA,IAAI,YAAA,KAAiB,CAAA,EAAG,EAAA,CAAG,KAAA,GAAQ,IAAA;AACnC,IAAA,MAAM,GAAA,GAAqB,EAAE,GAAG,IAAA,EAAM,cAAc,QAAA,EAAU,CAAA,GAAA,EAAM,IAAA,CAAK,EAAE,CAAA,CAAA,EAAG;AAC9E,IAAA,IAAI;AACF,MAAA,MAAM,OAAA,GAAU,GAAA,CAAI,MAAA,CAAO,EAAE,KAAA,EAAO,IAAA,CAAK,KAAA,EAAO,OAAA,EAAS,IAAA,CAAK,OAAA,EAAS,IAAA,EAAM,GAAA,EAAK,CAAA;AAElF,MAAA,IAAI,OAAA,KAAY,IAAA,IAAQ,OAAA,KAAY,KAAA,CAAA,IAAa,YAAY,KAAA,EAAO;AAClE,QAAA,IAAI,YAAA,KAAiB,CAAA,EAAG,EAAA,CAAG,KAAA,GAAQ,KAAA;AACnC,QAAA;AAAA,MACF;AACA,MAAA,GAAA,CAAI,IAAA,CAAK,EAAE,GAAA,EAAK,IAAA,CAAK,IAAI,IAAA,EAAM,IAAA,CAAK,IAAA,EAAM,OAAA,EAAS,CAAA;AAAA,IACrD,SAAS,GAAA,EAAK;AACZ,MAAA,IAAI,YAAA,KAAiB,CAAA,EAAG,EAAA,CAAG,KAAA,GAAQ,KAAA;AACnC,MAAA,OAAA,CAAQ,KAAA,CAAM,CAAA,gBAAA,EAAmB,IAAA,CAAK,QAAQ,CAAA,UAAA,EAAa,IAAA,CAAK,EAAE,CAAA,EAAA,EAAK,IAAA,CAAK,IAAI,CAAA,oCAAA,CAAA,EAAwC,GAAG,CAAA;AAAA,IAC7H;AAAA,EACF;AACA,EAAA,OAAO,GAAA;AACT;AAEA,SAAS,QAAQ,QAAA,EAAkB;AACjC,EAAA,MAAM,IAAA,GAAO,QAAA,CAAS,OAAA,CAAQ,MAAA,EAAQ,EAAE,CAAA;AACxC,EAAA,OAAO,CAAC,IAAA,KAAiB;AACvB,IAAA,IAAI,CAAC,KAAK,UAAA,CAAW,GAAG,KAAK,IAAA,CAAK,UAAA,CAAW,IAAI,CAAA,EAAG,OAAO,IAAA;AAC3D,IAAA,IAAI,CAAC,MAAM,OAAO,IAAA;AAElB,IAAA,OAAO,IAAA,KAAS,MAAM,CAAA,EAAG,IAAI,MAAM,CAAA,EAAG,IAAI,GAAG,IAAI,CAAA,CAAA;AAAA,EACnD,CAAA;AACF;AAOO,SAAS,aAAa,EAAE,QAAA,EAAU,MAAM,KAAA,EAAO,MAAA,EAAQ,WAAW,EAAA,EAAI,IAAA,GAAO,QAAQ,IAAA,EAAM,KAAA,uBAAY,GAAA,EAAI,EAAG,QAAQ,QAAA,GAAW,cAAA,EAAgB,OAAM,EAAsB;AAClL,EAAA,MAAM,OAAO,QAAA,CAAS,IAAA;AACtB,EAAA,MAAM,UAAU,IAAA,KAAS,SAAA;AACzB,EAAA,MAAM,IAAA,GAAO,MAAM,IAAA,IAAQ,MAAA;AAC3B,EAAA,MAAM,IAAA,GAAyD;AAAA,IAC7D,IAAA,EAAM,EAAE,IAAA,EAAM,IAAA,CAAK,IAAA,EAAM,IAAA,EAAM,IAAA,CAAK,IAAA,EAAM,GAAA,EAAK,IAAA,CAAK,GAAA,EAAK,IAAA,EAAK;AAAA,IAC9D,QAAA,EAAU,IAAA;AAAA,IACV,IAAA,EAAM,QAAQ,QAAQ,CAAA;AAAA,IACtB,KAAA;AAAA,IACA,IAAA;AAAA,IACA,GAAI,KAAA,GAAQ,EAAE,KAAA,KAAU;AAAC,GAC3B;AACA,EAAA,MAAM,MAAA,GAAS,CAAC,IAAA,EAAM,GAAA,CAAI,KAAK,KAAA,CAAM,cAAA;AACrC,EAAA,MAAM,EAAA,GAAK,EAAE,KAAA,EAAO,KAAA,EAAM;AAC1B,EAAA,MAAM,MAAA,GAAS,MAAA,GAAS,WAAA,CAAY,IAAA,CAAK,MAAA,EAAQ,IAAA,EAAM,EAAE,KAAA,EAAO,IAAA,EAAK,EAAG,QAAQ,CAAA,GAAI,EAAC;AACrF,EAAA,MAAM,IAAA,GAAO,IAAA,GAAO,WAAA,CAAY,IAAA,CAAK,GAAA,CAAI,SAAS,IAAA,EAAM,EAAA,EAAI,MAAM,CAAA,GAAI,EAAC;AACvE,EAAA,MAAM,MAAA,GAAS,MAAA,GAAS,WAAA,CAAY,IAAA,CAAK,MAAA,EAAQ,IAAA,EAAM,EAAE,KAAA,EAAO,IAAA,EAAK,EAAG,QAAQ,CAAA,GAAI,EAAC;AAErF,EAAA,MAAM,YAAY,IAAA,CAAK,IAAA,CAAK,CAAC,CAAA,KAAM,CAAA,CAAE,SAAS,YAAY,CAAA,GAAI,CAAC,GAAG,MAAM,MAAA,EAAQ,EAAE,GAAA,CAAI,YAAY,IAAI,EAAC;AACvG,EAAA,MAAM,UAAU,SAAA,CAAU,QAAA,CAAS,OAAO,CAAA,IAAK,SAAA,CAAU,SAAS,QAAQ,CAAA;AAC1E,EAAA,MAAM,aAAA,GAAgB,SAAA,CAAU,QAAA,CAAS,QAAQ,CAAA;AACjD,EAAA,MAAM,OAAA,GAAU,MAAA,CAAO,IAAA,CAAK,CAAC,CAAA,KAAM,CAAA,CAAE,IAAA,KAAS,KAAK,CAAA,IAAK,IAAA,CAAK,GAAA,CAAI,MAAA,GAAS,CAAA;AAE1E,EAAA,MAAM,SAAA,GAAY,IAAA,CAAK,IAAA,CAAK,CAAC,CAAA,KAAM,WAAW,CAAA,CAAE,IAAI,CAAA,EAAG,OAAA,KAAY,IAAI,CAAA;AACvE,EAAA,MAAM,IAAA,GAAO,OAAO,OAAA,IAAW,IAAA;AAE/B,EAAA,MAAM,IAAA,GAAO,OACT,QAAA,CAAS;AAAA,IACP,IAAA;AAAA,IACA,IAAA;AAAA,IACA,MAAA;AAAA,IACA,QAAA;AAAA,IACA,OAAA;AAAA,IACA,GAAI,IAAA,GAAO,EAAE,OAAA,EAAS,EAAE,eAAe,IAAA,CAAK,WAAA,EAAa,YAAA,EAAc,IAAA,CAAK,WAAW,GAAI,IAAA,CAAK,IAAI,MAAA,GAAS,EAAE,QAAQ,IAAA,CAAK,GAAA,CAAI,MAAA,CAAO,IAAA,KAAS,EAAC,EAAI,GAAI,IAAA,CAAK,IAAI,QAAA,GAAW,EAAE,OAAA,EAAS,IAAA,CAAK,IAAI,QAAA,EAAS,GAAI,EAAC,EAAG,KAAM;AAAC,GAC1N,CAAA,GACD,EAAE,KAAA,EAAO,CAAA,oBAAA,EAAoB,KAAK,IAAI,CAAA,CAAA,EAAI,SAAA,EAAW,EAAA,EAAI,QAAQ,mBAAA,EAAqB,SAAA,EAAW,EAAC,EAAG,OAAA,EAAS,EAAC,EAA4B;AAC/I,EAAA,MAAM,SAAoC,EAAC;AAC3C,EAAA,IAAI,IAAA,IAAQ,CAAC,OAAA,EAAS;AACpB,IAAA,IAAI,IAAA,CAAK,IAAA,KAAS,GAAA,EAAK,MAAA,CAAO,KAAK,kBAAA,CAAmB,IAAA,EAAM,MAAA,EAAQ,QAAQ,CAAA,EAAG,aAAA,CAAc,IAAA,EAAM,MAAA,EAAQ,QAAQ,CAAC,CAAA;AACpH,IAAA,IAAI,MAAM,MAAA,CAAO,IAAA,CAAK,aAAA,CAAc,IAAA,EAAM,MAAM,IAAA,CAAK,SAAA,IAAa,WAAA,CAAY,MAAA,EAAQ,UAAU,IAAA,CAAK,IAAI,CAAA,EAAG,MAAA,EAAQ,QAAQ,CAAC,CAAA;AAE7H,IAAA,MAAM,MAAA,GAAS,gBAAA,CAAiB,IAAA,GAAO,CAAC,GAAG,QAAA,CAAS,KAAA,EAAO,IAAI,CAAA,GAAI,QAAA,CAAS,KAAA,EAAO,IAAA,EAAM,QAAQ,QAAQ,CAAA;AACzG,IAAA,IAAI,MAAA,EAAQ,MAAA,CAAO,IAAA,CAAK,MAAM,CAAA;AAAA,EAChC;AACA,EAAA,MAAM,SAAA,GAAY,QAAA,GAAW,KAAA,CAAM,KAAA,CAAM,IAAI,QAAQ,CAAA,CAAE,MAAA,CAAO,CAAC,CAAA,KAAmB,OAAA,CAAQ,CAAC,CAAC,IAAI,EAAC;AAEjG,EAAA,4BACG,MAAA,EAAA,EAAK,IAAA,EAAM,IAAA,CAAK,IAAA,EAAM,gBAAa,EAAA,EAAI,GAAI,KAAA,CAAM,WAAA,KAAgB,SAAS,EAAE,SAAA,EAAW,MAAA,EAAO,GAAI,EAAC,EAClG,QAAA,EAAA;AAAA,oBAAA,IAAA,CAAC,MAAA,EAAA,EACC,QAAA,EAAA;AAAA,sBAAA,GAAA,CAAC,MAAA,EAAA,EAAK,SAAQ,OAAA,EAAQ,CAAA;AAAA,sBACtB,GAAA,CAAC,MAAA,EAAA,EAAK,IAAA,EAAK,UAAA,EAAW,SAAQ,qCAAA,EAAsC,CAAA;AAAA,sBACpE,GAAA,CAAC,OAAA,EAAA,EAAO,QAAA,EAAA,IAAA,CAAK,KAAA,EAAM,CAAA;AAAA,MAClB,IAAA,CAAK,8BAAc,GAAA,CAAC,MAAA,EAAA,EAAK,MAAK,aAAA,EAAc,OAAA,EAAS,IAAA,CAAK,WAAA,EAAa,CAAA,GAAK,IAAA;AAAA,0BAC5E,MAAA,EAAA,EAAK,IAAA,EAAK,QAAA,EAAS,OAAA,EAAS,KAAK,MAAA,EAAQ,CAAA;AAAA,MACzC,IAAA,CAAK,4BAAY,GAAA,CAAC,MAAA,EAAA,EAAK,KAAI,WAAA,EAAY,IAAA,EAAM,IAAA,CAAK,SAAA,EAAW,CAAA,GAAK,IAAA;AAAA,MAClE,OAAO,OAAA,CAAQ,IAAA,CAAK,SAAS,CAAA,CAAE,IAAI,CAAC,CAAC,QAAA,EAAU,OAAO,sBACrD,GAAA,CAAC,MAAA,EAAA,EAAoB,QAAA,EAAoB,OAAA,EAAA,EAA9B,QAAgD,CAC5D,CAAA;AAAA,MACA,OAAO,OAAA,CAAQ,IAAA,CAAK,OAAO,CAAA,CAAE,IAAI,CAAC,CAAC,IAAA,EAAM,OAAO,sBAC/C,GAAA,CAAC,MAAA,EAAA,EAAgB,IAAA,EAAY,OAAA,EAAA,EAAlB,IAAoC,CAChD,CAAA;AAAA,MACA,SAAA,CAAU,MAAA,mBACT,IAAA,CAAAA,QAAAA,EAAA,EACE,QAAA,EAAA;AAAA,wBAAA,GAAA,CAAC,MAAA,EAAA,EAAK,GAAA,EAAI,YAAA,EAAa,IAAA,EAAK,8BAAA,EAA+B,CAAA;AAAA,4BAC1D,MAAA,EAAA,EAAK,GAAA,EAAI,cAAa,IAAA,EAAK,2BAAA,EAA4B,aAAY,WAAA,EAAY,CAAA;AAAA,QAC/E,SAAA,CAAU,GAAA,CAAI,CAAC,IAAA,qBACd,GAAA,CAAC,UAAgB,GAAA,EAAI,YAAA,EAAa,IAAA,EAAA,EAAvB,IAAmC,CAC/C;AAAA,OAAA,EACH,CAAA,GACE,IAAA;AAAA,0BACH,MAAA,EAAA,EAAK,GAAA,EAAI,YAAA,EAAa,IAAA,EAAM,OAAO,GAAA,EAAK,CAAA;AAAA,MACxC,aAAA,uBAAiB,MAAA,EAAA,EAAK,GAAA,EAAI,cAAa,IAAA,EAAM,MAAA,CAAO,UAAU,CAAA,GAAK,IAAA;AAAA,sBAEpE,GAAA,CAAC,OAAA,EAAA,EAAM,eAAA,EAAc,EAAA,EAAG,uBAAA,EAAyB,EAAE,MAAA,EAAQ,QAAA,CAAS,KAAK,CAAA,EAAE,EAAG,CAAA;AAAA,MAC7E,OAAO,GAAA,CAAI,CAAC,IAAA,EAAM,CAAA,yBAChB,QAAA,EAAA,EAAe,IAAA,EAAK,qBAAA,EAAsB,uBAAA,EAAyB,EAAE,MAAA,EAAQ,eAAA,CAAgB,IAAI,CAAA,EAAE,EAAA,EAAvF,CAA0F,CACxG,CAAA;AAAA,MACA,KAAA,mBAAQ,GAAA,CAAC,MAAA,EAAA,EAAK,GAAA,EAAI,aAAY,IAAA,EAAK,qBAAA,EAAsB,KAAA,EAAO,IAAA,CAAK,MAAM,IAAA,EAAM,IAAA,CAAK,IAAA,CAAK,UAAU,GAAG,CAAA,GAAK,IAAA;AAAA,MAC7G,OAAA,IAAW,OAAA,IAAW,SAAA,mBAAY,GAAA,CAAC,QAAA,EAAA,EAAO,MAAK,QAAA,EAAS,GAAA,EAAK,MAAA,CAAO,OAAA,EAAS,CAAA,GAAK;AAAA,KAAA,EACrF,CAAA;AAAA,oBACA,IAAA,CAAC,MAAA,EAAA,EAAK,SAAA,EAAU,kEAAA,EACd,QAAA,EAAA;AAAA,sBAAA,GAAA;AAAA,QAAC,GAAA;AAAA,QAAA;AAAA,UACC,IAAA,EAAK,OAAA;AAAA,UACL,SAAA,EAAU,gLAAA;AAAA,UACX,QAAA,EAAA;AAAA;AAAA,OAED;AAAA,MACC,MAAA,CAAO,GAAA,CAAI,CAAC,CAAA,qBACX,GAAA,CAAO,KAAA,CAAA,QAAA,EAAN,EAA4B,QAAA,EAAA,CAAA,CAAE,OAAA,EAAA,EAAV,CAAA,CAAE,GAAgB,CACxC,CAAA;AAAA,2BACA,MAAA,EAAA,EAAK,EAAA,EAAG,QAAO,QAAA,EAAU,EAAA,EAAI,WAAU,cAAA,EACrC,QAAA,EAAA;AAAA,QAAA,IAAA,IAAQ,CAAC,EAAA,CAAG,KAAA,mBAAQ,GAAA,CAAC,IAAA,EAAA,EAAG,SAAA,EAAU,SAAA,EAAW,QAAA,EAAA,IAAA,CAAK,GAAA,CAAI,IAAA,CAAK,KAAA,CAAM,KAAA,EAAM,CAAA,GAAQ,IAAA;AAAA,QAC/E,OACC,IAAA,CAAK,GAAA,CAAI,CAAC,CAAA,yBAAa,KAAA,CAAA,QAAA,EAAN,EAA4B,QAAA,EAAA,CAAA,CAAE,OAAA,EAAA,EAAV,EAAE,GAAgB,CAAiB,oBAExE,IAAA,CAAC,KAAA,EAAA,EAAI,WAAU,2EAAA,EACb,QAAA,EAAA;AAAA,0BAAA,GAAA,CAAC,IAAA,EAAA,EAAG,SAAA,EAAU,gDAAA,EAAiD,QAAA,EAAA,gBAAA,EAAc,CAAA;AAAA,0BAC7E,GAAA,CAAC,GAAA,EAAA,EAAE,SAAA,EAAU,+BAAA,EAAgC,QAAA,EAAA,qDAAA,EAAmD,CAAA;AAAA,0BAChG,GAAA,CAAC,OAAE,IAAA,EAAM,IAAA,CAAK,KAAK,GAAG,CAAA,EAAG,SAAA,EAAU,oLAAA,EAAqL,QAAA,EAAA,qBAAA,EAExN;AAAA,SAAA,EACF;AAAA,OAAA,EAEJ,CAAA;AAAA,MACC,MAAA,CAAO,GAAA,CAAI,CAAC,CAAA,qBACX,GAAA,CAAO,KAAA,CAAA,QAAA,EAAN,EAA4B,QAAA,EAAA,CAAA,CAAE,OAAA,EAAA,EAAV,CAAA,CAAE,GAAgB,CACxC;AAAA,KAAA,EACH;AAAA,GAAA,EACF,CAAA;AAEJ;;;ACvMO,IAAM,aAAA,GAAgB;AAiD7B,IAAM,UAAA,GAAa,oEAAA;AACnB,IAAM,UAAA,GAAa,+DAAA;AACnB,IAAM,aAAA,GAAgB,mBAAA;AACtB,IAAM,WAAA,GAAc,qCAAA;AAEpB,IAAM,GAAA,GAAM,IAAI,WAAA,EAAY;AAE5B,eAAe,aAAa,IAAA,EAA+B;AACzD,EAAA,MAAM,MAAA,GAAS,MAAM,MAAA,CAAO,MAAA,CAAO,OAAO,SAAA,EAAW,GAAA,CAAI,MAAA,CAAO,IAAI,CAAC,CAAA;AACrE,EAAA,IAAI,MAAA,GAAS,EAAA;AACb,EAAA,KAAA,MAAW,IAAA,IAAQ,IAAI,UAAA,CAAW,MAAM,GAAG,MAAA,IAAU,MAAA,CAAO,aAAa,IAAI,CAAA;AAC7E,EAAA,OAAO,KAAK,MAAM,CAAA;AACpB;AAEA,SAAS,SAAS,GAAA,EAA4B;AAC5C,EAAA,IAAI;AACF,IAAA,MAAM,CAAA,GAAI,IAAI,GAAA,CAAI,GAAG,CAAA;AACrB,IAAA,OAAO,CAAA,CAAE,QAAA,KAAa,QAAA,GAAW,CAAA,CAAE,MAAA,GAAS,IAAA;AAAA,EAC9C,CAAA,CAAA,MAAQ;AACN,IAAA,OAAO,IAAA;AAAA,EACT;AACF;AAEA,eAAe,sBAAsB,SAAA,EAAmB,KAAA,EAAiD,KAAA,EAAgB,aAAA,GAAmC,EAAC,EAAoB;AAC/K,EAAA,MAAM,OAAA,mBAAU,IAAI,GAAA,CAAI,CAAC,QAAQ,CAAC,CAAA;AAElC,EAAA,MAAM,WAAA,mBAAc,IAAI,GAAA,CAAI,CAAC,QAAQ,CAAC,CAAA;AACtC,EAAA,MAAM,MAAA,uBAAa,GAAA,EAAY;AAE/B,EAAA,KAAA,MAAW,KAAK,aAAA,EAAe;AAC7B,IAAA,MAAM,CAAA,GAAI,SAAS,CAAC,CAAA;AACpB,IAAA,IAAI,CAAA,IAAK,CAAA,KAAM,CAAA,EAAG,MAAA,CAAO,IAAI,CAAC,CAAA;AAAA,EAChC;AACA,EAAA,KAAA,MAAW,IAAA,IAAQ,KAAA,CAAM,MAAA,EAAO,EAAG;AACjC,IAAA,IAAI,IAAA,EAAM,SAAS,YAAA,EAAc;AAC/B,MAAA,MAAM,CAAA,GAAI,QAAA,CAAS,IAAA,CAAK,SAAS,CAAA;AACjC,MAAA,IAAI,CAAA,EAAG;AACL,QAAA,OAAA,CAAQ,IAAI,CAAC,CAAA;AACb,QAAA,WAAA,CAAY,IAAI,CAAC,CAAA;AAAA,MACnB;AAAA,IACF,CAAA,MAAA,IAAW,IAAA,EAAM,IAAA,KAAS,OAAA,EAAS;AACjC,MAAA,MAAM,CAAA,GAAI,QAAA,CAAS,IAAA,CAAK,GAAG,CAAA;AAC3B,MAAA,IAAI,CAAA,EAAG,MAAA,CAAO,GAAA,CAAI,CAAC,CAAA;AAAA,IACrB;AAAA,EACF;AACA,EAAA,OAAO;AAAA,IACL,oBAAA;AAAA,IACA,mBAAA;AAAA,IACA,CAAA,yBAAA,EAA4B,MAAM,YAAA,CAAa,SAAS,CAAC,CAAA,CAAA,EAAI,KAAA,GAAQ,kCAAkC,EAAE,CAAA,CAAA;AAAA;AAAA,IAEzG,gCAAA;AAAA,IACA,6BAAA;AAAA,IACA,CAAA,eAAA,EAAkB,KAAA,GAAQ,4BAAA,GAA+B,EAAE,CAAA,CAAA;AAAA,IAC3D,eAAe,CAAC,GAAG,OAAO,CAAA,CAAE,IAAA,CAAK,GAAG,CAAC,CAAA,CAAA;AAAA,IACrC,CAAA,UAAA,EAAa,MAAA,CAAO,IAAA,GAAO,CAAC,GAAG,MAAM,CAAA,CAAE,IAAA,CAAK,GAAG,CAAA,GAAI,QAAQ,CAAA,CAAA;AAAA,IAC3D,eAAe,CAAC,GAAG,WAAW,CAAA,CAAE,IAAA,CAAK,GAAG,CAAC,CAAA,CAAA;AAAA,IACzC,mBAAA;AAAA,IACA,iBAAA;AAAA,IACA;AAAA,GACF,CAAE,KAAK,IAAI,CAAA;AACb;AAEA,SAAS,YAAA,CAAa,IAAA,EAAc,MAAA,EAAgB,IAAA,EAAc,SAAiC,IAAA,EAAyB;AAC1H,EAAA,OAAO,IAAI,QAAA,CAAS,IAAA,GAAO,IAAA,GAAO,IAAA,EAAM,EAAE,MAAA,EAAQ,OAAA,EAAS,EAAE,cAAA,EAAgB,MAAM,wBAAA,EAA0B,SAAA,EAAW,GAAG,OAAA,IAAW,CAAA;AACxI;AAGA,eAAe,aAAA,CAAc,OAAoC,MAAA,EAAiD;AAChH,EAAA,IAAI,CAAC,MAAM,QAAA,IAAY,CAAC,MAAM,SAAA,IAAa,CAAC,MAAM,YAAA,EAAc;AAC9D,IAAA,MAAM,IAAI,MAAM,qFAAqF,CAAA;AAAA,EACvG;AACA,EAAA,MAAM,KAAA,GAAQ,MAAM,KAAA,CAAM,QAAA,CAAS,MAAM,CAAA;AACzC,EAAA,IAAI,CAAC,OAAO,OAAO,IAAA;AACnB,EAAA,MAAM,KAAA,GAAQ,MAAM,KAAA,CAAM,SAAA,CAAU,MAAM,CAAA;AAC1C,EAAA,MAAM,QAAuB,EAAC;AAC9B,EAAA,KAAA,MAAW,QAAQ,KAAA,EAAO;AACxB,IAAA,MAAM,CAAA,GAAI,MAAM,KAAA,CAAM,YAAA,CAAa,KAAK,EAAE,CAAA;AAC1C,IAAA,IAAI,GAAG,KAAA,CAAM,IAAA,CAAK,EAAE,EAAA,EAAI,IAAA,CAAK,IAAI,IAAA,EAAM,CAAA,CAAE,MAAM,IAAA,EAAM,CAAA,CAAE,MAAM,GAAA,EAAK,WAAA,CAAY,EAAE,GAAG,CAAA,CAAE,KAAK,CAAA;AAAA,EAC5F;AACA,EAAA,MAAM,MAAA,GAAS,eAAA,CAAgB,SAAA,CAAU,EAAE,aAAA,EAAe,CAAA,EAAG,IAAA,EAAM,WAAA,CAAY,KAAA,CAAM,IAAI,CAAA,CAAE,IAAA,EAAM,OAAO,CAAA;AACxG,EAAA,IAAI,CAAC,OAAO,OAAA,EAAS;AACnB,IAAA,OAAA,CAAQ,MAAM,CAAA,mCAAA,EAAsC,MAAM,6BAA6B,MAAA,CAAO,KAAA,CAAM,OAAO,CAAA,CAAE,CAAA;AAC7G,IAAA,OAAO,IAAA;AAAA,EACT;AACA,EAAA,OAAO,MAAA,CAAO,IAAA;AAChB;AAGA,SAAS,SAAS,GAAA,EAAkB;AAClC,EAAA,MAAM,IAAI,MAAA,CAAO,GAAA,CAAI,aAAa,GAAA,CAAI,MAAM,KAAK,GAAG,CAAA;AACpD,EAAA,OAAO,MAAA,CAAO,UAAU,CAAC,CAAA,IAAK,KAAK,CAAA,IAAK,CAAA,IAAK,MAAQ,CAAA,GAAI,CAAA;AAC3D;AAEA,IAAM,aAAA,GAAgB,EAAA;AAGtB,eAAe,SAAA,CAAU,KAAA,EAAoC,MAAA,EAAgB,OAAA,EAAiD;AAC5H,EAAA,IAAI,CAAC,OAAA,EAAS;AACZ,IAAA,IAAI,CAAC,MAAM,aAAA,EAAe;AACxB,MAAA,OAAA,CAAQ,KAAA,CAAM,CAAA,qBAAA,EAAwB,MAAM,CAAA,sFAAA,CAAwF,CAAA;AACpI,MAAA,OAAO,IAAA;AAAA,IACT;AACA,IAAA,IAAI;AACF,MAAA,OAAO,MAAM,KAAA,CAAM,aAAA,CAAc,MAAM,CAAA;AAAA,IACzC,SAAS,GAAA,EAAK;AAEZ,MAAA,OAAA,CAAQ,KAAA,CAAM,CAAA,oDAAA,EAAuD,MAAM,CAAA,6BAAA,CAAA,EAAiC,GAAG,CAAA;AAC/G,MAAA,OAAO,IAAA;AAAA,IACT;AAAA,EACF;AACA,EAAA,IAAI,CAAC,KAAA,CAAM,SAAA,IAAa,CAAC,MAAM,OAAA,EAAS;AACtC,IAAA,OAAA,CAAQ,KAAA,CAAM,CAAA,qBAAA,EAAwB,MAAM,CAAA,4FAAA,CAA8F,CAAA;AAC1I,IAAA,OAAO,IAAA;AAAA,EACT;AACA,EAAA,MAAM,MAAqB,EAAC;AAC5B,EAAA,KAAA,MAAW,IAAA,IAAA,CAAS,MAAM,KAAA,CAAM,SAAA,CAAU,MAAM,CAAA,EAAG,KAAA,CAAM,CAAA,EAAG,aAAa,CAAA,EAAG;AAC1E,IAAA,MAAM,KAAA,GAAQ,MAAM,KAAA,CAAM,OAAA,CAAQ,KAAK,EAAE,CAAA;AACzC,IAAA,IAAI,KAAA,MAAW,IAAA,CAAK,WAAA,CAAY,EAAE,IAAA,EAAM,KAAA,CAAM,MAAM,WAAA,EAAa,KAAA,CAAM,eAAe,KAAA,CAAM,SAAA,EAAW,WAAW,KAAA,CAAM,SAAA,EAAW,KAAK,KAAA,CAAM,GAAA,EAAK,CAAC,CAAA;AAAA,EACtJ;AACA,EAAA,OAAO,GAAA;AACT;AAGA,eAAe,QAAA,CAAS,KAAA,EAAoC,MAAA,EAAgB,IAAA,EAAc,OAAA,EAA4C;AACpI,EAAA,IAAI,CAAC,OAAA,EAAS;AACZ,IAAA,IAAI,CAAC,KAAA,CAAM,WAAA,EAAa,OAAO,IAAA;AAC/B,IAAA,IAAI;AACF,MAAA,OAAO,MAAM,KAAA,CAAM,WAAA,CAAY,MAAA,EAAQ,IAAI,CAAA;AAAA,IAC7C,SAAS,GAAA,EAAK;AAEZ,MAAA,OAAA,CAAQ,KAAA,CAAM,8CAA8C,IAAA,CAAK,SAAA,CAAU,IAAI,CAAC,CAAA,SAAA,EAAY,MAAM,CAAA,6BAAA,CAAA,EAAiC,GAAG,CAAA;AACtI,MAAA,OAAO,IAAA;AAAA,IACT;AAAA,EACF;AACA,EAAA,IAAI,CAAC,KAAA,CAAM,SAAA,IAAa,CAAC,KAAA,CAAM,SAAS,OAAO,IAAA;AAC/C,EAAA,MAAM,IAAA,GAAA,CAAQ,MAAM,KAAA,CAAM,SAAA,CAAU,MAAM,CAAA,EAAG,IAAA,CAAK,CAAC,CAAA,KAAM,CAAA,CAAE,IAAA,KAAS,IAAI,CAAA;AACxE,EAAA,MAAM,QAAQ,IAAA,GAAO,MAAM,MAAM,OAAA,CAAQ,IAAA,CAAK,EAAE,CAAA,GAAI,IAAA;AACpD,EAAA,OAAO,QAAQ,EAAE,IAAA,EAAM,KAAA,CAAM,IAAA,EAAM,aAAa,KAAA,CAAM,WAAA,IAAe,KAAA,CAAM,SAAA,EAAW,WAAW,KAAA,CAAM,SAAA,EAAW,GAAA,EAAK,KAAA,CAAM,KAAI,GAAI,IAAA;AACvI;AAGA,SAAS,UAAA,CAAW,QAAA,EAAuB,IAAA,EAAgB,IAAA,EAA2B;AACpF,EAAA,MAAM,IAAI,IAAA,CAAK,GAAA;AACf,EAAA,MAAM,WAAA,GAAA,CAAe,EAAE,GAAA,EAAK,WAAA,IAAe,gBAAgB,CAAC,CAAA,GAAI,KAAA,CAAM,CAAA,EAAG,GAAG,CAAA;AAC5E,EAAA,MAAM,OAAA,GAAU,CAAA,CAAE,GAAA,EAAK,OAAA,IAAW,CAAA,CAAE,KAAA;AACpC,EAAA,MAAM,GAAA,GAAM;AAAA,IACV,GAAI,CAAA,CAAE,GAAA,EAAK,KAAA,GAAQ,EAAE,OAAO,CAAA,CAAE,GAAA,CAAI,KAAA,EAAM,GAAI,EAAC;AAAA,IAC7C,GAAI,WAAA,GAAc,EAAE,WAAA,KAAgB,EAAC;AAAA,IACrC,GAAI,OAAA,GAAU,EAAE,OAAA,KAAY,EAAC;AAAA,IAC7B,GAAI,CAAA,CAAE,GAAA,EAAK,SAAA,GAAY,EAAE,WAAW,CAAA,CAAE,GAAA,CAAI,SAAA,EAAU,GAAI,EAAC;AAAA,IACzD,GAAI,CAAA,CAAE,GAAA,EAAK,OAAA,IAAW,QAAA,CAAS,GAAA,CAAI,GAAA,EAAK,OAAA,GAAU,EAAE,OAAA,EAAS,IAAA,EAAK,GAAI;AAAC,GACzE;AACA,EAAA,OAAO,EAAE,GAAG,QAAA,EAAU,IAAA,EAAM,GAAA,EAAK,EAAE,GAAG,QAAA,CAAS,GAAA,EAAK,IAAA,EAAM,EAAE,KAAA,EAAO,EAAE,GAAG,QAAA,CAAS,GAAA,CAAI,IAAA,CAAK,KAAA,EAAO,KAAA,EAAO,CAAA,CAAE,KAAA,CAAM,KAAA,CAAM,CAAA,EAAG,GAAG,CAAA,EAAE,EAAE,EAAG,GAAA,EAAI,EAAE;AAC3I;AAGA,IAAM,UAAA,GAAa,CAAC,KAAA,KAAkB,KAAA,CAAM,OAAA,CAAQ,kBAAkB,CAAC,CAAA,KAAM,kBAAA,CAAmB,CAAC,CAAC,CAAA;AAGlG,SAAS,gBAAA,CAAiB,IAAY,QAAA,EAA0B;AAC9D,EAAA,IAAI,EAAA,CAAG,UAAA,CAAW,GAAG,CAAA,EAAG,OAAO,WAAW,CAAA,EAAG,QAAQ,CAAA,CAAA,EAAI,EAAE,CAAA,CAAE,CAAA;AAC7D,EAAA,IAAI,EAAA,CAAG,UAAA,CAAW,GAAG,CAAA,SAAU,UAAA,CAAW,EAAA,KAAO,GAAA,GAAM,CAAA,EAAG,QAAQ,CAAA,CAAA,CAAA,GAAM,CAAA,EAAG,QAAQ,CAAA,EAAG,EAAE,CAAA,CAAE,CAAA;AAC1F,EAAA,IAAI;AACF,IAAA,OAAO,IAAI,GAAA,CAAI,EAAE,CAAA,CAAE,IAAA;AAAA,EACrB,CAAA,CAAA,MAAQ;AACN,IAAA,OAAO,UAAA,CAAW,CAAA,EAAG,QAAQ,CAAA,CAAA,CAAG,CAAA;AAAA,EAClC;AACF;AAOA,eAAsB,iBAAA,CAAkB,SAAkB,OAAA,EAAuD;AAC/G,EAAA,MAAM,MAAA,GAAS,OAAA,CAAQ,MAAA,CAAO,WAAA,EAAY;AAC1C,EAAA,IAAI,MAAA,KAAW,KAAA,IAAS,MAAA,KAAW,MAAA,EAAQ,OAAO,IAAA;AAClD,EAAA,MAAM,OAAO,MAAA,KAAW,MAAA;AACxB,EAAA,MAAM,GAAA,GAAM,IAAI,GAAA,CAAI,OAAA,CAAQ,GAAG,CAAA;AAC/B,EAAA,MAAM,YAAY,OAAA,CAAQ,QAAA,IAAY,EAAA,EAAI,OAAA,CAAQ,QAAQ,EAAE,CAAA;AAC5D,EAAA,MAAM,QAAA,GAAW,QAAQ,QAAA,IAAY,sBAAA;AACrC,EAAA,MAAM,IAAA,GAAO,QAAQ,IAAA,IAAQ,MAAA;AAC7B,EAAA,MAAM,UAAU,IAAA,KAAS,SAAA;AAEzB,EAAA,IAAI,QAAA,IAAY,GAAA,CAAI,QAAA,KAAa,QAAA,IAAY,CAAC,GAAA,CAAI,QAAA,CAAS,UAAA,CAAW,CAAA,EAAG,QAAQ,CAAA,CAAA,CAAG,CAAA,EAAG,OAAO,IAAA;AAC9F,EAAA,MAAM,UAAU,GAAA,CAAI,QAAA,CAAS,KAAA,CAAM,QAAA,CAAS,MAAM,CAAA,IAAK,GAAA;AAGvD,EAAA,IAAI,OAAA,CAAQ,UAAA,CAAW,CAAA,EAAG,aAAa,GAAG,CAAA,EAAG;AAC3C,IAAA,MAAM,IAAA,GAAO,OAAA,CAAQ,KAAA,CAAM,aAAA,CAAc,SAAS,CAAC,CAAA;AACnD,IAAA,MAAM,KAAA,GAAQ,MAAA,CAAO,MAAA,CAAO,MAAM,CAAA,CAAE,KAAK,CAAC,CAAA,KAAM,CAAA,CAAE,IAAA,KAAS,IAAI,CAAA;AAC/D,IAAA,IAAI,CAAC,KAAA,EAAO,OAAO,YAAA,CAAa,aAAA,EAAe,GAAA,EAAK,2BAAA,EAA6B,EAAE,eAAA,EAAiB,UAAA,EAAW,EAAG,IAAI,CAAA;AACtH,IAAA,OAAO,YAAA,CAAa,KAAA,CAAM,IAAA,EAAM,GAAA,EAAK,KAAA,CAAM,IAAA,EAAM,EAAE,eAAA,EAAiB,WAAA,EAAa,6BAAA,EAA+B,GAAA,EAAI,EAAG,IAAI,CAAA;AAAA,EAC7H;AAEA,EAAA,MAAM,IAAA,GAAO,qBAAqB,OAAO,CAAA;AACzC,EAAA,IAAI,IAAA,KAAS,MAAM,OAAO,IAAA;AAC1B,EAAA,IAAI,cAAA,CAAe,IAAA,EAAM,QAAQ,CAAA,EAAG,OAAO,IAAA;AAG3C,EAAA,IAAI,OAAA,IAAW,CAAC,OAAA,CAAQ,MAAA,EAAQ,OAAO,IAAA;AAGvC,EAAA,IAAI,QAAA;AACJ,EAAA,IAAI,OAAA,GAA0B,IAAA;AAC9B,EAAA,IAAI,OAAA,EAAS;AACX,IAAA,QAAA,GAAW,MAAM,aAAA,CAAc,OAAA,CAAQ,KAAA,EAAO,QAAQ,MAAM,CAAA;AAAA,EAC9D,CAAA,MAAO;AACL,IAAA,OAAA,GAAU,MAAM,OAAA,CAAQ,KAAA,CAAM,OAAA,CAAQ,QAAQ,MAAM,CAAA;AACpD,IAAA,MAAM,KAAK,OAAA,EAAS,EAAA;AACpB,IAAA,QAAA,GAAW,UAAU,aAAA,CAAc,OAAA,CAAQ,QAAA,EAAU,CAAC,MAAM,OAAA,CAAQ,KAAA,CAAM,CAAA,wBAAA,EAA2B,EAAE,YAAY,OAAA,CAAQ,MAAM,KAAK,CAAC,CAAA,CAAE,CAAC,CAAA,GAAI,IAAA;AAAA,EAChJ;AAEA,EAAA,IAAI,CAAC,UAAU,OAAO,IAAA;AAEtB,EAAA,MAAM,UAAU,OAAA,CAAQ,MAAA,IAAU,IAAI,MAAA,EAAQ,OAAA,CAAQ,QAAQ,EAAE,CAAA;AAChE,EAAA,MAAM,IAAA,GAAO,CAAC,YAAA,CAAa,OAAA,CAAQ,MAAM,CAAC,CAAA;AAC1C,EAAA,MAAM,YAAA,GAAe,CAACC,OAAAA,EAAgB,KAAA,GAAQ,UAAA,KAAuC;AACnF,IAAA,IAAI,SAAS,OAAO,EAAE,eAAA,EAAiB,aAAA,EAAe,gBAAgB,mBAAA,EAAoB;AAC1F,IAAA,OAAA,CAAQ,eAAA,GAAkB,IAAA,EAAM,EAAE,MAAA,EAAQ,OAAA,CAAQ,MAAA,EAAQ,SAAA,EAAW,OAAA,EAAS,EAAA,IAAM,IAAA,EAAM,IAAA,EAAM,MAAA,EAAAA,SAAQ,CAAA;AACxG,IAAA,OAAO;AAAA,MACL,eAAA,EAAiB,KAAA;AAAA,MACjB,kBAAA,EAAoB,IAAA,CAAK,IAAA,CAAK,GAAG,CAAA;AAAA,MACjC,GAAI,UAAU,EAAE,mBAAA,EAAqB,OAAO,OAAA,CAAQ,MAAM,CAAA,EAAE,GAAI;AAAC,KACnE;AAAA,EACF,CAAA;AAEA,EAAA,IAAI,SAAS,aAAA,EAAe;AAC1B,IAAA,MAAM,WAAA,GAAc,OAAA,IAAW,QAAA,CAAS,IAAA,CAAK,IAAI,OAAA,KAAY,IAAA;AAC7D,IAAA,OAAO,YAAA,CAAa,SAAA,CAAU,EAAE,MAAA,EAAQ,QAAA,EAAU,WAAA,EAAa,CAAA,EAAG,GAAA,EAAK,2BAAA,EAA6B,YAAA,CAAa,GAAG,GAAG,IAAI,CAAA;AAAA,EAC7H;AAEA,EAAA,MAAM,YAAA,GAAe,SAAS,KAAA,CAAM,IAAA,CAAK,CAAC,CAAA,KAAM,CAAA,CAAE,IAAA,KAAS,MAAM,CAAA,IAAK,IAAA;AACtE,EAAA,IAAI,SAAS,cAAA,EAAgB;AAC3B,IAAA,IAAI,OAAA,SAAgB,YAAA,CAAa,aAAA,EAAe,KAAK,2BAAA,EAA6B,YAAA,CAAa,GAAG,CAAA,EAAG,IAAI,CAAA;AACzG,IAAA,MAAM,KAAA,GAAQ,YAAA,GAAiB,MAAM,SAAA,CAAU,OAAA,CAAQ,KAAA,EAAO,OAAA,CAAQ,MAAA,EAAQ,KAAK,CAAA,IAAM,EAAC,GAAK,EAAC;AAChG,IAAA,OAAO,YAAA;AAAA,MACL,UAAA,CAAW,EAAE,IAAA,EAAM,QAAA,CAAS,IAAA,EAAM,KAAA,EAAO,QAAA,CAAS,KAAA,EAAO,MAAA,EAAQ,QAAA,EAAU,YAAA,EAAc,OAAA,EAAS,WAAA,IAAe,IAAA,EAAM,GAAI,YAAA,GAAe,EAAE,KAAA,EAAO,EAAE,IAAA,EAAM,YAAA,CAAa,IAAA,EAAM,KAAA,EAAM,EAAE,GAAI,EAAC,EAAI,CAAA;AAAA,MAC/L,GAAA;AAAA,MACA,gCAAA;AAAA,MACA,aAAa,GAAG,CAAA;AAAA,MAChB;AAAA,KACF;AAAA,EACF;AACA,EAAA,IAAI,SAAS,UAAA,EAAY;AACvB,IAAA,IAAI,CAAC,cAAc,OAAO,IAAA;AAC1B,IAAA,IAAI,OAAA,SAAgB,YAAA,CAAa,aAAA,EAAe,KAAK,2BAAA,EAA6B,YAAA,CAAa,GAAG,CAAA,EAAG,IAAI,CAAA;AACzG,IAAA,MAAM,QAAQ,QAAA,CAAS,IAAA,CAAK,IAAI,OAAA,IAAW,YAAA,CAAa,IAAI,GAAA,EAAK,OAAA,GAAU,EAAC,GAAM,MAAM,UAAU,OAAA,CAAQ,KAAA,EAAO,QAAQ,MAAA,EAAQ,KAAK,KAAM,EAAC;AAC7I,IAAA,OAAO,aAAa,MAAA,CAAO,EAAE,MAAM,QAAA,CAAS,IAAA,EAAM,OAAO,KAAA,EAAO,SAAA,EAAW,YAAA,CAAa,IAAA,EAAM,OAAO,YAAA,CAAa,GAAA,CAAI,KAAK,KAAA,CAAM,KAAA,KAAU,SAAS,IAAA,CAAK,IAAA,GAAO,MAAA,GAAY,CAAA,EAAG,aAAa,GAAA,CAAI,IAAA,CAAK,MAAM,KAAK,CAAA,MAAA,EAAM,SAAS,IAAA,CAAK,IAAI,IAAI,MAAA,EAAQ,QAAA,EAAU,CAAA,EAAG,GAAA,EAAK,sCAAsC,YAAA,CAAa,GAAG,GAAG,IAAI,CAAA;AAAA,EACrU;AAGA,EAAA,MAAM,QAAA,GAAW,aAAA,CAAc,QAAA,CAAS,IAAA,CAAK,WAAW,IAAI,CAAA;AAC5D,EAAA,IAAI,QAAA,EAAU;AAEZ,IAAA,MAAM,QAAA,GAAW,gBAAA,CAAiB,QAAA,CAAS,EAAA,EAAI,QAAQ,CAAA,IAAK,QAAA,CAAS,EAAA,CAAG,UAAA,CAAW,GAAG,CAAA,GAAI,GAAA,CAAI,MAAA,GAAS,EAAA,CAAA;AACvG,IAAA,OAAO,IAAI,QAAA,CAAS,IAAA,EAAM,EAAE,MAAA,EAAQ,SAAS,MAAA,EAAQ,OAAA,EAAS,EAAE,QAAA,EAAU,GAAG,YAAA,CAAa,QAAA,CAAS,MAAM,CAAA,IAAK,CAAA;AAAA,EAChH;AACA,EAAA,IAAI,IAAA,GAAO,SAAS,KAAA,CAAM,IAAA,CAAK,CAAC,CAAA,KAAM,CAAA,CAAE,IAAA,KAAS,IAAI,CAAA,IAAK,IAAA;AAE1D,EAAA,IAAI,WAAA,GAA+B,IAAA;AACnC,EAAA,IAAI,CAAC,IAAA,IAAQ,YAAA,IAAgB,IAAA,CAAK,UAAA,CAAW,CAAA,EAAG,YAAA,CAAa,IAAA,KAAS,GAAA,GAAM,EAAA,GAAK,YAAA,CAAa,IAAI,GAAG,CAAA,EAAG;AACtG,IAAA,MAAM,OAAO,IAAA,CAAK,KAAA,CAAM,YAAA,CAAa,IAAA,CAAK,SAAS,CAAC,CAAA;AACpD,IAAA,IAAI,QAAA,CAAS,SAAA,CAAU,IAAI,CAAA,CAAE,OAAA,EAAS;AACpC,MAAA,WAAA,GAAc,MAAM,QAAA,CAAS,OAAA,CAAQ,OAAO,OAAA,CAAQ,MAAA,EAAQ,MAAM,OAAO,CAAA;AACzE,MAAA,IAAI,WAAA,EAAa,IAAA,GAAO,UAAA,CAAW,YAAA,EAAc,aAAa,IAAI,CAAA;AAAA,IACpE;AAAA,EACF;AAIA,EAAA,MAAM,QAAA,GAAW,OAAA,CAAQ,QAAQ,CAAA,IAAK,IAAI,QAAA,KAAa,QAAA;AACvD,EAAA,IAAI,IAAA,KAAS,YAAa,OAAA,KAAY,IAAA,IAAQ,EAAE,IAAA,KAAS,GAAA,IAAO,YAAY,GAAA,CAAA,CAAA,EAAQ;AAClF,IAAA,MAAM,QAAA,GAAW,CAAA,EAAG,UAAA,CAAW,CAAA,EAAG,QAAQ,CAAA,EAAG,IAAA,KAAS,GAAA,GAAM,GAAA,GAAM,IAAI,CAAA,CAAE,CAAC,CAAA,EAAG,IAAI,MAAM,CAAA,CAAA;AACtF,IAAA,OAAO,IAAI,QAAA,CAAS,IAAA,EAAM,EAAE,QAAQ,GAAA,EAAK,OAAA,EAAS,EAAE,QAAA,EAAU,GAAG,YAAA,CAAa,GAAG,CAAA,IAAK,CAAA;AAAA,EACxF;AACA,EAAA,IAAI,CAAC,IAAA,IAAA,CAAS,OAAA,CAAQ,WAAA,IAAe,cAAA,MAAoB,gBAAgB,OAAO,IAAA;AAGhF,EAAA,MAAM,QAAQ,MAAM,OAAA,CAAQ,KAAA,CAAM,QAAA,CAAS,QAAQ,MAAM,CAAA;AACzD,EAAA,IAAI,CAAC,KAAA,EAAO;AACV,IAAA,OAAA,CAAQ,KAAA,CAAM,CAAA,sDAAA,EAAyD,OAAA,CAAQ,MAAM,CAAA,yCAAA,CAA2C,CAAA;AAAA,EAClI;AACA,EAAA,MAAM,aAAA,GAAgB,EAAE,IAAA,EAAM,QAAA,CAAS,IAAA,CAAK,MAAM,MAAA,EAAQ,EAAE,OAAA,EAAS,SAAA,EAAU,EAAE;AACjF,EAAA,IAAI,KAAA;AACJ,EAAA,IAAI;AACF,IAAA,KAAA,GAAQ,SAAA,CAAU,KAAA,IAAS,aAAA,EAAe,QAAA,CAAS,KAAK,KAAK,CAAA;AAAA,EAC/D,SAAS,GAAA,EAAK;AACZ,IAAA,OAAA,CAAQ,KAAA,CAAM,CAAA,mCAAA,EAAsC,OAAA,CAAQ,MAAM,+FAA+F,GAAG,CAAA;AACpK,IAAA,KAAA,GAAQ,SAAA,CAAU,aAAA,EAAe,QAAA,CAAS,IAAA,CAAK,KAAK,CAAA;AAAA,EACtD;AAGA,EAAA,MAAM,eAAe,OAAA,CAAQ,OAAA,EAAS,SAAS,CAAA,IAAK,OAAA,CAAS,cAAc,KAAA,CAAM,SAAA;AACjF,EAAA,IAAI,YAAA,EAAc;AAChB,IAAA,OAAA,CAAQ,KAAK,CAAA,qBAAA,EAAwB,OAAA,CAAQ,MAAM,CAAA,kCAAA,EAAqC,OAAA,CAAS,MAAM,CAAA,mFAAA,CAAqF,CAAA;AAAA,EAC9L;AACA,EAAA,MAAM,KAAA,uBAAY,GAAA,EAAiC;AACnD,EAAA,MAAM,KAAA,GAAQ,gBAAA,CAAiB,QAAA,EAAU,IAAI,CAAA;AAC7C,EAAA,IAAI,KAAA,CAAM,MAAA,IAAU,CAAC,OAAA,CAAQ,OAAO,OAAA,CAAQ,KAAA,CAAM,CAAA,gBAAA,EAAmB,IAAI,CAAA,eAAA,EAAkB,KAAA,CAAM,IAAA,CAAK,IAAI,CAAC,CAAA,8CAAA,CAAgD,CAAA;AAC3J,EAAA,KAAA,MAAW,QAAQ,KAAA,EAAO;AAExB,IAAA,IAAA,CAAK,IAAA,CAAK,YAAA,CAAa,IAAI,CAAC,CAAA;AAC5B,IAAA,IAAI;AACF,MAAA,KAAA,CAAM,IAAI,IAAA,EAAM,OAAA,CAAQ,KAAA,GAAQ,MAAM,QAAQ,KAAA,CAAM,IAAA,EAAM,EAAE,MAAA,EAAQ,QAAQ,MAAA,EAAQ,QAAA,EAAU,MAAM,IAAA,EAAM,IAAI,IAAI,CAAA;AAAA,IACpH,SAAS,GAAA,EAAK;AACZ,MAAA,OAAA,CAAQ,KAAA,CAAM,CAAA,oCAAA,EAAuC,IAAI,CAAA,SAAA,CAAA,EAAa,GAAG,CAAA;AACzE,MAAA,KAAA,CAAM,GAAA,CAAI,MAAM,IAAI,CAAA;AAAA,IACtB;AAAA,EACF;AAEA,EAAA,IAAI,KAAA;AACJ,EAAA,IAAI,gBAAgB,IAAA,EAAM;AACxB,IAAA,MAAM,KAAA,GAAQ,IAAA,CAAK,GAAA,CAAI,OAAA,CAAQ,IAAA,CAAK,CAAC,CAAA,KAAM,CAAC,CAAA,CAAE,MAAA,IAAU,CAAA,CAAE,IAAA,KAAS,WAAW,CAAA;AAC9E,IAAA,MAAM,IAAA,GAAO,KAAA,GAAQ,MAAM,SAAA,CAAU,OAAA,CAAQ,OAAO,OAAA,CAAQ,MAAA,EAAQ,OAAO,CAAA,GAAI,EAAC;AAEhF,IAAA,KAAA,GAAQ,EAAE,IAAA,EAAM,YAAA,CAAa,IAAA,EAAM,IAAA,EAAM,IAAA,IAAQ,EAAC,EAAG,OAAA,EAAS,WAAA,EAAa,IAAA,EAAM,QAAA,CAAS,GAAG,CAAA,EAAE;AAAA,EACjG;AAEA,EAAA,MAAM,gBAA0B,EAAC;AACjC,EAAA,KAAA,MAAW,IAAA,IAAQ,IAAA,EAAM,GAAA,CAAI,OAAA,IAAW,EAAC,EAAG;AAC1C,IAAA,IAAI,KAAK,MAAA,EAAQ;AACjB,IAAA,MAAM,GAAA,GAAM,UAAA,CAAW,IAAA,CAAK,IAAI,CAAA;AAChC,IAAA,IAAI,CAAC,KAAK,YAAA,EAAc;AACxB,IAAA,MAAM,QAAA,GAAW,WAAA,CAAY,IAAI,CAAA,CAAE,IAAA;AACnC,IAAA,IAAI,QAAA,EAAU;AACZ,MAAA,IAAI;AACF,QAAA,aAAA,CAAc,KAAK,GAAG,GAAA,CAAI,YAAA,CAAa,QAAA,CAAS,KAAK,CAAC,CAAA;AAAA,MACxD,SAAS,GAAA,EAAK;AACZ,QAAA,OAAA,CAAQ,KAAA,CAAM,2BAA2B,IAAA,CAAK,EAAE,KAAK,IAAA,CAAK,IAAI,kCAAkC,GAAG,CAAA;AAAA,MACrG;AAAA,IACF;AAAA,EACF;AACA,EAAA,MAAM,IAAA,GAAO,SAAS,MAAA,IAAU,KAAA,IAAS,MAAM,IAAA,GAAO,EAAE,KAAK,KAAA,CAAM,IAAA,CAAK,KAAK,GAAA,EAAK,KAAA,CAAM,KAAK,GAAA,EAAK,IAAA,MAAU,gBAAA,CAAiB,KAAK,GAAE,GAAI,MAAA;AACxI,EAAA,MAAM,QAAA,GAAW,CAAC,IAAA,KAAiB,CAAA,EAAG,QAAQ,CAAA,EAAG,aAAa,IAAI,IAAI,CAAA,CAAA;AACtE,EAAA,MAAM,WAAW,OAAA,CAAQ,KAAA,KAAU,KAAA,GAAQ,KAAA,GAAS,QAAQ,KAAA,IAAS,cAAA;AAErE,EAAA,MAAM,OAAA,GAAU,cAAc,YAAA,EAAc;AAAA,IAC1C,QAAA;AAAA,IACA,IAAA;AAAA,IACA,KAAA;AAAA,IACA,MAAA;AAAA,IACA,QAAA;AAAA,IACA,IAAA;AAAA,IACA,GAAI,IAAA,GAAO,EAAE,IAAA,KAAS,EAAC;AAAA,IACvB,KAAA;AAAA,IACA,QAAQ,EAAE,GAAA,EAAK,SAAS,MAAA,CAAO,GAAA,CAAI,IAAI,CAAA,EAAG,QAAA,EAAU,SAAS,MAAA,CAAO,QAAA,CAAS,IAAI,CAAA,EAAG,OAAA,EAAS,SAAS,MAAA,CAAO,OAAA,CAAQ,IAAI,CAAA,EAAE;AAAA,IAC3H,QAAA;AAAA,IACA,GAAI,KAAA,GAAQ,EAAE,KAAA,KAAU;AAAC,GAC1B,CAAA;AACD,EAAA,IAAI,MAAA,GAAkB,IAAA;AACtB,EAAA,MAAM,MAAA,GAAS,MAAM,sBAAA,CAAuB,OAAA,EAAS;AAAA,IACnD,QAAQ,GAAA,EAAK;AACX,MAAA,MAAA,GAAS,GAAA;AACT,MAAA,OAAA,CAAQ,MAAM,CAAA,0BAAA,EAA6B,IAAI,YAAY,OAAA,CAAQ,MAAM,YAAY,GAAG,CAAA;AAAA,IAC1F;AAAA,GACD,CAAA;AACD,EAAA,MAAM,MAAA,CAAO,QAAA;AACb,EAAA,IAAI,MAAA,EAAQ,OAAO,YAAA,CAAa,gCAAA,EAAkC,GAAA,EAAK,6BAA6B,EAAE,eAAA,EAAiB,UAAA,EAAW,EAAG,IAAI,CAAA;AAEzI,EAAA,MAAM,MAAA,GAAS,OAAO,GAAA,GAAM,GAAA;AAC5B,EAAA,MAAM,OAAA,GAAkC;AAAA,IACtC,cAAA,EAAgB,0BAAA;AAAA,IAChB,wBAAA,EAA0B,SAAA;AAAA,IAC1B,iBAAA,EAAmB,iCAAA;AAAA,IACnB,GAAG,YAAA,CAAa,MAAA,EAAQ,QAAQ,CAAC,YAAA,GAAe,aAAa,UAAU;AAAA,GACzE;AACA,EAAA,IAAI,OAAA,CAAQ,QAAQ,KAAA,EAAO,OAAA,CAAQ,yBAAyB,CAAA,GAAI,MAAM,qBAAA,CAAsB,QAAA,CAAS,KAAK,CAAA,EAAG,OAAO,QAAA,KAAa,KAAA,IAAS,MAAM,KAAA,CAAM,IAAA,CAAK,CAAC,CAAA,KAAM,CAAA,CAAE,MAAM,CAAA,EAAG,aAAa,CAAA;AAC1L,EAAA,OAAO,IAAI,SAAS,IAAA,GAAO,IAAA,GAAO,QAAQ,EAAE,MAAA,EAAQ,SAAS,CAAA;AAC/D;AAOA,eAAsB,sBAAA,CAAuB,SAAkB,OAAA,EAAiG;AAC9J,EAAA,MAAM,GAAA,GAAM,MAAM,iBAAA,CAAkB,OAAA,EAAS,OAAO,CAAA;AACpD,EAAA,IAAI,CAAC,KAAK,OAAO,IAAA;AACjB,EAAA,OAAO,EAAE,MAAA,EAAQ,GAAA,CAAI,MAAA,EAAQ,IAAA,EAAM,MAAM,GAAA,CAAI,IAAA,EAAK,EAAG,OAAA,EAAS,GAAA,CAAI,OAAA,EAAQ;AAC5E","file":"chunk-HCICSNBH.js","sourcesContent":["/**\n * SiteRenderer: one page of a release as a whole HTML document. Server-only\n * output: it is rendered to a string or stream and never hydrated. The only\n * script is the islands loader, and only on a page with a phone menu or a\n * form.\n */\nimport * as React from \"react\";\nimport \"../sections/index\";\nimport type { PostsContext, RenderContext } from \"../core/registry\";\nimport { getSection } from \"../core/registry\";\nimport { migrateNode } from \"../core/migrate\";\nimport type { ReleasePage, ReleaseSnapshot, SectionNode } from \"../core/schemas\";\nimport { absoluteUrl, articleJsonLd, breadcrumbJsonLd, organizationJsonLd, pageMeta, safeJsonForHtml, websiteJsonLd } from \"../core/seo\";\nimport { themeCss, type FontInfo, type SiteTheme } from \"../core/theme\";\nimport type { ResolvedForm } from \"../core/forms\";\nimport { formDrawMode } from \"../sections/form-embed\";\n\nexport interface SiteAssets {\n /** The shared stylesheet. */\n css: string;\n /** The form stylesheet, linked only on pages with a form. */\n formsCss: string;\n /** The islands loader. */\n islands: string;\n}\n\nexport interface SiteRendererProps {\n snapshot: ReleaseSnapshot;\n /** The page to render, or null for the site's \"not found\" page. */\n page: ReleasePage | null;\n theme: SiteTheme;\n /** The site's primary origin, for canonical and Open Graph URLs. */\n origin: string;\n /** The path prefix the site is served under (`\"\"` at the root). */\n basePath?: string;\n mode?: \"live\" | \"preview\";\n logo?: { src: string; alt: string };\n /** Forms on the page, already resolved (see `collectFormSlugs`). */\n forms?: ReadonlyMap<string, ResolvedForm | null>;\n /** Asset URLs. */\n assets: SiteAssets;\n /** The stylesheet URL for a font, or null to rely on installed fonts. Default: Google Fonts. */\n fontHref?: ((font: FontInfo) => string | null) | false;\n /**\n * The blog, when the site has a post page: its path, the posts, the post\n * this page shows (`page` is then that post on the post page) and the\n * list's page number.\n */\n posts?: PostsContext;\n}\n\nexport const googleFontHref = (font: FontInfo): string | null =>\n font.google ? `https://fonts.googleapis.com/css2?family=${font.google}&display=swap` : null;\n\nconst FORM_SLUG = /^[a-z0-9][a-z0-9-]{0,79}$/;\n\n/** The form slugs a page uses, to resolve before rendering: well-formed slugs of visible form embeds in the page body. */\nexport function collectFormSlugs(_snapshot: ReleaseSnapshot, page: ReleasePage | null): string[] {\n const slugs = new Set<string>();\n for (const n of page?.doc.content ?? []) {\n if (n.type === \"form-embed\" && !n.hidden && typeof n.props.form === \"string\" && FORM_SLUG.test(n.props.form)) slugs.add(n.props.form);\n }\n return [...slugs];\n}\n\ninterface Rendered {\n key: string;\n element: React.ReactNode;\n type: string;\n}\n\nfunction renderNodes(nodes: readonly SectionNode[], base: Omit<RenderContext, \"headingLevel\" | \"idPrefix\">, h1: { given: boolean }, where: \"header\" | \"body\" | \"footer\"): Rendered[] {\n const out: Rendered[] = [];\n for (const raw of nodes) {\n if (raw.hidden) continue;\n if (raw.hideOn?.includes(\"mobile\") && raw.hideOn.includes(\"desktop\")) continue;\n const { node, issues } = migrateNode(raw);\n if (!node) {\n for (const issue of issues) console.error(`[genesis-sites] ${base.pagePath}: ${issue.message}`);\n continue;\n }\n const def = getSection(node.type)!;\n // The publish check refuses a section out of its place; a document that skipped it renders without it.\n if ((def.placement ?? \"body\") !== where) {\n console.error(`[genesis-sites] ${base.pagePath}: section ${node.id} (${node.type}) belongs in the ${def.placement ?? \"body\"}, not the ${where}; left out.`);\n continue;\n }\n const headingLevel: 1 | 2 = def.providesH1 && !h1.given ? 1 : 2;\n if (headingLevel === 1) h1.given = true;\n const ctx: RenderContext = { ...base, headingLevel, idPrefix: `gs-${node.id}` };\n try {\n const element = def.render({ props: node.props, variant: node.variant, node, ctx });\n // A section with nothing to show (the post section on the blog's index) gives the h1 back.\n if (element === null || element === undefined || element === false) {\n if (headingLevel === 1) h1.given = false;\n continue;\n }\n out.push({ key: node.id, type: node.type, element });\n } catch (err) {\n if (headingLevel === 1) h1.given = false;\n console.error(`[genesis-sites] ${base.pagePath}: section ${node.id} (${node.type}) failed to render and was left out.`, err);\n }\n }\n return out;\n}\n\nfunction hrefFor(basePath: string) {\n const base = basePath.replace(/\\/+$/, \"\");\n return (href: string) => {\n if (!href.startsWith(\"/\") || href.startsWith(\"//\")) return href;\n if (!base) return href;\n // The home page under a base path is `/base/`, as its canonical URL is.\n return href === \"/\" ? `${base}/` : `${base}${href}`;\n };\n}\n\n/**\n * The document for one page.\n *\n * @stability Beta\n */\nexport function SiteRenderer({ snapshot, page, theme, origin, basePath = \"\", mode = \"live\", logo, forms = new Map(), assets, fontHref = googleFontHref, posts }: SiteRendererProps) {\n const site = snapshot.site;\n const preview = mode === \"preview\";\n const path = page?.path ?? \"/404\";\n const base: Omit<RenderContext, \"headingLevel\" | \"idPrefix\"> = {\n site: { name: site.name, lang: site.lang, nav: site.nav, logo },\n pagePath: path,\n href: hrefFor(basePath),\n forms,\n mode,\n ...(posts ? { posts } : {}),\n };\n const chrome = !page?.doc.root.props.hideSiteChrome;\n const h1 = { given: false };\n const header = chrome ? renderNodes(site.header, base, { given: true }, \"header\") : [];\n const body = page ? renderNodes(page.doc.content, base, h1, \"body\") : [];\n const footer = chrome ? renderNodes(site.footer, base, { given: true }, \"footer\") : [];\n // Light forms need only the islands loader; a form that needs the engine also gets the form stylesheet and bundle.\n const formModes = body.some((r) => r.type === \"form-embed\") ? [...forms.values()].map(formDrawMode) : [];\n const hasForm = formModes.includes(\"light\") || formModes.includes(\"island\");\n const hasFormBundle = formModes.includes(\"island\");\n const hasMenu = header.some((r) => r.type === \"nav\") && site.nav.length > 0;\n // A section that asks for the loader (a click-to-load map).\n const hasIsland = body.some((r) => getSection(r.type)?.islands === true);\n const post = posts?.current ?? null;\n\n const meta = page\n ? pageMeta({\n site,\n page,\n origin,\n basePath,\n preview,\n ...(post ? { article: { publishedTime: post.publishedAt, modifiedTime: post.updatedAt, ...(post.doc.author ? { author: post.doc.author.name } : {}), ...(post.doc.category ? { section: post.doc.category } : {}) } } : {}),\n })\n : { title: `Page not found · ${site.name}`, canonical: \"\", robots: \"noindex, nofollow\", openGraph: {}, twitter: {} as Record<string, string> };\n const jsonLd: Record<string, unknown>[] = [];\n if (page && !preview) {\n if (page.path === \"/\") jsonLd.push(organizationJsonLd(site, origin, basePath), websiteJsonLd(site, origin, basePath));\n if (post) jsonLd.push(articleJsonLd(site, post, meta.canonical || absoluteUrl(origin, basePath, page.path), origin, basePath));\n // A post's trail runs through the blog's index to the post itself.\n const crumbs = breadcrumbJsonLd(post ? [...snapshot.pages, page] : snapshot.pages, page, origin, basePath);\n if (crumbs) jsonLd.push(crumbs);\n }\n const fontLinks = fontHref ? theme.fonts.map(fontHref).filter((h): h is string => Boolean(h)) : [];\n\n return (\n <html lang={site.lang} data-gs-site=\"\" {...(theme.colorScheme === \"dark\" ? { className: \"dark\" } : {})}>\n <head>\n <meta charSet=\"utf-8\" />\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\" />\n <title>{meta.title}</title>\n {meta.description ? <meta name=\"description\" content={meta.description} /> : null}\n <meta name=\"robots\" content={meta.robots} />\n {meta.canonical ? <link rel=\"canonical\" href={meta.canonical} /> : null}\n {Object.entries(meta.openGraph).map(([property, content]) => (\n <meta key={property} property={property} content={content} />\n ))}\n {Object.entries(meta.twitter).map(([name, content]) => (\n <meta key={name} name={name} content={content} />\n ))}\n {fontLinks.length ? (\n <>\n <link rel=\"preconnect\" href=\"https://fonts.googleapis.com\" />\n <link rel=\"preconnect\" href=\"https://fonts.gstatic.com\" crossOrigin=\"anonymous\" />\n {fontLinks.map((href) => (\n <link key={href} rel=\"stylesheet\" href={href} />\n ))}\n </>\n ) : null}\n <link rel=\"stylesheet\" href={assets.css} />\n {hasFormBundle ? <link rel=\"stylesheet\" href={assets.formsCss} /> : null}\n {/* The site's theme: CSS variables from the brand, after the shared stylesheet so they win. */}\n <style data-gs-theme=\"\" dangerouslySetInnerHTML={{ __html: themeCss(theme) }} />\n {jsonLd.map((data, i) => (\n <script key={i} type=\"application/ld+json\" dangerouslySetInnerHTML={{ __html: safeJsonForHtml(data) }} />\n ))}\n {posts ? <link rel=\"alternate\" type=\"application/rss+xml\" title={site.name} href={base.href(\"/rss.xml\")} /> : null}\n {hasMenu || hasForm || hasIsland ? <script type=\"module\" src={assets.islands} /> : null}\n </head>\n <body className=\"min-h-screen bg-background font-sans text-foreground antialiased\">\n <a\n href=\"#main\"\n className=\"sr-only rounded-md bg-background px-4 py-3 font-medium text-foreground focus:not-sr-only focus:fixed focus:left-4 focus:top-4 focus:z-[100] focus:outline-2 focus:outline-ring\"\n >\n Skip to content\n </a>\n {header.map((r) => (\n <React.Fragment key={r.key}>{r.element}</React.Fragment>\n ))}\n <main id=\"main\" tabIndex={-1} className=\"outline-none\">\n {page && !h1.given ? <h1 className=\"sr-only\">{page.doc.root.props.title}</h1> : null}\n {page ? (\n body.map((r) => <React.Fragment key={r.key}>{r.element}</React.Fragment>)\n ) : (\n <div className=\"mx-auto flex max-w-3xl flex-col items-center gap-6 px-4 py-24 text-center\">\n <h1 className=\"font-display text-4xl font-bold tracking-tight\">Page not found</h1>\n <p className=\"text-lg text-muted-foreground\">There's no page at this address. It may have moved.</p>\n <a href={base.href(\"/\")} className=\"inline-flex min-h-11 items-center rounded-md bg-primary px-6 font-medium text-primary-foreground focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ring\">\n Go to the home page\n </a>\n </div>\n )}\n </main>\n {footer.map((r) => (\n <React.Fragment key={r.key}>{r.element}</React.Fragment>\n ))}\n </body>\n </html>\n );\n}\n","/**\n * handleSiteRequest: serve a site from a `Request`, as a `Response`, or say\n * \"not mine\" (null) so the host app answers instead.\n *\n * A site shares its hostname with the host app. The host resolves the\n * request's Host to a site id itself (this package keeps no hostname table\n * and reads no Host header) and calls this before its own routes:\n *\n * const res = await handleSiteRequest(request, { store, brand, siteId, origin: \"https://riverside.example\" });\n * if (res) return res;\n * return app(request);\n *\n * Order: reserved paths (null), this package's assets, robots.txt and\n * sitemap.xml, redirects, the trailing-slash redirect, then the page. An\n * unknown path is null (or the site's 404 page, with `unknownPath:\n * \"not-found\"`). Pages render to HTML with no hydration.\n */\nimport { createElement } from \"react\";\nimport { renderToReadableStream } from \"react-dom/server\";\nimport type { FormsResolver, ResolvedForm } from \"../core/forms\";\nimport { migrateNode, migratePage, migrateSite } from \"../core/migrate\";\nimport { PostSlug, postSummary, type LivePost, type PostSummary } from \"../core/posts\";\nimport { getSection, type PostsContext } from \"../core/registry\";\nimport { ReleaseSnapshot, parseSnapshot, type Release, type ReleasePage } from \"../core/schemas\";\nimport { matchRedirect, postDescription, robotsTxt, rssXml, sitemapXml } from \"../core/seo\";\nimport { formCacheTag, siteCacheTag, type SiteStore } from \"../core/store\";\nimport { brandDisplayName, siteTheme, themeCss, type BrandSource, type FontInfo, type SiteTheme } from \"../core/theme\";\nimport { DEFAULT_RESERVED_PATHS, isReservedPath, normalizeRequestPath, type ReservedPaths } from \"../core/urls\";\nimport { ASSETS } from \"./assets.generated\";\nimport { SiteRenderer, collectFormSlugs, googleFontHref } from \"./document\";\n\n/** Where this package serves its own files, under the site's base path. Reserved for pages. */\nexport const ASSETS_PREFIX = \"/_gs\";\n\nexport interface SiteRequestOptions {\n /**\n * The live path needs only `getLive` (and, for a blog, `listLivePosts` and\n * `getLivePost`); preview needs the draft reads too (`listPosts` and\n * `getPost` for posts).\n */\n store: Pick<SiteStore, \"getLive\"> & Partial<Pick<SiteStore, \"getDraft\" | \"listPages\" | \"getPageDraft\" | \"listLivePosts\" | \"getLivePost\" | \"listPosts\" | \"getPost\">>;\n brand: BrandSource;\n /** The site this request is for, resolved by the host from the Host header (or a path). */\n siteId: string;\n /**\n * `live` (default) serves the published release, cached at the CDN.\n * `preview` serves the drafts, `noindex` and `no-store`; the store must be\n * one signed in as the viewer, so the database's `can_view` decides who sees them.\n * Preview also needs `viewer`: without one it answers null before reading a draft.\n */\n mode?: \"live\" | \"preview\";\n /**\n * Preview only: the signed-in viewer's user id, as the host's session\n * resolved it. Drafts are never read for an anonymous request (anon has no\n * grant on them), so a preview without a viewer is null: the host decides\n * what to show (its sign-in page, say). Live ignores it.\n */\n viewer?: string | null;\n /**\n * The site's primary origin (`https://riverside.example`) for canonical,\n * Open Graph and sitemap URLs. Default: the request URL's origin. Pass it\n * whenever a site has more than one hostname, so every alias names the same\n * canonical address.\n */\n origin?: string;\n /** The path the site is mounted under (`/sites-demo`); `\"\"` at the root. */\n basePath?: string;\n /** Paths that belong to the host app; null for them. Default `DEFAULT_RESERVED_PATHS`. */\n reserved?: ReservedPaths;\n /** Resolves a form embed's slug. Without it, form embeds render nothing (and log). */\n forms?: FormsResolver;\n /** Called with the cache tags of every cacheable response, for hosts that set their own cache headers. */\n onPublishedTags?: (tags: string[], info: { siteId: string; releaseId: string | null; path: string; status: number }) => void;\n /** An unknown path: `fall-through` (default) returns null; `not-found` serves the site's 404 page. */\n unknownPath?: \"fall-through\" | \"not-found\";\n /** Font stylesheets: Google Fonts by default, `false` for none, or a function returning a URL per font. */\n fonts?: false | ((font: FontInfo) => string | null);\n /** A Content-Security-Policy header (default true). */\n csp?: boolean;\n}\n\nconst LIVE_CACHE = \"public, max-age=0, s-maxage=31536000, stale-while-revalidate=86400\";\nconst MISS_CACHE = \"public, max-age=0, s-maxage=300, stale-while-revalidate=86400\";\nconst PREVIEW_CACHE = \"private, no-store\";\nconst ASSET_CACHE = \"public, max-age=31536000, immutable\";\n\nconst enc = new TextEncoder();\n\nasync function sha256Base64(text: string): Promise<string> {\n const digest = await crypto.subtle.digest(\"SHA-256\", enc.encode(text));\n let binary = \"\";\n for (const byte of new Uint8Array(digest)) binary += String.fromCharCode(byte);\n return btoa(binary);\n}\n\nfunction originOf(url: string): string | null {\n try {\n const u = new URL(url);\n return u.protocol === \"https:\" ? u.origin : null;\n } catch {\n return null;\n }\n}\n\nasync function contentSecurityPolicy(styleText: string, forms: ReadonlyMap<string, ResolvedForm | null>, fonts: boolean, sectionFrames: readonly string[] = []): Promise<string> {\n const connect = new Set([\"'self'\"]);\n // A light form posts natively when script is off, so its endpoint is a form target too.\n const formTargets = new Set([\"'self'\"]);\n const frames = new Set<string>();\n // Origins a section frames (a map), as the section builds them: https origins only.\n for (const f of sectionFrames) {\n const o = originOf(f);\n if (o && o === f) frames.add(o);\n }\n for (const form of forms.values()) {\n if (form?.kind === \"definition\") {\n const o = originOf(form.submitUrl);\n if (o) {\n connect.add(o);\n formTargets.add(o);\n }\n } else if (form?.kind === \"embed\") {\n const o = originOf(form.url);\n if (o) frames.add(o);\n }\n }\n return [\n \"default-src 'self'\",\n \"script-src 'self'\",\n `style-src 'self' 'sha256-${await sha256Base64(styleText)}'${fonts ? \" https://fonts.googleapis.com\" : \"\"}`,\n // Inline style attributes: an image's focal point, a frame's height, the form's own.\n \"style-src-attr 'unsafe-inline'\",\n \"img-src 'self' https: data:\",\n `font-src 'self'${fonts ? \" https://fonts.gstatic.com\" : \"\"}`,\n `connect-src ${[...connect].join(\" \")}`,\n `frame-src ${frames.size ? [...frames].join(\" \") : \"'none'\"}`,\n `form-action ${[...formTargets].join(\" \")}`,\n \"object-src 'none'\",\n \"base-uri 'none'\",\n \"frame-ancestors 'self'\",\n ].join(\"; \");\n}\n\nfunction textResponse(body: string, status: number, type: string, headers: Record<string, string>, head: boolean): Response {\n return new Response(head ? null : body, { status, headers: { \"content-type\": type, \"x-content-type-options\": \"nosniff\", ...headers } });\n}\n\n/** The drafts as a snapshot, for preview: migrated, unchecked. */\nasync function draftSnapshot(store: SiteRequestOptions[\"store\"], siteId: string): Promise<ReleaseSnapshot | null> {\n if (!store.getDraft || !store.listPages || !store.getPageDraft) {\n throw new Error(\"handleSiteRequest: preview needs a store with getDraft, listPages and getPageDraft.\");\n }\n const draft = await store.getDraft(siteId);\n if (!draft) return null;\n const metas = await store.listPages(siteId);\n const pages: ReleasePage[] = [];\n for (const meta of metas) {\n const p = await store.getPageDraft(meta.id);\n if (p) pages.push({ id: meta.id, path: p.path, kind: p.kind, doc: migratePage(p.doc).doc });\n }\n const parsed = ReleaseSnapshot.safeParse({ schemaVersion: 1, site: migrateSite(draft.site).site, pages });\n if (!parsed.success) {\n console.error(`[genesis-sites] the drafts of site ${siteId} don't form a valid site: ${parsed.error.message}`);\n return null;\n }\n return parsed.data;\n}\n\n/** The `?page=` of a post list: a whole number from 1 (1 when absent or nonsense). */\nfunction listPage(url: URL): number {\n const n = Number(url.searchParams.get(\"page\") ?? \"1\");\n return Number.isInteger(n) && n >= 1 && n <= 10000 ? n : 1;\n}\n\nconst PREVIEW_POSTS = 60;\n\n/** Published posts (live), or every post's draft (preview, newest first, at most 60). */\nasync function blogPosts(store: SiteRequestOptions[\"store\"], siteId: string, preview: boolean): Promise<PostSummary[] | null> {\n if (!preview) {\n if (!store.listLivePosts) {\n console.error(`[genesis-sites] site ${siteId} has a post page, but the store can't list posts (listLivePosts); the blog shows none.`);\n return null;\n }\n try {\n return await store.listLivePosts(siteId);\n } catch (err) {\n // One failed read leaves the list empty; the page around it still serves.\n console.error(`[genesis-sites] reading the published posts of site ${siteId} failed; the blog shows none.`, err);\n return null;\n }\n }\n if (!store.listPosts || !store.getPost) {\n console.error(`[genesis-sites] site ${siteId} has a post page, but the store can't read post drafts (listPosts, getPost) for the preview.`);\n return null;\n }\n const out: PostSummary[] = [];\n for (const meta of (await store.listPosts(siteId)).slice(0, PREVIEW_POSTS)) {\n const draft = await store.getPost(meta.id);\n if (draft) out.push(postSummary({ slug: draft.slug, publishedAt: draft.publishedAt ?? draft.updatedAt, updatedAt: draft.updatedAt, doc: draft.doc }));\n }\n return out;\n}\n\n/** One post: its published copy (live) or its draft (preview). */\nasync function blogPost(store: SiteRequestOptions[\"store\"], siteId: string, slug: string, preview: boolean): Promise<LivePost | null> {\n if (!preview) {\n if (!store.getLivePost) return null;\n try {\n return await store.getLivePost(siteId, slug);\n } catch (err) {\n // A failed read is the post not found (the site's 404), not a broken page.\n console.error(`[genesis-sites] reading the published post ${JSON.stringify(slug)} of site ${siteId} failed; answering not found.`, err);\n return null;\n }\n }\n if (!store.listPosts || !store.getPost) return null;\n const meta = (await store.listPosts(siteId)).find((p) => p.slug === slug);\n const draft = meta ? await store.getPost(meta.id) : null;\n return draft ? { slug: draft.slug, publishedAt: draft.publishedAt ?? draft.updatedAt, updatedAt: draft.updatedAt, doc: draft.doc } : null;\n}\n\n/** The post page, standing in for one post: the post's title and SEO on the template's sections. */\nfunction postAsPage(template: ReleasePage, post: LivePost, path: string): ReleasePage {\n const d = post.doc;\n const description = (d.seo?.description ?? postDescription(d))?.slice(0, 200);\n const ogImage = d.seo?.ogImage ?? d.cover;\n const seo = {\n ...(d.seo?.title ? { title: d.seo.title } : {}),\n ...(description ? { description } : {}),\n ...(ogImage ? { ogImage } : {}),\n ...(d.seo?.canonical ? { canonical: d.seo.canonical } : {}),\n ...(d.seo?.noindex || template.doc.seo?.noindex ? { noindex: true } : {}),\n };\n return { ...template, path, doc: { ...template.doc, root: { props: { ...template.doc.root.props, title: d.title.slice(0, 120) } }, seo } };\n}\n\n/** Percent-encode what a header can't carry (anything outside printable ASCII), leaving existing escapes alone. */\nconst headerSafe = (value: string) => value.replace(/[^\\x21-\\x7e]/gu, (c) => encodeURIComponent(c));\n\n/** A redirect's Location: a site path or fragment under the base path, or an absolute URL. */\nfunction redirectLocation(to: string, basePath: string): string {\n if (to.startsWith(\"#\")) return headerSafe(`${basePath}/${to}`);\n if (to.startsWith(\"/\")) return headerSafe(to === \"/\" ? `${basePath}/` : `${basePath}${to}`);\n try {\n return new URL(to).href;\n } catch {\n return headerSafe(`${basePath}/`);\n }\n}\n\n/**\n * Serve a site request, or return null when the request isn't the site's.\n *\n * @stability Beta\n */\nexport async function handleSiteRequest(request: Request, options: SiteRequestOptions): Promise<Response | null> {\n const method = request.method.toUpperCase();\n if (method !== \"GET\" && method !== \"HEAD\") return null;\n const head = method === \"HEAD\";\n const url = new URL(request.url);\n const basePath = (options.basePath ?? \"\").replace(/\\/+$/, \"\");\n const reserved = options.reserved ?? DEFAULT_RESERVED_PATHS;\n const mode = options.mode ?? \"live\";\n const preview = mode === \"preview\";\n\n if (basePath && url.pathname !== basePath && !url.pathname.startsWith(`${basePath}/`)) return null;\n const rawPath = url.pathname.slice(basePath.length) || \"/\";\n\n // This package's own files: content-hashed, immutable.\n if (rawPath.startsWith(`${ASSETS_PREFIX}/`)) {\n const name = rawPath.slice(ASSETS_PREFIX.length + 1);\n const asset = Object.values(ASSETS).find((a) => a.name === name);\n if (!asset) return textResponse(\"Not found\\n\", 404, \"text/plain; charset=utf-8\", { \"cache-control\": \"no-store\" }, head);\n return textResponse(asset.body, 200, asset.type, { \"cache-control\": ASSET_CACHE, \"access-control-allow-origin\": \"*\" }, head);\n }\n\n const path = normalizeRequestPath(rawPath);\n if (path === null) return null;\n if (isReservedPath(path, reserved)) return null;\n\n // Preview reads drafts, which only a signed-in viewer may: anonymous, the host answers.\n if (preview && !options.viewer) return null;\n\n // The release (live) or the drafts (preview).\n let snapshot: ReleaseSnapshot | null;\n let release: Release | null = null;\n if (preview) {\n snapshot = await draftSnapshot(options.store, options.siteId);\n } else {\n release = await options.store.getLive(options.siteId);\n const id = release?.id;\n snapshot = release ? parseSnapshot(release.snapshot, (m) => console.error(`[genesis-sites] release ${id} of site ${options.siteId}: ${m}`)) : null;\n }\n // Not published (or not visible): the host decides what this address shows.\n if (!snapshot) return null;\n\n const origin = (options.origin ?? url.origin).replace(/\\/+$/, \"\");\n const tags = [siteCacheTag(options.siteId)];\n const cacheHeaders = (status: number, cache = LIVE_CACHE): Record<string, string> => {\n if (preview) return { \"cache-control\": PREVIEW_CACHE, \"x-robots-tag\": \"noindex, nofollow\" };\n options.onPublishedTags?.(tags, { siteId: options.siteId, releaseId: release?.id ?? null, path, status });\n return {\n \"cache-control\": cache,\n \"vercel-cache-tag\": tags.join(\",\"),\n ...(release ? { \"x-genesis-release\": String(release.number) } : {}),\n };\n };\n\n if (path === \"/robots.txt\") {\n const disallowAll = preview || snapshot.site.seo.noindex === true;\n return textResponse(robotsTxt({ origin, basePath, disallowAll }), 200, \"text/plain; charset=utf-8\", cacheHeaders(200), head);\n }\n // The blog: the site's post page, at whose path the index lives and under which each post does.\n const postTemplate = snapshot.pages.find((p) => p.kind === \"post\") ?? null;\n if (path === \"/sitemap.xml\") {\n if (preview) return textResponse(\"Not found\\n\", 404, \"text/plain; charset=utf-8\", cacheHeaders(404), head);\n const items = postTemplate ? ((await blogPosts(options.store, options.siteId, false)) ?? []) : [];\n return textResponse(\n sitemapXml({ site: snapshot.site, pages: snapshot.pages, origin, basePath, lastModified: release?.publishedAt ?? null, ...(postTemplate ? { posts: { path: postTemplate.path, items } } : {}) }),\n 200,\n \"application/xml; charset=utf-8\",\n cacheHeaders(200),\n head,\n );\n }\n if (path === \"/rss.xml\") {\n if (!postTemplate) return null;\n if (preview) return textResponse(\"Not found\\n\", 404, \"text/plain; charset=utf-8\", cacheHeaders(404), head);\n const items = snapshot.site.seo.noindex || postTemplate.doc.seo?.noindex ? [] : ((await blogPosts(options.store, options.siteId, false)) ?? []);\n return textResponse(rssXml({ site: snapshot.site, posts: items, postsPath: postTemplate.path, title: postTemplate.doc.root.props.title === snapshot.site.name ? undefined : `${postTemplate.doc.root.props.title} · ${snapshot.site.name}`, origin, basePath }), 200, \"application/rss+xml; charset=utf-8\", cacheHeaders(200), head);\n }\n\n // Redirects first: a moved page's old path.\n const redirect = matchRedirect(snapshot.site.redirects, path);\n if (redirect) {\n // A moved page keeps the link's query string (campaign parameters, say); an outside URL is used as written.\n const location = redirectLocation(redirect.to, basePath) + (redirect.to.startsWith(\"/\") ? url.search : \"\");\n return new Response(null, { status: redirect.status, headers: { location, ...cacheHeaders(redirect.status) } });\n }\n let page = snapshot.pages.find((p) => p.path === path) ?? null;\n // A post: `<post page>/<slug>`, where no page is.\n let currentPost: LivePost | null = null;\n if (!page && postTemplate && path.startsWith(`${postTemplate.path === \"/\" ? \"\" : postTemplate.path}/`)) {\n const slug = path.slice(postTemplate.path.length + 1);\n if (PostSlug.safeParse(slug).success) {\n currentPost = await blogPost(options.store, options.siteId, slug, preview);\n if (currentPost) page = postAsPage(postTemplate, currentPost, path);\n }\n }\n // One address per page: lowercase, no trailing slash, and the home page under a base path\n // ends in one (`/base/`, as its canonical URL does). Only for the site's own pages, so the\n // host's paths (with their own casing or slashes) fall through untouched.\n const bareBase = Boolean(basePath) && url.pathname === basePath;\n if (page && (bareBase || (rawPath !== path && !(path === \"/\" && rawPath === \"/\")))) {\n const location = `${headerSafe(`${basePath}${path === \"/\" ? \"/\" : path}`)}${url.search}`;\n return new Response(null, { status: 308, headers: { location, ...cacheHeaders(308) } });\n }\n if (!page && (options.unknownPath ?? \"fall-through\") === \"fall-through\") return null;\n\n // Everything the page needs before it renders: the brand and its forms.\n const brand = await options.brand.getBrand(options.siteId);\n if (!brand) {\n console.error(`[genesis-sites] the BrandSource has no brand for site ${options.siteId}; rendering with Genesis's default theme.`);\n }\n const fallbackBrand = { name: snapshot.site.name, colors: { primary: \"#4f46e5\" } };\n let theme: SiteTheme;\n try {\n theme = siteTheme(brand ?? fallbackBrand, snapshot.site.theme);\n } catch (err) {\n console.error(`[genesis-sites] the brand for site ${options.siteId} can't make a theme (a colour that isn't #rrggbb?); rendering with Genesis's default theme.`, err);\n theme = siteTheme(fallbackBrand, snapshot.site.theme);\n }\n // The brand changed since the release was checked: its colours haven't passed the contrast\n // check. Serve it, but cache it briefly and say so; republishing checks it (and purges).\n const brandChanged = Boolean(release?.brandHash) && release!.brandHash !== theme.brandHash;\n if (brandChanged) {\n console.warn(`[genesis-sites] site ${options.siteId}: the brand changed since release ${release!.number} was checked; republish to check its colours. Caching this page for 5 minutes only.`);\n }\n const forms = new Map<string, ResolvedForm | null>();\n const slugs = collectFormSlugs(snapshot, page);\n if (slugs.length && !options.forms) console.error(`[genesis-sites] ${path} embeds forms (${slugs.join(\", \")}) but handleSiteRequest has no forms resolver.`);\n for (const slug of slugs) {\n // A page caches the forms it was rendered with: the host purges `form:<slug>` when it changes one.\n tags.push(formCacheTag(slug));\n try {\n forms.set(slug, options.forms ? await options.forms(slug, { siteId: options.siteId, pagePath: path, mode }) : null);\n } catch (err) {\n console.error(`[genesis-sites] resolving the form \"${slug}\" failed.`, err);\n forms.set(slug, null);\n }\n }\n // The posts a page lists, read once: for a post list on it, or the post page itself.\n let posts: PostsContext | undefined;\n if (postTemplate && page) {\n const lists = page.doc.content.some((n) => !n.hidden && n.type === \"blog-list\");\n const list = lists ? await blogPosts(options.store, options.siteId, preview) : [];\n // The list and the post are separate: a store that can't list still shows a post it can read.\n posts = { path: postTemplate.path, list: list ?? [], current: currentPost, page: listPage(url) };\n }\n // Origins the page's sections frame (a map), for the Content-Security-Policy.\n const sectionFrames: string[] = [];\n for (const node of page?.doc.content ?? []) {\n if (node.hidden) continue;\n const def = getSection(node.type);\n if (!def?.frameOrigins) continue;\n const migrated = migrateNode(node).node;\n if (migrated) {\n try {\n sectionFrames.push(...def.frameOrigins(migrated.props));\n } catch (err) {\n console.error(`[genesis-sites] section ${node.id} (${node.type}) couldn't say what it frames.`, err);\n }\n }\n }\n const logo = brand && \"logo\" in brand && brand.logo ? { src: brand.logo.src, alt: brand.logo.alt?.trim() || brandDisplayName(brand) } : undefined;\n const assetUrl = (name: string) => `${basePath}${ASSETS_PREFIX}/${name}`;\n const fontHref = options.fonts === false ? false : (options.fonts ?? googleFontHref);\n\n const element = createElement(SiteRenderer, {\n snapshot,\n page,\n theme,\n origin,\n basePath,\n mode,\n ...(logo ? { logo } : {}),\n forms,\n assets: { css: assetUrl(ASSETS.css.name), formsCss: assetUrl(ASSETS.formsCss.name), islands: assetUrl(ASSETS.islands.name) },\n fontHref,\n ...(posts ? { posts } : {}),\n });\n let failed: unknown = null;\n const stream = await renderToReadableStream(element, {\n onError(err) {\n failed = err;\n console.error(`[genesis-sites] rendering ${path} of site ${options.siteId} failed.`, err);\n },\n });\n await stream.allReady;\n if (failed) return textResponse(\"This page couldn't be shown.\\n\", 500, \"text/plain; charset=utf-8\", { \"cache-control\": \"no-store\" }, head);\n\n const status = page ? 200 : 404;\n const headers: Record<string, string> = {\n \"content-type\": \"text/html; charset=utf-8\",\n \"x-content-type-options\": \"nosniff\",\n \"referrer-policy\": \"strict-origin-when-cross-origin\",\n ...cacheHeaders(status, page && !brandChanged ? LIVE_CACHE : MISS_CACHE),\n };\n if (options.csp !== false) headers[\"content-security-policy\"] = await contentSecurityPolicy(themeCss(theme), forms, fontHref !== false && theme.fonts.some((f) => f.google), sectionFrames);\n return new Response(head ? null : stream, { status, headers });\n}\n\n/**\n * One page as an HTML string (tests, static export, the editor's preview frame).\n *\n * @stability Beta\n */\nexport async function renderSitePageToString(request: Request, options: SiteRequestOptions): Promise<{ status: number; html: string; headers: Headers } | null> {\n const res = await handleSiteRequest(request, options);\n if (!res) return null;\n return { status: res.status, html: await res.text(), headers: res.headers };\n}\n"]}