@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
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import {
|
|
2
|
+
createAuthoringPreview,
|
|
3
|
+
mintAuthoringPreviewHandoff,
|
|
4
|
+
translateAuthoringPreviewApiError,
|
|
5
|
+
waitForAuthoringPreview,
|
|
6
|
+
withRefusalGuidance,
|
|
7
|
+
} from "../api.js";
|
|
8
|
+
import { VERB_PREVIEW_PAGE } from "../constants.js";
|
|
9
|
+
import { asSiteAuthoringError, isCanonicalUuid, SiteAuthoringError } from "../errors.js";
|
|
10
|
+
import { openSession, successResult } from "../session.js";
|
|
11
|
+
import { normalizePagePath, readManifest } from "../workspace.js";
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Creates an immutable page preview, observes the generator-owned render, and
|
|
15
|
+
* mints the short browser handoff only after the snapshot is ready.
|
|
16
|
+
*
|
|
17
|
+
* A canonical UUID selects directly. A page path resolves through the pulled
|
|
18
|
+
* manifest, giving push, approve, and preview one human-facing selector. The
|
|
19
|
+
* URL is emitted only in
|
|
20
|
+
* the final success object — never in progress or failure text — because its
|
|
21
|
+
* query value is a two-minute bearer capability.
|
|
22
|
+
*/
|
|
23
|
+
export async function previewPage(invocation) {
|
|
24
|
+
if (typeof invocation.pageSelector !== "string" && !isCanonicalUuid(invocation.pageId)) {
|
|
25
|
+
throw new SiteAuthoringError(
|
|
26
|
+
"preview.page_id_invalid",
|
|
27
|
+
"preview page requires one canonical lowercase page UUID when called programmatically with pageId.",
|
|
28
|
+
{ field: "pageId", exitCode: 2 },
|
|
29
|
+
);
|
|
30
|
+
}
|
|
31
|
+
const selector = invocation.pageSelector ?? invocation.pageId;
|
|
32
|
+
if (
|
|
33
|
+
typeof selector === "string"
|
|
34
|
+
&& !isCanonicalUuid(selector)
|
|
35
|
+
&& isCanonicalUuid(selector.toLowerCase())
|
|
36
|
+
) {
|
|
37
|
+
throw new SiteAuthoringError(
|
|
38
|
+
"preview.page_selector_invalid",
|
|
39
|
+
"preview page UUID selectors must use canonical lowercase characters.",
|
|
40
|
+
{ field: "pageSelector", exitCode: 2 },
|
|
41
|
+
);
|
|
42
|
+
}
|
|
43
|
+
const { client, config, siteId, now, onProgress } = await openSession(invocation);
|
|
44
|
+
let pageId = selector;
|
|
45
|
+
if (!isCanonicalUuid(selector)) {
|
|
46
|
+
const pagePath = normalizePagePath(selector);
|
|
47
|
+
if (pagePath === undefined) {
|
|
48
|
+
throw new SiteAuthoringError(
|
|
49
|
+
"preview.page_selector_invalid",
|
|
50
|
+
`'${selector}' is not a usable page path or canonical lowercase page UUID.`,
|
|
51
|
+
{ field: "pageSelector", exitCode: 2 },
|
|
52
|
+
);
|
|
53
|
+
}
|
|
54
|
+
const manifest = await readManifest(config.workspaceDir, siteId);
|
|
55
|
+
const matches = manifest.pages.filter((entry) =>
|
|
56
|
+
typeof entry?.pageId === "string" && normalizePagePath(entry.path) === pagePath);
|
|
57
|
+
if (matches.length !== 1 || !isCanonicalUuid(matches[0].pageId)) {
|
|
58
|
+
throw new SiteAuthoringError(
|
|
59
|
+
"preview.page_not_found",
|
|
60
|
+
`No unique manifest page was found for path '${pagePath || "/"}'. Run 'taproot-site pull' and try again.`,
|
|
61
|
+
// The homepage normalizes to the empty path, which the emitters drop
|
|
62
|
+
// as falsy; name it by its documented spelling so the stable error
|
|
63
|
+
// contract keeps a field for every unknown path.
|
|
64
|
+
{ field: pagePath || "/" },
|
|
65
|
+
);
|
|
66
|
+
}
|
|
67
|
+
pageId = matches[0].pageId;
|
|
68
|
+
onProgress(`Resolved page path '${pagePath}' to pageId=${pageId}.`);
|
|
69
|
+
}
|
|
70
|
+
let created;
|
|
71
|
+
try {
|
|
72
|
+
return await withRefusalGuidance(onProgress, "authoring preview", async () => {
|
|
73
|
+
onProgress("Requesting an immutable authoring preview snapshot.");
|
|
74
|
+
created = await createAuthoringPreview(client, siteId, pageId);
|
|
75
|
+
onProgress(
|
|
76
|
+
`Authoring preview created: pageId=${created.pageId} snapshotId=${created.snapshotId} expiresAt=${created.expiresAt} storedPreviews=${created.storedPreviewCount}/${created.storedPreviewCap}.`,
|
|
77
|
+
);
|
|
78
|
+
for (const evicted of created.evictedPreviews) {
|
|
79
|
+
onProgress(
|
|
80
|
+
`Authoring preview evicted at the stored-preview cap: pageId=${evicted.pageId} snapshotId=${evicted.snapshotId} capturedAt=${evicted.capturedAt}.`,
|
|
81
|
+
);
|
|
82
|
+
}
|
|
83
|
+
onProgress("The authoring preview was accepted; waiting for the render to become ready.");
|
|
84
|
+
const ready = await waitForAuthoringPreview(client, { created, onProgress, now });
|
|
85
|
+
onProgress("The authoring preview is ready; minting a short-lived browser handoff.");
|
|
86
|
+
const handoff = await mintAuthoringPreviewHandoff(client, ready, { now });
|
|
87
|
+
return successResult(VERB_PREVIEW_PAGE, siteId, {
|
|
88
|
+
pageId: ready.pageId,
|
|
89
|
+
snapshotId: ready.snapshotId,
|
|
90
|
+
status: ready.status,
|
|
91
|
+
draftRevision: ready.draftRevision,
|
|
92
|
+
capturedAt: ready.capturedAt,
|
|
93
|
+
stagingHost: ready.stagingHost,
|
|
94
|
+
url: handoff.url,
|
|
95
|
+
expiresAt: ready.expiresAt,
|
|
96
|
+
handoffExpiresAt: handoff.handoffExpiresAt,
|
|
97
|
+
storedPreviewCap: created.storedPreviewCap,
|
|
98
|
+
storedPreviewCount: created.storedPreviewCount,
|
|
99
|
+
evictedPreviews: created.evictedPreviews,
|
|
100
|
+
});
|
|
101
|
+
});
|
|
102
|
+
} catch (error) {
|
|
103
|
+
const translated = asSiteAuthoringError(translateAuthoringPreviewApiError(error));
|
|
104
|
+
if (created) translated.withPreviewRecovery(created);
|
|
105
|
+
throw translated;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import {
|
|
2
|
+
AUTHORING_PREVIEW_STATUS_REVOKED,
|
|
3
|
+
revokeAuthoringPreview,
|
|
4
|
+
translateAuthoringPreviewApiError,
|
|
5
|
+
withRefusalGuidance,
|
|
6
|
+
} from "../api.js";
|
|
7
|
+
import { VERB_PREVIEW_REVOKE } from "../constants.js";
|
|
8
|
+
import { isCanonicalUuid, SiteAuthoringError } from "../errors.js";
|
|
9
|
+
import { openSession, successResult } from "../session.js";
|
|
10
|
+
|
|
11
|
+
export async function previewRevoke(invocation) {
|
|
12
|
+
if (!Array.isArray(invocation.previewIds) || invocation.previewIds.length !== 2) {
|
|
13
|
+
throw new SiteAuthoringError(
|
|
14
|
+
"preview.identity_invalid",
|
|
15
|
+
"preview revoke requires one canonical lowercase page UUID and one canonical lowercase snapshot UUID.",
|
|
16
|
+
{ field: "snapshotId", exitCode: 2 },
|
|
17
|
+
);
|
|
18
|
+
}
|
|
19
|
+
if (!isCanonicalUuid(invocation.previewIds[0])) {
|
|
20
|
+
throw new SiteAuthoringError(
|
|
21
|
+
"preview.identity_invalid",
|
|
22
|
+
"preview revoke requires a canonical lowercase page UUID.",
|
|
23
|
+
{ field: "pageId", exitCode: 2 },
|
|
24
|
+
);
|
|
25
|
+
}
|
|
26
|
+
if (!isCanonicalUuid(invocation.previewIds[1])) {
|
|
27
|
+
throw new SiteAuthoringError(
|
|
28
|
+
"preview.identity_invalid",
|
|
29
|
+
"preview revoke requires a canonical lowercase snapshot UUID.",
|
|
30
|
+
{ field: "snapshotId", exitCode: 2 },
|
|
31
|
+
);
|
|
32
|
+
}
|
|
33
|
+
const [pageId, snapshotId] = invocation.previewIds;
|
|
34
|
+
const { client, siteId, onProgress } = await openSession(invocation);
|
|
35
|
+
try {
|
|
36
|
+
return await withRefusalGuidance(onProgress, "authoring preview revocation", async () => {
|
|
37
|
+
onProgress("Revoking the authoring preview and scheduling its artifacts for cleanup.");
|
|
38
|
+
const revoked = await revokeAuthoringPreview(client, { siteId, pageId, snapshotId });
|
|
39
|
+
if (revoked.status !== AUTHORING_PREVIEW_STATUS_REVOKED) {
|
|
40
|
+
throw new SiteAuthoringError(
|
|
41
|
+
"preview.status_contract",
|
|
42
|
+
"Taproot returned an unexpected authoring-preview status after revocation.",
|
|
43
|
+
{ field: "status", status: revoked.status },
|
|
44
|
+
);
|
|
45
|
+
}
|
|
46
|
+
return successResult(VERB_PREVIEW_REVOKE, siteId, {
|
|
47
|
+
pageId: revoked.pageId,
|
|
48
|
+
snapshotId: revoked.snapshotId,
|
|
49
|
+
status: revoked.status,
|
|
50
|
+
expiresAt: revoked.expiresAt,
|
|
51
|
+
});
|
|
52
|
+
});
|
|
53
|
+
} catch (error) {
|
|
54
|
+
throw translateAuthoringPreviewApiError(error);
|
|
55
|
+
}
|
|
56
|
+
}
|