@riverbankcms/sdk 0.6.0 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (84) hide show
  1. package/README.md +239 -0
  2. package/dist/cli/index.js +22 -5
  3. package/dist/cli/index.js.map +1 -1
  4. package/dist/client/client.js +1 -1
  5. package/dist/client/client.js.map +1 -1
  6. package/dist/client/client.mjs +1 -1
  7. package/dist/client/client.mjs.map +1 -1
  8. package/dist/server/{Layout-BClXUTsd.d.mts → Layout-B7cvis7r.d.mts} +3 -3
  9. package/dist/server/{Layout-UXGjXv8M.d.ts → Layout-wBtJLTVX.d.ts} +3 -3
  10. package/dist/server/{chunk-Z5ZA6Q4D.mjs → chunk-74XUVNOO.mjs} +5 -3
  11. package/dist/server/chunk-74XUVNOO.mjs.map +1 -0
  12. package/dist/server/{chunk-PHS2KWJX.js → chunk-7BVRA5MY.js} +7 -52
  13. package/dist/server/chunk-7BVRA5MY.js.map +1 -0
  14. package/dist/server/{chunk-SQMGHEJM.mjs → chunk-7FIJSGHU.mjs} +2 -2
  15. package/dist/server/{chunk-SQMGHEJM.mjs.map → chunk-7FIJSGHU.mjs.map} +1 -1
  16. package/dist/server/chunk-ARNCLSQT.mjs +52 -0
  17. package/dist/server/chunk-ARNCLSQT.mjs.map +1 -0
  18. package/dist/server/chunk-BNQV3PXP.js +69 -0
  19. package/dist/server/chunk-BNQV3PXP.js.map +1 -0
  20. package/dist/server/{chunk-SSS7CCRR.js → chunk-EIVISR62.js} +15 -57
  21. package/dist/server/chunk-EIVISR62.js.map +1 -0
  22. package/dist/server/{chunk-I2D7KOEA.js → chunk-JWRNMNWI.js} +5 -3
  23. package/dist/server/chunk-JWRNMNWI.js.map +1 -0
  24. package/dist/server/{chunk-EOWGKCUZ.js → chunk-P7UVAMK6.js} +2 -2
  25. package/dist/server/{chunk-EOWGKCUZ.js.map → chunk-P7UVAMK6.js.map} +1 -1
  26. package/dist/server/{chunk-2HXHFSMI.mjs → chunk-RBJFXNDM.mjs} +6 -51
  27. package/dist/server/chunk-RBJFXNDM.mjs.map +1 -0
  28. package/dist/server/chunk-SWYWZT3L.mjs +69 -0
  29. package/dist/server/chunk-SWYWZT3L.mjs.map +1 -0
  30. package/dist/server/chunk-T26N3P26.js +52 -0
  31. package/dist/server/chunk-T26N3P26.js.map +1 -0
  32. package/dist/server/{chunk-PMHLZ3FW.mjs → chunk-YXA4GAAQ.mjs} +15 -57
  33. package/dist/server/chunk-YXA4GAAQ.mjs.map +1 -0
  34. package/dist/server/{components-DppHY5oD.d.ts → components-CICSJyp_.d.ts} +1 -1
  35. package/dist/server/{components-BmaJxgCV.d.mts → components-CMMwDXTW.d.mts} +1 -1
  36. package/dist/server/components.d.mts +2 -2
  37. package/dist/server/components.d.ts +2 -2
  38. package/dist/server/components.js +5 -3
  39. package/dist/server/components.js.map +1 -1
  40. package/dist/server/components.mjs +5 -3
  41. package/dist/server/index-BTwWvSBu.d.ts +130 -0
  42. package/dist/server/index-DI_qlYx3.d.mts +130 -0
  43. package/dist/server/index.js +2 -2
  44. package/dist/server/index.mjs +1 -1
  45. package/dist/server/{loadContent-BS-3wesN.d.mts → loadContent-C-YYUKQa.d.mts} +10 -2
  46. package/dist/server/{loadContent-Buvmudee.d.ts → loadContent-DmgpFcFC.d.ts} +10 -2
  47. package/dist/server/metadata.d.mts +8 -135
  48. package/dist/server/metadata.d.ts +8 -135
  49. package/dist/server/metadata.js +5 -65
  50. package/dist/server/metadata.js.map +1 -1
  51. package/dist/server/metadata.mjs +4 -64
  52. package/dist/server/metadata.mjs.map +1 -1
  53. package/dist/server/navigation.d.mts +80 -145
  54. package/dist/server/navigation.d.ts +80 -145
  55. package/dist/server/navigation.js +2 -10
  56. package/dist/server/navigation.js.map +1 -1
  57. package/dist/server/navigation.mjs +7 -15
  58. package/dist/server/next.d.mts +274 -0
  59. package/dist/server/next.d.ts +274 -0
  60. package/dist/server/next.js +150 -0
  61. package/dist/server/next.js.map +1 -0
  62. package/dist/server/next.mjs +150 -0
  63. package/dist/server/next.mjs.map +1 -0
  64. package/dist/server/rendering/server.d.mts +1 -1
  65. package/dist/server/rendering/server.d.ts +1 -1
  66. package/dist/server/rendering/server.js +5 -3
  67. package/dist/server/rendering/server.js.map +1 -1
  68. package/dist/server/rendering/server.mjs +5 -3
  69. package/dist/server/rendering.d.mts +4 -4
  70. package/dist/server/rendering.d.ts +4 -4
  71. package/dist/server/rendering.js +6 -4
  72. package/dist/server/rendering.js.map +1 -1
  73. package/dist/server/rendering.mjs +6 -4
  74. package/dist/server/server.d.mts +1 -1
  75. package/dist/server/server.d.ts +1 -1
  76. package/dist/server/server.js +3 -3
  77. package/dist/server/server.mjs +2 -2
  78. package/package.json +13 -1
  79. package/dist/server/chunk-2HXHFSMI.mjs.map +0 -1
  80. package/dist/server/chunk-I2D7KOEA.js.map +0 -1
  81. package/dist/server/chunk-PHS2KWJX.js.map +0 -1
  82. package/dist/server/chunk-PMHLZ3FW.mjs.map +0 -1
  83. package/dist/server/chunk-SSS7CCRR.js.map +0 -1
  84. package/dist/server/chunk-Z5ZA6Q4D.mjs.map +0 -1
@@ -0,0 +1,52 @@
1
+ import {
2
+ PageRenderer,
3
+ buildThemeRuntime
4
+ } from "./chunk-LNOUXALA.mjs";
5
+
6
+ // src/rendering/components/Page.tsx
7
+ import { jsx } from "react/jsx-runtime";
8
+ function Page({
9
+ page,
10
+ theme,
11
+ themeTokens: providedTokens,
12
+ siteId,
13
+ resolvedData,
14
+ routeMap,
15
+ wrapBlock,
16
+ registry,
17
+ usePlaceholders = false,
18
+ blockOverrides,
19
+ sdkConfig,
20
+ supabaseUrl,
21
+ dataContext
22
+ }) {
23
+ const baseTokens = providedTokens ?? buildThemeRuntime(theme).tokens;
24
+ const themeTokens = sdkConfig?.theme?.palette ? { ...baseTokens, palette: { ...baseTokens.palette, ...sdkConfig.theme.palette } } : baseTokens;
25
+ return /* @__PURE__ */ jsx(
26
+ PageRenderer,
27
+ {
28
+ theme,
29
+ page,
30
+ themeTokens,
31
+ usePlaceholders,
32
+ dataContext: {
33
+ siteId,
34
+ resolvedData,
35
+ routes: routeMap,
36
+ occurrenceContext: dataContext?.occurrenceContext ?? null,
37
+ contentEntry: dataContext?.contentEntry ?? null,
38
+ supabaseUrl
39
+ },
40
+ routeMap,
41
+ wrapBlock,
42
+ registry,
43
+ blockOverrides,
44
+ sdkConfig
45
+ }
46
+ );
47
+ }
48
+
49
+ export {
50
+ Page
51
+ };
52
+ //# sourceMappingURL=chunk-ARNCLSQT.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/rendering/components/Page.tsx"],"sourcesContent":["/**\n * Pure Page renderer component for Riverbank CMS.\n *\n * This component expects all data to be provided via props.\n * For data fetching, use:\n * - Server-side: `await loadPage({ client, siteId, path })`\n * - Client-side: `usePage({ client, siteId, path })`\n */\n\nimport { PageRenderer, buildThemeRuntime } from '@riverbankcms/blocks';\nimport type { PageOutline, RouteMap, Theme, ThemeTokens, BlockOverrides, OccurrenceContextData } from '@riverbankcms/blocks';\nimport type { ResolvedBlockData } from '../../data';\nimport type { RuntimeSdkConfig } from '../helpers/loadPage';\n\n// Re-export OccurrenceContextData for SDK consumers\nexport type { OccurrenceContextData };\n\nexport type PageProps = {\n // Required data (must be provided by caller)\n page: PageOutline;\n theme: Theme;\n siteId: string;\n\n // Optional data\n themeTokens?: ThemeTokens; // If not provided, will be built from theme\n resolvedData?: ResolvedBlockData; // Pre-fetched block data\n routeMap?: RouteMap;\n /**\n * SDK site configuration containing theme palette overrides.\n * When provided, the SDK palette tokens are merged into the theme tokens,\n * allowing blocks to use SDK-defined color tokens for section backgrounds.\n */\n sdkConfig?: RuntimeSdkConfig | null;\n /**\n * Supabase storage URL for direct image access.\n * SDK sites receive this from the API instead of requiring NEXT_PUBLIC_SUPABASE_URL env var.\n */\n supabaseUrl?: string;\n\n /**\n * Additional context data for content entry pages.\n * Used to pass occurrence context and content entry data to blocks.\n */\n dataContext?: {\n /** Occurrence context for event pages (from URL like /events/yoga/2025-01-15) */\n occurrenceContext?: OccurrenceContextData | null;\n /** Content entry data for template pages */\n contentEntry?: Record<string, unknown> | null;\n };\n\n // Customization\n wrapBlock?: (blockId: string, rendered: React.ReactNode) => React.ReactNode;\n registry?: Parameters<typeof PageRenderer>[0]['registry'];\n usePlaceholders?: boolean;\n /**\n * Custom components to override default block rendering.\n * Keys can be full block kind (\"block.hero\") or short form (\"hero\").\n *\n * This is SSR-safe - no context or hooks required.\n *\n * @example\n * ```tsx\n * <Page\n * {...pageData}\n * blockOverrides={{\n * 'hero': MyCustomHero,\n * 'block.testimonials': MyCustomTestimonials,\n * }}\n * />\n * ```\n */\n blockOverrides?: BlockOverrides;\n};\n\n/**\n * Pure renderer for Riverbank CMS pages.\n *\n * This component expects all data to be provided via props.\n * For data fetching, use:\n * - Server-side: `await loadPage({ client, siteId, path })`\n * - Client-side: `usePage({ client, siteId, path })`\n *\n * @example Server-side (Next.js App Router)\n * ```tsx\n * import { createRiverbankClient } from '@riverbankcms/sdk';\n * import { loadPage, Page } from '@riverbankcms/sdk/rendering';\n *\n * const client = createRiverbankClient({ apiKey, baseUrl });\n *\n * export default async function PageRoute({ params }) {\n * const pageData = await loadPage({\n * client,\n * siteId: 'site-id',\n * path: `/${params.slug}`,\n * });\n *\n * return <Page {...pageData} />;\n * }\n * ```\n *\n * @example Client-side\n * ```tsx\n * import { createRiverbankClient } from '@riverbankcms/sdk';\n * import { usePage, Page } from '@riverbankcms/sdk/rendering';\n *\n * const client = createRiverbankClient({ apiKey, baseUrl });\n *\n * function MyPage({ path }) {\n * const pageData = usePage({ client, siteId: 'site-id', path });\n *\n * if (pageData.loading) return <LoadingState />;\n * if (pageData.error) return <ErrorState error={pageData.error} />;\n * if (!pageData.page) return <NotFound />;\n *\n * return <Page {...pageData} />;\n * }\n * ```\n */\nexport function Page({\n page,\n theme,\n themeTokens: providedTokens,\n siteId,\n resolvedData,\n routeMap,\n wrapBlock,\n registry,\n usePlaceholders = false,\n blockOverrides,\n sdkConfig,\n supabaseUrl,\n dataContext,\n}: PageProps) {\n // Build theme tokens if not provided\n const baseTokens = providedTokens ?? buildThemeRuntime(theme).tokens;\n\n // Merge SDK palette tokens into theme tokens\n // This allows blocks to resolve SDK-defined color tokens (e.g., 'primary' -> '#6d28d9')\n const themeTokens = sdkConfig?.theme?.palette\n ? { ...baseTokens, palette: { ...baseTokens.palette, ...sdkConfig.theme.palette } }\n : baseTokens;\n\n return (\n <PageRenderer\n theme={theme}\n page={page}\n themeTokens={themeTokens}\n usePlaceholders={usePlaceholders}\n dataContext={{\n siteId,\n resolvedData,\n routes: routeMap,\n occurrenceContext: dataContext?.occurrenceContext ?? null,\n contentEntry: dataContext?.contentEntry ?? null,\n supabaseUrl,\n }}\n routeMap={routeMap}\n wrapBlock={wrapBlock}\n registry={registry}\n blockOverrides={blockOverrides}\n sdkConfig={sdkConfig}\n />\n );\n}\n"],"mappings":";;;;;;AA+II;AAzBG,SAAS,KAAK;AAAA,EACnB;AAAA,EACA;AAAA,EACA,aAAa;AAAA,EACb;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,kBAAkB;AAAA,EAClB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAAc;AAEZ,QAAM,aAAa,kBAAkB,kBAAkB,KAAK,EAAE;AAI9D,QAAM,cAAc,WAAW,OAAO,UAClC,EAAE,GAAG,YAAY,SAAS,EAAE,GAAG,WAAW,SAAS,GAAG,UAAU,MAAM,QAAQ,EAAE,IAChF;AAEJ,SACE;AAAA,IAAC;AAAA;AAAA,MACC;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA,aAAa;AAAA,QACX;AAAA,QACA;AAAA,QACA,QAAQ;AAAA,QACR,mBAAmB,aAAa,qBAAqB;AAAA,QACrD,cAAc,aAAa,gBAAgB;AAAA,QAC3C;AAAA,MACF;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA;AAAA,EACF;AAEJ;","names":[]}
@@ -0,0 +1,69 @@
1
+ "use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } } function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; }// src/metadata/generatePageMetadata.ts
2
+ function generatePageMetadata(input) {
3
+ const { page, site, path, siteUrl, overrides, googleSiteVerification } = input;
4
+ const pageTitle = _nullishCoalesce(_optionalChain([overrides, 'optionalAccess', _ => _.title]), () => ( page.name));
5
+ const fullTitle = pageTitle === site.title ? pageTitle : `${pageTitle} | ${site.title}`;
6
+ const description = _nullishCoalesce(_optionalChain([overrides, 'optionalAccess', _2 => _2.description]), () => ( page.purpose));
7
+ const canonicalUrl = _nullishCoalesce(_optionalChain([overrides, 'optionalAccess', _3 => _3.canonicalUrl]), () => ( `${siteUrl}${path}`));
8
+ const fullUrl = `${siteUrl}${path}`;
9
+ const metadata = {
10
+ title: fullTitle,
11
+ description: _nullishCoalesce(description, () => ( void 0)),
12
+ alternates: {
13
+ canonical: canonicalUrl
14
+ },
15
+ openGraph: {
16
+ title: pageTitle,
17
+ description: _nullishCoalesce(description, () => ( void 0)),
18
+ url: fullUrl,
19
+ siteName: site.title,
20
+ type: "website",
21
+ ..._optionalChain([overrides, 'optionalAccess', _4 => _4.ogImage]) ? {
22
+ images: [
23
+ {
24
+ url: overrides.ogImage,
25
+ alt: pageTitle
26
+ }
27
+ ]
28
+ } : {}
29
+ },
30
+ twitter: {
31
+ card: "summary_large_image",
32
+ title: pageTitle,
33
+ description: _nullishCoalesce(description, () => ( void 0)),
34
+ ..._optionalChain([overrides, 'optionalAccess', _5 => _5.ogImage]) ? {
35
+ images: [overrides.ogImage]
36
+ } : {}
37
+ }
38
+ };
39
+ if (_optionalChain([overrides, 'optionalAccess', _6 => _6.robots])) {
40
+ metadata.robots = {
41
+ index: _nullishCoalesce(overrides.robots.index, () => ( true)),
42
+ follow: _nullishCoalesce(overrides.robots.follow, () => ( true))
43
+ };
44
+ }
45
+ if (googleSiteVerification) {
46
+ metadata.verification = {
47
+ google: googleSiteVerification
48
+ };
49
+ }
50
+ return metadata;
51
+ }
52
+ function generatePreviewMetadata(input) {
53
+ return generatePageMetadata({
54
+ ...input,
55
+ overrides: {
56
+ ...input.overrides,
57
+ robots: {
58
+ index: false,
59
+ follow: false
60
+ }
61
+ }
62
+ });
63
+ }
64
+
65
+
66
+
67
+
68
+ exports.generatePageMetadata = generatePageMetadata; exports.generatePreviewMetadata = generatePreviewMetadata;
69
+ //# sourceMappingURL=chunk-BNQV3PXP.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["/Users/will/Projects/Business/cms/builder/packages/sdk/dist/server/chunk-BNQV3PXP.js","../../src/metadata/generatePageMetadata.ts"],"names":[],"mappings":"AAAA;ACgHO,SAAS,oBAAA,CAAqB,KAAA,EAAoC;AACvE,EAAA,MAAM,EAAE,IAAA,EAAM,IAAA,EAAM,IAAA,EAAM,OAAA,EAAS,SAAA,EAAW,uBAAuB,EAAA,EAAI,KAAA;AAGzE,EAAA,MAAM,UAAA,mCAAY,SAAA,2BAAW,OAAA,UAAS,IAAA,CAAK,MAAA;AAC3C,EAAA,MAAM,UAAA,EAAY,UAAA,IAAc,IAAA,CAAK,MAAA,EAAQ,UAAA,EAAY,CAAA,EAAA;AAGN,EAAA;AAGO,EAAA;AACzB,EAAA;AAGN,EAAA;AAClB,IAAA;AACqB,IAAA;AAChB,IAAA;AACC,MAAA;AACb,IAAA;AACW,IAAA;AACF,MAAA;AACqB,MAAA;AACvB,MAAA;AACU,MAAA;AACT,MAAA;AAEF,MAAA;AACU,QAAA;AACN,UAAA;AACiB,YAAA;AACV,YAAA;AACP,UAAA;AACF,QAAA;AAED,MAAA;AACP,IAAA;AACS,IAAA;AACD,MAAA;AACC,MAAA;AACqB,MAAA;AAExB,MAAA;AAC4B,QAAA;AAE3B,MAAA;AACP,IAAA;AACF,EAAA;AAGuB,EAAA;AACH,IAAA;AACiB,MAAA;AACE,MAAA;AACrC,IAAA;AACF,EAAA;AAG4B,EAAA;AACF,IAAA;AACd,MAAA;AACV,IAAA;AACF,EAAA;AAEO,EAAA;AACT;AAuB4E;AAC9C,EAAA;AACvB,IAAA;AACQ,IAAA;AACA,MAAA;AACD,MAAA;AACC,QAAA;AACC,QAAA;AACV,MAAA;AACF,IAAA;AACD,EAAA;AACH;ADrJ4D;AACA;AACA;AACA;AACA","file":"/Users/will/Projects/Business/cms/builder/packages/sdk/dist/server/chunk-BNQV3PXP.js","sourcesContent":[null,"/**\n * Metadata generation helper for Next.js pages\n *\n * Generates SEO-optimized metadata from Builder page data for use in\n * Next.js generateMetadata() functions.\n */\n\nimport type { LoadPageResult } from '../rendering/helpers/loadPage';\nimport type { SiteResponse } from '../client/types';\n\n/**\n * Next.js Metadata type\n * Simplified version to avoid requiring Next.js as a dependency\n */\nexport type Metadata = {\n title?: string;\n description?: string;\n alternates?: {\n canonical?: string;\n };\n openGraph?: {\n title?: string;\n description?: string;\n url?: string;\n siteName?: string;\n type?: string;\n images?: Array<{\n url: string;\n alt?: string;\n }>;\n };\n twitter?: {\n card?: string;\n title?: string;\n description?: string;\n images?: string[];\n };\n robots?: {\n index?: boolean;\n follow?: boolean;\n };\n verification?: {\n google?: string;\n };\n};\n\nexport type PageMetadataInput = {\n /**\n * Page data from loadPage() or custom page object\n */\n page: LoadPageResult['page'] | {\n name: string;\n purpose?: string;\n };\n\n /**\n * Site data from client.getSite()\n */\n site: SiteResponse['site'];\n\n /**\n * Current URL path (e.g., '/about', '/blog/post-1')\n */\n path: string;\n\n /**\n * Full site URL for canonical and OG URLs\n * Example: 'https://example.com'\n */\n siteUrl: string;\n\n /**\n * Optional custom metadata overrides\n */\n overrides?: {\n title?: string;\n description?: string;\n ogImage?: string;\n canonicalUrl?: string;\n robots?: {\n index?: boolean;\n follow?: boolean;\n };\n };\n\n /**\n * Optional Google site verification token\n */\n googleSiteVerification?: string;\n};\n\n/**\n * Generate Next.js Metadata object from Builder page data\n *\n * @example\n * ```tsx\n * import { generatePageMetadata } from '@riverbankcms/sdk/metadata';\n * import { loadPage } from '@riverbankcms/sdk';\n *\n * export async function generateMetadata({ params }) {\n * const pageData = await loadPage({ client, siteId, path: params.slug });\n * const siteData = await client.getSite({ id: siteId });\n *\n * return generatePageMetadata({\n * page: pageData.page,\n * site: siteData.site,\n * path: params.slug,\n * siteUrl: 'https://example.com',\n * });\n * }\n * ```\n */\nexport function generatePageMetadata(input: PageMetadataInput): Metadata {\n const { page, site, path, siteUrl, overrides, googleSiteVerification } = input;\n\n // Build page title\n const pageTitle = overrides?.title ?? page.name;\n const fullTitle = pageTitle === site.title ? pageTitle : `${pageTitle} | ${site.title}`;\n\n // Use page purpose as description fallback\n const description = overrides?.description ?? page.purpose;\n\n // Build full URLs\n const canonicalUrl = overrides?.canonicalUrl ?? `${siteUrl}${path}`;\n const fullUrl = `${siteUrl}${path}`;\n\n // Build metadata object\n const metadata: Metadata = {\n title: fullTitle,\n description: description ?? undefined,\n alternates: {\n canonical: canonicalUrl,\n },\n openGraph: {\n title: pageTitle,\n description: description ?? undefined,\n url: fullUrl,\n siteName: site.title,\n type: 'website',\n ...(overrides?.ogImage\n ? {\n images: [\n {\n url: overrides.ogImage,\n alt: pageTitle,\n },\n ],\n }\n : {}),\n },\n twitter: {\n card: 'summary_large_image',\n title: pageTitle,\n description: description ?? undefined,\n ...(overrides?.ogImage\n ? {\n images: [overrides.ogImage],\n }\n : {}),\n },\n };\n\n // Add robots meta if specified\n if (overrides?.robots) {\n metadata.robots = {\n index: overrides.robots.index ?? true,\n follow: overrides.robots.follow ?? true,\n };\n }\n\n // Add Google site verification if provided\n if (googleSiteVerification) {\n metadata.verification = {\n google: googleSiteVerification,\n };\n }\n\n return metadata;\n}\n\n/**\n * Generate metadata for preview/staging environments\n *\n * This helper adds noindex/nofollow robots tags to prevent search engines\n * from indexing preview or staging URLs.\n *\n * @example\n * ```tsx\n * export async function generateMetadata({ params }) {\n * const pageData = await loadPage({ client, siteId, path: params.slug });\n * const isPreview = process.env.VERCEL_ENV !== 'production';\n *\n * return generatePreviewMetadata({\n * page: pageData.page,\n * site: siteData.site,\n * path: params.slug,\n * siteUrl: 'https://example.com',\n * });\n * }\n * ```\n */\nexport function generatePreviewMetadata(input: PageMetadataInput): Metadata {\n return generatePageMetadata({\n ...input,\n overrides: {\n ...input.overrides,\n robots: {\n index: false,\n follow: false,\n },\n },\n });\n}\n"]}
@@ -21,14 +21,6 @@ function getNavItemsBySlug(navigation, slug) {
21
21
  }
22
22
  function transformToNavItems(menu) {
23
23
  if (!_optionalChain([menu, 'optionalAccess', _ => _.items]) || menu.items.length === 0) return [];
24
- return menu.items.filter((item) => !item.parentId && item.urlType !== "dropdown").sort((a, b) => (_nullishCoalesce(a.orderIndex, () => ( 0))) - (_nullishCoalesce(b.orderIndex, () => ( 0)))).map((item) => ({
25
- label: item.label,
26
- href: extractHref(item),
27
- isExternal: isExternalLink(item)
28
- })).filter((item) => Boolean(item.href));
29
- }
30
- function transformToNestedNavItems(menu) {
31
- if (!_optionalChain([menu, 'optionalAccess', _2 => _2.items]) || menu.items.length === 0) return [];
32
24
  const toNavLink = (item) => {
33
25
  const href = extractHref(item);
34
26
  if (!href) return null;
@@ -55,12 +47,6 @@ function transformToNestedNavItems(menu) {
55
47
  );
56
48
  return items;
57
49
  }
58
- function getNestedPrimaryNavItems(navigation) {
59
- return transformToNestedNavItems(getPrimaryNavigation(navigation));
60
- }
61
- function getNestedNavItemsBySlug(navigation, slug) {
62
- return transformToNestedNavItems(getNavigationBySlug(navigation, slug));
63
- }
64
50
  function buildMenuViewModel(navigation) {
65
51
  const menu = getPrimaryNavigation(navigation);
66
52
  if (!menu) {
@@ -93,12 +79,11 @@ function buildLogoViewModel(logo, fallbackTitle) {
93
79
  if (!logo.url && !logo.storagePath) {
94
80
  return null;
95
81
  }
96
- const alt = logo.alt && logo.alt.trim().length > 0 ? logo.alt : _nullishCoalesce(fallbackTitle, () => ( "Site logo"));
97
82
  return {
98
83
  type: "image",
99
84
  src: _nullishCoalesce(logo.url, () => ( "")),
100
85
  // Empty when using storagePath - MediaNode builds direct URL
101
- alt,
86
+ alt: resolveAltText(logo.alt, fallbackTitle),
102
87
  assetId: _nullishCoalesce(logo.assetId, () => ( void 0)),
103
88
  width: _nullishCoalesce(logo.width, () => ( void 0)),
104
89
  height: _nullishCoalesce(logo.height, () => ( void 0)),
@@ -106,6 +91,9 @@ function buildLogoViewModel(logo, fallbackTitle) {
106
91
  storageBucket: _nullishCoalesce(logo.storageBucket, () => ( void 0))
107
92
  };
108
93
  }
94
+ function resolveAltText(alt, fallback) {
95
+ return alt && alt.trim().length > 0 ? alt : _nullishCoalesce(fallback, () => ( "Site logo"));
96
+ }
109
97
  function extractHref(item) {
110
98
  const link = item.url;
111
99
  if (!link || typeof link !== "object" || !("href" in link)) return "";
@@ -125,7 +113,7 @@ function buildChildrenByParentId(items) {
125
113
  for (const item of items) {
126
114
  if (item.parentId) {
127
115
  const parent = itemsById.get(item.parentId);
128
- if (_optionalChain([parent, 'optionalAccess', _3 => _3.parentId]) && typeof process !== "undefined" && process.env.NODE_ENV !== "production") {
116
+ if (_optionalChain([parent, 'optionalAccess', _2 => _2.parentId]) && typeof process !== "undefined" && process.env.NODE_ENV !== "production") {
129
117
  console.warn(
130
118
  `[SDK Navigation] Deeply nested item detected: "${item.label}" (id: ${item.id}). Only 1 level of nesting is supported. This item will be ignored.`
131
119
  );
@@ -199,38 +187,12 @@ function convertToLinkValue(item) {
199
187
  }
200
188
  return null;
201
189
  }
202
- function buildSimpleMenu(navigation, routes) {
203
- const menu = getPrimaryNavigation(navigation);
204
- if (!menu) {
205
- return { items: [], ctaItem: null };
206
- }
207
- const flatItems = menu.items.filter((item) => !item.parentId && item.urlType !== "dropdown").sort((a, b) => (_nullishCoalesce(a.orderIndex, () => ( 0))) - (_nullishCoalesce(b.orderIndex, () => ( 0))));
208
- const items = [];
209
- let ctaItem = null;
210
- for (const item of flatItems) {
211
- const link = item.url;
212
- const href = resolveHref(link, routes);
213
- if (!href) continue;
214
- const navLink = {
215
- id: item.id,
216
- label: item.label,
217
- href,
218
- isExternal: _optionalChain([link, 'optionalAccess', _4 => _4.kind]) === "external"
219
- };
220
- if (!ctaItem && Boolean(item.isCta)) {
221
- ctaItem = navLink;
222
- continue;
223
- }
224
- items.push(navLink);
225
- }
226
- return { items, ctaItem };
227
- }
228
- function buildSimpleNestedMenu(navigation, routes) {
190
+ function buildMenu(navigation, routes) {
229
191
  const menu = getPrimaryNavigation(navigation);
230
192
  if (!menu) {
231
193
  return { items: [], ctaItem: null };
232
194
  }
233
- const toSimpleLink = (item) => {
195
+ const toNavLink = (item) => {
234
196
  const link = item.url;
235
197
  const href = resolveHref(link, routes);
236
198
  if (!href) return null;
@@ -239,13 +201,13 @@ function buildSimpleNestedMenu(navigation, routes) {
239
201
  id: item.id,
240
202
  label: item.label,
241
203
  href,
242
- isExternal: _optionalChain([link, 'optionalAccess', _5 => _5.kind]) === "external"
204
+ isExternal: _optionalChain([link, 'optionalAccess', _3 => _3.kind]) === "external"
243
205
  };
244
206
  };
245
207
  return buildNestedStructure(
246
208
  menu.items,
247
209
  {
248
- toLink: toSimpleLink,
210
+ toLink: toNavLink,
249
211
  createDropdown: (id, label, children) => ({
250
212
  kind: "dropdown",
251
213
  id,
@@ -256,6 +218,7 @@ function buildSimpleNestedMenu(navigation, routes) {
256
218
  }
257
219
  );
258
220
  }
221
+ var buildSimpleMenu = buildMenu;
259
222
  function resolveHref(link, routes) {
260
223
  if (!link) return null;
261
224
  if (link.kind === "external" || link.kind === "url") {
@@ -273,11 +236,11 @@ function resolveHref(link, routes) {
273
236
  }
274
237
  return null;
275
238
  }
276
- function buildSimpleLogo(logo, fallbackAlt) {
239
+ function buildLogo(logo, fallbackAlt) {
277
240
  if (!logo || !logo.url) {
278
241
  return null;
279
242
  }
280
- const alt = logo.alt && logo.alt.trim().length > 0 ? logo.alt : _nullishCoalesce(fallbackAlt, () => ( "Site logo"));
243
+ const alt = resolveAltText(logo.alt, fallbackAlt);
281
244
  const result = {
282
245
  type: "image",
283
246
  src: logo.url,
@@ -291,12 +254,7 @@ function buildSimpleLogo(logo, fallbackAlt) {
291
254
  }
292
255
  return result;
293
256
  }
294
- var transformNavItems = transformToNavItems;
295
- var selectPrimaryMenu = getPrimaryNavigation;
296
-
297
-
298
-
299
-
257
+ var buildSimpleLogo = buildLogo;
300
258
 
301
259
 
302
260
 
@@ -312,5 +270,5 @@ var selectPrimaryMenu = getPrimaryNavigation;
312
270
 
313
271
 
314
272
 
315
- exports.isNavLink = isNavLink; exports.isNavDropdown = isNavDropdown; exports.getPrimaryNavigation = getPrimaryNavigation; exports.getNavigationBySlug = getNavigationBySlug; exports.getPrimaryNavItems = getPrimaryNavItems; exports.getNavItemsBySlug = getNavItemsBySlug; exports.transformToNavItems = transformToNavItems; exports.transformToNestedNavItems = transformToNestedNavItems; exports.getNestedPrimaryNavItems = getNestedPrimaryNavItems; exports.getNestedNavItemsBySlug = getNestedNavItemsBySlug; exports.buildMenuViewModel = buildMenuViewModel; exports.buildLogoViewModel = buildLogoViewModel; exports.buildSimpleMenu = buildSimpleMenu; exports.buildSimpleNestedMenu = buildSimpleNestedMenu; exports.buildSimpleLogo = buildSimpleLogo; exports.transformNavItems = transformNavItems; exports.selectPrimaryMenu = selectPrimaryMenu;
316
- //# sourceMappingURL=chunk-SSS7CCRR.js.map
273
+ exports.isNavLink = isNavLink; exports.isNavDropdown = isNavDropdown; exports.getPrimaryNavigation = getPrimaryNavigation; exports.getNavigationBySlug = getNavigationBySlug; exports.getPrimaryNavItems = getPrimaryNavItems; exports.getNavItemsBySlug = getNavItemsBySlug; exports.transformToNavItems = transformToNavItems; exports.buildMenuViewModel = buildMenuViewModel; exports.buildLogoViewModel = buildLogoViewModel; exports.buildMenu = buildMenu; exports.buildSimpleMenu = buildSimpleMenu; exports.buildLogo = buildLogo; exports.buildSimpleLogo = buildSimpleLogo;
274
+ //# sourceMappingURL=chunk-EIVISR62.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["/Users/will/Projects/Business/cms/builder/packages/sdk/dist/server/chunk-EIVISR62.js","../../src/navigation/index.ts"],"names":[],"mappings":"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","file":"/Users/will/Projects/Business/cms/builder/packages/sdk/dist/server/chunk-EIVISR62.js","sourcesContent":[null,"/**\n * Navigation helper utilities for SDK sites.\n *\n * Provides helpers to transform CMS navigation data into render-ready structures.\n * All navigation functions return nested structures supporting dropdowns.\n *\n * @example Getting nav items\n * ```ts\n * import { getPrimaryNavItems, isNavLink, isNavDropdown } from '@riverbankcms/sdk/navigation';\n *\n * const headerNav = getPrimaryNavItems(siteData.navigation);\n * headerNav.forEach(item => {\n * if (isNavLink(item)) {\n * console.log(item.href);\n * } else {\n * console.log(`${item.label} has ${item.children.length} children`);\n * }\n * });\n * ```\n *\n * @example Building menu and logo\n * ```ts\n * import { buildMenu, buildLogo } from '@riverbankcms/sdk/navigation';\n *\n * const menu = buildMenu(siteData.navigation, siteData.routes);\n * const logo = buildLogo(siteData.layout.logo, siteData.site.title);\n * ```\n *\n * @packageDocumentation\n */\n\nimport type { NavigationMenuWithItems, NavigationItemRecord, LinkPayload } from '@riverbankcms/api';\nimport type { RouteMap } from '@riverbankcms/blocks';\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Types for block rendering (full LinkValue data)\n// ─────────────────────────────────────────────────────────────────────────────\n\n/**\n * Link value types matching @riverbankcms/blocks\n */\nexport type InternalLinkValue = {\n kind: 'internal';\n routeId: string;\n entityId: string;\n entityType: 'page' | 'content';\n href: string;\n title: string;\n typeLabel: string;\n contentTypeKey?: string | null;\n contentTypeName?: string | null;\n updatedAt?: string | null;\n};\n\nexport type ExternalLinkValue = {\n kind: 'external';\n href: string;\n};\n\nexport type CustomLinkValue = {\n kind: 'url';\n href: string;\n};\n\nexport type LinkValue = InternalLinkValue | ExternalLinkValue | CustomLinkValue;\n\n/**\n * Menu link view model for block rendering\n */\nexport type MenuLinkViewModel = {\n id: string;\n label: string;\n link: LinkValue | null;\n target: string | null;\n rel: string | null;\n active?: boolean;\n};\n\n/**\n * CTA link view model (extends MenuLinkViewModel with variant)\n */\nexport type MenuCtaViewModel = MenuLinkViewModel & {\n variant: string;\n};\n\n/**\n * Complete menu view model for header/footer blocks\n */\nexport type MenuViewModel = {\n items: MenuLinkViewModel[];\n ctaItem: MenuCtaViewModel | null;\n};\n\n/**\n * Logo source data from site layout\n */\nexport type LogoSource = {\n url: string | null;\n alt: string | null;\n assetId?: string | null;\n width?: number | null;\n height?: number | null;\n storagePath?: string | null;\n storageBucket?: string | null;\n} | null;\n\n/**\n * Logo view model for block rendering\n */\nexport type LogoViewModel = {\n type: 'image';\n src: string;\n alt: string;\n assetId?: string;\n width?: number | null;\n height?: number | null;\n storagePath?: string;\n storageBucket?: string;\n} | null;\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Navigation Item Types (always nested)\n// ─────────────────────────────────────────────────────────────────────────────\n\n/**\n * A navigation link item with a direct URL.\n * Use `kind` discriminator for type-safe narrowing.\n *\n * @example\n * ```ts\n * if (item.kind === 'link') {\n * console.log(item.href); // TypeScript knows href exists\n * }\n * ```\n */\nexport type NavLink = {\n /** Discriminator for type narrowing */\n kind: 'link';\n /** Unique identifier from CMS */\n id: string;\n /** Display text for the navigation link */\n label: string;\n /** The URL to navigate to */\n href: string;\n /** Whether link should open in new tab (external links) */\n isExternal: boolean;\n};\n\n/**\n * A dropdown container holding child navigation items.\n * Clicking reveals children instead of navigating.\n * Max 1 level of nesting (children are always NavLink, not NavDropdown).\n */\nexport type NavDropdown = {\n /** Discriminator for type narrowing */\n kind: 'dropdown';\n /** Unique identifier from CMS */\n id: string;\n /** Display text for the dropdown trigger */\n label: string;\n /** Child navigation links */\n children: NavLink[];\n};\n\n/**\n * Navigation item - either a link or a dropdown container.\n * Use `kind` property for type-safe discrimination.\n *\n * @example\n * ```ts\n * const items = getPrimaryNavItems(navigation);\n * items.forEach(item => {\n * if (item.kind === 'link') {\n * console.log(item.href);\n * } else {\n * console.log(item.children.length);\n * }\n * });\n * ```\n */\nexport type NavItem = NavLink | NavDropdown;\n\n/**\n * Type guard to check if a navigation item is a link.\n */\nexport function isNavLink(item: NavItem): item is NavLink {\n return item.kind === 'link';\n}\n\n/**\n * Type guard to check if a navigation item is a dropdown.\n */\nexport function isNavDropdown(item: NavItem): item is NavDropdown {\n return item.kind === 'dropdown';\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Menu and Logo types for SDK sites\n// ─────────────────────────────────────────────────────────────────────────────\n\n/**\n * Navigation menu with items and optional CTA.\n * Built from CMS navigation data with pre-resolved hrefs.\n *\n * @example\n * ```ts\n * const menu = buildMenu(siteData.navigation, siteData.routes);\n * menu.items.forEach(item => {\n * if (item.kind === 'link') {\n * console.log(item.href);\n * } else {\n * console.log(item.children);\n * }\n * });\n * ```\n */\nexport type Menu = {\n items: NavItem[];\n ctaItem: NavLink | null;\n};\n\n/**\n * Logo data for site rendering.\n *\n * @example\n * ```ts\n * const logo = buildLogo(siteData.layout.logo, siteData.site.title);\n * if (logo) {\n * <img src={logo.src} alt={logo.alt} />\n * }\n * ```\n */\nexport type Logo = {\n type: 'image';\n src: string;\n alt: string;\n width?: number;\n height?: number;\n} | null;\n\n// Legacy type aliases for backwards compatibility\n/** @deprecated Use `NavLink` instead */\nexport type SimpleNavLink = NavLink;\n/** @deprecated Use `NavDropdown` instead */\nexport type SimpleNavDropdown = NavDropdown;\n/** @deprecated Use `NavItem` instead */\nexport type SimpleNavItem = NavItem;\n/** @deprecated Use `Menu` instead */\nexport type SimpleMenuViewModel = Menu;\n/** @deprecated Use `Logo` instead */\nexport type SimpleLogo = Logo;\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Menu selection helpers\n// ─────────────────────────────────────────────────────────────────────────────\n\n/**\n * Get the primary navigation menu object.\n * Returns menu marked as isPrimary, or first menu if none marked.\n *\n * @example\n * ```ts\n * const menu = getPrimaryNavigation(siteData.navigation);\n * console.log(menu?.name); // \"main\"\n * ```\n */\nexport function getPrimaryNavigation(\n navigation: NavigationMenuWithItems[],\n): NavigationMenuWithItems | null {\n if (!navigation || navigation.length === 0) return null;\n return navigation.find((menu) => menu.isPrimary) ?? navigation[0] ?? null;\n}\n\n/**\n * Get a navigation menu by name/slug.\n *\n * @example\n * ```ts\n * const footerMenu = getNavigationBySlug(siteData.navigation, 'footer');\n * ```\n */\nexport function getNavigationBySlug(\n navigation: NavigationMenuWithItems[],\n slug: string,\n): NavigationMenuWithItems | null {\n if (!navigation || navigation.length === 0) return null;\n return navigation.find((menu) => menu.name === slug) ?? null;\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// NavItem transformations\n// ─────────────────────────────────────────────────────────────────────────────\n\n/**\n * Get nav items from the primary menu (marked isPrimary, or first menu).\n * Returns nested structure supporting both links and dropdowns.\n *\n * @example\n * ```ts\n * const headerNav = getPrimaryNavItems(siteData.navigation);\n * headerNav.forEach(item => {\n * if (item.kind === 'dropdown') {\n * console.log(`${item.label} has ${item.children.length} children`);\n * }\n * });\n * ```\n */\nexport function getPrimaryNavItems(navigation: NavigationMenuWithItems[]): NavItem[] {\n return transformToNavItems(getPrimaryNavigation(navigation));\n}\n\n/**\n * Get nav items from a specific menu by slug.\n * Returns nested structure supporting both links and dropdowns.\n *\n * @example\n * ```ts\n * const footerNav = getNavItemsBySlug(siteData.navigation, 'footer');\n * ```\n */\nexport function getNavItemsBySlug(navigation: NavigationMenuWithItems[], slug: string): NavItem[] {\n return transformToNavItems(getNavigationBySlug(navigation, slug));\n}\n\n/**\n * Transform a menu into NavItem array.\n * Builds nested structure from flat items, supporting dropdowns with children.\n *\n * @example\n * ```ts\n * const nav = transformToNavItems(menu);\n * // [\n * // { kind: 'link', href: '/', label: 'Home', ... },\n * // { kind: 'dropdown', label: 'Services', children: [...] },\n * // ]\n * ```\n */\nexport function transformToNavItems(menu: NavigationMenuWithItems | null): NavItem[] {\n if (!menu?.items || menu.items.length === 0) return [];\n\n const toNavLink = (item: NavigationItemRecord): NavLink | null => {\n const href = extractHref(item);\n if (!href) return null;\n return {\n kind: 'link',\n id: item.id,\n label: item.label,\n href,\n isExternal: isExternalLink(item),\n };\n };\n\n const { items } = buildNestedStructure<NavLink, NavDropdown, NavLink>(\n menu.items,\n {\n toLink: toNavLink,\n createDropdown: (id, label, children) => ({\n kind: 'dropdown',\n id,\n label,\n children,\n }),\n extractCta: false,\n }\n );\n\n return items;\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Full MenuViewModel for block rendering\n// ─────────────────────────────────────────────────────────────────────────────\n\n/**\n * Build a MenuViewModel from navigation data for block rendering.\n * Extracts CTA item separately and preserves full link data.\n *\n * @example\n * ```ts\n * const menuViewModel = buildMenuViewModel(siteData.navigation);\n *\n * renderBlock(siteHeaderManifest, layout.header, {\n * viewModelOverrides: { menu: menuViewModel },\n * });\n * ```\n */\nexport function buildMenuViewModel(navigation: NavigationMenuWithItems[]): MenuViewModel {\n const menu = getPrimaryNavigation(navigation);\n\n if (!menu) {\n return { items: [], ctaItem: null };\n }\n\n const flatItems = menu.items\n .filter((item) => !item.parentId && item.urlType !== 'dropdown')\n .sort((a, b) => (a.orderIndex ?? 0) - (b.orderIndex ?? 0));\n\n const items: MenuLinkViewModel[] = [];\n let ctaItem: MenuCtaViewModel | null = null;\n\n for (const item of flatItems) {\n const viewItem: MenuLinkViewModel = {\n id: item.id,\n label: item.label,\n link: convertToLinkValue(item),\n target: null,\n rel: null,\n active: false,\n };\n\n // Extract first CTA item separately\n if (!ctaItem && Boolean(item.isCta)) {\n ctaItem = { ...viewItem, variant: 'primary' };\n continue;\n }\n\n items.push(viewItem);\n }\n\n return { items, ctaItem };\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Logo view model helper\n// ─────────────────────────────────────────────────────────────────────────────\n\n/**\n * Build a LogoViewModel from site layout data.\n *\n * @param logo - Logo source from siteData.layout.logo\n * @param fallbackTitle - Site title to use as alt text fallback\n *\n * @example\n * ```ts\n * const logoViewModel = buildLogoViewModel(siteData.layout.logo, siteData.site.title);\n *\n * renderBlock(siteHeaderManifest, layout.header, {\n * viewModelOverrides: { content: { logo: logoViewModel } },\n * });\n * ```\n */\nexport function buildLogoViewModel(\n logo: LogoSource,\n fallbackTitle: string | null | undefined,\n): LogoViewModel {\n if (!logo) {\n return null;\n }\n\n // Logo must have storagePath (for direct Supabase URL) or explicit url\n if (!logo.url && !logo.storagePath) {\n return null;\n }\n\n return {\n type: 'image',\n src: logo.url ?? '', // Empty when using storagePath - MediaNode builds direct URL\n alt: resolveAltText(logo.alt, fallbackTitle),\n assetId: logo.assetId ?? undefined,\n width: logo.width ?? undefined,\n height: logo.height ?? undefined,\n storagePath: logo.storagePath ?? undefined,\n storageBucket: logo.storageBucket ?? undefined,\n };\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Internal helpers\n// ─────────────────────────────────────────────────────────────────────────────\n\n/**\n * Resolve alt text with fallback.\n * Returns the provided alt if non-empty, otherwise falls back to the provided fallback or 'Site logo'.\n * @internal\n */\nfunction resolveAltText(alt: string | null | undefined, fallback: string | null | undefined): string {\n return (alt && alt.trim().length > 0) ? alt : (fallback ?? 'Site logo');\n}\n\n/**\n * Extracts the href from a navigation item's url field.\n */\nfunction extractHref(item: NavigationItemRecord): string {\n const link = item.url as LinkPayload | null | undefined;\n if (!link || typeof link !== 'object' || !('href' in link)) return '';\n return link.href;\n}\n\n/**\n * Determines if a navigation item's link is external.\n */\nfunction isExternalLink(item: NavigationItemRecord): boolean {\n const link = item.url as LinkPayload | null | undefined;\n if (!link || typeof link !== 'object' || !('kind' in link)) return false;\n return link.kind === 'external';\n}\n\n/**\n * Build a map of children items by parent ID.\n * Also warns about deeply nested items (grandchildren) which CMS shouldn't allow.\n * @internal\n */\nfunction buildChildrenByParentId(\n items: NavigationItemRecord[]\n): Map<string, NavigationItemRecord[]> {\n const childrenByParentId = new Map<string, NavigationItemRecord[]>();\n const itemsById = new Map<string, NavigationItemRecord>();\n\n // First pass: index items by ID\n for (const item of items) {\n itemsById.set(item.id, item);\n }\n\n // Second pass: build children map and warn about deep nesting\n for (const item of items) {\n if (item.parentId) {\n // Check if parent is itself a child (deep nesting)\n const parent = itemsById.get(item.parentId);\n if (parent?.parentId && typeof process !== 'undefined' && process.env.NODE_ENV !== 'production') {\n console.warn(\n `[SDK Navigation] Deeply nested item detected: \"${item.label}\" (id: ${item.id}). ` +\n `Only 1 level of nesting is supported. This item will be ignored.`\n );\n continue; // Skip deeply nested items\n }\n\n const siblings = childrenByParentId.get(item.parentId) ?? [];\n siblings.push(item);\n childrenByParentId.set(item.parentId, siblings);\n }\n }\n\n return childrenByParentId;\n}\n\n/**\n * Generic nested structure builder.\n * @internal\n */\nfunction buildNestedStructure<TLink, TDropdown, TCta extends TLink>(\n items: NavigationItemRecord[],\n options: {\n /** Convert a navigation item record to a link type */\n toLink: (item: NavigationItemRecord) => TLink | null;\n /** Create a dropdown from id, label, and children */\n createDropdown: (id: string, label: string, children: TLink[]) => TDropdown;\n /** Extract CTA from items (return null to skip CTA extraction) */\n extractCta?: boolean;\n }\n): { items: (TLink | TDropdown)[]; ctaItem: TCta | null } {\n const childrenByParentId = buildChildrenByParentId(items);\n\n // Process root items (no parentId) sorted by orderIndex\n const rootItems = items\n .filter((item) => !item.parentId)\n .sort((a, b) => (a.orderIndex ?? 0) - (b.orderIndex ?? 0));\n\n const result: (TLink | TDropdown)[] = [];\n let ctaItem: TCta | null = null;\n\n for (const item of rootItems) {\n // Handle CTA extraction (CTAs are always links, not dropdowns)\n if (options.extractCta && !ctaItem && Boolean(item.isCta) && item.urlType !== 'dropdown') {\n const link = options.toLink(item);\n if (link) {\n ctaItem = link as TCta;\n continue;\n }\n }\n\n if (item.urlType === 'dropdown') {\n // It's a dropdown - gather its children\n const childRecords = childrenByParentId.get(item.id) ?? [];\n const children = childRecords\n .sort((a, b) => (a.orderIndex ?? 0) - (b.orderIndex ?? 0))\n .map(options.toLink)\n .filter((child): child is TLink => child !== null);\n\n // Only include dropdown if it has children\n if (children.length > 0) {\n result.push(options.createDropdown(item.id, item.label, children));\n }\n } else {\n // It's a regular link\n const link = options.toLink(item);\n if (link) {\n result.push(link);\n }\n }\n }\n\n return { items: result, ctaItem };\n}\n\n/**\n * Convert navigation item to LinkValue for block rendering.\n */\nfunction convertToLinkValue(item: NavigationItemRecord): LinkValue | null {\n const payload = item.url as Record<string, unknown> | null;\n if (!payload) return null;\n\n const kind = typeof payload.kind === 'string' ? payload.kind : null;\n\n if (kind === 'external' || kind === 'url') {\n const href = typeof payload.href === 'string' ? payload.href : null;\n return href ? { kind, href } as ExternalLinkValue | CustomLinkValue : null;\n }\n\n if (kind === 'internal') {\n const routeId = typeof payload.routeId === 'string' ? payload.routeId : null;\n const entityId = typeof payload.entityId === 'string' ? payload.entityId : null;\n const entityType = payload.entityType === 'page' || payload.entityType === 'content'\n ? payload.entityType\n : null;\n const href = typeof payload.href === 'string' ? payload.href : null;\n const title = typeof payload.title === 'string' ? payload.title : null;\n const typeLabel = typeof payload.typeLabel === 'string' ? payload.typeLabel : null;\n\n if (!routeId || !entityId || !entityType || !href || !title || !typeLabel) {\n return null;\n }\n\n return {\n kind: 'internal',\n routeId,\n entityId,\n entityType,\n href,\n title,\n typeLabel,\n contentTypeKey: typeof payload.contentTypeKey === 'string' ? payload.contentTypeKey : null,\n contentTypeName: typeof payload.contentTypeName === 'string' ? payload.contentTypeName : null,\n updatedAt: typeof payload.updatedAt === 'string' ? payload.updatedAt : null,\n };\n }\n\n return null;\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Menu and Logo builders\n// ─────────────────────────────────────────────────────────────────────────────\n\n/**\n * Build a Menu from navigation data with pre-resolved hrefs.\n * Supports dropdown containers with nested children.\n *\n * @param navigation - Navigation menus from site data\n * @param routes - Route map for resolving internal links\n *\n * @example\n * ```ts\n * const menu = buildMenu(siteData.navigation, siteData.routes);\n * menu.items.forEach(item => {\n * if (item.kind === 'dropdown') {\n * console.log(item.children);\n * }\n * });\n * ```\n */\nexport function buildMenu(\n navigation: NavigationMenuWithItems[],\n routes: RouteMap,\n): Menu {\n const menu = getPrimaryNavigation(navigation);\n\n if (!menu) {\n return { items: [], ctaItem: null };\n }\n\n const toNavLink = (item: NavigationItemRecord): NavLink | null => {\n const link = item.url as LinkPayload | null;\n const href = resolveHref(link, routes);\n if (!href) return null;\n\n return {\n kind: 'link',\n id: item.id,\n label: item.label,\n href,\n isExternal: link?.kind === 'external',\n };\n };\n\n return buildNestedStructure<NavLink, NavDropdown, NavLink>(\n menu.items,\n {\n toLink: toNavLink,\n createDropdown: (id, label, children) => ({\n kind: 'dropdown',\n id,\n label,\n children,\n }),\n extractCta: true,\n }\n );\n}\n\n/** @deprecated Use `buildMenu` instead */\nexport const buildSimpleMenu = buildMenu;\n\n/**\n * Resolve href from a link payload using route map.\n * @internal\n */\nfunction resolveHref(link: LinkPayload | null, routes: RouteMap): string | null {\n if (!link) return null;\n\n // External or custom URL links - use href directly\n if (link.kind === 'external' || link.kind === 'url') {\n return link.href || null;\n }\n\n // Internal link - resolve from route map\n if (link.kind === 'internal' && link.routeId) {\n const route = routes[link.routeId];\n if (route) {\n if (typeof route === 'string') {\n return route;\n }\n // Try path first, then href, then draftPath\n return route.path ?? route.href ?? route.draftPath ?? link.href ?? null;\n }\n // Fall back to link.href if route not found\n return link.href ?? null;\n }\n\n return null;\n}\n\n/**\n * Build a Logo from site layout data.\n *\n * @param logo - Logo data from site layout\n * @param fallbackAlt - Fallback alt text (usually site title)\n *\n * @example\n * ```ts\n * const logo = buildLogo(siteData.layout.logo, siteData.site.title);\n * // { src: 'https://...', alt: 'Site Name', width: 200, height: 50 }\n * ```\n */\nexport function buildLogo(\n logo: Partial<LogoSource> | null,\n fallbackAlt: string | null | undefined,\n): Logo {\n if (!logo || !logo.url) {\n return null;\n }\n\n const alt = resolveAltText(logo.alt, fallbackAlt);\n\n const result: NonNullable<Logo> = {\n type: 'image',\n src: logo.url,\n alt,\n };\n\n if (logo.width != null) {\n result.width = logo.width;\n }\n if (logo.height != null) {\n result.height = logo.height;\n }\n\n return result;\n}\n\n/** @deprecated Use `buildLogo` instead */\nexport const buildSimpleLogo = buildLogo;\n\n// Re-export types for convenience\nexport type { NavigationMenuWithItems, NavigationItemRecord, LinkPayload } from '@riverbankcms/api';\n"]}
@@ -45,7 +45,8 @@ async function loadContent(params) {
45
45
  resolvedData: resolvedData2,
46
46
  dataContext: { contentEntry: entry.content },
47
47
  theme: site.theme,
48
- siteId
48
+ siteId,
49
+ site: site.site
49
50
  };
50
51
  }
51
52
  const { page: pageData } = contentResponse;
@@ -70,7 +71,8 @@ async function loadContent(params) {
70
71
  page: pageOutline,
71
72
  theme: site.theme,
72
73
  siteId,
73
- resolvedData
74
+ resolvedData,
75
+ site: site.site
74
76
  };
75
77
  }
76
78
  function isEntryResponse(response) {
@@ -137,4 +139,4 @@ async function processEntryTemplate(templates, entry, context, client) {
137
139
 
138
140
 
139
141
  exports.isPageContent = isPageContent; exports.isEntryContent = isEntryContent; exports.loadContent = loadContent;
140
- //# sourceMappingURL=chunk-I2D7KOEA.js.map
142
+ //# sourceMappingURL=chunk-JWRNMNWI.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["/Users/will/Projects/Business/cms/builder/packages/sdk/dist/server/chunk-JWRNMNWI.js","../../src/rendering/helpers/loadContent.ts"],"names":["resolvedData"],"mappings":"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","file":"/Users/will/Projects/Business/cms/builder/packages/sdk/dist/server/chunk-JWRNMNWI.js","sourcesContent":[null,"/**\n * Server-side helper to fetch content (page or entry) by path.\n *\n * Use this for dynamic routing where a path could resolve to either\n * a page or a content entry.\n */\n\nimport type { Theme } from '@riverbankcms/blocks';\nimport type { RiverbankClient, PageResponse, SiteResponse } from '../../client/types';\nimport type { PageProps } from '../components/Page';\nimport { prefetchBlockData } from '../../data/prefetchBlockData';\nimport type { ResolvedBlockData } from '../../data/prefetchBlockData';\n\n/**\n * Site data included in content results for metadata generation.\n */\nexport type SiteData = SiteResponse['site'];\n\nexport type LoadContentParams = {\n client: RiverbankClient;\n siteId: string;\n path: string;\n /**\n * If true, fetches draft/unpublished content instead of published content.\n * This affects both pages and entries.\n * Requires API key with site access.\n *\n * @default false\n */\n preview?: boolean;\n};\n\n/**\n * Content entry data returned when a path resolves to an entry\n */\nexport type ContentEntryData = {\n id: string;\n /** Content type key (e.g., 'blog-post', 'product') */\n type: string | null;\n title: string;\n slug: string | null;\n path: string | null;\n status: string;\n publishAt: string | null;\n /** The raw content fields - use these to render your own UI */\n content: Record<string, unknown>;\n metaTitle: string | null;\n metaDescription: string | null;\n createdAt: string;\n updatedAt: string;\n};\n\n/**\n * Result when path resolves to a page\n */\nexport type PageContentResult = {\n type: 'page';\n /** Page outline ready for rendering with <Page> component */\n page: PageProps['page'];\n /** Site theme for styling */\n theme: Theme;\n /** Site ID */\n siteId: string;\n /** Pre-fetched block data for data loaders */\n resolvedData: ResolvedBlockData;\n /** Site data for metadata generation */\n site: SiteData;\n};\n\n/**\n * Result when path resolves to a content entry\n */\nexport type EntryContentResult = {\n type: 'entry';\n /** Raw entry data - render this however you want */\n entry: ContentEntryData;\n /** Template page for rendering the entry (if available) */\n templatePage: PageProps['page'] | null;\n /** Pre-fetched block data for template page data loaders */\n resolvedData: ResolvedBlockData;\n /** Data context for template blocks (includes entry content for bindings) */\n dataContext: { contentEntry: Record<string, unknown> };\n /** Site theme for styling (useful if rendering with SDK components) */\n theme: Theme;\n /** Site ID */\n siteId: string;\n /** Site data for metadata generation */\n site: SiteData;\n};\n\n/**\n * Discriminated union result from loadContent\n */\nexport type LoadContentResult = PageContentResult | EntryContentResult;\n\n/**\n * Type guard to check if result is a page\n */\nexport function isPageContent(result: LoadContentResult): result is PageContentResult {\n return result.type === 'page';\n}\n\n/**\n * Type guard to check if result is an entry\n */\nexport function isEntryContent(result: LoadContentResult): result is EntryContentResult {\n return result.type === 'entry';\n}\n\n/**\n * Server-side helper to fetch content by path.\n *\n * Returns a discriminated union - either page data (ready for `<Page>` component)\n * or raw entry data (for custom rendering).\n *\n * @example Dynamic routing with both pages and entries\n * ```tsx\n * import { loadContent, Page, isPageContent } from '@riverbankcms/sdk';\n *\n * export default async function DynamicRoute({ params }) {\n * const content = await loadContent({\n * client,\n * siteId: 'site-123',\n * path: `/${params.slug?.join('/') || ''}`,\n * });\n *\n * if (isPageContent(content)) {\n * return <Page {...content} />;\n * }\n *\n * // Render entry with custom UI\n * return (\n * <article>\n * <h1>{content.entry.title}</h1>\n * <div>{content.entry.content.body}</div>\n * </article>\n * );\n * }\n * ```\n *\n * @example Entry-specific rendering based on content type\n * ```tsx\n * const content = await loadContent({ client, siteId, path });\n *\n * if (content.type === 'entry') {\n * switch (content.entry.type) {\n * case 'blog-post':\n * return <BlogPost entry={content.entry} theme={content.theme} />;\n * case 'product':\n * return <ProductPage entry={content.entry} theme={content.theme} />;\n * default:\n * return <GenericEntry entry={content.entry} />;\n * }\n * }\n *\n * return <Page {...content} />;\n * ```\n *\n * @example Preview mode for draft content\n * ```tsx\n * const content = await loadContent({\n * client,\n * siteId,\n * path,\n * preview: true, // Fetches draft content for both pages and entries\n * });\n * ```\n */\nexport async function loadContent(params: LoadContentParams): Promise<LoadContentResult> {\n const { client, siteId, path, preview = false } = params;\n\n // Fetch site and content in parallel\n const [site, contentResponse] = await Promise.all([\n client.getSite({ id: siteId }),\n client.getPage({ siteId, path, preview }),\n ]);\n\n // Check if response is an entry\n if (isEntryResponse(contentResponse)) {\n const entryData = contentResponse.entry;\n\n const entry: ContentEntryData = {\n id: entryData.id,\n type: entryData.type,\n title: entryData.title,\n slug: entryData.slug,\n path: entryData.path,\n status: entryData.status,\n publishAt: entryData.publishAt,\n // Use draft content in preview mode, otherwise use published content\n content: preview\n ? (entryData.draftContent ?? entryData.content)\n : entryData.content,\n metaTitle: preview\n ? (entryData.draftMetaTitle ?? entryData.metaTitle)\n : entryData.metaTitle,\n metaDescription: preview\n ? (entryData.draftMetaDescription ?? entryData.metaDescription)\n : entryData.metaDescription,\n createdAt: entryData.createdAt,\n updatedAt: entryData.updatedAt,\n };\n\n // Process template if available (uses first template from content type)\n const { templatePage, resolvedData } = await processEntryTemplate(\n contentResponse.templates as Template[] | undefined,\n entry,\n { siteId, preview },\n client\n );\n\n return {\n type: 'entry',\n entry,\n templatePage,\n resolvedData,\n dataContext: { contentEntry: entry.content },\n theme: site.theme,\n siteId,\n site: site.site,\n };\n }\n\n // Handle page response\n const { page: pageData } = contentResponse;\n\n // Convert API response blocks to PageOutline format with validation\n const blocks = pageData.blocks.map((block) => validateAndConvertBlock(block, 'page'));\n\n const pageOutline = {\n name: pageData.name,\n path: pageData.path,\n purpose: pageData.purpose,\n blocks,\n };\n\n // Prefetch block data loaders for pages\n const resolvedData = await prefetchBlockData(\n pageOutline,\n {\n siteId,\n pageId: pageData.id,\n previewStage: preview ? 'preview' : 'published',\n },\n client\n );\n\n return {\n type: 'page',\n page: pageOutline,\n theme: site.theme,\n siteId,\n resolvedData,\n site: site.site,\n };\n}\n\n/**\n * Type guard to check if API response is an entry\n */\nfunction isEntryResponse(response: PageResponse): response is Extract<PageResponse, { type: 'entry' }> {\n return 'type' in response && response.type === 'entry';\n}\n\n/**\n * Validates and converts a raw block from API response to PageOutline block format.\n * Used for both page blocks and template blocks to ensure consistent validation.\n */\nfunction validateAndConvertBlock(\n block: unknown,\n source: 'page' | 'template'\n): { id: string | null; kind: string; purpose: string; content?: Record<string, unknown> } {\n if (!block || typeof block !== 'object') {\n throw new Error(`Invalid block format in ${source} API response`);\n }\n\n const blockRecord = block as Record<string, unknown>;\n\n // Template blocks use 'blockKind', page blocks use 'kind'\n const kindField = source === 'template' ? 'blockKind' : 'kind';\n const kindValue = blockRecord[kindField];\n\n if (typeof blockRecord.id !== 'string' && blockRecord.id !== null) {\n throw new Error(`Invalid block id in ${source}: expected string or null, got ${typeof blockRecord.id}`);\n }\n if (typeof kindValue !== 'string') {\n throw new Error(`Invalid block ${kindField} in ${source}: expected string, got ${typeof kindValue}`);\n }\n\n // Template blocks derive purpose from scope, page blocks have explicit purpose\n if (source === 'page') {\n if (typeof blockRecord.purpose !== 'string') {\n throw new Error(`Invalid block purpose in ${source}: expected string, got ${typeof blockRecord.purpose}`);\n }\n return {\n id: blockRecord.id as string | null,\n kind: kindValue,\n purpose: blockRecord.purpose,\n };\n }\n\n // Template block: derive purpose from scope, include content\n const scope = blockRecord.scope as 'entry' | 'template' | undefined;\n const content = (blockRecord.content as Record<string, unknown> | null) ?? {};\n\n return {\n id: blockRecord.id as string | null,\n kind: kindValue,\n purpose: scope === 'entry' ? 'entry-content' : 'template-layout',\n content,\n };\n}\n\n/** Template block structure from API response */\ntype TemplateBlock = {\n id: string;\n blockKind: string;\n scope: 'entry' | 'template';\n content: Record<string, unknown> | null;\n};\n\n/** Template structure from API response */\ntype Template = {\n id: string;\n name: string;\n templateKey: string;\n blocks: TemplateBlock[];\n};\n\n/**\n * Processes an entry's template into a PageOutline format and prefetches block data.\n * Returns null templatePage if no valid template with blocks is available.\n */\nasync function processEntryTemplate(\n templates: Template[] | undefined,\n entry: ContentEntryData,\n context: { siteId: string; preview: boolean },\n client: RiverbankClient\n): Promise<{ templatePage: PageProps['page'] | null; resolvedData: ResolvedBlockData }> {\n const template = templates?.[0];\n\n // Templates without blocks are treated as \"no template\" - the entry should be\n // rendered with custom UI rather than an empty template page\n if (!template || !template.blocks?.length) {\n return { templatePage: null, resolvedData: {} };\n }\n\n // Convert template blocks to PageOutline format with validation\n const blocks = template.blocks.map((block) => validateAndConvertBlock(block, 'template'));\n\n const templatePage: PageProps['page'] = {\n name: template.name || 'Entry Template',\n path: entry.path || '/',\n purpose: 'entry-template',\n blocks,\n };\n\n // Prefetch block data for template\n const resolvedData = await prefetchBlockData(\n templatePage,\n {\n siteId: context.siteId,\n pageId: template.id,\n previewStage: context.preview ? 'preview' : 'published',\n },\n client\n );\n\n return { templatePage, resolvedData };\n}\n"]}
@@ -1996,7 +1996,7 @@ var SimpleCache = class {
1996
1996
  };
1997
1997
 
1998
1998
  // src/version.ts
1999
- var SDK_VERSION = "0.6.0";
1999
+ var SDK_VERSION = "0.7.0";
2000
2000
 
2001
2001
  // src/client/error.ts
2002
2002
  var RiverbankApiError = class _RiverbankApiError extends Error {
@@ -2267,4 +2267,4 @@ function createRiverbankClient(config) {
2267
2267
 
2268
2268
 
2269
2269
  exports.API_ENDPOINTS = API_ENDPOINTS; exports.buildEndpointURL = buildEndpointURL; exports.createRiverbankClient = createRiverbankClient;
2270
- //# sourceMappingURL=chunk-EOWGKCUZ.js.map
2270
+ //# sourceMappingURL=chunk-P7UVAMK6.js.map