@taprootio/site-authoring 0.2.0 → 0.4.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/README.md +106 -13
- package/examples/riverbend-wellness/manifest.fixture.json +79 -0
- package/examples/riverbend-wellness/nav.json +26 -0
- package/examples/riverbend-wellness/pages/index.md +93 -0
- package/examples/riverbend-wellness/pages/visit.md +33 -0
- package/examples/riverbend-wellness/redirects.json +33 -0
- package/examples/riverbend-wellness/settings/brand.json +7 -0
- package/examples/riverbend-wellness/settings/site-header.json +21 -0
- package/examples/riverbend-wellness/settings/site-publishing-preferences.json +107 -0
- package/examples/riverbend-wellness/settings/taproot-styles.json +730 -0
- package/package.json +2 -1
- package/src/api.js +203 -3
- package/src/cli-release.js +152 -0
- package/src/cli.js +120 -11
- package/src/constants.js +59 -2
- package/src/contact-url.js +63 -0
- package/src/content/components.js +26 -4
- package/src/content/free-form-section-registry.json +3 -2
- package/src/content/inline-facts.js +5 -1
- package/src/content/tiptap-prosemirror.ts +26 -12
- package/src/credentials.js +39 -4
- package/src/errors.js +25 -0
- package/src/fixture-contract.js +64 -0
- package/src/footer-contract.js +28 -30
- package/src/json-path-diff.js +167 -0
- package/src/output.js +9 -0
- package/src/presentation-reference.js +5 -3
- package/src/redirects-contract.js +444 -0
- package/src/redirects-workspace.js +72 -0
- package/src/reference-help.js +262 -10
- package/src/session.js +161 -7
- package/src/transport.js +111 -6
- package/src/verbs/approve.js +7 -2
- package/src/verbs/deploy.js +29 -2
- package/src/verbs/footer-push.js +7 -2
- package/src/verbs/index.js +7 -1
- package/src/verbs/login.js +11 -1
- package/src/verbs/media-upload.js +7 -2
- package/src/verbs/nav-push.js +17 -11
- package/src/verbs/pages-push.js +93 -8
- package/src/verbs/preview-page.js +7 -2
- package/src/verbs/preview-revoke.js +7 -2
- package/src/verbs/pull.js +357 -22
- package/src/verbs/redirects-pull.js +90 -0
- package/src/verbs/redirects-push.js +100 -0
- package/src/verbs/status.js +90 -3
- package/src/verbs/theme-push.js +7 -2
- package/src/verbs/validate.js +93 -18
- package/src/verbs/whoami.js +79 -1
- package/src/workspace.js +153 -15
package/README.md
CHANGED
|
@@ -34,13 +34,50 @@ Node 22 or later. The package has one runtime dependency,
|
|
|
34
34
|
against one Espalier theme contract, and a floating range would let an install
|
|
35
35
|
resolve a different contract than the CLI was tested against.
|
|
36
36
|
|
|
37
|
+
## Keeping the CLI current
|
|
38
|
+
|
|
39
|
+
Taproot accepts only the latest published release of this package. There is no
|
|
40
|
+
compatibility window: the contract moves with every release, and a CLI behind
|
|
41
|
+
the current one is refused at sign-in and at every online verb that exchanges
|
|
42
|
+
the sign-in for a site credential, before it validates or writes anything.
|
|
43
|
+
`TAPROOT_SITE_KEY` runs perform no sign-in and no exchange, so they are not
|
|
44
|
+
version-gated and their `status` reports `cliRelease.latestKnown: false`; keep
|
|
45
|
+
an automation's CLI current yourself.
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
npm install -g @taprootio/site-authoring@latest
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
A refusal names the field `CliUpgradeRequired`, classifies as
|
|
52
|
+
`refusal: "cli_outdated"`, and carries both versions and that command. Nothing
|
|
53
|
+
is wrong with the credential, the request, or the site, and no retry of an
|
|
54
|
+
outdated version succeeds. If the newer release is only minutes old, npm may
|
|
55
|
+
not serve it yet — wait a moment and run the upgrade again.
|
|
56
|
+
|
|
57
|
+
Each sign-in exchange also reports the latest published release. `status` shows
|
|
58
|
+
it as `cliRelease`, `whoami` reports what the last exchange recorded (dated with
|
|
59
|
+
that exchange), and once a newer release has been recorded the offline verbs —
|
|
60
|
+
`help`, `validate`, and `whoami` — refuse with the same message rather than
|
|
61
|
+
letting an agent keep authoring against a contract that has moved. With nothing
|
|
62
|
+
recorded they run, so a clean machine can still validate a fixture.
|
|
63
|
+
`taproot-site --version` and `--help` always answer.
|
|
64
|
+
|
|
65
|
+
Do not pin this package's version in a script. Every release retires the one
|
|
66
|
+
before it, so a pin is a scheduled outage.
|
|
67
|
+
|
|
68
|
+
**0.4.0 introduces this gate.** From the API deploy that ships with it, every
|
|
69
|
+
CLI older than the release that API was built with is refused with
|
|
70
|
+
`CliUpgradeRequired`. The release order is: merge to private `main`, then the
|
|
71
|
+
npm publish of 0.4.0, then the API deploy — publishing first is what keeps the
|
|
72
|
+
upgrade available to anyone the deploy refuses.
|
|
73
|
+
|
|
37
74
|
## First commands
|
|
38
75
|
|
|
39
76
|
`taproot-site --help` lists every verb and the configuration contract.
|
|
40
77
|
`taproot-site help` is the offline reference family agents read before
|
|
41
78
|
authoring: page types, components, navigation, media, previews, themes,
|
|
42
|
-
appearance, and the
|
|
43
|
-
output.
|
|
79
|
+
appearance, the footer, and the offline fixture contract, each with `--json`
|
|
80
|
+
for stable machine-readable output.
|
|
44
81
|
|
|
45
82
|
```bash
|
|
46
83
|
taproot-site help
|
|
@@ -50,7 +87,8 @@ taproot-site help component hero-section --json
|
|
|
50
87
|
|
|
51
88
|
`taproot-site validate <fixture-directory>` checks a complete offline fixture
|
|
52
89
|
with no credential, no network, and no write: a directory laid out like a
|
|
53
|
-
pulled workspace — `pages/`, `nav.json`, and the four
|
|
90
|
+
pulled workspace — `pages/`, `nav.json`, `redirects.json`, and the four
|
|
91
|
+
`settings/` documents —
|
|
54
92
|
whose `manifest.fixture.json` binds deterministic page, resource, image, and
|
|
55
93
|
settings identities under a versioned `fixture` block. It proves structure,
|
|
56
94
|
page content and theme contexts, navigation references, the complete theme,
|
|
@@ -58,6 +96,23 @@ appearance, header, brand, and footer, and fixture-local image identities. It
|
|
|
58
96
|
does not prove authorization, site ownership, concurrency, server round trips,
|
|
59
97
|
or rendering; a real pull and an authorized preview do that.
|
|
60
98
|
|
|
99
|
+
The package ships one, so `validate` is runnable the moment the install
|
|
100
|
+
finishes. `taproot-site help fixture` states the manifest contract and prints
|
|
101
|
+
the shipped fixture's absolute path along with the exact command to validate
|
|
102
|
+
it:
|
|
103
|
+
|
|
104
|
+
```bash
|
|
105
|
+
taproot-site help fixture
|
|
106
|
+
taproot-site validate "$(npm root --global)/@taprootio/site-authoring/examples/riverbend-wellness"
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
The fixture is a fictional wellness studio — `example.test` hostnames, a
|
|
110
|
+
reserved `555-01xx` telephone number, an invented street and town — with two
|
|
111
|
+
free-form pages, a three-item navigation tree, and all four settings
|
|
112
|
+
documents. Nothing in it describes a real business. Copy the directory
|
|
113
|
+
somewhere writable before you edit it; `validate` never writes to the fixture
|
|
114
|
+
it reads.
|
|
115
|
+
|
|
61
116
|
## Authorize
|
|
62
117
|
|
|
63
118
|
Sign in once. `login` starts a device-style authorization: it prints an
|
|
@@ -169,9 +224,12 @@ taproot-site deploy --staging
|
|
|
169
224
|
taproot-site deploy --production
|
|
170
225
|
```
|
|
171
226
|
|
|
172
|
-
`pull` snapshots pages, navigation, and settings into the
|
|
173
|
-
`pages/`, `nav.json`, `settings/`, `media/`, and
|
|
174
|
-
that carry site binding and page identity between
|
|
227
|
+
`pull` snapshots pages, navigation, redirects, and settings into the
|
|
228
|
+
workspace: `pages/`, `nav.json`, `redirects.json`, `settings/`, `media/`, and
|
|
229
|
+
two dot-prefixed manifests that carry site binding and page identity between
|
|
230
|
+
commands. Against a Taproot that does not serve the redirect map yet, `pull`
|
|
231
|
+
writes no `redirects.json` and records no redirect baseline, and
|
|
232
|
+
`redirects push` asks for a pull once the site has one. Every page path
|
|
175
233
|
has exactly one authoritative source — `pages/<name>.md` in the documented
|
|
176
234
|
Markdown subset with `title`, `path`, and `description` front matter, or
|
|
177
235
|
`pages/<name>.pm.json` as a ProseMirror document — and pull never writes a
|
|
@@ -182,11 +240,18 @@ edited on the site since the last pull is a `pages.pull_conflict` refusal
|
|
|
182
240
|
before anything under `pages/` changes, with both recovery paths named.
|
|
183
241
|
|
|
184
242
|
`pages push [page-path...]` validates and sends the selected pages, or every
|
|
185
|
-
workspace page when none is named; the homepage is addressed as `/`.
|
|
243
|
+
workspace page when none is named; the homepage is addressed as `/`. It fails
|
|
244
|
+
closed with `pages.push_conflict` when a page's stored-state revision has
|
|
245
|
+
moved since this workspace last reconciled with it, naming both revisions;
|
|
246
|
+
`pull` first, or push after a refused pull has shown you the site's version.
|
|
186
247
|
`media upload [path...]` uploads raster files and records component-ready
|
|
187
248
|
delivery fields. `nav push`, `theme push`, and `footer push` replace the whole
|
|
188
249
|
navigation tree, the complete light/dark theme pair with its appearance
|
|
189
|
-
settings, and the closed footer document.
|
|
250
|
+
settings, and the closed footer document. `redirects pull` writes the site's
|
|
251
|
+
redirect map to `redirects.json` with a baseline, and `redirects push`
|
|
252
|
+
validates the file offline and replaces the whole map; `help redirects`
|
|
253
|
+
states the entry shape (`path`, `kind` redirect or gone, `target`, `status`
|
|
254
|
+
defaulting to 301), the normalization rule, and every refusal.
|
|
190
255
|
|
|
191
256
|
`preview page <page-path-or-id>` renders one persisted draft behind the
|
|
192
257
|
staging gate and mints a browser handoff URL, reported only in the final JSON
|
|
@@ -199,7 +264,8 @@ for another. The handoff also expires two minutes after it is minted;
|
|
|
199
264
|
`approve [page-path...]` publishes drafts into the approved candidate pool.
|
|
200
265
|
It stages; nothing reaches an audience until `deploy --staging` publishes the
|
|
201
266
|
candidate and `deploy --production` promotes that completed staging
|
|
202
|
-
deployment. `status` reports
|
|
267
|
+
deployment. `status` reports the platform authoring switch, the CLI release,
|
|
268
|
+
deployments, readiness, and image processing.
|
|
203
269
|
|
|
204
270
|
## Output contract
|
|
205
271
|
|
|
@@ -208,11 +274,17 @@ whose approval code and URL exist only as progress). Stdout carries exactly
|
|
|
208
274
|
one JSON object per run, schema version `1`: `ok`, the CLI name and version,
|
|
209
275
|
the verb, and the verb's own result on success; a stable `error.code` with an
|
|
210
276
|
optional `field`, `status`, and classified `refusal` on failure. Exit codes are
|
|
211
|
-
`0` success, `1` failure, and `2` usage fault. The
|
|
212
|
-
automation should branch on are `
|
|
277
|
+
`0` success, `1` failure, and `2` usage fault. The six refusal classes an
|
|
278
|
+
automation should branch on are `cli_outdated` (this package is behind the
|
|
279
|
+
latest published release, which is the only one Taproot accepts; upgrade it,
|
|
280
|
+
and see "Keeping the CLI current"), `platform_paused` (external authoring is
|
|
213
281
|
paused; retry later), `credential_rejected` (stop and re-issue),
|
|
214
|
-
`
|
|
215
|
-
|
|
282
|
+
`capability_missing` (the credential is valid and correctly scoped but was
|
|
283
|
+
not exchanged for a capability the request needs; the refusal carries
|
|
284
|
+
`field: "GrantedCapabilities"`, and the CLI's human progress names the
|
|
285
|
+
granted and required capabilities, so report the verb's declared set rather
|
|
286
|
+
than re-issuing the key), `plan_limit` (the plan's published-page allowance,
|
|
287
|
+
surfaced at deploy), and `throttled` (back off).
|
|
216
288
|
|
|
217
289
|
When `GITHUB_OUTPUT` names the runner's existing output file, every
|
|
218
290
|
operational verb appends the same JSON under `taproot_site_result` through a
|
|
@@ -221,6 +293,27 @@ Authorization values, signed upload headers, preview handoffs outside the
|
|
|
221
293
|
final result, and page bodies never appear in progress, diagnostics, JSON, or
|
|
222
294
|
Actions output.
|
|
223
295
|
|
|
296
|
+
### Every write answers `platform_paused`
|
|
297
|
+
|
|
298
|
+
`pull` and `status` succeed, and every write is refused with
|
|
299
|
+
`refusal: "platform_paused"` and `field: "SiteAuthoringRollout"`. Nothing is
|
|
300
|
+
wrong with the credential, the request, or the site: Taproot has external site
|
|
301
|
+
authoring switched off platform-wide. A Taproot administrator turns it back on
|
|
302
|
+
with the platform setting `site_authoring.external_writes_enabled`, under
|
|
303
|
+
Admin → Platform settings → Site Authoring ("External site authoring writes
|
|
304
|
+
enabled"); nobody else can, and no amount of retrying or re-issuing changes it.
|
|
305
|
+
|
|
306
|
+
You do not have to discover this from a refusal. Each sign-in exchange reports
|
|
307
|
+
the switch's state, so `status` names it as `platform.externalWritesEnabled`
|
|
308
|
+
and every write verb prints one warning before it does any work when the last
|
|
309
|
+
exchange said the platform was paused. The warning is advisory and never a
|
|
310
|
+
gate — the switch is enforced inside each write's own transaction, so a run
|
|
311
|
+
that starts while it is off still succeeds if an operator turns it on
|
|
312
|
+
meanwhile. `whoami` stays offline and reports what the last exchange recorded,
|
|
313
|
+
dated with that exchange; `status` is where a current answer comes from.
|
|
314
|
+
`TAPROOT_SITE_KEY` runs perform no exchange, so their `status` reports
|
|
315
|
+
`platform.externalWritesKnown: false` rather than guessing.
|
|
316
|
+
|
|
224
317
|
## Public release
|
|
225
318
|
|
|
226
319
|
Private Taproot is the source authority. Merging a version bump to private
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
{
|
|
2
|
+
"manifestVersion": 6,
|
|
3
|
+
"siteId": "a0000000-0000-4000-8000-000000000001",
|
|
4
|
+
"pulledAt": "2026-09-04T00:00:00.000Z",
|
|
5
|
+
"pages": [
|
|
6
|
+
{
|
|
7
|
+
"pageId": "a0000000-0000-4000-8000-000000000010",
|
|
8
|
+
"resourceId": "a0000000-0000-4000-8000-000000000011",
|
|
9
|
+
"path": "",
|
|
10
|
+
"title": "Riverbend Wellness",
|
|
11
|
+
"status": "PAGE_STATUS_DRAFT",
|
|
12
|
+
"templateType": "TEMPLATE_TYPE_FREE_FORM",
|
|
13
|
+
"hasDraft": true,
|
|
14
|
+
"isGenerated": false,
|
|
15
|
+
"description": "An example home page for the offline authoring fixture.",
|
|
16
|
+
"file": "pages/index.md",
|
|
17
|
+
"sourceFormat": "markdown",
|
|
18
|
+
"workspaceMode": "editable"
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
"pageId": "a0000000-0000-4000-8000-000000000012",
|
|
22
|
+
"resourceId": "a0000000-0000-4000-8000-000000000013",
|
|
23
|
+
"path": "visit",
|
|
24
|
+
"title": "Visit the studio",
|
|
25
|
+
"status": "PAGE_STATUS_DRAFT",
|
|
26
|
+
"templateType": "TEMPLATE_TYPE_FREE_FORM",
|
|
27
|
+
"hasDraft": true,
|
|
28
|
+
"isGenerated": false,
|
|
29
|
+
"description": "An example second page for the offline authoring fixture.",
|
|
30
|
+
"file": "pages/visit.md",
|
|
31
|
+
"sourceFormat": "markdown",
|
|
32
|
+
"workspaceMode": "editable"
|
|
33
|
+
}
|
|
34
|
+
],
|
|
35
|
+
"pagesTruncated": false,
|
|
36
|
+
"navigation": {
|
|
37
|
+
"file": "nav.json",
|
|
38
|
+
"items": 3
|
|
39
|
+
},
|
|
40
|
+
"redirects": {
|
|
41
|
+
"file": "redirects.json",
|
|
42
|
+
"revision": "0000000000000000000000000000000000000000000000000000000000000001",
|
|
43
|
+
"entries": 4
|
|
44
|
+
},
|
|
45
|
+
"settings": [
|
|
46
|
+
{
|
|
47
|
+
"settingsType": "SETTING_TYPE_TAPROOT_STYLES",
|
|
48
|
+
"file": "settings/taproot-styles.json",
|
|
49
|
+
"entityId": "a0000000-0000-4000-8000-000000000020"
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
"settingsType": "SETTING_TYPE_BRAND",
|
|
53
|
+
"file": "settings/brand.json",
|
|
54
|
+
"entityId": "a0000000-0000-4000-8000-000000000021"
|
|
55
|
+
},
|
|
56
|
+
{
|
|
57
|
+
"settingsType": "SETTING_TYPE_SITE_HEADER",
|
|
58
|
+
"file": "settings/site-header.json",
|
|
59
|
+
"entityId": "a0000000-0000-4000-8000-000000000022"
|
|
60
|
+
},
|
|
61
|
+
{
|
|
62
|
+
"settingsType": "SETTING_TYPE_SITE_PUBLISHING_PREFERENCES",
|
|
63
|
+
"file": "settings/site-publishing-preferences.json",
|
|
64
|
+
"entityId": "a0000000-0000-4000-8000-000000000023"
|
|
65
|
+
}
|
|
66
|
+
],
|
|
67
|
+
"settingsSkipped": [],
|
|
68
|
+
"fixture": {
|
|
69
|
+
"contractVersion": 1,
|
|
70
|
+
"imageIds": [
|
|
71
|
+
"a0000000-0000-4000-8000-0000000000a0",
|
|
72
|
+
"a0000000-0000-4000-8000-0000000000a1",
|
|
73
|
+
"a0000000-0000-4000-8000-0000000000a2"
|
|
74
|
+
],
|
|
75
|
+
"deliveryOrigins": [
|
|
76
|
+
"https://static.example.test"
|
|
77
|
+
]
|
|
78
|
+
}
|
|
79
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
{
|
|
2
|
+
"siteId": "a0000000-0000-4000-8000-000000000001",
|
|
3
|
+
"navItems": [
|
|
4
|
+
{
|
|
5
|
+
"id": "a0000000-0000-4000-8000-000000000030",
|
|
6
|
+
"kind": "NAV_ITEM_KIND_PAGE",
|
|
7
|
+
"title": "Home",
|
|
8
|
+
"resourceId": "a0000000-0000-4000-8000-000000000011",
|
|
9
|
+
"children": []
|
|
10
|
+
},
|
|
11
|
+
{
|
|
12
|
+
"id": "a0000000-0000-4000-8000-000000000031",
|
|
13
|
+
"kind": "NAV_ITEM_KIND_PAGE",
|
|
14
|
+
"title": "Visit",
|
|
15
|
+
"resourceId": "a0000000-0000-4000-8000-000000000013",
|
|
16
|
+
"children": []
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
"id": "a0000000-0000-4000-8000-000000000032",
|
|
20
|
+
"kind": "NAV_ITEM_KIND_EXTERNAL_URL",
|
|
21
|
+
"title": "Book a class",
|
|
22
|
+
"externalUrl": "https://schedule.example.test/riverbend",
|
|
23
|
+
"children": []
|
|
24
|
+
}
|
|
25
|
+
]
|
|
26
|
+
}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Riverbend Wellness
|
|
3
|
+
path: ""
|
|
4
|
+
description: An example home page for the offline authoring fixture.
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
```component:hero-section
|
|
8
|
+
{
|
|
9
|
+
"overline": "Riverbend Wellness · Elm Harbor",
|
|
10
|
+
"title": "An example site you can validate before you have one.",
|
|
11
|
+
"titleSize": "display",
|
|
12
|
+
"lead": "Riverbend Wellness is invented. It exists so taproot-site validate has a complete workspace to check on a machine that has never pulled a real site.",
|
|
13
|
+
"primaryAction": {
|
|
14
|
+
"label": "Visit the studio",
|
|
15
|
+
"url": "/visit"
|
|
16
|
+
},
|
|
17
|
+
"secondaryAction": {
|
|
18
|
+
"label": "Book a class",
|
|
19
|
+
"url": "https://schedule.example.test/riverbend"
|
|
20
|
+
},
|
|
21
|
+
"alignment": "start",
|
|
22
|
+
"media": {
|
|
23
|
+
"imageId": "a0000000-0000-4000-8000-0000000000a2",
|
|
24
|
+
"src": "https://static.example.test/site/img/riverbend-studio-1280.webp",
|
|
25
|
+
"urls": [
|
|
26
|
+
{
|
|
27
|
+
"minWidth": 640,
|
|
28
|
+
"url": "https://static.example.test/site/img/riverbend-studio-640.webp"
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
"minWidth": 1280,
|
|
32
|
+
"url": "https://static.example.test/site/img/riverbend-studio-1280.webp"
|
|
33
|
+
}
|
|
34
|
+
],
|
|
35
|
+
"width": 1600,
|
|
36
|
+
"height": 1067,
|
|
37
|
+
"alt": "An example photograph placeholder for the fixture studio"
|
|
38
|
+
},
|
|
39
|
+
"mediaArrangement": "split",
|
|
40
|
+
"mediaPosition": "after",
|
|
41
|
+
"mediaWidth": "equal"
|
|
42
|
+
}
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
:::section {"context":"subtle","contentPadding":"standard","surface":"none"}
|
|
46
|
+
## What this fixture is for
|
|
47
|
+
|
|
48
|
+
Every value here is fictional. The studio, the street, the telephone number,
|
|
49
|
+
and the image delivery host are all invented, and the image identities are
|
|
50
|
+
declared in `manifest.fixture.json` under the `fixture` block so `validate`
|
|
51
|
+
can bind them without a network.
|
|
52
|
+
|
|
53
|
+
Run `taproot-site help fixture` for the manifest contract this directory
|
|
54
|
+
follows, and `taproot-site help page free-form` for the document vocabulary
|
|
55
|
+
these pages are written in.
|
|
56
|
+
|
|
57
|
+
```inline-facts
|
|
58
|
+
[
|
|
59
|
+
{
|
|
60
|
+
"value": "2 pages",
|
|
61
|
+
"label": "Free-form sources"
|
|
62
|
+
},
|
|
63
|
+
{
|
|
64
|
+
"value": "(555) 555-0147",
|
|
65
|
+
"label": "Reserved example number",
|
|
66
|
+
"url": "tel:+15555550147"
|
|
67
|
+
},
|
|
68
|
+
{
|
|
69
|
+
"value": "12 Alder Street, Elm Harbor",
|
|
70
|
+
"label": "Invented address"
|
|
71
|
+
}
|
|
72
|
+
]
|
|
73
|
+
```
|
|
74
|
+
:::
|
|
75
|
+
|
|
76
|
+
:::section {"context":"inverted","contentPadding":"standard","surface":"none"}
|
|
77
|
+
## Change one thing and watch it fail
|
|
78
|
+
|
|
79
|
+
The fastest way to learn the contract is to break it. Point a navigation item
|
|
80
|
+
at a resource identity this fixture does not declare, or set
|
|
81
|
+
`defaultScheme` to a value outside `system`, `light`, and `dark`, and
|
|
82
|
+
`validate` names the file and the field it refused.
|
|
83
|
+
|
|
84
|
+
```component:cta
|
|
85
|
+
{
|
|
86
|
+
"heading": "Then put it back.",
|
|
87
|
+
"description": "Copy this directory somewhere writable before you edit it; validate never writes to the fixture it reads.",
|
|
88
|
+
"buttonText": "Visit the studio",
|
|
89
|
+
"buttonUrl": "/visit",
|
|
90
|
+
"variant": "primary"
|
|
91
|
+
}
|
|
92
|
+
```
|
|
93
|
+
:::
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Visit the studio
|
|
3
|
+
path: visit
|
|
4
|
+
description: An example second page for the offline authoring fixture.
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
:::section {"contentPadding":"standard","surface":"none"}
|
|
8
|
+
## Visit the studio
|
|
9
|
+
|
|
10
|
+
Riverbend Wellness is a fictional studio in the fictional town of Elm Harbor.
|
|
11
|
+
Nothing on this page describes a real place, and no address, telephone number,
|
|
12
|
+
or hostname here resolves to one.
|
|
13
|
+
|
|
14
|
+
- **12 Alder Street, Elm Harbor** — an invented address
|
|
15
|
+
- **(555) 555-0147** — from the reserved example telephone range
|
|
16
|
+
- **hello@example.test** — a reserved example domain
|
|
17
|
+
|
|
18
|
+
This page exists so the fixture binds more than one page identity: the
|
|
19
|
+
navigation tree points at both, the footer links to both, and `validate`
|
|
20
|
+
proves every reference resolves inside the fixture.
|
|
21
|
+
:::
|
|
22
|
+
|
|
23
|
+
:::section {"context":"raised","contentPadding":"standard","surface":"elevated"}
|
|
24
|
+
## Hours
|
|
25
|
+
|
|
26
|
+
| Day | Doors | Last class |
|
|
27
|
+
| --- | --- | --- |
|
|
28
|
+
| Monday to Friday | 6:00 AM | 7:30 PM |
|
|
29
|
+
| Saturday | 7:30 AM | 4:00 PM |
|
|
30
|
+
| Sunday | 8:00 AM | 2:00 PM |
|
|
31
|
+
|
|
32
|
+
Every figure above is invented for the fixture.
|
|
33
|
+
:::
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
{
|
|
2
|
+
"siteId": "a0000000-0000-4000-8000-000000000001",
|
|
3
|
+
"revision": "0000000000000000000000000000000000000000000000000000000000000001",
|
|
4
|
+
"entries": [
|
|
5
|
+
{
|
|
6
|
+
"path": "/classes.html",
|
|
7
|
+
"kind": "redirect",
|
|
8
|
+
"target": "/visit",
|
|
9
|
+
"status": 301,
|
|
10
|
+
"origin": "authored"
|
|
11
|
+
},
|
|
12
|
+
{
|
|
13
|
+
"path": "/schedule",
|
|
14
|
+
"kind": "redirect",
|
|
15
|
+
"target": "https://booking.example.test/riverbend",
|
|
16
|
+
"status": 302,
|
|
17
|
+
"origin": "authored"
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
"path": "/summer-2019-retreat.html",
|
|
21
|
+
"kind": "gone",
|
|
22
|
+
"status": 410,
|
|
23
|
+
"origin": "authored"
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
"path": "/welcome.html",
|
|
27
|
+
"kind": "redirect",
|
|
28
|
+
"target": "/",
|
|
29
|
+
"status": 301,
|
|
30
|
+
"origin": "path_history"
|
|
31
|
+
}
|
|
32
|
+
]
|
|
33
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
{
|
|
2
|
+
"entityId": "a0000000-0000-4000-8000-000000000022",
|
|
3
|
+
"settingsType": "SETTING_TYPE_SITE_HEADER",
|
|
4
|
+
"settings": {
|
|
5
|
+
"headerLayout": "standard",
|
|
6
|
+
"headerWidth": "contained",
|
|
7
|
+
"navDrawerStyle": "full-screen",
|
|
8
|
+
"navDrawerTransition": "fade",
|
|
9
|
+
"brandText": "Riverbend Wellness",
|
|
10
|
+
"logoAlt": "Riverbend Wellness",
|
|
11
|
+
"navMenuDisplay": "auto",
|
|
12
|
+
"headerPosition": "sticky",
|
|
13
|
+
"showThemeToggle": true,
|
|
14
|
+
"showBrandText": true,
|
|
15
|
+
"brandLogoSize": "standard",
|
|
16
|
+
"brandHoverGrow": false,
|
|
17
|
+
"brandColor": "--esp-color-headings",
|
|
18
|
+
"lightBrandColor": "--esp-color-headings",
|
|
19
|
+
"darkBrandColor": "--esp-color-headings"
|
|
20
|
+
}
|
|
21
|
+
}
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
{
|
|
2
|
+
"entityId": "a0000000-0000-4000-8000-000000000023",
|
|
3
|
+
"settingsType": "SETTING_TYPE_SITE_PUBLISHING_PREFERENCES",
|
|
4
|
+
"settings": {
|
|
5
|
+
"footerSettings": {
|
|
6
|
+
"enabled": true,
|
|
7
|
+
"showBrand": true,
|
|
8
|
+
"showBrandText": true,
|
|
9
|
+
"asideCta": {
|
|
10
|
+
"id": "a0000000-0000-4000-8000-000000000040",
|
|
11
|
+
"label": "Book a class",
|
|
12
|
+
"externalUrl": "https://schedule.example.test/riverbend"
|
|
13
|
+
},
|
|
14
|
+
"bottomLinks": [
|
|
15
|
+
{
|
|
16
|
+
"id": "a0000000-0000-4000-8000-000000000041",
|
|
17
|
+
"label": "Visit",
|
|
18
|
+
"pageResourceId": "a0000000-0000-4000-8000-000000000013"
|
|
19
|
+
}
|
|
20
|
+
],
|
|
21
|
+
"light": {
|
|
22
|
+
"backgroundColor": "--esp-color-layer-1",
|
|
23
|
+
"textColor": "--esp-color-text",
|
|
24
|
+
"headingColor": "--esp-color-headings",
|
|
25
|
+
"linkColor": "--esp-color-link",
|
|
26
|
+
"linkHoverColor": "--esp-color-link-hover",
|
|
27
|
+
"backgroundImageId": "",
|
|
28
|
+
"backgroundImageOpacity": 0,
|
|
29
|
+
"backgroundPresentation": "FOOTER_BACKGROUND_PRESENTATION_COVER",
|
|
30
|
+
"backgroundFade": "FOOTER_FADE_MODE_NONE",
|
|
31
|
+
"backgroundRepeatHeightPx": 160,
|
|
32
|
+
"additionalTopPaddingRem": 1
|
|
33
|
+
},
|
|
34
|
+
"dark": {
|
|
35
|
+
"backgroundColor": "--esp-color-layer-1",
|
|
36
|
+
"textColor": "--esp-color-text",
|
|
37
|
+
"headingColor": "--esp-color-headings",
|
|
38
|
+
"linkColor": "--esp-color-link",
|
|
39
|
+
"linkHoverColor": "--esp-color-link-hover",
|
|
40
|
+
"backgroundImageId": "",
|
|
41
|
+
"backgroundImageOpacity": 0,
|
|
42
|
+
"backgroundPresentation": "FOOTER_BACKGROUND_PRESENTATION_COVER",
|
|
43
|
+
"backgroundFade": "FOOTER_FADE_MODE_NONE",
|
|
44
|
+
"backgroundRepeatHeightPx": 160,
|
|
45
|
+
"additionalTopPaddingRem": 1
|
|
46
|
+
},
|
|
47
|
+
"linkColumns": [
|
|
48
|
+
{
|
|
49
|
+
"id": "a0000000-0000-4000-8000-000000000042",
|
|
50
|
+
"groups": [
|
|
51
|
+
{
|
|
52
|
+
"id": "a0000000-0000-4000-8000-000000000043",
|
|
53
|
+
"heading": "Studio",
|
|
54
|
+
"links": [
|
|
55
|
+
{
|
|
56
|
+
"id": "a0000000-0000-4000-8000-000000000044",
|
|
57
|
+
"label": "Home",
|
|
58
|
+
"pageResourceId": "a0000000-0000-4000-8000-000000000011"
|
|
59
|
+
},
|
|
60
|
+
{
|
|
61
|
+
"id": "a0000000-0000-4000-8000-000000000045",
|
|
62
|
+
"label": "Visit",
|
|
63
|
+
"pageResourceId": "a0000000-0000-4000-8000-000000000013"
|
|
64
|
+
}
|
|
65
|
+
]
|
|
66
|
+
}
|
|
67
|
+
]
|
|
68
|
+
}
|
|
69
|
+
],
|
|
70
|
+
"asideHeadingContent": {
|
|
71
|
+
"paragraphs": [
|
|
72
|
+
{
|
|
73
|
+
"runs": [
|
|
74
|
+
{
|
|
75
|
+
"text": "Everything here is invented.",
|
|
76
|
+
"bold": true
|
|
77
|
+
}
|
|
78
|
+
]
|
|
79
|
+
}
|
|
80
|
+
]
|
|
81
|
+
},
|
|
82
|
+
"asideBodyContent": {
|
|
83
|
+
"paragraphs": [
|
|
84
|
+
{
|
|
85
|
+
"runs": [
|
|
86
|
+
{
|
|
87
|
+
"text": "Riverbend Wellness is a fictional studio used to demonstrate the authoring contract offline."
|
|
88
|
+
}
|
|
89
|
+
]
|
|
90
|
+
}
|
|
91
|
+
]
|
|
92
|
+
},
|
|
93
|
+
"bottomContent": {
|
|
94
|
+
"paragraphs": [
|
|
95
|
+
{
|
|
96
|
+
"runs": [
|
|
97
|
+
{
|
|
98
|
+
"text": "An example fixture. Not a real business."
|
|
99
|
+
}
|
|
100
|
+
]
|
|
101
|
+
}
|
|
102
|
+
]
|
|
103
|
+
},
|
|
104
|
+
"featureImage": null
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
}
|