@riverbankcms/sdk 0.7.2 → 0.7.3

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 (187) hide show
  1. package/README.md +54 -0
  2. package/dist/client/bookings.d.mts +2 -0
  3. package/dist/client/bookings.d.ts +2 -0
  4. package/dist/client/bookings.js +2956 -104
  5. package/dist/client/bookings.js.map +1 -1
  6. package/dist/client/bookings.mjs +2929 -70
  7. package/dist/client/bookings.mjs.map +1 -1
  8. package/dist/client/client.d.mts +2 -2
  9. package/dist/client/client.d.ts +2 -2
  10. package/dist/client/client.js +80 -11
  11. package/dist/client/client.js.map +1 -1
  12. package/dist/client/client.mjs +87 -11
  13. package/dist/client/client.mjs.map +1 -1
  14. package/dist/client/hooks.d.mts +2 -2
  15. package/dist/client/hooks.d.ts +2 -2
  16. package/dist/client/rendering/client.js +3070 -259
  17. package/dist/client/rendering/client.js.map +1 -1
  18. package/dist/client/rendering/client.mjs +3212 -395
  19. package/dist/client/rendering/client.mjs.map +1 -1
  20. package/dist/client/spam-protection.d.mts +55 -0
  21. package/dist/client/spam-protection.d.ts +55 -0
  22. package/dist/client/spam-protection.js +2915 -0
  23. package/dist/client/spam-protection.js.map +1 -0
  24. package/dist/client/spam-protection.mjs +2893 -0
  25. package/dist/client/spam-protection.mjs.map +1 -0
  26. package/dist/client/{usePage-Db9kzA41.d.ts → usePage-BYmJCCm1.d.ts} +14 -2
  27. package/dist/client/{usePage-C9tJpuKa.d.mts → usePage-DZtrWajy.d.mts} +14 -2
  28. package/dist/server/{Layout-Ce7PU9I5.d.ts → Layout-Yluyb6sK.d.ts} +1 -1
  29. package/dist/server/{Layout-WllR8Zug.d.mts → Layout-qWLdVm5-.d.mts} +1 -1
  30. package/dist/server/chunk-2IZ6S225.js +122 -0
  31. package/dist/server/chunk-2IZ6S225.js.map +1 -0
  32. package/dist/server/chunk-4CV4JOE5.js +27 -0
  33. package/dist/server/chunk-4CV4JOE5.js.map +1 -0
  34. package/dist/server/{chunk-AET56TQX.mjs → chunk-5LRR64Y6.mjs} +32 -5
  35. package/dist/server/chunk-5LRR64Y6.mjs.map +1 -0
  36. package/dist/server/{chunk-VODFQMUW.js → chunk-NBTRDLCM.js} +32 -5
  37. package/dist/server/chunk-NBTRDLCM.js.map +1 -0
  38. package/dist/server/chunk-NFEGQTCC.mjs +27 -0
  39. package/dist/server/{chunk-5JT452F2.mjs → chunk-NFQLH5IA.mjs} +340 -19
  40. package/dist/server/chunk-NFQLH5IA.mjs.map +1 -0
  41. package/dist/server/chunk-PPHZV6YD.mjs +122 -0
  42. package/dist/server/chunk-PPHZV6YD.mjs.map +1 -0
  43. package/dist/server/{chunk-HMENX4Y7.js → chunk-VLXTNB2C.js} +370 -49
  44. package/dist/server/chunk-VLXTNB2C.js.map +1 -0
  45. package/dist/server/{components-RPzRQve6.d.mts → components-DNHfSCML.d.mts} +3 -3
  46. package/dist/server/{components--LT61IKp.d.ts → components-Di5ME6He.d.ts} +3 -3
  47. package/dist/server/components.d.mts +5 -5
  48. package/dist/server/components.d.ts +5 -5
  49. package/dist/server/components.js +1 -0
  50. package/dist/server/components.js.map +1 -1
  51. package/dist/server/components.mjs +1 -0
  52. package/dist/server/config-validation.js +1 -0
  53. package/dist/server/config-validation.js.map +1 -1
  54. package/dist/server/config-validation.mjs +1 -0
  55. package/dist/server/config.js +1 -0
  56. package/dist/server/config.js.map +1 -1
  57. package/dist/server/config.mjs +1 -0
  58. package/dist/server/config.mjs.map +1 -1
  59. package/dist/server/data.d.mts +2 -2
  60. package/dist/server/data.d.ts +2 -2
  61. package/dist/server/data.js +1 -0
  62. package/dist/server/data.js.map +1 -1
  63. package/dist/server/data.mjs +1 -0
  64. package/dist/server/env.d.mts +91 -5
  65. package/dist/server/env.d.ts +91 -5
  66. package/dist/server/env.js +9 -2
  67. package/dist/server/env.js.map +1 -1
  68. package/dist/server/env.mjs +8 -1
  69. package/dist/server/{index-BL66CU6d.d.mts → index--Oyunk_B.d.mts} +2 -2
  70. package/dist/server/{index-CJk9iQQW.d.ts → index-C9Ra8dza.d.ts} +2 -2
  71. package/dist/server/{index-Bkva0WAj.d.mts → index-Clm3skz_.d.mts} +1 -1
  72. package/dist/server/{index-CSBWKA3r.d.ts → index-DLvNddi-.d.ts} +1 -1
  73. package/dist/server/index.d.mts +215 -4
  74. package/dist/server/index.d.ts +215 -4
  75. package/dist/server/index.js +301 -3
  76. package/dist/server/index.js.map +1 -1
  77. package/dist/server/index.mjs +301 -3
  78. package/dist/server/index.mjs.map +1 -1
  79. package/dist/server/{loadContent-CXUWMuzY.d.ts → loadContent-D7LQwI0o.d.ts} +3 -3
  80. package/dist/server/{loadContent-F_tAS0Nl.d.mts → loadContent-DVfuBLiZ.d.mts} +3 -3
  81. package/dist/server/{loadPage-6I7F6GRF.js → loadPage-AXNAERDS.js} +2 -1
  82. package/dist/server/loadPage-AXNAERDS.js.map +1 -0
  83. package/dist/server/{loadPage-i2r-X5b9.d.ts → loadPage-BmYJCe_V.d.ts} +2 -2
  84. package/dist/server/{loadPage-CxlYLe5K.d.mts → loadPage-BucnLHmE.d.mts} +2 -2
  85. package/dist/server/{loadPage-JI2SML4M.mjs → loadPage-XR7ORQ2E.mjs} +2 -1
  86. package/dist/server/loadPage-XR7ORQ2E.mjs.map +1 -0
  87. package/dist/server/metadata.d.mts +4 -4
  88. package/dist/server/metadata.d.ts +4 -4
  89. package/dist/server/metadata.js +1 -0
  90. package/dist/server/metadata.js.map +1 -1
  91. package/dist/server/metadata.mjs +1 -0
  92. package/dist/server/navigation.d.mts +2 -2
  93. package/dist/server/navigation.d.ts +2 -2
  94. package/dist/server/navigation.js +1 -0
  95. package/dist/server/navigation.js.map +1 -1
  96. package/dist/server/navigation.mjs +1 -0
  97. package/dist/server/next/revalidate.js +5 -4
  98. package/dist/server/next/revalidate.js.map +1 -1
  99. package/dist/server/next/revalidate.mjs +3 -2
  100. package/dist/server/next/revalidate.mjs.map +1 -1
  101. package/dist/server/next/tags.d.mts +3 -0
  102. package/dist/server/next/tags.d.ts +3 -0
  103. package/dist/server/next/tags.js +3 -1
  104. package/dist/server/next/tags.js.map +1 -1
  105. package/dist/server/next/tags.mjs +2 -0
  106. package/dist/server/next/tags.mjs.map +1 -1
  107. package/dist/server/next.d.mts +5 -5
  108. package/dist/server/next.d.ts +5 -5
  109. package/dist/server/next.js +5 -4
  110. package/dist/server/next.js.map +1 -1
  111. package/dist/server/next.mjs +3 -2
  112. package/dist/server/next.mjs.map +1 -1
  113. package/dist/server/rendering/server.d.mts +4 -4
  114. package/dist/server/rendering/server.d.ts +4 -4
  115. package/dist/server/rendering/server.js +1 -0
  116. package/dist/server/rendering/server.js.map +1 -1
  117. package/dist/server/rendering/server.mjs +1 -0
  118. package/dist/server/rendering.d.mts +7 -7
  119. package/dist/server/rendering.d.ts +7 -7
  120. package/dist/server/rendering.js +1 -0
  121. package/dist/server/rendering.js.map +1 -1
  122. package/dist/server/rendering.mjs +1 -0
  123. package/dist/server/routing.d.mts +3 -3
  124. package/dist/server/routing.d.ts +3 -3
  125. package/dist/server/routing.js +4 -2
  126. package/dist/server/routing.js.map +1 -1
  127. package/dist/server/routing.mjs +3 -1
  128. package/dist/server/routing.mjs.map +1 -1
  129. package/dist/server/server.d.mts +5 -5
  130. package/dist/server/server.d.ts +5 -5
  131. package/dist/server/server.js +3 -2
  132. package/dist/server/server.js.map +1 -1
  133. package/dist/server/server.mjs +2 -1
  134. package/dist/server/theme-bridge.js +1 -0
  135. package/dist/server/theme-bridge.js.map +1 -1
  136. package/dist/server/theme-bridge.mjs +1 -0
  137. package/dist/server/theme-bridge.mjs.map +1 -1
  138. package/dist/server/theme.js +3 -1
  139. package/dist/server/theme.js.map +1 -1
  140. package/dist/server/theme.mjs +2 -0
  141. package/dist/server/theme.mjs.map +1 -1
  142. package/dist/server/{types-DnkRh0UL.d.ts → types-BRQyLrQU.d.ts} +14 -2
  143. package/dist/server/{types-Dsu9wsUh.d.mts → types-BSV6Vc-P.d.mts} +2 -2
  144. package/dist/server/{types-MF2AWoKv.d.mts → types-C-LShyIg.d.mts} +14 -2
  145. package/dist/server/{types-CVykEqXN.d.ts → types-Dt98DeYa.d.ts} +2 -2
  146. package/dist/server/webhooks.d.mts +8 -2
  147. package/dist/server/webhooks.d.ts +8 -2
  148. package/dist/server/webhooks.js +3 -2
  149. package/dist/server/webhooks.js.map +1 -1
  150. package/dist/server/webhooks.mjs +2 -1
  151. package/package.json +7 -1
  152. package/dist/client/resolver-BhueZVxZ.d.mts +0 -61
  153. package/dist/client/resolver-BhueZVxZ.d.ts +0 -61
  154. package/dist/client/usePage--fGlyrgj.d.mts +0 -6439
  155. package/dist/client/usePage-BBcFCxOU.d.ts +0 -6297
  156. package/dist/client/usePage-BC8Q2E3t.d.mts +0 -6431
  157. package/dist/client/usePage-BTPnCuWC.d.mts +0 -6511
  158. package/dist/client/usePage-BXjk8BhD.d.mts +0 -6704
  159. package/dist/client/usePage-BafOS9UT.d.mts +0 -6512
  160. package/dist/client/usePage-BcjWPXvh.d.mts +0 -6388
  161. package/dist/client/usePage-BiOReg0_.d.ts +0 -6704
  162. package/dist/client/usePage-Bnx-kA6x.d.mts +0 -6670
  163. package/dist/client/usePage-BvKAa3Zw.d.mts +0 -366
  164. package/dist/client/usePage-BvKAa3Zw.d.ts +0 -366
  165. package/dist/client/usePage-BydHcMYB.d.mts +0 -6297
  166. package/dist/client/usePage-C3ZKNwY7.d.mts +0 -6393
  167. package/dist/client/usePage-CE7X5NcN.d.ts +0 -6439
  168. package/dist/client/usePage-CHEybPMD.d.ts +0 -6429
  169. package/dist/client/usePage-CrKw1H6Y.d.ts +0 -6338
  170. package/dist/client/usePage-CyYpOJud.d.ts +0 -6388
  171. package/dist/client/usePage-D4fxZbRR.d.mts +0 -6429
  172. package/dist/client/usePage-DMI8ImsU.d.mts +0 -6338
  173. package/dist/client/usePage-DoPI6b8V.d.ts +0 -6511
  174. package/dist/client/usePage-DpRNZUtP.d.ts +0 -6431
  175. package/dist/client/usePage-QNWArrVO.d.ts +0 -6670
  176. package/dist/client/usePage-fBgPB6Oq.d.ts +0 -6512
  177. package/dist/client/usePage-gpVaeWDy.d.ts +0 -6393
  178. package/dist/server/chunk-5JT452F2.mjs.map +0 -1
  179. package/dist/server/chunk-AET56TQX.mjs.map +0 -1
  180. package/dist/server/chunk-HMENX4Y7.js.map +0 -1
  181. package/dist/server/chunk-LQUKXIW7.mjs +0 -13
  182. package/dist/server/chunk-LQUKXIW7.mjs.map +0 -1
  183. package/dist/server/chunk-VODFQMUW.js.map +0 -1
  184. package/dist/server/chunk-WYNEYDXO.js +0 -13
  185. package/dist/server/chunk-WYNEYDXO.js.map +0 -1
  186. package/dist/server/loadPage-6I7F6GRF.js.map +0 -1
  187. /package/dist/server/{loadPage-JI2SML4M.mjs.map → chunk-NFEGQTCC.mjs.map} +0 -0
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/next/catch-all.tsx","../../src/rendering/hooks/usePage.ts","../../src/rendering/hooks/useContent.ts","../../src/next/static-params.ts","../../src/next/index.ts"],"sourcesContent":["/**\n * Next.js catch-all page factory for Riverbank CMS.\n *\n * Provides a simple, opinionated way to render CMS content in Next.js catch-all routes.\n * Reduces typical page.tsx boilerplate from ~160 lines to ~10 lines.\n *\n * @example\n * ```tsx\n * // src/app/[[...slug]]/page.tsx\n * import { createCatchAllPage } from '@riverbankcms/sdk/next';\n * import { getRiverbankClient } from '@/lib/builder-client';\n * import config from '@/riverbank.config';\n *\n * const { Page, generateMetadata } = createCatchAllPage({\n * getClient: getRiverbankClient,\n * config,\n * blockOverrides: { hero: MyCustomHero },\n * });\n *\n * export default Page;\n * export { generateMetadata };\n * ```\n */\n\n// Note: next is a peerDependency - this module only works in Next.js projects.\nimport { notFound } from 'next/navigation';\n\nimport {\n loadContent,\n isPageContent,\n isEntryContent,\n} from '../rendering/helpers/loadContent';\nimport type { LoadContentResult } from '../rendering/helpers/loadContent';\nimport { Page as PageComponent } from '../rendering/components/Page';\nimport {\n generatePageMetadata,\n generatePreviewMetadata,\n type Metadata,\n} from '../metadata/generatePageMetadata';\nimport type {\n CreateCatchAllPageOptions,\n CatchAllPageProps,\n CreateCatchAllPageResult,\n} from './types';\n\n/**\n * Detect preview mode from environment variable.\n *\n * Sites can enable preview mode by setting:\n * ```\n * RIVERBANK_PREVIEW_MODE=true\n * ```\n *\n * This is typically set in preview/staging environments to fetch\n * draft content instead of published content.\n */\nfunction isPreviewMode(): boolean {\n return process.env.RIVERBANK_PREVIEW_MODE === 'true';\n}\n\n/**\n * Convert URL slug segments to a path string.\n *\n * @example\n * ```\n * slugToPath(undefined) // '/'\n * slugToPath([]) // '/'\n * slugToPath(['about']) // '/about'\n * slugToPath(['blog', 'post']) // '/blog/post'\n * ```\n */\nfunction slugToPath(slug?: string[]): string {\n if (!slug || slug.length === 0) return '/';\n return '/' + slug.join('/');\n}\n\n/**\n * Factory function to create a Next.js catch-all page component and metadata generator.\n *\n * This provides a simple, opinionated setup for rendering Riverbank CMS content\n * in external SDK sites. It handles:\n *\n * - Page and content entry routing\n * - Preview mode detection (via `RIVERBANK_PREVIEW_MODE` env var)\n * - SEO metadata generation\n * - Block overrides for custom components\n * - 404 handling for missing content\n *\n * ## Escape Hatches\n *\n * For customization beyond the defaults, use these options:\n *\n * - `beforeRender`: Intercept before rendering (maintenance mode, access control)\n * - `customMetadata`: Full control over SEO metadata\n * - `wrapper`: Wrap all content (analytics, error boundaries)\n *\n * For maximum control, use the lower-level helpers directly:\n * - `loadContent()` from `@riverbankcms/sdk/rendering`\n * - `Page` component from `@riverbankcms/sdk/rendering`\n * - `generatePageMetadata()` from `@riverbankcms/sdk/metadata`\n *\n * @param options - Configuration options\n * @returns Object with `Page` component and `generateMetadata` function\n *\n * @example Basic usage\n * ```tsx\n * const { Page, generateMetadata } = createCatchAllPage({\n * getClient: getRiverbankClient,\n * config,\n * });\n *\n * export default Page;\n * export { generateMetadata };\n * ```\n *\n * @example With maintenance mode\n * ```tsx\n * const { Page, generateMetadata } = createCatchAllPage({\n * getClient,\n * config,\n * beforeRender: async () => {\n * if (process.env.MAINTENANCE_MODE === 'true') {\n * return <MaintenancePage />;\n * }\n * return null;\n * },\n * });\n * ```\n */\nexport function createCatchAllPage(\n options: CreateCatchAllPageOptions\n): CreateCatchAllPageResult {\n const {\n getClient,\n config,\n blockOverrides,\n siteUrl,\n beforeRender,\n customMetadata,\n wrapper: Wrapper,\n } = options;\n\n /**\n * Main page component that loads and renders CMS content.\n */\n async function Page({ params, searchParams: searchParamsPromise }: CatchAllPageProps): Promise<React.ReactNode> {\n const [{ slug }, searchParams] = await Promise.all([\n params,\n searchParamsPromise ?? Promise.resolve({}),\n ]);\n const path = slugToPath(slug);\n const preview = isPreviewMode();\n const client = getClient();\n\n // Load content (page or entry)\n let content: LoadContentResult;\n try {\n content = await loadContent({\n client,\n siteId: config.siteId,\n path,\n preview,\n });\n } catch (error) {\n // Log the error for debugging - could be a network error, auth failure, etc.\n // We treat all errors as \"not found\" since the page can't be rendered\n console.debug('[createCatchAllPage] Failed to load content for path:', path, error);\n return notFound();\n }\n\n // ESCAPE HATCH: beforeRender for maintenance mode, access control, etc.\n if (beforeRender) {\n const intercepted = await beforeRender({ content, path, preview, searchParams });\n if (intercepted !== null) {\n return Wrapper ? <Wrapper>{intercepted}</Wrapper> : intercepted;\n }\n }\n\n let rendered: React.ReactNode;\n\n // Render page content\n if (isPageContent(content)) {\n rendered = (\n <PageComponent\n page={content.page}\n theme={content.theme}\n siteId={content.siteId}\n resolvedData={content.resolvedData}\n blockOverrides={blockOverrides}\n />\n );\n }\n // Render entry content with template\n else if (isEntryContent(content)) {\n // Entries without template pages should 404\n if (!content.templatePage) {\n return notFound();\n }\n\n rendered = (\n <PageComponent\n page={content.templatePage}\n theme={content.theme}\n siteId={content.siteId}\n resolvedData={content.resolvedData}\n blockOverrides={blockOverrides}\n dataContext={{\n contentEntry: content.dataContext.contentEntry,\n }}\n />\n );\n }\n // Unexpected content type - should never happen\n else {\n return notFound();\n }\n\n return Wrapper ? <Wrapper>{rendered}</Wrapper> : rendered;\n }\n\n /**\n * Generate SEO metadata for the page.\n */\n async function generateMetadataFn({ params, searchParams: searchParamsPromise }: CatchAllPageProps): Promise<Metadata> {\n const [{ slug }, searchParams] = await Promise.all([\n params,\n searchParamsPromise ?? Promise.resolve({}),\n ]);\n const path = slugToPath(slug);\n const preview = isPreviewMode();\n\n try {\n const content = await loadContent({\n client: getClient(),\n siteId: config.siteId,\n path,\n preview,\n });\n\n // ESCAPE HATCH: Custom metadata generation\n if (customMetadata) {\n return customMetadata({ content, path, preview, searchParams });\n }\n\n // Resolve site URL (use config URLs as fallback)\n const resolvedUrl =\n siteUrl ?? (preview ? config.previewUrl : config.liveUrl) ?? '';\n\n // Warn if no site URL is configured - OG/Twitter tags require absolute URLs\n if (!resolvedUrl) {\n console.warn(\n '[createCatchAllPage] No siteUrl configured. OpenGraph and Twitter tags will have relative URLs which may cause social sharing previews to fail. ' +\n 'Set siteUrl option, or config.liveUrl/config.previewUrl in your riverbank.config.'\n );\n }\n\n // Build page object for metadata\n // For entries, we use metaTitle/metaDescription; purpose is used as description fallback\n const page = isPageContent(content)\n ? content.page\n : isEntryContent(content)\n ? {\n name: content.entry.metaTitle ?? content.entry.title,\n purpose: content.entry.metaDescription ?? undefined,\n }\n : { name: 'Page' };\n\n // Use preview metadata (noindex) in preview mode\n const metadataGenerator = preview\n ? generatePreviewMetadata\n : generatePageMetadata;\n\n // Use site data from loadContent result (no duplicate API call needed)\n return metadataGenerator({\n page,\n site: content.site,\n path,\n siteUrl: resolvedUrl,\n });\n } catch (error) {\n // Log the error for debugging purposes\n console.debug('[createCatchAllPage] Failed to generate metadata for path:', path, error);\n // Return minimal metadata on error - page will handle the actual 404\n return {\n title: 'Page Not Found',\n robots: { index: false, follow: false },\n };\n }\n }\n\n return {\n Page,\n generateMetadata: generateMetadataFn,\n };\n}\n","/**\n * Client-side React hook to fetch page data.\n *\n * Use this in client components for dynamic page loading.\n */\n\nimport { useState, useEffect } from 'react';\nimport type { RiverbankClient } from '../../client/types';\nimport type { PageProps } from '../components/Page';\nimport type { RuntimeSdkConfig } from '../helpers/loadPage';\nimport { prefetchBlockData } from '../../data/prefetchBlockData';\n\nexport type UsePageParams = {\n client: RiverbankClient;\n siteId: string;\n path: string;\n pageId?: string;\n /**\n * If true, fetches draft/unpublished content instead of published content.\n * This affects both the page structure and block data loaders.\n * Requires API key with site access.\n *\n * @default false\n */\n preview?: boolean;\n};\n\nexport type UsePageResult =\n | { loading: true; error: null; page: null; theme: null; siteId: string; resolvedData: null; sdkConfig: null }\n | { loading: false; error: Error; page: null; theme: null; siteId: string; resolvedData: null; sdkConfig: null }\n | { loading: false; error: null; sdkConfig: RuntimeSdkConfig | null } & Omit<PageProps, 'registry' | 'wrapBlock' | 'usePlaceholders' | 'sdkConfig'>;\n\n/**\n * Client-side React hook to fetch all data needed for <Page> component.\n *\n * Fetches site data, page data, and prefetches block data loaders.\n * Returns loading and error states for proper UI handling.\n *\n * IMPORTANT: The client object should be stable across renders to avoid\n * unnecessary re-fetches. Create it outside your component or use useMemo:\n *\n * ```tsx\n * // ✅ Good - stable reference\n * const client = useMemo(\n * () => createRiverbankClient({ apiKey, baseUrl }),\n * [apiKey, baseUrl]\n * );\n *\n * // ❌ Bad - new client on every render (causes infinite loops)\n * const client = createRiverbankClient({ apiKey, baseUrl });\n * ```\n *\n * @example Basic usage\n * ```tsx\n * import { createRiverbankClient } from '@riverbankcms/sdk';\n * import { usePage, Page } from '@riverbankcms/sdk/rendering';\n *\n * const client = createRiverbankClient({\n * apiKey: process.env.NEXT_PUBLIC_RIVERBANK_API_KEY!,\n * baseUrl: process.env.NEXT_PUBLIC_DASHBOARD_URL + '/api',\n * });\n *\n * function MyPage({ path }: { path: string }) {\n * const pageData = usePage({ client, siteId: 'site-123', path });\n *\n * if (pageData.loading) {\n * return <div>Loading...</div>;\n * }\n *\n * if (pageData.error) {\n * return <div>Error: {pageData.error.message}</div>;\n * }\n *\n * return <Page {...pageData} />;\n * }\n * ```\n *\n * @example With custom loading/error states\n * ```tsx\n * function MyPage({ path }: { path: string }) {\n * const pageData = usePage({ client, siteId: 'site-123', path });\n *\n * if (pageData.loading) {\n * return <Skeleton />;\n * }\n *\n * if (pageData.error) {\n * return (\n * <ErrorBoundary\n * error={pageData.error}\n * onRetry={() => window.location.reload()}\n * />\n * );\n * }\n *\n * return <Page {...pageData} />;\n * }\n * ```\n */\nexport function usePage(params: UsePageParams): UsePageResult {\n const { client, siteId, path, pageId, preview = false } = params;\n\n const [result, setResult] = useState<UsePageResult>({\n loading: true,\n error: null,\n page: null,\n theme: null,\n siteId,\n resolvedData: null,\n sdkConfig: null,\n });\n\n useEffect(() => {\n let cancelled = false;\n\n async function fetchPage() {\n try {\n // Fetch site and page data in parallel\n const [site, pageResponse] = await Promise.all([\n client.getSite({ id: siteId }),\n client.getPage({ siteId, path, preview }),\n ]);\n\n // If component unmounted, don't update state\n if (cancelled) return;\n\n // Extract page data (getContentByPath can return page or entry)\n if ('entry' in pageResponse) {\n throw new Error(\n 'This path resolves to a content entry, not a page. ' +\n 'Use useContent() instead, which handles both pages and entries. ' +\n 'For entries, useContent() returns the raw entry data for custom rendering.'\n );\n }\n\n const { page: pageData } = pageResponse;\n\n // Convert API response blocks to PageOutline format\n // API returns blocks with full content, but PageOutline only needs structure\n const blocks = pageData.blocks.map((block) => {\n if (!block || typeof block !== 'object') {\n throw new Error('Invalid block format in API response');\n }\n if (typeof block.id !== 'string' && block.id !== null) {\n throw new Error(`Invalid block id: expected string or null, got ${typeof block.id}`);\n }\n if (typeof block.kind !== 'string') {\n throw new Error(`Invalid block kind: expected string, got ${typeof block.kind}`);\n }\n if (typeof block.purpose !== 'string') {\n throw new Error(`Invalid block purpose: expected string, got ${typeof block.purpose}`);\n }\n return {\n id: block.id,\n kind: block.kind,\n purpose: block.purpose,\n };\n });\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\n const resolvedData = await prefetchBlockData(\n pageOutline,\n {\n siteId,\n pageId: pageId ?? pageData.id,\n previewStage: preview ? 'preview' : 'published',\n },\n client\n );\n\n // If component unmounted, don't update state\n if (cancelled) return;\n\n setResult({\n loading: false,\n error: null,\n page: pageOutline,\n theme: site.theme,\n siteId,\n resolvedData,\n sdkConfig: site.sdkConfig ?? null,\n });\n } catch (error) {\n if (cancelled) return;\n\n setResult({\n loading: false,\n error: error instanceof Error ? error : new Error(String(error)),\n page: null,\n theme: null,\n siteId,\n resolvedData: null,\n sdkConfig: null,\n });\n }\n }\n\n fetchPage();\n\n return () => {\n cancelled = true;\n };\n }, [client, siteId, path, pageId, preview]);\n\n return result;\n}\n","/**\n * Client-side React hook to fetch content (page or entry) by path.\n *\n * Use this in client components for dynamic routing where a path\n * could resolve to either a page or content entry.\n */\n\nimport { useState, useEffect } from 'react';\nimport type { Theme } from '@riverbankcms/blocks';\nimport type { RiverbankClient, PageResponse } from '../../client/types';\nimport type { PageProps } from '../components/Page';\nimport { prefetchBlockData } from '../../data/prefetchBlockData';\nimport type { ResolvedBlockData } from '../../data/prefetchBlockData';\nimport type { ContentEntryData } from '../helpers/loadContent';\n\nexport type UseContentParams = {\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 * Loading state\n */\ntype LoadingState = {\n loading: true;\n error: null;\n type: null;\n page: null;\n entry: null;\n theme: null;\n siteId: string;\n resolvedData: null;\n};\n\n/**\n * Error state\n */\ntype ErrorState = {\n loading: false;\n error: Error;\n type: null;\n page: null;\n entry: null;\n theme: null;\n siteId: string;\n resolvedData: null;\n};\n\n/**\n * Success state for page content\n */\ntype PageSuccessState = {\n loading: false;\n error: null;\n type: 'page';\n page: PageProps['page'];\n entry: null;\n theme: Theme;\n siteId: string;\n resolvedData: ResolvedBlockData;\n};\n\n/**\n * Success state for entry content\n */\ntype EntrySuccessState = {\n loading: false;\n error: null;\n type: 'entry';\n page: null;\n entry: ContentEntryData;\n theme: Theme;\n siteId: string;\n resolvedData: null;\n};\n\nexport type UseContentResult = LoadingState | ErrorState | PageSuccessState | EntrySuccessState;\n\n/**\n * Type guard to check if result is loading\n */\nexport function isContentLoading(result: UseContentResult): result is LoadingState {\n return result.loading === true;\n}\n\n/**\n * Type guard to check if result has an error\n */\nexport function isContentError(result: UseContentResult): result is ErrorState {\n return result.loading === false && result.error !== null;\n}\n\n/**\n * Type guard to check if result is a page\n */\nexport function isPageContentResult(result: UseContentResult): result is PageSuccessState {\n return result.loading === false && result.error === null && result.type === 'page';\n}\n\n/**\n * Type guard to check if result is an entry\n */\nexport function isEntryContentResult(result: UseContentResult): result is EntrySuccessState {\n return result.loading === false && result.error === null && result.type === 'entry';\n}\n\n/**\n * Client-side React hook to fetch content by path.\n *\n * Returns a discriminated union with loading/error states, and either\n * page data (ready for `<Page>` component) or raw entry data (for custom rendering).\n *\n * IMPORTANT: The client object should be stable across renders to avoid\n * unnecessary re-fetches. Create it outside your component or use useMemo.\n *\n * @example Dynamic routing with both pages and entries\n * ```tsx\n * \"use client\";\n *\n * import { useContent, Page, isPageContentResult } from '@riverbankcms/sdk/client';\n *\n * function DynamicPage({ path }: { path: string }) {\n * const content = useContent({ client, siteId: 'site-123', path });\n *\n * if (content.loading) return <div>Loading...</div>;\n * if (content.error) return <div>Error: {content.error.message}</div>;\n *\n * if (isPageContentResult(content)) {\n * return <Page page={content.page} theme={content.theme} siteId={content.siteId} resolvedData={content.resolvedData} />;\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 = useContent({ client, siteId, path });\n *\n * if (content.loading) return <Spinner />;\n * if (content.error) return <Error error={content.error} />;\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 * }\n * }\n *\n * return <Page {...content} />;\n * ```\n */\nexport function useContent(params: UseContentParams): UseContentResult {\n const { client, siteId, path, preview = false } = params;\n\n const [result, setResult] = useState<UseContentResult>({\n loading: true,\n error: null,\n type: null,\n page: null,\n entry: null,\n theme: null,\n siteId,\n resolvedData: null,\n });\n\n useEffect(() => {\n let cancelled = false;\n\n async function fetchContent() {\n try {\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 // If component unmounted, don't update state\n if (cancelled) return;\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 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 setResult({\n loading: false,\n error: null,\n type: 'entry',\n page: null,\n entry,\n theme: site.theme,\n siteId,\n resolvedData: null,\n });\n return;\n }\n\n // Handle page response\n const { page: pageData } = contentResponse;\n\n // Convert API response blocks to PageOutline format\n const blocks = pageData.blocks.map((block) => {\n if (!block || typeof block !== 'object') {\n throw new Error('Invalid block format in API response');\n }\n if (typeof block.id !== 'string' && block.id !== null) {\n throw new Error(`Invalid block id: expected string or null, got ${typeof block.id}`);\n }\n if (typeof block.kind !== 'string') {\n throw new Error(`Invalid block kind: expected string, got ${typeof block.kind}`);\n }\n if (typeof block.purpose !== 'string') {\n throw new Error(`Invalid block purpose: expected string, got ${typeof block.purpose}`);\n }\n return {\n id: block.id,\n kind: block.kind,\n purpose: block.purpose,\n };\n });\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 // If component unmounted, don't update state\n if (cancelled) return;\n\n setResult({\n loading: false,\n error: null,\n type: 'page',\n page: pageOutline,\n entry: null,\n theme: site.theme,\n siteId,\n resolvedData,\n });\n } catch (error) {\n if (cancelled) return;\n\n setResult({\n loading: false,\n error: error instanceof Error ? error : new Error(String(error)),\n type: null,\n page: null,\n entry: null,\n theme: null,\n siteId,\n resolvedData: null,\n });\n }\n }\n\n fetchContent();\n\n return () => {\n cancelled = true;\n };\n }, [client, siteId, path, preview]);\n\n return result;\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 * Static params generation for Next.js SSG\n *\n * Provides helpers for generating static params from published CMS routes.\n *\n * @example\n * ```tsx\n * // app/[[...slug]]/page.tsx\n * import { generateAllStaticParams } from '@riverbankcms/sdk/next';\n *\n * export { generateAllStaticParams as generateStaticParams };\n * ```\n */\n\nimport { createRiverbankClient } from '../client';\n\n/**\n * Environment variable validation result\n */\nexport type StaticParamsEnvResult =\n | { valid: true; config: { apiKey: string; siteId: string; baseUrl: string } }\n | { valid: false; missingVars: string[] };\n\n/**\n * Validate that all required environment variables are set.\n *\n * Required env vars:\n * - RIVERBANK_API_KEY: API key for published content\n * - RIVERBANK_SITE_ID: Site ID\n * - NEXT_PUBLIC_DASHBOARD_URL: Dashboard API URL\n *\n * @returns Validation result with config or missing vars\n */\nexport function validateStaticParamsEnv(): StaticParamsEnvResult {\n const apiKey = process.env.RIVERBANK_API_KEY;\n const siteId = process.env.RIVERBANK_SITE_ID;\n const baseUrl = process.env.NEXT_PUBLIC_DASHBOARD_URL;\n\n const missingVars: string[] = [];\n\n if (!apiKey) missingVars.push('RIVERBANK_API_KEY');\n if (!siteId) missingVars.push('RIVERBANK_SITE_ID');\n if (!baseUrl) missingVars.push('NEXT_PUBLIC_DASHBOARD_URL');\n\n if (missingVars.length > 0) {\n return { valid: false, missingVars };\n }\n\n // TypeScript can't narrow through the checks above, so assert non-null\n return {\n valid: true,\n config: { apiKey: apiKey!, siteId: siteId!, baseUrl: baseUrl! },\n };\n}\n\n/**\n * Convert a route path to a Next.js slug array.\n *\n * @param path - The route path (e.g., '/about', '/blog/post')\n * @returns The slug array for Next.js catch-all route\n *\n * @example\n * ```ts\n * pathToSlugArray('/') // []\n * pathToSlugArray('/about') // ['about']\n * pathToSlugArray('/blog/post') // ['blog', 'post']\n * ```\n */\nexport function pathToSlugArray(path: string): string[] {\n if (path === '/') return [];\n return path.slice(1).split('/');\n}\n\n/**\n * Generate static params for all published routes.\n *\n * This function fetches all published routes from the CMS and converts them\n * to the static params format expected by Next.js catch-all routes.\n *\n * Requires environment variables:\n * - RIVERBANK_API_KEY: API key for published content\n * - RIVERBANK_SITE_ID: Site ID\n * - NEXT_PUBLIC_DASHBOARD_URL: Dashboard API URL\n *\n * @throws Error if required env vars are missing (prevents silent empty SSG in CI)\n * @returns Array of static params objects for Next.js\n *\n * @example\n * ```tsx\n * // app/[[...slug]]/page.tsx\n * import { generateAllStaticParams } from '@riverbankcms/sdk/next';\n *\n * export { generateAllStaticParams as generateStaticParams };\n *\n * // Or with custom logic:\n * export async function generateStaticParams() {\n * const params = await generateAllStaticParams();\n * // Filter or modify params as needed\n * return params.filter(p => !p.slug.includes('private'));\n * }\n * ```\n */\nexport async function generateAllStaticParams(): Promise<{ slug: string[] }[]> {\n const envResult = validateStaticParamsEnv();\n\n if (!envResult.valid) {\n throw new Error(\n `[Riverbank] generateAllStaticParams requires env vars: ${envResult.missingVars.join(', ')}. ` +\n `This error prevents accidentally deploying with zero static pages.`\n );\n }\n\n const { apiKey, siteId: _siteId, baseUrl } = envResult.config;\n\n // Create client (unused until getAllPublishedRoutes is implemented)\n const _client = createRiverbankClient({\n apiKey,\n baseUrl,\n // Disable caching for build-time fetches\n cache: { enabled: false },\n // Disable resilience for build-time fetches (want fast failure)\n resilience: { enabled: false },\n });\n\n // TODO: This requires adding getAllPublishedRoutes to the client\n // which requires the CMS endpoint to be updated to accept API key auth\n // and support the publishedOnly query param.\n //\n // For now, we'll use getPage to fetch the site's homepage which contains\n // the navigation data, and extract routes from there.\n //\n // In the future, implement:\n // const routes = await client.getAllPublishedRoutes({ siteId });\n // return routes.map(path => ({ slug: pathToSlugArray(path) }));\n\n // Temporary: Return empty array with warning\n // This allows the helper to be used but won't pre-render any pages\n console.warn(\n '[Riverbank] generateAllStaticParams: getAllPublishedRoutes API not yet implemented. ' +\n 'Pages will be generated on-demand with ISR.'\n );\n\n // Return at least the homepage\n return [{ slug: [] }];\n}\n","/**\n * Next.js integration helpers for Riverbank CMS SDK.\n *\n * Provides opinionated factories for common Next.js patterns, reducing\n * boilerplate while maintaining full customizability through escape hatches.\n *\n * @example Basic catch-all page\n * ```tsx\n * // src/app/[[...slug]]/page.tsx\n * import { createCatchAllPage } from '@riverbankcms/sdk/next';\n * import { getRiverbankClient } from '@/lib/builder-client';\n * import config from '@/riverbank.config';\n *\n * const { Page, generateMetadata } = createCatchAllPage({\n * getClient: getRiverbankClient,\n * config,\n * });\n *\n * export default Page;\n * export { generateMetadata };\n * ```\n *\n * @example With customization\n * ```tsx\n * const { Page, generateMetadata } = createCatchAllPage({\n * getClient,\n * config,\n * blockOverrides: { hero: MyCustomHero },\n * beforeRender: async () => {\n * if (process.env.MAINTENANCE_MODE === 'true') {\n * return <MaintenancePage />;\n * }\n * return null;\n * },\n * });\n * ```\n *\n * @packageDocumentation\n */\n\nimport { isPreviewMode } from '../env';\n\n// Catch-all page factory\nexport { createCatchAllPage } from './catch-all';\nexport type {\n CreateCatchAllPageOptions,\n CreateCatchAllPageResult,\n CatchAllPageProps,\n CatchAllContext,\n} from './types';\n\n// Static params utilities\nexport {\n generateAllStaticParams,\n validateStaticParamsEnv,\n pathToSlugArray,\n type StaticParamsEnvResult,\n} from './static-params';\n\n/**\n * ISR revalidation duration in seconds for production mode.\n * 5 minutes provides a good balance between freshness and performance.\n */\nexport const ISR_REVALIDATE_SECONDS = 300;\n\nexport interface ISRConfig {\n /**\n * Revalidation interval in seconds.\n * - 0: Dynamic rendering (no caching) - used in preview mode\n * - 300: 5 minute ISR - used in production mode\n */\n revalidate: number | false;\n\n /**\n * Whether the current environment is in preview mode.\n * Useful for conditional rendering or data fetching behavior.\n */\n isPreview: boolean;\n}\n\n/**\n * Get ISR configuration based on the current environment.\n *\n * @example\n * ```tsx\n * // app/[[...slug]]/page.tsx\n * import { getISRConfig } from '@riverbankcms/sdk/next';\n *\n * export const revalidate = getISRConfig().revalidate;\n * ```\n *\n * @returns ISR configuration with revalidate interval and preview flag\n */\nexport function getISRConfig(): ISRConfig {\n const preview = isPreviewMode();\n return {\n revalidate: preview ? 0 : ISR_REVALIDATE_SECONDS,\n isPreview: preview,\n };\n}\n"],"mappings":"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","names":["isPreviewMode","Page","useState","useEffect"]}
1
+ {"version":3,"sources":["../../src/next/catch-all.tsx","../../src/rendering/hooks/usePage.ts","../../src/rendering/hooks/useContent.ts","../../src/next/static-params.ts","../../src/next/index.ts"],"sourcesContent":["/**\n * Next.js catch-all page factory for Riverbank CMS.\n *\n * Provides a simple, opinionated way to render CMS content in Next.js catch-all routes.\n * Reduces typical page.tsx boilerplate from ~160 lines to ~10 lines.\n *\n * @example\n * ```tsx\n * // src/app/[[...slug]]/page.tsx\n * import { createCatchAllPage } from '@riverbankcms/sdk/next';\n * import { getRiverbankClient } from '@/lib/builder-client';\n * import config from '@/riverbank.config';\n *\n * const { Page, generateMetadata } = createCatchAllPage({\n * getClient: getRiverbankClient,\n * config,\n * blockOverrides: { hero: MyCustomHero },\n * });\n *\n * export default Page;\n * export { generateMetadata };\n * ```\n */\n\n// Note: next is a peerDependency - this module only works in Next.js projects.\nimport { notFound } from 'next/navigation';\n\nimport {\n loadContent,\n isPageContent,\n isEntryContent,\n} from '../rendering/helpers/loadContent';\nimport type { LoadContentResult } from '../rendering/helpers/loadContent';\nimport { Page as PageComponent } from '../rendering/components/Page';\nimport {\n generatePageMetadata,\n generatePreviewMetadata,\n type Metadata,\n} from '../metadata/generatePageMetadata';\nimport type {\n CreateCatchAllPageOptions,\n CatchAllPageProps,\n CreateCatchAllPageResult,\n} from './types';\n\n/**\n * Detect preview mode from environment variable.\n *\n * Sites can enable preview mode by setting:\n * ```\n * RIVERBANK_PREVIEW_MODE=true\n * ```\n *\n * This is typically set in preview/staging environments to fetch\n * draft content instead of published content.\n */\nfunction isPreviewMode(): boolean {\n return process.env.RIVERBANK_PREVIEW_MODE === 'true';\n}\n\n/**\n * Convert URL slug segments to a path string.\n *\n * @example\n * ```\n * slugToPath(undefined) // '/'\n * slugToPath([]) // '/'\n * slugToPath(['about']) // '/about'\n * slugToPath(['blog', 'post']) // '/blog/post'\n * ```\n */\nfunction slugToPath(slug?: string[]): string {\n if (!slug || slug.length === 0) return '/';\n return '/' + slug.join('/');\n}\n\n/**\n * Factory function to create a Next.js catch-all page component and metadata generator.\n *\n * This provides a simple, opinionated setup for rendering Riverbank CMS content\n * in external SDK sites. It handles:\n *\n * - Page and content entry routing\n * - Preview mode detection (via `RIVERBANK_PREVIEW_MODE` env var)\n * - SEO metadata generation\n * - Block overrides for custom components\n * - 404 handling for missing content\n *\n * ## Escape Hatches\n *\n * For customization beyond the defaults, use these options:\n *\n * - `beforeRender`: Intercept before rendering (maintenance mode, access control)\n * - `customMetadata`: Full control over SEO metadata\n * - `wrapper`: Wrap all content (analytics, error boundaries)\n *\n * For maximum control, use the lower-level helpers directly:\n * - `loadContent()` from `@riverbankcms/sdk/rendering`\n * - `Page` component from `@riverbankcms/sdk/rendering`\n * - `generatePageMetadata()` from `@riverbankcms/sdk/metadata`\n *\n * @param options - Configuration options\n * @returns Object with `Page` component and `generateMetadata` function\n *\n * @example Basic usage\n * ```tsx\n * const { Page, generateMetadata } = createCatchAllPage({\n * getClient: getRiverbankClient,\n * config,\n * });\n *\n * export default Page;\n * export { generateMetadata };\n * ```\n *\n * @example With maintenance mode\n * ```tsx\n * const { Page, generateMetadata } = createCatchAllPage({\n * getClient,\n * config,\n * beforeRender: async () => {\n * if (process.env.MAINTENANCE_MODE === 'true') {\n * return <MaintenancePage />;\n * }\n * return null;\n * },\n * });\n * ```\n */\nexport function createCatchAllPage(\n options: CreateCatchAllPageOptions\n): CreateCatchAllPageResult {\n const {\n getClient,\n config,\n blockOverrides,\n siteUrl,\n beforeRender,\n customMetadata,\n wrapper: Wrapper,\n } = options;\n\n /**\n * Main page component that loads and renders CMS content.\n */\n async function Page({ params, searchParams: searchParamsPromise }: CatchAllPageProps): Promise<React.ReactNode> {\n const [{ slug }, searchParams] = await Promise.all([\n params,\n searchParamsPromise ?? Promise.resolve({}),\n ]);\n const path = slugToPath(slug);\n const preview = isPreviewMode();\n const client = getClient();\n\n // Load content (page or entry)\n let content: LoadContentResult;\n try {\n content = await loadContent({\n client,\n siteId: config.siteId,\n path,\n preview,\n });\n } catch (error) {\n // Log the error for debugging - could be a network error, auth failure, etc.\n // We treat all errors as \"not found\" since the page can't be rendered\n console.debug('[createCatchAllPage] Failed to load content for path:', path, error);\n return notFound();\n }\n\n // ESCAPE HATCH: beforeRender for maintenance mode, access control, etc.\n if (beforeRender) {\n const intercepted = await beforeRender({ content, path, preview, searchParams });\n if (intercepted !== null) {\n return Wrapper ? <Wrapper>{intercepted}</Wrapper> : intercepted;\n }\n }\n\n let rendered: React.ReactNode;\n\n // Render page content\n if (isPageContent(content)) {\n rendered = (\n <PageComponent\n page={content.page}\n theme={content.theme}\n siteId={content.siteId}\n resolvedData={content.resolvedData}\n blockOverrides={blockOverrides}\n />\n );\n }\n // Render entry content with template\n else if (isEntryContent(content)) {\n // Entries without template pages should 404\n if (!content.templatePage) {\n return notFound();\n }\n\n rendered = (\n <PageComponent\n page={content.templatePage}\n theme={content.theme}\n siteId={content.siteId}\n resolvedData={content.resolvedData}\n blockOverrides={blockOverrides}\n dataContext={{\n contentEntry: content.dataContext.contentEntry,\n }}\n />\n );\n }\n // Unexpected content type - should never happen\n else {\n return notFound();\n }\n\n return Wrapper ? <Wrapper>{rendered}</Wrapper> : rendered;\n }\n\n /**\n * Generate SEO metadata for the page.\n */\n async function generateMetadataFn({ params, searchParams: searchParamsPromise }: CatchAllPageProps): Promise<Metadata> {\n const [{ slug }, searchParams] = await Promise.all([\n params,\n searchParamsPromise ?? Promise.resolve({}),\n ]);\n const path = slugToPath(slug);\n const preview = isPreviewMode();\n\n try {\n const content = await loadContent({\n client: getClient(),\n siteId: config.siteId,\n path,\n preview,\n });\n\n // ESCAPE HATCH: Custom metadata generation\n if (customMetadata) {\n return customMetadata({ content, path, preview, searchParams });\n }\n\n // Resolve site URL (use config URLs as fallback)\n const resolvedUrl =\n siteUrl ?? (preview ? config.previewUrl : config.liveUrl) ?? '';\n\n // Warn if no site URL is configured - OG/Twitter tags require absolute URLs\n if (!resolvedUrl) {\n console.warn(\n '[createCatchAllPage] No siteUrl configured. OpenGraph and Twitter tags will have relative URLs which may cause social sharing previews to fail. ' +\n 'Set siteUrl option, or config.liveUrl/config.previewUrl in your riverbank.config.'\n );\n }\n\n // Build page object for metadata\n // For entries, we use metaTitle/metaDescription; purpose is used as description fallback\n const page = isPageContent(content)\n ? content.page\n : isEntryContent(content)\n ? {\n name: content.entry.metaTitle ?? content.entry.title,\n purpose: content.entry.metaDescription ?? undefined,\n }\n : { name: 'Page' };\n\n // Use preview metadata (noindex) in preview mode\n const metadataGenerator = preview\n ? generatePreviewMetadata\n : generatePageMetadata;\n\n // Use site data from loadContent result (no duplicate API call needed)\n return metadataGenerator({\n page,\n site: content.site,\n path,\n siteUrl: resolvedUrl,\n });\n } catch (error) {\n // Log the error for debugging purposes\n console.debug('[createCatchAllPage] Failed to generate metadata for path:', path, error);\n // Return minimal metadata on error - page will handle the actual 404\n return {\n title: 'Page Not Found',\n robots: { index: false, follow: false },\n };\n }\n }\n\n return {\n Page,\n generateMetadata: generateMetadataFn,\n };\n}\n","/**\n * Client-side React hook to fetch page data.\n *\n * Use this in client components for dynamic page loading.\n */\n\nimport { useState, useEffect } from 'react';\nimport type { RiverbankClient } from '../../client/types';\nimport type { PageProps } from '../components/Page';\nimport type { RuntimeSdkConfig } from '../helpers/loadPage';\nimport { prefetchBlockData } from '../../data/prefetchBlockData';\n\nexport type UsePageParams = {\n client: RiverbankClient;\n siteId: string;\n path: string;\n pageId?: string;\n /**\n * If true, fetches draft/unpublished content instead of published content.\n * This affects both the page structure and block data loaders.\n * Requires API key with site access.\n *\n * @default false\n */\n preview?: boolean;\n};\n\nexport type UsePageResult =\n | { loading: true; error: null; page: null; theme: null; siteId: string; resolvedData: null; sdkConfig: null }\n | { loading: false; error: Error; page: null; theme: null; siteId: string; resolvedData: null; sdkConfig: null }\n | { loading: false; error: null; sdkConfig: RuntimeSdkConfig | null } & Omit<PageProps, 'registry' | 'wrapBlock' | 'usePlaceholders' | 'sdkConfig'>;\n\n/**\n * Client-side React hook to fetch all data needed for <Page> component.\n *\n * Fetches site data, page data, and prefetches block data loaders.\n * Returns loading and error states for proper UI handling.\n *\n * IMPORTANT: The client object should be stable across renders to avoid\n * unnecessary re-fetches. Create it outside your component or use useMemo:\n *\n * ```tsx\n * // ✅ Good - stable reference\n * const client = useMemo(\n * () => createRiverbankClient({ apiKey, baseUrl }),\n * [apiKey, baseUrl]\n * );\n *\n * // ❌ Bad - new client on every render (causes infinite loops)\n * const client = createRiverbankClient({ apiKey, baseUrl });\n * ```\n *\n * @example Basic usage\n * ```tsx\n * import { createRiverbankClient } from '@riverbankcms/sdk';\n * import { usePage, Page } from '@riverbankcms/sdk/rendering';\n *\n * const client = createRiverbankClient({\n * apiKey: process.env.NEXT_PUBLIC_RIVERBANK_API_KEY!,\n * baseUrl: process.env.NEXT_PUBLIC_DASHBOARD_URL + '/api',\n * });\n *\n * function MyPage({ path }: { path: string }) {\n * const pageData = usePage({ client, siteId: 'site-123', path });\n *\n * if (pageData.loading) {\n * return <div>Loading...</div>;\n * }\n *\n * if (pageData.error) {\n * return <div>Error: {pageData.error.message}</div>;\n * }\n *\n * return <Page {...pageData} />;\n * }\n * ```\n *\n * @example With custom loading/error states\n * ```tsx\n * function MyPage({ path }: { path: string }) {\n * const pageData = usePage({ client, siteId: 'site-123', path });\n *\n * if (pageData.loading) {\n * return <Skeleton />;\n * }\n *\n * if (pageData.error) {\n * return (\n * <ErrorBoundary\n * error={pageData.error}\n * onRetry={() => window.location.reload()}\n * />\n * );\n * }\n *\n * return <Page {...pageData} />;\n * }\n * ```\n */\nexport function usePage(params: UsePageParams): UsePageResult {\n const { client, siteId, path, pageId, preview = false } = params;\n\n const [result, setResult] = useState<UsePageResult>({\n loading: true,\n error: null,\n page: null,\n theme: null,\n siteId,\n resolvedData: null,\n sdkConfig: null,\n });\n\n useEffect(() => {\n let cancelled = false;\n\n async function fetchPage() {\n try {\n // Fetch site and page data in parallel\n const [site, pageResponse] = await Promise.all([\n client.getSite({ id: siteId }),\n client.getPage({ siteId, path, preview }),\n ]);\n\n // If component unmounted, don't update state\n if (cancelled) return;\n\n // Extract page data (getContentByPath can return page or entry)\n if ('entry' in pageResponse) {\n throw new Error(\n 'This path resolves to a content entry, not a page. ' +\n 'Use useContent() instead, which handles both pages and entries. ' +\n 'For entries, useContent() returns the raw entry data for custom rendering.'\n );\n }\n\n const { page: pageData } = pageResponse;\n\n // Convert API response blocks to PageOutline format\n // API returns blocks with full content, but PageOutline only needs structure\n const blocks = pageData.blocks.map((block) => {\n if (!block || typeof block !== 'object') {\n throw new Error('Invalid block format in API response');\n }\n if (typeof block.id !== 'string' && block.id !== null) {\n throw new Error(`Invalid block id: expected string or null, got ${typeof block.id}`);\n }\n if (typeof block.kind !== 'string') {\n throw new Error(`Invalid block kind: expected string, got ${typeof block.kind}`);\n }\n if (typeof block.purpose !== 'string') {\n throw new Error(`Invalid block purpose: expected string, got ${typeof block.purpose}`);\n }\n return {\n id: block.id,\n kind: block.kind,\n purpose: block.purpose,\n };\n });\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\n const resolvedData = await prefetchBlockData(\n pageOutline,\n {\n siteId,\n pageId: pageId ?? pageData.id,\n previewStage: preview ? 'preview' : 'published',\n },\n client\n );\n\n // If component unmounted, don't update state\n if (cancelled) return;\n\n setResult({\n loading: false,\n error: null,\n page: pageOutline,\n theme: site.theme,\n siteId,\n resolvedData,\n sdkConfig: site.sdkConfig ?? null,\n });\n } catch (error) {\n if (cancelled) return;\n\n setResult({\n loading: false,\n error: error instanceof Error ? error : new Error(String(error)),\n page: null,\n theme: null,\n siteId,\n resolvedData: null,\n sdkConfig: null,\n });\n }\n }\n\n fetchPage();\n\n return () => {\n cancelled = true;\n };\n }, [client, siteId, path, pageId, preview]);\n\n return result;\n}\n","/**\n * Client-side React hook to fetch content (page or entry) by path.\n *\n * Use this in client components for dynamic routing where a path\n * could resolve to either a page or content entry.\n */\n\nimport { useState, useEffect } from 'react';\nimport type { Theme } from '@riverbankcms/blocks';\nimport type { RiverbankClient, PageResponse } from '../../client/types';\nimport type { PageProps } from '../components/Page';\nimport { prefetchBlockData } from '../../data/prefetchBlockData';\nimport type { ResolvedBlockData } from '../../data/prefetchBlockData';\nimport type { ContentEntryData } from '../helpers/loadContent';\n\nexport type UseContentParams = {\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 * Loading state\n */\ntype LoadingState = {\n loading: true;\n error: null;\n type: null;\n page: null;\n entry: null;\n theme: null;\n siteId: string;\n resolvedData: null;\n};\n\n/**\n * Error state\n */\ntype ErrorState = {\n loading: false;\n error: Error;\n type: null;\n page: null;\n entry: null;\n theme: null;\n siteId: string;\n resolvedData: null;\n};\n\n/**\n * Success state for page content\n */\ntype PageSuccessState = {\n loading: false;\n error: null;\n type: 'page';\n page: PageProps['page'];\n entry: null;\n theme: Theme;\n siteId: string;\n resolvedData: ResolvedBlockData;\n};\n\n/**\n * Success state for entry content\n */\ntype EntrySuccessState = {\n loading: false;\n error: null;\n type: 'entry';\n page: null;\n entry: ContentEntryData;\n theme: Theme;\n siteId: string;\n resolvedData: null;\n};\n\nexport type UseContentResult = LoadingState | ErrorState | PageSuccessState | EntrySuccessState;\n\n/**\n * Type guard to check if result is loading\n */\nexport function isContentLoading(result: UseContentResult): result is LoadingState {\n return result.loading === true;\n}\n\n/**\n * Type guard to check if result has an error\n */\nexport function isContentError(result: UseContentResult): result is ErrorState {\n return result.loading === false && result.error !== null;\n}\n\n/**\n * Type guard to check if result is a page\n */\nexport function isPageContentResult(result: UseContentResult): result is PageSuccessState {\n return result.loading === false && result.error === null && result.type === 'page';\n}\n\n/**\n * Type guard to check if result is an entry\n */\nexport function isEntryContentResult(result: UseContentResult): result is EntrySuccessState {\n return result.loading === false && result.error === null && result.type === 'entry';\n}\n\n/**\n * Client-side React hook to fetch content by path.\n *\n * Returns a discriminated union with loading/error states, and either\n * page data (ready for `<Page>` component) or raw entry data (for custom rendering).\n *\n * IMPORTANT: The client object should be stable across renders to avoid\n * unnecessary re-fetches. Create it outside your component or use useMemo.\n *\n * @example Dynamic routing with both pages and entries\n * ```tsx\n * \"use client\";\n *\n * import { useContent, Page, isPageContentResult } from '@riverbankcms/sdk/client';\n *\n * function DynamicPage({ path }: { path: string }) {\n * const content = useContent({ client, siteId: 'site-123', path });\n *\n * if (content.loading) return <div>Loading...</div>;\n * if (content.error) return <div>Error: {content.error.message}</div>;\n *\n * if (isPageContentResult(content)) {\n * return <Page page={content.page} theme={content.theme} siteId={content.siteId} resolvedData={content.resolvedData} />;\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 = useContent({ client, siteId, path });\n *\n * if (content.loading) return <Spinner />;\n * if (content.error) return <Error error={content.error} />;\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 * }\n * }\n *\n * return <Page {...content} />;\n * ```\n */\nexport function useContent(params: UseContentParams): UseContentResult {\n const { client, siteId, path, preview = false } = params;\n\n const [result, setResult] = useState<UseContentResult>({\n loading: true,\n error: null,\n type: null,\n page: null,\n entry: null,\n theme: null,\n siteId,\n resolvedData: null,\n });\n\n useEffect(() => {\n let cancelled = false;\n\n async function fetchContent() {\n try {\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 // If component unmounted, don't update state\n if (cancelled) return;\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 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 setResult({\n loading: false,\n error: null,\n type: 'entry',\n page: null,\n entry,\n theme: site.theme,\n siteId,\n resolvedData: null,\n });\n return;\n }\n\n // Handle page response\n const { page: pageData } = contentResponse;\n\n // Convert API response blocks to PageOutline format\n const blocks = pageData.blocks.map((block) => {\n if (!block || typeof block !== 'object') {\n throw new Error('Invalid block format in API response');\n }\n if (typeof block.id !== 'string' && block.id !== null) {\n throw new Error(`Invalid block id: expected string or null, got ${typeof block.id}`);\n }\n if (typeof block.kind !== 'string') {\n throw new Error(`Invalid block kind: expected string, got ${typeof block.kind}`);\n }\n if (typeof block.purpose !== 'string') {\n throw new Error(`Invalid block purpose: expected string, got ${typeof block.purpose}`);\n }\n return {\n id: block.id,\n kind: block.kind,\n purpose: block.purpose,\n };\n });\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 // If component unmounted, don't update state\n if (cancelled) return;\n\n setResult({\n loading: false,\n error: null,\n type: 'page',\n page: pageOutline,\n entry: null,\n theme: site.theme,\n siteId,\n resolvedData,\n });\n } catch (error) {\n if (cancelled) return;\n\n setResult({\n loading: false,\n error: error instanceof Error ? error : new Error(String(error)),\n type: null,\n page: null,\n entry: null,\n theme: null,\n siteId,\n resolvedData: null,\n });\n }\n }\n\n fetchContent();\n\n return () => {\n cancelled = true;\n };\n }, [client, siteId, path, preview]);\n\n return result;\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 * Static params generation for Next.js SSG\n *\n * Provides helpers for generating static params from published CMS routes.\n *\n * @example\n * ```tsx\n * // app/[[...slug]]/page.tsx\n * import { generateAllStaticParams } from '@riverbankcms/sdk/next';\n *\n * export { generateAllStaticParams as generateStaticParams };\n * ```\n */\n\nimport { createRiverbankClient } from '../client';\n\n/**\n * Environment variable validation result\n */\nexport type StaticParamsEnvResult =\n | { valid: true; config: { apiKey: string; siteId: string; baseUrl: string } }\n | { valid: false; missingVars: string[] };\n\n/**\n * Validate that all required environment variables are set.\n *\n * Required env vars:\n * - RIVERBANK_API_KEY: API key for published content\n * - RIVERBANK_SITE_ID: Site ID\n * - NEXT_PUBLIC_DASHBOARD_URL: Dashboard API URL\n *\n * @returns Validation result with config or missing vars\n */\nexport function validateStaticParamsEnv(): StaticParamsEnvResult {\n const apiKey = process.env.RIVERBANK_API_KEY;\n const siteId = process.env.RIVERBANK_SITE_ID;\n const baseUrl = process.env.NEXT_PUBLIC_DASHBOARD_URL;\n\n const missingVars: string[] = [];\n\n if (!apiKey) missingVars.push('RIVERBANK_API_KEY');\n if (!siteId) missingVars.push('RIVERBANK_SITE_ID');\n if (!baseUrl) missingVars.push('NEXT_PUBLIC_DASHBOARD_URL');\n\n if (missingVars.length > 0) {\n return { valid: false, missingVars };\n }\n\n // TypeScript can't narrow through the checks above, so assert non-null\n return {\n valid: true,\n config: { apiKey: apiKey!, siteId: siteId!, baseUrl: baseUrl! },\n };\n}\n\n/**\n * Convert a route path to a Next.js slug array.\n *\n * @param path - The route path (e.g., '/about', '/blog/post')\n * @returns The slug array for Next.js catch-all route\n *\n * @example\n * ```ts\n * pathToSlugArray('/') // []\n * pathToSlugArray('/about') // ['about']\n * pathToSlugArray('/blog/post') // ['blog', 'post']\n * ```\n */\nexport function pathToSlugArray(path: string): string[] {\n if (path === '/') return [];\n return path.slice(1).split('/');\n}\n\n/**\n * Generate static params for all published routes.\n *\n * This function fetches all published routes from the CMS and converts them\n * to the static params format expected by Next.js catch-all routes.\n *\n * Requires environment variables:\n * - RIVERBANK_API_KEY: API key for published content\n * - RIVERBANK_SITE_ID: Site ID\n * - NEXT_PUBLIC_DASHBOARD_URL: Dashboard API URL\n *\n * @throws Error if required env vars are missing (prevents silent empty SSG in CI)\n * @returns Array of static params objects for Next.js\n *\n * @example\n * ```tsx\n * // app/[[...slug]]/page.tsx\n * import { generateAllStaticParams } from '@riverbankcms/sdk/next';\n *\n * export { generateAllStaticParams as generateStaticParams };\n *\n * // Or with custom logic:\n * export async function generateStaticParams() {\n * const params = await generateAllStaticParams();\n * // Filter or modify params as needed\n * return params.filter(p => !p.slug.includes('private'));\n * }\n * ```\n */\nexport async function generateAllStaticParams(): Promise<{ slug: string[] }[]> {\n const envResult = validateStaticParamsEnv();\n\n if (!envResult.valid) {\n throw new Error(\n `[Riverbank] generateAllStaticParams requires env vars: ${envResult.missingVars.join(', ')}. ` +\n `This error prevents accidentally deploying with zero static pages.`\n );\n }\n\n const { apiKey, siteId: _siteId, baseUrl } = envResult.config;\n\n // Create client (unused until getAllPublishedRoutes is implemented)\n const _client = createRiverbankClient({\n apiKey,\n baseUrl,\n // Disable caching for build-time fetches\n cache: { enabled: false },\n // Disable resilience for build-time fetches (want fast failure)\n resilience: { enabled: false },\n });\n\n // TODO: This requires adding getAllPublishedRoutes to the client\n // which requires the CMS endpoint to be updated to accept API key auth\n // and support the publishedOnly query param.\n //\n // For now, we'll use getPage to fetch the site's homepage which contains\n // the navigation data, and extract routes from there.\n //\n // In the future, implement:\n // const routes = await client.getAllPublishedRoutes({ siteId });\n // return routes.map(path => ({ slug: pathToSlugArray(path) }));\n\n // Temporary: Return empty array with warning\n // This allows the helper to be used but won't pre-render any pages\n console.warn(\n '[Riverbank] generateAllStaticParams: getAllPublishedRoutes API not yet implemented. ' +\n 'Pages will be generated on-demand with ISR.'\n );\n\n // Return at least the homepage\n return [{ slug: [] }];\n}\n","/**\n * Next.js integration helpers for Riverbank CMS SDK.\n *\n * Provides opinionated factories for common Next.js patterns, reducing\n * boilerplate while maintaining full customizability through escape hatches.\n *\n * @example Basic catch-all page\n * ```tsx\n * // src/app/[[...slug]]/page.tsx\n * import { createCatchAllPage } from '@riverbankcms/sdk/next';\n * import { getRiverbankClient } from '@/lib/builder-client';\n * import config from '@/riverbank.config';\n *\n * const { Page, generateMetadata } = createCatchAllPage({\n * getClient: getRiverbankClient,\n * config,\n * });\n *\n * export default Page;\n * export { generateMetadata };\n * ```\n *\n * @example With customization\n * ```tsx\n * const { Page, generateMetadata } = createCatchAllPage({\n * getClient,\n * config,\n * blockOverrides: { hero: MyCustomHero },\n * beforeRender: async () => {\n * if (process.env.MAINTENANCE_MODE === 'true') {\n * return <MaintenancePage />;\n * }\n * return null;\n * },\n * });\n * ```\n *\n * @packageDocumentation\n */\n\nimport { isPreviewMode } from '../env';\n\n// Catch-all page factory\nexport { createCatchAllPage } from './catch-all';\nexport type {\n CreateCatchAllPageOptions,\n CreateCatchAllPageResult,\n CatchAllPageProps,\n CatchAllContext,\n} from './types';\n\n// Static params utilities\nexport {\n generateAllStaticParams,\n validateStaticParamsEnv,\n pathToSlugArray,\n type StaticParamsEnvResult,\n} from './static-params';\n\n/**\n * ISR revalidation duration in seconds for production mode.\n * 5 minutes provides a good balance between freshness and performance.\n */\nexport const ISR_REVALIDATE_SECONDS = 300;\n\nexport interface ISRConfig {\n /**\n * Revalidation interval in seconds.\n * - 0: Dynamic rendering (no caching) - used in preview mode\n * - 300: 5 minute ISR - used in production mode\n */\n revalidate: number | false;\n\n /**\n * Whether the current environment is in preview mode.\n * Useful for conditional rendering or data fetching behavior.\n */\n isPreview: boolean;\n}\n\n/**\n * Get ISR configuration based on the current environment.\n *\n * @example\n * ```tsx\n * // app/[[...slug]]/page.tsx\n * import { getISRConfig } from '@riverbankcms/sdk/next';\n *\n * export const revalidate = getISRConfig().revalidate;\n * ```\n *\n * @returns ISR configuration with revalidate interval and preview flag\n */\nexport function getISRConfig(): ISRConfig {\n const preview = isPreviewMode();\n return {\n revalidate: preview ? 0 : ISR_REVALIDATE_SECONDS,\n isPreview: preview,\n };\n}\n"],"mappings":"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","names":["isPreviewMode","Page","useState","useEffect"]}
@@ -1,10 +1,10 @@
1
- export { b as Page, P as PageProps } from '../loadPage-CxlYLe5K.mjs';
2
- export { H as HeaderData, L as Layout, a as LayoutProps } from '../Layout-WllR8Zug.mjs';
1
+ export { b as Page, P as PageProps } from '../loadPage-BucnLHmE.mjs';
2
+ export { H as HeaderData, L as Layout, a as LayoutProps } from '../Layout-qWLdVm5-.mjs';
3
3
  import * as react_jsx_runtime from 'react/jsx-runtime';
4
4
  import * as React from 'react';
5
- import { T as Theme } from '../types-Dsu9wsUh.mjs';
5
+ import { T as Theme } from '../types-BSV6Vc-P.mjs';
6
6
  import '../schema-Z6-afHJG.mjs';
7
- import { R as RiverbankClient } from '../types-MF2AWoKv.mjs';
7
+ import { R as RiverbankClient } from '../types-C-LShyIg.mjs';
8
8
  import { B as BlockKind } from '../blockKinds-B6MWzNWp.mjs';
9
9
  import '../core-DsNWrl3o.mjs';
10
10
  import 'zod';
@@ -1,10 +1,10 @@
1
- export { b as Page, P as PageProps } from '../loadPage-i2r-X5b9.js';
2
- export { H as HeaderData, L as Layout, a as LayoutProps } from '../Layout-Ce7PU9I5.js';
1
+ export { b as Page, P as PageProps } from '../loadPage-BmYJCe_V.js';
2
+ export { H as HeaderData, L as Layout, a as LayoutProps } from '../Layout-Yluyb6sK.js';
3
3
  import * as react_jsx_runtime from 'react/jsx-runtime';
4
4
  import * as React from 'react';
5
- import { T as Theme } from '../types-CVykEqXN.js';
5
+ import { T as Theme } from '../types-Dt98DeYa.js';
6
6
  import '../schema-Z6-afHJG.js';
7
- import { R as RiverbankClient } from '../types-DnkRh0UL.js';
7
+ import { R as RiverbankClient } from '../types-BRQyLrQU.js';
8
8
  import { B as BlockKind } from '../blockKinds-B6MWzNWp.js';
9
9
  import '../core-DsNWrl3o.js';
10
10
  import 'zod';
@@ -11,6 +11,7 @@ var _chunkT26N3P26js = require('../chunk-T26N3P26.js');
11
11
  require('../chunk-7UPVCT3K.js');
12
12
  require('../chunk-RVDS7VSP.js');
13
13
  require('../chunk-YYO3RIFO.js');
14
+ require('../chunk-4CV4JOE5.js');
14
15
 
15
16
 
16
17
 
@@ -1 +1 @@
1
- {"version":3,"sources":["/Users/will/Projects/Business/cms/builder/packages/sdk/dist/server/rendering/server.js"],"names":[],"mappings":"AAAA;AACE;AACF,uDAA6B;AAC7B;AACE;AACF,uDAA6B;AAC7B,gCAA6B;AAC7B;AACE;AACF,uDAA6B;AAC7B,gCAA6B;AAC7B,gCAA6B;AAC7B,gCAA6B;AAC7B;AACE;AACA;AACA;AACF,uHAAC","file":"/Users/will/Projects/Business/cms/builder/packages/sdk/dist/server/rendering/server.js"}
1
+ {"version":3,"sources":["/Users/will/Projects/Business/cms/builder/packages/sdk/dist/server/rendering/server.js"],"names":[],"mappings":"AAAA;AACE;AACF,uDAA6B;AAC7B;AACE;AACF,uDAA6B;AAC7B,gCAA6B;AAC7B;AACE;AACF,uDAA6B;AAC7B,gCAA6B;AAC7B,gCAA6B;AAC7B,gCAA6B;AAC7B,gCAA6B;AAC7B;AACE;AACA;AACA;AACF,uHAAC","file":"/Users/will/Projects/Business/cms/builder/packages/sdk/dist/server/rendering/server.js"}
@@ -11,6 +11,7 @@ import {
11
11
  import "../chunk-LNOUXALA.mjs";
12
12
  import "../chunk-AEFWG657.mjs";
13
13
  import "../chunk-BYBJA6SP.mjs";
14
+ import "../chunk-NFEGQTCC.mjs";
14
15
  export {
15
16
  Block,
16
17
  Layout,
@@ -1,16 +1,16 @@
1
- export { d as BlockOverrideComponent, e as BlockOverrideProps, B as BlockOverrides, L as LoadPageParams, a as LoadPageResult, b as Page, P as PageProps, c as PageRenderer, l as loadPage } from './loadPage-CxlYLe5K.mjs';
2
- export { H as HeaderData, L as Layout, a as LayoutProps } from './Layout-WllR8Zug.mjs';
1
+ export { d as BlockOverrideComponent, e as BlockOverrideProps, B as BlockOverrides, L as LoadPageParams, a as LoadPageResult, b as Page, P as PageProps, c as PageRenderer, l as loadPage } from './loadPage-BucnLHmE.mjs';
2
+ export { H as HeaderData, L as Layout, a as LayoutProps } from './Layout-qWLdVm5-.mjs';
3
3
  export { Block, BlockProps } from './rendering/server.mjs';
4
- export { C as ContentEntryData, L as LoadContentParams, b as LoadContentResult, S as SiteData, a as isEntryContent, i as isPageContent, l as loadContent } from './loadContent-F_tAS0Nl.mjs';
5
- export { c as SiteFooterContent, S as SiteHeaderContent, T as Theme } from './types-Dsu9wsUh.mjs';
4
+ export { C as ContentEntryData, L as LoadContentParams, b as LoadContentResult, S as SiteData, a as isEntryContent, i as isPageContent, l as loadContent } from './loadContent-DVfuBLiZ.mjs';
5
+ export { c as SiteFooterContent, S as SiteHeaderContent, T as Theme } from './types-BSV6Vc-P.mjs';
6
6
  export { P as PageOutline } from './schema-Z6-afHJG.mjs';
7
- import { G as GradientConfig } from './components-RPzRQve6.mjs';
8
- export { f as BackgroundInput, B as BodyTextContent, H as HeroContent, a as HeroMedia, g as ResolvedBackground, R as RichText, c as RichTextPrimitiveProps, e as SectionBackground, h as SectionBackgroundProps, S as SystemBlockComponentProps, b as buildThemeRuntime, d as resolveBackground, r as resolveImageUrl } from './components-RPzRQve6.mjs';
7
+ import { G as GradientConfig } from './components-DNHfSCML.mjs';
8
+ export { f as BackgroundInput, B as BodyTextContent, H as HeroContent, a as HeroMedia, g as ResolvedBackground, R as RichText, c as RichTextPrimitiveProps, e as SectionBackground, h as SectionBackgroundProps, S as SystemBlockComponentProps, b as buildThemeRuntime, d as resolveBackground, r as resolveImageUrl } from './components-DNHfSCML.mjs';
9
9
  export { R as RouteMap, a as ThemeTokens } from './core-DsNWrl3o.mjs';
10
10
  import { M as Media } from './richTextSchema-DURiozvD.mjs';
11
11
  export { T as TipTapNode } from './richTextSchema-DURiozvD.mjs';
12
12
  export { C as CustomLinkValue, E as ExternalLinkValue, I as InternalLinkValue, L as LinkValue } from './link-DjxLyC82.mjs';
13
- import './types-MF2AWoKv.mjs';
13
+ import './types-C-LShyIg.mjs';
14
14
  import 'react/jsx-runtime';
15
15
  import 'react';
16
16
  import './types-CbagRQ_7.mjs';
@@ -1,16 +1,16 @@
1
- export { d as BlockOverrideComponent, e as BlockOverrideProps, B as BlockOverrides, L as LoadPageParams, a as LoadPageResult, b as Page, P as PageProps, c as PageRenderer, l as loadPage } from './loadPage-i2r-X5b9.js';
2
- export { H as HeaderData, L as Layout, a as LayoutProps } from './Layout-Ce7PU9I5.js';
1
+ export { d as BlockOverrideComponent, e as BlockOverrideProps, B as BlockOverrides, L as LoadPageParams, a as LoadPageResult, b as Page, P as PageProps, c as PageRenderer, l as loadPage } from './loadPage-BmYJCe_V.js';
2
+ export { H as HeaderData, L as Layout, a as LayoutProps } from './Layout-Yluyb6sK.js';
3
3
  export { Block, BlockProps } from './rendering/server.js';
4
- export { C as ContentEntryData, L as LoadContentParams, b as LoadContentResult, S as SiteData, a as isEntryContent, i as isPageContent, l as loadContent } from './loadContent-CXUWMuzY.js';
5
- export { c as SiteFooterContent, S as SiteHeaderContent, T as Theme } from './types-CVykEqXN.js';
4
+ export { C as ContentEntryData, L as LoadContentParams, b as LoadContentResult, S as SiteData, a as isEntryContent, i as isPageContent, l as loadContent } from './loadContent-D7LQwI0o.js';
5
+ export { c as SiteFooterContent, S as SiteHeaderContent, T as Theme } from './types-Dt98DeYa.js';
6
6
  export { P as PageOutline } from './schema-Z6-afHJG.js';
7
- import { G as GradientConfig } from './components--LT61IKp.js';
8
- export { f as BackgroundInput, B as BodyTextContent, H as HeroContent, a as HeroMedia, g as ResolvedBackground, R as RichText, c as RichTextPrimitiveProps, e as SectionBackground, h as SectionBackgroundProps, S as SystemBlockComponentProps, b as buildThemeRuntime, d as resolveBackground, r as resolveImageUrl } from './components--LT61IKp.js';
7
+ import { G as GradientConfig } from './components-Di5ME6He.js';
8
+ export { f as BackgroundInput, B as BodyTextContent, H as HeroContent, a as HeroMedia, g as ResolvedBackground, R as RichText, c as RichTextPrimitiveProps, e as SectionBackground, h as SectionBackgroundProps, S as SystemBlockComponentProps, b as buildThemeRuntime, d as resolveBackground, r as resolveImageUrl } from './components-Di5ME6He.js';
9
9
  export { R as RouteMap, a as ThemeTokens } from './core-DsNWrl3o.js';
10
10
  import { M as Media } from './richTextSchema-DURiozvD.js';
11
11
  export { T as TipTapNode } from './richTextSchema-DURiozvD.js';
12
12
  export { C as CustomLinkValue, E as ExternalLinkValue, I as InternalLinkValue, L as LinkValue } from './link-DjxLyC82.js';
13
- import './types-DnkRh0UL.js';
13
+ import './types-BRQyLrQU.js';
14
14
  import 'react/jsx-runtime';
15
15
  import 'react';
16
16
  import './types-DuQCNVV0.js';
@@ -27,6 +27,7 @@ var _chunk7UPVCT3Kjs = require('./chunk-7UPVCT3K.js');
27
27
  require('./chunk-Y7347JMZ.js');
28
28
  require('./chunk-RVDS7VSP.js');
29
29
  require('./chunk-YYO3RIFO.js');
30
+ require('./chunk-4CV4JOE5.js');
30
31
 
31
32
 
32
33
 
@@ -1 +1 @@
1
- {"version":3,"sources":["/Users/will/Projects/Business/cms/builder/packages/sdk/dist/server/rendering.js"],"names":[],"mappings":"AAAA;AACE;AACF,sDAA4B;AAC5B;AACE;AACA;AACA;AACF,sDAA4B;AAC5B;AACE;AACF,sDAA4B;AAC5B;AACE;AACF,sDAA4B;AAC5B,+BAA4B;AAC5B;AACE;AACF,sDAA4B;AAC5B;AACE;AACA;AACA;AACA;AACA;AACA;AACF,sDAA4B;AAC5B,+BAA4B;AAC5B,+BAA4B;AAC5B,+BAA4B;AAC5B;AACE;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACF,2qBAAC","file":"/Users/will/Projects/Business/cms/builder/packages/sdk/dist/server/rendering.js"}
1
+ {"version":3,"sources":["/Users/will/Projects/Business/cms/builder/packages/sdk/dist/server/rendering.js"],"names":[],"mappings":"AAAA;AACE;AACF,sDAA4B;AAC5B;AACE;AACA;AACA;AACF,sDAA4B;AAC5B;AACE;AACF,sDAA4B;AAC5B;AACE;AACF,sDAA4B;AAC5B,+BAA4B;AAC5B;AACE;AACF,sDAA4B;AAC5B;AACE;AACA;AACA;AACA;AACA;AACA;AACF,sDAA4B;AAC5B,+BAA4B;AAC5B,+BAA4B;AAC5B,+BAA4B;AAC5B,+BAA4B;AAC5B;AACE;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACF,2qBAAC","file":"/Users/will/Projects/Business/cms/builder/packages/sdk/dist/server/rendering.js"}
@@ -27,6 +27,7 @@ import {
27
27
  import "./chunk-A2FZMRDW.mjs";
28
28
  import "./chunk-AEFWG657.mjs";
29
29
  import "./chunk-BYBJA6SP.mjs";
30
+ import "./chunk-NFEGQTCC.mjs";
30
31
  export {
31
32
  Block,
32
33
  Layout,
@@ -1,8 +1,8 @@
1
- import { R as RiverbankClient, e as ResolveEventOccurrenceResponse } from './types-MF2AWoKv.mjs';
2
- import { a as LoadPageResult } from './loadPage-CxlYLe5K.mjs';
1
+ import { R as RiverbankClient, c as ResolveEventOccurrenceResponse } from './types-C-LShyIg.mjs';
2
+ import { a as LoadPageResult } from './loadPage-BucnLHmE.mjs';
3
3
  import { R as RiverbankSiteConfig } from './types-BjgZt8xJ.mjs';
4
4
  import './schema-Z6-afHJG.mjs';
5
- import './types-Dsu9wsUh.mjs';
5
+ import './types-BSV6Vc-P.mjs';
6
6
  import '@riverbankcms/ai';
7
7
  import 'zod';
8
8
  import './link-DjxLyC82.mjs';
@@ -1,8 +1,8 @@
1
- import { R as RiverbankClient, e as ResolveEventOccurrenceResponse } from './types-DnkRh0UL.js';
2
- import { a as LoadPageResult } from './loadPage-i2r-X5b9.js';
1
+ import { R as RiverbankClient, c as ResolveEventOccurrenceResponse } from './types-BRQyLrQU.js';
2
+ import { a as LoadPageResult } from './loadPage-BmYJCe_V.js';
3
3
  import { R as RiverbankSiteConfig } from './types-DLBhEPSt.js';
4
4
  import './schema-Z6-afHJG.js';
5
- import './types-CVykEqXN.js';
5
+ import './types-Dt98DeYa.js';
6
6
  import '@riverbankcms/ai';
7
7
  import 'zod';
8
8
  import './link-DjxLyC82.js';
@@ -1,4 +1,6 @@
1
- "use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { newObj[key] = obj[key]; } } } newObj.default = obj; return newObj; } } 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/routing/checkRedirect.ts
1
+ "use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { newObj[key] = obj[key]; } } } newObj.default = obj; return newObj; } } 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; }require('./chunk-4CV4JOE5.js');
2
+
3
+ // src/routing/checkRedirect.ts
2
4
  async function checkRedirectForPath(params) {
3
5
  const { client, siteId, path } = params;
4
6
  try {
@@ -37,7 +39,7 @@ async function resolveRoute(params) {
37
39
  preview
38
40
  });
39
41
  if (pageResponse) {
40
- const { loadPage } = await Promise.resolve().then(() => _interopRequireWildcard(require("./loadPage-6I7F6GRF.js")));
42
+ const { loadPage } = await Promise.resolve().then(() => _interopRequireWildcard(require("./loadPage-AXNAERDS.js")));
41
43
  const pageData = await loadPage({
42
44
  client,
43
45
  siteId,
@@ -1 +1 @@
1
- {"version":3,"sources":["/Users/will/Projects/Business/cms/builder/packages/sdk/dist/server/routing.js","../../src/routing/checkRedirect.ts","../../src/routing/resolveRoute.ts","../../src/routing/contentRoutes.ts","../../src/routing/resolveOccurrence.ts"],"names":[],"mappings":"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","file":"/Users/will/Projects/Business/cms/builder/packages/sdk/dist/server/routing.js","sourcesContent":[null,"/**\n * Redirect checking for 404 handling\n *\n * Check if a path has a configured redirect rule before returning 404.\n */\n\nimport type { RiverbankClient } from '../client/types';\n\n/**\n * Redirect rule data\n */\nexport type RedirectRule = {\n /**\n * Destination URL to redirect to\n */\n destination: string;\n\n /**\n * Whether this is a permanent redirect (301/308) or temporary (302/307)\n */\n permanent: boolean;\n};\n\n/**\n * Result of redirect checking\n */\nexport type RedirectResult =\n | {\n hasRedirect: true;\n redirect: RedirectRule;\n }\n | {\n hasRedirect: false;\n redirect: null;\n };\n\n/**\n * Parameters for checking redirects\n */\nexport type CheckRedirectParams = {\n /**\n * SDK client instance\n */\n client: RiverbankClient;\n\n /**\n * Site ID\n */\n siteId: string;\n\n /**\n * URL path to check (e.g., '/old-page')\n */\n path: string;\n};\n\n/**\n * Check if a path has a configured redirect rule\n *\n * Use this when a page is not found to check if there's a redirect\n * configured for the path before returning a 404.\n *\n * @example\n * ```tsx\n * import { checkRedirectForPath } from '@riverbankcms/sdk/routing';\n * import { notFound, redirect } from 'next/navigation';\n *\n * export default async function Page({ params }) {\n * const page = await getPage(params.path);\n *\n * if (!page) {\n * // Check for redirect before 404\n * const result = await checkRedirectForPath({\n * client,\n * siteId: 'your-site-id',\n * path: params.path,\n * });\n *\n * if (result.hasRedirect) {\n * redirect(result.redirect.destination);\n * }\n *\n * notFound();\n * }\n *\n * return <PageContent page={page} />;\n * }\n * ```\n */\nexport async function checkRedirectForPath(\n params: CheckRedirectParams\n): Promise<RedirectResult> {\n const { client, siteId, path } = params;\n\n try {\n const response = await client.checkRedirect({\n siteId,\n path,\n });\n\n // API returns { redirectTo: string | null, status?: number }\n if (response.redirectTo) {\n // Status 301 and 308 are permanent, others are temporary\n const isPermanent = response.status === 301 || response.status === 308;\n\n return {\n hasRedirect: true,\n redirect: {\n destination: response.redirectTo,\n permanent: isPermanent,\n },\n };\n }\n\n return { hasRedirect: false, redirect: null };\n } catch (error) {\n console.warn('[checkRedirectForPath] Failed to check redirect', {\n siteId,\n path,\n error: error instanceof Error ? error.message : String(error),\n });\n\n return { hasRedirect: false, redirect: null };\n }\n}\n","/**\n * Route resolution helper for dynamic page routing\n *\n * Resolves URL paths to pages, entries, redirects, or 404s.\n */\n\nimport type { RiverbankClient } from '../client/types';\nimport type { LoadPageResult } from '../rendering/helpers/loadPage';\nimport { checkRedirectForPath } from './checkRedirect';\n\nexport type RouteResolution =\n | {\n type: 'page';\n pageData: LoadPageResult;\n }\n | {\n type: 'redirect';\n destination: string;\n permanent: boolean;\n }\n | {\n type: 'not-found';\n };\n\nexport type ResolveRouteParams = {\n /**\n * Builder client instance\n */\n client: RiverbankClient;\n\n /**\n * Site ID\n */\n siteId: string;\n\n /**\n * URL path to resolve (e.g., '/about', '/blog/post-1')\n */\n path: string;\n\n /**\n * If true, fetches draft/unpublished content instead of published content.\n * @default false\n */\n preview?: boolean;\n};\n\n/**\n * Resolve a URL path to page data, redirect, or 404\n *\n * This helper attempts to fetch the page at the given path. If the page\n * is not found, it checks for configured redirects before returning 404.\n *\n * Returns a discriminated union indicating:\n * - `page`: Page was found, includes full page data\n * - `redirect`: Path has a redirect rule configured\n * - `not-found`: No page or redirect exists for this path\n *\n * @example\n * ```tsx\n * import { resolveRoute } from '@riverbankcms/sdk/routing';\n * import { notFound, redirect } from 'next/navigation';\n *\n * export default async function DynamicPage({ params }) {\n * const path = `/${params.slug?.join('/') || ''}`;\n * const resolution = await resolveRoute({\n * client,\n * siteId: 'your-site-id',\n * path,\n * });\n *\n * if (resolution.type === 'redirect') {\n * redirect(resolution.destination);\n * }\n *\n * if (resolution.type === 'not-found') {\n * notFound();\n * }\n *\n * // resolution.type === 'page'\n * return <Page {...resolution.pageData} />;\n * }\n * ```\n */\nexport async function resolveRoute(\n params: ResolveRouteParams\n): Promise<RouteResolution> {\n const { client, siteId, path, preview = false } = params;\n\n try {\n // Attempt to fetch page data\n const pageResponse = await client.getPage({\n siteId,\n path,\n preview,\n });\n\n if (pageResponse) {\n // Successfully found page - load full page data\n const { loadPage } = await import('../rendering/helpers/loadPage');\n const pageData = await loadPage({\n client,\n siteId,\n path,\n preview,\n });\n\n return {\n type: 'page',\n pageData,\n };\n }\n } catch (error) {\n // Distinguish between expected 404s and unexpected errors\n const is404 = error instanceof Error &&\n (error.message.includes('404') ||\n error.message.includes('Not Found') ||\n error.message.includes('not found'));\n\n if (is404) {\n console.debug('[resolveRoute] Page not found', { path });\n } else {\n // Unexpected error - log as warning for visibility\n console.warn('[resolveRoute] Failed to fetch page', {\n path,\n error: error instanceof Error ? error.message : String(error),\n });\n }\n }\n\n // No page found - check for redirect before returning 404\n try {\n const redirectResult = await checkRedirectForPath({\n client,\n siteId,\n path,\n });\n\n if (redirectResult.hasRedirect) {\n return {\n type: 'redirect',\n destination: redirectResult.redirect.destination,\n permanent: redirectResult.redirect.permanent,\n };\n }\n } catch (redirectError) {\n console.debug('[resolveRoute] Redirect check failed', {\n path,\n error: redirectError instanceof Error ? redirectError.message : String(redirectError),\n });\n }\n\n return {\n type: 'not-found',\n };\n}\n\n/**\n * Batch resolve multiple routes in parallel\n *\n * Useful for pre-fetching multiple routes or validating a sitemap.\n *\n * @example\n * ```tsx\n * const resolutions = await resolveRoutes({\n * client,\n * siteId: 'your-site-id',\n * paths: ['/', '/about', '/blog', '/contact'],\n * });\n *\n * resolutions.forEach(({ path, resolution }) => {\n * if (resolution.type === 'page') {\n * console.log(`${path} → Page: ${resolution.pageData.page.name}`);\n * } else if (resolution.type === 'redirect') {\n * console.log(`${path} → Redirect to ${resolution.destination}`);\n * } else {\n * console.log(`${path} → Not found`);\n * }\n * });\n * ```\n */\nexport async function resolveRoutes(params: {\n client: RiverbankClient;\n siteId: string;\n paths: string[];\n preview?: boolean;\n}): Promise<Array<{ path: string; resolution: RouteResolution }>> {\n const { client, siteId, paths, preview } = params;\n\n const resolutions = await Promise.all(\n paths.map(async (path) => {\n const resolution = await resolveRoute({\n client,\n siteId,\n path,\n preview,\n });\n\n return { path, resolution };\n })\n );\n\n return resolutions;\n}\n","/**\n * Content route matching utilities.\n *\n * Derive route matching from SDK config to determine whether a URL path\n * is a CMS page or a content entry, without duplicating route patterns.\n */\n\nimport type { RiverbankSiteConfig, ContentTypeConfig } from '../config';\n\n/**\n * Extract the first path segment from each routable content type's routePattern.\n *\n * Useful for simple prefix-based routing decisions. For more precise matching\n * that handles nested patterns, use `isContentEntryPath` instead.\n *\n * @param config - The SDK config object from defineConfig()\n * @returns Array of first path segments from content type routePatterns\n *\n * @example\n * ```typescript\n * import { getContentEntryPrefixes } from '@riverbankcms/sdk/routing';\n * import config from '../riverbank.config';\n *\n * // Config with routePatterns: '/blog/{slug}', '/work/projects/{slug}'\n * const prefixes = getContentEntryPrefixes(config);\n * // Returns ['blog', 'work']\n * ```\n */\nexport function getContentEntryPrefixes(config: RiverbankSiteConfig): string[] {\n const contentTypes = config.content?.contentTypes ?? [];\n\n return contentTypes\n .filter((ct): ct is ContentTypeConfig & { routePattern: string } =>\n ct.hasPages && typeof ct.routePattern === 'string'\n )\n .map(ct => {\n // '/blog/{slug}' → 'blog'\n // '/work/projects/{slug}' → 'work'\n // '/{slug}' → undefined (no static prefix)\n const match = ct.routePattern.match(/^\\/([^/]+)/);\n const segment = match?.[1];\n // Skip dynamic segments (those containing {})\n if (segment?.includes('{')) return undefined;\n return segment;\n })\n .filter((prefix): prefix is string => typeof prefix === 'string');\n}\n\n/**\n * Result of checking if a path matches a content entry route.\n */\nexport type ContentEntryMatch = {\n /** Whether the path matches a content entry route pattern */\n isEntry: boolean;\n /** The content type key if matched (e.g., 'blog-post') */\n contentType?: string;\n /** The slug extracted from the path if matched */\n slug?: string;\n};\n\n/**\n * Check if a URL path matches any content entry route pattern.\n *\n * Supports nested patterns like '/work/projects/{slug}'. Returns the matched\n * content type key and extracted slug, useful for routing decisions.\n *\n * **Note:** Content types are checked in array order. If multiple patterns\n * could match a path, the first matching content type wins. Order more specific\n * patterns before generic ones in your config.\n *\n * @param config - The SDK config object from defineConfig()\n * @param path - URL path as string ('/blog/my-post') or segments ['blog', 'my-post']\n * @returns Match result with content type and slug if matched\n *\n * @example\n * ```typescript\n * import { isContentEntryPath } from '@riverbankcms/sdk/routing';\n * import config from '../riverbank.config';\n *\n * // Simple pattern: '/blog/{slug}'\n * isContentEntryPath(config, '/blog/my-post');\n * // Returns { isEntry: true, contentType: 'blog-post', slug: 'my-post' }\n *\n * // Nested pattern: '/work/projects/{slug}'\n * isContentEntryPath(config, '/work/projects/website-redesign');\n * // Returns { isEntry: true, contentType: 'project', slug: 'website-redesign' }\n *\n * // Non-matching path\n * isContentEntryPath(config, '/about');\n * // Returns { isEntry: false }\n * ```\n */\nexport function isContentEntryPath(\n config: RiverbankSiteConfig,\n path: string | string[]\n): ContentEntryMatch {\n const segments = typeof path === 'string'\n ? path.split('/').filter(Boolean)\n : path;\n\n const contentTypes = config.content?.contentTypes ?? [];\n\n for (const ct of contentTypes) {\n if (!ct.hasPages || !ct.routePattern) continue;\n\n // Parse pattern: '/blog/{slug}' → ['blog'], '/work/projects/{slug}' → ['work', 'projects']\n const patternSegments = parseRoutePattern(ct.routePattern);\n\n // Check if path segments match the pattern\n const match = matchPattern(segments, patternSegments);\n if (match.matches) {\n return {\n isEntry: true,\n contentType: ct.key,\n slug: match.slug,\n };\n }\n }\n\n return { isEntry: false };\n}\n\n/**\n * Parse a route pattern into static segments before {slug}.\n *\n * @example\n * parseRoutePattern('/blog/{slug}') // ['blog']\n * parseRoutePattern('/work/projects/{slug}') // ['work', 'projects']\n * parseRoutePattern('/{slug}') // []\n */\nfunction parseRoutePattern(pattern: string): string[] {\n const segments = pattern.split('/').filter(Boolean);\n const staticSegments: string[] = [];\n\n for (const segment of segments) {\n if (segment.includes('{')) break; // Stop at dynamic segment\n staticSegments.push(segment);\n }\n\n return staticSegments;\n}\n\n/**\n * Check if path segments match pattern segments.\n * Path must have at least one more segment than pattern (the slug).\n *\n * @returns Match result with extracted slug if matched\n */\nfunction matchPattern(\n pathSegments: string[],\n patternSegments: string[]\n): { matches: boolean; slug?: string } {\n // Path needs: pattern segments + at least 1 slug segment\n if (pathSegments.length < patternSegments.length + 1) {\n return { matches: false };\n }\n\n // All pattern segments must match exactly\n for (let i = 0; i < patternSegments.length; i++) {\n if (pathSegments[i] !== patternSegments[i]) {\n return { matches: false };\n }\n }\n\n // Extract slug (everything after the pattern segments)\n // For '/blog/{slug}' with path '/blog/my-post' → slug = 'my-post'\n // For nested slugs, join remaining segments (rare but supported)\n const slugSegments = pathSegments.slice(patternSegments.length);\n const slug = slugSegments.join('/');\n\n return { matches: true, slug };\n}\n","/**\n * Event occurrence resolution for dynamic URLs\n *\n * Resolves URL segments like \"2025-01-15\" or \"abc123-uuid\" to event occurrence data.\n */\n\nimport type { RiverbankClient, ResolveEventOccurrenceResponse } from '../client/types';\n\n/**\n * Data for a resolved event occurrence\n */\nexport type OccurrenceData = NonNullable<ResolveEventOccurrenceResponse['occurrence']>;\n\n/**\n * Result of occurrence resolution\n */\nexport type OccurrenceResolution =\n | {\n found: true;\n occurrence: OccurrenceData;\n }\n | {\n found: false;\n occurrence: null;\n };\n\n/**\n * Parameters for resolving an event occurrence\n */\nexport type ResolveOccurrenceParams = {\n /**\n * SDK client instance\n */\n client: RiverbankClient;\n\n /**\n * Site ID\n */\n siteId: string;\n\n /**\n * Parent content entry ID (the event entry)\n */\n entryId: string;\n\n /**\n * URL segment to resolve - either a date (YYYY-MM-DD) or UUID\n */\n segment: string;\n};\n\n/**\n * Check if a string is a valid ISO date (YYYY-MM-DD)\n */\nfunction isISODateString(str: string): boolean {\n return /^\\d{4}-\\d{2}-\\d{2}$/.test(str);\n}\n\n/**\n * Check if a string is a valid UUID\n */\nfunction isUUID(str: string): boolean {\n return /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(str);\n}\n\n/**\n * Validate URL segment format before making API call\n */\nfunction isValidSegment(segment: string): boolean {\n return isISODateString(segment) || isUUID(segment);\n}\n\n/**\n * Resolve an event occurrence from a URL segment\n *\n * Supports two segment formats:\n * - Date format: \"2025-01-15\" → finds occurrence starting on that date\n * - UUID format: \"abc123...\" → finds occurrence by ID\n *\n * @example\n * ```tsx\n * import { resolveOccurrence } from '@riverbankcms/sdk/routing';\n *\n * // In a Next.js dynamic route: /events/[slug]/[occurrence]\n * export default async function EventOccurrencePage({ params }) {\n * const result = await resolveOccurrence({\n * client,\n * siteId: 'your-site-id',\n * entryId: eventEntry.id,\n * segment: params.occurrence, // e.g., \"2025-01-15\"\n * });\n *\n * if (!result.found) {\n * notFound();\n * }\n *\n * return <EventOccurrence occurrence={result.occurrence} />;\n * }\n * ```\n */\nexport async function resolveOccurrence(\n params: ResolveOccurrenceParams\n): Promise<OccurrenceResolution> {\n const { client, siteId, entryId, segment } = params;\n\n // Validate segment format client-side to avoid unnecessary API calls\n if (!isValidSegment(segment)) {\n console.debug('[resolveOccurrence] Invalid segment format', { segment });\n return { found: false, occurrence: null };\n }\n\n try {\n const response = await client.resolveEventOccurrence({\n siteId,\n entryId,\n segment,\n });\n\n if (response.occurrence) {\n return {\n found: true,\n occurrence: response.occurrence,\n };\n }\n\n return { found: false, occurrence: null };\n } catch (error) {\n console.warn('[resolveOccurrence] Failed to resolve occurrence', {\n siteId,\n entryId,\n segment,\n error: error instanceof Error ? error.message : String(error),\n });\n\n return { found: false, occurrence: null };\n }\n}\n\nexport { isISODateString, isUUID, isValidSegment };\n"]}
1
+ {"version":3,"sources":["/Users/will/Projects/Business/cms/builder/packages/sdk/dist/server/routing.js","../../src/routing/checkRedirect.ts","../../src/routing/resolveRoute.ts","../../src/routing/contentRoutes.ts","../../src/routing/resolveOccurrence.ts"],"names":[],"mappings":"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","file":"/Users/will/Projects/Business/cms/builder/packages/sdk/dist/server/routing.js","sourcesContent":[null,"/**\n * Redirect checking for 404 handling\n *\n * Check if a path has a configured redirect rule before returning 404.\n */\n\nimport type { RiverbankClient } from '../client/types';\n\n/**\n * Redirect rule data\n */\nexport type RedirectRule = {\n /**\n * Destination URL to redirect to\n */\n destination: string;\n\n /**\n * Whether this is a permanent redirect (301/308) or temporary (302/307)\n */\n permanent: boolean;\n};\n\n/**\n * Result of redirect checking\n */\nexport type RedirectResult =\n | {\n hasRedirect: true;\n redirect: RedirectRule;\n }\n | {\n hasRedirect: false;\n redirect: null;\n };\n\n/**\n * Parameters for checking redirects\n */\nexport type CheckRedirectParams = {\n /**\n * SDK client instance\n */\n client: RiverbankClient;\n\n /**\n * Site ID\n */\n siteId: string;\n\n /**\n * URL path to check (e.g., '/old-page')\n */\n path: string;\n};\n\n/**\n * Check if a path has a configured redirect rule\n *\n * Use this when a page is not found to check if there's a redirect\n * configured for the path before returning a 404.\n *\n * @example\n * ```tsx\n * import { checkRedirectForPath } from '@riverbankcms/sdk/routing';\n * import { notFound, redirect } from 'next/navigation';\n *\n * export default async function Page({ params }) {\n * const page = await getPage(params.path);\n *\n * if (!page) {\n * // Check for redirect before 404\n * const result = await checkRedirectForPath({\n * client,\n * siteId: 'your-site-id',\n * path: params.path,\n * });\n *\n * if (result.hasRedirect) {\n * redirect(result.redirect.destination);\n * }\n *\n * notFound();\n * }\n *\n * return <PageContent page={page} />;\n * }\n * ```\n */\nexport async function checkRedirectForPath(\n params: CheckRedirectParams\n): Promise<RedirectResult> {\n const { client, siteId, path } = params;\n\n try {\n const response = await client.checkRedirect({\n siteId,\n path,\n });\n\n // API returns { redirectTo: string | null, status?: number }\n if (response.redirectTo) {\n // Status 301 and 308 are permanent, others are temporary\n const isPermanent = response.status === 301 || response.status === 308;\n\n return {\n hasRedirect: true,\n redirect: {\n destination: response.redirectTo,\n permanent: isPermanent,\n },\n };\n }\n\n return { hasRedirect: false, redirect: null };\n } catch (error) {\n console.warn('[checkRedirectForPath] Failed to check redirect', {\n siteId,\n path,\n error: error instanceof Error ? error.message : String(error),\n });\n\n return { hasRedirect: false, redirect: null };\n }\n}\n","/**\n * Route resolution helper for dynamic page routing\n *\n * Resolves URL paths to pages, entries, redirects, or 404s.\n */\n\nimport type { RiverbankClient } from '../client/types';\nimport type { LoadPageResult } from '../rendering/helpers/loadPage';\nimport { checkRedirectForPath } from './checkRedirect';\n\nexport type RouteResolution =\n | {\n type: 'page';\n pageData: LoadPageResult;\n }\n | {\n type: 'redirect';\n destination: string;\n permanent: boolean;\n }\n | {\n type: 'not-found';\n };\n\nexport type ResolveRouteParams = {\n /**\n * Builder client instance\n */\n client: RiverbankClient;\n\n /**\n * Site ID\n */\n siteId: string;\n\n /**\n * URL path to resolve (e.g., '/about', '/blog/post-1')\n */\n path: string;\n\n /**\n * If true, fetches draft/unpublished content instead of published content.\n * @default false\n */\n preview?: boolean;\n};\n\n/**\n * Resolve a URL path to page data, redirect, or 404\n *\n * This helper attempts to fetch the page at the given path. If the page\n * is not found, it checks for configured redirects before returning 404.\n *\n * Returns a discriminated union indicating:\n * - `page`: Page was found, includes full page data\n * - `redirect`: Path has a redirect rule configured\n * - `not-found`: No page or redirect exists for this path\n *\n * @example\n * ```tsx\n * import { resolveRoute } from '@riverbankcms/sdk/routing';\n * import { notFound, redirect } from 'next/navigation';\n *\n * export default async function DynamicPage({ params }) {\n * const path = `/${params.slug?.join('/') || ''}`;\n * const resolution = await resolveRoute({\n * client,\n * siteId: 'your-site-id',\n * path,\n * });\n *\n * if (resolution.type === 'redirect') {\n * redirect(resolution.destination);\n * }\n *\n * if (resolution.type === 'not-found') {\n * notFound();\n * }\n *\n * // resolution.type === 'page'\n * return <Page {...resolution.pageData} />;\n * }\n * ```\n */\nexport async function resolveRoute(\n params: ResolveRouteParams\n): Promise<RouteResolution> {\n const { client, siteId, path, preview = false } = params;\n\n try {\n // Attempt to fetch page data\n const pageResponse = await client.getPage({\n siteId,\n path,\n preview,\n });\n\n if (pageResponse) {\n // Successfully found page - load full page data\n const { loadPage } = await import('../rendering/helpers/loadPage');\n const pageData = await loadPage({\n client,\n siteId,\n path,\n preview,\n });\n\n return {\n type: 'page',\n pageData,\n };\n }\n } catch (error) {\n // Distinguish between expected 404s and unexpected errors\n const is404 = error instanceof Error &&\n (error.message.includes('404') ||\n error.message.includes('Not Found') ||\n error.message.includes('not found'));\n\n if (is404) {\n console.debug('[resolveRoute] Page not found', { path });\n } else {\n // Unexpected error - log as warning for visibility\n console.warn('[resolveRoute] Failed to fetch page', {\n path,\n error: error instanceof Error ? error.message : String(error),\n });\n }\n }\n\n // No page found - check for redirect before returning 404\n try {\n const redirectResult = await checkRedirectForPath({\n client,\n siteId,\n path,\n });\n\n if (redirectResult.hasRedirect) {\n return {\n type: 'redirect',\n destination: redirectResult.redirect.destination,\n permanent: redirectResult.redirect.permanent,\n };\n }\n } catch (redirectError) {\n console.debug('[resolveRoute] Redirect check failed', {\n path,\n error: redirectError instanceof Error ? redirectError.message : String(redirectError),\n });\n }\n\n return {\n type: 'not-found',\n };\n}\n\n/**\n * Batch resolve multiple routes in parallel\n *\n * Useful for pre-fetching multiple routes or validating a sitemap.\n *\n * @example\n * ```tsx\n * const resolutions = await resolveRoutes({\n * client,\n * siteId: 'your-site-id',\n * paths: ['/', '/about', '/blog', '/contact'],\n * });\n *\n * resolutions.forEach(({ path, resolution }) => {\n * if (resolution.type === 'page') {\n * console.log(`${path} → Page: ${resolution.pageData.page.name}`);\n * } else if (resolution.type === 'redirect') {\n * console.log(`${path} → Redirect to ${resolution.destination}`);\n * } else {\n * console.log(`${path} → Not found`);\n * }\n * });\n * ```\n */\nexport async function resolveRoutes(params: {\n client: RiverbankClient;\n siteId: string;\n paths: string[];\n preview?: boolean;\n}): Promise<Array<{ path: string; resolution: RouteResolution }>> {\n const { client, siteId, paths, preview } = params;\n\n const resolutions = await Promise.all(\n paths.map(async (path) => {\n const resolution = await resolveRoute({\n client,\n siteId,\n path,\n preview,\n });\n\n return { path, resolution };\n })\n );\n\n return resolutions;\n}\n","/**\n * Content route matching utilities.\n *\n * Derive route matching from SDK config to determine whether a URL path\n * is a CMS page or a content entry, without duplicating route patterns.\n */\n\nimport type { RiverbankSiteConfig, ContentTypeConfig } from '../config';\n\n/**\n * Extract the first path segment from each routable content type's routePattern.\n *\n * Useful for simple prefix-based routing decisions. For more precise matching\n * that handles nested patterns, use `isContentEntryPath` instead.\n *\n * @param config - The SDK config object from defineConfig()\n * @returns Array of first path segments from content type routePatterns\n *\n * @example\n * ```typescript\n * import { getContentEntryPrefixes } from '@riverbankcms/sdk/routing';\n * import config from '../riverbank.config';\n *\n * // Config with routePatterns: '/blog/{slug}', '/work/projects/{slug}'\n * const prefixes = getContentEntryPrefixes(config);\n * // Returns ['blog', 'work']\n * ```\n */\nexport function getContentEntryPrefixes(config: RiverbankSiteConfig): string[] {\n const contentTypes = config.content?.contentTypes ?? [];\n\n return contentTypes\n .filter((ct): ct is ContentTypeConfig & { routePattern: string } =>\n ct.hasPages && typeof ct.routePattern === 'string'\n )\n .map(ct => {\n // '/blog/{slug}' → 'blog'\n // '/work/projects/{slug}' → 'work'\n // '/{slug}' → undefined (no static prefix)\n const match = ct.routePattern.match(/^\\/([^/]+)/);\n const segment = match?.[1];\n // Skip dynamic segments (those containing {})\n if (segment?.includes('{')) return undefined;\n return segment;\n })\n .filter((prefix): prefix is string => typeof prefix === 'string');\n}\n\n/**\n * Result of checking if a path matches a content entry route.\n */\nexport type ContentEntryMatch = {\n /** Whether the path matches a content entry route pattern */\n isEntry: boolean;\n /** The content type key if matched (e.g., 'blog-post') */\n contentType?: string;\n /** The slug extracted from the path if matched */\n slug?: string;\n};\n\n/**\n * Check if a URL path matches any content entry route pattern.\n *\n * Supports nested patterns like '/work/projects/{slug}'. Returns the matched\n * content type key and extracted slug, useful for routing decisions.\n *\n * **Note:** Content types are checked in array order. If multiple patterns\n * could match a path, the first matching content type wins. Order more specific\n * patterns before generic ones in your config.\n *\n * @param config - The SDK config object from defineConfig()\n * @param path - URL path as string ('/blog/my-post') or segments ['blog', 'my-post']\n * @returns Match result with content type and slug if matched\n *\n * @example\n * ```typescript\n * import { isContentEntryPath } from '@riverbankcms/sdk/routing';\n * import config from '../riverbank.config';\n *\n * // Simple pattern: '/blog/{slug}'\n * isContentEntryPath(config, '/blog/my-post');\n * // Returns { isEntry: true, contentType: 'blog-post', slug: 'my-post' }\n *\n * // Nested pattern: '/work/projects/{slug}'\n * isContentEntryPath(config, '/work/projects/website-redesign');\n * // Returns { isEntry: true, contentType: 'project', slug: 'website-redesign' }\n *\n * // Non-matching path\n * isContentEntryPath(config, '/about');\n * // Returns { isEntry: false }\n * ```\n */\nexport function isContentEntryPath(\n config: RiverbankSiteConfig,\n path: string | string[]\n): ContentEntryMatch {\n const segments = typeof path === 'string'\n ? path.split('/').filter(Boolean)\n : path;\n\n const contentTypes = config.content?.contentTypes ?? [];\n\n for (const ct of contentTypes) {\n if (!ct.hasPages || !ct.routePattern) continue;\n\n // Parse pattern: '/blog/{slug}' → ['blog'], '/work/projects/{slug}' → ['work', 'projects']\n const patternSegments = parseRoutePattern(ct.routePattern);\n\n // Check if path segments match the pattern\n const match = matchPattern(segments, patternSegments);\n if (match.matches) {\n return {\n isEntry: true,\n contentType: ct.key,\n slug: match.slug,\n };\n }\n }\n\n return { isEntry: false };\n}\n\n/**\n * Parse a route pattern into static segments before {slug}.\n *\n * @example\n * parseRoutePattern('/blog/{slug}') // ['blog']\n * parseRoutePattern('/work/projects/{slug}') // ['work', 'projects']\n * parseRoutePattern('/{slug}') // []\n */\nfunction parseRoutePattern(pattern: string): string[] {\n const segments = pattern.split('/').filter(Boolean);\n const staticSegments: string[] = [];\n\n for (const segment of segments) {\n if (segment.includes('{')) break; // Stop at dynamic segment\n staticSegments.push(segment);\n }\n\n return staticSegments;\n}\n\n/**\n * Check if path segments match pattern segments.\n * Path must have at least one more segment than pattern (the slug).\n *\n * @returns Match result with extracted slug if matched\n */\nfunction matchPattern(\n pathSegments: string[],\n patternSegments: string[]\n): { matches: boolean; slug?: string } {\n // Path needs: pattern segments + at least 1 slug segment\n if (pathSegments.length < patternSegments.length + 1) {\n return { matches: false };\n }\n\n // All pattern segments must match exactly\n for (let i = 0; i < patternSegments.length; i++) {\n if (pathSegments[i] !== patternSegments[i]) {\n return { matches: false };\n }\n }\n\n // Extract slug (everything after the pattern segments)\n // For '/blog/{slug}' with path '/blog/my-post' → slug = 'my-post'\n // For nested slugs, join remaining segments (rare but supported)\n const slugSegments = pathSegments.slice(patternSegments.length);\n const slug = slugSegments.join('/');\n\n return { matches: true, slug };\n}\n","/**\n * Event occurrence resolution for dynamic URLs\n *\n * Resolves URL segments like \"2025-01-15\" or \"abc123-uuid\" to event occurrence data.\n */\n\nimport type { RiverbankClient, ResolveEventOccurrenceResponse } from '../client/types';\n\n/**\n * Data for a resolved event occurrence\n */\nexport type OccurrenceData = NonNullable<ResolveEventOccurrenceResponse['occurrence']>;\n\n/**\n * Result of occurrence resolution\n */\nexport type OccurrenceResolution =\n | {\n found: true;\n occurrence: OccurrenceData;\n }\n | {\n found: false;\n occurrence: null;\n };\n\n/**\n * Parameters for resolving an event occurrence\n */\nexport type ResolveOccurrenceParams = {\n /**\n * SDK client instance\n */\n client: RiverbankClient;\n\n /**\n * Site ID\n */\n siteId: string;\n\n /**\n * Parent content entry ID (the event entry)\n */\n entryId: string;\n\n /**\n * URL segment to resolve - either a date (YYYY-MM-DD) or UUID\n */\n segment: string;\n};\n\n/**\n * Check if a string is a valid ISO date (YYYY-MM-DD)\n */\nfunction isISODateString(str: string): boolean {\n return /^\\d{4}-\\d{2}-\\d{2}$/.test(str);\n}\n\n/**\n * Check if a string is a valid UUID\n */\nfunction isUUID(str: string): boolean {\n return /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(str);\n}\n\n/**\n * Validate URL segment format before making API call\n */\nfunction isValidSegment(segment: string): boolean {\n return isISODateString(segment) || isUUID(segment);\n}\n\n/**\n * Resolve an event occurrence from a URL segment\n *\n * Supports two segment formats:\n * - Date format: \"2025-01-15\" → finds occurrence starting on that date\n * - UUID format: \"abc123...\" → finds occurrence by ID\n *\n * @example\n * ```tsx\n * import { resolveOccurrence } from '@riverbankcms/sdk/routing';\n *\n * // In a Next.js dynamic route: /events/[slug]/[occurrence]\n * export default async function EventOccurrencePage({ params }) {\n * const result = await resolveOccurrence({\n * client,\n * siteId: 'your-site-id',\n * entryId: eventEntry.id,\n * segment: params.occurrence, // e.g., \"2025-01-15\"\n * });\n *\n * if (!result.found) {\n * notFound();\n * }\n *\n * return <EventOccurrence occurrence={result.occurrence} />;\n * }\n * ```\n */\nexport async function resolveOccurrence(\n params: ResolveOccurrenceParams\n): Promise<OccurrenceResolution> {\n const { client, siteId, entryId, segment } = params;\n\n // Validate segment format client-side to avoid unnecessary API calls\n if (!isValidSegment(segment)) {\n console.debug('[resolveOccurrence] Invalid segment format', { segment });\n return { found: false, occurrence: null };\n }\n\n try {\n const response = await client.resolveEventOccurrence({\n siteId,\n entryId,\n segment,\n });\n\n if (response.occurrence) {\n return {\n found: true,\n occurrence: response.occurrence,\n };\n }\n\n return { found: false, occurrence: null };\n } catch (error) {\n console.warn('[resolveOccurrence] Failed to resolve occurrence', {\n siteId,\n entryId,\n segment,\n error: error instanceof Error ? error.message : String(error),\n });\n\n return { found: false, occurrence: null };\n }\n}\n\nexport { isISODateString, isUUID, isValidSegment };\n"]}
@@ -1,3 +1,5 @@
1
+ import "./chunk-NFEGQTCC.mjs";
2
+
1
3
  // src/routing/checkRedirect.ts
2
4
  async function checkRedirectForPath(params) {
3
5
  const { client, siteId, path } = params;
@@ -37,7 +39,7 @@ async function resolveRoute(params) {
37
39
  preview
38
40
  });
39
41
  if (pageResponse) {
40
- const { loadPage } = await import("./loadPage-JI2SML4M.mjs");
42
+ const { loadPage } = await import("./loadPage-XR7ORQ2E.mjs");
41
43
  const pageData = await loadPage({
42
44
  client,
43
45
  siteId,