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.
Files changed (127) hide show
  1. package/dist/core/createApp.d.ts +1 -0
  2. package/dist/core/createApp.js +1 -0
  3. package/dist/core/editing/contentEdits.js +78 -0
  4. package/dist/core/editing/createEditableBlock.js +277 -311
  5. package/dist/core/layoutContext.d.ts +1 -0
  6. package/dist/core/lib/contentType.js +2 -2
  7. package/dist/core/recordItem.js +3 -3
  8. package/dist/features/canvas/CamoxCanvas.js +15 -15
  9. package/dist/features/canvas/CanvasLoadingScreen.js +15 -11
  10. package/dist/features/canvas/CanvasPageFrame.js +81 -87
  11. package/dist/features/canvas/CanvasPreviewPageSlot.js +1 -1
  12. package/dist/features/canvas/DerivedPagePathInput.js +12 -12
  13. package/dist/features/canvas/canvasOverlayGeometry.js +1 -1
  14. package/dist/features/canvas/canvasStore.js +2 -2
  15. package/dist/features/canvas/sidebar/CanvasInspector.js +1 -1
  16. package/dist/features/canvas/sidebar/CuratedPageInspector.js +1 -1
  17. package/dist/features/canvas/sidebar/LayoutPageBlocks.js +23 -31
  18. package/dist/features/canvas/useCanvasCamera.js +1 -1
  19. package/dist/features/canvas/useDiscoveredPreviewPaths.js +16 -9
  20. package/dist/features/content/ContentAssets.js +84 -88
  21. package/dist/features/page/CuratedPageContent.js +1 -1
  22. package/dist/features/preview/CamoxPreview.d.ts +8 -4
  23. package/dist/features/preview/CamoxPreview.js +258 -443
  24. package/dist/features/preview/EditablePageContent.js +244 -2
  25. package/dist/features/preview/EditablePageExperience.js +19 -13
  26. package/dist/features/preview/components/AddBlockSheet.js +55 -15
  27. package/dist/features/preview/components/BlockActionsPopover.js +23 -51
  28. package/dist/features/preview/components/BlockPeek.js +66 -0
  29. package/dist/features/preview/components/BlockThumbnail.js +134 -119
  30. package/dist/features/preview/components/CanvasPreview.js +1 -1
  31. package/dist/features/preview/components/CreatePageModal.js +150 -156
  32. package/dist/features/preview/components/FieldToolbar.js +1 -1
  33. package/dist/features/preview/components/Frame.js +27 -4
  34. package/dist/features/preview/components/MobilePreviewDrawer.js +6 -9
  35. package/dist/features/preview/components/PageInfo.js +161 -126
  36. package/dist/features/preview/components/PagePicker.js +2 -2
  37. package/dist/features/preview/components/PageTree.js +174 -190
  38. package/dist/features/preview/components/PreviewPanel.js +13 -67
  39. package/dist/features/preview/components/PreviewToolbar.js +1 -1
  40. package/dist/features/preview/components/ReferenceFieldEditor.js +4 -4
  41. package/dist/features/preview/components/ReferenceListFieldEditor.js +4 -4
  42. package/dist/features/preview/components/RemovedBlockPlaceholder.js +37 -70
  43. package/dist/features/preview/components/RepeatableItemsList.js +1 -1
  44. package/dist/features/preview/components/SelectionEditor.js +248 -256
  45. package/dist/features/preview/components/StudioEditingFallback.js +22 -26
  46. package/dist/features/preview/components/useFieldWriter.js +19 -19
  47. package/dist/features/preview/components/useRecordView.js +105 -147
  48. package/dist/features/preview/components/useRepeatableItemActions.js +0 -2
  49. package/dist/features/preview/pageRecord.js +30 -24
  50. package/dist/features/preview/previewConstants.js +2 -8
  51. package/dist/features/preview/previewStore.js +2 -0
  52. package/dist/features/preview/studioNotice.js +20 -0
  53. package/dist/features/preview/usePreviewViewportActions.js +1 -1
  54. package/dist/features/provider/AuthenticatedCamoxProvider.js +47 -31
  55. package/dist/features/provider/components/CommandPalette.js +2 -5
  56. package/dist/features/provider/signInShortcut.js +1 -2
  57. package/dist/features/provider/useAdminShortcuts.js +47 -53
  58. package/dist/features/renderer/RendererApp.js +542 -0
  59. package/dist/features/renderer/RendererFrames.js +450 -0
  60. package/dist/features/renderer/definitionAnswers.js +26 -0
  61. package/dist/features/renderer/protocol.js +35 -0
  62. package/dist/features/renderer/rendererHost.js +249 -0
  63. package/dist/features/renderer/studioConnection.js +186 -0
  64. package/dist/features/routes/pageRuntime.d.ts +2 -1
  65. package/dist/features/routes/pageRuntime.js +34 -22
  66. package/dist/features/runtime/RuntimeChrome.js +2 -5
  67. package/dist/features/runtime/assetPrefix.js +14 -0
  68. package/dist/features/runtime/breadcrumbs.js +1 -1
  69. package/dist/features/runtime/editingFallback.js +4 -8
  70. package/dist/features/runtime/livePages.js +1 -1
  71. package/dist/features/runtime/pageApp.js +57 -83
  72. package/dist/features/runtime/pageClient.js +1 -1
  73. package/dist/features/runtime/pageNavigation.js +3 -2
  74. package/dist/features/runtime/publicCache.js +95 -0
  75. package/dist/features/runtime/publishingDraft.js +23 -0
  76. package/dist/features/runtime/rendererClient.d.ts +8 -0
  77. package/dist/features/runtime/rendererClient.js +77 -0
  78. package/dist/features/runtime/runtime.d.ts +22 -3
  79. package/dist/features/runtime/runtime.js +209 -96
  80. package/dist/features/runtime/runtimeRoutes.d.ts +2 -0
  81. package/dist/features/runtime/runtimeRoutes.js +2 -0
  82. package/dist/features/runtime/worker.d.ts +14 -1
  83. package/dist/features/runtime/worker.js +34 -4
  84. package/dist/features/settings/AnalyticsSettings.js +622 -0
  85. package/dist/features/settings/CamoxSettings.js +95 -0
  86. package/dist/features/settings/FaviconSettings.js +254 -0
  87. package/dist/features/settings/ProjectNameSettings.js +145 -0
  88. package/dist/features/settings/SettingsPart.js +73 -0
  89. package/dist/features/settings/SocialProfilesSettings.js +245 -0
  90. package/dist/features/settings/useProjectMutation.js +61 -0
  91. package/dist/features/studio/FollowAgentProvider.js +10 -10
  92. package/dist/features/studio/components/DraftsControl.js +136 -131
  93. package/dist/features/studio/components/LocalePicker.js +178 -174
  94. package/dist/features/studio/components/Navbar.js +107 -66
  95. package/dist/features/studio/components/ProjectMenu.js +119 -171
  96. package/dist/features/studio/components/ReviewDraftSheet.js +1151 -991
  97. package/dist/features/studio/components/ThreadMessages.js +100 -65
  98. package/dist/features/studio/components/UserButton.js +2 -2
  99. package/dist/features/studio/routes.js +3 -1
  100. package/dist/features/studio/showInCanvas.js +129 -0
  101. package/dist/features/vite/definitionsSync.js +5 -6
  102. package/dist/features/vite/hostedChecks.js +127 -0
  103. package/dist/features/vite/runtimeDev.js +58 -17
  104. package/dist/features/vite/staticAssetHeaders.js +52 -0
  105. package/dist/features/vite/vite.d.ts +1 -1
  106. package/dist/features/vite/vite.js +49 -9
  107. package/dist/hooks/use-page-destinations.js +10 -10
  108. package/dist/lib/api-client-server.d.ts +1 -1
  109. package/dist/lib/api-client.d.ts +2 -1
  110. package/dist/lib/draft-scope.d.ts +1 -0
  111. package/dist/lib/locale-path.d.ts +3 -11
  112. package/dist/lib/locale-path.js +3 -27
  113. package/dist/lib/queries.js +7 -1
  114. package/dist/lib/revisions.js +20 -0
  115. package/dist/lib/shortcuts.js +29 -0
  116. package/dist/lib/studio-definitions.js +160 -0
  117. package/dist/lib/studio-reads.d.ts +1 -0
  118. package/dist/lib/studio-reads.js +65 -21
  119. package/dist/lib/use-project-room.js +16 -10
  120. package/dist/lib/utils.js +1 -104
  121. package/dist/package.js +1 -1
  122. package/dist/studio.css +1 -1
  123. package/package.json +9 -4
  124. package/skills/camox/SKILL.md +4 -0
  125. package/skills/camox/references/artifacts.md +1 -1
  126. package/dist/features/studio/components/FaviconDialog.js +0 -318
  127. 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(38);
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 t7;
188
- if ($[17] !== camoxApp || $[18] !== hasHydrated || $[19] !== input || $[20] !== queryClient) {
189
- t7 = hasHydrated ? /* @__PURE__ */ jsx(AllBlocks, {
174
+ let t6;
175
+ if ($[14] !== camoxApp || $[15] !== hasHydrated || $[16] !== input || $[17] !== queryClient) {
176
+ t6 = hasHydrated ? /* @__PURE__ */ jsx(LazyEditablePageExperience, {
190
177
  camoxApp,
191
- children: /* @__PURE__ */ jsx(LazyEditablePageExperience, {
192
- camoxApp,
193
- input,
194
- queryClient
195
- })
178
+ input,
179
+ queryClient
196
180
  }) : null;
197
- $[17] = camoxApp;
198
- $[18] = hasHydrated;
199
- $[19] = input;
200
- $[20] = queryClient;
201
- $[21] = t7;
202
- } else t7 = $[21];
203
- const editor = t7;
204
- const t8 = input.routeKind ? loading : isolatedPage;
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 ($[22] !== editor || $[23] !== input.derived || $[24] !== input.previewDocument || $[25] !== input.routeKind || $[26] !== readOnlyPage) {
207
- t9 = input.routeKind ? /* @__PURE__ */ jsx(React.Suspense, {
208
- fallback: loading,
209
- children: editor ?? loading
210
- }) : /* @__PURE__ */ jsx(PreviewActivation, {
211
- fallback: /* @__PURE__ */ jsx(EditingFallbackSlot, {
212
- layoutPage: !!input.derived,
213
- children: /* @__PURE__ */ jsx(Frame, {
214
- serverOnly: !!input.previewDocument,
215
- children: readOnlyPage
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
- $[22] = editor;
221
- $[23] = input.derived;
222
- $[24] = input.previewDocument;
223
- $[25] = input.routeKind;
224
- $[26] = readOnlyPage;
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 ($[28] !== fallback || $[29] !== t9) {
229
- t10 = /* @__PURE__ */ jsx(EditingActivationBoundary, {
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: t8,
242
- children: t10
215
+ fallback: t7,
216
+ children: t9
243
217
  });
244
- $[31] = input;
245
- $[32] = t10;
246
- $[33] = t8;
247
- $[34] = t11;
248
- } else t11 = $[34];
249
- return t11;
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 ($[35] === Symbol.for("react.memo_cache_sentinel")) {
255
- t7 = { height: "100vh" };
256
- $[35] = t7;
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
- $[36] = isolatedPage;
265
- $[37] = t8;
266
- } else t8 = $[37];
267
- return t8;
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 Promise.all([camoxApp.loadBlocks(getPageBlockTypes(input.dehydratedState)).catch((error) => console.error("Camox: a block of this page failed to load.", error)), input.presentation === "studio" ? loadRuntimeChrome() : void 0]);
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
- export type RuntimeRouteKind = RuntimeOwnedRouteKind | "canvas" | "page" | "studio" | "studio-content" | "studio-nested";
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?: "studio" | "studio-content" | "studio-nested";
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 {};