@taprootio/site-authoring 0.1.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 +15 -0
- package/README.md +239 -0
- package/bin/taproot-site.js +5 -0
- package/package.json +40 -0
- package/src/api.js +1507 -0
- package/src/appearance-contract.js +311 -0
- package/src/atomic-file.js +136 -0
- package/src/bounded-file.js +59 -0
- package/src/capabilities.js +29 -0
- package/src/cli.js +872 -0
- package/src/config.js +412 -0
- package/src/constants.js +173 -0
- package/src/content/components.js +870 -0
- package/src/content/free-form-section-registry.json +524 -0
- package/src/content/free-form-sections.js +582 -0
- package/src/content/index.js +29 -0
- package/src/content/inline-facts.js +97 -0
- package/src/content/markdown.js +1625 -0
- package/src/content/tiptap-prosemirror.ts +1266 -0
- package/src/content/validate-document.js +1079 -0
- package/src/content/vocabulary.js +397 -0
- package/src/credentials.js +544 -0
- package/src/errors.js +133 -0
- package/src/footer-contract.js +687 -0
- package/src/footer-draft-hash.js +203 -0
- package/src/footer-workspace.js +136 -0
- package/src/image-metadata.js +169 -0
- package/src/index.js +16 -0
- package/src/json-guard.js +111 -0
- package/src/output.js +157 -0
- package/src/presentation-reference.js +600 -0
- package/src/reference-help.js +927 -0
- package/src/session.js +316 -0
- package/src/settings-catalog.js +187 -0
- package/src/settings.js +157 -0
- package/src/theme-validation.js +344 -0
- package/src/transport.js +777 -0
- package/src/verbs/approve.js +178 -0
- package/src/verbs/deploy.js +327 -0
- package/src/verbs/env.js +89 -0
- package/src/verbs/footer-push.js +115 -0
- package/src/verbs/index.js +125 -0
- package/src/verbs/login.js +529 -0
- package/src/verbs/logout.js +39 -0
- package/src/verbs/media-upload.js +319 -0
- package/src/verbs/nav-push.js +333 -0
- package/src/verbs/pages-push.js +990 -0
- package/src/verbs/preview-page.js +107 -0
- package/src/verbs/preview-revoke.js +56 -0
- package/src/verbs/pull.js +727 -0
- package/src/verbs/sites.js +57 -0
- package/src/verbs/status.js +94 -0
- package/src/verbs/theme-push.js +197 -0
- package/src/verbs/use.js +129 -0
- package/src/verbs/validate.js +477 -0
- package/src/verbs/whoami.js +108 -0
- package/src/workspace.js +1085 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
ISC License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Taproot
|
|
4
|
+
|
|
5
|
+
Permission to use, copy, modify, and/or distribute this software for any
|
|
6
|
+
purpose with or without fee is hereby granted, provided that the above
|
|
7
|
+
copyright notice and this permission notice appear in all copies.
|
|
8
|
+
|
|
9
|
+
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
|
10
|
+
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
|
11
|
+
AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
|
12
|
+
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
|
13
|
+
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
|
14
|
+
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
|
15
|
+
PERFORMANCE OF THIS SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
# `@taprootio/site-authoring`
|
|
2
|
+
|
|
3
|
+
The command-line authoring surface for a Taproot site. `taproot-site` pulls a
|
|
4
|
+
site's pages, navigation, and settings into a local workspace, validates what
|
|
5
|
+
you author there against the same contracts the published site renders, and
|
|
6
|
+
pushes, previews, approves, and deploys the result — for a person at a
|
|
7
|
+
terminal or an agent acting on an owner's behalf.
|
|
8
|
+
|
|
9
|
+
The CLI is deliberately strict where the server is permissive. Taproot stores
|
|
10
|
+
a page body as it is sent and renders what it recognizes, so a malformed
|
|
11
|
+
document fails silently at render time. Every push therefore validates every
|
|
12
|
+
document it will send before sending any of them, and refuses anything the
|
|
13
|
+
renderer would drop.
|
|
14
|
+
|
|
15
|
+
## Availability, as of September 2026
|
|
16
|
+
|
|
17
|
+
Taproot cannot yet deliver sign-in email to people outside the team, so no
|
|
18
|
+
one outside it can create an account or approve a sign-in today. The package
|
|
19
|
+
is published ahead of that so it is installed and ready when access opens.
|
|
20
|
+
Until then, `help` and `validate` work fully offline, and `login` will print
|
|
21
|
+
its approval code and URL and then wait for an approval that cannot yet be
|
|
22
|
+
given, giving up when the approval window closes (about fifteen minutes) with
|
|
23
|
+
`login.timeout`. This note is removed when access opens.
|
|
24
|
+
|
|
25
|
+
## Install
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
npm install --global @taprootio/site-authoring
|
|
29
|
+
taproot-site --help
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
Node 22 or later. The package has one runtime dependency,
|
|
33
|
+
`@taprootio/espalier`, pinned to an exact version: the CLI validates themes
|
|
34
|
+
against one Espalier theme contract, and a floating range would let an install
|
|
35
|
+
resolve a different contract than the CLI was tested against.
|
|
36
|
+
|
|
37
|
+
## First commands
|
|
38
|
+
|
|
39
|
+
`taproot-site --help` lists every verb and the configuration contract.
|
|
40
|
+
`taproot-site help` is the offline reference family agents read before
|
|
41
|
+
authoring: page types, components, navigation, media, previews, themes,
|
|
42
|
+
appearance, and the footer, each with `--json` for stable machine-readable
|
|
43
|
+
output.
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
taproot-site help
|
|
47
|
+
taproot-site help page free-form
|
|
48
|
+
taproot-site help component hero-section --json
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
`taproot-site validate <fixture-directory>` checks a complete offline fixture
|
|
52
|
+
with no credential, no network, and no write: a directory laid out like a
|
|
53
|
+
pulled workspace — `pages/`, `nav.json`, and the four `settings/` documents —
|
|
54
|
+
whose `manifest.fixture.json` binds deterministic page, resource, image, and
|
|
55
|
+
settings identities under a versioned `fixture` block. It proves structure,
|
|
56
|
+
page content and theme contexts, navigation references, the complete theme,
|
|
57
|
+
appearance, header, brand, and footer, and fixture-local image identities. It
|
|
58
|
+
does not prove authorization, site ownership, concurrency, server round trips,
|
|
59
|
+
or rendering; a real pull and an authorized preview do that.
|
|
60
|
+
|
|
61
|
+
## Authorize
|
|
62
|
+
|
|
63
|
+
Sign in once. `login` starts a device-style authorization: it prints an
|
|
64
|
+
eight-character code and the approval URL on the Taproot origin it is
|
|
65
|
+
configured to talk to, and waits while an owner enters that code in a browser
|
|
66
|
+
already signed in to Taproot. Typing the code — rather than following a
|
|
67
|
+
prefilled link — is what proves the person approving can see the terminal
|
|
68
|
+
that asked, so the URL never carries the code and the page accepts none.
|
|
69
|
+
|
|
70
|
+
```bash
|
|
71
|
+
taproot-site login
|
|
72
|
+
taproot-site sites
|
|
73
|
+
taproot-site use "My Site"
|
|
74
|
+
taproot-site whoami
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
The issued sign-in is stored outside any repository at
|
|
78
|
+
`$XDG_CONFIG_HOME/taproot-site/credentials.json` (falling back to
|
|
79
|
+
`~/.config/taproot-site/`), directory `0700` and file `0600`, one per Taproot
|
|
80
|
+
origin. It authorizes nothing on any site. It can do exactly two things: list
|
|
81
|
+
the account's authorable sites, and exchange itself for a short-lived site
|
|
82
|
+
credential, which every authoring command mints for itself and holds only in
|
|
83
|
+
memory. Exchanged credentials expire within the hour and carry only the
|
|
84
|
+
capabilities the command needs — a content push never holds deploy. The
|
|
85
|
+
sign-in expires 24 hours after its last successful exchange, and never past
|
|
86
|
+
any expiry the approving owner chose.
|
|
87
|
+
|
|
88
|
+
`sites` lists what the sign-in may author; `use <name or id>` records the
|
|
89
|
+
choice as `siteId` in `taproot-site.json`, creating the file in the current
|
|
90
|
+
directory when there is none. `whoami` answers from local state alone: which
|
|
91
|
+
Taproot, which account, which site, what the last exchange granted, and when
|
|
92
|
+
the sign-in expires. `logout` discards the stored sign-in locally and revokes
|
|
93
|
+
nothing; the credential stays valid until an owner revokes it under
|
|
94
|
+
Account → Settings → API keys. The secret itself is never displayed, logged,
|
|
95
|
+
or placed in any result: credentials are named by id and display prefix only.
|
|
96
|
+
|
|
97
|
+
### `TAPROOT_SITE_KEY` overrides the sign-in
|
|
98
|
+
|
|
99
|
+
CI and non-interactive agents authenticate with a site-scoped site authoring
|
|
100
|
+
key issued under Account → Settings → API keys and supplied only through the
|
|
101
|
+
environment:
|
|
102
|
+
|
|
103
|
+
```bash
|
|
104
|
+
export TAPROOT_SITE_KEY='the one-time displayed site authoring key'
|
|
105
|
+
taproot-site status
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
When the variable is set it wins outright and the exchange is skipped, so
|
|
109
|
+
logging in on a machine never changes what existing automation does. Presence
|
|
110
|
+
is authoritative: an empty or malformed value is refused as `auth.key_invalid`
|
|
111
|
+
rather than falling through to a stored sign-in and running the command as a
|
|
112
|
+
different identity. There is no flag and no configuration field for either
|
|
113
|
+
credential, and no second environment variable.
|
|
114
|
+
|
|
115
|
+
## Configuration
|
|
116
|
+
|
|
117
|
+
Commands that act on a site read `taproot-site.json`, discovered by walking
|
|
118
|
+
upward from the current directory through a bounded number of parents.
|
|
119
|
+
Exactly one must be found: none is `config.not_found`, more than one is
|
|
120
|
+
`config.ambiguous`, and `--config <path>` placed before the verb selects one
|
|
121
|
+
explicitly and bypasses discovery. `login`, `logout`, `sites`, `use`,
|
|
122
|
+
`whoami`, and `env` need no configuration and no site.
|
|
123
|
+
|
|
124
|
+
```json
|
|
125
|
+
{
|
|
126
|
+
"configVersion": 1,
|
|
127
|
+
"siteId": "11111111-1111-4111-8111-111111111111",
|
|
128
|
+
"workspaceDir": "site"
|
|
129
|
+
}
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
| Field | Rule |
|
|
133
|
+
| --------------- | ----------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
134
|
+
| `configVersion` | Must be `1`. |
|
|
135
|
+
| `siteId` | Optional. The canonical lowercase UUID of the site the next command writes to. `use` writes it; a present but malformed value is refused. |
|
|
136
|
+
| `workspaceDir` | Required. A relative POSIX path beneath the configuration directory that `pull` writes into. Every existing segment must be a real directory. |
|
|
137
|
+
|
|
138
|
+
The file is a small, closed JSON object. Unknown fields, duplicate keys, links
|
|
139
|
+
in the path, and unsupported versions are refused before any credential is
|
|
140
|
+
attached to a request. `apiBaseUrl` is not a field and is refused by name:
|
|
141
|
+
which Taproot the CLI talks to is a property of the machine, not the project.
|
|
142
|
+
|
|
143
|
+
### Which Taproot the CLI talks to
|
|
144
|
+
|
|
145
|
+
```bash
|
|
146
|
+
taproot-site env # report
|
|
147
|
+
taproot-site env local # https://app.taproot.test/api
|
|
148
|
+
taproot-site env production # https://app.taproot.io/api, the default
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
The choice is remembered per machine in `settings.json` beside the
|
|
152
|
+
credential, because it has to be known before any `taproot-site.json` exists.
|
|
153
|
+
Only a reviewed origin set is accepted: `app.taproot.io`, `app.taproot.test`
|
|
154
|
+
for a local Tilt stack, or an explicit loopback host such as
|
|
155
|
+
`http://localhost:8080/api` for development. Taproot is not a self-hostable
|
|
156
|
+
distribution, and every other host is refused before a bearer can be built.
|
|
157
|
+
Sign-ins are stored per origin, so switching away and back finds the one that
|
|
158
|
+
was already there.
|
|
159
|
+
|
|
160
|
+
## The authoring loop
|
|
161
|
+
|
|
162
|
+
```bash
|
|
163
|
+
taproot-site pull
|
|
164
|
+
# edit site/pages/about.md
|
|
165
|
+
taproot-site pages push about
|
|
166
|
+
taproot-site preview page about
|
|
167
|
+
taproot-site approve about
|
|
168
|
+
taproot-site deploy --staging
|
|
169
|
+
taproot-site deploy --production
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
`pull` snapshots pages, navigation, and settings into the workspace:
|
|
173
|
+
`pages/`, `nav.json`, `settings/`, `media/`, and two dot-prefixed manifests
|
|
174
|
+
that carry site binding and page identity between commands. Every page path
|
|
175
|
+
has exactly one authoritative source — `pages/<name>.md` in the documented
|
|
176
|
+
Markdown subset with `title`, `path`, and `description` front matter, or
|
|
177
|
+
`pages/<name>.pm.json` as a ProseMirror document — and pull never writes a
|
|
178
|
+
generated `.pm.json` beside a tracked `.md`. For a page tracked as Markdown
|
|
179
|
+
the site's own document is kept as internal state under
|
|
180
|
+
`.taproot-site-state/`, which is never a page source and never pushed. A page
|
|
181
|
+
edited on the site since the last pull is a `pages.pull_conflict` refusal
|
|
182
|
+
before anything under `pages/` changes, with both recovery paths named.
|
|
183
|
+
|
|
184
|
+
`pages push [page-path...]` validates and sends the selected pages, or every
|
|
185
|
+
workspace page when none is named; the homepage is addressed as `/`.
|
|
186
|
+
`media upload [path...]` uploads raster files and records component-ready
|
|
187
|
+
delivery fields. `nav push`, `theme push`, and `footer push` replace the whole
|
|
188
|
+
navigation tree, the complete light/dark theme pair with its appearance
|
|
189
|
+
settings, and the closed footer document.
|
|
190
|
+
|
|
191
|
+
`preview page <page-path-or-id>` renders one persisted draft behind the
|
|
192
|
+
staging gate and mints a browser handoff URL, reported only in the final JSON
|
|
193
|
+
result. **Each handoff is single-use.** Opening the URL consumes it; a reused,
|
|
194
|
+
shared, or bookmarked preview URL answers `Not found`, which is the handoff
|
|
195
|
+
being spent rather than the preview being broken. Run `preview page` again
|
|
196
|
+
for another. The handoff also expires two minutes after it is minted;
|
|
197
|
+
`preview revoke <page-id> <snapshot-id>` releases a preview early.
|
|
198
|
+
|
|
199
|
+
`approve [page-path...]` publishes drafts into the approved candidate pool.
|
|
200
|
+
It stages; nothing reaches an audience until `deploy --staging` publishes the
|
|
201
|
+
candidate and `deploy --production` promotes that completed staging
|
|
202
|
+
deployment. `status` reports deployments, readiness, and image processing.
|
|
203
|
+
|
|
204
|
+
## Output contract
|
|
205
|
+
|
|
206
|
+
Human progress goes to stderr and `--quiet` silences it (except for `login`,
|
|
207
|
+
whose approval code and URL exist only as progress). Stdout carries exactly
|
|
208
|
+
one JSON object per run, schema version `1`: `ok`, the CLI name and version,
|
|
209
|
+
the verb, and the verb's own result on success; a stable `error.code` with an
|
|
210
|
+
optional `field`, `status`, and classified `refusal` on failure. Exit codes are
|
|
211
|
+
`0` success, `1` failure, and `2` usage fault. The four refusal classes an
|
|
212
|
+
automation should branch on are `platform_paused` (external authoring is
|
|
213
|
+
paused; retry later), `credential_rejected` (stop and re-issue),
|
|
214
|
+
`plan_limit` (the plan's published-page allowance, surfaced at deploy), and
|
|
215
|
+
`throttled` (back off).
|
|
216
|
+
|
|
217
|
+
When `GITHUB_OUTPUT` names the runner's existing output file, every
|
|
218
|
+
operational verb appends the same JSON under `taproot_site_result` through a
|
|
219
|
+
random delimiter block, plus `taproot_site_verb` on success. Credentials,
|
|
220
|
+
Authorization values, signed upload headers, preview handoffs outside the
|
|
221
|
+
final result, and page bodies never appear in progress, diagnostics, JSON, or
|
|
222
|
+
Actions output.
|
|
223
|
+
|
|
224
|
+
## Public release
|
|
225
|
+
|
|
226
|
+
Private Taproot is the source authority. A private `site-authoring-v<version>`
|
|
227
|
+
tag verifies this package — the release commit must belong to `main`, the tag
|
|
228
|
+
must name the version `package.json` declares, and `@taprootio/espalier` must
|
|
229
|
+
be pinned to the exact version the release manifest records — then
|
|
230
|
+
allowlist-exports only `packages/site-authoring/` to public
|
|
231
|
+
[`taprootio/trunk`](https://github.com/taprootio/trunk). Trunk's separate
|
|
232
|
+
`publish-site-authoring.yml` workflow tests and publishes those exact public
|
|
233
|
+
tag bytes with npm trusted-publishing provenance, under its own concurrency
|
|
234
|
+
group, GitHub Environment, tag prefix, and npm identity.
|
|
235
|
+
|
|
236
|
+
The package's tests run in both places. A few compare this package against
|
|
237
|
+
sources that live only in the private monorepo — the canonical renderer, the
|
|
238
|
+
shared section registry, the seeded default theme, and the API's contracts —
|
|
239
|
+
and those skip by name in the public tree rather than pretending to pass.
|
package/package.json
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@taprootio/site-authoring",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Taproot site authoring CLI: sign in once in the browser, then pull, validate, push, preview, approve, and deploy a site from the terminal",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"license": "ISC",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "https://github.com/taprootio/trunk.git",
|
|
10
|
+
"directory": "packages/site-authoring"
|
|
11
|
+
},
|
|
12
|
+
"engines": {
|
|
13
|
+
"node": ">=22"
|
|
14
|
+
},
|
|
15
|
+
"exports": {
|
|
16
|
+
".": {
|
|
17
|
+
"import": "./src/index.js"
|
|
18
|
+
}
|
|
19
|
+
},
|
|
20
|
+
"bin": {
|
|
21
|
+
"taproot-site": "./bin/taproot-site.js"
|
|
22
|
+
},
|
|
23
|
+
"files": [
|
|
24
|
+
"bin/",
|
|
25
|
+
"src/",
|
|
26
|
+
"LICENSE",
|
|
27
|
+
"README.md"
|
|
28
|
+
],
|
|
29
|
+
"dependencies": {
|
|
30
|
+
"@taprootio/espalier": "4.4.0"
|
|
31
|
+
},
|
|
32
|
+
"scripts": {
|
|
33
|
+
"test": "node --test",
|
|
34
|
+
"prepack": "npm test"
|
|
35
|
+
},
|
|
36
|
+
"publishConfig": {
|
|
37
|
+
"access": "public",
|
|
38
|
+
"provenance": true
|
|
39
|
+
}
|
|
40
|
+
}
|