@gradial/aci 0.1.45 → 0.1.47

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 (90) hide show
  1. package/README.md +4 -4
  2. package/dist/astro/error-page.d.ts +56 -0
  3. package/dist/astro/error-page.js +79 -0
  4. package/dist/astro/index.d.ts +4 -0
  5. package/dist/astro/index.js +9 -0
  6. package/dist/audience-data-models.d.ts +142 -2
  7. package/dist/audience-data-models.js +2 -0
  8. package/dist/cli/compile-registry.d.mts +1 -5
  9. package/dist/cli/compile-registry.mjs +10 -25
  10. package/dist/cli/emit-error-pages.d.mts +39 -0
  11. package/dist/cli/emit-error-pages.mjs +402 -0
  12. package/dist/cli/validate-content.d.mts +1 -31
  13. package/dist/cli/validate-content.mjs +12 -50
  14. package/dist/cli/verify-next-cdn-routes.mjs +1 -0
  15. package/dist/content/default-error-page-html.d.ts +2 -0
  16. package/dist/content/default-error-page-html.js +6 -0
  17. package/dist/content/default-error-page.d.ts +23 -0
  18. package/dist/content/default-error-page.js +34 -0
  19. package/dist/content/error-page-kind.d.ts +57 -0
  20. package/dist/content/error-page-kind.js +106 -0
  21. package/dist/content/error-pages.d.ts +88 -0
  22. package/dist/content/error-pages.js +120 -0
  23. package/dist/content/index.d.ts +2 -0
  24. package/dist/content/index.js +2 -0
  25. package/dist/content/provider.d.ts +1 -1
  26. package/dist/content/resolve-slots.js +0 -1
  27. package/dist/content/types.d.ts +12 -2
  28. package/dist/internal/render-proof.d.ts +1 -0
  29. package/dist/internal/render-proof.js +197 -0
  30. package/dist/next/config.js +17 -0
  31. package/dist/next/error-page-instrumentation.d.ts +23 -0
  32. package/dist/next/error-page-instrumentation.js +36 -0
  33. package/dist/next/error-page-marker.d.ts +11 -0
  34. package/dist/next/error-page-marker.js +20 -0
  35. package/dist/next/error-pages.d.ts +52 -0
  36. package/dist/next/error-pages.js +94 -0
  37. package/dist/next/error-seat-payload.d.ts +33 -0
  38. package/dist/next/error-seat-payload.js +143 -0
  39. package/dist/next/error-seat.d.ts +57 -0
  40. package/dist/next/error-seat.js +83 -0
  41. package/dist/next/index.d.ts +4 -0
  42. package/dist/next/index.js +8 -0
  43. package/dist/next/middleware.js +9 -54
  44. package/dist/next/page.js +9 -4
  45. package/dist/next/personalization-host.d.ts +5 -0
  46. package/dist/next/server.js +88 -75
  47. package/dist/next/vercel-collect-sdk-route.js +6 -30
  48. package/dist/next/vercel-edge-projection-v1.d.ts +24 -0
  49. package/dist/next/vercel-edge-projection-v1.js +880 -0
  50. package/dist/next/vercel-s3-personalization.js +7 -11
  51. package/dist/providers/s3.d.ts +2 -4
  52. package/dist/providers/s3.js +173 -19
  53. package/dist/types/block-ref.d.ts +0 -2
  54. package/dist/types/block-ref.js +0 -1
  55. package/package.json +15 -9
  56. package/src/cli/compile-registry.mjs +10 -24
  57. package/src/cli/emit-error-pages.mjs +424 -0
  58. package/src/cli/validate-content.mjs +13 -52
  59. package/src/cli/verify-next-cdn-routes.mjs +6 -0
  60. package/src/types/block-ref.ts +0 -2
  61. package/templates/astro/starter-content/src/fixtures/atomicBlocks.ts +6 -7
  62. package/templates/astro/starter-content/src/fixtures/composition.ts +2 -3
  63. package/templates/astro/starter-content/src/fixtures/sections.ts +1 -2
  64. package/templates/astro/template/package.json.tmpl +1 -1
  65. package/templates/astro/template/src/aci/error-page-instrumentation.mjs +33 -0
  66. package/templates/astro/template/src/pages/404.astro +54 -0
  67. package/templates/astro/template/src/pages/500.astro +53 -0
  68. package/templates/astro/template/src/pages/[...slug].astro +15 -2
  69. package/templates/common/.agents/skills/authoring/SKILL.md +74 -11
  70. package/templates/common/.agents/skills/authoring/references/block-slots.md +0 -3
  71. package/templates/common/.content/config/robots.txt +4 -0
  72. package/templates/common/.content/config/site.json.tmpl +4 -0
  73. package/templates/common/.content/pages/404/_index.json +31 -0
  74. package/templates/common/.content/pages/error/_index.json +31 -0
  75. package/templates/common/.content/pages/home/_index.json +0 -10
  76. package/templates/common/.content/schemas/site.schema.json +9 -0
  77. package/templates/common/starter-content/.content/config/robots.txt +2 -0
  78. package/templates/common/starter-content/.content/config/site.json +4 -0
  79. package/templates/common/starter-content/.content/pages/404/_index.json +31 -0
  80. package/templates/common/starter-content/.content/pages/about/_index.json +0 -3
  81. package/templates/common/starter-content/.content/pages/error/_index.json +31 -0
  82. package/templates/common/starter-content/.content/pages/home/_index.json +21 -14
  83. package/templates/common/starter-content/.content/schemas/site.schema.json +9 -0
  84. package/templates/nextjs/template/package.json.tmpl +1 -1
  85. package/templates/nextjs/template/src/aci/error-page-instrumentation.d.mts +4 -0
  86. package/templates/nextjs/template/src/aci/error-page-instrumentation.mjs +38 -0
  87. package/templates/nextjs/template/src/app/error.tsx +18 -0
  88. package/templates/nextjs/template/src/app/not-found.tsx +13 -0
  89. package/src/types/block-ref.test.ts +0 -45
  90. package/templates/nextjs/template/src/app/collect/route.ts +0 -18
@@ -0,0 +1,54 @@
1
+ ---
2
+ // Astro's native notFound error seat (authorable-error-pages REQ-7).
3
+ // Renders the page bound by site.json#errorPages.notFound through the normal
4
+ // SiteLayout path; static builds emit dist/404.html, which doubles as the
5
+ // direct /404/ route and the error surface (D-11 — the instrumentation on a
6
+ // direct visit is the accepted consequence). If the bound page cannot be
7
+ // loaded, the SDK degrades to its shipped default fragment — this seat never
8
+ // throws.
9
+ import { getErrorPageData, createRenderChildren } from '@gradial/aci/astro';
10
+ import { registry } from '../cms/registry';
11
+ import SiteLayout from '../layouts/SiteLayout.astro';
12
+ import { pushErrorPageView } from '../aci/error-page-instrumentation.mjs';
13
+
14
+ const errorPage = await getErrorPageData(Astro, 'notFound', { pushErrorPageView });
15
+ const data = errorPage.data;
16
+ const renderChildren = createRenderChildren(registry);
17
+ ---
18
+
19
+ <SiteLayout
20
+ title={errorPage.meta.title}
21
+ description={errorPage.meta.description}
22
+ canonical={errorPage.meta.canonical}
23
+ siteName={errorPage.meta.siteName}
24
+ >
25
+ {data ? (
26
+ data.layout.slots.map((slot) => {
27
+ const blocks = data.slots[slot.name];
28
+ if (!blocks?.length) return null;
29
+ const rendered = renderChildren(blocks);
30
+ if (slot.name === 'main') {
31
+ return (
32
+ <main id="content-main">
33
+ {rendered.map((child) => {
34
+ const Comp = child.Component as any;
35
+ return Comp ? <Comp {...child.props} /> : null;
36
+ })}
37
+ </main>
38
+ );
39
+ }
40
+ return rendered.map((child) => {
41
+ const Comp = child.Component as any;
42
+ return Comp ? <Comp {...child.props} /> : null;
43
+ });
44
+ })
45
+ ) : (
46
+ <Fragment set:html={errorPage.fallbackHtml} />
47
+ )}
48
+ <!-- D-14 error-surface analytics: ONE page_view push with the visitor's
49
+ requested path. The push body is site-owned (D-15): the SDK serializes
50
+ src/aci/error-page-instrumentation.mjs into this tag, injected like
51
+ SiteLayout's collectTag; inline scripts execute here — Astro serves
52
+ real HTML documents (unlike the Next flight-rendered seat). -->
53
+ <Fragment set:html={errorPage.instrumentationTag} />
54
+ </SiteLayout>
@@ -0,0 +1,53 @@
1
+ ---
2
+ // Astro's native catchAll (500) error seat (authorable-error-pages REQ-7).
3
+ // Renders the page bound by site.json#errorPages.catchAll through the normal
4
+ // SiteLayout path. Static builds emit dist/500.html for the hosting layer;
5
+ // server output serves this seat when a route throws during on-demand
6
+ // rendering. If the bound page cannot be loaded, the SDK degrades to its
7
+ // shipped default fragment — this seat never throws.
8
+ import { getErrorPageData, createRenderChildren } from '@gradial/aci/astro';
9
+ import { registry } from '../cms/registry';
10
+ import SiteLayout from '../layouts/SiteLayout.astro';
11
+ import { pushErrorPageView } from '../aci/error-page-instrumentation.mjs';
12
+
13
+ const errorPage = await getErrorPageData(Astro, 'catchAll', { pushErrorPageView });
14
+ const data = errorPage.data;
15
+ const renderChildren = createRenderChildren(registry);
16
+ ---
17
+
18
+ <SiteLayout
19
+ title={errorPage.meta.title}
20
+ description={errorPage.meta.description}
21
+ canonical={errorPage.meta.canonical}
22
+ siteName={errorPage.meta.siteName}
23
+ >
24
+ {data ? (
25
+ data.layout.slots.map((slot) => {
26
+ const blocks = data.slots[slot.name];
27
+ if (!blocks?.length) return null;
28
+ const rendered = renderChildren(blocks);
29
+ if (slot.name === 'main') {
30
+ return (
31
+ <main id="content-main">
32
+ {rendered.map((child) => {
33
+ const Comp = child.Component as any;
34
+ return Comp ? <Comp {...child.props} /> : null;
35
+ })}
36
+ </main>
37
+ );
38
+ }
39
+ return rendered.map((child) => {
40
+ const Comp = child.Component as any;
41
+ return Comp ? <Comp {...child.props} /> : null;
42
+ });
43
+ })
44
+ ) : (
45
+ <Fragment set:html={errorPage.fallbackHtml} />
46
+ )}
47
+ <!-- D-14 error-surface analytics: ONE page_view push with the visitor's
48
+ requested path. The push body is site-owned (D-15): the SDK serializes
49
+ src/aci/error-page-instrumentation.mjs into this tag, injected like
50
+ SiteLayout's collectTag; inline scripts execute here — Astro serves
51
+ real HTML documents (unlike the Next flight-rendered seat). -->
52
+ <Fragment set:html={errorPage.instrumentationTag} />
53
+ </SiteLayout>
@@ -1,5 +1,5 @@
1
1
  ---
2
- import { getGradialStaticPaths, getPageData, createRenderChildren } from '@gradial/aci/astro';
2
+ import { getGradialStaticPaths, getPageData, createRenderChildren, PageNotFoundError, type PageData } from '@gradial/aci/astro';
3
3
  import { registry } from '../cms/registry';
4
4
  import SiteLayout from '../layouts/SiteLayout.astro';
5
5
 
@@ -7,7 +7,20 @@ export const getStaticPaths = getGradialStaticPaths;
7
7
 
8
8
  // SDK owns route normalization, content loading, metadata, layout/slot
9
9
  // resolution, and collect-tag emission. No FileContentProvider imports.
10
- const data = await getPageData(Astro);
10
+ //
11
+ // Server output: unknown routes raise PageNotFoundError here; rewrite to the
12
+ // 404 seat so misses answer HTTP 404, not 500 (authorable-error-pages D-10).
13
+ // src/pages/404.astro is load-bearing for this rewrite — deleting it turns
14
+ // every miss into a 500.
15
+ // Static output never takes this branch — getStaticPaths only emits known
16
+ // routes.
17
+ let data: PageData;
18
+ try {
19
+ data = await getPageData(Astro);
20
+ } catch (error) {
21
+ if (error instanceof PageNotFoundError) return Astro.rewrite('/404/');
22
+ throw error;
23
+ }
11
24
  const renderChildren = createRenderChildren(registry);
12
25
  ---
13
26
 
@@ -98,7 +98,6 @@ The `home` slug is special-cased to route `/`. All other slugs map directly.
98
98
 
99
99
  ```json
100
100
  {
101
- "$type": "component",
102
101
  "id": "hero-section",
103
102
  "component": "home_hero",
104
103
  "props": {
@@ -110,7 +109,6 @@ The `home` slug is special-cased to route `/`. All other slugs map directly.
110
109
  ```
111
110
 
112
111
  - `component` must match a contract `id` exactly.
113
- - Every embedded block must declare `"$type": "component"`.
114
112
  - `props` must validate against the contract's Zod schema.
115
113
  - `id` is unique within the page.
116
114
 
@@ -143,14 +141,12 @@ export const containerContract = defineComponentContract({
143
141
  **Content JSON:**
144
142
  ```json
145
143
  {
146
- "$type": "component",
147
144
  "id": "cta-section",
148
145
  "component": "container",
149
146
  "props": {
150
147
  "className": "space-y-4",
151
148
  "content": [
152
149
  {
153
- "$type": "component",
154
150
  "id": "primary-cta",
155
151
  "component": "button",
156
152
  "props": {
@@ -160,7 +156,6 @@ export const containerContract = defineComponentContract({
160
156
  }
161
157
  },
162
158
  {
163
- "$type": "component",
164
159
  "id": "secondary-cta",
165
160
  "component": "button",
166
161
  "props": {
@@ -187,7 +182,6 @@ The `button` component is a common atomic block:
187
182
 
188
183
  ```json
189
184
  {
190
- "$type": "component",
191
185
  "id": "cta-button",
192
186
  "component": "button",
193
187
  "props": {
@@ -244,6 +238,77 @@ Global elements (navigation, footer, branding, SEO) live in
244
238
  }
245
239
  ```
246
240
 
241
+ ### Error Pages
242
+
243
+ Sites bind their error pages in `site.json` via `errorPages`:
244
+
245
+ ```json
246
+ {
247
+ "errorPages": {
248
+ "notFound": "/404/",
249
+ "catchAll": "/error/"
250
+ }
251
+ }
252
+ ```
253
+
254
+ - Each binding is a route path resolving to an **ordinary content page**
255
+ (`.content/pages/404/_index.json`, `.content/pages/error/_index.json`) —
256
+ authored with normal layouts and components like any other page.
257
+ `notFound` serves unknown routes with HTTP 404; `catchAll` serves
258
+ application errors (500-class).
259
+ - Error pages stay out of discovery: set `metadata.sitemap.visible: false`
260
+ on both pages, and the template `robots.txt` disallows both routes.
261
+ - If `errorPages` is absent, the build warns loudly and ships a
262
+ branded-neutral default page (warn-then-enforce migration: absent
263
+ bindings will eventually fail the build, so author them). Present but
264
+ broken bindings — malformed paths, no matching page, unpublished page —
265
+ fail the build immediately.
266
+ - Error pages are base-audience only: no personalization, audience, or
267
+ experiment overlays (validation rejects them), and collect/assignment
268
+ markup is stripped from materialized copies.
269
+ - Analytics: error-surface renders push one `page_view` event to
270
+ `window.dataLayer` with `page_type` (`not_found` | `error`),
271
+ `page_path` (the visitor's requested path), `page_location`,
272
+ `page_referrer`, and `page_title`. The push body is site-owned
273
+ (`src/aci/error-page-instrumentation.mjs` — see the GTM/GA4 section
274
+ below); the SDK wrappers handle delivery.
275
+
276
+ ### Error-page analytics (GTM/GA4)
277
+
278
+ The push body is YOURS: it lives in the site-owned
279
+ `src/aci/error-page-instrumentation.mjs`, whose `pushErrorPageView`
280
+ function every error surface (Next seats, Astro seats, the emitted static
281
+ `500.html`) executes. The scaffolded default pushes one event to
282
+ `window.dataLayer`:
283
+
284
+ ```js
285
+ { event: 'page_view', page_type: 'not_found' | 'error',
286
+ page_path, page_location, page_referrer, page_title }
287
+ ```
288
+
289
+ - **Customize** — edit the function in place. It must stay self-contained
290
+ (browser globals only, no imports, no closed-over identifiers): some
291
+ surfaces execute it stringified.
292
+ - **Disable** — make the function body a no-op. That turns off error-page
293
+ analytics on every surface, and no GTM-side suppression is needed.
294
+
295
+ Container-side dedup is only needed when you KEEP the default push
296
+ alongside automatic GA4 page views: because the event name is `page_view`,
297
+ either disable `send_page_view` and drive all page views from the
298
+ dataLayer, or add a trigger exception on the config tag for error
299
+ surfaces. The stable suppression signal is the `data-aci-error-page`
300
+ attribute (values `not_found` | `error`; the attribute is SDK-owned and
301
+ stable regardless of what your push does): present on the instrumentation
302
+ script tag (Astro, parse-time) and on a server-rendered marker element
303
+ (Next, in the DOM before container load), so container-load triggers can
304
+ gate on it in both frameworks. If you leave automatic page views on and
305
+ also keep the default push, error pages will double-count — that is a
306
+ container configuration choice, not an SDK default.
307
+
308
+ Direct visits to `/404/` and `/error/` do not carry error context and do
309
+ not push the event (Astro static output is the one exception: its
310
+ `404.html` doubles as the direct route).
311
+
247
312
  ### ID Conventions
248
313
 
249
314
  - Every block must have a unique `id` within the page.
@@ -417,7 +482,6 @@ framework.
417
482
  ```json
418
483
  // .content/pages/home/_index.json (inside regions.main)
419
484
  {
420
- "$type": "component",
421
485
  "id": "features",
422
486
  "component": "feature_showcase",
423
487
  "props": {
@@ -506,10 +570,9 @@ export default [
506
570
  ];
507
571
  ```
508
572
 
509
- Fragment content lives in `.content/fragments/{id}/_index.json` and retains
510
- the resource envelope `"$type": "fragment"` while declaring `component` +
511
- `props`. Chrome components (Header, Footer) are registered in the registry
512
- alongside page components.
573
+ Fragment content lives in `.content/fragments/{id}/_index.json` and has
574
+ a `component` + `props` just like page blocks. Chrome components (Header,
575
+ Footer) are registered in the registry alongside page components.
513
576
 
514
577
  The `layout` field in page JSON is **required** — there is no implicit
515
578
  default. Every page must declare which layout it uses.
@@ -113,14 +113,12 @@ type RenderChildren<T extends readonly BlockRef[]> = (blocks: T) => Promise<Reac
113
113
 
114
114
  ```json
115
115
  {
116
- "$type": "component",
117
116
  "id": "cta-section",
118
117
  "component": "container",
119
118
  "props": {
120
119
  "theme": "dark",
121
120
  "blocks": [
122
121
  {
123
- "$type": "component",
124
122
  "id": "primary-cta",
125
123
  "component": "callout",
126
124
  "props": {
@@ -129,7 +127,6 @@ type RenderChildren<T extends readonly BlockRef[]> = (blocks: T) => Promise<Reac
129
127
  }
130
128
  },
131
129
  {
132
- "$type": "component",
133
130
  "id": "heading",
134
131
  "component": "heading",
135
132
  "props": {
@@ -0,0 +1,4 @@
1
+ User-agent: *
2
+ Allow: /
3
+ Disallow: /404/
4
+ Disallow: /error/
@@ -6,6 +6,10 @@
6
6
  "domain": "localhost",
7
7
  "defaultLocale": "en-us",
8
8
  "locales": ["en-us"],
9
+ "errorPages": {
10
+ "notFound": "/404/",
11
+ "catchAll": "/error/"
12
+ },
9
13
  "overlayResolution": ["locale"],
10
14
  "brandHref": "/",
11
15
  "seo": {
@@ -0,0 +1,31 @@
1
+ {
2
+ "$type": "page",
3
+ "id": "meridian-not-found",
4
+ "status": "published",
5
+ "layout": "default",
6
+ "metadata": {
7
+ "title": "Page not found — Meridian",
8
+ "description": "The page you are looking for does not exist.",
9
+ "sitemap": { "visible": false }
10
+ },
11
+ "regions": {
12
+ "main": [
13
+ {
14
+ "id": "not-found-hero",
15
+ "component": "home_hero",
16
+ "props": {
17
+ "eyebrow": "404",
18
+ "headline": "This page drifted out of orbit.",
19
+ "description": "The page you are looking for does not exist or has moved. Check the address, or head back to the homepage.",
20
+ "ctaLabel": "Back to home",
21
+ "ctaHref": "/",
22
+ "image": {
23
+ "$type": "dam.assetRef",
24
+ "assetId": "ast_site_home_hero",
25
+ "alt": "Light abstract background with satellite constellation"
26
+ }
27
+ }
28
+ }
29
+ ]
30
+ }
31
+ }
@@ -0,0 +1,31 @@
1
+ {
2
+ "$type": "page",
3
+ "id": "meridian-error",
4
+ "status": "published",
5
+ "layout": "default",
6
+ "metadata": {
7
+ "title": "Something went wrong — Meridian",
8
+ "description": "An unexpected error occurred while serving this page.",
9
+ "sitemap": { "visible": false }
10
+ },
11
+ "regions": {
12
+ "main": [
13
+ {
14
+ "id": "error-hero",
15
+ "component": "home_hero",
16
+ "props": {
17
+ "eyebrow": "Temporary interruption",
18
+ "headline": "Something went wrong on our end.",
19
+ "description": "An unexpected error occurred while serving this page. Please try again in a moment, or head back to the homepage.",
20
+ "ctaLabel": "Back to home",
21
+ "ctaHref": "/",
22
+ "image": {
23
+ "$type": "dam.assetRef",
24
+ "assetId": "ast_site_home_hero",
25
+ "alt": "Light abstract background with satellite constellation"
26
+ }
27
+ }
28
+ }
29
+ ]
30
+ }
31
+ }
@@ -11,7 +11,6 @@
11
11
  "regions": {
12
12
  "main": [
13
13
  {
14
- "$type": "component",
15
14
  "id": "hero",
16
15
  "component": "home_hero",
17
16
  "props": {
@@ -28,7 +27,6 @@
28
27
  }
29
28
  },
30
29
  {
31
- "$type": "component",
32
30
  "id": "why-meridian",
33
31
  "component": "feature_grid",
34
32
  "props": {
@@ -36,7 +34,6 @@
36
34
  "subtitle": "Engineered for the edges of the world.",
37
35
  "features": [
38
36
  {
39
- "$type": "component",
40
37
  "id": "global-coverage",
41
38
  "component": "feature_card",
42
39
  "props": {
@@ -46,7 +43,6 @@
46
43
  }
47
44
  },
48
45
  {
49
- "$type": "component",
50
46
  "id": "low-latency",
51
47
  "component": "feature_card",
52
48
  "props": {
@@ -56,7 +52,6 @@
56
52
  }
57
53
  },
58
54
  {
59
- "$type": "component",
60
55
  "id": "easy-setup",
61
56
  "component": "feature_card",
62
57
  "props": {
@@ -69,7 +64,6 @@
69
64
  }
70
65
  },
71
66
  {
72
- "$type": "component",
73
67
  "id": "personalization-choice",
74
68
  "component": "personalization_choice",
75
69
  "props": {
@@ -81,7 +75,6 @@
81
75
  }
82
76
  },
83
77
  {
84
- "$type": "component",
85
78
  "id": "stats",
86
79
  "component": "stats_bar",
87
80
  "props": {
@@ -94,7 +87,6 @@
94
87
  }
95
88
  },
96
89
  {
97
- "$type": "component",
98
90
  "id": "cta",
99
91
  "component": "container",
100
92
  "props": {
@@ -102,7 +94,6 @@
102
94
  "className": "items-center justify-center text-center",
103
95
  "content": [
104
96
  {
105
- "$type": "component",
106
97
  "id": "cta-primary",
107
98
  "component": "button",
108
99
  "props": {
@@ -114,7 +105,6 @@
114
105
  }
115
106
  },
116
107
  {
117
- "$type": "component",
118
108
  "id": "cta-secondary",
119
109
  "component": "button",
120
110
  "props": {
@@ -7,6 +7,15 @@
7
7
  "additionalProperties": true,
8
8
  "properties": {
9
9
  "$type": { "const": "site" },
10
+ "errorPages": {
11
+ "type": "object",
12
+ "required": ["notFound", "catchAll"],
13
+ "additionalProperties": false,
14
+ "properties": {
15
+ "notFound": { "type": "string", "pattern": "^/(?:[A-Za-z0-9._~-]+/)+$" },
16
+ "catchAll": { "type": "string", "pattern": "^/(?:[A-Za-z0-9._~-]+/)+$" }
17
+ }
18
+ },
10
19
  "id": { "type": "string", "minLength": 1 },
11
20
  "status": { "type": "string", "minLength": 1 },
12
21
  "title": { "type": "string", "minLength": 1 },
@@ -1,2 +1,4 @@
1
1
  User-agent: *
2
2
  Allow: /
3
+ Disallow: /404/
4
+ Disallow: /error/
@@ -6,6 +6,10 @@
6
6
  "domain": "www.meridian.local",
7
7
  "defaultLocale": "en-us",
8
8
  "locales": ["en-us"],
9
+ "errorPages": {
10
+ "notFound": "/404/",
11
+ "catchAll": "/error/"
12
+ },
9
13
  "overlayResolution": ["locale", "audience"],
10
14
  "brandHref": "/",
11
15
  "brandLogo": {
@@ -0,0 +1,31 @@
1
+ {
2
+ "$type": "page",
3
+ "id": "meridian-not-found",
4
+ "status": "published",
5
+ "layout": "default",
6
+ "metadata": {
7
+ "title": "Page not found — Meridian",
8
+ "description": "The page you are looking for does not exist.",
9
+ "sitemap": { "visible": false }
10
+ },
11
+ "regions": {
12
+ "main": [
13
+ {
14
+ "id": "not-found-hero",
15
+ "component": "home_hero",
16
+ "props": {
17
+ "eyebrow": "404",
18
+ "headline": "This page drifted out of orbit.",
19
+ "description": "The page you are looking for does not exist or has moved. Check the address, or head back to the homepage.",
20
+ "ctaLabel": "Back to home",
21
+ "ctaHref": "/",
22
+ "image": {
23
+ "$type": "dam.assetRef",
24
+ "assetId": "ast_site_home_hero",
25
+ "alt": "Light abstract background with satellite constellation"
26
+ }
27
+ }
28
+ }
29
+ ]
30
+ }
31
+ }
@@ -11,7 +11,6 @@
11
11
  "regions": {
12
12
  "main": [
13
13
  {
14
- "$type": "component",
15
14
  "id": "about-hero",
16
15
  "component": "home_hero",
17
16
  "props": {
@@ -28,14 +27,12 @@
28
27
  }
29
28
  },
30
29
  {
31
- "$type": "component",
32
30
  "id": "about-actions",
33
31
  "component": "container",
34
32
  "props": {
35
33
  "className": "items-center justify-center text-center",
36
34
  "content": [
37
35
  {
38
- "$type": "component",
39
36
  "id": "home-link",
40
37
  "component": "button",
41
38
  "props": {
@@ -0,0 +1,31 @@
1
+ {
2
+ "$type": "page",
3
+ "id": "meridian-error",
4
+ "status": "published",
5
+ "layout": "default",
6
+ "metadata": {
7
+ "title": "Something went wrong — Meridian",
8
+ "description": "An unexpected error occurred while serving this page.",
9
+ "sitemap": { "visible": false }
10
+ },
11
+ "regions": {
12
+ "main": [
13
+ {
14
+ "id": "error-hero",
15
+ "component": "home_hero",
16
+ "props": {
17
+ "eyebrow": "Temporary interruption",
18
+ "headline": "Something went wrong on our end.",
19
+ "description": "An unexpected error occurred while serving this page. Please try again in a moment, or head back to the homepage.",
20
+ "ctaLabel": "Back to home",
21
+ "ctaHref": "/",
22
+ "image": {
23
+ "$type": "dam.assetRef",
24
+ "assetId": "ast_site_home_hero",
25
+ "alt": "Light abstract background with satellite constellation"
26
+ }
27
+ }
28
+ }
29
+ ]
30
+ }
31
+ }