@universalcms/connect 0.0.0-stage → 0.13.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 +779 -2
- package/dist/angular-runtime.d.mts +4 -0
- package/dist/angular-runtime.mjs +7 -0
- package/dist/angular-setup.mjs +124 -0
- package/dist/astro-runtime.d.mts +6 -0
- package/dist/astro-runtime.mjs +255 -0
- package/dist/astro-setup.mjs +215 -0
- package/dist/astro-source.mjs +451 -0
- package/dist/astro-website.d.mts +5 -0
- package/dist/astro-website.mjs +42 -0
- package/dist/cli.mjs +552 -0
- package/dist/connector-runtime.mjs +130 -0
- package/dist/content-next-setup.mjs +200 -0
- package/dist/content-registry-validation.mjs +40 -0
- package/dist/content-registry.mjs +44 -0
- package/dist/content-setup.mjs +13 -0
- package/dist/content-storefront-setup.mjs +155 -0
- package/dist/content-vite-setup.mjs +107 -0
- package/dist/core.mjs +205 -0
- package/dist/framework-ownership.mjs +68 -0
- package/dist/framework.mjs +54 -0
- package/dist/guided.mjs +335 -0
- package/dist/native-adapters.mjs +31 -0
- package/dist/native-chrome-model.d.mts +3 -0
- package/dist/native-chrome-model.mjs +14 -0
- package/dist/native-chrome-review.mjs +49 -0
- package/dist/native-chrome-source.mjs +69 -0
- package/dist/native-route-review.mjs +152 -0
- package/dist/native-routes.mjs +27 -0
- package/dist/native-website-model.d.mts +18 -0
- package/dist/native-website-model.mjs +67 -0
- package/dist/native-website-server.d.mts +1 -0
- package/dist/native-website-server.mjs +35222 -0
- package/dist/native-website-server.mjs.LEGAL.txt +56 -0
- package/dist/native-website.d.mts +3 -0
- package/dist/native-website.mjs +120 -0
- package/dist/native-website.mjs.LEGAL.txt +56 -0
- package/dist/next-publication.d.mts +12 -0
- package/dist/next-publication.mjs +107 -0
- package/dist/next-settings-layout.mjs +233 -0
- package/dist/next-source.d.mts +8 -0
- package/dist/next-source.mjs +93 -0
- package/dist/page-discovery.mjs +236 -0
- package/dist/page-mapping-review.mjs +160 -0
- package/dist/preflight-recovery.mjs +122 -0
- package/dist/preflight.mjs +138 -0
- package/dist/project-files.mjs +56 -0
- package/dist/public-bindings.d.mts +3 -0
- package/dist/public-bindings.mjs +28 -0
- package/dist/publication-browser.d.mts +5 -0
- package/dist/publication-browser.mjs +258 -0
- package/dist/publication-check.mjs +141 -0
- package/dist/publication-document.d.mts +2 -0
- package/dist/publication-document.mjs +59 -0
- package/dist/publication-next-setup.mjs +134 -0
- package/dist/publication-setup.mjs +138 -0
- package/dist/publication-verification.mjs +85 -0
- package/dist/publication.css +6 -0
- package/dist/publication.d.mts +31 -0
- package/dist/publication.mjs +320 -0
- package/dist/react-publication.d.mts +22 -0
- package/dist/react-publication.mjs +301 -0
- package/dist/react-website.d.mts +15 -0
- package/dist/react-website.mjs +735 -0
- package/dist/reviewed-commerce-ownership.mjs +26 -0
- package/dist/reviewed-commerce-settings-setup.mjs +243 -0
- package/dist/reviewed-native-ownership.mjs +118 -0
- package/dist/reviewed-storefront-source.mjs +227 -0
- package/dist/runtime-dependencies.mjs +59 -0
- package/dist/setup-environment.mjs +307 -0
- package/dist/source-bindings.mjs +77 -0
- package/dist/source-check.mjs +149 -0
- package/dist/source-collect.mjs +80 -0
- package/dist/source-config.mjs +20 -0
- package/dist/source-extract.mjs +514 -0
- package/dist/source-finalize.mjs +52 -0
- package/dist/source-routes.mjs +65 -0
- package/dist/source-setup.mjs +490 -0
- package/dist/staged-setup.mjs +287 -0
- package/dist/static-runtime.mjs +67 -0
- package/dist/static-setup.mjs +136 -0
- package/dist/svelte-runtime.d.mts +27 -0
- package/dist/svelte-runtime.mjs +40 -0
- package/dist/svelte-setup.mjs +135 -0
- package/dist/svelte-template.mjs +48 -0
- package/dist/templates.mjs +248 -0
- package/dist/vendor/analyze-project.mjs +82 -0
- package/dist/vendor/astro-compiler/LICENSE +53 -0
- package/dist/vendor/astro-compiler/astro.wasm +0 -0
- package/dist/vendor/astro-compiler/chunk-W5DTLHV4.js +1 -0
- package/dist/vendor/astro-compiler/node/index.js +1 -0
- package/dist/vendor/check-delivery.mjs +158 -0
- package/dist/vendor/dependency-diagnostics.mjs +72 -0
- package/dist/vendor/inspect-installed-dependencies.mjs +125 -0
- package/dist/vendor/project-analysis.mjs +498 -0
- package/dist/vendor/react-LICENSE.txt +21 -0
- package/dist/vendor/react-document.mjs +124 -0
- package/dist/vendor/react-dom-LICENSE.txt +21 -0
- package/dist/vendor/react-form.mjs +63 -0
- package/dist/vendor/react-safety.mjs +47 -0
- package/dist/vendor/source-manifest-provider.mjs +104 -0
- package/dist/vendor/typescript-LICENSE.txt +55 -0
- package/dist/vendor/typescript.cjs +200276 -0
- package/dist/vite-handler.mjs +54 -0
- package/dist/vite-publication-server.d.mts +3 -0
- package/dist/vite-publication-server.mjs +82 -0
- package/dist/vite-publication.d.mts +15 -0
- package/dist/vite-publication.mjs +241 -0
- package/dist/vite-source-discovery.mjs +315 -0
- package/dist/vite-source.d.mts +30 -0
- package/dist/vite-source.mjs +141 -0
- package/dist/vue-runtime.d.mts +10 -0
- package/dist/vue-runtime.mjs +50 -0
- package/dist/vue-server.d.mts +2 -0
- package/dist/vue-server.mjs +58 -0
- package/dist/vue-setup.mjs +127 -0
- package/dist/vue-source.mjs +168 -0
- package/dist/website-check.mjs +41 -0
- package/dist/website-http.mjs +149 -0
- package/dist/website-model.d.mts +20 -0
- package/dist/website-model.mjs +155 -0
- package/dist/website-next.d.mts +2 -0
- package/dist/website-next.mjs +29 -0
- package/dist/website-node.d.mts +3 -0
- package/dist/website-node.mjs +50 -0
- package/dist/website-runtime.d.mts +13 -0
- package/dist/website-runtime.mjs +396 -0
- package/dist/website-schema.mjs +22175 -0
- package/dist/website-setup.mjs +256 -0
- package/dist/website.css +49 -0
- package/dist/wizard.mjs +298 -0
- package/package.json +60 -4
|
@@ -0,0 +1,301 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
3
|
+
import React, { createContext, useContext, useEffect, useMemo, useRef, useState } from "react";
|
|
4
|
+
import { bindingTextFingerprint } from './public-bindings.mjs';
|
|
5
|
+
import { validatePublishedDocumentEntry as validateEntry } from './publication-document.mjs';
|
|
6
|
+
export { validatePublishedDocumentEntry as validateEntry } from './publication-document.mjs';
|
|
7
|
+
import { CmsDocument } from "./vendor/react-document.mjs";
|
|
8
|
+
const Context = createContext(null);
|
|
9
|
+
const MetadataOwner = createContext(false);
|
|
10
|
+
/** Website settings own head metadata when both integrations share a tree. */
|
|
11
|
+
export function PublicationMetadataOwner({ children }) {
|
|
12
|
+
return _jsx(MetadataOwner.Provider, { value: true, children: children });
|
|
13
|
+
}
|
|
14
|
+
const revision = (entry) => `${entry.id}:${entry.version}${entry.revisionId ? `:${entry.revisionId}` : ""}`;
|
|
15
|
+
const flatten = (blocks) => blocks.flatMap(block => [block, ...(block.type === "section" && Array.isArray(block.data?.blocks) ? flatten(block.data.blocks) : block.type === "columns" && Array.isArray(block.data?.columns) ? flatten(block.data.columns.flat()) : [])]);
|
|
16
|
+
/** The initial payload was validated server-side. Refreshes only use the same-origin
|
|
17
|
+
* published-document endpoint; there is deliberately no native/draft fallback. */
|
|
18
|
+
export function PublishedDocumentProvider({ initialEntry, pathname, route, children, formEndpoint }) {
|
|
19
|
+
const identity = route.identity ?? initialEntry;
|
|
20
|
+
if (!identity || !route.baseline || !route.bindings)
|
|
21
|
+
throw Error("Missing reviewed CMS binding inventory");
|
|
22
|
+
const key = `${pathname}:${identity.kind}:${identity.slug}:${identity.locale}`;
|
|
23
|
+
return _jsx(PublishedProvider, { initialEntry: initialEntry, pathname: pathname, route: route, identity: identity, formEndpoint: formEndpoint, children: children }, key);
|
|
24
|
+
}
|
|
25
|
+
function PublishedProvider({ initialEntry, pathname, route, identity, children, formEndpoint }) {
|
|
26
|
+
const websiteOwnsMetadata = useContext(MetadataOwner);
|
|
27
|
+
const [entry, setEntry] = useState(() => initialEntry ? validateEntry(initialEntry, identity) : undefined);
|
|
28
|
+
const [failure, setFailure] = useState(null);
|
|
29
|
+
const generation = useRef(0);
|
|
30
|
+
const previousTitle = useRef(initialEntry?.document.title);
|
|
31
|
+
useEffect(() => {
|
|
32
|
+
let controller;
|
|
33
|
+
let disposed = false;
|
|
34
|
+
const refresh = () => {
|
|
35
|
+
const request = ++generation.current;
|
|
36
|
+
controller?.abort();
|
|
37
|
+
const requestController = new AbortController();
|
|
38
|
+
controller = requestController;
|
|
39
|
+
const timeout = window.setTimeout(() => requestController.abort(), 10000);
|
|
40
|
+
void (async () => {
|
|
41
|
+
try {
|
|
42
|
+
const response = await fetch(`/api/universalcms/document?path=${encodeURIComponent(pathname)}`, { cache: "no-store", credentials: "same-origin", redirect: "error", signal: requestController.signal, headers: { Accept: "application/json" } });
|
|
43
|
+
if (response.status !== 200)
|
|
44
|
+
throw response.status === 404 ? 404 : 503;
|
|
45
|
+
if (!response.headers.get("content-type")?.includes("application/json"))
|
|
46
|
+
throw 503;
|
|
47
|
+
const value = await readPayload(response);
|
|
48
|
+
if (value?.state !== "published")
|
|
49
|
+
throw 503;
|
|
50
|
+
const next = validateEntry(value.entry, identity);
|
|
51
|
+
if (value.revision !== revision(next))
|
|
52
|
+
throw 503;
|
|
53
|
+
if (!disposed && request === generation.current) {
|
|
54
|
+
setEntry(next);
|
|
55
|
+
setFailure(null);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
catch (error) {
|
|
59
|
+
if (!disposed && request === generation.current) {
|
|
60
|
+
const status = error === 404 ? 404 : 503;
|
|
61
|
+
setEntry(undefined);
|
|
62
|
+
setFailure(status);
|
|
63
|
+
window.dispatchEvent(new CustomEvent("universalcms:document-unavailable", { detail: { status, path: pathname } }));
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
finally {
|
|
67
|
+
window.clearTimeout(timeout);
|
|
68
|
+
}
|
|
69
|
+
})();
|
|
70
|
+
};
|
|
71
|
+
const update = (event) => {
|
|
72
|
+
const change = event;
|
|
73
|
+
if (change.detail?.path !== pathname)
|
|
74
|
+
return;
|
|
75
|
+
change.preventDefault();
|
|
76
|
+
refresh();
|
|
77
|
+
};
|
|
78
|
+
window.addEventListener("universalcms:publication-change", update);
|
|
79
|
+
if (!initialEntry)
|
|
80
|
+
refresh();
|
|
81
|
+
return () => { disposed = true; generation.current++; controller?.abort(); window.removeEventListener("universalcms:publication-change", update); };
|
|
82
|
+
}, [pathname, identity.kind, identity.slug, identity.locale]);
|
|
83
|
+
useEffect(() => {
|
|
84
|
+
if (initialEntry) {
|
|
85
|
+
generation.current++;
|
|
86
|
+
setEntry(validateEntry(initialEntry, identity));
|
|
87
|
+
setFailure(null);
|
|
88
|
+
}
|
|
89
|
+
}, [initialEntry]);
|
|
90
|
+
useEffect(() => {
|
|
91
|
+
if (!entry || websiteOwnsMetadata)
|
|
92
|
+
return;
|
|
93
|
+
const before = previousTitle.current, title = entry.document.title;
|
|
94
|
+
if (before !== title) {
|
|
95
|
+
const current = document.title;
|
|
96
|
+
// Keep a server-rendered layout title template, such as “Page | Brand”.
|
|
97
|
+
// Unknown client-only templates need an explicit native integration.
|
|
98
|
+
document.title = before && current.split(before).length === 2 ? current.replace(before, title) : title;
|
|
99
|
+
}
|
|
100
|
+
previousTitle.current = title;
|
|
101
|
+
let description = document.querySelector('meta[name="description"]');
|
|
102
|
+
if (!description) {
|
|
103
|
+
description = document.createElement('meta');
|
|
104
|
+
description.name = 'description';
|
|
105
|
+
document.head.append(description);
|
|
106
|
+
}
|
|
107
|
+
description.content = entry.document.excerpt;
|
|
108
|
+
}, [entry, websiteOwnsMetadata]);
|
|
109
|
+
const value = useMemo(() => entry ? ({ entry, route, blocks: new Map(flatten(entry.document.blocks).map(block => [block.id, block])), formEndpoint }) : null, [entry, route, formEndpoint]);
|
|
110
|
+
if (failure)
|
|
111
|
+
return _jsxs("main", { role: "alert", children: [_jsx("h1", { children: failure }), _jsx("p", { children: failure === 404 ? "Page not found" : "Page temporarily unavailable" })] });
|
|
112
|
+
if (!value || !entry)
|
|
113
|
+
return _jsx("main", { role: "status", "aria-busy": "true", children: "Loading published content\u2026" });
|
|
114
|
+
return _jsxs(Context.Provider, { value: value, children: [_jsx("meta", { name: "universalcms-publication-revision", content: revision(entry), "data-path": pathname }), children] });
|
|
115
|
+
}
|
|
116
|
+
async function readPayload(response) {
|
|
117
|
+
const limit = 2 * 1024 * 1024;
|
|
118
|
+
if (Number(response.headers.get('content-length')) > limit)
|
|
119
|
+
throw 503;
|
|
120
|
+
if (!response.body)
|
|
121
|
+
throw 503;
|
|
122
|
+
const reader = response.body.getReader();
|
|
123
|
+
const chunks = [];
|
|
124
|
+
let length = 0;
|
|
125
|
+
try {
|
|
126
|
+
for (;;) {
|
|
127
|
+
const { value, done } = await reader.read();
|
|
128
|
+
if (done)
|
|
129
|
+
break;
|
|
130
|
+
length += value.length;
|
|
131
|
+
if (length > limit)
|
|
132
|
+
throw 503;
|
|
133
|
+
chunks.push(value);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
catch (error) {
|
|
137
|
+
await reader.cancel();
|
|
138
|
+
throw error;
|
|
139
|
+
}
|
|
140
|
+
finally {
|
|
141
|
+
reader.releaseLock();
|
|
142
|
+
}
|
|
143
|
+
const bytes = new Uint8Array(length);
|
|
144
|
+
let offset = 0;
|
|
145
|
+
for (const chunk of chunks) {
|
|
146
|
+
bytes.set(chunk, offset);
|
|
147
|
+
offset += chunk.length;
|
|
148
|
+
}
|
|
149
|
+
return JSON.parse(new TextDecoder('utf-8', { fatal: true }).decode(bytes));
|
|
150
|
+
}
|
|
151
|
+
function usePublished() { const value = useContext(Context); if (!value)
|
|
152
|
+
throw Error("Published content requires its server-loaded document"); return value; }
|
|
153
|
+
/** Read the validated public snapshot for reviewed native component mappings. */
|
|
154
|
+
export function usePublishedDocument() {
|
|
155
|
+
const { entry, route, blocks } = usePublished();
|
|
156
|
+
return { entry, route, blocks };
|
|
157
|
+
}
|
|
158
|
+
/** Search published display copy without mutating native row identities/handlers. */
|
|
159
|
+
export function useCmsSearchText() {
|
|
160
|
+
const { route, blocks } = usePublished();
|
|
161
|
+
return (selector) => Object.values(route.bindings).flatMap(slot => slot.variants.filter(variant => JSON.stringify(variant.values) === JSON.stringify(selector)).map(variant => blocks.get(variant.id)?.text ?? "")).join(" ");
|
|
162
|
+
}
|
|
163
|
+
function presentation(block) {
|
|
164
|
+
const token = block.style ?? {};
|
|
165
|
+
const sizes = { small: ".875em", normal: "1em", large: "1.25em" };
|
|
166
|
+
const colors = { default: "inherit", muted: "var(--text-muted, #64748b)", accent: "var(--primary, #0d9488)", inverse: "#fff" };
|
|
167
|
+
const spacing = { none: "0", small: ".5rem", medium: "1.25rem", large: "2.5rem" };
|
|
168
|
+
const style = {};
|
|
169
|
+
if (["left", "center", "right"].includes(String(token.align)))
|
|
170
|
+
style.textAlign = token.align;
|
|
171
|
+
if (sizes[String(token.size)])
|
|
172
|
+
style.fontSize = sizes[String(token.size)];
|
|
173
|
+
if (colors[String(token.tone)])
|
|
174
|
+
style.color = colors[String(token.tone)];
|
|
175
|
+
if (spacing[String(token.spaceAbove)])
|
|
176
|
+
style.marginTop = spacing[String(token.spaceAbove)];
|
|
177
|
+
if (spacing[String(token.spaceBelow)])
|
|
178
|
+
style.marginBottom = spacing[String(token.spaceBelow)];
|
|
179
|
+
return { className: [token.hideOnMobile === true ? "cms-hide-mobile" : "", token.hideOnDesktop === true ? "cms-hide-desktop" : ""].filter(Boolean).join(" "), style, ...(typeof token.anchor === "string" && /^[a-z0-9]+(?:-[a-z0-9]+)*$/.test(token.anchor) ? { id: token.anchor } : {}) };
|
|
180
|
+
}
|
|
181
|
+
function replaceNativeText(children, text) {
|
|
182
|
+
// Custom components may render copy from props, context or closures. Traversing
|
|
183
|
+
// their supplied children cannot prove that their visible text was replaced.
|
|
184
|
+
// Keep an enclosing native control's handlers, but replace opaque contents.
|
|
185
|
+
if (hasOpaqueContent(children))
|
|
186
|
+
return text;
|
|
187
|
+
let written = false;
|
|
188
|
+
const visit = (value) => React.Children.map(value, child => {
|
|
189
|
+
if (typeof child === "string" || typeof child === "number") {
|
|
190
|
+
if (!String(child).trim())
|
|
191
|
+
return child;
|
|
192
|
+
if (written)
|
|
193
|
+
return "";
|
|
194
|
+
written = true;
|
|
195
|
+
return text;
|
|
196
|
+
}
|
|
197
|
+
if (!React.isValidElement(child))
|
|
198
|
+
return child;
|
|
199
|
+
if (typeof child.type !== "string" && child.type !== React.Fragment)
|
|
200
|
+
return child;
|
|
201
|
+
if (typeof child.type === "string" && ["select", "option", "input", "textarea", "script", "style", "svg", "form", "noscript", "iframe"].includes(child.type))
|
|
202
|
+
return child;
|
|
203
|
+
return React.cloneElement(child, {}, visit(child.props.children));
|
|
204
|
+
});
|
|
205
|
+
const result = visit(children);
|
|
206
|
+
return written ? result : _jsxs(_Fragment, { children: [result, text] });
|
|
207
|
+
}
|
|
208
|
+
function hasOpaqueContent(children) {
|
|
209
|
+
return React.Children.toArray(children).some(child => React.isValidElement(child) && (typeof child.type !== "string" && child.type !== React.Fragment ||
|
|
210
|
+
child.props.dangerouslySetInnerHTML != null || hasOpaqueContent(child.props.children)));
|
|
211
|
+
}
|
|
212
|
+
/** Exact source slot + immutable native row identity selects a frozen block ID.
|
|
213
|
+
* Existing elements and handlers remain mounted when a publication refreshes. */
|
|
214
|
+
export function CmsBound({ slot, selector, children }) {
|
|
215
|
+
const { entry, route, blocks } = usePublished();
|
|
216
|
+
const variants = route.bindings[slot]?.variants;
|
|
217
|
+
if (!variants)
|
|
218
|
+
throw Error("Unknown reviewed CMS source slot");
|
|
219
|
+
const variant = variants.find(candidate => JSON.stringify(candidate.values) === JSON.stringify(selector));
|
|
220
|
+
// An application-created row that was never exported is outside CMS ownership.
|
|
221
|
+
if (!variant)
|
|
222
|
+
return route.publicSource === 1 ? null : _jsx(_Fragment, { children: children });
|
|
223
|
+
const block = blocks.get(variant.id);
|
|
224
|
+
if (!block)
|
|
225
|
+
return null;
|
|
226
|
+
const baseline = flatten(route.baseline.blocks).find(candidate => candidate.id === variant.id);
|
|
227
|
+
if (!baseline)
|
|
228
|
+
throw Error("Missing frozen source block");
|
|
229
|
+
const projected = route.publicSource === 1;
|
|
230
|
+
if (projected && !route.fingerprints?.blocks[variant.id])
|
|
231
|
+
throw Error("Missing public binding comparison");
|
|
232
|
+
let text = block.text;
|
|
233
|
+
const sameText = projected ? bindingTextFingerprint(block.text) === route.fingerprints.blocks[variant.id] : block.text === baseline.text;
|
|
234
|
+
if (sameText && variant.id === route.titleBlockId && (projected ? bindingTextFingerprint(entry.document.title) !== route.fingerprints.title : entry.document.title !== route.baseline.title))
|
|
235
|
+
text = entry.document.title;
|
|
236
|
+
const excerptBlockId = route.excerptBlockId ?? route.baseline.blocks.find(candidate => candidate.type === "paragraph")?.id;
|
|
237
|
+
if (sameText && variant.id === excerptBlockId && (projected ? bindingTextFingerprint(entry.document.excerpt) !== route.fingerprints.excerpt : entry.document.excerpt !== route.baseline.excerpt))
|
|
238
|
+
text = entry.document.excerpt;
|
|
239
|
+
const tokens = presentation(block);
|
|
240
|
+
const unchanged = !projected && text === baseline.text && block.type === baseline.type;
|
|
241
|
+
if (block.type !== baseline.type || !["paragraph", "heading", "list", "quote", "code"].includes(block.type) && JSON.stringify(block.data) !== JSON.stringify(baseline.data))
|
|
242
|
+
return _jsx(CmsBlock, { block: block });
|
|
243
|
+
if (React.isValidElement(children) && children.type === React.Fragment) {
|
|
244
|
+
const meaningful = React.Children.toArray(children.props.children).filter(child => typeof child !== "string" || child.trim());
|
|
245
|
+
if (meaningful.length === 1 && React.isValidElement(meaningful[0]) && typeof meaningful[0].type === "string")
|
|
246
|
+
children = meaningful[0];
|
|
247
|
+
}
|
|
248
|
+
if (React.isValidElement(children) && typeof children.type === "string") {
|
|
249
|
+
const properties = { ...tokens, "data-cms-block": block.id, "data-cms-slot": slot, className: [children.props.className, tokens.className].filter(Boolean).join(" "), style: { ...children.props.style, ...tokens.style } };
|
|
250
|
+
if (!tokens.id)
|
|
251
|
+
delete properties.id;
|
|
252
|
+
if (block.type === "list" && children.type === "li" && !unchanged && text.includes("\n")) {
|
|
253
|
+
const native = children;
|
|
254
|
+
return _jsx(_Fragment, { children: text.split("\n").map((line, index) => React.cloneElement(native, { ...properties, key: `${block.id}:${index}` }, replaceNativeText(native.props.children, line))) });
|
|
255
|
+
}
|
|
256
|
+
const renderedChildren = unchanged ? children.props.children : replaceNativeText(children.props.children, text);
|
|
257
|
+
if (block.type === "heading" && block.data?.level !== baseline.data?.level && [2, 3, 4].includes(Number(block.data?.level)))
|
|
258
|
+
return React.createElement(`h${block.data?.level}`, { ...children.props, ...properties }, renderedChildren);
|
|
259
|
+
return React.cloneElement(children, properties, renderedChildren);
|
|
260
|
+
}
|
|
261
|
+
// An opaque root has no known native element to preserve. Render only the
|
|
262
|
+
// reviewed published block instead of appending new text beside stale copy.
|
|
263
|
+
if (!unchanged && hasOpaqueContent(children))
|
|
264
|
+
return _jsx(CmsBlock, { block: { ...block, text } });
|
|
265
|
+
return _jsx("span", { ...tokens, className: `${tokens.className} cms-inline-bound`, "data-cms-block": block.id, "data-cms-slot": slot, children: unchanged ? children : replaceNativeText(children, text) });
|
|
266
|
+
}
|
|
267
|
+
function CmsBlock({ block }) {
|
|
268
|
+
return _jsx(PublishedBlocks, { blocks: [block] });
|
|
269
|
+
}
|
|
270
|
+
/** Render a complete public page using the same reviewed block and form policy. */
|
|
271
|
+
export function CmsPublishedDocument() {
|
|
272
|
+
const { entry } = usePublished();
|
|
273
|
+
return _jsx(PublishedBlocks, { blocks: entry.document.blocks });
|
|
274
|
+
}
|
|
275
|
+
function PublishedBlocks({ blocks }) {
|
|
276
|
+
const { entry, formEndpoint } = usePublished();
|
|
277
|
+
const endpoint = typeof formEndpoint === 'string' && /^\/(?!\/)/.test(formEndpoint) && !/[\s\\]/.test(formEndpoint) ? formEndpoint : undefined;
|
|
278
|
+
return _jsx(CmsDocument, { document: { blocks: blocks }, entryId: entry.id, revisionId: entry.revisionId, formEndpoint: endpoint, components: endpoint ? {} : { form: () => _jsx("p", { role: "status", children: "Submissions are currently unavailable." }) } });
|
|
279
|
+
}
|
|
280
|
+
export function CmsAdditionalContent({ includeUnbound = false } = {}) {
|
|
281
|
+
const { entry, route } = usePublished();
|
|
282
|
+
const known = new Set(flatten(route.baseline.blocks).map(block => block.id));
|
|
283
|
+
const bound = new Set(Object.values(route.bindings).flatMap(slot => slot.variants.map(variant => variant.id)));
|
|
284
|
+
// Bound IDs stay at native design locations. Reviewed partial adapters may
|
|
285
|
+
// explicitly include baseline blocks that have no native binding as well.
|
|
286
|
+
const additional = (blocks) => blocks.flatMap(block => {
|
|
287
|
+
if (bound.has(block.id))
|
|
288
|
+
return [];
|
|
289
|
+
if (block.type === "section" && Array.isArray(block.data?.blocks)) {
|
|
290
|
+
const children = additional(block.data.blocks);
|
|
291
|
+
return children.length ? [{ ...block, data: { ...block.data, blocks: children } }] : [];
|
|
292
|
+
}
|
|
293
|
+
if (block.type === "columns" && Array.isArray(block.data?.columns)) {
|
|
294
|
+
const columns = block.data.columns.map(additional);
|
|
295
|
+
return columns.some(column => column.length) ? [{ ...block, data: { ...block.data, columns } }] : [];
|
|
296
|
+
}
|
|
297
|
+
return known.has(block.id) && !includeUnbound ? [] : [block];
|
|
298
|
+
});
|
|
299
|
+
const blocks = additional(entry.document.blocks);
|
|
300
|
+
return blocks.length ? _jsx("section", { className: "cms-additional-content", "aria-label": "Additional information", "data-cms-additional-content": true, children: blocks.map(block => _jsx(CmsBlock, { block: block }, block.id)) }) : null;
|
|
301
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { ReactNode } from 'react';
|
|
2
|
+
import type { PublishedDocumentEntry } from './publication.mjs';
|
|
3
|
+
import type { WebsiteSettings, Consent } from './website-model.mjs';
|
|
4
|
+
export type WebsiteEntry = PublishedDocumentEntry & Record<string, any>;
|
|
5
|
+
export type WebsiteArchive = { kind: 'category' | 'tag'; path: string; title: string; description: string };
|
|
6
|
+
export type WebsiteProps = { children: ReactNode; initialSettings?: unknown; entry?: WebsiteEntry; archive?: WebsiteArchive; scope?: string; originalPaths?: string[]; applyTemplate?: boolean; chrome?: boolean };
|
|
7
|
+
export function WebsiteProvider(props: Omit<WebsiteProps, 'originalPaths' | 'applyTemplate' | 'chrome'>): ReactNode;
|
|
8
|
+
export function CmsWebsite(props: WebsiteProps): ReactNode;
|
|
9
|
+
export function CmsWebsitePage(props: { scope?: string; pathname?: string; initialEntry?: WebsiteEntry; initialResult?: unknown; initialSettings?: unknown; chrome?: boolean }): ReactNode;
|
|
10
|
+
export function useWebsiteSettings(): { settings: WebsiteSettings; entry?: WebsiteEntry; archive?: WebsiteArchive; related: unknown[]; scope: string; consent: Consent | null; saveConsent(choices: { analytics: boolean; marketing: boolean }): void; preferencesOpen: boolean; setPreferencesOpen(value: boolean): void };
|
|
11
|
+
export function CmsBranding(props?: { homePath?: string; fallback?: ReactNode }): ReactNode;
|
|
12
|
+
export function CmsNavigation(props?: { location?: 'header' | 'footer'; originalLinks?: Array<{ label: string; url: string }>; excludeUrls?: string[]; onNavigate?: () => void; className?: string }): ReactNode;
|
|
13
|
+
export function CmsSocialLinks(): ReactNode;
|
|
14
|
+
export function CmsLanguages(): ReactNode;
|
|
15
|
+
export function CmsExperience(props?: { notFound?: boolean }): ReactNode;
|