@sprintup-cms/sdk 1.8.48 → 1.8.51

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.
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/client.ts"],"names":[],"mappings":"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","file":"index.cjs","sourcesContent":["/**\n * @sprintup-cms/sdk — Core Client\n *\n * Zero-dependency, framework-agnostic typed API client for SprintUp Forge CMS.\n *\n * @example\n * import { cmsClient } from '@sprintup-cms/sdk'\n * const page = await cmsClient.getPage('about')\n *\n * @example Custom instance\n * import { createCMSClient } from '@sprintup-cms/sdk'\n * const cms = createCMSClient({ baseUrl: '...', apiKey: '...', appId: '...' })\n */\n\n// ── Types ─────────────────────────────────────────────────────────────────────\n\nexport interface CMSBlock {\n id: string\n type: string\n label?: string\n locked?: boolean\n data?: Record<string, any>\n /** Legacy field — blocks created before v1.1 used `content` instead of `data` */\n content?: Record<string, any>\n order?: number\n}\n\nexport interface CMSPage {\n _id?: string\n slug: string\n title: string\n description?: string\n pageType?: string\n pageTypeId?: string\n variant?: string\n status: 'draft' | 'published' | 'archived'\n visibility?: 'public' | 'private' | 'password'\n blocks: CMSBlock[]\n publishedAt?: string\n updatedAt?: string\n seo?: {\n title?: string\n description?: string\n keywords?: string[]\n ogImage?: string\n noIndex?: boolean\n }\n}\n\nexport interface CMSPageTypeField {\n id: string\n name: string\n label: string\n fieldType:\n | 'text' | 'textarea' | 'richtext' | 'image' | 'url'\n | 'number' | 'boolean' | 'date' | 'select' | 'relation'\n | 'email' | 'phone' | 'slug' | 'color' | 'embed'\n | 'multi-select' | 'repeater' | 'file' | 'code'\n required?: boolean\n options?: string[]\n description?: string\n maxLength?: number\n multiple?: boolean\n targetPageTypeKey?: string\n}\n\nexport interface CMSPageTypeSection {\n id: string\n name: string\n label: string\n order: number\n locked?: boolean\n fields: CMSPageTypeField[]\n}\n\nexport interface CMSPageTypeVariant {\n key: string\n label: string\n description?: string\n visibleSections?: string[]\n}\n\nexport interface CMSPageType {\n _id: string\n name: string\n key: string\n description?: string\n icon?: string\n /** Used by page-type-renderers to pick a specialised React component */\n rendererKey?: string\n /** Active variant key (set per-page) */\n variant?: string\n category: 'singleton' | 'collection' | 'global'\n contentCategory: 'singleton' | 'collection' | 'global'\n allowedRoles?: string[]\n variants: CMSPageTypeVariant[]\n sections: CMSPageTypeSection[]\n}\n\n// ── Sitemap ────────────────────────────────────────────────────────────────────\n\nexport interface CMSSitemapUrl {\n loc: string\n lastmod?: string\n changefreq?: string\n priority?: number\n title?: string\n}\n\nexport interface CMSSitemapResponse {\n enabled: boolean\n urls: CMSSitemapUrl[]\n config?: {\n defaultChangeFreq?: string\n defaultPriority?: number\n }\n}\n\n// ── Status ─────────────────────────────────────────────────────────────────────\n\nexport interface CMSStatusResponse {\n appId: string\n totalPages: number\n publishedPages: number\n connected: boolean\n pages: Array<{\n slug: string\n title: string\n pageType?: string\n group?: string\n }>\n}\n\n// ── Site Structure ─────────────────────────────────────────────────────────────\n\nexport type CMSMenuItemType = 'page' | 'url' | 'dynamic'\n\nexport interface CMSMenuItem {\n id: string\n type: CMSMenuItemType\n label: string\n contentId?: string\n url?: string\n /** Resolved href — ready to pass to <a href> or <Link href>. Never null, falls back to \"#\". */\n href: string\n locked: boolean\n openInNewTab: boolean\n children: CMSMenuItem[]\n page?: {\n title?: string\n slug?: string\n seoTitle?: string\n seoDescription?: string\n }\n}\n\nexport interface CMSPageTreeNode {\n id: string\n contentId: string\n parentId: string | null\n order: number\n locked: boolean\n visible?: boolean\n page?: {\n title?: string\n slug?: string\n seoTitle?: string\n seoDescription?: string\n }\n}\n\nexport interface CMSFooterGroup {\n id: string\n heading: string\n headingUrl?: string\n locked: boolean\n links: CMSMenuItem[]\n}\n\nexport interface CMSSiteStructure {\n appId: string\n pageTree: CMSPageTreeNode[]\n menus: {\n header: CMSMenuItem[]\n footer: CMSFooterGroup[]\n footerBottom: CMSMenuItem[]\n sidebar: CMSMenuItem[]\n [slot: string]: CMSMenuItem[] | CMSFooterGroup[]\n }\n updatedAt?: string\n}\n\n// ── Pagination & Responses ─────────────────────────────────────────────────────\n\n// v1 route returns { data, count } — flat, no nested meta object\ninterface CMSListResponse {\n data: CMSPage[]\n count: number\n appId?: string\n}\n\ninterface CMSSingleResponse {\n data: CMSPage\n}\n\n// ── Client Options ─────────────────────────────────────────────────────────────\n\nexport interface CMSClientOptions {\n /** Base URL of your Forge CMS instance, e.g. https://cms.yourschool.io */\n baseUrl?: string\n /** API key generated in CMS Admin → API Keys. Server-side only. */\n apiKey?: string\n /** App ID from CMS Admin → Apps, e.g. \"school-website\" */\n appId?: string\n}\n\nexport interface CMSGlobals {\n navigation: CMSPage | null\n footer: CMSPage | null\n}\n\nexport interface CMSGetPagesOptions {\n type?: string\n group?: string\n page?: number\n perPage?: number\n status?: 'published' | 'draft' | 'archived'\n}\n\n// ── Factory ───────────────────────────────────────────────────────────────────\n\nexport function createCMSClient(options?: CMSClientOptions) {\n /**\n * Resolve config lazily at request time — NOT at module/build time.\n * This prevents static prerendering crashes when env vars are absent during build.\n */\n function cfg() {\n return {\n baseUrl: (options?.baseUrl ?? process.env.NEXT_PUBLIC_CMS_URL ?? process.env.CMS_BASE_URL ?? '').replace(/\\/$/, ''),\n apiKey: options?.apiKey ?? process.env.CMS_API_KEY ?? '',\n appId: options?.appId ?? process.env.CMS_APP_ID ?? '',\n }\n }\n\n function headers() {\n return { 'X-CMS-API-Key': cfg().apiKey, 'Content-Type': 'application/json' }\n }\n\n // ── Pages ──────────────────────────────────────────────────────────────────\n\n async function getPages(params?: CMSGetPagesOptions): Promise<CMSPage[]> {\n const { baseUrl, apiKey, appId } = cfg()\n if (!baseUrl || !apiKey || !appId) {\n console.warn('[sprintup-cms] Missing CMS_BASE_URL / CMS_API_KEY / CMS_APP_ID — returning []')\n return []\n }\n try {\n const qs = new URLSearchParams()\n if (params?.type) qs.set('type', params.type)\n if (params?.group) qs.set('group', params.group)\n if (params?.page) qs.set('page', String(params.page))\n if (params?.perPage) qs.set('perPage', String(params.perPage))\n const url = `${baseUrl}/api/v1/${appId}/pages${qs.size ? `?${qs}` : ''}`\n const res = await fetch(url, {\n headers: headers(),\n next: { revalidate: 60, tags: [`cms-pages-${appId}`] },\n } as RequestInit)\n if (!res.ok) { console.error(`[sprintup-cms] getPages (${res.status})`); return [] }\n const json: CMSListResponse = await res.json()\n return json.data ?? []\n } catch (err) {\n console.error('[sprintup-cms] getPages error:', err)\n return []\n }\n }\n\n async function getPage(slug: string): Promise<CMSPage | null> {\n const { baseUrl, apiKey, appId } = cfg()\n if (!baseUrl || !apiKey || !appId) {\n console.warn('[sprintup-cms] Missing config — returning null')\n return null\n }\n try {\n const res = await fetch(`${baseUrl}/api/v1/${appId}/pages/${slug}`, {\n headers: headers(),\n next: { revalidate: 60, tags: [`cms-page-${slug}`, `cms-pages-${appId}`] },\n } as RequestInit)\n if (res.status === 404) return null\n if (!res.ok) { console.error(`[sprintup-cms] getPage \"${slug}\" (${res.status})`); return null }\n const json: CMSSingleResponse = await res.json()\n return json.data ?? null\n } catch (err) {\n console.error(`[sprintup-cms] getPage \"${slug}\" error:`, err)\n return null\n }\n }\n\n async function getBlogPosts(): Promise<CMSPage[]> {\n return getPages({ type: 'blog-post' })\n }\n\n async function getEvents(): Promise<CMSPage[]> {\n return getPages({ type: 'event-page' })\n }\n\n async function getAnnouncements(): Promise<CMSPage[]> {\n return getPages({ type: 'announcement-page' })\n }\n\n // ── Preview ────────────────────────────────────────────────────────────────\n\n async function getPreviewPage(token: string): Promise<CMSPage | null> {\n const { baseUrl, appId } = cfg()\n if (!baseUrl || !appId) {\n console.warn('[sprintup-cms] getPreviewPage: Missing baseUrl or appId — returning null')\n return null\n }\n try {\n const url = `${baseUrl}/api/v1/${appId}/preview?token=${encodeURIComponent(token)}`\n const res = await fetch(url, { cache: 'no-store' })\n if (!res.ok) {\n const errorText = await res.text().catch(() => '')\n console.error(`[sprintup-cms] getPreviewPage failed (${res.status}): ${errorText}`)\n return null\n }\n const json = await res.json()\n return json.data ?? null\n } catch (err) {\n console.error('[sprintup-cms] getPreviewPage error:', err)\n return null\n }\n }\n\n async function getPageWithPreview(slug: string, previewToken?: string | null): Promise<CMSPage | null> {\n if (previewToken) {\n const preview = await getPreviewPage(previewToken)\n if (preview?.slug === slug) return preview\n }\n return getPage(slug)\n }\n\n // ── Page Types ─────────────────────────────────────────────────────────────\n\n // ── Globals (Navigation / Footer) ──────────────────────────────────────────\n\n /**\n * Fetch global structural pages (navigation and footer).\n * These are singleton pages with pageType keys 'navigation' and 'footer'.\n * Returns { navigation, footer } — either can be null if not defined.\n *\n * @example Client app layout.tsx\n * const { navigation, footer } = await cmsClient.getGlobals()\n * // render <Header data={navigation?.blocks} /> and <Footer data={footer?.blocks} />\n */\n async function getGlobals(): Promise<CMSGlobals> {\n const { baseUrl, apiKey, appId } = cfg()\n if (!baseUrl || !apiKey || !appId) {\n return { navigation: null, footer: null }\n }\n try {\n const res = await fetch(`${baseUrl}/api/v1/${appId}/globals`, {\n headers: headers(),\n next: { revalidate: 300, tags: [`cms-globals-${appId}`] },\n } as RequestInit)\n if (!res.ok) {\n // Fallback: try fetching by page type key\n const [navPages, footerPages] = await Promise.all([\n getPages({ type: 'navigation' }),\n getPages({ type: 'footer' }),\n ])\n return {\n navigation: navPages[0] ?? null,\n footer: footerPages[0] ?? null,\n }\n }\n const json = await res.json()\n return {\n navigation: json.data?.navigation ?? null,\n footer: json.data?.footer ?? null,\n }\n } catch {\n return { navigation: null, footer: null }\n }\n }\n\n async function getPageType(pageTypeId: string): Promise<CMSPageType | null> {\n const { baseUrl, apiKey, appId } = cfg()\n if (!baseUrl || !apiKey || !appId || !pageTypeId) return null\n try {\n const res = await fetch(`${baseUrl}/api/v1/${appId}/page-types/${pageTypeId}`, {\n headers: headers(),\n next: { revalidate: 3600, tags: [`cms-page-type-${pageTypeId}`] },\n } as RequestInit)\n if (!res.ok) return null\n const json = await res.json()\n return json.data ?? null\n } catch {\n return null\n }\n }\n\n // ── Site Structure ─────────────────────────────────────────────────────────\n\n async function getSiteStructure(): Promise<CMSSiteStructure | null> {\n const { baseUrl, apiKey, appId } = cfg()\n if (!baseUrl || !apiKey || !appId) return null\n try {\n const res = await fetch(`${baseUrl}/api/v1/${appId}/site-structure`, {\n headers: headers(),\n next: {\n revalidate: 300,\n tags: [`site-structure-${appId}`],\n },\n } as RequestInit)\n if (!res.ok) return null\n const json = await res.json()\n return json.data ?? null\n } catch {\n return null\n }\n }\n\n /**\n * GET /api/v1/{appId}/sitemap\n * Fetch sitemap data for all published pages (priority, changefreq, lastmod).\n *\n * @example app/sitemap.ts\n * import type { MetadataRoute } from 'next'\n * import { cmsClient } from '@sprintup-cms/sdk'\n *\n * export default async function sitemap(): Promise<MetadataRoute.Sitemap> {\n * const data = await cmsClient.getSitemap()\n * if (!data?.enabled) return []\n * return data.urls.map(url => ({\n * url: `${process.env.NEXT_PUBLIC_SITE_URL}${url.loc}`,\n * lastModified: url.lastmod,\n * changeFrequency: url.changefreq as MetadataRoute.Sitemap[0]['changeFrequency'],\n * priority: url.priority,\n * }))\n * }\n */\n async function getSitemap(): Promise<CMSSitemapResponse | null> {\n const { baseUrl, apiKey, appId } = cfg()\n if (!baseUrl || !apiKey || !appId) return null\n try {\n const res = await fetch(`${baseUrl}/api/v1/${appId}/sitemap`, {\n headers: headers(),\n next: { revalidate: 3600 },\n } as RequestInit)\n if (!res.ok) return null\n const json = await res.json()\n return json.data ?? null\n } catch { return null }\n }\n\n /**\n * GET /api/v1/{appId}/status\n * Verify connectivity and retrieve page counts. Never cached — always fresh.\n * Useful for debugging API key setup.\n */\n async function getStatus(): Promise<CMSStatusResponse | null> {\n const { baseUrl, apiKey, appId } = cfg()\n if (!baseUrl || !apiKey || !appId) return null\n try {\n const res = await fetch(`${baseUrl}/api/v1/${appId}/status`, {\n headers: headers(),\n cache: 'no-store',\n } as RequestInit)\n if (!res.ok) return null\n return await res.json()\n } catch { return null }\n }\n\n return {\n getPages,\n getPage,\n getGlobals,\n getBlogPosts,\n getEvents,\n getAnnouncements,\n getPreviewPage,\n getPageWithPreview,\n getPageType,\n getSiteStructure,\n getSitemap,\n getStatus,\n }\n}\n\n// ── Default singleton ─────────────────────────────────────────────────────────\n\n/** Pre-configured singleton. Reads env vars lazily at request time. */\nexport const cmsClient = createCMSClient()\n"]}
1
+ {"version":3,"sources":["../src/client.ts"],"names":[],"mappings":"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","file":"index.cjs","sourcesContent":["/**\n * @sprintup-cms/sdk — Core Client\n *\n * Zero-dependency, framework-agnostic typed API client for SprintUp Forge CMS.\n *\n * @example\n * import { cmsClient } from '@sprintup-cms/sdk'\n * const page = await cmsClient.getPage('about')\n *\n * @example Custom instance\n * import { createCMSClient } from '@sprintup-cms/sdk'\n * const cms = createCMSClient({ baseUrl: '...', apiKey: '...', appId: '...' })\n */\n\n// ── Types ─────────────────────────────────────────────────────────────────────\n\nexport interface CMSBlock {\n id: string\n type: string\n label?: string\n locked?: boolean\n data?: Record<string, any>\n /** Legacy field — blocks created before v1.1 used `content` instead of `data` */\n content?: Record<string, any>\n order?: number\n}\n\nexport interface CMSPage {\n _id?: string\n slug: string\n title: string\n description?: string\n pageType?: string\n pageTypeId?: string\n variant?: string\n status: 'draft' | 'published' | 'archived'\n visibility?: 'public' | 'private' | 'password'\n blocks: CMSBlock[]\n publishedAt?: string\n updatedAt?: string\n seo?: {\n title?: string\n description?: string\n keywords?: string[]\n ogImage?: string\n noIndex?: boolean\n }\n}\n\nexport interface CMSPageTypeField {\n id: string\n name: string\n label: string\n fieldType:\n | 'text' | 'textarea' | 'richtext' | 'image' | 'url'\n | 'number' | 'boolean' | 'date' | 'select' | 'relation'\n | 'email' | 'phone' | 'slug' | 'color' | 'embed'\n | 'multi-select' | 'repeater' | 'file' | 'code'\n required?: boolean\n options?: string[]\n description?: string\n maxLength?: number\n multiple?: boolean\n targetPageTypeKey?: string\n}\n\nexport interface CMSPageTypeSection {\n id: string\n name: string\n label: string\n order: number\n locked?: boolean\n fields: CMSPageTypeField[]\n}\n\nexport interface CMSPageTypeVariant {\n key: string\n label: string\n description?: string\n visibleSections?: string[]\n}\n\nexport interface CMSPageType {\n _id: string\n name: string\n key: string\n description?: string\n icon?: string\n /** Used by page-type-renderers to pick a specialised React component */\n rendererKey?: string\n /** Active variant key (set per-page) */\n variant?: string\n category: 'singleton' | 'collection' | 'global'\n contentCategory: 'singleton' | 'collection' | 'global'\n allowedRoles?: string[]\n variants: CMSPageTypeVariant[]\n sections: CMSPageTypeSection[]\n}\n\n// ── Sitemap ────────────────────────────────────────────────────────────────────\n\nexport interface CMSSitemapUrl {\n loc: string\n lastmod?: string\n changefreq?: string\n priority?: number\n title?: string\n}\n\nexport interface CMSSitemapResponse {\n enabled: boolean\n urls: CMSSitemapUrl[]\n config?: {\n defaultChangeFreq?: string\n defaultPriority?: number\n }\n}\n\n// ── Status ─────────────────────────────────────────────────────────────────────\n\nexport interface CMSStatusResponse {\n appId: string\n totalPages: number\n publishedPages: number\n connected: boolean\n pages: Array<{\n slug: string\n title: string\n pageType?: string\n group?: string\n }>\n}\n\n// ── Site Structure ─────────────────────────────────────────────────────────────\n\nexport type CMSMenuItemType = 'page' | 'url' | 'dynamic'\n\nexport interface CMSMenuItem {\n id: string\n type: CMSMenuItemType\n label: string\n contentId?: string\n url?: string\n /** Resolved href — ready to pass to <a href> or <Link href>. Never null, falls back to \"#\". */\n href: string\n locked: boolean\n openInNewTab: boolean\n children: CMSMenuItem[]\n page?: {\n title?: string\n slug?: string\n seoTitle?: string\n seoDescription?: string\n }\n}\n\nexport interface CMSPageTreeNode {\n id: string\n contentId: string\n parentId: string | null\n order: number\n locked: boolean\n visible?: boolean\n page?: {\n title?: string\n slug?: string\n seoTitle?: string\n seoDescription?: string\n }\n}\n\nexport interface CMSFooterGroup {\n id: string\n heading: string\n headingUrl?: string\n locked: boolean\n links: CMSMenuItem[]\n}\n\nexport interface CMSSiteStructure {\n appId: string\n pageTree: CMSPageTreeNode[]\n menus: {\n header: CMSMenuItem[]\n footer: CMSFooterGroup[]\n footerBottom: CMSMenuItem[]\n sidebar: CMSMenuItem[]\n [slot: string]: CMSMenuItem[] | CMSFooterGroup[]\n }\n updatedAt?: string\n}\n\n// ── Pagination & Responses ─────────────────────────────────────────────────────\n\n// v1 route returns { data, count } — flat, no nested meta object\ninterface CMSListResponse {\n data: CMSPage[]\n count: number\n appId?: string\n}\n\ninterface CMSSingleResponse {\n data: CMSPage\n}\n\n// ── Client Options ─────────────────────────────────────────────────────────────\n\nexport interface CMSClientOptions {\n /** Base URL of your Forge CMS instance, e.g. https://cms.yourschool.io */\n baseUrl?: string\n /** API key generated in CMS Admin → API Keys. Server-side only. */\n apiKey?: string\n /** App ID from CMS Admin → Apps, e.g. \"school-website\" */\n appId?: string\n}\n\nexport interface CMSGetPagesOptions {\n type?: string\n group?: string\n page?: number\n perPage?: number\n status?: 'published' | 'draft' | 'archived'\n}\n\n// ── Factory ───────────────────────────────────────────────────────────────────\n\nexport function createCMSClient(options?: CMSClientOptions) {\n /**\n * Resolve config lazily at request time — NOT at module/build time.\n * This prevents static prerendering crashes when env vars are absent during build.\n */\n function cfg() {\n return {\n baseUrl: (options?.baseUrl ?? process.env.NEXT_PUBLIC_CMS_URL ?? process.env.CMS_BASE_URL ?? '').replace(/\\/$/, ''),\n apiKey: options?.apiKey ?? process.env.CMS_API_KEY ?? '',\n appId: options?.appId ?? process.env.CMS_APP_ID ?? '',\n }\n }\n\n function headers() {\n return { 'X-CMS-API-Key': cfg().apiKey, 'Content-Type': 'application/json' }\n }\n\n // ── Pages ──────────────────────────────────────────────────────────────────\n\n async function getPages(params?: CMSGetPagesOptions): Promise<CMSPage[]> {\n const { baseUrl, apiKey, appId } = cfg()\n if (!baseUrl || !apiKey || !appId) {\n console.warn('[sprintup-cms] Missing CMS_BASE_URL / CMS_API_KEY / CMS_APP_ID — returning []')\n return []\n }\n try {\n const qs = new URLSearchParams()\n if (params?.type) qs.set('type', params.type)\n if (params?.group) qs.set('group', params.group)\n if (params?.page) qs.set('page', String(params.page))\n if (params?.perPage) qs.set('perPage', String(params.perPage))\n const url = `${baseUrl}/api/v1/${appId}/pages${qs.size ? `?${qs}` : ''}`\n const res = await fetch(url, {\n headers: headers(),\n next: { revalidate: 60, tags: [`cms-pages-${appId}`] },\n } as RequestInit)\n if (!res.ok) { console.error(`[sprintup-cms] getPages (${res.status})`); return [] }\n const json: CMSListResponse = await res.json()\n return json.data ?? []\n } catch (err) {\n console.error('[sprintup-cms] getPages error:', err)\n return []\n }\n }\n\n async function getPage(slug: string): Promise<CMSPage | null> {\n const { baseUrl, apiKey, appId } = cfg()\n if (!baseUrl || !apiKey || !appId) {\n console.warn('[sprintup-cms] Missing config — returning null')\n return null\n }\n try {\n const res = await fetch(`${baseUrl}/api/v1/${appId}/pages/${slug}`, {\n headers: headers(),\n next: { revalidate: 60, tags: [`cms-page-${slug}`, `cms-pages-${appId}`] },\n } as RequestInit)\n if (res.status === 404) return null\n if (!res.ok) { console.error(`[sprintup-cms] getPage \"${slug}\" (${res.status})`); return null }\n const json: CMSSingleResponse = await res.json()\n return json.data ?? null\n } catch (err) {\n console.error(`[sprintup-cms] getPage \"${slug}\" error:`, err)\n return null\n }\n }\n\n async function getBlogPosts(): Promise<CMSPage[]> {\n return getPages({ type: 'blog-post' })\n }\n\n async function getEvents(): Promise<CMSPage[]> {\n return getPages({ type: 'event-page' })\n }\n\n async function getAnnouncements(): Promise<CMSPage[]> {\n return getPages({ type: 'announcement-page' })\n }\n\n // ── Preview ────────────────────────────────────────────────────────────────\n\n async function getPreviewPage(token: string): Promise<CMSPage | null> {\n const { baseUrl, appId } = cfg()\n if (!baseUrl || !appId) {\n console.warn('[sprintup-cms] getPreviewPage: Missing baseUrl or appId — returning null')\n return null\n }\n try {\n const url = `${baseUrl}/api/v1/${appId}/preview?token=${encodeURIComponent(token)}`\n const res = await fetch(url, { cache: 'no-store' })\n if (!res.ok) {\n const errorText = await res.text().catch(() => '')\n console.error(`[sprintup-cms] getPreviewPage failed (${res.status}): ${errorText}`)\n return null\n }\n const json = await res.json()\n return json.data ?? null\n } catch (err) {\n console.error('[sprintup-cms] getPreviewPage error:', err)\n return null\n }\n }\n\n async function getPageWithPreview(slug: string, previewToken?: string | null): Promise<CMSPage | null> {\n if (previewToken) {\n const preview = await getPreviewPage(previewToken)\n if (preview?.slug === slug) return preview\n }\n return getPage(slug)\n }\n\n // ── Page Types ─────────────────────────────────────────────────────────────\n\n async function getPageType(pageTypeId: string): Promise<CMSPageType | null> {\n const { baseUrl, apiKey, appId } = cfg()\n if (!baseUrl || !apiKey || !appId || !pageTypeId) return null\n try {\n const res = await fetch(`${baseUrl}/api/v1/${appId}/page-types/${pageTypeId}`, {\n headers: headers(),\n next: { revalidate: 3600, tags: [`cms-page-type-${pageTypeId}`] },\n } as RequestInit)\n if (!res.ok) return null\n const json = await res.json()\n return json.data ?? null\n } catch {\n return null\n }\n }\n\n // ── Site Structure ─────────────────────────────────────────────────────────\n\n async function getSiteStructure(): Promise<CMSSiteStructure | null> {\n const { baseUrl, apiKey, appId } = cfg()\n if (!baseUrl || !apiKey || !appId) return null\n try {\n const res = await fetch(`${baseUrl}/api/v1/${appId}/site-structure`, {\n headers: headers(),\n next: {\n revalidate: 300,\n tags: [`site-structure-${appId}`],\n },\n } as RequestInit)\n if (!res.ok) return null\n const json = await res.json()\n return json.data ?? null\n } catch {\n return null\n }\n }\n\n /**\n * GET /api/v1/{appId}/sitemap\n * Fetch sitemap data for all published pages (priority, changefreq, lastmod).\n *\n * @example app/sitemap.ts\n * import type { MetadataRoute } from 'next'\n * import { cmsClient } from '@sprintup-cms/sdk'\n *\n * export default async function sitemap(): Promise<MetadataRoute.Sitemap> {\n * const data = await cmsClient.getSitemap()\n * if (!data?.enabled) return []\n * return data.urls.map(url => ({\n * url: `${process.env.NEXT_PUBLIC_SITE_URL}${url.loc}`,\n * lastModified: url.lastmod,\n * changeFrequency: url.changefreq as MetadataRoute.Sitemap[0]['changeFrequency'],\n * priority: url.priority,\n * }))\n * }\n */\n async function getSitemap(): Promise<CMSSitemapResponse | null> {\n const { baseUrl, apiKey, appId } = cfg()\n if (!baseUrl || !apiKey || !appId) return null\n try {\n const res = await fetch(`${baseUrl}/api/v1/${appId}/sitemap`, {\n headers: headers(),\n next: { revalidate: 3600 },\n } as RequestInit)\n if (!res.ok) return null\n const json = await res.json()\n return json.data ?? null\n } catch { return null }\n }\n\n /**\n * GET /api/v1/{appId}/status\n * Verify connectivity and retrieve page counts. Never cached — always fresh.\n * Useful for debugging API key setup.\n */\n async function getStatus(): Promise<CMSStatusResponse | null> {\n const { baseUrl, apiKey, appId } = cfg()\n if (!baseUrl || !apiKey || !appId) return null\n try {\n const res = await fetch(`${baseUrl}/api/v1/${appId}/status`, {\n headers: headers(),\n cache: 'no-store',\n } as RequestInit)\n if (!res.ok) return null\n return await res.json()\n } catch { return null }\n }\n\n async function getGlobals(): Promise<{ navigation: CMSPage | null; footer: CMSPage | null }> {\n const { baseUrl, appId } = cfg()\n if (!baseUrl || !appId) return { navigation: null, footer: null }\n try {\n const res = await fetch(`${baseUrl}/api/v1/${appId}/globals`, {\n headers: headers(),\n next: { revalidate: 300, tags: [`cms-globals-${appId}`] },\n } as RequestInit)\n if (!res.ok) {\n const [navPages, footerPages] = await Promise.all([\n getPages({ type: 'navigation' }),\n getPages({ type: 'footer' }),\n ])\n return { navigation: navPages[0] ?? null, footer: footerPages[0] ?? null }\n }\n const json = await res.json()\n return {\n navigation: json.data?.navigation ?? null,\n footer: json.data?.footer ?? null,\n }\n } catch {\n return { navigation: null, footer: null }\n }\n }\n\n return {\n getPages,\n getPage,\n getGlobals,\n getBlogPosts,\n getEvents,\n getAnnouncements,\n getPreviewPage,\n getPageWithPreview,\n getPageType,\n getSiteStructure,\n getSitemap,\n getStatus,\n }\n}\n\n// ── Default singleton ─────────────────────────────────────────────────────────\n\n/** Pre-configured singleton. Reads env vars lazily at request time. */\nexport const cmsClient = createCMSClient()\n"]}
package/dist/index.js CHANGED
@@ -100,35 +100,6 @@ function createCMSClient(options) {
100
100
  }
101
101
  return getPage(slug);
102
102
  }
103
- async function getGlobals() {
104
- const { baseUrl, apiKey, appId } = cfg();
105
- if (!baseUrl || !apiKey || !appId) {
106
- return { navigation: null, footer: null };
107
- }
108
- try {
109
- const res = await fetch(`${baseUrl}/api/v1/${appId}/globals`, {
110
- headers: headers(),
111
- next: { revalidate: 300, tags: [`cms-globals-${appId}`] }
112
- });
113
- if (!res.ok) {
114
- const [navPages, footerPages] = await Promise.all([
115
- getPages({ type: "navigation" }),
116
- getPages({ type: "footer" })
117
- ]);
118
- return {
119
- navigation: navPages[0] ?? null,
120
- footer: footerPages[0] ?? null
121
- };
122
- }
123
- const json = await res.json();
124
- return {
125
- navigation: json.data?.navigation ?? null,
126
- footer: json.data?.footer ?? null
127
- };
128
- } catch {
129
- return { navigation: null, footer: null };
130
- }
131
- }
132
103
  async function getPageType(pageTypeId) {
133
104
  const { baseUrl, apiKey, appId } = cfg();
134
105
  if (!baseUrl || !apiKey || !appId || !pageTypeId) return null;
@@ -191,6 +162,30 @@ function createCMSClient(options) {
191
162
  return null;
192
163
  }
193
164
  }
165
+ async function getGlobals() {
166
+ const { baseUrl, appId } = cfg();
167
+ if (!baseUrl || !appId) return { navigation: null, footer: null };
168
+ try {
169
+ const res = await fetch(`${baseUrl}/api/v1/${appId}/globals`, {
170
+ headers: headers(),
171
+ next: { revalidate: 300, tags: [`cms-globals-${appId}`] }
172
+ });
173
+ if (!res.ok) {
174
+ const [navPages, footerPages] = await Promise.all([
175
+ getPages({ type: "navigation" }),
176
+ getPages({ type: "footer" })
177
+ ]);
178
+ return { navigation: navPages[0] ?? null, footer: footerPages[0] ?? null };
179
+ }
180
+ const json = await res.json();
181
+ return {
182
+ navigation: json.data?.navigation ?? null,
183
+ footer: json.data?.footer ?? null
184
+ };
185
+ } catch {
186
+ return { navigation: null, footer: null };
187
+ }
188
+ }
194
189
  return {
195
190
  getPages,
196
191
  getPage,
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/client.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["/**\n * @sprintup-cms/sdk — Core Client\n *\n * Zero-dependency, framework-agnostic typed API client for SprintUp Forge CMS.\n *\n * @example\n * import { cmsClient } from '@sprintup-cms/sdk'\n * const page = await cmsClient.getPage('about')\n *\n * @example Custom instance\n * import { createCMSClient } from '@sprintup-cms/sdk'\n * const cms = createCMSClient({ baseUrl: '...', apiKey: '...', appId: '...' })\n */\n\n// ── Types ─────────────────────────────────────────────────────────────────────\n\nexport interface CMSBlock {\n id: string\n type: string\n label?: string\n locked?: boolean\n data?: Record<string, any>\n /** Legacy field — blocks created before v1.1 used `content` instead of `data` */\n content?: Record<string, any>\n order?: number\n}\n\nexport interface CMSPage {\n _id?: string\n slug: string\n title: string\n description?: string\n pageType?: string\n pageTypeId?: string\n variant?: string\n status: 'draft' | 'published' | 'archived'\n visibility?: 'public' | 'private' | 'password'\n blocks: CMSBlock[]\n publishedAt?: string\n updatedAt?: string\n seo?: {\n title?: string\n description?: string\n keywords?: string[]\n ogImage?: string\n noIndex?: boolean\n }\n}\n\nexport interface CMSPageTypeField {\n id: string\n name: string\n label: string\n fieldType:\n | 'text' | 'textarea' | 'richtext' | 'image' | 'url'\n | 'number' | 'boolean' | 'date' | 'select' | 'relation'\n | 'email' | 'phone' | 'slug' | 'color' | 'embed'\n | 'multi-select' | 'repeater' | 'file' | 'code'\n required?: boolean\n options?: string[]\n description?: string\n maxLength?: number\n multiple?: boolean\n targetPageTypeKey?: string\n}\n\nexport interface CMSPageTypeSection {\n id: string\n name: string\n label: string\n order: number\n locked?: boolean\n fields: CMSPageTypeField[]\n}\n\nexport interface CMSPageTypeVariant {\n key: string\n label: string\n description?: string\n visibleSections?: string[]\n}\n\nexport interface CMSPageType {\n _id: string\n name: string\n key: string\n description?: string\n icon?: string\n /** Used by page-type-renderers to pick a specialised React component */\n rendererKey?: string\n /** Active variant key (set per-page) */\n variant?: string\n category: 'singleton' | 'collection' | 'global'\n contentCategory: 'singleton' | 'collection' | 'global'\n allowedRoles?: string[]\n variants: CMSPageTypeVariant[]\n sections: CMSPageTypeSection[]\n}\n\n// ── Sitemap ────────────────────────────────────────────────────────────────────\n\nexport interface CMSSitemapUrl {\n loc: string\n lastmod?: string\n changefreq?: string\n priority?: number\n title?: string\n}\n\nexport interface CMSSitemapResponse {\n enabled: boolean\n urls: CMSSitemapUrl[]\n config?: {\n defaultChangeFreq?: string\n defaultPriority?: number\n }\n}\n\n// ── Status ─────────────────────────────────────────────────────────────────────\n\nexport interface CMSStatusResponse {\n appId: string\n totalPages: number\n publishedPages: number\n connected: boolean\n pages: Array<{\n slug: string\n title: string\n pageType?: string\n group?: string\n }>\n}\n\n// ── Site Structure ─────────────────────────────────────────────────────────────\n\nexport type CMSMenuItemType = 'page' | 'url' | 'dynamic'\n\nexport interface CMSMenuItem {\n id: string\n type: CMSMenuItemType\n label: string\n contentId?: string\n url?: string\n /** Resolved href — ready to pass to <a href> or <Link href>. Never null, falls back to \"#\". */\n href: string\n locked: boolean\n openInNewTab: boolean\n children: CMSMenuItem[]\n page?: {\n title?: string\n slug?: string\n seoTitle?: string\n seoDescription?: string\n }\n}\n\nexport interface CMSPageTreeNode {\n id: string\n contentId: string\n parentId: string | null\n order: number\n locked: boolean\n visible?: boolean\n page?: {\n title?: string\n slug?: string\n seoTitle?: string\n seoDescription?: string\n }\n}\n\nexport interface CMSFooterGroup {\n id: string\n heading: string\n headingUrl?: string\n locked: boolean\n links: CMSMenuItem[]\n}\n\nexport interface CMSSiteStructure {\n appId: string\n pageTree: CMSPageTreeNode[]\n menus: {\n header: CMSMenuItem[]\n footer: CMSFooterGroup[]\n footerBottom: CMSMenuItem[]\n sidebar: CMSMenuItem[]\n [slot: string]: CMSMenuItem[] | CMSFooterGroup[]\n }\n updatedAt?: string\n}\n\n// ── Pagination & Responses ─────────────────────────────────────────────────────\n\n// v1 route returns { data, count } — flat, no nested meta object\ninterface CMSListResponse {\n data: CMSPage[]\n count: number\n appId?: string\n}\n\ninterface CMSSingleResponse {\n data: CMSPage\n}\n\n// ── Client Options ─────────────────────────────────────────────────────────────\n\nexport interface CMSClientOptions {\n /** Base URL of your Forge CMS instance, e.g. https://cms.yourschool.io */\n baseUrl?: string\n /** API key generated in CMS Admin → API Keys. Server-side only. */\n apiKey?: string\n /** App ID from CMS Admin → Apps, e.g. \"school-website\" */\n appId?: string\n}\n\nexport interface CMSGlobals {\n navigation: CMSPage | null\n footer: CMSPage | null\n}\n\nexport interface CMSGetPagesOptions {\n type?: string\n group?: string\n page?: number\n perPage?: number\n status?: 'published' | 'draft' | 'archived'\n}\n\n// ── Factory ───────────────────────────────────────────────────────────────────\n\nexport function createCMSClient(options?: CMSClientOptions) {\n /**\n * Resolve config lazily at request time — NOT at module/build time.\n * This prevents static prerendering crashes when env vars are absent during build.\n */\n function cfg() {\n return {\n baseUrl: (options?.baseUrl ?? process.env.NEXT_PUBLIC_CMS_URL ?? process.env.CMS_BASE_URL ?? '').replace(/\\/$/, ''),\n apiKey: options?.apiKey ?? process.env.CMS_API_KEY ?? '',\n appId: options?.appId ?? process.env.CMS_APP_ID ?? '',\n }\n }\n\n function headers() {\n return { 'X-CMS-API-Key': cfg().apiKey, 'Content-Type': 'application/json' }\n }\n\n // ── Pages ──────────────────────────────────────────────────────────────────\n\n async function getPages(params?: CMSGetPagesOptions): Promise<CMSPage[]> {\n const { baseUrl, apiKey, appId } = cfg()\n if (!baseUrl || !apiKey || !appId) {\n console.warn('[sprintup-cms] Missing CMS_BASE_URL / CMS_API_KEY / CMS_APP_ID — returning []')\n return []\n }\n try {\n const qs = new URLSearchParams()\n if (params?.type) qs.set('type', params.type)\n if (params?.group) qs.set('group', params.group)\n if (params?.page) qs.set('page', String(params.page))\n if (params?.perPage) qs.set('perPage', String(params.perPage))\n const url = `${baseUrl}/api/v1/${appId}/pages${qs.size ? `?${qs}` : ''}`\n const res = await fetch(url, {\n headers: headers(),\n next: { revalidate: 60, tags: [`cms-pages-${appId}`] },\n } as RequestInit)\n if (!res.ok) { console.error(`[sprintup-cms] getPages (${res.status})`); return [] }\n const json: CMSListResponse = await res.json()\n return json.data ?? []\n } catch (err) {\n console.error('[sprintup-cms] getPages error:', err)\n return []\n }\n }\n\n async function getPage(slug: string): Promise<CMSPage | null> {\n const { baseUrl, apiKey, appId } = cfg()\n if (!baseUrl || !apiKey || !appId) {\n console.warn('[sprintup-cms] Missing config — returning null')\n return null\n }\n try {\n const res = await fetch(`${baseUrl}/api/v1/${appId}/pages/${slug}`, {\n headers: headers(),\n next: { revalidate: 60, tags: [`cms-page-${slug}`, `cms-pages-${appId}`] },\n } as RequestInit)\n if (res.status === 404) return null\n if (!res.ok) { console.error(`[sprintup-cms] getPage \"${slug}\" (${res.status})`); return null }\n const json: CMSSingleResponse = await res.json()\n return json.data ?? null\n } catch (err) {\n console.error(`[sprintup-cms] getPage \"${slug}\" error:`, err)\n return null\n }\n }\n\n async function getBlogPosts(): Promise<CMSPage[]> {\n return getPages({ type: 'blog-post' })\n }\n\n async function getEvents(): Promise<CMSPage[]> {\n return getPages({ type: 'event-page' })\n }\n\n async function getAnnouncements(): Promise<CMSPage[]> {\n return getPages({ type: 'announcement-page' })\n }\n\n // ── Preview ────────────────────────────────────────────────────────────────\n\n async function getPreviewPage(token: string): Promise<CMSPage | null> {\n const { baseUrl, appId } = cfg()\n if (!baseUrl || !appId) {\n console.warn('[sprintup-cms] getPreviewPage: Missing baseUrl or appId — returning null')\n return null\n }\n try {\n const url = `${baseUrl}/api/v1/${appId}/preview?token=${encodeURIComponent(token)}`\n const res = await fetch(url, { cache: 'no-store' })\n if (!res.ok) {\n const errorText = await res.text().catch(() => '')\n console.error(`[sprintup-cms] getPreviewPage failed (${res.status}): ${errorText}`)\n return null\n }\n const json = await res.json()\n return json.data ?? null\n } catch (err) {\n console.error('[sprintup-cms] getPreviewPage error:', err)\n return null\n }\n }\n\n async function getPageWithPreview(slug: string, previewToken?: string | null): Promise<CMSPage | null> {\n if (previewToken) {\n const preview = await getPreviewPage(previewToken)\n if (preview?.slug === slug) return preview\n }\n return getPage(slug)\n }\n\n // ── Page Types ─────────────────────────────────────────────────────────────\n\n // ── Globals (Navigation / Footer) ──────────────────────────────────────────\n\n /**\n * Fetch global structural pages (navigation and footer).\n * These are singleton pages with pageType keys 'navigation' and 'footer'.\n * Returns { navigation, footer } — either can be null if not defined.\n *\n * @example Client app layout.tsx\n * const { navigation, footer } = await cmsClient.getGlobals()\n * // render <Header data={navigation?.blocks} /> and <Footer data={footer?.blocks} />\n */\n async function getGlobals(): Promise<CMSGlobals> {\n const { baseUrl, apiKey, appId } = cfg()\n if (!baseUrl || !apiKey || !appId) {\n return { navigation: null, footer: null }\n }\n try {\n const res = await fetch(`${baseUrl}/api/v1/${appId}/globals`, {\n headers: headers(),\n next: { revalidate: 300, tags: [`cms-globals-${appId}`] },\n } as RequestInit)\n if (!res.ok) {\n // Fallback: try fetching by page type key\n const [navPages, footerPages] = await Promise.all([\n getPages({ type: 'navigation' }),\n getPages({ type: 'footer' }),\n ])\n return {\n navigation: navPages[0] ?? null,\n footer: footerPages[0] ?? null,\n }\n }\n const json = await res.json()\n return {\n navigation: json.data?.navigation ?? null,\n footer: json.data?.footer ?? null,\n }\n } catch {\n return { navigation: null, footer: null }\n }\n }\n\n async function getPageType(pageTypeId: string): Promise<CMSPageType | null> {\n const { baseUrl, apiKey, appId } = cfg()\n if (!baseUrl || !apiKey || !appId || !pageTypeId) return null\n try {\n const res = await fetch(`${baseUrl}/api/v1/${appId}/page-types/${pageTypeId}`, {\n headers: headers(),\n next: { revalidate: 3600, tags: [`cms-page-type-${pageTypeId}`] },\n } as RequestInit)\n if (!res.ok) return null\n const json = await res.json()\n return json.data ?? null\n } catch {\n return null\n }\n }\n\n // ── Site Structure ─────────────────────────────────────────────────────────\n\n async function getSiteStructure(): Promise<CMSSiteStructure | null> {\n const { baseUrl, apiKey, appId } = cfg()\n if (!baseUrl || !apiKey || !appId) return null\n try {\n const res = await fetch(`${baseUrl}/api/v1/${appId}/site-structure`, {\n headers: headers(),\n next: {\n revalidate: 300,\n tags: [`site-structure-${appId}`],\n },\n } as RequestInit)\n if (!res.ok) return null\n const json = await res.json()\n return json.data ?? null\n } catch {\n return null\n }\n }\n\n /**\n * GET /api/v1/{appId}/sitemap\n * Fetch sitemap data for all published pages (priority, changefreq, lastmod).\n *\n * @example app/sitemap.ts\n * import type { MetadataRoute } from 'next'\n * import { cmsClient } from '@sprintup-cms/sdk'\n *\n * export default async function sitemap(): Promise<MetadataRoute.Sitemap> {\n * const data = await cmsClient.getSitemap()\n * if (!data?.enabled) return []\n * return data.urls.map(url => ({\n * url: `${process.env.NEXT_PUBLIC_SITE_URL}${url.loc}`,\n * lastModified: url.lastmod,\n * changeFrequency: url.changefreq as MetadataRoute.Sitemap[0]['changeFrequency'],\n * priority: url.priority,\n * }))\n * }\n */\n async function getSitemap(): Promise<CMSSitemapResponse | null> {\n const { baseUrl, apiKey, appId } = cfg()\n if (!baseUrl || !apiKey || !appId) return null\n try {\n const res = await fetch(`${baseUrl}/api/v1/${appId}/sitemap`, {\n headers: headers(),\n next: { revalidate: 3600 },\n } as RequestInit)\n if (!res.ok) return null\n const json = await res.json()\n return json.data ?? null\n } catch { return null }\n }\n\n /**\n * GET /api/v1/{appId}/status\n * Verify connectivity and retrieve page counts. Never cached — always fresh.\n * Useful for debugging API key setup.\n */\n async function getStatus(): Promise<CMSStatusResponse | null> {\n const { baseUrl, apiKey, appId } = cfg()\n if (!baseUrl || !apiKey || !appId) return null\n try {\n const res = await fetch(`${baseUrl}/api/v1/${appId}/status`, {\n headers: headers(),\n cache: 'no-store',\n } as RequestInit)\n if (!res.ok) return null\n return await res.json()\n } catch { return null }\n }\n\n return {\n getPages,\n getPage,\n getGlobals,\n getBlogPosts,\n getEvents,\n getAnnouncements,\n getPreviewPage,\n getPageWithPreview,\n getPageType,\n getSiteStructure,\n getSitemap,\n getStatus,\n }\n}\n\n// ── Default singleton ─────────────────────────────────────────────────────────\n\n/** Pre-configured singleton. Reads env vars lazily at request time. */\nexport const cmsClient = createCMSClient()\n"]}
1
+ {"version":3,"sources":["../src/client.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["/**\n * @sprintup-cms/sdk — Core Client\n *\n * Zero-dependency, framework-agnostic typed API client for SprintUp Forge CMS.\n *\n * @example\n * import { cmsClient } from '@sprintup-cms/sdk'\n * const page = await cmsClient.getPage('about')\n *\n * @example Custom instance\n * import { createCMSClient } from '@sprintup-cms/sdk'\n * const cms = createCMSClient({ baseUrl: '...', apiKey: '...', appId: '...' })\n */\n\n// ── Types ─────────────────────────────────────────────────────────────────────\n\nexport interface CMSBlock {\n id: string\n type: string\n label?: string\n locked?: boolean\n data?: Record<string, any>\n /** Legacy field — blocks created before v1.1 used `content` instead of `data` */\n content?: Record<string, any>\n order?: number\n}\n\nexport interface CMSPage {\n _id?: string\n slug: string\n title: string\n description?: string\n pageType?: string\n pageTypeId?: string\n variant?: string\n status: 'draft' | 'published' | 'archived'\n visibility?: 'public' | 'private' | 'password'\n blocks: CMSBlock[]\n publishedAt?: string\n updatedAt?: string\n seo?: {\n title?: string\n description?: string\n keywords?: string[]\n ogImage?: string\n noIndex?: boolean\n }\n}\n\nexport interface CMSPageTypeField {\n id: string\n name: string\n label: string\n fieldType:\n | 'text' | 'textarea' | 'richtext' | 'image' | 'url'\n | 'number' | 'boolean' | 'date' | 'select' | 'relation'\n | 'email' | 'phone' | 'slug' | 'color' | 'embed'\n | 'multi-select' | 'repeater' | 'file' | 'code'\n required?: boolean\n options?: string[]\n description?: string\n maxLength?: number\n multiple?: boolean\n targetPageTypeKey?: string\n}\n\nexport interface CMSPageTypeSection {\n id: string\n name: string\n label: string\n order: number\n locked?: boolean\n fields: CMSPageTypeField[]\n}\n\nexport interface CMSPageTypeVariant {\n key: string\n label: string\n description?: string\n visibleSections?: string[]\n}\n\nexport interface CMSPageType {\n _id: string\n name: string\n key: string\n description?: string\n icon?: string\n /** Used by page-type-renderers to pick a specialised React component */\n rendererKey?: string\n /** Active variant key (set per-page) */\n variant?: string\n category: 'singleton' | 'collection' | 'global'\n contentCategory: 'singleton' | 'collection' | 'global'\n allowedRoles?: string[]\n variants: CMSPageTypeVariant[]\n sections: CMSPageTypeSection[]\n}\n\n// ── Sitemap ────────────────────────────────────────────────────────────────────\n\nexport interface CMSSitemapUrl {\n loc: string\n lastmod?: string\n changefreq?: string\n priority?: number\n title?: string\n}\n\nexport interface CMSSitemapResponse {\n enabled: boolean\n urls: CMSSitemapUrl[]\n config?: {\n defaultChangeFreq?: string\n defaultPriority?: number\n }\n}\n\n// ── Status ─────────────────────────────────────────────────────────────────────\n\nexport interface CMSStatusResponse {\n appId: string\n totalPages: number\n publishedPages: number\n connected: boolean\n pages: Array<{\n slug: string\n title: string\n pageType?: string\n group?: string\n }>\n}\n\n// ── Site Structure ─────────────────────────────────────────────────────────────\n\nexport type CMSMenuItemType = 'page' | 'url' | 'dynamic'\n\nexport interface CMSMenuItem {\n id: string\n type: CMSMenuItemType\n label: string\n contentId?: string\n url?: string\n /** Resolved href — ready to pass to <a href> or <Link href>. Never null, falls back to \"#\". */\n href: string\n locked: boolean\n openInNewTab: boolean\n children: CMSMenuItem[]\n page?: {\n title?: string\n slug?: string\n seoTitle?: string\n seoDescription?: string\n }\n}\n\nexport interface CMSPageTreeNode {\n id: string\n contentId: string\n parentId: string | null\n order: number\n locked: boolean\n visible?: boolean\n page?: {\n title?: string\n slug?: string\n seoTitle?: string\n seoDescription?: string\n }\n}\n\nexport interface CMSFooterGroup {\n id: string\n heading: string\n headingUrl?: string\n locked: boolean\n links: CMSMenuItem[]\n}\n\nexport interface CMSSiteStructure {\n appId: string\n pageTree: CMSPageTreeNode[]\n menus: {\n header: CMSMenuItem[]\n footer: CMSFooterGroup[]\n footerBottom: CMSMenuItem[]\n sidebar: CMSMenuItem[]\n [slot: string]: CMSMenuItem[] | CMSFooterGroup[]\n }\n updatedAt?: string\n}\n\n// ── Pagination & Responses ─────────────────────────────────────────────────────\n\n// v1 route returns { data, count } — flat, no nested meta object\ninterface CMSListResponse {\n data: CMSPage[]\n count: number\n appId?: string\n}\n\ninterface CMSSingleResponse {\n data: CMSPage\n}\n\n// ── Client Options ─────────────────────────────────────────────────────────────\n\nexport interface CMSClientOptions {\n /** Base URL of your Forge CMS instance, e.g. https://cms.yourschool.io */\n baseUrl?: string\n /** API key generated in CMS Admin → API Keys. Server-side only. */\n apiKey?: string\n /** App ID from CMS Admin → Apps, e.g. \"school-website\" */\n appId?: string\n}\n\nexport interface CMSGetPagesOptions {\n type?: string\n group?: string\n page?: number\n perPage?: number\n status?: 'published' | 'draft' | 'archived'\n}\n\n// ── Factory ───────────────────────────────────────────────────────────────────\n\nexport function createCMSClient(options?: CMSClientOptions) {\n /**\n * Resolve config lazily at request time — NOT at module/build time.\n * This prevents static prerendering crashes when env vars are absent during build.\n */\n function cfg() {\n return {\n baseUrl: (options?.baseUrl ?? process.env.NEXT_PUBLIC_CMS_URL ?? process.env.CMS_BASE_URL ?? '').replace(/\\/$/, ''),\n apiKey: options?.apiKey ?? process.env.CMS_API_KEY ?? '',\n appId: options?.appId ?? process.env.CMS_APP_ID ?? '',\n }\n }\n\n function headers() {\n return { 'X-CMS-API-Key': cfg().apiKey, 'Content-Type': 'application/json' }\n }\n\n // ── Pages ──────────────────────────────────────────────────────────────────\n\n async function getPages(params?: CMSGetPagesOptions): Promise<CMSPage[]> {\n const { baseUrl, apiKey, appId } = cfg()\n if (!baseUrl || !apiKey || !appId) {\n console.warn('[sprintup-cms] Missing CMS_BASE_URL / CMS_API_KEY / CMS_APP_ID — returning []')\n return []\n }\n try {\n const qs = new URLSearchParams()\n if (params?.type) qs.set('type', params.type)\n if (params?.group) qs.set('group', params.group)\n if (params?.page) qs.set('page', String(params.page))\n if (params?.perPage) qs.set('perPage', String(params.perPage))\n const url = `${baseUrl}/api/v1/${appId}/pages${qs.size ? `?${qs}` : ''}`\n const res = await fetch(url, {\n headers: headers(),\n next: { revalidate: 60, tags: [`cms-pages-${appId}`] },\n } as RequestInit)\n if (!res.ok) { console.error(`[sprintup-cms] getPages (${res.status})`); return [] }\n const json: CMSListResponse = await res.json()\n return json.data ?? []\n } catch (err) {\n console.error('[sprintup-cms] getPages error:', err)\n return []\n }\n }\n\n async function getPage(slug: string): Promise<CMSPage | null> {\n const { baseUrl, apiKey, appId } = cfg()\n if (!baseUrl || !apiKey || !appId) {\n console.warn('[sprintup-cms] Missing config — returning null')\n return null\n }\n try {\n const res = await fetch(`${baseUrl}/api/v1/${appId}/pages/${slug}`, {\n headers: headers(),\n next: { revalidate: 60, tags: [`cms-page-${slug}`, `cms-pages-${appId}`] },\n } as RequestInit)\n if (res.status === 404) return null\n if (!res.ok) { console.error(`[sprintup-cms] getPage \"${slug}\" (${res.status})`); return null }\n const json: CMSSingleResponse = await res.json()\n return json.data ?? null\n } catch (err) {\n console.error(`[sprintup-cms] getPage \"${slug}\" error:`, err)\n return null\n }\n }\n\n async function getBlogPosts(): Promise<CMSPage[]> {\n return getPages({ type: 'blog-post' })\n }\n\n async function getEvents(): Promise<CMSPage[]> {\n return getPages({ type: 'event-page' })\n }\n\n async function getAnnouncements(): Promise<CMSPage[]> {\n return getPages({ type: 'announcement-page' })\n }\n\n // ── Preview ────────────────────────────────────────────────────────────────\n\n async function getPreviewPage(token: string): Promise<CMSPage | null> {\n const { baseUrl, appId } = cfg()\n if (!baseUrl || !appId) {\n console.warn('[sprintup-cms] getPreviewPage: Missing baseUrl or appId — returning null')\n return null\n }\n try {\n const url = `${baseUrl}/api/v1/${appId}/preview?token=${encodeURIComponent(token)}`\n const res = await fetch(url, { cache: 'no-store' })\n if (!res.ok) {\n const errorText = await res.text().catch(() => '')\n console.error(`[sprintup-cms] getPreviewPage failed (${res.status}): ${errorText}`)\n return null\n }\n const json = await res.json()\n return json.data ?? null\n } catch (err) {\n console.error('[sprintup-cms] getPreviewPage error:', err)\n return null\n }\n }\n\n async function getPageWithPreview(slug: string, previewToken?: string | null): Promise<CMSPage | null> {\n if (previewToken) {\n const preview = await getPreviewPage(previewToken)\n if (preview?.slug === slug) return preview\n }\n return getPage(slug)\n }\n\n // ── Page Types ─────────────────────────────────────────────────────────────\n\n async function getPageType(pageTypeId: string): Promise<CMSPageType | null> {\n const { baseUrl, apiKey, appId } = cfg()\n if (!baseUrl || !apiKey || !appId || !pageTypeId) return null\n try {\n const res = await fetch(`${baseUrl}/api/v1/${appId}/page-types/${pageTypeId}`, {\n headers: headers(),\n next: { revalidate: 3600, tags: [`cms-page-type-${pageTypeId}`] },\n } as RequestInit)\n if (!res.ok) return null\n const json = await res.json()\n return json.data ?? null\n } catch {\n return null\n }\n }\n\n // ── Site Structure ─────────────────────────────────────────────────────────\n\n async function getSiteStructure(): Promise<CMSSiteStructure | null> {\n const { baseUrl, apiKey, appId } = cfg()\n if (!baseUrl || !apiKey || !appId) return null\n try {\n const res = await fetch(`${baseUrl}/api/v1/${appId}/site-structure`, {\n headers: headers(),\n next: {\n revalidate: 300,\n tags: [`site-structure-${appId}`],\n },\n } as RequestInit)\n if (!res.ok) return null\n const json = await res.json()\n return json.data ?? null\n } catch {\n return null\n }\n }\n\n /**\n * GET /api/v1/{appId}/sitemap\n * Fetch sitemap data for all published pages (priority, changefreq, lastmod).\n *\n * @example app/sitemap.ts\n * import type { MetadataRoute } from 'next'\n * import { cmsClient } from '@sprintup-cms/sdk'\n *\n * export default async function sitemap(): Promise<MetadataRoute.Sitemap> {\n * const data = await cmsClient.getSitemap()\n * if (!data?.enabled) return []\n * return data.urls.map(url => ({\n * url: `${process.env.NEXT_PUBLIC_SITE_URL}${url.loc}`,\n * lastModified: url.lastmod,\n * changeFrequency: url.changefreq as MetadataRoute.Sitemap[0]['changeFrequency'],\n * priority: url.priority,\n * }))\n * }\n */\n async function getSitemap(): Promise<CMSSitemapResponse | null> {\n const { baseUrl, apiKey, appId } = cfg()\n if (!baseUrl || !apiKey || !appId) return null\n try {\n const res = await fetch(`${baseUrl}/api/v1/${appId}/sitemap`, {\n headers: headers(),\n next: { revalidate: 3600 },\n } as RequestInit)\n if (!res.ok) return null\n const json = await res.json()\n return json.data ?? null\n } catch { return null }\n }\n\n /**\n * GET /api/v1/{appId}/status\n * Verify connectivity and retrieve page counts. Never cached — always fresh.\n * Useful for debugging API key setup.\n */\n async function getStatus(): Promise<CMSStatusResponse | null> {\n const { baseUrl, apiKey, appId } = cfg()\n if (!baseUrl || !apiKey || !appId) return null\n try {\n const res = await fetch(`${baseUrl}/api/v1/${appId}/status`, {\n headers: headers(),\n cache: 'no-store',\n } as RequestInit)\n if (!res.ok) return null\n return await res.json()\n } catch { return null }\n }\n\n async function getGlobals(): Promise<{ navigation: CMSPage | null; footer: CMSPage | null }> {\n const { baseUrl, appId } = cfg()\n if (!baseUrl || !appId) return { navigation: null, footer: null }\n try {\n const res = await fetch(`${baseUrl}/api/v1/${appId}/globals`, {\n headers: headers(),\n next: { revalidate: 300, tags: [`cms-globals-${appId}`] },\n } as RequestInit)\n if (!res.ok) {\n const [navPages, footerPages] = await Promise.all([\n getPages({ type: 'navigation' }),\n getPages({ type: 'footer' }),\n ])\n return { navigation: navPages[0] ?? null, footer: footerPages[0] ?? null }\n }\n const json = await res.json()\n return {\n navigation: json.data?.navigation ?? null,\n footer: json.data?.footer ?? null,\n }\n } catch {\n return { navigation: null, footer: null }\n }\n }\n\n return {\n getPages,\n getPage,\n getGlobals,\n getBlogPosts,\n getEvents,\n getAnnouncements,\n getPreviewPage,\n getPageWithPreview,\n getPageType,\n getSiteStructure,\n getSitemap,\n getStatus,\n }\n}\n\n// ── Default singleton ─────────────────────────────────────────────────────────\n\n/** Pre-configured singleton. Reads env vars lazily at request time. */\nexport const cmsClient = createCMSClient()\n"]}
@@ -201,35 +201,6 @@ function createCMSClient(options) {
201
201
  }
202
202
  return getPage(slug);
203
203
  }
204
- async function getGlobals() {
205
- const { baseUrl, apiKey, appId } = cfg();
206
- if (!baseUrl || !apiKey || !appId) {
207
- return { navigation: null, footer: null };
208
- }
209
- try {
210
- const res = await fetch(`${baseUrl}/api/v1/${appId}/globals`, {
211
- headers: headers(),
212
- next: { revalidate: 300, tags: [`cms-globals-${appId}`] }
213
- });
214
- if (!res.ok) {
215
- const [navPages, footerPages] = await Promise.all([
216
- getPages({ type: "navigation" }),
217
- getPages({ type: "footer" })
218
- ]);
219
- return {
220
- navigation: navPages[0] ?? null,
221
- footer: footerPages[0] ?? null
222
- };
223
- }
224
- const json = await res.json();
225
- return {
226
- navigation: json.data?.navigation ?? null,
227
- footer: json.data?.footer ?? null
228
- };
229
- } catch {
230
- return { navigation: null, footer: null };
231
- }
232
- }
233
204
  async function getPageType(pageTypeId) {
234
205
  const { baseUrl, apiKey, appId } = cfg();
235
206
  if (!baseUrl || !apiKey || !appId || !pageTypeId) return null;
@@ -292,6 +263,30 @@ function createCMSClient(options) {
292
263
  return null;
293
264
  }
294
265
  }
266
+ async function getGlobals() {
267
+ const { baseUrl, appId } = cfg();
268
+ if (!baseUrl || !appId) return { navigation: null, footer: null };
269
+ try {
270
+ const res = await fetch(`${baseUrl}/api/v1/${appId}/globals`, {
271
+ headers: headers(),
272
+ next: { revalidate: 300, tags: [`cms-globals-${appId}`] }
273
+ });
274
+ if (!res.ok) {
275
+ const [navPages, footerPages] = await Promise.all([
276
+ getPages({ type: "navigation" }),
277
+ getPages({ type: "footer" })
278
+ ]);
279
+ return { navigation: navPages[0] ?? null, footer: footerPages[0] ?? null };
280
+ }
281
+ const json = await res.json();
282
+ return {
283
+ navigation: json.data?.navigation ?? null,
284
+ footer: json.data?.footer ?? null
285
+ };
286
+ } catch {
287
+ return { navigation: null, footer: null };
288
+ }
289
+ }
295
290
  return {
296
291
  getPages,
297
292
  getPage,
@@ -763,18 +758,6 @@ function SplitHeroBlock({ block }) {
763
758
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-1 w-full", children: d.image ? /* @__PURE__ */ jsxRuntime.jsx("img", { src: d.image, alt: d.title || "", className: "w-full rounded-2xl object-cover shadow-lg border border-border" }) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-full rounded-2xl bg-muted border border-border aspect-video flex items-center justify-center", children: /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs text-muted-foreground", children: "No image set" }) }) })
764
759
  ] }) });
765
760
  }
766
- function NavbarBlock({ block }) {
767
- const d = getData(block);
768
- const links = Array.isArray(d.links) ? d.links : [];
769
- return /* @__PURE__ */ jsxRuntime.jsx("header", { className: `w-full border-b border-border bg-background/95 backdrop-blur ${d.sticky ? "sticky top-0 z-50" : ""}`, children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "max-w-6xl mx-auto px-4 h-16 flex items-center justify-between gap-4", children: [
770
- /* @__PURE__ */ jsxRuntime.jsxs("a", { href: "/", className: "flex items-center gap-2", children: [
771
- d.logo && /* @__PURE__ */ jsxRuntime.jsx("img", { src: d.logo, alt: d.logoText || "Logo", className: "h-8 w-auto" }),
772
- d.logoText && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-bold text-base", children: d.logoText })
773
- ] }),
774
- /* @__PURE__ */ jsxRuntime.jsx("nav", { className: "hidden md:flex items-center gap-6", children: links.map((link, i) => /* @__PURE__ */ jsxRuntime.jsx("a", { href: link.url || "#", className: "text-sm text-muted-foreground hover:text-foreground transition-colors", children: link.label }, i)) }),
775
- d.ctaLabel && /* @__PURE__ */ jsxRuntime.jsx("a", { href: d.ctaUrl || "#", className: "inline-flex items-center px-4 py-2 rounded-lg bg-primary text-primary-foreground text-sm font-semibold hover:opacity-90 transition-opacity", children: d.ctaLabel })
776
- ] }) });
777
- }
778
761
  function AccordionBlock({ block }) {
779
762
  const d = getData(block);
780
763
  const items = Array.isArray(d.items) ? d.items : [];
@@ -1037,7 +1020,6 @@ var BUILT_IN = {
1037
1020
  "feature-grid": (b) => /* @__PURE__ */ jsxRuntime.jsx(FeatureGridBlock, { block: b }),
1038
1021
  "two-column": (b) => /* @__PURE__ */ jsxRuntime.jsx(TwoColumnBlock, { block: b }),
1039
1022
  "split-hero": (b) => /* @__PURE__ */ jsxRuntime.jsx(SplitHeroBlock, { block: b }),
1040
- navbar: (b) => /* @__PURE__ */ jsxRuntime.jsx(NavbarBlock, { block: b }),
1041
1023
  accordion: (b) => /* @__PURE__ */ jsxRuntime.jsx(AccordionBlock, { block: b }),
1042
1024
  tabs: (b) => /* @__PURE__ */ jsxRuntime.jsx(TabsBlock, { block: b }),
1043
1025
  columns: (b) => /* @__PURE__ */ jsxRuntime.jsx(ColumnsBlock, { block: b }),
@@ -1169,6 +1151,97 @@ function ServerProductListBlock({ block }) {
1169
1151
  }) })
1170
1152
  ] }) });
1171
1153
  }
1154
+ function parseNavItems(html) {
1155
+ if (!html) return [];
1156
+ const text = html.replace(/<[^>]*>/g, "").replace(/&nbsp;/g, " ");
1157
+ return text.split(/\r?\n/).filter(Boolean).map((line) => {
1158
+ const [label, url] = line.split("|").map((s) => s.trim());
1159
+ return { label: label || "", url: url || "#" };
1160
+ });
1161
+ }
1162
+ function CMSHeader({ nav }) {
1163
+ if (!nav) return null;
1164
+ const primary = nav.sections?.primary ?? nav.content?.primary ?? nav.primary ?? {};
1165
+ const navItems = parseNavItems(primary.nav_items || "");
1166
+ const logoUrl = primary.logo_url || "";
1167
+ const logoAlt = primary.logo_alt || "";
1168
+ if (!logoUrl && !logoAlt && navItems.length === 0) return null;
1169
+ return /* @__PURE__ */ jsxRuntime.jsx("header", { className: "w-full border-b border-border bg-background/95 backdrop-blur sticky top-0 z-50", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "max-w-6xl mx-auto px-4 h-16 flex items-center justify-between gap-4", children: [
1170
+ /* @__PURE__ */ jsxRuntime.jsxs("a", { href: "/", className: "flex items-center gap-2 shrink-0", children: [
1171
+ logoUrl && /* @__PURE__ */ jsxRuntime.jsx("img", { src: logoUrl, alt: logoAlt || "Logo", className: "h-8 w-auto" }),
1172
+ !logoUrl && logoAlt && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-bold text-base", children: logoAlt })
1173
+ ] }),
1174
+ navItems.length > 0 && /* @__PURE__ */ jsxRuntime.jsx("nav", { className: "hidden md:flex items-center gap-6", children: navItems.map((item, i) => /* @__PURE__ */ jsxRuntime.jsx("a", { href: item.url, className: "text-sm text-muted-foreground hover:text-foreground transition-colors", children: item.label }, i)) })
1175
+ ] }) });
1176
+ }
1177
+ function parseFooterColumns(html) {
1178
+ if (!html) return [];
1179
+ const text = html.replace(/<[^>]*>/g, "").replace(/&nbsp;/g, " ");
1180
+ const lines = text.split(/\r?\n/).filter(Boolean);
1181
+ const cols = [];
1182
+ let current = null;
1183
+ for (const line of lines) {
1184
+ const parts = line.split("|").map((s) => s.trim());
1185
+ if (parts.length === 1) {
1186
+ if (current) cols.push(current);
1187
+ current = { title: parts[0], links: [] };
1188
+ } else if (parts.length >= 2) {
1189
+ if (!current) current = { title: "", links: [] };
1190
+ current.links.push({ label: parts[0], url: parts[1] || "#" });
1191
+ }
1192
+ }
1193
+ if (current) cols.push(current);
1194
+ return cols;
1195
+ }
1196
+ function parseLegalLinks(html) {
1197
+ if (!html) return [];
1198
+ const text = html.replace(/<[^>]*>/g, "").replace(/&nbsp;/g, " ");
1199
+ return text.split(/\r?\n/).filter(Boolean).map((line) => {
1200
+ const [label, url] = line.split("|").map((s) => s.trim());
1201
+ return { label: label || "", url: url || "#" };
1202
+ });
1203
+ }
1204
+ function CMSFooter({ footer }) {
1205
+ if (!footer) return null;
1206
+ const c = footer.sections?.content ?? footer.content?.content ?? footer.content ?? {};
1207
+ const contact = footer.sections?.contact ?? footer.content?.contact ?? footer.contact ?? {};
1208
+ const social = footer.sections?.social ?? footer.content?.social ?? footer.social ?? {};
1209
+ const legal = footer.sections?.legal ?? footer.content?.legal ?? footer.legal ?? {};
1210
+ const columns = parseFooterColumns(c.columns || "");
1211
+ const legalLinks = parseLegalLinks(legal.legal_links || "");
1212
+ const hasSocial = social.facebook || social.twitter || social.instagram || social.linkedin || social.youtube;
1213
+ const isEmpty = !c.tagline && !c.logo_url && columns.length === 0 && !contact.email && !contact.phone && !hasSocial && !legal.copyright && legalLinks.length === 0;
1214
+ if (isEmpty) return null;
1215
+ return /* @__PURE__ */ jsxRuntime.jsx("footer", { className: "border-t border-border bg-muted/30 pt-12 pb-6 mt-16", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "max-w-6xl mx-auto px-4", children: [
1216
+ (c.tagline || c.logo_url || columns.length > 0 || contact.email) && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `grid gap-8 mb-10 grid-cols-1 sm:grid-cols-2 ${columns.length > 0 ? "lg:grid-cols-4" : "lg:grid-cols-2"}`, children: [
1217
+ (c.logo_url || c.tagline) && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-3", children: [
1218
+ c.logo_url && /* @__PURE__ */ jsxRuntime.jsx("img", { src: c.logo_url, alt: "Logo", className: "h-8 w-auto" }),
1219
+ c.tagline && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-muted-foreground leading-relaxed", children: c.tagline })
1220
+ ] }),
1221
+ columns.map((col, i) => /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
1222
+ col.title && /* @__PURE__ */ jsxRuntime.jsx("h4", { className: "font-semibold text-sm mb-3", children: col.title }),
1223
+ /* @__PURE__ */ jsxRuntime.jsx("ul", { className: "space-y-2", children: col.links.map((link, j) => /* @__PURE__ */ jsxRuntime.jsx("li", { children: /* @__PURE__ */ jsxRuntime.jsx("a", { href: link.url, className: "text-sm text-muted-foreground hover:text-foreground transition-colors", children: link.label }) }, j)) })
1224
+ ] }, i)),
1225
+ (contact.email || contact.phone || contact.address) && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-2", children: [
1226
+ /* @__PURE__ */ jsxRuntime.jsx("h4", { className: "font-semibold text-sm mb-3", children: "Contact" }),
1227
+ contact.email && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-muted-foreground", children: contact.email }),
1228
+ contact.phone && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-muted-foreground", children: contact.phone }),
1229
+ contact.address && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-muted-foreground whitespace-pre-line", children: contact.address })
1230
+ ] })
1231
+ ] }),
1232
+ hasSocial && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-wrap gap-4 mb-6", children: [
1233
+ social.facebook && /* @__PURE__ */ jsxRuntime.jsx("a", { href: social.facebook, className: "text-sm text-muted-foreground hover:text-foreground transition-colors", children: "Facebook" }),
1234
+ social.twitter && /* @__PURE__ */ jsxRuntime.jsx("a", { href: social.twitter, className: "text-sm text-muted-foreground hover:text-foreground transition-colors", children: "X" }),
1235
+ social.instagram && /* @__PURE__ */ jsxRuntime.jsx("a", { href: social.instagram, className: "text-sm text-muted-foreground hover:text-foreground transition-colors", children: "Instagram" }),
1236
+ social.linkedin && /* @__PURE__ */ jsxRuntime.jsx("a", { href: social.linkedin, className: "text-sm text-muted-foreground hover:text-foreground transition-colors", children: "LinkedIn" }),
1237
+ social.youtube && /* @__PURE__ */ jsxRuntime.jsx("a", { href: social.youtube, className: "text-sm text-muted-foreground hover:text-foreground transition-colors", children: "YouTube" })
1238
+ ] }),
1239
+ (legal.copyright || legalLinks.length > 0) && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-wrap items-center justify-between gap-4 pt-6 border-t border-border", children: [
1240
+ legal.copyright && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs text-muted-foreground", children: legal.copyright }),
1241
+ legalLinks.length > 0 && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-wrap gap-4", children: legalLinks.map((link, i) => /* @__PURE__ */ jsxRuntime.jsx("a", { href: link.url, className: "text-xs text-muted-foreground hover:text-foreground transition-colors", children: link.label }, i)) })
1242
+ ] })
1243
+ ] }) });
1244
+ }
1172
1245
  var client = createCMSClient();
1173
1246
  async function generateMetadata({ params }) {
1174
1247
  const { slug } = await params;
@@ -1225,12 +1298,13 @@ async function CMSCatchAllPage({ params }) {
1225
1298
  page = await client.getPage(slugStr);
1226
1299
  }
1227
1300
  if (!page) navigation.notFound();
1228
- let pageType = null;
1229
- if (page.pageTypeId) {
1230
- pageType = await client.getPageType(page.pageTypeId);
1231
- }
1301
+ const [pageType, globals] = await Promise.all([
1302
+ page.pageTypeId ? client.getPageType(page.pageTypeId) : Promise.resolve(null),
1303
+ client.getGlobals().catch(() => ({ navigation: null, footer: null }))
1304
+ ]);
1232
1305
  const resolvedBlocks = await resolveProductListBlocks(page.blocks ?? []);
1233
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "min-h-screen bg-background", children: [
1306
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "min-h-screen bg-background flex flex-col", children: [
1307
+ /* @__PURE__ */ jsxRuntime.jsx(CMSHeader, { nav: globals?.navigation }),
1234
1308
  isPreview && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "sticky top-0 z-50 flex items-center justify-between px-4 py-2.5 bg-amber-500 text-white text-sm font-medium shadow", children: [
1235
1309
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [
1236
1310
  /* @__PURE__ */ jsxRuntime.jsx("span", { children: "Draft Preview" }),
@@ -1259,7 +1333,8 @@ async function CMSCatchAllPage({ params }) {
1259
1333
  ] }) : /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col items-center justify-center py-24 text-muted-foreground border-2 border-dashed border-border rounded-xl", children: [
1260
1334
  /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm", children: "No content added yet." }),
1261
1335
  isPreview && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs mt-1 opacity-60", children: "Add blocks in the CMS editor to see them here." })
1262
- ] }) })
1336
+ ] }) }),
1337
+ /* @__PURE__ */ jsxRuntime.jsx(CMSFooter, { footer: globals?.footer })
1263
1338
  ] });
1264
1339
  }
1265
1340
  async function dynamicProxyGET(req) {