### Install @cmssy/react Source: https://github.com/cmssy-io/cmssy-sdk/blob/main/packages/react/README.md Install the @cmssy/react package using pnpm. ```bash pnpm add @cmssy/react ``` -------------------------------- ### Minimal Next.js Setup Source: https://github.com/cmssy-io/cmssy-sdk/blob/main/_autodocs/README.md Demonstrates the minimal setup required for a Next.js application using the cmssy SDK, including configuration and page routing. ```APIDOC ## Minimal Next.js Setup ### Description This section provides a minimal setup for a Next.js application using the cmssy SDK. It includes the necessary configuration file (`cmssy.config.ts`), the main page component (`app/[[...path]]/page.tsx`), and the draft route handler (`app/api/draft/route.ts`). ### Installation ```bash npm install @cmssy/react @cmssy/next ``` ### Configuration (`cmssy.config.ts`) ```typescript // cmssy.config.ts import type { CmssyNextConfig } from "@cmssy/next"; export const cmssy: CmssyNextConfig = { apiUrl: process.env.CMSSY_API_URL!, workspaceSlug: process.env.CMSSY_WORKSPACE_SLUG!, draftSecret: process.env.CMSSY_DRAFT_SECRET!, editorOrigin: process.env.CMSSY_EDITOR_ORIGIN!, }; ``` ### Page Route (`app/[[...path]]/page.tsx`) ```typescript // app/[[...path]]/page.tsx import { createCmssyPage } from "@cmssy/next"; import { cmssy } from "@/cmssy.config"; import { blocks } from "@/cmssy/blocks"; export default createCmssyPage(cmssy, blocks); ``` ### Draft Route (`app/api/draft/route.ts`) ```typescript // app/api/draft/route.ts import { createDraftRoute } from "@cmssy/next"; import { cmssy } from "@/cmssy.config"; export const GET = createDraftRoute(cmssy); ``` ### Environment Variables - `CMSSY_API_URL` — GraphQL API endpoint (e.g., `https://api.cmssy.io/graphql`) - `CMSSY_WORKSPACE_SLUG` — Workspace identifier - `CMSSY_DRAFT_SECRET` — Secret for accessing draft content (16+ chars) - `CMSSY_EDITOR_ORIGIN` — Origin allowed to frame the app (e.g., `https://app.cmssy.io`) - `CMSSY_AUTH_SESSION_SECRET` — Secret for encrypting session cookies (32+ chars) ``` -------------------------------- ### Startup Validation Example Source: https://github.com/cmssy-io/cmssy-sdk/blob/main/_autodocs/configuration.md Example of how to validate CMSSY authentication configuration during application startup. This ensures that necessary authentication settings are correctly configured before proceeding. ```typescript // app.ts or server startup import { assertAuthConfig } from "@cmssy/next"; if (process.env.CMSSY_ENABLE_AUTH === "true") { assertAuthConfig(cmssy); // Throws if invalid console.log("Auth enabled and validated"); } ``` -------------------------------- ### Install cmssy SDK Packages Source: https://github.com/cmssy-io/cmssy-sdk/blob/main/_autodocs/README.md Install the core React and Next.js adapter packages for the cmssy SDK. ```bash npm install @cmssy/react @cmssy/next ``` -------------------------------- ### .env.local Example Source: https://github.com/cmssy-io/cmssy-sdk/blob/main/_autodocs/configuration.md An example of how to populate Cmssy configuration using environment variables in a .env.local file. This includes required and optional settings for API, workspace, draft mode, editor origin, authentication, and site URL. ```bash CMSSY_API_URL=https://api.cmssy.io/graphql CMSSY_WORKSPACE_SLUG=my-workspace CMSSY_DRAFT_SECRET=your-16-or-more-character-secret-here CMSSY_EDITOR_ORIGIN=https://app.cmssy.io CMSSY_AUTH_SESSION_SECRET=your-32-or-more-character-secret-here CMSSY_SITE_URL=https://example.com ``` -------------------------------- ### Next.js Configuration Example Source: https://github.com/cmssy-io/cmssy-sdk/blob/main/_autodocs/configuration.md Example of how to configure Cmssy SDK for a Next.js application using environment variables. Ensure all required environment variables are set. ```typescript // cmssy.config.ts import type { CmssyNextConfig } from "@cmssy/next"; export const cmssy: CmssyNextConfig = { apiUrl: process.env.CMSSY_API_URL!, workspaceSlug: process.env.CMSSY_WORKSPACE_SLUG!, draftSecret: process.env.CMSSY_DRAFT_SECRET!, editorOrigin: process.env.CMSSY_EDITOR_ORIGIN!, siteUrl: "https://example.com", defaultLocale: "en", enabledLocales: ["en", "es", "fr"], auth: { modelSlug: "user", sessionSecret: process.env.CMSSY_AUTH_SESSION_SECRET!, }, }; ``` -------------------------------- ### Add @cmssy/next and @cmssy/react Source: https://github.com/cmssy-io/cmssy-sdk/blob/main/packages/next/README.md Install the necessary packages for @cmssy/next and @cmssy/react using pnpm. ```bash pnpm add @cmssy/next @cmssy/react ``` -------------------------------- ### Install cmssy Next.js and React SDKs Source: https://github.com/cmssy-io/cmssy-sdk/blob/main/docs/getting-started/quickstart.md Install the necessary packages for integrating cmssy with your Next.js application. ```bash npm i @cmssy/next @cmssy/react ``` -------------------------------- ### Example: Static Locale Configuration Source: https://github.com/cmssy-io/cmssy-sdk/blob/main/_autodocs/api-reference/locale.md Illustrates how to configure `CmssyNextConfig` to always use a specific static locale for resolution. ```typescript // Always use a specific locale const cmssy: CmssyNextConfig = { ...config, resolveLocale: () => "es", }; ``` -------------------------------- ### Environment Variables Setup Source: https://github.com/cmssy-io/cmssy-sdk/blob/main/examples/fakturownia-invoicing/README.md Required environment variables for integrating with cmssy and Fakturownia. Ensure these are set in your application's environment. ```env CMSSY_WEBHOOK_SECRET=whsec_... CMSSY_API_URL=https://api.cmssy.io CMSSY_API_TOKEN=cs_... FAKTUROWNIA_DOMAIN=your-account FAKTUROWNIA_API_TOKEN=... ``` -------------------------------- ### Example: Sending a Ready Message to the Editor Source: https://github.com/cmssy-io/cmssy-sdk/blob/main/_autodocs/api-reference/editor-bridge.md Demonstrates how to use the `postToEditor` function to send a 'ready' message, including protocol version, block information, and schema definitions. ```typescript import { postToEditor, PROTOCOL_VERSION } from "@cmssy/react"; postToEditor({ type: "cmssy:ready", protocolVersion: PROTOCOL_VERSION, blocks: [ { id: "b1", type: "hero", bounds: { x: 0, y: 0, width: 100, height: 200 } }, ], schemas: { hero: { title: { type: "singleLine", label: "Title" } }, }, }); ``` -------------------------------- ### GET /api/draft Route Handler Source: https://github.com/cmssy-io/cmssy-sdk/blob/main/_autodocs/api-reference/create-draft-route.md Example of how to implement the draft route handler in your Next.js application. ```APIDOC ## GET /api/draft ### Description This route handler enables Next.js Draft Mode preview for accessing unpublished pages. ### Method GET ### Endpoint `/api/draft` ### Query Parameters - `secret` (string) - Required - The draft secret (must match `config.draftSecret`) - `redirect` (string) - Optional - Path to redirect to (must start with `/`, defaults to `defaultRedirect`) ### Request Example ``` GET /api/draft?secret=your-secret-here&redirect=/about ``` ### Response - `307 Temporary Redirect` to the specified path with draft mode enabled. ### Security - Secret is compared using **timing-safe comparison**. - Redirect URL is validated to prevent open redirects (must start with `/`, cannot contain `//` or `\`, must be same-origin). ``` -------------------------------- ### Render Header Layout with CmssyServerLayout Source: https://github.com/cmssy-io/cmssy-sdk/blob/main/_autodocs/api-reference/block-components.md Example demonstrating how to fetch layouts and render the header section using CmssyServerLayout. Ensure fetchLayouts is configured correctly. ```typescript import { CmssyServerLayout, fetchLayouts } from "@cmssy/react"; const layouts = await fetchLayouts(config, "/"); const headerBlocks = layouts.find(g => g.position === "header")?.blocks; return (
); ``` -------------------------------- ### CmssyEditor Component Example Source: https://github.com/cmssy-io/cmssy-sdk/blob/main/_autodocs/api-reference/create-cmssy-page.md An example of how to use the CmssyEditor component in a Next.js application. This demonstrates mounting the live-edit bridge and rendering blocks based on the provided configuration. ```APIDOC Use in your editor component to mount the live-edit bridge: ```typescript // components/CmssyEditor.tsx "use client"; import { CmssyLocaleProvider } from "@cmssy/react/client"; import type { CmssyEditorProps } from "@cmssy/next"; export default function CmssyEditor({ page, locale, defaultLocale, edit, }: CmssyEditorProps) { return ( {/* Mount editor bridge here */} {page.blocks.map((block) => { const Component = edit.blockMap[block.type]; if (!Component) return null; return ( ); })} ); } ``` ``` -------------------------------- ### Example: User-Specific Locale Configuration Source: https://github.com/cmssy-io/cmssy-sdk/blob/main/_autodocs/api-reference/locale.md Shows how to set up `CmssyNextConfig` to resolve the locale based on a logged-in user's preferences, using an asynchronous function. ```typescript // Get locale from logged-in user's preferences const cmssy: CmssyNextConfig = { ...config, resolveLocale: async () => { const user = await getCmssyUser(); return user?.preferredLocale ?? "en"; }, }; ``` -------------------------------- ### Example: Define a Hero Block Component Source: https://github.com/cmssy-io/cmssy-sdk/blob/main/_autodocs/api-reference/block-registry.md Demonstrates how to use `defineBlock` to create a 'Hero Section' block. This example includes defining editable fields for title, subtitle, image, CTA text, and CTA link, along with an optional server-side data loader and a React component for rendering the hero section. ```typescript import { defineBlock, fields } from "@cmssy/react"; const HeroBlock = defineBlock({ type: "hero", label: "Hero Section", category: "Content", icon: "image", props: { title: fields.singleLine({ label: "Title", placeholder: "Enter title", required: true, }), subtitle: fields.multiLine({ label: "Subtitle", }), image: fields.media({ label: "Background Image", }), ctaText: fields.singleLine({ label: "CTA Button Label", }), ctaUrl: fields.link({ label: "CTA Link", }), }, loader: async ({ content }) => { // Optional: fetch data server-side for this block const imageMetadata = await fetchImageMetadata(content.image); return { imageMetadata }; }, component: ({ content, data }) => (
{content.image && ( {content.title} )}

{content.title}

{content.subtitle &&

{content.subtitle}

} {content.ctaUrl && ( {content.ctaText || "Learn More"} )}
), }); export default HeroBlock; ``` -------------------------------- ### Normalize Slug Examples Source: https://github.com/cmssy-io/cmssy-sdk/blob/main/_autodocs/api-reference/content-client.md Examples demonstrating the usage of the normalizeSlug function with various inputs, including undefined, empty strings, single strings, and string arrays. ```typescript import { normalizeSlug } from "@cmssy/react"; normalizeSlug(undefined); // "/" normalizeSlug("/"); // "/" normalizeSlug("about"); // "/about" normalizeSlug(["blog", "post"]); // "/blog/post" normalizeSlug(["", "en", "about"]); // "/en/about" ``` -------------------------------- ### Server Page Rendering Example Source: https://github.com/cmssy-io/cmssy-sdk/blob/main/_autodocs/api-reference/block-components.md Example of how to use CmssyServerPage in a Next.js App Router page. It fetches page data and passes it along with block definitions and locale information to the component. ```typescript // app/[[...path]]/page.tsx import { CmssyServerPage, fetchPage } from "@cmssy/react"; import { blocks } from "@/cmssy/blocks"; const config = { apiUrl: process.env.CMSSY_API_URL!, workspaceSlug: process.env.CMSSY_WORKSPACE_SLUG!, }; export default async function Page({ params }: { params: Promise<{ path?: string[] }> }) { const { path } = await params; const page = await fetchPage(config, path); return ( ); } ``` -------------------------------- ### Example Usage of fetchProduct Source: https://github.com/cmssy-io/cmssy-sdk/blob/main/_autodocs/api-reference/commerce.md Demonstrates how to import and use the fetchProduct function in a Next.js application. It shows how to handle the product data and render different components based on whether the product was found. ```typescript import { fetchProduct } from "@cmssy/next"; const product = await fetchProduct(config, "prod_123", { populate: ["variants", "reviews"], }); if (product) { return ; } else { return ; } ``` -------------------------------- ### Mounting createCmssyAuthRoute in Next.js Source: https://github.com/cmssy-io/cmssy-sdk/blob/main/_autodocs/api-reference/authentication.md Example of how to export the POST and GET methods generated by `createCmssyAuthRoute` to be used as your Next.js API route handler. ```typescript // app/api/auth/[action]/route.ts import { createCmssyAuthRoute } from "@cmssy/next"; import { cmssy } from "@/cmssy.config"; export const { POST, GET } = createCmssyAuthRoute(cmssy); ``` -------------------------------- ### Next.js Page Component Example Source: https://github.com/cmssy-io/cmssy-sdk/blob/main/_autodocs/api-reference/create-cmssy-page.md Example of how to use createCmssyPage in a Next.js App Router catch-all route (`app/[[...path]]/page.tsx`). This sets up the dynamic page rendering with optional edit mode support. ```typescript // app/[[...path]]/page.tsx import { createCmssyPage } from "@cmssy/next"; import { cmssy } from "@/cmssy.config"; import { blocks } from "@/cmssy/blocks"; import CmssyEditor from "@/components/CmssyEditor"; export default createCmssyPage(cmssy, blocks, { editor: CmssyEditor, }); ``` -------------------------------- ### Example Usage of resolveForms Source: https://github.com/cmssy-io/cmssy-sdk/blob/main/_autodocs/api-reference/forms.md Demonstrates how to fetch a page and then resolve its associated forms. This is useful for integrating forms into your application's UI. ```typescript import { resolveForms, fetchPage } from "@cmssy/react"; const page = await fetchPage(config, "/contact"); const forms = await resolveForms(config, page.blocks, "en", "en"); if (forms["contact-form"]) { // Render the form in a block } ``` -------------------------------- ### Define Products Grid Block with Fields Source: https://github.com/cmssy-io/cmssy-sdk/blob/main/_autodocs/api-reference/fields.md Example of defining a 'products' grid block using singleLine, select, and numeric fields. Demonstrates setting default values and specifying required fields. ```typescript const ProductsBlock = defineBlock({ type: "products", label: "Products Grid", props: { title: fields.singleLine({ label: "Section Title", defaultValue: "Our Products", }), category: fields.select({ label: "Filter by Category", options: ["All", "Electronics", "Clothing", "Home"], defaultValue: "All", }), columns: fields.numeric({ label: "Grid Columns", defaultValue: 3, required: true, }), limit: fields.numeric({ label: "Max Products", defaultValue: 12, }), }, component: ({ content }) => { return (

{content.title}

{/* Fetch and render products based on content.category, content.limit */}
); }, }); ``` -------------------------------- ### Example Usage of getBlockContentForLanguage Source: https://github.com/cmssy-io/cmssy-sdk/blob/main/_autodocs/api-reference/locale.md Demonstrates how to use `getBlockContentForLanguage` to retrieve localized content for a specific locale, showing fallback behavior. ```typescript // In a block loader or CmssyServerPage import { getBlockContentForLanguage } from "@cmssy/react"; const block = { id: "b1", type: "hero", content: { title: { en: "Welcome", es: "Bienvenido" }, subtitle: "Same in all languages", description: { en: "English only" }, }, }; const esContent = getBlockContentForLanguage( block.content, "es", "en", ["en", "es"], ); // esContent.title = "Bienvenido" // esContent.subtitle = "Same in all languages" // esContent.description = "English only" (fallback to any) ``` -------------------------------- ### Client-Side Login Form Example Source: https://github.com/cmssy-io/cmssy-sdk/blob/main/_autodocs/api-reference/authentication.md A React component for handling user login. It manages form state, submits credentials to the /api/auth/signin endpoint, and handles success or error responses. ```typescript // components/LoginForm.tsx "use client"; import { useState } from "react"; export default function LoginForm() { const [email, setEmail] = useState(""); const [password, setPassword] = useState(""); const [error, setError] = useState(""); const handleSubmit = async (e: React.FormEvent) => { e.preventDefault(); const res = await fetch("/api/auth/signin", { method: "POST", headers: { "content-type": "application/json" }, body: JSON.stringify({ email, password }), }); const result = await res.json(); if (result.success) { window.location.href = "/account"; } else { setError(result.message); } }; return (
setEmail(e.target.value)} placeholder="Email" required /> setPassword(e.target.value)} placeholder="Password" required /> {error &&

{error}

}
); } ``` -------------------------------- ### Complete Webhook Handler Source: https://github.com/cmssy-io/cmssy-sdk/blob/main/_autodocs/api-reference/webhooks.md This example demonstrates a complete webhook handler for Cmssy events using Next.js. It includes verification of the webhook signature, idempotency checks, event handling logic for different order statuses, and logging of processed events. ```APIDOC ## POST /api/webhooks/cmssy ### Description Handles incoming webhook events from Cmssy. Verifies the signature, processes the event based on its type, and logs the outcome. ### Method POST ### Endpoint /api/webhooks/cmssy ### Request Headers - **x-cmssy-signature** (string) - Required - The signature of the webhook payload. ### Request Body (Raw request body text) ### Request Example (See provided TypeScript example for structure) ### Response #### Success Response (200) - Returns 'OK' if the webhook is processed successfully. - Returns 'Already processed' if the event has already been handled. #### Error Response (401) - Returns 'Verification failed' if the webhook signature is invalid. #### Error Response (500) - Returns 'Processing error' if an error occurs during event processing, indicating a retry might be necessary. ### Notes - Ensure the `CMSSY_WEBHOOK_SECRET` environment variable is set. - The `verifyCmssyWebhook` function from `@cmssy/next` is used for signature verification. - The example includes logic for `order.paid`, `order.refunded`, and `order.fulfilled` events. ``` -------------------------------- ### Define a Block with Form Integration Source: https://github.com/cmssy-io/cmssy-sdk/blob/main/_autodocs/api-reference/forms.md This example shows how to define a custom block that integrates with form definitions provided via context. It demonstrates accessing form data and rendering a form dynamically. ```typescript // Block definition const ContactBlock = defineBlock({ type: "contact", props: { formId: fields.singleLine({ label: "Form" }), }, component: ({ content, context }) => { const form = context?.forms?.[content.formId]; if (!form) return

Form not found

; return (
{form.fields.map(field => ( ))}
); }, }); ``` -------------------------------- ### Create Draft Route Setup Source: https://github.com/cmssy-io/cmssy-sdk/blob/main/_autodocs/api-reference/create-draft-route.md This snippet shows how to import and use the `createDraftRoute` function from the `@cmssy/next` package within your Next.js API route. It demonstrates passing your Cmssy configuration to the function. ```APIDOC ## GET /api/draft ### Description This endpoint enables draft mode for content preview. It is typically called by an external editor or preview service. ### Method GET ### Endpoint /api/draft ### Parameters #### Query Parameters - **secret** (string) - Required - The secret token used to authenticate the request and enable draft mode. - **redirect** (string) - Optional - The URL path to redirect to after enabling draft mode. If not provided, it defaults to the homepage. ### Request Example ```bash curl "https://yoursite.com/api/draft?secret=your-16-or-more-character-secret-here&redirect=/about" ``` ### Response #### Redirect - **307 Temporary Redirect** - Redirects the user to the specified `redirect` path with draft mode enabled. ### Error Handling #### Invalid Secret - **401 Unauthorized** - Returned when the provided `secret` query parameter does not match the configured draft secret. ``` GET /api/draft?secret=wrong-secret Response: 401 Unauthorized Body: "Invalid draft secret" ``` #### Unsafe Redirect - **307 Temporary Redirect** - If the `redirect` URL is deemed unsafe (e.g., contains `//`, `\`, or points outside the origin), it will redirect to the homepage (`/`) instead. ``` GET /api/draft?secret=...&redirect=https://evil.com Response: 307 to / (default, safe) ``` ``` -------------------------------- ### CmssyEditor Component Example Source: https://github.com/cmssy-io/cmssy-sdk/blob/main/_autodocs/api-reference/create-cmssy-page.md A Next.js client component that uses CmssyLocaleProvider and renders blocks based on the edit bridge configuration. Ensure the 'use client' directive is present. ```typescript // components/CmssyEditor.tsx "use client"; import { CmssyLocaleProvider } from "@cmssy/react/client"; import type { CmssyEditorProps } from "@cmssy/next"; export default function CmssyEditor({ page, locale, defaultLocale, edit, }: CmssyEditorProps) { return ( {/* Mount editor bridge here */} {page.blocks.map((block) => { const Component = edit.blockMap[block.type]; if (!Component) return null; return ( ); })} ); } ``` -------------------------------- ### Define Hero Block with Fields Source: https://github.com/cmssy-io/cmssy-sdk/blob/main/_autodocs/api-reference/fields.md Example of defining a 'hero' block using various field types like singleLine, multiLine, media, and link. Includes default values and required properties. ```typescript const HeroBlock = defineBlock({ type: "hero", label: "Hero Section", props: { title: fields.singleLine({ label: "Title", required: true, placeholder: "Enter hero title", }), subtitle: fields.multiLine({ label: "Subtitle", placeholder: "Optional tagline", }), backgroundImage: fields.media({ label: "Background Image", }), ctaText: fields.singleLine({ label: "CTA Button Text", defaultValue: "Learn More", }), ctaLink: fields.link({ label: "CTA Link", }), }, component: ({ content }) => { const bgStyle = content.backgroundImage ? { backgroundImage: `url(${content.backgroundImage})` } : undefined; return (

{content.title}

{content.subtitle &&

{content.subtitle}

} {content.ctaLink && ( {content.ctaText || "Learn More"} )}
); }, }); ``` -------------------------------- ### Example: Handling Messages from the Editor Source: https://github.com/cmssy-io/cmssy-sdk/blob/main/_autodocs/api-reference/editor-bridge.md Illustrates how to use `parseEditorMessage` within a 'message' event listener to process different types of messages from the editor, such as 'select' and 'patch'. ```typescript import { parseEditorMessage } from "@cmssy/react"; window.addEventListener("message", (event) => { const message = parseEditorMessage(event.data); if (!message) return; switch (message.type) { case "cmssy:select": selectBlock(message.blockId); break; case "cmssy:patch": updateBlockContent(message.blockId, message.content); break; } }); ``` -------------------------------- ### Fetch Site Configuration Source: https://github.com/cmssy-io/cmssy-sdk/blob/main/_autodocs/api-reference/data-client.md Fetches workspace branding and configuration, including site name, enabled languages, default language, 404 page ID, and branding details. Requires CmssyClientConfig. ```typescript async function fetchSiteConfig( config: CmssyClientConfig, options?: GraphqlRequestOptions, ): Promise; ``` ```typescript import { fetchSiteConfig } from "@cmssy/react"; const siteConfig = await fetchSiteConfig(config); console.log(siteConfig?.branding?.brandName); // "ACME Inc" console.log(siteConfig?.enabledLanguages); // ["en", "es"] ``` -------------------------------- ### Content Defaulting Best Practices Source: https://github.com/cmssy-io/cmssy-sdk/blob/main/docs/building-blocks/authoring-blocks.md Illustrates the correct way to handle potentially empty content props by rendering nothing, contrasting it with an incorrect approach that fabricates content. ```tsx // good - empty content renders nothing return <>{heading &&

{heading}

}; ``` ```tsx // bad - invents content the editor never wrote const { heading = "Building the future" } = content; ``` -------------------------------- ### fetchSiteConfig Source: https://github.com/cmssy-io/cmssy-sdk/blob/main/_autodocs/api-reference/data-client.md Fetches the workspace branding and configuration, including site name, enabled languages, default language, 404 page ID, and branding details. ```APIDOC ## fetchSiteConfig ### Description Fetch workspace branding and configuration. ### Method ```typescript async function fetchSiteConfig( config: CmssyClientConfig, options?: GraphqlRequestOptions, ): Promise; ``` ### Returns Site name, enabled languages, default language, 404 page ID, and branding. ### Example ```typescript import { fetchSiteConfig } from "@cmssy/react"; const siteConfig = await fetchSiteConfig(config); console.log(siteConfig?.branding?.brandName); // "ACME Inc" console.log(siteConfig?.enabledLanguages); // ["en", "es"] ``` ``` -------------------------------- ### ReadyMessage Source: https://github.com/cmssy-io/cmssy-sdk/blob/main/_autodocs/api-reference/editor-bridge.md Sent when the app is ready, containing all blocks, their locations, schemas, and metadata. ```APIDOC ## ReadyMessage Sent when the app is ready, containing all blocks, their locations, schemas, and metadata. ```typescript interface ReadyMessage { type: "cmssy:ready"; protocolVersion: number; blocks: Array<{ id: string; type: string; bounds: BlockRect; layoutPosition?: string; }>; schemas: Record; blockMeta?: Record; } interface BlockRect { x: number; y: number; width: number; height: number; } type BlockSchema = Record; interface BlockMeta { label: string; category?: string; icon?: string; layoutPositions?: string[]; } ``` ``` -------------------------------- ### createDraftRoute Function Signature Source: https://github.com/cmssy-io/cmssy-sdk/blob/main/_autodocs/api-reference/create-draft-route.md The createDraftRoute function takes a configuration object and returns a Next.js GET route handler. ```APIDOC ## createDraftRoute ### Description Creates a GET handler for the draft preview route. ### Parameters #### Configuration (`CmssyDraftRouteConfig`) - `draftSecret` (string) - Required - Secret for accessing draft (16+ chars) - `defaultRedirect` (string) - Optional - Path to redirect to if none specified (defaults to "/") ### Return Type A GET route handler function that accepts a `Request` and returns a `Promise`. ### Throws - `Error` if `draftSecret` is less than 16 characters - `Response(401)` if the provided secret is invalid - `Response(500)` if secret validation fails ``` -------------------------------- ### Fetch Site Configuration Source: https://github.com/cmssy-io/cmssy-sdk/blob/main/docs/reference/sdk-api.md Retrieve the global site configuration. This includes settings relevant to the entire site. ```typescript fetchSiteConfig(config) => Promise; ``` -------------------------------- ### Override Locale Detection Source: https://github.com/cmssy-io/cmssy-sdk/blob/main/_autodocs/api-reference/create-cmssy-page.md Set `config.resolveLocale()` to override the default locale detection mechanism. This example forces the locale to always be English. ```typescript const cmssy = { ...config, resolveLocale: () => "en", // Always use English }; ``` -------------------------------- ### Initialize Live Editor Client Source: https://github.com/cmssy-io/cmssy-sdk/blob/main/packages/react/README.md Use CmssyLazyEditor from @cmssy/react/client to enable the live editor. It loads block chunks lazily and communicates with the cmssy admin via the bridge protocol. ```tsx "use client"; import { CmssyLazyEditor } from "@cmssy/react/client"; export const CmssyEditor = (props) => ( import("./blocks")} /> ); ``` -------------------------------- ### Get Authenticated User (Server Component) Source: https://github.com/cmssy-io/cmssy-sdk/blob/main/_autodocs/api-reference/authentication.md Retrieve the current authenticated user in Server Components. Returns null if the user is not authenticated. ```typescript import { getCmssyUser } from "@cmssy/next"; export default async function AccountPage() { const user = await getCmssyUser(); if (!user) { return

Not authenticated

; } return (

Welcome, {user.email}

User ID: {user.recordId}

); } ``` -------------------------------- ### CmssyAuthRouteHandlers Interface Source: https://github.com/cmssy-io/cmssy-sdk/blob/main/_autodocs/api-reference/authentication.md Defines the structure for handlers that can be passed to `createCmssyAuthRoute`. It specifies the expected POST and GET methods for handling authentication requests. ```typescript interface CmssyAuthRouteHandlers { POST( request: Request, context: { params: Promise<{ action: string }> }, ): Promise; GET( request: Request, context: { params: Promise<{ action: string }> }, ): Promise; } ``` -------------------------------- ### Local Testing with ngrok Source: https://github.com/cmssy-io/cmssy-sdk/blob/main/_autodocs/api-reference/webhooks.md Use ngrok to expose your local server to the internet for testing webhook delivery. Register the ngrok URL in cmssy. ```bash # Start ngrok tunnel ngrok http 3000 # Register webhook in cmssy: # https://xxxxxxxx.ngrok.io/api/webhooks/cmssy # Test from cmssy admin panel (send test webhook) ``` -------------------------------- ### Fetch Products for a Listing Page Source: https://github.com/cmssy-io/cmssy-sdk/blob/main/_autodocs/api-reference/commerce.md Demonstrates how to fetch a list of published products with filtering, sorting, and population of related data for a Next.js page. Ensure you have configured the cmssy SDK with your API URL and workspace slug. ```typescript // app/products/page.tsx import { fetchProducts } from "@cmssy/next"; import { cmssy } from "@/cmssy.config"; const config = { apiUrl: cmssy.apiUrl, workspaceSlug: cmssy.workspaceSlug, }; export default async function ProductsPage() { const products = await fetchProducts(config, { filter: { status: "published" }, sort: "name", limit: 50, populate: ["variants"], }); return (
{products.map(product => ( ))}
); } function ProductCard({ product }: { product: CmssyProduct }) { const defaultVariant = product.variants[0]; return (

{product.data.name}

{defaultVariant &&

${defaultVariant.price}

}
); } ``` -------------------------------- ### Get Locale from Headers Source: https://github.com/cmssy-io/cmssy-sdk/blob/main/_autodocs/api-reference/locale.md Retrieves the current request's locale from the 'x-cmssy-locale' header, which is set by middleware. If the header is not present, it returns the default locale. ```typescript async function getCmssyLocale(config: CmssyClientConfig): Promise; ``` ```typescript import { getCmssyLocale } from "@cmssy/next"; export async function MyServerComponent() { const locale = await getCmssyLocale(config); // Render with locale-specific content } ``` -------------------------------- ### Create Cmssy Client Source: https://github.com/cmssy-io/cmssy-sdk/blob/main/docs/reference/sdk-api.md Instantiate the Cmssy delivery client with configuration. The `apiUrl` defaults to the Cmssy cloud if not provided. ```typescript createCmssyClient(config: CmssyClientConfig): CmssyClient; ``` -------------------------------- ### Get Session Access Token Source: https://github.com/cmssy-io/cmssy-sdk/blob/main/_autodocs/api-reference/authentication.md Obtain the current session's access token, which is useful for making authenticated GraphQL queries as the logged-in user. ```typescript import { getCmssyAccessToken } from "@cmssy/next"; import { graphqlRequest } from "@cmssy/react"; export async function fetchMyOrders(config: CmssyClientConfig) { const token = await getCmssyAccessToken(); return graphqlRequest( config, MY_ORDERS_QUERY, {}, { headers: { authorization: `Bearer ${token}` } }, ); } ``` -------------------------------- ### Configure Site URL for Canonicalization Source: https://github.com/cmssy-io/cmssy-sdk/blob/main/_autodocs/api-reference/seo.md Explicitly set the `siteUrl` in your CmssyNextConfig for multi-domain setups. If omitted, the canonical URL is derived from the request host at render time. ```typescript const cmssy: CmssyNextConfig = { ...config, siteUrl: process.env.CMSSY_SITE_URL || "https://example.com", }; ``` -------------------------------- ### Idempotent Webhook Handler Example Source: https://github.com/cmssy-io/cmssy-sdk/blob/main/_autodocs/api-reference/webhooks.md Ensures webhook events are processed only once by checking for existing records before processing and creating a new record upon successful processing. ```typescript import { db } from "@/lib/db"; async function handleOrderPaid(order: CmssyWebhookOrder, eventId: string) { // Check if we've already processed this event const existing = await db.webhookEvent.findUnique({ where: { id: eventId }, }); if (existing) { console.log("Event already processed"); return; } // Process the order await db.order.update({ where: { id: order.id }, data: { paymentStatus: "completed", paidAt: new Date() }, }); // Record that we processed this event await db.webhookEvent.create({ data: { id: eventId, processedAt: new Date() }, }); } ``` -------------------------------- ### Create Draft Route Handler Source: https://github.com/cmssy-io/cmssy-sdk/blob/main/_autodocs/api-reference/create-draft-route.md Use this snippet to create a GET handler for your draft preview API route. Ensure you import createDraftRoute and your Cmssy configuration. ```typescript import { createDraftRoute } from "@cmssy/next"; import { cmssy } from "@/cmssy.config"; export const GET = createDraftRoute(cmssy); ``` -------------------------------- ### Create Cmssy Client Source: https://github.com/cmssy-io/cmssy-sdk/blob/main/_autodocs/api-reference/data-client.md Use `createCmssyClient` to initialize a GraphQL client. Configure it with your API URL and workspace slug. This client can then be used to make queries. ```typescript import { createCmssyClient } from "@cmssy/react"; const client = createCmssyClient({ apiUrl: "https://api.cmssy.io/graphql", workspaceSlug: "my-workspace", }); // Query without workspace ID const siteConfig = await client.query(SITE_CONFIG_QUERY, { workspaceSlug: "my-workspace", }); // Query with automatic workspace ID resolution const models = await client.queryScoped(MODEL_DEFINITIONS_QUERY); ``` -------------------------------- ### Requesting Draft Mode from Editor Source: https://github.com/cmssy-io/cmssy-sdk/blob/main/_autodocs/api-reference/create-draft-route.md Send a GET request to your draft API endpoint with the `secret` and `redirect` query parameters. The editor uses this to enable draft mode. ```bash # Request from editor curl "https://yoursite.com/api/draft?secret=your-16-or-more-character-secret-here&redirect=/about" # Redirects to: /about (with draft mode enabled) ``` -------------------------------- ### SITE_CONFIG_QUERY Source: https://github.com/cmssy-io/cmssy-sdk/blob/main/_autodocs/api-reference/data-client.md A pre-built GraphQL query to fetch site configuration, including languages, branding, and the 404 page. ```APIDOC ## SITE_CONFIG_QUERY ### Description Fetch site configuration (languages, branding, 404 page). ### Query ```graphql query PublicSiteConfig($workspaceSlug: String!) { publicSiteConfig(workspaceSlug: $workspaceSlug) { id workspaceId siteName defaultLanguage enabledLanguages enabledFeatures notFoundPageId previewUrl branding { brandName logoUrl faviconUrl ogImageUrl } } } ``` ``` -------------------------------- ### buildLocaleSwitchHref Source: https://github.com/cmssy-io/cmssy-sdk/blob/main/_autodocs/api-reference/seo.md Builds a URL that switches to a different language while keeping the same page. ```APIDOC ## buildLocaleSwitchHref ### Description Builds a URL that switches to a different language while keeping the same page. ### Signature ```typescript function buildLocaleSwitchHref( pathname: string, fromLocale: string, toLocale: string, defaultLocale: string, ): string; ``` ### Parameters * **pathname** (string) - The current URL path. * **fromLocale** (string) - The current locale. * **toLocale** (string) - The target locale. * **defaultLocale** (string) - The default locale of the application. ### Example ```typescript // Current URL: /es/about const linkToEnglish = buildLocaleSwitchHref( "/es/about", "es", "en", "en", ); // Result: "/about" const linkToFrench = buildLocaleSwitchHref( "/es/about", "es", "fr", "en", ); // Result: "/fr/about" ``` ``` -------------------------------- ### Resolve Site Locales Source: https://github.com/cmssy-io/cmssy-sdk/blob/main/_autodocs/api-reference/locale.md Fetches the workspace's enabled languages and default locale. The result is cached for 60 seconds. Use this to get locale information for your application. ```typescript async function resolveSiteLocales( config: CmssyClientConfig, options?: GraphqlRequestOptions, ): Promise; ``` ```typescript interface CmssySiteLocales { defaultLocale: string; locales: string[]; } ``` ```typescript import { resolveSiteLocales } from "@cmssy/react"; const siteLocales = await resolveSiteLocales(config); console.log(siteLocales.defaultLocale); // "en" console.log(siteLocales.locales); // ["en", "es", "fr"] ``` -------------------------------- ### Example Hero Block Component Source: https://github.com/cmssy-io/cmssy-sdk/blob/main/docs/building-blocks/authoring-blocks.md A basic block component that accepts a `content` prop with `heading` and `showCta` fields. It renders a heading if provided and a call-to-action button if `showCta` is true. ```tsx function Hero({ content, }: { content: { heading?: string; showCta?: boolean }; }) { const { heading, showCta = true } = content; return (
{heading &&

{heading}

} {showCta && Get started}
); } ``` -------------------------------- ### SITE_CONFIG_QUERY GraphQL Source: https://github.com/cmssy-io/cmssy-sdk/blob/main/_autodocs/api-reference/data-client.md A pre-built GraphQL query to fetch site configuration, including languages, branding, and the 404 page ID. ```graphql export const SITE_CONFIG_QUERY = `query PublicSiteConfig($workspaceSlug: String!) { publicSiteConfig(workspaceSlug: $workspaceSlug) { id workspaceId siteName defaultLanguage enabledLanguages enabledFeatures notFoundPageId previewUrl branding { brandName logoUrl faviconUrl ogImageUrl } } }`; ``` -------------------------------- ### createCmssyRobots Source: https://github.com/cmssy-io/cmssy-sdk/blob/main/_autodocs/api-reference/seo.md Generates a `robots.txt` file based on workspace configuration, controlling search engine indexing. ```APIDOC ## createCmssyRobots ### Description Generate `robots.txt` from workspace configuration. Allows or disallows indexing based on workspace settings. ### Signature ```typescript function createCmssyRobots(config: CmssyNextConfig): () => MetadataRoute.Robots; ``` ### Parameters #### `config` - **Type**: `CmssyNextConfig` - **Required**: Yes - **Description**: API configuration ### Return Type Returns a function that generates Next.js robots metadata route handler. ### Example ```typescript // app/robots.ts import { createCmssyRobots } from "@cmssy/next"; import { cmssy } from "@/cmssy.config"; export default createCmssyRobots(cmssy); ``` ### Generated Output Example ``` User-agent: * Allow: / Sitemap: https://example.com/sitemap.xml ``` ``` -------------------------------- ### Module Dependencies Overview Source: https://github.com/cmssy-io/cmssy-sdk/blob/main/_autodocs/MANIFEST.md Visualizes the interdependencies between different modules within the CMSSY SDK, categorized into Core, Next.js, Auth, SEO, and Commerce chains. ```text ✓ README.md → INDEX.md → api-reference/ → types.md, configuration.md, errors.md Core Chain: block-registry ↔ fields ↔ block-components content-client ↔ data-client ↔ forms editor-bridge ↔ block-registry Next.js Chain: create-cmssy-page ├→ content-client ├→ block-registry ├→ locale └→ forms Auth Chain: authentication ↔ configuration ├→ session constants └→ cookies SEO Chain: seo ├→ content-client ├→ data-client └→ locale Commerce Chain: commerce ↔ data-client webhooks ↔ errors ``` -------------------------------- ### Fetch Data with Generic GraphQL Client Source: https://github.com/cmssy-io/cmssy-sdk/blob/main/packages/react/README.md Create a cmssy client to query the public API. Use query for raw GraphQL or queryScoped for workspace-specific queries with automatic headers and variables. ```ts import { createCmssyClient, MODEL_RECORDS_QUERY } from "@cmssy/react"; const cmssy = createCmssyClient({ workspaceSlug: "acme" }); // raw query (you own scoping) await cmssy.query(MY_QUERY, vars); // workspace-scoped (auto x-workspace-id header + $workspaceId var) const { publicModelRecords } = await cmssy.queryScoped(MODEL_RECORDS_QUERY, { modelSlug: "posts", }); ``` -------------------------------- ### Test Hero Component Rendering Source: https://github.com/cmssy-io/cmssy-sdk/blob/main/docs/building-blocks/authoring-blocks.md Renders a Hero component with mock content and asserts that the heading is present in the DOM. This test uses built-in React Testing Library matchers and does not require additional setup like @testing-library/jest-dom. ```tsx // blocks/hero/Hero.test.tsx import { render, screen } from "@testing-library/react"; import Hero from "./Hero"; test("renders the heading when set", () => { render(); // getByRole throws if absent, so this asserts the heading rendered expect(screen.getByRole("heading", { name: "Hello" })).toBeTruthy(); }); test("renders nothing for an empty heading", () => { const { container } = render(); expect(container.querySelector("h1")).toBeNull(); }); ``` -------------------------------- ### Provide Custom Fetch Implementation Source: https://github.com/cmssy-io/cmssy-sdk/blob/main/_autodocs/errors.md When running in an environment without a global fetch implementation, pass a custom fetch function in the options. ```typescript const page = await fetchPage(config, "/", { fetch: (url, init) => { // Custom fetch implementation } }); ``` -------------------------------- ### @cmssy/react/client Source: https://github.com/cmssy-io/cmssy-sdk/blob/main/docs/reference/sdk-api.md Client-only helpers for the editor bridge and providers. ```APIDOC ## @cmssy/react/client Client-only helpers for the editor bridge and providers: - `CmssyLazyEditor` - `CmssyLazyLayout` - `CmssyLocaleProvider` / `useCmssyLocale` - `useEditBridge` - `CmssyAuthProvider` / `useCmssyUser` - `CmssyCommerceProvider` / `useCart` - `useCmssyOrders` - Currency helpers (`formatPrice`, `toMinorUnits`, `fromMinorUnits`, `fractionDigits`). ``` -------------------------------- ### Middleware Setup for CSP Headers Source: https://github.com/cmssy-io/cmssy-sdk/blob/main/_autodocs/api-reference/create-cmssy-page.md Configure your Next.js middleware to apply Content Security Policy (CSP) headers, allowing the editor to frame your application. This snippet handles detecting edit requests and setting necessary headers. ```typescript // middleware.ts import { NextResponse, type NextRequest } from "next/server"; import { applyCmssyCsp, isCmssyEditRequest, CMSSY_EDIT_HEADER, } from "@cmssy/next"; import { cmssy } from "@/cmssy.config"; export function middleware(request: NextRequest) { const editMode = isCmssyEditRequest(request); const headers = new Headers(request.headers); headers.delete(CMSSY_EDIT_HEADER); if (editMode) headers.set(CMSSY_EDIT_HEADER, "1"); const response = NextResponse.next({ request: { headers } }); if (editMode) applyCmssyCsp(response, { editorOrigin: cmssy.editorOrigin }); return response; } ```