@marktiderman/genesis-sites 0.0.0-stage → 0.2.0
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.
- package/LICENSE +21 -0
- package/README.md +465 -2
- package/dist/assets/form-island.c0f6fac859.js +51 -0
- package/dist/assets/islands.77d11f5697.js +1 -0
- package/dist/assets/sites-forms.4e6aa42497.css +10 -0
- package/dist/assets/sites-forms.css +10 -0
- package/dist/assets/sites.css +2 -0
- package/dist/assets/sites.e1ffd2708b.css +2 -0
- package/dist/blocks.d.ts +13 -0
- package/dist/blocks.js +26 -0
- package/dist/blocks.js.map +1 -0
- package/dist/chunk-3VKTBE7S.js +69 -0
- package/dist/chunk-3VKTBE7S.js.map +1 -0
- package/dist/chunk-C375ZPU2.js +2241 -0
- package/dist/chunk-C375ZPU2.js.map +1 -0
- package/dist/chunk-IXMY4B56.js +877 -0
- package/dist/chunk-IXMY4B56.js.map +1 -0
- package/dist/chunk-VZUDRPFG.js +13 -0
- package/dist/chunk-VZUDRPFG.js.map +1 -0
- package/dist/chunk-ZGWGQGT2.js +595 -0
- package/dist/chunk-ZGWGQGT2.js.map +1 -0
- package/dist/import.d.ts +130 -0
- package/dist/import.js +330 -0
- package/dist/import.js.map +1 -0
- package/dist/index.d.ts +273 -0
- package/dist/index.js +254 -0
- package/dist/index.js.map +1 -0
- package/dist/registry-D_DCWRTc.d.ts +1030 -0
- package/dist/sections.d.ts +802 -0
- package/dist/sections.js +6 -0
- package/dist/sections.js.map +1 -0
- package/dist/store-DkSycDhq.d.ts +443 -0
- package/dist/supabase.d.ts +41 -0
- package/dist/supabase.js +283 -0
- package/dist/supabase.js.map +1 -0
- package/dist/web.d.ts +136 -0
- package/dist/web.js +479 -0
- package/dist/web.js.map +1 -0
- package/migrations/0001_genesis_sites.sql +821 -0
- package/migrations/0002_genesis_sites_posts.sql +404 -0
- package/migrations/tests/0001_genesis_sites.test.sql +349 -0
- package/migrations/tests/0002_genesis_sites_posts.test.sql +242 -0
- package/package.json +152 -3
|
@@ -0,0 +1,1030 @@
|
|
|
1
|
+
import { ReactNode } from 'react';
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* The documents a site is made of, as zod 4 schemas: a `SiteDoc` (theme,
|
|
6
|
+
* header, navigation, footer, SEO defaults, redirects), one `PageDoc` per
|
|
7
|
+
* page (its sections and SEO), and the `ReleaseSnapshot` a publish freezes.
|
|
8
|
+
*
|
|
9
|
+
* A page's `content` is an array of `{ id, type, v, variant, props }`
|
|
10
|
+
* section nodes, which is the shape Puck's `content` uses, so the editor
|
|
11
|
+
* reads and writes these documents unchanged. A section's `props` are
|
|
12
|
+
* checked by that section's own schema (see `defineSection`), not here.
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
/** A link target: a site path, a fragment, or an http(s), mailto or tel URL. */
|
|
16
|
+
declare const Href: z.ZodString & z.ZodType<string, string, z.core.$ZodTypeInternals<string, string>>;
|
|
17
|
+
/** An analytics event name for a link: `data-gs-event` on the page (default `cta` on buttons). */
|
|
18
|
+
declare const LinkEvent: z.ZodString;
|
|
19
|
+
/**
|
|
20
|
+
* A labelled link: a button, a menu entry, a footer link, an outbound
|
|
21
|
+
* booking link.
|
|
22
|
+
*
|
|
23
|
+
* - `external: true` marks a link that leaves the site (a booking or
|
|
24
|
+
* membership system): it must be an `http(s)` URL, and it renders with
|
|
25
|
+
* `rel="noopener noreferrer"` and `data-gs-outbound` for analytics.
|
|
26
|
+
* - `target` (`_blank` for a new tab) applies to http(s) links only;
|
|
27
|
+
* `newTab: true` is the older spelling of `target: "_blank"`.
|
|
28
|
+
* - `event` names the click for the host's analytics (`data-gs-event`).
|
|
29
|
+
*/
|
|
30
|
+
declare const Link: z.ZodObject<{
|
|
31
|
+
label: z.ZodString;
|
|
32
|
+
href: z.ZodString & z.ZodType<string, string, z.core.$ZodTypeInternals<string, string>>;
|
|
33
|
+
newTab: z.ZodOptional<z.ZodBoolean>;
|
|
34
|
+
target: z.ZodOptional<z.ZodEnum<{
|
|
35
|
+
_self: "_self";
|
|
36
|
+
_blank: "_blank";
|
|
37
|
+
}>>;
|
|
38
|
+
external: z.ZodOptional<z.ZodBoolean>;
|
|
39
|
+
event: z.ZodOptional<z.ZodString>;
|
|
40
|
+
}, z.core.$strip>;
|
|
41
|
+
type Link = z.infer<typeof Link>;
|
|
42
|
+
/** Does this link open in a new tab? Only an http(s) link can. */
|
|
43
|
+
declare const opensInNewTab: (link: Pick<Link, "href" | "newTab" | "target">) => boolean;
|
|
44
|
+
/**
|
|
45
|
+
* A downloadable file (a PDF timetable, a waiver): a site path (an upload
|
|
46
|
+
* under `sites/<site_id>/files/`) or an https URL. `bytes` and `mime` say
|
|
47
|
+
* what the visitor is about to download.
|
|
48
|
+
*/
|
|
49
|
+
declare const FileRef: z.ZodObject<{
|
|
50
|
+
src: z.ZodString & z.ZodType<string, string, z.core.$ZodTypeInternals<string, string>>;
|
|
51
|
+
mime: z.ZodDefault<z.ZodEnum<{
|
|
52
|
+
"application/pdf": "application/pdf";
|
|
53
|
+
"image/png": "image/png";
|
|
54
|
+
"image/jpeg": "image/jpeg";
|
|
55
|
+
"image/webp": "image/webp";
|
|
56
|
+
"image/avif": "image/avif";
|
|
57
|
+
"image/gif": "image/gif";
|
|
58
|
+
}>>;
|
|
59
|
+
bytes: z.ZodOptional<z.ZodNumber>;
|
|
60
|
+
}, z.core.$strip>;
|
|
61
|
+
type FileRef = z.infer<typeof FileRef>;
|
|
62
|
+
/**
|
|
63
|
+
* An image. `alt` describes it for someone who can't see it; an image that
|
|
64
|
+
* adds nothing (a texture) is `decorative: true` and gets an empty alt. The
|
|
65
|
+
* accessibility check refuses to publish an image with neither.
|
|
66
|
+
*/
|
|
67
|
+
declare const Image: z.ZodObject<{
|
|
68
|
+
src: z.ZodString & z.ZodType<string, string, z.core.$ZodTypeInternals<string, string>>;
|
|
69
|
+
alt: z.ZodDefault<z.ZodString>;
|
|
70
|
+
decorative: z.ZodOptional<z.ZodBoolean>;
|
|
71
|
+
width: z.ZodOptional<z.ZodNumber>;
|
|
72
|
+
height: z.ZodOptional<z.ZodNumber>;
|
|
73
|
+
focal: z.ZodOptional<z.ZodObject<{
|
|
74
|
+
x: z.ZodNumber;
|
|
75
|
+
y: z.ZodNumber;
|
|
76
|
+
}, z.core.$strip>>;
|
|
77
|
+
}, z.core.$strip>;
|
|
78
|
+
type Image = z.infer<typeof Image>;
|
|
79
|
+
/**
|
|
80
|
+
* Rich text: the JSON genesis-ui's `RichTextEditor` (TipTap) produces. Only
|
|
81
|
+
* its outline is checked here; genesis-ui's `RichText` sanitizes it again
|
|
82
|
+
* when it renders, so a stored document can never carry raw HTML.
|
|
83
|
+
*/
|
|
84
|
+
declare const RichText: z.ZodObject<{
|
|
85
|
+
type: z.ZodLiteral<"doc">;
|
|
86
|
+
content: z.ZodOptional<z.ZodArray<z.ZodRecord<z.ZodString, z.ZodUnknown>>>;
|
|
87
|
+
}, z.core.$strip>;
|
|
88
|
+
type RichText = z.infer<typeof RichText>;
|
|
89
|
+
/** The band a section sits on. */
|
|
90
|
+
declare const Surface: z.ZodEnum<{
|
|
91
|
+
base: "base";
|
|
92
|
+
muted: "muted";
|
|
93
|
+
accent: "accent";
|
|
94
|
+
inverse: "inverse";
|
|
95
|
+
}>;
|
|
96
|
+
type Surface = z.infer<typeof Surface>;
|
|
97
|
+
/** A short line of copy (a heading, a label). */
|
|
98
|
+
declare const Line: (max?: number) => z.ZodString;
|
|
99
|
+
/** A paragraph of plain copy. */
|
|
100
|
+
declare const Copy: (max?: number) => z.ZodString;
|
|
101
|
+
/** A section's id: stable, letters, digits, `-` and `_` (Shopify's rule). */
|
|
102
|
+
declare const SectionId: z.ZodString;
|
|
103
|
+
declare const SectionNode: z.ZodObject<{
|
|
104
|
+
id: z.ZodString;
|
|
105
|
+
type: z.ZodString;
|
|
106
|
+
v: z.ZodNumber;
|
|
107
|
+
variant: z.ZodOptional<z.ZodString>;
|
|
108
|
+
hidden: z.ZodOptional<z.ZodBoolean>;
|
|
109
|
+
hideOn: z.ZodOptional<z.ZodArray<z.ZodEnum<{
|
|
110
|
+
mobile: "mobile";
|
|
111
|
+
desktop: "desktop";
|
|
112
|
+
}>>>;
|
|
113
|
+
placeholder: z.ZodOptional<z.ZodBoolean>;
|
|
114
|
+
props: z.ZodRecord<z.ZodString, z.ZodUnknown>;
|
|
115
|
+
}, z.core.$strip>;
|
|
116
|
+
type SectionNode = z.infer<typeof SectionNode>;
|
|
117
|
+
declare const Seo: z.ZodObject<{
|
|
118
|
+
title: z.ZodOptional<z.ZodString>;
|
|
119
|
+
description: z.ZodOptional<z.ZodString>;
|
|
120
|
+
ogImage: z.ZodOptional<z.ZodObject<{
|
|
121
|
+
src: z.ZodString & z.ZodType<string, string, z.core.$ZodTypeInternals<string, string>>;
|
|
122
|
+
alt: z.ZodDefault<z.ZodString>;
|
|
123
|
+
decorative: z.ZodOptional<z.ZodBoolean>;
|
|
124
|
+
width: z.ZodOptional<z.ZodNumber>;
|
|
125
|
+
height: z.ZodOptional<z.ZodNumber>;
|
|
126
|
+
focal: z.ZodOptional<z.ZodObject<{
|
|
127
|
+
x: z.ZodNumber;
|
|
128
|
+
y: z.ZodNumber;
|
|
129
|
+
}, z.core.$strip>>;
|
|
130
|
+
}, z.core.$strip>>;
|
|
131
|
+
canonical: z.ZodOptional<z.ZodURL>;
|
|
132
|
+
noindex: z.ZodOptional<z.ZodBoolean>;
|
|
133
|
+
}, z.core.$strip>;
|
|
134
|
+
type Seo = z.infer<typeof Seo>;
|
|
135
|
+
declare const PageDoc: z.ZodObject<{
|
|
136
|
+
schemaVersion: z.ZodLiteral<1>;
|
|
137
|
+
root: z.ZodObject<{
|
|
138
|
+
props: z.ZodObject<{
|
|
139
|
+
title: z.ZodString;
|
|
140
|
+
hideSiteChrome: z.ZodOptional<z.ZodBoolean>;
|
|
141
|
+
}, z.core.$loose>;
|
|
142
|
+
}, z.core.$strip>;
|
|
143
|
+
content: z.ZodArray<z.ZodObject<{
|
|
144
|
+
id: z.ZodString;
|
|
145
|
+
type: z.ZodString;
|
|
146
|
+
v: z.ZodNumber;
|
|
147
|
+
variant: z.ZodOptional<z.ZodString>;
|
|
148
|
+
hidden: z.ZodOptional<z.ZodBoolean>;
|
|
149
|
+
hideOn: z.ZodOptional<z.ZodArray<z.ZodEnum<{
|
|
150
|
+
mobile: "mobile";
|
|
151
|
+
desktop: "desktop";
|
|
152
|
+
}>>>;
|
|
153
|
+
placeholder: z.ZodOptional<z.ZodBoolean>;
|
|
154
|
+
props: z.ZodRecord<z.ZodString, z.ZodUnknown>;
|
|
155
|
+
}, z.core.$strip>>;
|
|
156
|
+
seo: z.ZodOptional<z.ZodObject<{
|
|
157
|
+
title: z.ZodOptional<z.ZodString>;
|
|
158
|
+
description: z.ZodOptional<z.ZodString>;
|
|
159
|
+
ogImage: z.ZodOptional<z.ZodObject<{
|
|
160
|
+
src: z.ZodString & z.ZodType<string, string, z.core.$ZodTypeInternals<string, string>>;
|
|
161
|
+
alt: z.ZodDefault<z.ZodString>;
|
|
162
|
+
decorative: z.ZodOptional<z.ZodBoolean>;
|
|
163
|
+
width: z.ZodOptional<z.ZodNumber>;
|
|
164
|
+
height: z.ZodOptional<z.ZodNumber>;
|
|
165
|
+
focal: z.ZodOptional<z.ZodObject<{
|
|
166
|
+
x: z.ZodNumber;
|
|
167
|
+
y: z.ZodNumber;
|
|
168
|
+
}, z.core.$strip>>;
|
|
169
|
+
}, z.core.$strip>>;
|
|
170
|
+
canonical: z.ZodOptional<z.ZodURL>;
|
|
171
|
+
noindex: z.ZodOptional<z.ZodBoolean>;
|
|
172
|
+
}, z.core.$strip>>;
|
|
173
|
+
}, z.core.$strip>;
|
|
174
|
+
type PageDoc = z.infer<typeof PageDoc>;
|
|
175
|
+
/**
|
|
176
|
+
* What a page is: an ordinary `page`, a focused `landing` page, or the
|
|
177
|
+
* `post` page: the template every blog post renders in. A site has at most
|
|
178
|
+
* one post page; at its own path (`/blog`) it is the blog's index, and at
|
|
179
|
+
* `/blog/<slug>` it shows that post in its `post-body` section.
|
|
180
|
+
*/
|
|
181
|
+
declare const PageKind: z.ZodEnum<{
|
|
182
|
+
page: "page";
|
|
183
|
+
landing: "landing";
|
|
184
|
+
post: "post";
|
|
185
|
+
}>;
|
|
186
|
+
type PageKind = z.infer<typeof PageKind>;
|
|
187
|
+
declare const PagePath: z.ZodString;
|
|
188
|
+
declare const MenuItem: z.ZodObject<{
|
|
189
|
+
label: z.ZodString;
|
|
190
|
+
href: z.ZodString & z.ZodType<string, string, z.core.$ZodTypeInternals<string, string>>;
|
|
191
|
+
}, z.core.$strip>;
|
|
192
|
+
type MenuItem = z.infer<typeof MenuItem>;
|
|
193
|
+
declare const Redirect: z.ZodObject<{
|
|
194
|
+
from: z.ZodString;
|
|
195
|
+
to: z.ZodString & z.ZodType<string, string, z.core.$ZodTypeInternals<string, string>>;
|
|
196
|
+
status: z.ZodUnion<readonly [z.ZodLiteral<301>, z.ZodLiteral<302>]>;
|
|
197
|
+
}, z.core.$strip>;
|
|
198
|
+
type Redirect = z.infer<typeof Redirect>;
|
|
199
|
+
/** Fonts a site may choose; see `FONT_CHOICES` in theme.ts for their stacks. */
|
|
200
|
+
declare const FontChoice: z.ZodEnum<{
|
|
201
|
+
system: "system";
|
|
202
|
+
inter: "inter";
|
|
203
|
+
"dm-sans": "dm-sans";
|
|
204
|
+
"work-sans": "work-sans";
|
|
205
|
+
montserrat: "montserrat";
|
|
206
|
+
poppins: "poppins";
|
|
207
|
+
"space-grotesk": "space-grotesk";
|
|
208
|
+
"source-serif": "source-serif";
|
|
209
|
+
merriweather: "merriweather";
|
|
210
|
+
"playfair-display": "playfair-display";
|
|
211
|
+
lora: "lora";
|
|
212
|
+
}>;
|
|
213
|
+
type FontChoice = z.infer<typeof FontChoice>;
|
|
214
|
+
/**
|
|
215
|
+
* A site's theme choices on top of the brand: token choices only, never raw
|
|
216
|
+
* CSS, so a site stays inside the design system.
|
|
217
|
+
*/
|
|
218
|
+
declare const TokenOverrides: z.ZodObject<{
|
|
219
|
+
radius: z.ZodOptional<z.ZodEnum<{
|
|
220
|
+
none: "none";
|
|
221
|
+
sm: "sm";
|
|
222
|
+
md: "md";
|
|
223
|
+
lg: "lg";
|
|
224
|
+
full: "full";
|
|
225
|
+
}>>;
|
|
226
|
+
headingFont: z.ZodOptional<z.ZodEnum<{
|
|
227
|
+
system: "system";
|
|
228
|
+
inter: "inter";
|
|
229
|
+
"dm-sans": "dm-sans";
|
|
230
|
+
"work-sans": "work-sans";
|
|
231
|
+
montserrat: "montserrat";
|
|
232
|
+
poppins: "poppins";
|
|
233
|
+
"space-grotesk": "space-grotesk";
|
|
234
|
+
"source-serif": "source-serif";
|
|
235
|
+
merriweather: "merriweather";
|
|
236
|
+
"playfair-display": "playfair-display";
|
|
237
|
+
lora: "lora";
|
|
238
|
+
}>>;
|
|
239
|
+
bodyFont: z.ZodOptional<z.ZodEnum<{
|
|
240
|
+
system: "system";
|
|
241
|
+
inter: "inter";
|
|
242
|
+
"dm-sans": "dm-sans";
|
|
243
|
+
"work-sans": "work-sans";
|
|
244
|
+
montserrat: "montserrat";
|
|
245
|
+
poppins: "poppins";
|
|
246
|
+
"space-grotesk": "space-grotesk";
|
|
247
|
+
"source-serif": "source-serif";
|
|
248
|
+
merriweather: "merriweather";
|
|
249
|
+
"playfair-display": "playfair-display";
|
|
250
|
+
lora: "lora";
|
|
251
|
+
}>>;
|
|
252
|
+
}, z.core.$strip>;
|
|
253
|
+
type TokenOverrides = z.infer<typeof TokenOverrides>;
|
|
254
|
+
declare const SeoDefaults: z.ZodObject<{
|
|
255
|
+
titleTemplate: z.ZodOptional<z.ZodString>;
|
|
256
|
+
description: z.ZodOptional<z.ZodString>;
|
|
257
|
+
ogImage: z.ZodOptional<z.ZodObject<{
|
|
258
|
+
src: z.ZodString & z.ZodType<string, string, z.core.$ZodTypeInternals<string, string>>;
|
|
259
|
+
alt: z.ZodDefault<z.ZodString>;
|
|
260
|
+
decorative: z.ZodOptional<z.ZodBoolean>;
|
|
261
|
+
width: z.ZodOptional<z.ZodNumber>;
|
|
262
|
+
height: z.ZodOptional<z.ZodNumber>;
|
|
263
|
+
focal: z.ZodOptional<z.ZodObject<{
|
|
264
|
+
x: z.ZodNumber;
|
|
265
|
+
y: z.ZodNumber;
|
|
266
|
+
}, z.core.$strip>>;
|
|
267
|
+
}, z.core.$strip>>;
|
|
268
|
+
organization: z.ZodOptional<z.ZodObject<{
|
|
269
|
+
name: z.ZodString;
|
|
270
|
+
logo: z.ZodOptional<z.ZodString & z.ZodType<string, string, z.core.$ZodTypeInternals<string, string>>>;
|
|
271
|
+
sameAs: z.ZodOptional<z.ZodArray<z.ZodURL>>;
|
|
272
|
+
}, z.core.$strip>>;
|
|
273
|
+
noindex: z.ZodOptional<z.ZodBoolean>;
|
|
274
|
+
}, z.core.$strip>;
|
|
275
|
+
type SeoDefaults = z.infer<typeof SeoDefaults>;
|
|
276
|
+
declare const SiteDoc: z.ZodObject<{
|
|
277
|
+
schemaVersion: z.ZodLiteral<1>;
|
|
278
|
+
name: z.ZodString;
|
|
279
|
+
lang: z.ZodDefault<z.ZodString>;
|
|
280
|
+
theme: z.ZodDefault<z.ZodObject<{
|
|
281
|
+
colorScheme: z.ZodDefault<z.ZodEnum<{
|
|
282
|
+
light: "light";
|
|
283
|
+
dark: "dark";
|
|
284
|
+
auto: "auto";
|
|
285
|
+
}>>;
|
|
286
|
+
overrides: z.ZodOptional<z.ZodObject<{
|
|
287
|
+
radius: z.ZodOptional<z.ZodEnum<{
|
|
288
|
+
none: "none";
|
|
289
|
+
sm: "sm";
|
|
290
|
+
md: "md";
|
|
291
|
+
lg: "lg";
|
|
292
|
+
full: "full";
|
|
293
|
+
}>>;
|
|
294
|
+
headingFont: z.ZodOptional<z.ZodEnum<{
|
|
295
|
+
system: "system";
|
|
296
|
+
inter: "inter";
|
|
297
|
+
"dm-sans": "dm-sans";
|
|
298
|
+
"work-sans": "work-sans";
|
|
299
|
+
montserrat: "montserrat";
|
|
300
|
+
poppins: "poppins";
|
|
301
|
+
"space-grotesk": "space-grotesk";
|
|
302
|
+
"source-serif": "source-serif";
|
|
303
|
+
merriweather: "merriweather";
|
|
304
|
+
"playfair-display": "playfair-display";
|
|
305
|
+
lora: "lora";
|
|
306
|
+
}>>;
|
|
307
|
+
bodyFont: z.ZodOptional<z.ZodEnum<{
|
|
308
|
+
system: "system";
|
|
309
|
+
inter: "inter";
|
|
310
|
+
"dm-sans": "dm-sans";
|
|
311
|
+
"work-sans": "work-sans";
|
|
312
|
+
montserrat: "montserrat";
|
|
313
|
+
poppins: "poppins";
|
|
314
|
+
"space-grotesk": "space-grotesk";
|
|
315
|
+
"source-serif": "source-serif";
|
|
316
|
+
merriweather: "merriweather";
|
|
317
|
+
"playfair-display": "playfair-display";
|
|
318
|
+
lora: "lora";
|
|
319
|
+
}>>;
|
|
320
|
+
}, z.core.$strip>>;
|
|
321
|
+
}, z.core.$strip>>;
|
|
322
|
+
header: z.ZodDefault<z.ZodArray<z.ZodObject<{
|
|
323
|
+
id: z.ZodString;
|
|
324
|
+
type: z.ZodString;
|
|
325
|
+
v: z.ZodNumber;
|
|
326
|
+
variant: z.ZodOptional<z.ZodString>;
|
|
327
|
+
hidden: z.ZodOptional<z.ZodBoolean>;
|
|
328
|
+
hideOn: z.ZodOptional<z.ZodArray<z.ZodEnum<{
|
|
329
|
+
mobile: "mobile";
|
|
330
|
+
desktop: "desktop";
|
|
331
|
+
}>>>;
|
|
332
|
+
placeholder: z.ZodOptional<z.ZodBoolean>;
|
|
333
|
+
props: z.ZodRecord<z.ZodString, z.ZodUnknown>;
|
|
334
|
+
}, z.core.$strip>>>;
|
|
335
|
+
nav: z.ZodDefault<z.ZodArray<z.ZodObject<{
|
|
336
|
+
label: z.ZodString;
|
|
337
|
+
href: z.ZodString & z.ZodType<string, string, z.core.$ZodTypeInternals<string, string>>;
|
|
338
|
+
}, z.core.$strip>>>;
|
|
339
|
+
footer: z.ZodDefault<z.ZodArray<z.ZodObject<{
|
|
340
|
+
id: z.ZodString;
|
|
341
|
+
type: z.ZodString;
|
|
342
|
+
v: z.ZodNumber;
|
|
343
|
+
variant: z.ZodOptional<z.ZodString>;
|
|
344
|
+
hidden: z.ZodOptional<z.ZodBoolean>;
|
|
345
|
+
hideOn: z.ZodOptional<z.ZodArray<z.ZodEnum<{
|
|
346
|
+
mobile: "mobile";
|
|
347
|
+
desktop: "desktop";
|
|
348
|
+
}>>>;
|
|
349
|
+
placeholder: z.ZodOptional<z.ZodBoolean>;
|
|
350
|
+
props: z.ZodRecord<z.ZodString, z.ZodUnknown>;
|
|
351
|
+
}, z.core.$strip>>>;
|
|
352
|
+
seo: z.ZodDefault<z.ZodObject<{
|
|
353
|
+
titleTemplate: z.ZodOptional<z.ZodString>;
|
|
354
|
+
description: z.ZodOptional<z.ZodString>;
|
|
355
|
+
ogImage: z.ZodOptional<z.ZodObject<{
|
|
356
|
+
src: z.ZodString & z.ZodType<string, string, z.core.$ZodTypeInternals<string, string>>;
|
|
357
|
+
alt: z.ZodDefault<z.ZodString>;
|
|
358
|
+
decorative: z.ZodOptional<z.ZodBoolean>;
|
|
359
|
+
width: z.ZodOptional<z.ZodNumber>;
|
|
360
|
+
height: z.ZodOptional<z.ZodNumber>;
|
|
361
|
+
focal: z.ZodOptional<z.ZodObject<{
|
|
362
|
+
x: z.ZodNumber;
|
|
363
|
+
y: z.ZodNumber;
|
|
364
|
+
}, z.core.$strip>>;
|
|
365
|
+
}, z.core.$strip>>;
|
|
366
|
+
organization: z.ZodOptional<z.ZodObject<{
|
|
367
|
+
name: z.ZodString;
|
|
368
|
+
logo: z.ZodOptional<z.ZodString & z.ZodType<string, string, z.core.$ZodTypeInternals<string, string>>>;
|
|
369
|
+
sameAs: z.ZodOptional<z.ZodArray<z.ZodURL>>;
|
|
370
|
+
}, z.core.$strip>>;
|
|
371
|
+
noindex: z.ZodOptional<z.ZodBoolean>;
|
|
372
|
+
}, z.core.$strip>>;
|
|
373
|
+
redirects: z.ZodDefault<z.ZodArray<z.ZodObject<{
|
|
374
|
+
from: z.ZodString;
|
|
375
|
+
to: z.ZodString & z.ZodType<string, string, z.core.$ZodTypeInternals<string, string>>;
|
|
376
|
+
status: z.ZodUnion<readonly [z.ZodLiteral<301>, z.ZodLiteral<302>]>;
|
|
377
|
+
}, z.core.$strip>>>;
|
|
378
|
+
}, z.core.$strip>;
|
|
379
|
+
type SiteDoc = z.infer<typeof SiteDoc>;
|
|
380
|
+
/** A `SiteDoc` as written, before defaults. */
|
|
381
|
+
type SiteDocInput = z.input<typeof SiteDoc>;
|
|
382
|
+
declare const ReleasePage: z.ZodObject<{
|
|
383
|
+
id: z.ZodString;
|
|
384
|
+
path: z.ZodString;
|
|
385
|
+
kind: z.ZodEnum<{
|
|
386
|
+
page: "page";
|
|
387
|
+
landing: "landing";
|
|
388
|
+
post: "post";
|
|
389
|
+
}>;
|
|
390
|
+
doc: z.ZodObject<{
|
|
391
|
+
schemaVersion: z.ZodLiteral<1>;
|
|
392
|
+
root: z.ZodObject<{
|
|
393
|
+
props: z.ZodObject<{
|
|
394
|
+
title: z.ZodString;
|
|
395
|
+
hideSiteChrome: z.ZodOptional<z.ZodBoolean>;
|
|
396
|
+
}, z.core.$loose>;
|
|
397
|
+
}, z.core.$strip>;
|
|
398
|
+
content: z.ZodArray<z.ZodObject<{
|
|
399
|
+
id: z.ZodString;
|
|
400
|
+
type: z.ZodString;
|
|
401
|
+
v: z.ZodNumber;
|
|
402
|
+
variant: z.ZodOptional<z.ZodString>;
|
|
403
|
+
hidden: z.ZodOptional<z.ZodBoolean>;
|
|
404
|
+
hideOn: z.ZodOptional<z.ZodArray<z.ZodEnum<{
|
|
405
|
+
mobile: "mobile";
|
|
406
|
+
desktop: "desktop";
|
|
407
|
+
}>>>;
|
|
408
|
+
placeholder: z.ZodOptional<z.ZodBoolean>;
|
|
409
|
+
props: z.ZodRecord<z.ZodString, z.ZodUnknown>;
|
|
410
|
+
}, z.core.$strip>>;
|
|
411
|
+
seo: z.ZodOptional<z.ZodObject<{
|
|
412
|
+
title: z.ZodOptional<z.ZodString>;
|
|
413
|
+
description: z.ZodOptional<z.ZodString>;
|
|
414
|
+
ogImage: z.ZodOptional<z.ZodObject<{
|
|
415
|
+
src: z.ZodString & z.ZodType<string, string, z.core.$ZodTypeInternals<string, string>>;
|
|
416
|
+
alt: z.ZodDefault<z.ZodString>;
|
|
417
|
+
decorative: z.ZodOptional<z.ZodBoolean>;
|
|
418
|
+
width: z.ZodOptional<z.ZodNumber>;
|
|
419
|
+
height: z.ZodOptional<z.ZodNumber>;
|
|
420
|
+
focal: z.ZodOptional<z.ZodObject<{
|
|
421
|
+
x: z.ZodNumber;
|
|
422
|
+
y: z.ZodNumber;
|
|
423
|
+
}, z.core.$strip>>;
|
|
424
|
+
}, z.core.$strip>>;
|
|
425
|
+
canonical: z.ZodOptional<z.ZodURL>;
|
|
426
|
+
noindex: z.ZodOptional<z.ZodBoolean>;
|
|
427
|
+
}, z.core.$strip>>;
|
|
428
|
+
}, z.core.$strip>;
|
|
429
|
+
}, z.core.$strip>;
|
|
430
|
+
type ReleasePage = z.infer<typeof ReleasePage>;
|
|
431
|
+
/** Everything a published site serves: the site document and every page, frozen at publish. */
|
|
432
|
+
declare const ReleaseSnapshot: z.ZodObject<{
|
|
433
|
+
schemaVersion: z.ZodLiteral<1>;
|
|
434
|
+
site: z.ZodObject<{
|
|
435
|
+
schemaVersion: z.ZodLiteral<1>;
|
|
436
|
+
name: z.ZodString;
|
|
437
|
+
lang: z.ZodDefault<z.ZodString>;
|
|
438
|
+
theme: z.ZodDefault<z.ZodObject<{
|
|
439
|
+
colorScheme: z.ZodDefault<z.ZodEnum<{
|
|
440
|
+
light: "light";
|
|
441
|
+
dark: "dark";
|
|
442
|
+
auto: "auto";
|
|
443
|
+
}>>;
|
|
444
|
+
overrides: z.ZodOptional<z.ZodObject<{
|
|
445
|
+
radius: z.ZodOptional<z.ZodEnum<{
|
|
446
|
+
none: "none";
|
|
447
|
+
sm: "sm";
|
|
448
|
+
md: "md";
|
|
449
|
+
lg: "lg";
|
|
450
|
+
full: "full";
|
|
451
|
+
}>>;
|
|
452
|
+
headingFont: z.ZodOptional<z.ZodEnum<{
|
|
453
|
+
system: "system";
|
|
454
|
+
inter: "inter";
|
|
455
|
+
"dm-sans": "dm-sans";
|
|
456
|
+
"work-sans": "work-sans";
|
|
457
|
+
montserrat: "montserrat";
|
|
458
|
+
poppins: "poppins";
|
|
459
|
+
"space-grotesk": "space-grotesk";
|
|
460
|
+
"source-serif": "source-serif";
|
|
461
|
+
merriweather: "merriweather";
|
|
462
|
+
"playfair-display": "playfair-display";
|
|
463
|
+
lora: "lora";
|
|
464
|
+
}>>;
|
|
465
|
+
bodyFont: z.ZodOptional<z.ZodEnum<{
|
|
466
|
+
system: "system";
|
|
467
|
+
inter: "inter";
|
|
468
|
+
"dm-sans": "dm-sans";
|
|
469
|
+
"work-sans": "work-sans";
|
|
470
|
+
montserrat: "montserrat";
|
|
471
|
+
poppins: "poppins";
|
|
472
|
+
"space-grotesk": "space-grotesk";
|
|
473
|
+
"source-serif": "source-serif";
|
|
474
|
+
merriweather: "merriweather";
|
|
475
|
+
"playfair-display": "playfair-display";
|
|
476
|
+
lora: "lora";
|
|
477
|
+
}>>;
|
|
478
|
+
}, z.core.$strip>>;
|
|
479
|
+
}, z.core.$strip>>;
|
|
480
|
+
header: z.ZodDefault<z.ZodArray<z.ZodObject<{
|
|
481
|
+
id: z.ZodString;
|
|
482
|
+
type: z.ZodString;
|
|
483
|
+
v: z.ZodNumber;
|
|
484
|
+
variant: z.ZodOptional<z.ZodString>;
|
|
485
|
+
hidden: z.ZodOptional<z.ZodBoolean>;
|
|
486
|
+
hideOn: z.ZodOptional<z.ZodArray<z.ZodEnum<{
|
|
487
|
+
mobile: "mobile";
|
|
488
|
+
desktop: "desktop";
|
|
489
|
+
}>>>;
|
|
490
|
+
placeholder: z.ZodOptional<z.ZodBoolean>;
|
|
491
|
+
props: z.ZodRecord<z.ZodString, z.ZodUnknown>;
|
|
492
|
+
}, z.core.$strip>>>;
|
|
493
|
+
nav: z.ZodDefault<z.ZodArray<z.ZodObject<{
|
|
494
|
+
label: z.ZodString;
|
|
495
|
+
href: z.ZodString & z.ZodType<string, string, z.core.$ZodTypeInternals<string, string>>;
|
|
496
|
+
}, z.core.$strip>>>;
|
|
497
|
+
footer: z.ZodDefault<z.ZodArray<z.ZodObject<{
|
|
498
|
+
id: z.ZodString;
|
|
499
|
+
type: z.ZodString;
|
|
500
|
+
v: z.ZodNumber;
|
|
501
|
+
variant: z.ZodOptional<z.ZodString>;
|
|
502
|
+
hidden: z.ZodOptional<z.ZodBoolean>;
|
|
503
|
+
hideOn: z.ZodOptional<z.ZodArray<z.ZodEnum<{
|
|
504
|
+
mobile: "mobile";
|
|
505
|
+
desktop: "desktop";
|
|
506
|
+
}>>>;
|
|
507
|
+
placeholder: z.ZodOptional<z.ZodBoolean>;
|
|
508
|
+
props: z.ZodRecord<z.ZodString, z.ZodUnknown>;
|
|
509
|
+
}, z.core.$strip>>>;
|
|
510
|
+
seo: z.ZodDefault<z.ZodObject<{
|
|
511
|
+
titleTemplate: z.ZodOptional<z.ZodString>;
|
|
512
|
+
description: z.ZodOptional<z.ZodString>;
|
|
513
|
+
ogImage: z.ZodOptional<z.ZodObject<{
|
|
514
|
+
src: z.ZodString & z.ZodType<string, string, z.core.$ZodTypeInternals<string, string>>;
|
|
515
|
+
alt: z.ZodDefault<z.ZodString>;
|
|
516
|
+
decorative: z.ZodOptional<z.ZodBoolean>;
|
|
517
|
+
width: z.ZodOptional<z.ZodNumber>;
|
|
518
|
+
height: z.ZodOptional<z.ZodNumber>;
|
|
519
|
+
focal: z.ZodOptional<z.ZodObject<{
|
|
520
|
+
x: z.ZodNumber;
|
|
521
|
+
y: z.ZodNumber;
|
|
522
|
+
}, z.core.$strip>>;
|
|
523
|
+
}, z.core.$strip>>;
|
|
524
|
+
organization: z.ZodOptional<z.ZodObject<{
|
|
525
|
+
name: z.ZodString;
|
|
526
|
+
logo: z.ZodOptional<z.ZodString & z.ZodType<string, string, z.core.$ZodTypeInternals<string, string>>>;
|
|
527
|
+
sameAs: z.ZodOptional<z.ZodArray<z.ZodURL>>;
|
|
528
|
+
}, z.core.$strip>>;
|
|
529
|
+
noindex: z.ZodOptional<z.ZodBoolean>;
|
|
530
|
+
}, z.core.$strip>>;
|
|
531
|
+
redirects: z.ZodDefault<z.ZodArray<z.ZodObject<{
|
|
532
|
+
from: z.ZodString;
|
|
533
|
+
to: z.ZodString & z.ZodType<string, string, z.core.$ZodTypeInternals<string, string>>;
|
|
534
|
+
status: z.ZodUnion<readonly [z.ZodLiteral<301>, z.ZodLiteral<302>]>;
|
|
535
|
+
}, z.core.$strip>>>;
|
|
536
|
+
}, z.core.$strip>;
|
|
537
|
+
pages: z.ZodArray<z.ZodObject<{
|
|
538
|
+
id: z.ZodString;
|
|
539
|
+
path: z.ZodString;
|
|
540
|
+
kind: z.ZodEnum<{
|
|
541
|
+
page: "page";
|
|
542
|
+
landing: "landing";
|
|
543
|
+
post: "post";
|
|
544
|
+
}>;
|
|
545
|
+
doc: z.ZodObject<{
|
|
546
|
+
schemaVersion: z.ZodLiteral<1>;
|
|
547
|
+
root: z.ZodObject<{
|
|
548
|
+
props: z.ZodObject<{
|
|
549
|
+
title: z.ZodString;
|
|
550
|
+
hideSiteChrome: z.ZodOptional<z.ZodBoolean>;
|
|
551
|
+
}, z.core.$loose>;
|
|
552
|
+
}, z.core.$strip>;
|
|
553
|
+
content: z.ZodArray<z.ZodObject<{
|
|
554
|
+
id: z.ZodString;
|
|
555
|
+
type: z.ZodString;
|
|
556
|
+
v: z.ZodNumber;
|
|
557
|
+
variant: z.ZodOptional<z.ZodString>;
|
|
558
|
+
hidden: z.ZodOptional<z.ZodBoolean>;
|
|
559
|
+
hideOn: z.ZodOptional<z.ZodArray<z.ZodEnum<{
|
|
560
|
+
mobile: "mobile";
|
|
561
|
+
desktop: "desktop";
|
|
562
|
+
}>>>;
|
|
563
|
+
placeholder: z.ZodOptional<z.ZodBoolean>;
|
|
564
|
+
props: z.ZodRecord<z.ZodString, z.ZodUnknown>;
|
|
565
|
+
}, z.core.$strip>>;
|
|
566
|
+
seo: z.ZodOptional<z.ZodObject<{
|
|
567
|
+
title: z.ZodOptional<z.ZodString>;
|
|
568
|
+
description: z.ZodOptional<z.ZodString>;
|
|
569
|
+
ogImage: z.ZodOptional<z.ZodObject<{
|
|
570
|
+
src: z.ZodString & z.ZodType<string, string, z.core.$ZodTypeInternals<string, string>>;
|
|
571
|
+
alt: z.ZodDefault<z.ZodString>;
|
|
572
|
+
decorative: z.ZodOptional<z.ZodBoolean>;
|
|
573
|
+
width: z.ZodOptional<z.ZodNumber>;
|
|
574
|
+
height: z.ZodOptional<z.ZodNumber>;
|
|
575
|
+
focal: z.ZodOptional<z.ZodObject<{
|
|
576
|
+
x: z.ZodNumber;
|
|
577
|
+
y: z.ZodNumber;
|
|
578
|
+
}, z.core.$strip>>;
|
|
579
|
+
}, z.core.$strip>>;
|
|
580
|
+
canonical: z.ZodOptional<z.ZodURL>;
|
|
581
|
+
noindex: z.ZodOptional<z.ZodBoolean>;
|
|
582
|
+
}, z.core.$strip>>;
|
|
583
|
+
}, z.core.$strip>;
|
|
584
|
+
}, z.core.$strip>>;
|
|
585
|
+
}, z.core.$strip>;
|
|
586
|
+
type ReleaseSnapshot = z.infer<typeof ReleaseSnapshot>;
|
|
587
|
+
/**
|
|
588
|
+
* Read a stored snapshot page by page: a page that no longer parses (a
|
|
589
|
+
* schema this release tightened, a document written past the checks) is
|
|
590
|
+
* left out and reported, so one bad page doesn't take the whole site down.
|
|
591
|
+
* Null only when the site document itself doesn't parse.
|
|
592
|
+
*/
|
|
593
|
+
declare function parseSnapshot(value: unknown, report?: (message: string) => void): ReleaseSnapshot | null;
|
|
594
|
+
interface ReleaseMeta {
|
|
595
|
+
id: string;
|
|
596
|
+
siteId: string;
|
|
597
|
+
number: number;
|
|
598
|
+
note: string | null;
|
|
599
|
+
createdAt: string;
|
|
600
|
+
publishedAt: string | null;
|
|
601
|
+
/** The brand the release was checked against (`brandHash`), when known. */
|
|
602
|
+
brandHash: string | null;
|
|
603
|
+
}
|
|
604
|
+
interface Release extends ReleaseMeta {
|
|
605
|
+
snapshot: ReleaseSnapshot;
|
|
606
|
+
}
|
|
607
|
+
interface PageMeta {
|
|
608
|
+
id: string;
|
|
609
|
+
siteId: string;
|
|
610
|
+
path: string;
|
|
611
|
+
kind: PageKind;
|
|
612
|
+
title: string;
|
|
613
|
+
sort: number;
|
|
614
|
+
rev: number;
|
|
615
|
+
}
|
|
616
|
+
/** The size limit of one stored document, as the database enforces it (512 KiB of JSON). */
|
|
617
|
+
declare const MAX_DOC_BYTES: number;
|
|
618
|
+
|
|
619
|
+
/**
|
|
620
|
+
* The form embed's contract with the host. A `form-embed` section names a
|
|
621
|
+
* form by its slug; the host says what that slug is, through a resolver
|
|
622
|
+
* passed to the renderer. This package assumes no forms table.
|
|
623
|
+
*
|
|
624
|
+
* - `definition`: a genesis-forms definition, sent to an endpoint. The page
|
|
625
|
+
* renders it on the server (a plain HTML form when it needs no engine,
|
|
626
|
+
* forms-web's `<Form>` and the form island otherwise), and answers are
|
|
627
|
+
* POSTed as JSON (`{ form, answers }`) to `submitUrl`: a site path (the
|
|
628
|
+
* host's own endpoint, for example one that runs genesis-forms
|
|
629
|
+
* destinations) or an https URL (another service's endpoint).
|
|
630
|
+
* - `mailto`: no form at all, a button that opens the visitor's email to
|
|
631
|
+
* `email`. The fallback for a site with no endpoint yet: nothing is
|
|
632
|
+
* stored, and a page never posts a form to `mailto:`.
|
|
633
|
+
* - `embed`: an https URL the page shows in an iframe (a hosted form).
|
|
634
|
+
*/
|
|
635
|
+
type ResolvedForm = {
|
|
636
|
+
kind: "definition";
|
|
637
|
+
/** A canonical genesis-forms definition (`defineForm(...).definition`, or a stored one). */
|
|
638
|
+
definition: unknown;
|
|
639
|
+
/** Where the form POSTs `{ form, answers }` (form: the slug): a site path or an https URL. */
|
|
640
|
+
submitUrl: string;
|
|
641
|
+
layout?: "classic" | "focus";
|
|
642
|
+
submitLabel?: string;
|
|
643
|
+
} | {
|
|
644
|
+
kind: "mailto";
|
|
645
|
+
/** The address the button writes to. */
|
|
646
|
+
email: string;
|
|
647
|
+
/** The email's subject line. */
|
|
648
|
+
subject?: string;
|
|
649
|
+
/** The button's label (default "Email us"). */
|
|
650
|
+
label?: string;
|
|
651
|
+
/** A line above the button. */
|
|
652
|
+
note?: string;
|
|
653
|
+
} | {
|
|
654
|
+
kind: "embed";
|
|
655
|
+
/** An https URL. */
|
|
656
|
+
url: string;
|
|
657
|
+
/** The iframe's accessible name. */
|
|
658
|
+
title: string;
|
|
659
|
+
/** The iframe's height in CSS pixels (default 640). */
|
|
660
|
+
height?: number;
|
|
661
|
+
};
|
|
662
|
+
interface FormsResolverContext {
|
|
663
|
+
siteId: string;
|
|
664
|
+
pagePath: string;
|
|
665
|
+
mode: "live" | "preview";
|
|
666
|
+
}
|
|
667
|
+
/** slug → the form to show, or null when the host has no such form (the section then renders nothing and logs). */
|
|
668
|
+
type FormsResolver = (slug: string, ctx: FormsResolverContext) => ResolvedForm | null | Promise<ResolvedForm | null>;
|
|
669
|
+
/** A plain email address (no display name, no `%` escapes, no header injection): what a `mailto` form may write to. */
|
|
670
|
+
declare function isPlainEmail(value: unknown): value is string;
|
|
671
|
+
/** The `mailto:` link for a mailto form, or null when its address isn't a plain one. */
|
|
672
|
+
declare function mailtoHref(form: Extract<ResolvedForm, {
|
|
673
|
+
kind: "mailto";
|
|
674
|
+
}>): string | null;
|
|
675
|
+
|
|
676
|
+
/**
|
|
677
|
+
* Blog posts: a site's collection of dated articles.
|
|
678
|
+
*
|
|
679
|
+
* A post is its own document (`PostDoc`), kept beside the pages rather than
|
|
680
|
+
* in them, with a draft and a published copy. `publishPost` checks the draft
|
|
681
|
+
* and copies it to the published copy; that copy is what visitors see, so
|
|
682
|
+
* a post goes live (and comes down) on its own, without republishing the
|
|
683
|
+
* site. The site's `post` page is the template every post renders in: at its
|
|
684
|
+
* own path it is the blog's index, and at `<path>/<slug>` it shows the post.
|
|
685
|
+
*/
|
|
686
|
+
|
|
687
|
+
/** A post's slug: lowercase letters, digits and single hyphens (`september-testing`). */
|
|
688
|
+
declare const PostSlug: z.ZodString;
|
|
689
|
+
declare const PostAuthor: z.ZodObject<{
|
|
690
|
+
name: z.ZodString;
|
|
691
|
+
url: z.ZodOptional<z.ZodString & z.ZodType<string, string, z.core.$ZodTypeInternals<string, string>>>;
|
|
692
|
+
}, z.core.$strip>;
|
|
693
|
+
declare const PostDoc: z.ZodObject<{
|
|
694
|
+
schemaVersion: z.ZodLiteral<1>;
|
|
695
|
+
title: z.ZodString;
|
|
696
|
+
excerpt: z.ZodOptional<z.ZodString>;
|
|
697
|
+
body: z.ZodObject<{
|
|
698
|
+
type: z.ZodLiteral<"doc">;
|
|
699
|
+
content: z.ZodOptional<z.ZodArray<z.ZodRecord<z.ZodString, z.ZodUnknown>>>;
|
|
700
|
+
}, z.core.$strip>;
|
|
701
|
+
cover: z.ZodOptional<z.ZodObject<{
|
|
702
|
+
src: z.ZodString & z.ZodType<string, string, z.core.$ZodTypeInternals<string, string>>;
|
|
703
|
+
alt: z.ZodDefault<z.ZodString>;
|
|
704
|
+
decorative: z.ZodOptional<z.ZodBoolean>;
|
|
705
|
+
width: z.ZodOptional<z.ZodNumber>;
|
|
706
|
+
height: z.ZodOptional<z.ZodNumber>;
|
|
707
|
+
focal: z.ZodOptional<z.ZodObject<{
|
|
708
|
+
x: z.ZodNumber;
|
|
709
|
+
y: z.ZodNumber;
|
|
710
|
+
}, z.core.$strip>>;
|
|
711
|
+
}, z.core.$strip>>;
|
|
712
|
+
author: z.ZodOptional<z.ZodObject<{
|
|
713
|
+
name: z.ZodString;
|
|
714
|
+
url: z.ZodOptional<z.ZodString & z.ZodType<string, string, z.core.$ZodTypeInternals<string, string>>>;
|
|
715
|
+
}, z.core.$strip>>;
|
|
716
|
+
category: z.ZodOptional<z.ZodString>;
|
|
717
|
+
tags: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
718
|
+
seo: z.ZodOptional<z.ZodObject<{
|
|
719
|
+
title: z.ZodOptional<z.ZodString>;
|
|
720
|
+
description: z.ZodOptional<z.ZodString>;
|
|
721
|
+
ogImage: z.ZodOptional<z.ZodObject<{
|
|
722
|
+
src: z.ZodString & z.ZodType<string, string, z.core.$ZodTypeInternals<string, string>>;
|
|
723
|
+
alt: z.ZodDefault<z.ZodString>;
|
|
724
|
+
decorative: z.ZodOptional<z.ZodBoolean>;
|
|
725
|
+
width: z.ZodOptional<z.ZodNumber>;
|
|
726
|
+
height: z.ZodOptional<z.ZodNumber>;
|
|
727
|
+
focal: z.ZodOptional<z.ZodObject<{
|
|
728
|
+
x: z.ZodNumber;
|
|
729
|
+
y: z.ZodNumber;
|
|
730
|
+
}, z.core.$strip>>;
|
|
731
|
+
}, z.core.$strip>>;
|
|
732
|
+
canonical: z.ZodOptional<z.ZodURL>;
|
|
733
|
+
noindex: z.ZodOptional<z.ZodBoolean>;
|
|
734
|
+
}, z.core.$strip>>;
|
|
735
|
+
}, z.core.$strip>;
|
|
736
|
+
type PostDoc = z.infer<typeof PostDoc>;
|
|
737
|
+
type PostDocInput = z.input<typeof PostDoc>;
|
|
738
|
+
type PostStatus = "draft" | "published";
|
|
739
|
+
/** A post as the editor lists it. */
|
|
740
|
+
interface PostMeta {
|
|
741
|
+
id: string;
|
|
742
|
+
siteId: string;
|
|
743
|
+
slug: string;
|
|
744
|
+
title: string;
|
|
745
|
+
status: PostStatus;
|
|
746
|
+
/** The draft's revision, for compare-and-swap saves. */
|
|
747
|
+
rev: number;
|
|
748
|
+
/** When it was first published (kept when it is published again), or null. */
|
|
749
|
+
publishedAt: string | null;
|
|
750
|
+
updatedAt: string;
|
|
751
|
+
}
|
|
752
|
+
/** A post's draft, for the editor. */
|
|
753
|
+
interface PostDraft extends PostMeta {
|
|
754
|
+
doc: PostDoc;
|
|
755
|
+
}
|
|
756
|
+
/** A published post, as visitors see it. */
|
|
757
|
+
interface LivePost {
|
|
758
|
+
slug: string;
|
|
759
|
+
publishedAt: string;
|
|
760
|
+
/** When its published copy last changed. */
|
|
761
|
+
updatedAt: string;
|
|
762
|
+
doc: PostDoc;
|
|
763
|
+
}
|
|
764
|
+
/** A published post in a list: everything but the body. */
|
|
765
|
+
interface PostSummary {
|
|
766
|
+
slug: string;
|
|
767
|
+
title: string;
|
|
768
|
+
excerpt?: string;
|
|
769
|
+
cover?: Image;
|
|
770
|
+
author?: z.infer<typeof PostAuthor>;
|
|
771
|
+
category?: string;
|
|
772
|
+
/** Kept out of the sitemap and the feed (`seo.noindex`, or a canonical elsewhere). */
|
|
773
|
+
noindex?: boolean;
|
|
774
|
+
publishedAt: string;
|
|
775
|
+
updatedAt: string;
|
|
776
|
+
}
|
|
777
|
+
/** A list item as a store returns it, checked: a stored row that skipped the checks is left out. */
|
|
778
|
+
declare const PostSummarySchema: z.ZodObject<{
|
|
779
|
+
slug: z.ZodString;
|
|
780
|
+
title: z.ZodString;
|
|
781
|
+
excerpt: z.ZodOptional<z.ZodString>;
|
|
782
|
+
cover: z.ZodOptional<z.ZodObject<{
|
|
783
|
+
src: z.ZodString & z.ZodType<string, string, z.core.$ZodTypeInternals<string, string>>;
|
|
784
|
+
alt: z.ZodDefault<z.ZodString>;
|
|
785
|
+
decorative: z.ZodOptional<z.ZodBoolean>;
|
|
786
|
+
width: z.ZodOptional<z.ZodNumber>;
|
|
787
|
+
height: z.ZodOptional<z.ZodNumber>;
|
|
788
|
+
focal: z.ZodOptional<z.ZodObject<{
|
|
789
|
+
x: z.ZodNumber;
|
|
790
|
+
y: z.ZodNumber;
|
|
791
|
+
}, z.core.$strip>>;
|
|
792
|
+
}, z.core.$strip>>;
|
|
793
|
+
author: z.ZodOptional<z.ZodObject<{
|
|
794
|
+
name: z.ZodString;
|
|
795
|
+
url: z.ZodOptional<z.ZodString & z.ZodType<string, string, z.core.$ZodTypeInternals<string, string>>>;
|
|
796
|
+
}, z.core.$strip>>;
|
|
797
|
+
category: z.ZodOptional<z.ZodString>;
|
|
798
|
+
noindex: z.ZodOptional<z.ZodBoolean>;
|
|
799
|
+
publishedAt: z.ZodString;
|
|
800
|
+
updatedAt: z.ZodString;
|
|
801
|
+
}, z.core.$strip>;
|
|
802
|
+
/** The most posts a list reads (the newest first). */
|
|
803
|
+
declare const MAX_LIVE_POSTS = 500;
|
|
804
|
+
/** A save of a post: a new one (no `id`), or a compare-and-swap of an existing one's draft. */
|
|
805
|
+
interface SavePostInput {
|
|
806
|
+
id?: string;
|
|
807
|
+
slug: string;
|
|
808
|
+
/** The draft revision this save started from (required with `id`). */
|
|
809
|
+
rev?: number;
|
|
810
|
+
doc: PostDocInput;
|
|
811
|
+
}
|
|
812
|
+
type SavePostResult = {
|
|
813
|
+
id: string;
|
|
814
|
+
rev: number;
|
|
815
|
+
} | {
|
|
816
|
+
id: string;
|
|
817
|
+
conflict: PostDoc;
|
|
818
|
+
rev: number;
|
|
819
|
+
};
|
|
820
|
+
/** One post to import (a move from another site): published as of `publishedAt` unless it fails a check. */
|
|
821
|
+
interface ImportPost {
|
|
822
|
+
slug: string;
|
|
823
|
+
doc: PostDocInput;
|
|
824
|
+
/** When it was first published, kept from the old site (default: now). */
|
|
825
|
+
publishedAt?: string;
|
|
826
|
+
/** `draft` imports it unpublished. Default `published`. */
|
|
827
|
+
status?: PostStatus;
|
|
828
|
+
}
|
|
829
|
+
interface ImportPostsResult {
|
|
830
|
+
/** Imported posts: published, or a draft when asked for or when a check failed (see `issues`). */
|
|
831
|
+
imported: {
|
|
832
|
+
slug: string;
|
|
833
|
+
id: string;
|
|
834
|
+
status: PostStatus;
|
|
835
|
+
issues?: string[];
|
|
836
|
+
}[];
|
|
837
|
+
/** Posts left out: a slug the site already has, or a document that doesn't parse. */
|
|
838
|
+
skipped: {
|
|
839
|
+
slug: string;
|
|
840
|
+
reason: string;
|
|
841
|
+
}[];
|
|
842
|
+
}
|
|
843
|
+
/** The most posts one `importPosts` call takes. */
|
|
844
|
+
declare const MAX_IMPORT_POSTS = 500;
|
|
845
|
+
/** A published post as a list item. */
|
|
846
|
+
declare function postSummary(post: LivePost): PostSummary;
|
|
847
|
+
/** Plain text of a rich text document (for a feed's description when a post has no excerpt). */
|
|
848
|
+
declare function richTextPlain(value: unknown, max?: number): string;
|
|
849
|
+
/** Validate a post slug, throwing with a readable message. */
|
|
850
|
+
declare function validatePostSlug(slug: string): string;
|
|
851
|
+
|
|
852
|
+
/**
|
|
853
|
+
* Sections: `defineSection` describes one kind of section (its props schema,
|
|
854
|
+
* variants, presets, migrations and server-safe React renderer), and the
|
|
855
|
+
* registry holds every kind a site can use.
|
|
856
|
+
*
|
|
857
|
+
* Modelled on genesis-forms' `registerContentType`: one registry per process,
|
|
858
|
+
* kept on a `Symbol.for` global so two copies of this package (a host's and a
|
|
859
|
+
* dependency's) share it; manifests are frozen; a host adds its own kinds and
|
|
860
|
+
* replaces a built-in only with `{ replace: true }`.
|
|
861
|
+
*
|
|
862
|
+
* Everything the editor needs comes from the definition: `label`,
|
|
863
|
+
* `category`, `variants`, `presets` and `sectionJsonSchema(type)`
|
|
864
|
+
* (`z.toJSONSchema` of the props schema) for its fields and for AI output.
|
|
865
|
+
*/
|
|
866
|
+
|
|
867
|
+
declare const SECTION_CATEGORIES: readonly ["header", "intro", "content", "social-proof", "conversion", "footer"];
|
|
868
|
+
type SectionCategory = (typeof SECTION_CATEGORIES)[number];
|
|
869
|
+
/** Where on a page a section may go: the site header, the page body, or the site footer. */
|
|
870
|
+
type SectionPlacement = "header" | "body" | "footer";
|
|
871
|
+
interface SectionVariant {
|
|
872
|
+
id: string;
|
|
873
|
+
label: string;
|
|
874
|
+
}
|
|
875
|
+
interface SectionPreset<P = Record<string, unknown>> {
|
|
876
|
+
/** Shown in the editor's Add picker. */
|
|
877
|
+
name: string;
|
|
878
|
+
variant: string;
|
|
879
|
+
props: P;
|
|
880
|
+
}
|
|
881
|
+
/** What a section's renderer is told about the page it is on. */
|
|
882
|
+
interface RenderContext {
|
|
883
|
+
/**
|
|
884
|
+
* The level of the section's title: 1 for the page's main heading (the
|
|
885
|
+
* first section that `providesH1`), 2 for every other section. Items in a
|
|
886
|
+
* section are one level below it.
|
|
887
|
+
*/
|
|
888
|
+
headingLevel: 1 | 2;
|
|
889
|
+
site: {
|
|
890
|
+
name: string;
|
|
891
|
+
lang: string;
|
|
892
|
+
nav: readonly MenuItem[];
|
|
893
|
+
logo?: {
|
|
894
|
+
src: string;
|
|
895
|
+
alt: string;
|
|
896
|
+
} | undefined;
|
|
897
|
+
};
|
|
898
|
+
/** The page's own path, such as `/about`. */
|
|
899
|
+
pagePath: string;
|
|
900
|
+
/** A stored link as the page should print it: site paths get the host's base path. */
|
|
901
|
+
href(href: string): string;
|
|
902
|
+
/** Forms on the page, resolved by slug before rendering (null: the host doesn't know the slug). */
|
|
903
|
+
forms: ReadonlyMap<string, ResolvedForm | null>;
|
|
904
|
+
mode: "live" | "preview";
|
|
905
|
+
/** A prefix that makes ids unique on the page: the section node's id. */
|
|
906
|
+
idPrefix: string;
|
|
907
|
+
/**
|
|
908
|
+
* The site's blog, when it has a post page: its path, the published posts
|
|
909
|
+
* (newest first, without bodies), the post this page shows (on
|
|
910
|
+
* `<path>/<slug>`), and the list's page number (`?page=`).
|
|
911
|
+
*/
|
|
912
|
+
posts?: PostsContext;
|
|
913
|
+
}
|
|
914
|
+
interface PostsContext {
|
|
915
|
+
/** The post page's path (`/blog`): posts live at `<path>/<slug>`. */
|
|
916
|
+
path: string;
|
|
917
|
+
list: readonly PostSummary[];
|
|
918
|
+
current: LivePost | null;
|
|
919
|
+
/** The list's page, from `?page=` (1 when absent). */
|
|
920
|
+
page: number;
|
|
921
|
+
}
|
|
922
|
+
interface SectionRenderProps<P> {
|
|
923
|
+
props: P;
|
|
924
|
+
variant: string;
|
|
925
|
+
node: SectionNode;
|
|
926
|
+
ctx: RenderContext;
|
|
927
|
+
}
|
|
928
|
+
interface SectionDefinition<P = Record<string, unknown>> {
|
|
929
|
+
/** The registry key stored in each node: lowercase, such as `hero`. */
|
|
930
|
+
type: string;
|
|
931
|
+
/** The current props version. Nodes at a lower `v` are migrated on read. */
|
|
932
|
+
version: number;
|
|
933
|
+
label: string;
|
|
934
|
+
description?: string;
|
|
935
|
+
category: SectionCategory;
|
|
936
|
+
/** The props schema (zod 4). Its input type is what an editor writes. */
|
|
937
|
+
schema: z.ZodType<P>;
|
|
938
|
+
variants: readonly SectionVariant[];
|
|
939
|
+
defaultVariant: string;
|
|
940
|
+
presets: readonly SectionPreset<z.input<z.ZodType<P>>>[];
|
|
941
|
+
/** At most this many on a page. */
|
|
942
|
+
limit?: number;
|
|
943
|
+
/** The page kinds it may appear on (default: every kind). */
|
|
944
|
+
enabledOn?: readonly PageKind[];
|
|
945
|
+
/** Where it may go (default: the page body). */
|
|
946
|
+
placement?: SectionPlacement;
|
|
947
|
+
/** Its title is the page's `h1` when it comes first (a hero). */
|
|
948
|
+
providesH1?: boolean;
|
|
949
|
+
/**
|
|
950
|
+
* `migrations[n]` turns version `n - 1` props into version `n` props. Pure
|
|
951
|
+
* functions: `migrateNode` runs them in order on read, in the editor and in
|
|
952
|
+
* the renderer, and a publish writes the result into the release.
|
|
953
|
+
*/
|
|
954
|
+
migrations?: Readonly<Record<number, (props: Record<string, unknown>) => Record<string, unknown>>>;
|
|
955
|
+
/**
|
|
956
|
+
* The https origins this section frames (a map), so the page's
|
|
957
|
+
* Content-Security-Policy allows them in `frame-src`. Only origins the
|
|
958
|
+
* section builds itself, never a URL an editor typed.
|
|
959
|
+
*/
|
|
960
|
+
frameOrigins?: (props: P) => readonly string[];
|
|
961
|
+
/** It needs the islands loader (a click-to-load map, say). */
|
|
962
|
+
islands?: boolean;
|
|
963
|
+
/** Server-safe React: no state, no effects, no browser APIs. Returning null gives the page's h1 back. */
|
|
964
|
+
render: (input: SectionRenderProps<P>) => ReactNode;
|
|
965
|
+
}
|
|
966
|
+
/** A definition as the registry holds it: props erased to a record. */
|
|
967
|
+
type AnySectionDefinition = SectionDefinition<any>;
|
|
968
|
+
declare class SectionDefinitionError extends Error {
|
|
969
|
+
readonly problems: string[];
|
|
970
|
+
constructor(problems: string[]);
|
|
971
|
+
}
|
|
972
|
+
/**
|
|
973
|
+
* Declare a section kind. Checks the definition (and every preset against its
|
|
974
|
+
* schema) and returns it frozen. It isn't registered until `registerSection`.
|
|
975
|
+
*
|
|
976
|
+
* @stability Beta
|
|
977
|
+
*/
|
|
978
|
+
declare function defineSection<P>(def: SectionDefinition<P>): Readonly<SectionDefinition<P>>;
|
|
979
|
+
/** The built-in section kinds, which a host replaces only with `{ replace: true }`. */
|
|
980
|
+
declare const BUILT_IN_SECTION_TYPES: readonly ["nav", "hero", "features", "cta", "testimonials", "faq", "pricing", "logos", "stats", "gallery", "rich-text", "form-embed", "footer", "schedule", "team", "blog-list", "post-body", "map", "timeline", "file"];
|
|
981
|
+
type BuiltInSectionType = (typeof BUILT_IN_SECTION_TYPES)[number];
|
|
982
|
+
interface RegisterSectionOptions {
|
|
983
|
+
/** Replace a built-in kind, for every site in this process. */
|
|
984
|
+
replace?: boolean;
|
|
985
|
+
}
|
|
986
|
+
/**
|
|
987
|
+
* Register a section kind (from `defineSection`, or a plain definition, which
|
|
988
|
+
* is checked the same way). Re-registering a host kind replaces it, so hot
|
|
989
|
+
* reload works; a built-in kind's name throws unless `{ replace: true }`.
|
|
990
|
+
*
|
|
991
|
+
* @stability Beta
|
|
992
|
+
*/
|
|
993
|
+
declare function registerSection(def: AnySectionDefinition, options?: RegisterSectionOptions): void;
|
|
994
|
+
/** For `@marktiderman/genesis-sites/sections`: register a built-in unless one (or a replacement) is there. @internal */
|
|
995
|
+
declare function registerBuiltInSection(def: AnySectionDefinition): void;
|
|
996
|
+
declare function getSection(type: string): AnySectionDefinition | undefined;
|
|
997
|
+
declare function listSections(): AnySectionDefinition[];
|
|
998
|
+
/** Remove a host section kind (tests, hot reload). Built-ins can't be removed, only replaced. */
|
|
999
|
+
declare function unregisterSection(type: string): boolean;
|
|
1000
|
+
/**
|
|
1001
|
+
* The JSON Schema of a section's props (`z.toJSONSchema`, input side), for the
|
|
1002
|
+
* editor's fields and for validating AI output. Refinements (safe URLs) don't
|
|
1003
|
+
* appear in JSON Schema; the zod schema still checks them on save.
|
|
1004
|
+
*
|
|
1005
|
+
* @stability Beta
|
|
1006
|
+
*/
|
|
1007
|
+
declare function sectionJsonSchema(typeOrDef: string | AnySectionDefinition): Record<string, unknown>;
|
|
1008
|
+
/** What the editor's Add picker and fields need about one section kind. */
|
|
1009
|
+
interface SectionManifest {
|
|
1010
|
+
type: string;
|
|
1011
|
+
version: number;
|
|
1012
|
+
label: string;
|
|
1013
|
+
description?: string;
|
|
1014
|
+
category: SectionCategory;
|
|
1015
|
+
placement: SectionPlacement;
|
|
1016
|
+
variants: readonly SectionVariant[];
|
|
1017
|
+
defaultVariant: string;
|
|
1018
|
+
presets: readonly SectionPreset[];
|
|
1019
|
+
limit?: number;
|
|
1020
|
+
enabledOn?: readonly PageKind[];
|
|
1021
|
+
jsonSchema: Record<string, unknown>;
|
|
1022
|
+
}
|
|
1023
|
+
/** Every registered section as editor data (no renderers): the editor's catalog. @stability Beta */
|
|
1024
|
+
declare function sectionManifests(): SectionManifest[];
|
|
1025
|
+
/** A new section id: short, alphanumeric, unique enough within a page. */
|
|
1026
|
+
declare function newSectionId(prefix?: string): string;
|
|
1027
|
+
/** A section node from a preset: the editor's Add, and templates. */
|
|
1028
|
+
declare function nodeFromPreset(type: string, presetName?: string, id?: string): SectionNode;
|
|
1029
|
+
|
|
1030
|
+
export { type SectionManifest as $, type AnySectionDefinition as A, BUILT_IN_SECTION_TYPES as B, Copy as C, PostSummarySchema as D, type PostsContext as E, FileRef as F, type RegisterSectionOptions as G, Href as H, Image as I, type Release as J, type ReleaseMeta as K, type LivePost as L, MAX_DOC_BYTES as M, ReleaseSnapshot as N, type RenderContext as O, PageDoc as P, type ResolvedForm as Q, ReleasePage as R, SectionNode as S, RichText as T, SECTION_CATEGORIES as U, type SavePostInput as V, type SavePostResult as W, type SectionCategory as X, type SectionDefinition as Y, SectionDefinitionError as Z, SectionId as _, SiteDoc as a, type SectionPlacement as a0, type SectionPreset as a1, type SectionRenderProps as a2, type SectionVariant as a3, Seo as a4, SeoDefaults as a5, Surface as a6, TokenOverrides as a7, defineSection as a8, getSection as a9, isPlainEmail as aa, listSections as ab, mailtoHref as ac, newSectionId as ad, nodeFromPreset as ae, opensInNewTab as af, parseSnapshot as ag, postSummary as ah, registerBuiltInSection as ai, registerSection as aj, richTextPlain as ak, sectionJsonSchema as al, sectionManifests as am, unregisterSection as an, validatePostSlug as ao, type PostSummary as b, Redirect as c, Link as d, type SiteDocInput as e, PageKind as f, type BuiltInSectionType as g, FontChoice as h, type FormsResolver as i, type FormsResolverContext as j, type ImportPost as k, type ImportPostsResult as l, Line as m, LinkEvent as n, MAX_IMPORT_POSTS as o, MAX_LIVE_POSTS as p, MenuItem as q, type PageMeta as r, PagePath as s, PostAuthor as t, PostDoc as u, type PostDocInput as v, type PostDraft as w, type PostMeta as x, PostSlug as y, type PostStatus as z };
|