camox 0.48.0 → 0.52.1
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/dist/core/createApp.d.ts +1 -0
- package/dist/core/createApp.js +1 -0
- package/dist/core/editing/contentEdits.js +78 -0
- package/dist/core/editing/createEditableBlock.js +277 -311
- package/dist/core/layoutContext.d.ts +1 -0
- package/dist/core/lib/contentType.js +2 -2
- package/dist/core/recordItem.js +3 -3
- package/dist/features/canvas/CamoxCanvas.js +15 -15
- package/dist/features/canvas/CanvasLoadingScreen.js +15 -11
- package/dist/features/canvas/CanvasPageFrame.js +81 -87
- package/dist/features/canvas/CanvasPreviewPageSlot.js +1 -1
- package/dist/features/canvas/DerivedPagePathInput.js +12 -12
- package/dist/features/canvas/canvasOverlayGeometry.js +1 -1
- package/dist/features/canvas/canvasStore.js +2 -2
- package/dist/features/canvas/sidebar/CanvasInspector.js +1 -1
- package/dist/features/canvas/sidebar/CuratedPageInspector.js +1 -1
- package/dist/features/canvas/sidebar/LayoutPageBlocks.js +23 -31
- package/dist/features/canvas/useCanvasCamera.js +1 -1
- package/dist/features/canvas/useDiscoveredPreviewPaths.js +16 -9
- package/dist/features/content/ContentAssets.js +84 -88
- package/dist/features/page/CuratedPageContent.js +1 -1
- package/dist/features/preview/CamoxPreview.d.ts +8 -4
- package/dist/features/preview/CamoxPreview.js +258 -443
- package/dist/features/preview/EditablePageContent.js +244 -2
- package/dist/features/preview/EditablePageExperience.js +19 -13
- package/dist/features/preview/components/AddBlockSheet.js +55 -15
- package/dist/features/preview/components/BlockActionsPopover.js +23 -51
- package/dist/features/preview/components/BlockPeek.js +66 -0
- package/dist/features/preview/components/BlockThumbnail.js +134 -119
- package/dist/features/preview/components/CanvasPreview.js +1 -1
- package/dist/features/preview/components/CreatePageModal.js +150 -156
- package/dist/features/preview/components/FieldToolbar.js +1 -1
- package/dist/features/preview/components/Frame.js +27 -4
- package/dist/features/preview/components/MobilePreviewDrawer.js +6 -9
- package/dist/features/preview/components/PageInfo.js +161 -126
- package/dist/features/preview/components/PagePicker.js +2 -2
- package/dist/features/preview/components/PageTree.js +174 -190
- package/dist/features/preview/components/PreviewPanel.js +13 -67
- package/dist/features/preview/components/PreviewToolbar.js +1 -1
- package/dist/features/preview/components/ReferenceFieldEditor.js +4 -4
- package/dist/features/preview/components/ReferenceListFieldEditor.js +4 -4
- package/dist/features/preview/components/RemovedBlockPlaceholder.js +37 -70
- package/dist/features/preview/components/RepeatableItemsList.js +1 -1
- package/dist/features/preview/components/SelectionEditor.js +248 -256
- package/dist/features/preview/components/StudioEditingFallback.js +22 -26
- package/dist/features/preview/components/useFieldWriter.js +19 -19
- package/dist/features/preview/components/useRecordView.js +105 -147
- package/dist/features/preview/components/useRepeatableItemActions.js +0 -2
- package/dist/features/preview/pageRecord.js +30 -24
- package/dist/features/preview/previewConstants.js +2 -8
- package/dist/features/preview/previewStore.js +2 -0
- package/dist/features/preview/studioNotice.js +20 -0
- package/dist/features/preview/usePreviewViewportActions.js +1 -1
- package/dist/features/provider/AuthenticatedCamoxProvider.js +47 -31
- package/dist/features/provider/components/CommandPalette.js +2 -5
- package/dist/features/provider/signInShortcut.js +1 -2
- package/dist/features/provider/useAdminShortcuts.js +47 -53
- package/dist/features/renderer/RendererApp.js +542 -0
- package/dist/features/renderer/RendererFrames.js +450 -0
- package/dist/features/renderer/definitionAnswers.js +26 -0
- package/dist/features/renderer/protocol.js +35 -0
- package/dist/features/renderer/rendererHost.js +249 -0
- package/dist/features/renderer/studioConnection.js +186 -0
- package/dist/features/routes/pageRuntime.d.ts +2 -1
- package/dist/features/routes/pageRuntime.js +34 -22
- package/dist/features/runtime/RuntimeChrome.js +2 -5
- package/dist/features/runtime/assetPrefix.js +14 -0
- package/dist/features/runtime/breadcrumbs.js +1 -1
- package/dist/features/runtime/editingFallback.js +4 -8
- package/dist/features/runtime/livePages.js +1 -1
- package/dist/features/runtime/pageApp.js +57 -83
- package/dist/features/runtime/pageClient.js +1 -1
- package/dist/features/runtime/pageNavigation.js +3 -2
- package/dist/features/runtime/publicCache.js +95 -0
- package/dist/features/runtime/publishingDraft.js +23 -0
- package/dist/features/runtime/rendererClient.d.ts +8 -0
- package/dist/features/runtime/rendererClient.js +77 -0
- package/dist/features/runtime/runtime.d.ts +22 -3
- package/dist/features/runtime/runtime.js +209 -96
- package/dist/features/runtime/runtimeRoutes.d.ts +2 -0
- package/dist/features/runtime/runtimeRoutes.js +2 -0
- package/dist/features/runtime/worker.d.ts +14 -1
- package/dist/features/runtime/worker.js +34 -4
- package/dist/features/settings/AnalyticsSettings.js +622 -0
- package/dist/features/settings/CamoxSettings.js +95 -0
- package/dist/features/settings/FaviconSettings.js +254 -0
- package/dist/features/settings/ProjectNameSettings.js +145 -0
- package/dist/features/settings/SettingsPart.js +73 -0
- package/dist/features/settings/SocialProfilesSettings.js +245 -0
- package/dist/features/settings/useProjectMutation.js +61 -0
- package/dist/features/studio/FollowAgentProvider.js +10 -10
- package/dist/features/studio/components/DraftsControl.js +136 -131
- package/dist/features/studio/components/LocalePicker.js +178 -174
- package/dist/features/studio/components/Navbar.js +107 -66
- package/dist/features/studio/components/ProjectMenu.js +119 -171
- package/dist/features/studio/components/ReviewDraftSheet.js +1151 -991
- package/dist/features/studio/components/ThreadMessages.js +100 -65
- package/dist/features/studio/components/UserButton.js +2 -2
- package/dist/features/studio/routes.js +3 -1
- package/dist/features/studio/showInCanvas.js +129 -0
- package/dist/features/vite/definitionsSync.js +5 -6
- package/dist/features/vite/hostedChecks.js +127 -0
- package/dist/features/vite/runtimeDev.js +58 -17
- package/dist/features/vite/staticAssetHeaders.js +52 -0
- package/dist/features/vite/vite.d.ts +1 -1
- package/dist/features/vite/vite.js +49 -9
- package/dist/hooks/use-page-destinations.js +10 -10
- package/dist/lib/api-client-server.d.ts +1 -1
- package/dist/lib/api-client.d.ts +2 -1
- package/dist/lib/draft-scope.d.ts +1 -0
- package/dist/lib/locale-path.d.ts +3 -11
- package/dist/lib/locale-path.js +3 -27
- package/dist/lib/queries.js +7 -1
- package/dist/lib/revisions.js +20 -0
- package/dist/lib/shortcuts.js +29 -0
- package/dist/lib/studio-definitions.js +160 -0
- package/dist/lib/studio-reads.d.ts +1 -0
- package/dist/lib/studio-reads.js +65 -21
- package/dist/lib/use-project-room.js +16 -10
- package/dist/lib/utils.js +1 -104
- package/dist/package.js +1 -1
- package/dist/studio.css +1 -1
- package/package.json +9 -4
- package/skills/camox/SKILL.md +4 -0
- package/skills/camox/references/artifacts.md +1 -1
- package/dist/features/studio/components/FaviconDialog.js +0 -318
- package/dist/features/studio/components/SocialProfilesDialog.js +0 -342
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { useAuthState, useSignInRedirect } from "../../lib/auth.js";
|
|
2
2
|
import { DraftScopeContext } from "../../lib/draft-scope.js";
|
|
3
|
-
import { DerivedPageContent } from "../page/DerivedPageContent.js";
|
|
4
3
|
import { useSignInShortcut } from "../provider/signInShortcut.js";
|
|
5
4
|
import { PreviewDocumentContext } from "./PreviewDocumentContext.js";
|
|
6
5
|
import { PreviewActivation } from "../preview/components/PreviewActivation.js";
|
|
@@ -8,6 +7,7 @@ import { Frame } from "../preview/components/Frame.js";
|
|
|
8
7
|
import { PreviewSignInPage } from "../provider/PreviewSignInPage.js";
|
|
9
8
|
import { CoreCamoxProvider } from "../provider/CoreCamoxProvider.js";
|
|
10
9
|
import { CuratedPageContent } from "../page/CuratedPageContent.js";
|
|
10
|
+
import { DerivedPageContent } from "../page/DerivedPageContent.js";
|
|
11
11
|
import { EditingFallbackSlot } from "./editingFallback.js";
|
|
12
12
|
import { PageNavigationProvider } from "./pageNavigation.js";
|
|
13
13
|
import { RuntimeChromeSlot } from "./runtimeChromeLoader.js";
|
|
@@ -97,11 +97,6 @@ function PageApp(t0) {
|
|
|
97
97
|
return t6;
|
|
98
98
|
}
|
|
99
99
|
const LazyEditablePageExperience = React.lazy(() => import("../preview/EditablePageExperience.js").then((module) => ({ default: module.EditablePageExperience })));
|
|
100
|
-
/** Editing lists and adds any block, so it waits for every definition, not only the page's. */
|
|
101
|
-
function AllBlocks({ camoxApp, children }) {
|
|
102
|
-
React.use(camoxApp.loadAllBlocks());
|
|
103
|
-
return children;
|
|
104
|
-
}
|
|
105
100
|
var EditingActivationBoundary = class extends React.Component {
|
|
106
101
|
state = { failed: false };
|
|
107
102
|
static getDerivedStateFromError() {
|
|
@@ -116,7 +111,7 @@ var EditingActivationBoundary = class extends React.Component {
|
|
|
116
111
|
};
|
|
117
112
|
const subscribeToHydration = () => () => {};
|
|
118
113
|
function PageExperience(t0) {
|
|
119
|
-
const $ = c(
|
|
114
|
+
const $ = c(31);
|
|
120
115
|
const { camoxApp, input, queryClient, studioDocument } = t0;
|
|
121
116
|
const { isAuthenticated, isLoading } = useAuthState();
|
|
122
117
|
const hasHydrated = React.useSyncExternalStore(subscribeToHydration, _temp, _temp2);
|
|
@@ -175,96 +170,75 @@ function PageExperience(t0) {
|
|
|
175
170
|
$[13] = t5;
|
|
176
171
|
} else t5 = $[13];
|
|
177
172
|
const isolatedPage = t5;
|
|
178
|
-
let t6;
|
|
179
|
-
if ($[14] !== input.routeKind || $[15] !== isolatedPage) {
|
|
180
|
-
t6 = input.routeKind ? /* @__PURE__ */ jsx(StudioLoadError, {}) : isolatedPage;
|
|
181
|
-
$[14] = input.routeKind;
|
|
182
|
-
$[15] = isolatedPage;
|
|
183
|
-
$[16] = t6;
|
|
184
|
-
} else t6 = $[16];
|
|
185
|
-
const fallback = t6;
|
|
186
173
|
if (isAuthenticated) {
|
|
187
|
-
let
|
|
188
|
-
if ($[
|
|
189
|
-
|
|
174
|
+
let t6;
|
|
175
|
+
if ($[14] !== camoxApp || $[15] !== hasHydrated || $[16] !== input || $[17] !== queryClient) {
|
|
176
|
+
t6 = hasHydrated ? /* @__PURE__ */ jsx(LazyEditablePageExperience, {
|
|
190
177
|
camoxApp,
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
input,
|
|
194
|
-
queryClient
|
|
195
|
-
})
|
|
178
|
+
input,
|
|
179
|
+
queryClient
|
|
196
180
|
}) : null;
|
|
197
|
-
$[
|
|
198
|
-
$[
|
|
199
|
-
$[
|
|
200
|
-
$[
|
|
201
|
-
$[
|
|
202
|
-
} else
|
|
203
|
-
const editor =
|
|
204
|
-
const
|
|
181
|
+
$[14] = camoxApp;
|
|
182
|
+
$[15] = hasHydrated;
|
|
183
|
+
$[16] = input;
|
|
184
|
+
$[17] = queryClient;
|
|
185
|
+
$[18] = t6;
|
|
186
|
+
} else t6 = $[18];
|
|
187
|
+
const editor = t6;
|
|
188
|
+
const t7 = input.routeKind ? loading : studioDocument ? null : isolatedPage;
|
|
189
|
+
let t8;
|
|
190
|
+
if ($[19] === Symbol.for("react.memo_cache_sentinel")) {
|
|
191
|
+
t8 = /* @__PURE__ */ jsx(StudioLoadError, {});
|
|
192
|
+
$[19] = t8;
|
|
193
|
+
} else t8 = $[19];
|
|
205
194
|
let t9;
|
|
206
|
-
if ($[
|
|
207
|
-
t9 =
|
|
208
|
-
fallback:
|
|
209
|
-
children:
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
})
|
|
217
|
-
}),
|
|
218
|
-
children: editor
|
|
195
|
+
if ($[20] !== editor || $[21] !== input.derived || $[22] !== input.routeKind) {
|
|
196
|
+
t9 = /* @__PURE__ */ jsx(EditingActivationBoundary, {
|
|
197
|
+
fallback: t8,
|
|
198
|
+
children: input.routeKind ? /* @__PURE__ */ jsx(React.Suspense, {
|
|
199
|
+
fallback: loading,
|
|
200
|
+
children: editor ?? loading
|
|
201
|
+
}) : /* @__PURE__ */ jsx(PreviewActivation, {
|
|
202
|
+
fallback: /* @__PURE__ */ jsx(EditingFallbackSlot, { layoutPage: !!input.derived }),
|
|
203
|
+
children: editor
|
|
204
|
+
})
|
|
219
205
|
});
|
|
220
|
-
$[
|
|
221
|
-
$[
|
|
222
|
-
$[
|
|
223
|
-
$[
|
|
224
|
-
|
|
225
|
-
$[27] = t9;
|
|
226
|
-
} else t9 = $[27];
|
|
206
|
+
$[20] = editor;
|
|
207
|
+
$[21] = input.derived;
|
|
208
|
+
$[22] = input.routeKind;
|
|
209
|
+
$[23] = t9;
|
|
210
|
+
} else t9 = $[23];
|
|
227
211
|
let t10;
|
|
228
|
-
if ($[
|
|
229
|
-
t10 = /* @__PURE__ */ jsx(
|
|
230
|
-
fallback,
|
|
231
|
-
children: t9
|
|
232
|
-
});
|
|
233
|
-
$[28] = fallback;
|
|
234
|
-
$[29] = t9;
|
|
235
|
-
$[30] = t10;
|
|
236
|
-
} else t10 = $[30];
|
|
237
|
-
let t11;
|
|
238
|
-
if ($[31] !== input || $[32] !== t10 || $[33] !== t8) {
|
|
239
|
-
t11 = /* @__PURE__ */ jsx(RuntimeChromeSlot, {
|
|
212
|
+
if ($[24] !== input || $[25] !== t7 || $[26] !== t9) {
|
|
213
|
+
t10 = /* @__PURE__ */ jsx(RuntimeChromeSlot, {
|
|
240
214
|
input,
|
|
241
|
-
fallback:
|
|
242
|
-
children:
|
|
215
|
+
fallback: t7,
|
|
216
|
+
children: t9
|
|
243
217
|
});
|
|
244
|
-
$[
|
|
245
|
-
$[
|
|
246
|
-
$[
|
|
247
|
-
$[
|
|
248
|
-
} else
|
|
249
|
-
return
|
|
218
|
+
$[24] = input;
|
|
219
|
+
$[25] = t7;
|
|
220
|
+
$[26] = t9;
|
|
221
|
+
$[27] = t10;
|
|
222
|
+
} else t10 = $[27];
|
|
223
|
+
return t10;
|
|
250
224
|
}
|
|
251
225
|
if (studioOnly) return null;
|
|
252
226
|
if (studioDocument) {
|
|
227
|
+
let t6;
|
|
228
|
+
if ($[28] === Symbol.for("react.memo_cache_sentinel")) {
|
|
229
|
+
t6 = { height: "100vh" };
|
|
230
|
+
$[28] = t6;
|
|
231
|
+
} else t6 = $[28];
|
|
253
232
|
let t7;
|
|
254
|
-
if ($[
|
|
255
|
-
t7 =
|
|
256
|
-
|
|
257
|
-
} else t7 = $[35];
|
|
258
|
-
let t8;
|
|
259
|
-
if ($[36] !== isolatedPage) {
|
|
260
|
-
t8 = /* @__PURE__ */ jsx("div", {
|
|
261
|
-
style: t7,
|
|
233
|
+
if ($[29] !== isolatedPage) {
|
|
234
|
+
t7 = /* @__PURE__ */ jsx("div", {
|
|
235
|
+
style: t6,
|
|
262
236
|
children: isolatedPage
|
|
263
237
|
});
|
|
264
|
-
$[
|
|
265
|
-
$[
|
|
266
|
-
} else
|
|
267
|
-
return
|
|
238
|
+
$[29] = isolatedPage;
|
|
239
|
+
$[30] = t7;
|
|
240
|
+
} else t7 = $[30];
|
|
241
|
+
return t7;
|
|
268
242
|
}
|
|
269
243
|
return readOnlyPage;
|
|
270
244
|
}
|
|
@@ -18,7 +18,7 @@ async function hydratePageWithApp(camoxApp) {
|
|
|
18
18
|
...hydrationData,
|
|
19
19
|
dehydratedState: restoreArtifactSources(hydrationData.dehydratedState, root)
|
|
20
20
|
};
|
|
21
|
-
await
|
|
21
|
+
await (input.presentation === "studio" ? loadRuntimeChrome() : camoxApp.loadBlocks(getPageBlockTypes(input.dehydratedState)).catch((error) => console.error("Camox: a block of this page failed to load.", error)));
|
|
22
22
|
const draftScope = createDraftScope({
|
|
23
23
|
queryClient,
|
|
24
24
|
browser: window
|
|
@@ -93,12 +93,13 @@ function PageNavigationProvider({ camoxApp, children, initialInput, queryClient
|
|
|
93
93
|
if (!response.ok) throw new Error(`Navigation failed: ${response.status}`);
|
|
94
94
|
const next = await response.json();
|
|
95
95
|
if (next.pathname !== pathname || !next.dehydratedState) throw new Error("Invalid navigation response");
|
|
96
|
-
await camoxApp.loadBlocks(getPageBlockTypes(next.dehydratedState));
|
|
96
|
+
if (initialInput.presentation !== "studio") await camoxApp.loadBlocks(getPageBlockTypes(next.dehydratedState));
|
|
97
97
|
return next;
|
|
98
98
|
}, [
|
|
99
99
|
camoxApp,
|
|
100
100
|
draftScope,
|
|
101
|
-
initialInput.runtimeBasePath
|
|
101
|
+
initialInput.runtimeBasePath,
|
|
102
|
+
initialInput.presentation
|
|
102
103
|
]);
|
|
103
104
|
const [prefetched] = React.useState(() => createPrefetchCache());
|
|
104
105
|
const headManager = React.useRef(null);
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import { prefersMarkdown } from "../routes/pageRuntime.js";
|
|
2
|
+
|
|
3
|
+
//#region src/features/runtime/publicCache.ts
|
|
4
|
+
/** The named cache public renders are kept in. */
|
|
5
|
+
const PUBLIC_CACHE_NAME = "camox-public-renders";
|
|
6
|
+
/** How long the edge keeps a render. Live changing replaces its key long before. */
|
|
7
|
+
const EDGE_MAX_AGE = 86400;
|
|
8
|
+
/** Browsers can't be purged, so they ask again each time; the edge answers them. */
|
|
9
|
+
const VISITOR_CACHE_CONTROL = "public, max-age=0, must-revalidate";
|
|
10
|
+
/** The search parameter the cache key adds to a request's URL. */
|
|
11
|
+
const CACHE_KEY_PARAM = "__camox_cache";
|
|
12
|
+
/** The project's Live cache key, as the API serves it from its edge cache. */
|
|
13
|
+
async function readLiveCacheKey(apiUrl, projectUuid) {
|
|
14
|
+
try {
|
|
15
|
+
const response = await fetch(`${apiUrl}/live-cache-keys/${encodeURIComponent(projectUuid)}`);
|
|
16
|
+
return response.ok ? await response.text() || null : null;
|
|
17
|
+
} catch {
|
|
18
|
+
return null;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Answers a public request from the cache, or renders it and keeps the render when it may be
|
|
23
|
+
* shared. Without a Live cache key, it renders without the cache: nothing tells when Live changed.
|
|
24
|
+
*/
|
|
25
|
+
async function respondThroughPublicCache(request, publicCache, render) {
|
|
26
|
+
const liveCacheKey = await publicCache.liveCacheKey();
|
|
27
|
+
if (!liveCacheKey) return render();
|
|
28
|
+
const key = cacheKeyOf(request, `${liveCacheKey}.${publicCache.build}`);
|
|
29
|
+
let cache;
|
|
30
|
+
let cached;
|
|
31
|
+
try {
|
|
32
|
+
cache = await publicCache.cache();
|
|
33
|
+
cached = await cache.match(withValidators(key, request));
|
|
34
|
+
} catch (error) {
|
|
35
|
+
console.error("The edge cache couldn't be read: the page renders without it", error);
|
|
36
|
+
return render();
|
|
37
|
+
}
|
|
38
|
+
if (cached) return forVisitors(cached, request);
|
|
39
|
+
const response = await render();
|
|
40
|
+
if (!response || !mayBeShared(response)) return response;
|
|
41
|
+
publicCache.waitUntil((async () => cache.put(key, forEdge(response.clone())))().catch((error) => {
|
|
42
|
+
console.error("The edge cache couldn't keep a render", error);
|
|
43
|
+
}));
|
|
44
|
+
return forVisitors(response, request);
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* What a request's render is kept under: its URL, the representation it asks for, and the
|
|
48
|
+
* version of Live and of the website it renders.
|
|
49
|
+
*/
|
|
50
|
+
function cacheKeyOf(request, version) {
|
|
51
|
+
const url = new URL(request.url);
|
|
52
|
+
const representation = prefersMarkdown(request.headers.get("Accept") ?? "") ? "md" : "any";
|
|
53
|
+
url.searchParams.set(CACHE_KEY_PARAM, `${version}.${representation}`);
|
|
54
|
+
return new Request(url.toString(), { method: "GET" });
|
|
55
|
+
}
|
|
56
|
+
/** The cache key, asking for the part of the render the request does. */
|
|
57
|
+
function withValidators(key, request) {
|
|
58
|
+
const headers = new Headers();
|
|
59
|
+
for (const name of [
|
|
60
|
+
"Range",
|
|
61
|
+
"If-None-Match",
|
|
62
|
+
"If-Modified-Since"
|
|
63
|
+
]) {
|
|
64
|
+
const value = request.headers.get(name);
|
|
65
|
+
if (value !== null) headers.set(name, value);
|
|
66
|
+
}
|
|
67
|
+
return new Request(key, { headers });
|
|
68
|
+
}
|
|
69
|
+
/** Whether a response is the same for every visitor: what a private render never is. */
|
|
70
|
+
function mayBeShared(response) {
|
|
71
|
+
if (response.status !== 200 || response.headers.has("Set-Cookie")) return false;
|
|
72
|
+
const cacheControl = response.headers.get("Cache-Control") ?? "";
|
|
73
|
+
return !/\b(private|no-store)\b/i.test(cacheControl);
|
|
74
|
+
}
|
|
75
|
+
/** How long browsers may keep a render: what it asked for, if it's versioned, else not at all. */
|
|
76
|
+
function visitorCacheControl(response) {
|
|
77
|
+
const cacheControl = response.headers.get("Cache-Control") ?? "";
|
|
78
|
+
return /\bmax-age=[1-9]/.test(cacheControl) ? cacheControl : VISITOR_CACHE_CONTROL;
|
|
79
|
+
}
|
|
80
|
+
/** The render as the edge keeps it: as visitors get it, for as long as the edge keeps renders. */
|
|
81
|
+
function forEdge(response) {
|
|
82
|
+
const kept = new Response(response.body, response);
|
|
83
|
+
kept.headers.set("Cache-Control", `${visitorCacheControl(response)}, s-maxage=${EDGE_MAX_AGE}`);
|
|
84
|
+
return kept;
|
|
85
|
+
}
|
|
86
|
+
/** The render as visitors get it, without the edge's lifetime, which no cache downstream gets. */
|
|
87
|
+
function forVisitors(response, request) {
|
|
88
|
+
const cacheControl = visitorCacheControl(response).replace(/,\s*s-maxage=\d+/, "");
|
|
89
|
+
const answered = new Response(request.method === "HEAD" ? null : response.body, response);
|
|
90
|
+
answered.headers.set("Cache-Control", cacheControl);
|
|
91
|
+
return answered;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
//#endregion
|
|
95
|
+
export { PUBLIC_CACHE_NAME, readLiveCacheKey, respondThroughPublicCache };
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { PUBLISHING_DRAFT_VAR } from "@camox/api-contract/drafts";
|
|
2
|
+
|
|
3
|
+
//#region src/features/runtime/publishingDraft.ts
|
|
4
|
+
/**
|
|
5
|
+
* The Worker a publish with code uploaded carries the publishing draft's ID as a variable
|
|
6
|
+
* (`PUBLISHING_DRAFT_VAR`). Its reads of the API name that draft, so while the draft publishes
|
|
7
|
+
* they see Live overlaid with it, the content its code was previewed with. Once the draft has
|
|
8
|
+
* ended, the API reads Live for them. The variable is the same for every request a Worker
|
|
9
|
+
* serves, so it's kept from the first.
|
|
10
|
+
*/
|
|
11
|
+
const PUBLISHING_DRAFT_GLOBAL = "__CAMOX_PUBLISHING_DRAFT__";
|
|
12
|
+
/** Keeps the publishing draft a Worker's environment names, if it names one. */
|
|
13
|
+
function rememberPublishingDraft(env) {
|
|
14
|
+
const draft = env?.[PUBLISHING_DRAFT_VAR];
|
|
15
|
+
if (typeof draft === "string" && draft) globalThis[PUBLISHING_DRAFT_GLOBAL] = draft;
|
|
16
|
+
}
|
|
17
|
+
/** The draft this Worker publishes, if a publish with code uploaded it. */
|
|
18
|
+
function publishingDraft() {
|
|
19
|
+
return globalThis[PUBLISHING_DRAFT_GLOBAL];
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
//#endregion
|
|
23
|
+
export { publishingDraft, rememberPublishingDraft };
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { CamoxApp } from "../../core/createApp.js";
|
|
2
|
+
//#region src/features/runtime/rendererClient.d.ts
|
|
3
|
+
/**
|
|
4
|
+
* Starts the renderer frame's document (ADR 0023): connects to the studio that loaded it, and
|
|
5
|
+
* renders the pages of the frames it is given with the site's code, in a cache of its own.
|
|
6
|
+
*/
|
|
7
|
+
export declare function startRendererWithApp(camoxApp: CamoxApp): Promise<void>;
|
|
8
|
+
//#endregion
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { getDraftReadClient } from "../../lib/api-client.js";
|
|
2
|
+
import { DraftScopeContext, createDraftScope, followUrl } from "../../lib/draft-scope.js";
|
|
3
|
+
import { StudioCacheContext, createStudioCache } from "../../lib/studio-reads.js";
|
|
4
|
+
import { STUDIO_HANDLE_KEY } from "../renderer/protocol.js";
|
|
5
|
+
import { CoreCamoxProvider } from "../provider/CoreCamoxProvider.js";
|
|
6
|
+
import { readHydrationData } from "./hydrationData.js";
|
|
7
|
+
import { CompleteBlockEditingRuntimeProvider } from "../../core/editing/CompleteBlockEditingRuntime.js";
|
|
8
|
+
import { RendererApp } from "../renderer/RendererApp.js";
|
|
9
|
+
import { connectToStudio } from "../renderer/studioConnection.js";
|
|
10
|
+
import { jsx } from "react/jsx-runtime";
|
|
11
|
+
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
|
|
12
|
+
import { createRoot } from "react-dom/client";
|
|
13
|
+
|
|
14
|
+
//#region src/features/runtime/rendererClient.tsx
|
|
15
|
+
/**
|
|
16
|
+
* Starts the renderer frame's document (ADR 0023): connects to the studio that loaded it, and
|
|
17
|
+
* renders the pages of the frames it is given with the site's code, in a cache of its own.
|
|
18
|
+
*/
|
|
19
|
+
async function startRendererWithApp(camoxApp) {
|
|
20
|
+
const handle = window.frameElement?.[STUDIO_HANDLE_KEY];
|
|
21
|
+
if (!handle) {
|
|
22
|
+
console.error("Camox: the renderer document only runs in the studio's renderer frame.");
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
const input = readHydrationData();
|
|
26
|
+
if (!input.apiUrl || !input.authenticationUrl || !input.projectUuid) {
|
|
27
|
+
console.error("Camox: the renderer document is missing its API or project.");
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
const codeLoaded = camoxApp.loadAllBlocks();
|
|
31
|
+
codeLoaded.catch((error) => console.error("Camox: the site's code failed to load.", error));
|
|
32
|
+
const queryClient = new QueryClient();
|
|
33
|
+
const draftScope = createDraftScope({
|
|
34
|
+
queryClient,
|
|
35
|
+
browser: window
|
|
36
|
+
});
|
|
37
|
+
followUrl(draftScope, window.location.href);
|
|
38
|
+
const studioCache = createStudioCache({
|
|
39
|
+
queryClient,
|
|
40
|
+
scope: draftScope,
|
|
41
|
+
api: getDraftReadClient
|
|
42
|
+
});
|
|
43
|
+
const connection = connectToStudio(handle, {
|
|
44
|
+
queryClient,
|
|
45
|
+
studioCache,
|
|
46
|
+
draftScope,
|
|
47
|
+
camoxApp
|
|
48
|
+
});
|
|
49
|
+
const container = document.createElement("div");
|
|
50
|
+
document.body.append(container);
|
|
51
|
+
createRoot(container).render(/* @__PURE__ */ jsx(QueryClientProvider, {
|
|
52
|
+
client: queryClient,
|
|
53
|
+
children: /* @__PURE__ */ jsx(DraftScopeContext.Provider, {
|
|
54
|
+
value: draftScope,
|
|
55
|
+
children: /* @__PURE__ */ jsx(StudioCacheContext.Provider, {
|
|
56
|
+
value: studioCache,
|
|
57
|
+
children: /* @__PURE__ */ jsx(CoreCamoxProvider, {
|
|
58
|
+
apiUrl: input.apiUrl,
|
|
59
|
+
authenticationUrl: input.authenticationUrl,
|
|
60
|
+
camoxApp,
|
|
61
|
+
projectUuid: input.projectUuid,
|
|
62
|
+
initialAuthenticated: true,
|
|
63
|
+
children: /* @__PURE__ */ jsx(CompleteBlockEditingRuntimeProvider, { children: /* @__PURE__ */ jsx(RendererApp, {
|
|
64
|
+
connection,
|
|
65
|
+
camoxApp,
|
|
66
|
+
codeLoaded,
|
|
67
|
+
projectUuid: input.projectUuid,
|
|
68
|
+
runtimeBasePath: input.runtimeBasePath
|
|
69
|
+
}) })
|
|
70
|
+
})
|
|
71
|
+
})
|
|
72
|
+
})
|
|
73
|
+
}));
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
//#endregion
|
|
77
|
+
export { startRendererWithApp };
|
|
@@ -8,7 +8,10 @@ import { StudioRenderInput } from "./studioApp.js";
|
|
|
8
8
|
import { DraftRead, MemberProject, PageHead, createServerApiClient } from "../routes/pageRuntime.js";
|
|
9
9
|
import { UseHeadInput } from "unhead/types";
|
|
10
10
|
//#region src/features/runtime/runtime.d.ts
|
|
11
|
-
|
|
11
|
+
/** The studio's own routes, rather than a page or the Canvas. */
|
|
12
|
+
declare const STUDIO_ROUTE_KINDS: readonly ["studio", "studio-content", "studio-settings", "studio-nested"];
|
|
13
|
+
export type StudioRouteKind = (typeof STUDIO_ROUTE_KINDS)[number];
|
|
14
|
+
export type RuntimeRouteKind = RuntimeOwnedRouteKind | "canvas" | "page" | StudioRouteKind;
|
|
12
15
|
export interface RuntimeRouteMatch {
|
|
13
16
|
kind: RuntimeRouteKind;
|
|
14
17
|
pathname: string;
|
|
@@ -32,7 +35,7 @@ export interface PageRenderInput extends DraftRead {
|
|
|
32
35
|
/** Authenticate before rendering, to show the Studio around the page. */
|
|
33
36
|
previewHandoff?: boolean;
|
|
34
37
|
previewDocument?: string;
|
|
35
|
-
routeKind?:
|
|
38
|
+
routeKind?: StudioRouteKind;
|
|
36
39
|
/** Whether the location is the Canvas with nothing selected, which only the Canvas shows. */
|
|
37
40
|
canvasOnly?: boolean;
|
|
38
41
|
project?: MemberProject;
|
|
@@ -86,6 +89,8 @@ export interface RuntimeOptions {
|
|
|
86
89
|
developmentDraft?: boolean;
|
|
87
90
|
pageClientEntryUrl?: string;
|
|
88
91
|
studioClientEntryUrl?: string;
|
|
92
|
+
/** The renderer frame's client entry, which renders the studio's pages (ADR 0023). */
|
|
93
|
+
rendererClientEntryUrl?: string;
|
|
89
94
|
getCamoxApp?: () => Promise<CamoxApp>;
|
|
90
95
|
getDocument?: () => Promise<CamoxDocument>;
|
|
91
96
|
projectUuid?: string;
|
|
@@ -96,7 +101,21 @@ export interface RuntimeOptions {
|
|
|
96
101
|
studioStylesheetUrl?: string;
|
|
97
102
|
}
|
|
98
103
|
export declare function getRuntimePathname(url: string, runtimeBasePath?: string): string | null;
|
|
104
|
+
/**
|
|
105
|
+
* Whether a request reads what every visitor sees, so a shared cache may answer it: a GET or
|
|
106
|
+
* HEAD of a page or a public output, with no session, no draft named and no preview handoff. A
|
|
107
|
+
* response that comes out private all the same, such as the Studio's, still says so.
|
|
108
|
+
*/
|
|
109
|
+
export declare function isPublicRequest(request: Request, options?: RuntimeOptions): boolean;
|
|
99
110
|
export declare function matchRuntimeRoute(pathname: string): RuntimeRouteMatch;
|
|
100
111
|
export declare function createPageHeadInput(head: PageHead): UseHeadInput;
|
|
112
|
+
/** What the renderer document tells its client: where the API, sign-in and runtime are. */
|
|
113
|
+
export interface RendererDocumentInput {
|
|
114
|
+
apiUrl?: string;
|
|
115
|
+
authenticationUrl?: string;
|
|
116
|
+
projectUuid?: string;
|
|
117
|
+
runtimeBasePath: string;
|
|
118
|
+
}
|
|
101
119
|
export declare function handleCamoxRequest(request: Request, options?: RuntimeOptions): Promise<Response | null>;
|
|
102
|
-
//#endregion
|
|
120
|
+
//#endregion
|
|
121
|
+
export {};
|