@cmssy/cli 10.10.0 → 11.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.
@@ -15,6 +15,13 @@ export interface LayoutSlotProps {
15
15
  enabledLocales: string[];
16
16
  /** Set on a VERIFIED editor request; the loader reports it as `isEdit`. */
17
17
  edit?: { editorOrigin: string | string[] };
18
+ /**
19
+ * The editor's block data, both halves, straight from the loader. Without
20
+ * `resolvedContent` the canvas shows a relation field as the raw ids it
21
+ * stores - the site looks right and the editor cannot fill the header.
22
+ */
23
+ data?: Record<string, unknown>;
24
+ resolvedContent?: Record<string, Record<string, unknown>>;
18
25
  }
19
26
 
20
27
  /**
@@ -34,6 +41,8 @@ export default function LayoutSlot({
34
41
  defaultLocale,
35
42
  enabledLocales,
36
43
  edit,
44
+ data,
45
+ resolvedContent,
37
46
  }: LayoutSlotProps) {
38
47
  if (edit) {
39
48
  return (
@@ -44,6 +53,8 @@ export default function LayoutSlot({
44
53
  defaultLocale={defaultLocale}
45
54
  enabledLocales={enabledLocales}
46
55
  edit={edit}
56
+ data={data}
57
+ resolvedContent={resolvedContent}
47
58
  load={() => import("./blocks")}
48
59
  />
49
60
  );
@@ -1,15 +1,19 @@
1
1
  ---
2
2
  import { loadCmssyPage } from "@cmssy/astro";
3
3
  import { cmssy } from "../cmssy.config";
4
+ import { blocks } from "../cmssy/blocks";
4
5
  import { Blocks } from "../components/Blocks";
5
6
  import LayoutSlot from "../cmssy/layout-slot";
6
7
 
7
8
  export const prerender = false;
8
9
 
10
+ // `blocks` is what lets the loader resolve the editor's layout data. Drop it
11
+ // and the site still renders - the editor just cannot fill the header.
9
12
  const { page, layouts, locale, defaultLocale, enabledLocales } = await loadCmssyPage(
10
13
  cmssy,
11
14
  Astro.request,
12
15
  Astro.url,
16
+ { blocks },
13
17
  );
14
18
 
15
19
  if (!page) {
@@ -1,6 +1,7 @@
1
1
  ---
2
2
  import { loadCmssyPage } from "@cmssy/astro";
3
3
  import { cmssy } from "../../cmssy.config";
4
+ import { blocks } from "../../cmssy/blocks";
4
5
  import CmssyEditor from "../../cmssy/editor";
5
6
  import LayoutSlot from "../../cmssy/layout-slot";
6
7
 
@@ -8,11 +9,10 @@ import LayoutSlot from "../../cmssy/layout-slot";
8
9
  // must be dynamic: a prerendered page never sees the query string.
9
10
  export const prerender = false;
10
11
 
11
- const { page, layouts, locale, defaultLocale, enabledLocales } = await loadCmssyPage(
12
- cmssy,
13
- Astro.request,
14
- Astro.url,
15
- );
12
+ // `blocks` is what resolves the editor's layout data. Without it the editor
13
+ // can select the header and not fill it, and a relation field shows raw ids.
14
+ const { page, layouts, locale, defaultLocale, enabledLocales, editorData } =
15
+ await loadCmssyPage(cmssy, Astro.request, Astro.url, { blocks });
16
16
  const editorOrigin = cmssy.editorOrigin ?? "*";
17
17
  ---
18
18
 
@@ -30,6 +30,8 @@ const editorOrigin = cmssy.editorOrigin ?? "*";
30
30
  defaultLocale={defaultLocale}
31
31
  enabledLocales={enabledLocales}
32
32
  edit={{ editorOrigin }}
33
+ data={editorData?.header?.data}
34
+ resolvedContent={editorData?.header?.resolvedContent}
33
35
  />
34
36
  <CmssyEditor
35
37
  client:load
@@ -47,6 +49,8 @@ const editorOrigin = cmssy.editorOrigin ?? "*";
47
49
  defaultLocale={defaultLocale}
48
50
  enabledLocales={enabledLocales}
49
51
  edit={{ editorOrigin }}
52
+ data={editorData?.footer?.data}
53
+ resolvedContent={editorData?.footer?.resolvedContent}
50
54
  />
51
55
  </body>
52
56
  </html>
@@ -3,12 +3,25 @@ import { cmssy } from "@/cmssy.config";
3
3
  import { blocks } from "@/cmssy/blocks";
4
4
  import { CmssyEditor } from "@/cmssy/editor";
5
5
  import { EditableLayout } from "@/cmssy/editable-layout";
6
+ import { publishedPaths } from "@/services/pages";
6
7
 
7
8
  type PageProps = {
8
9
  params: Promise<{ path?: string[] }>;
9
10
  searchParams: Promise<Record<string, string | string[] | undefined>>;
10
11
  };
11
12
 
13
+ // Three lines that decide whether this site is cached at all. Without
14
+ // generateStaticParams a catch-all is rendered on demand every time and
15
+ // `revalidate` is ignored - the build prints a blank Revalidate column, which is
16
+ // the only warning you get. `dynamicParams` keeps pages published after the
17
+ // build working: first request renders them, and they cache from then on.
18
+ export const revalidate = 3600;
19
+ export const dynamicParams = true;
20
+
21
+ export function generateStaticParams() {
22
+ return publishedPaths();
23
+ }
24
+
12
25
  const CmssyPage = createCmssyPage(cmssy, blocks, { editor: CmssyEditor });
13
26
 
14
27
  // The header and footer are layout blocks. They are mounted here rather than in
@@ -22,7 +35,8 @@ export default async function Page(props: PageProps) {
22
35
  config={cmssy}
23
36
  blocks={blocks}
24
37
  position={position}
25
- path={path}
38
+ path={path ?? []}
39
+ editMode={false}
26
40
  editable={EditableLayout}
27
41
  />
28
42
  );
@@ -1,4 +1,8 @@
1
- import { createCmssyEditPage, CmssyLayoutSlot } from "@cmssy/next/server";
1
+ import {
2
+ createCmssyEditPage,
3
+ CmssyLayoutSlot,
4
+ isCmssyEditMode,
5
+ } from "@cmssy/next/server";
2
6
  import { cmssy } from "@/cmssy.config";
3
7
  import { blocks } from "@/cmssy/blocks";
4
8
  import { CmssyEditor } from "@/cmssy/editor";
@@ -24,12 +28,20 @@ const CmssyEditPage = createCmssyEditPage(cmssy, blocks, {
24
28
  // header missing here is a header missing in the editor.
25
29
  export default async function EditPage(props: PageProps) {
26
30
  const { path } = await props.params;
31
+
32
+ // Not a hard `true`: this route is reachable directly, and only the proxy's
33
+ // verified rewrite sets the edit header. Hard-coding it would fetch layouts
34
+ // with the draft secret for anyone who typed the URL. Reading a header costs
35
+ // nothing here - the route is force-dynamic either way.
36
+ const editMode = await isCmssyEditMode();
37
+
27
38
  const slot = (position: "header" | "footer") => (
28
39
  <CmssyLayoutSlot
29
40
  config={cmssy}
30
41
  blocks={blocks}
31
42
  position={position}
32
- path={path}
43
+ path={path ?? []}
44
+ editMode={editMode}
33
45
  editable={EditableLayout}
34
46
  />
35
47
  );
@@ -0,0 +1,58 @@
1
+ import { createCmssyClient } from "@cmssy/core";
2
+ import { cmssy } from "@/cmssy.config";
3
+
4
+ // Your query, in your repo. The SDK does not mirror the graph - it gives you a
5
+ // client and gets out of the way, so adding a field here is your call and not a
6
+ // release of ours.
7
+ const PUBLISHED_PAGES = `query PublishedPages($workspaceSlug: String!) {
8
+ public {
9
+ page {
10
+ list(workspaceSlug: $workspaceSlug) {
11
+ slug
12
+ publishedAt
13
+ }
14
+ }
15
+ }
16
+ }`;
17
+
18
+ type PublishedPages = {
19
+ public?: {
20
+ page?: { list?: { slug?: string | null; publishedAt?: string | null }[] | null } | null;
21
+ } | null;
22
+ };
23
+
24
+ const client = createCmssyClient({
25
+ apiUrl: cmssy.apiUrl,
26
+ org: cmssy.org,
27
+ workspaceSlug: cmssy.workspaceSlug,
28
+ });
29
+
30
+ /**
31
+ * The paths to prerender, as `generateStaticParams` wants them.
32
+ *
33
+ * A catch-all route with no params generated is served on demand and cached by
34
+ * nothing, whatever `revalidate` says - so this function is what makes the
35
+ * route cacheable at all, not an optimisation on top of it.
36
+ */
37
+ export async function publishedPaths(): Promise<{ path: string[] }[]> {
38
+ const data = await client.query<PublishedPages>(
39
+ PUBLISHED_PAGES,
40
+ { workspaceSlug: cmssy.workspaceSlug },
41
+ {
42
+ // `public` routes through the org-scoped delivery path. Without it the
43
+ // request lands on the base endpoint, where an unauthenticated workspace
44
+ // lookup is by slug alone - across every org.
45
+ public: true,
46
+ // A build reads this once. A single 429 from the delivery API would fail
47
+ // the whole deploy, so retry - it is a read, nothing to double-apply.
48
+ retry: {},
49
+ },
50
+ );
51
+
52
+ const pages = data?.public?.page?.list ?? [];
53
+ return pages
54
+ .filter((page) => page.publishedAt)
55
+ .map((page) => ({
56
+ path: (page.slug ?? "").split("/").filter(Boolean),
57
+ }));
58
+ }
@@ -15,6 +15,13 @@ export interface LayoutSlotProps {
15
15
  enabledLocales: string[];
16
16
  /** Set on a VERIFIED editor request; the loader reports it as `isEdit`. */
17
17
  edit?: { editorOrigin: string | string[] };
18
+ /**
19
+ * The editor's block data, both halves, straight from the loader. Without
20
+ * `resolvedContent` the canvas shows a relation field as the raw ids it
21
+ * stores - the site looks right and the editor cannot fill the header.
22
+ */
23
+ data?: Record<string, unknown>;
24
+ resolvedContent?: Record<string, Record<string, unknown>>;
18
25
  }
19
26
 
20
27
  /**
@@ -31,6 +38,8 @@ export function LayoutSlot({
31
38
  defaultLocale,
32
39
  enabledLocales,
33
40
  edit,
41
+ data,
42
+ resolvedContent,
34
43
  }: LayoutSlotProps) {
35
44
  if (edit) {
36
45
  return (
@@ -41,6 +50,8 @@ export function LayoutSlot({
41
50
  defaultLocale={defaultLocale}
42
51
  enabledLocales={enabledLocales}
43
52
  edit={edit}
53
+ data={data}
54
+ resolvedContent={resolvedContent}
44
55
  load={() => import("./blocks")}
45
56
  />
46
57
  );
@@ -6,7 +6,9 @@ import { CmssyEditor } from "../cmssy/editor";
6
6
  import { LayoutSlot } from "../cmssy/layout-slot";
7
7
  import type { Route } from "./+types/page";
8
8
 
9
- export const loader = createCmssyLoader(cmssy);
9
+ // `blocks` is what lets the loader resolve the editor's layout data. Drop it
10
+ // and the site still renders - the editor just cannot fill the header.
11
+ export const loader = createCmssyLoader(cmssy, { blocks });
10
12
 
11
13
  // Without these the admin cannot frame the site, and the editor shows an empty
12
14
  // box with no error anywhere.
@@ -22,6 +24,7 @@ export default function CmssyPage({ loaderData }: Route.ComponentProps) {
22
24
  isEdit,
23
25
  editorOrigin,
24
26
  diagnostics,
27
+ editorData,
25
28
  } = loaderData;
26
29
 
27
30
  if (diagnostics) {
@@ -38,6 +41,8 @@ export default function CmssyPage({ loaderData }: Route.ComponentProps) {
38
41
  defaultLocale={defaultLocale}
39
42
  enabledLocales={enabledLocales}
40
43
  edit={isEdit ? { editorOrigin } : undefined}
44
+ data={editorData?.[position]?.data}
45
+ resolvedContent={editorData?.[position]?.resolvedContent}
41
46
  />
42
47
  );
43
48
 
package/dist/index.js CHANGED
@@ -161,10 +161,11 @@ var FRAMEWORKS = [
161
161
  label: "Next.js",
162
162
  createCommand: "npx create-next-app@latest",
163
163
  detect: ["next"],
164
- dependencies: ["@cmssy/next", "@cmssy/react"],
164
+ dependencies: ["@cmssy/next", "@cmssy/react", "@cmssy/core"],
165
165
  files: [
166
166
  "cmssy.config.ts",
167
167
  "proxy.ts",
168
+ "services/pages.ts",
168
169
  "cmssy/blocks.ts",
169
170
  "cmssy/editor.tsx",
170
171
  "cmssy/editable-layout.tsx",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cmssy/cli",
3
- "version": "10.10.0",
3
+ "version": "11.1.0",
4
4
  "description": "The cmssy CLI: `cmssy init` wires cmssy into an existing Next.js, Astro or React Router app; `cmssy link` connects it to a workspace and verifies the editor wiring.",
5
5
  "keywords": [
6
6
  "cmssy",
@@ -27,7 +27,7 @@
27
27
  "assets"
28
28
  ],
29
29
  "dependencies": {
30
- "@cmssy/core": "10.10.0"
30
+ "@cmssy/core": "11.1.0"
31
31
  },
32
32
  "devDependencies": {
33
33
  "@types/node": "^20.0.0",