@riverbankcms/sdk 0.4.1 → 0.4.2
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.
- package/README.md +73 -0
- package/dist/cli/index.js +46 -5
- package/dist/cli/index.js.map +1 -1
- package/dist/client/client.d.mts +3 -3
- package/dist/client/client.d.ts +3 -3
- package/dist/client/client.js +80 -170
- package/dist/client/client.js.map +1 -1
- package/dist/client/client.mjs +80 -170
- package/dist/client/client.mjs.map +1 -1
- package/dist/client/hooks.d.mts +3 -3
- package/dist/client/hooks.d.ts +3 -3
- package/dist/client/rendering/client.d.mts +1 -1
- package/dist/client/rendering/client.d.ts +1 -1
- package/dist/client/rendering/client.js +80 -142
- package/dist/client/rendering/client.js.map +1 -1
- package/dist/client/rendering/client.mjs +80 -142
- package/dist/client/rendering/client.mjs.map +1 -1
- package/dist/client/resolver-CYyfzTQ9.d.mts +61 -0
- package/dist/client/resolver-CYyfzTQ9.d.ts +61 -0
- package/dist/client/usePage-BC8Q2E3t.d.mts +6431 -0
- package/dist/client/usePage-CHEybPMD.d.ts +6429 -0
- package/dist/client/usePage-D4fxZbRR.d.mts +6429 -0
- package/dist/client/usePage-DpRNZUtP.d.ts +6431 -0
- package/dist/server/{Layout-B_zUr9ci.d.mts → Layout-ByUnm35V.d.mts} +1 -1
- package/dist/server/{Layout-CHG77dhK.d.ts → Layout-kRv5sU81.d.ts} +1 -1
- package/dist/server/{chunk-ZIM53VP6.js → chunk-6JBKKV3G.js} +27 -4
- package/dist/server/chunk-6JBKKV3G.js.map +1 -0
- package/dist/server/{chunk-SFQ7VF3G.mjs → chunk-7BOIO2S7.mjs} +7 -5
- package/dist/server/{chunk-SFQ7VF3G.mjs.map → chunk-7BOIO2S7.mjs.map} +1 -1
- package/dist/server/{chunk-P6CDRJN3.js → chunk-BLKVTULP.js} +13 -11
- package/dist/server/chunk-BLKVTULP.js.map +1 -0
- package/dist/server/{chunk-BOHTTHY5.mjs → chunk-I6K5REFT.mjs} +27 -4
- package/dist/server/chunk-I6K5REFT.mjs.map +1 -0
- package/dist/server/{chunk-BUCJWG6G.js → chunk-NW5KHH4A.js} +5 -5
- package/dist/server/{chunk-BUCJWG6G.js.map → chunk-NW5KHH4A.js.map} +1 -1
- package/dist/server/{chunk-XK2YIISA.mjs → chunk-SPXMMX3C.mjs} +2 -2
- package/dist/server/{chunk-IT5ICP43.js → chunk-TKMA6D6U.js} +139 -229
- package/dist/server/chunk-TKMA6D6U.js.map +1 -0
- package/dist/server/{chunk-NKXS4TBK.mjs → chunk-TNRADRPH.mjs} +79 -169
- package/dist/server/chunk-TNRADRPH.mjs.map +1 -0
- package/dist/server/{components-Bo3LPpVb.d.mts → components-CY8jDQjv.d.mts} +20 -12
- package/dist/server/{components-ClFs4PUa.d.ts → components-D1Z2mSDr.d.ts} +20 -12
- package/dist/server/components.d.mts +5 -5
- package/dist/server/components.d.ts +5 -5
- package/dist/server/components.js +3 -3
- package/dist/server/components.mjs +2 -2
- package/dist/server/config-validation.d.mts +2 -2
- package/dist/server/config-validation.d.ts +2 -2
- package/dist/server/config-validation.js +3 -3
- package/dist/server/config-validation.mjs +2 -2
- package/dist/server/config.d.mts +3 -3
- package/dist/server/config.d.ts +3 -3
- package/dist/server/config.js +3 -3
- package/dist/server/config.mjs +2 -2
- package/dist/server/data.d.mts +2 -2
- package/dist/server/data.d.ts +2 -2
- package/dist/server/{index-DbSfrRA0.d.ts → index-DCIz9Ptv.d.ts} +1 -1
- package/dist/server/{index-Dj7VKH34.d.mts → index-DFQwtj3J.d.mts} +1 -1
- package/dist/server/index.d.mts +4 -4
- package/dist/server/index.d.ts +4 -4
- package/dist/server/{loadContent-C_FipaAC.d.mts → loadContent-CWuE8FCx.d.mts} +3 -3
- package/dist/server/{loadContent-C2SwqmXy.d.ts → loadContent-DynBuR5f.d.ts} +3 -3
- package/dist/server/{loadPage-mavT3Jae.d.mts → loadPage-B8RmlYgV.d.mts} +60 -27
- package/dist/server/{loadPage-naVvoua8.d.ts → loadPage-BTkKpizX.d.ts} +60 -27
- package/dist/server/metadata.d.mts +3 -3
- package/dist/server/metadata.d.ts +3 -3
- package/dist/server/navigation.d.mts +2 -2
- package/dist/server/navigation.d.ts +2 -2
- package/dist/server/rendering/server.d.mts +4 -4
- package/dist/server/rendering/server.d.ts +4 -4
- package/dist/server/rendering/server.js +4 -4
- package/dist/server/rendering/server.mjs +3 -3
- package/dist/server/rendering.d.mts +7 -7
- package/dist/server/rendering.d.ts +7 -7
- package/dist/server/rendering.js +4 -4
- package/dist/server/rendering.mjs +3 -3
- package/dist/server/routing.d.mts +78 -5
- package/dist/server/routing.d.ts +78 -5
- package/dist/server/routing.js +56 -2
- package/dist/server/routing.js.map +1 -1
- package/dist/server/routing.mjs +54 -0
- package/dist/server/routing.mjs.map +1 -1
- package/dist/server/server.d.mts +5 -5
- package/dist/server/server.d.ts +5 -5
- package/dist/server/theme-bridge.js +7 -7
- package/dist/server/theme-bridge.mjs +1 -1
- package/dist/server/{types-5XdVD2J1.d.ts → types-BiRZnxDx.d.ts} +17 -6
- package/dist/server/{types-CMqVHYLG.d.ts → types-CL916r6x.d.ts} +23 -1
- package/dist/server/{types-BA-J9K8r.d.mts → types-CdrJqlKx.d.mts} +17 -6
- package/dist/server/{types-BC9eB2KH.d.mts → types-DkKEctWn.d.mts} +1 -1
- package/dist/server/{types-CAnC529E.d.ts → types-oCM-fw4O.d.ts} +1 -1
- package/dist/server/{types-CYfHxUhe.d.mts → types-txWsSxN7.d.mts} +23 -1
- package/dist/server/{validation-C7W2Fe0i.d.ts → validation-CoU8uAiu.d.ts} +1 -1
- package/dist/server/{validation-hg1sqhrt.d.mts → validation-DzvDwwRo.d.mts} +1 -1
- package/package.json +1 -1
- package/dist/server/chunk-BOHTTHY5.mjs.map +0 -1
- package/dist/server/chunk-IT5ICP43.js.map +0 -1
- package/dist/server/chunk-NKXS4TBK.mjs.map +0 -1
- package/dist/server/chunk-P6CDRJN3.js.map +0 -1
- package/dist/server/chunk-ZIM53VP6.js.map +0 -1
- /package/dist/server/{chunk-XK2YIISA.mjs.map → chunk-SPXMMX3C.mjs.map} +0 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["/Users/will/Projects/Business/cms/builder/packages/sdk/dist/server/routing.js","../../src/routing/resolveRoute.ts"],"names":[],"mappings":"AAAA,0XAA4B;AAC5B;AACA;ACgFA,MAAA,SAAsB,YAAA,CACpB,MAAA,EAC0B;AAC1B,EAAA,MAAM,EAAE,MAAA,EAAQ,MAAA,EAAQ,IAAA,EAAM,QAAA,EAAU,MAAM,EAAA,EAAI,MAAA;AAElD,EAAA,IAAI;AAEF,IAAA,MAAM,aAAA,EAAe,MAAM,MAAA,CAAO,OAAA,CAAQ;AAAA,MACxC,MAAA;AAAA,MACA,IAAA;AAAA,MACA;AAAA,IACF,CAAC,CAAA;AAED,IAAA,GAAA,CAAI,YAAA,EAAc;AAEhB,MAAA,MAAM,EAAE,SAAS,EAAA,EAAI,MAAM,4DAAA,CAAO,wBAA+B,GAAA;AACjE,MAAA,MAAM,SAAA,EAAW,MAAM,QAAA,CAAS;AAAA,QAC9B,MAAA;AAAA,QACA,MAAA;AAAA,QACA,IAAA;AAAA,QACA;AAAA,MACF,CAAC,CAAA;AAED,MAAA,OAAO;AAAA,QACL,IAAA,EAAM,MAAA;AAAA,QACN;AAAA,MACF,CAAA;AAAA,IACF;AAAA,EACF,EAAA,MAAA,CAAS,KAAA,EAAO;AAEd,IAAA,MAAM,MAAA,EAAQ,MAAA,WAAiB,MAAA,GAAA,CAC5B,KAAA,CAAM,OAAA,CAAQ,QAAA,CAAS,KAAK,EAAA,GAC5B,KAAA,CAAM,OAAA,CAAQ,QAAA,CAAS,WAAW,EAAA,GAClC,KAAA,CAAM,OAAA,CAAQ,QAAA,CAAS,WAAW,CAAA,CAAA;AAErC,IAAA,GAAA,CAAI,KAAA,EAAO;AACT,MAAA,OAAA,CAAQ,KAAA,CAAM,+BAAA,EAAiC,EAAE,KAAK,CAAC,CAAA;AAAA,IACzD,EAAA,KAAO;AAEL,MAAA,OAAA,CAAQ,IAAA,CAAK,qCAAA,EAAuC;AAAA,QAClD,IAAA;AAAA,QACA,KAAA,EAAO,MAAA,WAAiB,MAAA,EAAQ,KAAA,CAAM,QAAA,EAAU,MAAA,CAAO,KAAK;AAAA,MAC9D,CAAC,CAAA;AAAA,IACH;AAAA,EACF;AAIA,EAAA,OAAO;AAAA,IACL,IAAA,EAAM;AAAA,EACR,CAAA;AACF;AA0BA,MAAA,SAAsB,aAAA,CAAc,MAAA,EAK8B;AAChE,EAAA,MAAM,EAAE,MAAA,EAAQ,MAAA,EAAQ,KAAA,EAAO,QAAQ,EAAA,EAAI,MAAA;AAE3C,EAAA,MAAM,YAAA,EAAc,MAAM,OAAA,CAAQ,GAAA;AAAA,IAChC,KAAA,CAAM,GAAA,CAAI,MAAA,CAAO,IAAA,EAAA,GAAS;AACxB,MAAA,MAAM,WAAA,EAAa,MAAM,YAAA,CAAa;AAAA,QACpC,MAAA;AAAA,QACA,MAAA;AAAA,QACA,IAAA;AAAA,QACA;AAAA,MACF,CAAC,CAAA;AAED,MAAA,OAAO,EAAE,IAAA,EAAM,WAAW,CAAA;AAAA,IAC5B,CAAC;AAAA,EACH,CAAA;AAEA,EAAA,OAAO,WAAA;AACT;AD/HA;AACE;AACA;AACF,2EAAC","file":"/Users/will/Projects/Business/cms/builder/packages/sdk/dist/server/routing.js","sourcesContent":[null,"/**\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';\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 and returns\n * a discriminated union indicating whether the page was found or not.\n *\n * **Note:** Redirect support is not yet implemented. The `redirect` type\n * exists in the return type for future compatibility, but this function\n * currently only returns `page` or `not-found` types.\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 - return not-found\n // Note: Redirect support will be added when client.getRedirect() API is available\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"]}
|
|
1
|
+
{"version":3,"sources":["/Users/will/Projects/Business/cms/builder/packages/sdk/dist/server/routing.js","../../src/routing/resolveRoute.ts","../../src/routing/contentRoutes.ts"],"names":[],"mappings":"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","file":"/Users/will/Projects/Business/cms/builder/packages/sdk/dist/server/routing.js","sourcesContent":[null,"/**\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';\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 and returns\n * a discriminated union indicating whether the page was found or not.\n *\n * **Note:** Redirect support is not yet implemented. The `redirect` type\n * exists in the return type for future compatibility, but this function\n * currently only returns `page` or `not-found` types.\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 - return not-found\n // Note: Redirect support will be added when client.getRedirect() API is available\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"]}
|
package/dist/server/routing.mjs
CHANGED
|
@@ -52,7 +52,61 @@ async function resolveRoutes(params) {
|
|
|
52
52
|
);
|
|
53
53
|
return resolutions;
|
|
54
54
|
}
|
|
55
|
+
|
|
56
|
+
// src/routing/contentRoutes.ts
|
|
57
|
+
function getContentEntryPrefixes(config) {
|
|
58
|
+
const contentTypes = config.content?.contentTypes ?? [];
|
|
59
|
+
return contentTypes.filter(
|
|
60
|
+
(ct) => ct.hasPages && typeof ct.routePattern === "string"
|
|
61
|
+
).map((ct) => {
|
|
62
|
+
const match = ct.routePattern.match(/^\/([^/]+)/);
|
|
63
|
+
const segment = match?.[1];
|
|
64
|
+
if (segment?.includes("{")) return void 0;
|
|
65
|
+
return segment;
|
|
66
|
+
}).filter((prefix) => typeof prefix === "string");
|
|
67
|
+
}
|
|
68
|
+
function isContentEntryPath(config, path) {
|
|
69
|
+
const segments = typeof path === "string" ? path.split("/").filter(Boolean) : path;
|
|
70
|
+
const contentTypes = config.content?.contentTypes ?? [];
|
|
71
|
+
for (const ct of contentTypes) {
|
|
72
|
+
if (!ct.hasPages || !ct.routePattern) continue;
|
|
73
|
+
const patternSegments = parseRoutePattern(ct.routePattern);
|
|
74
|
+
const match = matchPattern(segments, patternSegments);
|
|
75
|
+
if (match.matches) {
|
|
76
|
+
return {
|
|
77
|
+
isEntry: true,
|
|
78
|
+
contentType: ct.key,
|
|
79
|
+
slug: match.slug
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
return { isEntry: false };
|
|
84
|
+
}
|
|
85
|
+
function parseRoutePattern(pattern) {
|
|
86
|
+
const segments = pattern.split("/").filter(Boolean);
|
|
87
|
+
const staticSegments = [];
|
|
88
|
+
for (const segment of segments) {
|
|
89
|
+
if (segment.includes("{")) break;
|
|
90
|
+
staticSegments.push(segment);
|
|
91
|
+
}
|
|
92
|
+
return staticSegments;
|
|
93
|
+
}
|
|
94
|
+
function matchPattern(pathSegments, patternSegments) {
|
|
95
|
+
if (pathSegments.length < patternSegments.length + 1) {
|
|
96
|
+
return { matches: false };
|
|
97
|
+
}
|
|
98
|
+
for (let i = 0; i < patternSegments.length; i++) {
|
|
99
|
+
if (pathSegments[i] !== patternSegments[i]) {
|
|
100
|
+
return { matches: false };
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
const slugSegments = pathSegments.slice(patternSegments.length);
|
|
104
|
+
const slug = slugSegments.join("/");
|
|
105
|
+
return { matches: true, slug };
|
|
106
|
+
}
|
|
55
107
|
export {
|
|
108
|
+
getContentEntryPrefixes,
|
|
109
|
+
isContentEntryPath,
|
|
56
110
|
resolveRoute,
|
|
57
111
|
resolveRoutes
|
|
58
112
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/routing/resolveRoute.ts"],"sourcesContent":["/**\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';\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 and returns\n * a discriminated union indicating whether the page was found or not.\n *\n * **Note:** Redirect support is not yet implemented. The `redirect` type\n * exists in the return type for future compatibility, but this function\n * currently only returns `page` or `not-found` types.\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 - return not-found\n // Note: Redirect support will be added when client.getRedirect() API is available\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"],"mappings":";;;AAkFA,eAAsB,aACpB,QAC0B;AAC1B,QAAM,EAAE,QAAQ,QAAQ,MAAM,UAAU,MAAM,IAAI;AAElD,MAAI;AAEF,UAAM,eAAe,MAAM,OAAO,QAAQ;AAAA,MACxC;AAAA,MACA;AAAA,MACA;AAAA,IACF,CAAC;AAED,QAAI,cAAc;AAEhB,YAAM,EAAE,SAAS,IAAI,MAAM,OAAO,yBAA+B;AACjE,YAAM,WAAW,MAAM,SAAS;AAAA,QAC9B;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MACF,CAAC;AAED,aAAO;AAAA,QACL,MAAM;AAAA,QACN;AAAA,MACF;AAAA,IACF;AAAA,EACF,SAAS,OAAO;AAEd,UAAM,QAAQ,iBAAiB,UAC5B,MAAM,QAAQ,SAAS,KAAK,KAC5B,MAAM,QAAQ,SAAS,WAAW,KAClC,MAAM,QAAQ,SAAS,WAAW;AAErC,QAAI,OAAO;AACT,cAAQ,MAAM,iCAAiC,EAAE,KAAK,CAAC;AAAA,IACzD,OAAO;AAEL,cAAQ,KAAK,uCAAuC;AAAA,QAClD;AAAA,QACA,OAAO,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;AAAA,MAC9D,CAAC;AAAA,IACH;AAAA,EACF;AAIA,SAAO;AAAA,IACL,MAAM;AAAA,EACR;AACF;AA0BA,eAAsB,cAAc,QAK8B;AAChE,QAAM,EAAE,QAAQ,QAAQ,OAAO,QAAQ,IAAI;AAE3C,QAAM,cAAc,MAAM,QAAQ;AAAA,IAChC,MAAM,IAAI,OAAO,SAAS;AACxB,YAAM,aAAa,MAAM,aAAa;AAAA,QACpC;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MACF,CAAC;AAED,aAAO,EAAE,MAAM,WAAW;AAAA,IAC5B,CAAC;AAAA,EACH;AAEA,SAAO;AACT;","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../src/routing/resolveRoute.ts","../../src/routing/contentRoutes.ts"],"sourcesContent":["/**\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';\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 and returns\n * a discriminated union indicating whether the page was found or not.\n *\n * **Note:** Redirect support is not yet implemented. The `redirect` type\n * exists in the return type for future compatibility, but this function\n * currently only returns `page` or `not-found` types.\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 - return not-found\n // Note: Redirect support will be added when client.getRedirect() API is available\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"],"mappings":";;;AAkFA,eAAsB,aACpB,QAC0B;AAC1B,QAAM,EAAE,QAAQ,QAAQ,MAAM,UAAU,MAAM,IAAI;AAElD,MAAI;AAEF,UAAM,eAAe,MAAM,OAAO,QAAQ;AAAA,MACxC;AAAA,MACA;AAAA,MACA;AAAA,IACF,CAAC;AAED,QAAI,cAAc;AAEhB,YAAM,EAAE,SAAS,IAAI,MAAM,OAAO,yBAA+B;AACjE,YAAM,WAAW,MAAM,SAAS;AAAA,QAC9B;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MACF,CAAC;AAED,aAAO;AAAA,QACL,MAAM;AAAA,QACN;AAAA,MACF;AAAA,IACF;AAAA,EACF,SAAS,OAAO;AAEd,UAAM,QAAQ,iBAAiB,UAC5B,MAAM,QAAQ,SAAS,KAAK,KAC5B,MAAM,QAAQ,SAAS,WAAW,KAClC,MAAM,QAAQ,SAAS,WAAW;AAErC,QAAI,OAAO;AACT,cAAQ,MAAM,iCAAiC,EAAE,KAAK,CAAC;AAAA,IACzD,OAAO;AAEL,cAAQ,KAAK,uCAAuC;AAAA,QAClD;AAAA,QACA,OAAO,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;AAAA,MAC9D,CAAC;AAAA,IACH;AAAA,EACF;AAIA,SAAO;AAAA,IACL,MAAM;AAAA,EACR;AACF;AA0BA,eAAsB,cAAc,QAK8B;AAChE,QAAM,EAAE,QAAQ,QAAQ,OAAO,QAAQ,IAAI;AAE3C,QAAM,cAAc,MAAM,QAAQ;AAAA,IAChC,MAAM,IAAI,OAAO,SAAS;AACxB,YAAM,aAAa,MAAM,aAAa;AAAA,QACpC;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MACF,CAAC;AAED,aAAO,EAAE,MAAM,WAAW;AAAA,IAC5B,CAAC;AAAA,EACH;AAEA,SAAO;AACT;;;ACzJO,SAAS,wBAAwB,QAAuC;AAC7E,QAAM,eAAe,OAAO,SAAS,gBAAgB,CAAC;AAEtD,SAAO,aACJ;AAAA,IAAO,CAAC,OACP,GAAG,YAAY,OAAO,GAAG,iBAAiB;AAAA,EAC5C,EACC,IAAI,QAAM;AAIT,UAAM,QAAQ,GAAG,aAAa,MAAM,YAAY;AAChD,UAAM,UAAU,QAAQ,CAAC;AAEzB,QAAI,SAAS,SAAS,GAAG,EAAG,QAAO;AACnC,WAAO;AAAA,EACT,CAAC,EACA,OAAO,CAAC,WAA6B,OAAO,WAAW,QAAQ;AACpE;AA8CO,SAAS,mBACd,QACA,MACmB;AACnB,QAAM,WAAW,OAAO,SAAS,WAC7B,KAAK,MAAM,GAAG,EAAE,OAAO,OAAO,IAC9B;AAEJ,QAAM,eAAe,OAAO,SAAS,gBAAgB,CAAC;AAEtD,aAAW,MAAM,cAAc;AAC7B,QAAI,CAAC,GAAG,YAAY,CAAC,GAAG,aAAc;AAGtC,UAAM,kBAAkB,kBAAkB,GAAG,YAAY;AAGzD,UAAM,QAAQ,aAAa,UAAU,eAAe;AACpD,QAAI,MAAM,SAAS;AACjB,aAAO;AAAA,QACL,SAAS;AAAA,QACT,aAAa,GAAG;AAAA,QAChB,MAAM,MAAM;AAAA,MACd;AAAA,IACF;AAAA,EACF;AAEA,SAAO,EAAE,SAAS,MAAM;AAC1B;AAUA,SAAS,kBAAkB,SAA2B;AACpD,QAAM,WAAW,QAAQ,MAAM,GAAG,EAAE,OAAO,OAAO;AAClD,QAAM,iBAA2B,CAAC;AAElC,aAAW,WAAW,UAAU;AAC9B,QAAI,QAAQ,SAAS,GAAG,EAAG;AAC3B,mBAAe,KAAK,OAAO;AAAA,EAC7B;AAEA,SAAO;AACT;AAQA,SAAS,aACP,cACA,iBACqC;AAErC,MAAI,aAAa,SAAS,gBAAgB,SAAS,GAAG;AACpD,WAAO,EAAE,SAAS,MAAM;AAAA,EAC1B;AAGA,WAAS,IAAI,GAAG,IAAI,gBAAgB,QAAQ,KAAK;AAC/C,QAAI,aAAa,CAAC,MAAM,gBAAgB,CAAC,GAAG;AAC1C,aAAO,EAAE,SAAS,MAAM;AAAA,IAC1B;AAAA,EACF;AAKA,QAAM,eAAe,aAAa,MAAM,gBAAgB,MAAM;AAC9D,QAAM,OAAO,aAAa,KAAK,GAAG;AAElC,SAAO,EAAE,SAAS,MAAM,KAAK;AAC/B;","names":[]}
|
package/dist/server/server.d.mts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
export { c as createRiverbankClient } from './index-
|
|
2
|
-
export { E as EntriesResponse, b as EntryResponse, P as PageResponse, R as RiverbankClient, a as RiverbankClientConfig, S as SiteResponse } from './types-
|
|
3
|
-
export { L as LoadPageParams, a as LoadPageResult, R as RuntimeSdkConfig, l as loadPage } from './loadPage-
|
|
4
|
-
export { C as ContentEntryData, E as EntryContentResult, L as LoadContentParams, b as LoadContentResult, P as PageContentResult, a as isEntryContent, i as isPageContent, l as loadContent } from './loadContent-
|
|
5
|
-
import './types-
|
|
1
|
+
export { c as createRiverbankClient } from './index-DFQwtj3J.mjs';
|
|
2
|
+
export { E as EntriesResponse, b as EntryResponse, P as PageResponse, R as RiverbankClient, a as RiverbankClientConfig, S as SiteResponse } from './types-DkKEctWn.mjs';
|
|
3
|
+
export { L as LoadPageParams, a as LoadPageResult, R as RuntimeSdkConfig, l as loadPage } from './loadPage-B8RmlYgV.mjs';
|
|
4
|
+
export { C as ContentEntryData, E as EntryContentResult, L as LoadContentParams, b as LoadContentResult, P as PageContentResult, a as isEntryContent, i as isPageContent, l as loadContent } from './loadContent-CWuE8FCx.mjs';
|
|
5
|
+
import './types-CdrJqlKx.mjs';
|
|
6
6
|
import '@riverbankcms/ai';
|
|
7
7
|
import './schema-Bpy9N5ZI.mjs';
|
|
8
8
|
import 'zod';
|
package/dist/server/server.d.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
export { c as createRiverbankClient } from './index-
|
|
2
|
-
export { E as EntriesResponse, b as EntryResponse, P as PageResponse, R as RiverbankClient, a as RiverbankClientConfig, S as SiteResponse } from './types-
|
|
3
|
-
export { L as LoadPageParams, a as LoadPageResult, R as RuntimeSdkConfig, l as loadPage } from './loadPage-
|
|
4
|
-
export { C as ContentEntryData, E as EntryContentResult, L as LoadContentParams, b as LoadContentResult, P as PageContentResult, a as isEntryContent, i as isPageContent, l as loadContent } from './loadContent-
|
|
5
|
-
import './types-
|
|
1
|
+
export { c as createRiverbankClient } from './index-DCIz9Ptv.js';
|
|
2
|
+
export { E as EntriesResponse, b as EntryResponse, P as PageResponse, R as RiverbankClient, a as RiverbankClientConfig, S as SiteResponse } from './types-oCM-fw4O.js';
|
|
3
|
+
export { L as LoadPageParams, a as LoadPageResult, R as RuntimeSdkConfig, l as loadPage } from './loadPage-BTkKpizX.js';
|
|
4
|
+
export { C as ContentEntryData, E as EntryContentResult, L as LoadContentParams, b as LoadContentResult, P as PageContentResult, a as isEntryContent, i as isPageContent, l as loadContent } from './loadContent-DynBuR5f.js';
|
|
5
|
+
import './types-BiRZnxDx.js';
|
|
6
6
|
import '@riverbankcms/ai';
|
|
7
7
|
import './schema-Bpy9N5ZI.js';
|
|
8
8
|
import 'zod';
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
|
|
6
6
|
|
|
7
7
|
|
|
8
|
-
var
|
|
8
|
+
var _chunkTKMA6D6Ujs = require('./chunk-TKMA6D6U.js');
|
|
9
9
|
require('./chunk-EGTDJ4PL.js');
|
|
10
10
|
require('./chunk-DGUM43GV.js');
|
|
11
11
|
|
|
@@ -147,7 +147,7 @@ ${cssLines}
|
|
|
147
147
|
}
|
|
148
148
|
function generateButtonsCss(config, corners, shadows) {
|
|
149
149
|
const buttonConfig = _optionalChain([config, 'access', _7 => _7.components, 'optionalAccess', _8 => _8.buttons]);
|
|
150
|
-
let variants =
|
|
150
|
+
let variants = _chunkTKMA6D6Ujs.getDefaultButtonVariants.call(void 0, );
|
|
151
151
|
if (typeof buttonConfig === "object" && buttonConfig.variants) {
|
|
152
152
|
const requestedIds = new Set(buttonConfig.variants);
|
|
153
153
|
variants = variants.filter((v) => requestedIds.has(v.id));
|
|
@@ -159,11 +159,11 @@ function generateButtonsCss(config, corners, shadows) {
|
|
|
159
159
|
fontWeight: 500,
|
|
160
160
|
textTransform: "none"
|
|
161
161
|
};
|
|
162
|
-
return
|
|
162
|
+
return _chunkTKMA6D6Ujs.generateButtonCoreCss.call(void 0, SCOPE_ID, variants, settings);
|
|
163
163
|
}
|
|
164
164
|
function generateCardsCss(config, corners, shadows) {
|
|
165
165
|
const cardConfig = _optionalChain([config, 'access', _9 => _9.components, 'optionalAccess', _10 => _10.cards]);
|
|
166
|
-
let variants =
|
|
166
|
+
let variants = _chunkTKMA6D6Ujs.getDefaultCardVariants.call(void 0, );
|
|
167
167
|
if (typeof cardConfig === "object" && cardConfig.variants) {
|
|
168
168
|
const requestedIds = new Set(cardConfig.variants);
|
|
169
169
|
variants = variants.filter((v) => requestedIds.has(v.id));
|
|
@@ -173,13 +173,13 @@ function generateCardsCss(config, corners, shadows) {
|
|
|
173
173
|
shadow: shadows,
|
|
174
174
|
borderWidth: "thin"
|
|
175
175
|
};
|
|
176
|
-
return
|
|
176
|
+
return _chunkTKMA6D6Ujs.generateCardCoreCss.call(void 0, SCOPE_ID, variants, settings);
|
|
177
177
|
}
|
|
178
178
|
function generateInputsCss(config, corners, shadows) {
|
|
179
|
-
const settings =
|
|
179
|
+
const settings = _chunkTKMA6D6Ujs.getDefaultInputSettings.call(void 0, );
|
|
180
180
|
settings.corners = corners;
|
|
181
181
|
settings.shadow = shadows === "none" ? "none" : "low";
|
|
182
|
-
return
|
|
182
|
+
return _chunkTKMA6D6Ujs.generateInputCoreCss.call(void 0, SCOPE_ID, settings);
|
|
183
183
|
}
|
|
184
184
|
function generateOverrideCss(overrides) {
|
|
185
185
|
const chunks = [];
|
|
@@ -2320,15 +2320,20 @@ type DevToolsSeedResponse = {
|
|
|
2320
2320
|
blocksCreated: number;
|
|
2321
2321
|
};
|
|
2322
2322
|
};
|
|
2323
|
+
type SiteInvitee = {
|
|
2324
|
+
email: string;
|
|
2325
|
+
role: 'editor' | 'administrator';
|
|
2326
|
+
};
|
|
2323
2327
|
type CreateManualSiteRequest = {
|
|
2324
2328
|
title: string;
|
|
2329
|
+
deploymentMode?: 'managed' | 'headless_full';
|
|
2325
2330
|
description?: string | null;
|
|
2326
|
-
themeKey
|
|
2327
|
-
contentTypes
|
|
2328
|
-
scaffoldHome
|
|
2329
|
-
scaffoldContact
|
|
2330
|
-
scaffoldBlog
|
|
2331
|
-
invitees?:
|
|
2331
|
+
themeKey?: string;
|
|
2332
|
+
contentTypes?: Array<'page' | 'post' | 'event' | 'testimonial'>;
|
|
2333
|
+
scaffoldHome?: boolean;
|
|
2334
|
+
scaffoldContact?: boolean;
|
|
2335
|
+
scaffoldBlog?: boolean;
|
|
2336
|
+
invitees?: SiteInvitee[];
|
|
2332
2337
|
};
|
|
2333
2338
|
type CreateManualSiteResponse = {
|
|
2334
2339
|
siteId: string;
|
|
@@ -2346,6 +2351,10 @@ type CreateManualSiteResponse = {
|
|
|
2346
2351
|
}>;
|
|
2347
2352
|
contentTypes: Array<'page' | 'post' | 'event' | 'testimonial'>;
|
|
2348
2353
|
};
|
|
2354
|
+
invitations?: {
|
|
2355
|
+
sent: number;
|
|
2356
|
+
failed: number;
|
|
2357
|
+
};
|
|
2349
2358
|
};
|
|
2350
2359
|
type APIEndpoints = {
|
|
2351
2360
|
aiContentUpdateChat: APIEndpoint<{
|
|
@@ -3921,6 +3930,7 @@ type ContentTypeSummary = {
|
|
|
3921
3930
|
hasPages: boolean;
|
|
3922
3931
|
routePattern: string | null;
|
|
3923
3932
|
isBuiltin: boolean;
|
|
3933
|
+
isSingleton: boolean;
|
|
3924
3934
|
createdAt: string;
|
|
3925
3935
|
updatedAt: string;
|
|
3926
3936
|
};
|
|
@@ -3934,6 +3944,7 @@ type CreateContentTypeRequest = {
|
|
|
3934
3944
|
description?: string | null;
|
|
3935
3945
|
schema: Record<string, unknown>;
|
|
3936
3946
|
titleField?: string | null;
|
|
3947
|
+
isSingleton?: boolean;
|
|
3937
3948
|
};
|
|
3938
3949
|
type UpdateContentTypeRequest = {
|
|
3939
3950
|
name?: string;
|
|
@@ -16,6 +16,7 @@ import { B as BlockKind } from './blockKinds-B6MWzNWp.js';
|
|
|
16
16
|
*
|
|
17
17
|
* @example
|
|
18
18
|
* ```typescript
|
|
19
|
+
* // Regular content type with multiple entries
|
|
19
20
|
* {
|
|
20
21
|
* key: 'testimonial',
|
|
21
22
|
* name: 'Testimonial',
|
|
@@ -25,6 +26,17 @@ import { B as BlockKind } from './blockKinds-B6MWzNWp.js';
|
|
|
25
26
|
* { id: 'author', type: 'text', label: 'Author', required: true },
|
|
26
27
|
* ],
|
|
27
28
|
* }
|
|
29
|
+
*
|
|
30
|
+
* // Singleton content type (only one entry allowed)
|
|
31
|
+
* {
|
|
32
|
+
* key: 'site-settings',
|
|
33
|
+
* name: 'Site Settings',
|
|
34
|
+
* isSingleton: true,
|
|
35
|
+
* hasPages: false,
|
|
36
|
+
* fields: [
|
|
37
|
+
* { id: 'contactEmail', type: 'text', label: 'Contact Email' },
|
|
38
|
+
* ],
|
|
39
|
+
* }
|
|
28
40
|
* ```
|
|
29
41
|
*/
|
|
30
42
|
interface ContentTypeConfig {
|
|
@@ -36,12 +48,22 @@ interface ContentTypeConfig {
|
|
|
36
48
|
description?: string;
|
|
37
49
|
/** Whether entries of this type have routable pages */
|
|
38
50
|
hasPages: boolean;
|
|
39
|
-
/**
|
|
51
|
+
/**
|
|
52
|
+
* Route pattern for routable content types. Required if hasPages=true.
|
|
53
|
+
* - For regular types: must include {slug} placeholder (e.g., '/blog/{slug}')
|
|
54
|
+
* - For singletons: can be a fixed path without {slug} (e.g., '/')
|
|
55
|
+
*/
|
|
40
56
|
routePattern?: string;
|
|
41
57
|
/** Field definitions for this content type */
|
|
42
58
|
fields: FieldDefinition[];
|
|
43
59
|
/** Field ID to use as the entry title source */
|
|
44
60
|
titleField?: string;
|
|
61
|
+
/**
|
|
62
|
+
* When true, only one entry can exist for this content type.
|
|
63
|
+
* The entry is auto-created when the content type is created.
|
|
64
|
+
* Use for homepage content, site-wide settings, etc.
|
|
65
|
+
*/
|
|
66
|
+
isSingleton?: boolean;
|
|
45
67
|
}
|
|
46
68
|
/**
|
|
47
69
|
* Block content for page scaffolding.
|
|
@@ -2320,15 +2320,20 @@ type DevToolsSeedResponse = {
|
|
|
2320
2320
|
blocksCreated: number;
|
|
2321
2321
|
};
|
|
2322
2322
|
};
|
|
2323
|
+
type SiteInvitee = {
|
|
2324
|
+
email: string;
|
|
2325
|
+
role: 'editor' | 'administrator';
|
|
2326
|
+
};
|
|
2323
2327
|
type CreateManualSiteRequest = {
|
|
2324
2328
|
title: string;
|
|
2329
|
+
deploymentMode?: 'managed' | 'headless_full';
|
|
2325
2330
|
description?: string | null;
|
|
2326
|
-
themeKey
|
|
2327
|
-
contentTypes
|
|
2328
|
-
scaffoldHome
|
|
2329
|
-
scaffoldContact
|
|
2330
|
-
scaffoldBlog
|
|
2331
|
-
invitees?:
|
|
2331
|
+
themeKey?: string;
|
|
2332
|
+
contentTypes?: Array<'page' | 'post' | 'event' | 'testimonial'>;
|
|
2333
|
+
scaffoldHome?: boolean;
|
|
2334
|
+
scaffoldContact?: boolean;
|
|
2335
|
+
scaffoldBlog?: boolean;
|
|
2336
|
+
invitees?: SiteInvitee[];
|
|
2332
2337
|
};
|
|
2333
2338
|
type CreateManualSiteResponse = {
|
|
2334
2339
|
siteId: string;
|
|
@@ -2346,6 +2351,10 @@ type CreateManualSiteResponse = {
|
|
|
2346
2351
|
}>;
|
|
2347
2352
|
contentTypes: Array<'page' | 'post' | 'event' | 'testimonial'>;
|
|
2348
2353
|
};
|
|
2354
|
+
invitations?: {
|
|
2355
|
+
sent: number;
|
|
2356
|
+
failed: number;
|
|
2357
|
+
};
|
|
2349
2358
|
};
|
|
2350
2359
|
type APIEndpoints = {
|
|
2351
2360
|
aiContentUpdateChat: APIEndpoint<{
|
|
@@ -3921,6 +3930,7 @@ type ContentTypeSummary = {
|
|
|
3921
3930
|
hasPages: boolean;
|
|
3922
3931
|
routePattern: string | null;
|
|
3923
3932
|
isBuiltin: boolean;
|
|
3933
|
+
isSingleton: boolean;
|
|
3924
3934
|
createdAt: string;
|
|
3925
3935
|
updatedAt: string;
|
|
3926
3936
|
};
|
|
@@ -3934,6 +3944,7 @@ type CreateContentTypeRequest = {
|
|
|
3934
3944
|
description?: string | null;
|
|
3935
3945
|
schema: Record<string, unknown>;
|
|
3936
3946
|
titleField?: string | null;
|
|
3947
|
+
isSingleton?: boolean;
|
|
3937
3948
|
};
|
|
3938
3949
|
type UpdateContentTypeRequest = {
|
|
3939
3950
|
name?: string;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { A as APIEndpoints } from './types-
|
|
1
|
+
import { A as APIEndpoints } from './types-CdrJqlKx.mjs';
|
|
2
2
|
|
|
3
3
|
type SiteResponse = NonNullable<APIEndpoints['getSite']['response']>;
|
|
4
4
|
type PageResponse = NonNullable<APIEndpoints['getContentByPath']['response']>;
|
|
@@ -16,6 +16,7 @@ import { B as BlockKind } from './blockKinds-B6MWzNWp.mjs';
|
|
|
16
16
|
*
|
|
17
17
|
* @example
|
|
18
18
|
* ```typescript
|
|
19
|
+
* // Regular content type with multiple entries
|
|
19
20
|
* {
|
|
20
21
|
* key: 'testimonial',
|
|
21
22
|
* name: 'Testimonial',
|
|
@@ -25,6 +26,17 @@ import { B as BlockKind } from './blockKinds-B6MWzNWp.mjs';
|
|
|
25
26
|
* { id: 'author', type: 'text', label: 'Author', required: true },
|
|
26
27
|
* ],
|
|
27
28
|
* }
|
|
29
|
+
*
|
|
30
|
+
* // Singleton content type (only one entry allowed)
|
|
31
|
+
* {
|
|
32
|
+
* key: 'site-settings',
|
|
33
|
+
* name: 'Site Settings',
|
|
34
|
+
* isSingleton: true,
|
|
35
|
+
* hasPages: false,
|
|
36
|
+
* fields: [
|
|
37
|
+
* { id: 'contactEmail', type: 'text', label: 'Contact Email' },
|
|
38
|
+
* ],
|
|
39
|
+
* }
|
|
28
40
|
* ```
|
|
29
41
|
*/
|
|
30
42
|
interface ContentTypeConfig {
|
|
@@ -36,12 +48,22 @@ interface ContentTypeConfig {
|
|
|
36
48
|
description?: string;
|
|
37
49
|
/** Whether entries of this type have routable pages */
|
|
38
50
|
hasPages: boolean;
|
|
39
|
-
/**
|
|
51
|
+
/**
|
|
52
|
+
* Route pattern for routable content types. Required if hasPages=true.
|
|
53
|
+
* - For regular types: must include {slug} placeholder (e.g., '/blog/{slug}')
|
|
54
|
+
* - For singletons: can be a fixed path without {slug} (e.g., '/')
|
|
55
|
+
*/
|
|
40
56
|
routePattern?: string;
|
|
41
57
|
/** Field definitions for this content type */
|
|
42
58
|
fields: FieldDefinition[];
|
|
43
59
|
/** Field ID to use as the entry title source */
|
|
44
60
|
titleField?: string;
|
|
61
|
+
/**
|
|
62
|
+
* When true, only one entry can exist for this content type.
|
|
63
|
+
* The entry is auto-created when the content type is created.
|
|
64
|
+
* Use for homepage content, site-wide settings, etc.
|
|
65
|
+
*/
|
|
66
|
+
isSingleton?: boolean;
|
|
45
67
|
}
|
|
46
68
|
/**
|
|
47
69
|
* Block content for page scaffolding.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
2
|
import { S as SdkCustomBlock } from './schema-Bpy9N5ZI.js';
|
|
3
|
-
import { C as ContentConfig, l as ContentTypeConfig, R as RiverbankSiteConfig, i as BlockFieldOptionsMap, j as BlockFieldExtension, k as BlockFieldExtensionsMap } from './types-
|
|
3
|
+
import { C as ContentConfig, l as ContentTypeConfig, R as RiverbankSiteConfig, i as BlockFieldOptionsMap, j as BlockFieldExtension, k as BlockFieldExtensionsMap } from './types-CL916r6x.js';
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* SDK Content Config Validation
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
2
|
import { S as SdkCustomBlock } from './schema-Bpy9N5ZI.mjs';
|
|
3
|
-
import { C as ContentConfig, l as ContentTypeConfig, R as RiverbankSiteConfig, i as BlockFieldOptionsMap, j as BlockFieldExtension, k as BlockFieldExtensionsMap } from './types-
|
|
3
|
+
import { C as ContentConfig, l as ContentTypeConfig, R as RiverbankSiteConfig, i as BlockFieldOptionsMap, j as BlockFieldExtension, k as BlockFieldExtensionsMap } from './types-txWsSxN7.mjs';
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* SDK Content Config Validation
|
package/package.json
CHANGED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/config/validation.ts","../../src/config/content-validation.ts"],"sourcesContent":["/**\n * Zod validation schemas for SDK site configuration.\n *\n * These schemas are used to validate configuration fetched from\n * SDK sites before storing in the database.\n */\n\nimport { z } from 'zod';\nimport { blockCategoryEnum, fieldSchema, getBlockDefinition, type SdkCustomBlock, type FieldDefinition } from '@riverbankcms/blocks';\nimport type { RiverbankSiteConfig, BlockFieldOptionsMap, BlockFieldExtensionsMap, BlockFieldExtension } from './types';\nimport { contentConfigSchema } from './content-validation';\n\n// Re-export content schemas for convenience\nexport { contentConfigSchema } from './content-validation';\nexport type { ContentConfigInput, ContentConfigOutput } from './content-validation';\n\n/**\n * Schema for SDK theme palette.\n * Maps token names to CSS color values.\n */\nexport const sdkThemePaletteSchema = z.record(z.string(), z.string());\n\n/**\n * Schema for SDK theme configuration.\n */\nexport const sdkThemeConfigSchema = z.object({\n palette: sdkThemePaletteSchema,\n});\n\n/**\n * Schema for section background color options.\n */\nexport const sectionBackgroundSchema = z.object({\n id: z.string(),\n label: z.string(),\n token: z.string(), // Reference to theme palette token\n});\n\n/**\n * Schema for section spacing values.\n */\nexport const sectionSpacingSchema = z.enum(['compact', 'default', 'spacious']);\n\n/**\n * Schema for container max-width values.\n */\nexport const containerMaxWidthSchema = z.enum(['narrow', 'default', 'wide', 'full']);\n\n/**\n * Schema for container alignment values.\n */\nexport const containerAlignmentSchema = z.enum(['left', 'center', 'right']);\n\n/**\n * Schema for section options configuration.\n */\nexport const sectionOptionsSchema = z.object({\n backgroundColor: z.boolean().optional(),\n backgroundImage: z.boolean().optional(),\n backgroundGradient: z.boolean().optional(),\n spacing: z.union([\n z.array(sectionSpacingSchema),\n z.boolean(),\n ]).optional(),\n textColor: z.boolean().optional(),\n}).optional();\n\n/**\n * Schema for container options configuration.\n */\nexport const containerOptionsSchema = z.object({\n maxWidth: z.union([\n z.array(containerMaxWidthSchema),\n z.boolean(),\n ]).optional(),\n alignment: z.union([\n z.array(containerAlignmentSchema),\n z.boolean(),\n ]).optional(),\n}).optional();\n\n/**\n * Schema for site style configuration.\n */\nexport const siteStyleConfigSchema = z.object({\n sectionBackgrounds: z.array(sectionBackgroundSchema).optional(),\n sectionOptions: sectionOptionsSchema,\n containerOptions: containerOptionsSchema,\n}).optional();\n\n// ============================================================================\n// Data Loader Schemas\n// ============================================================================\n\nimport { SUPPORTED_LOADER_ENDPOINTS } from '../data/prefetchBlockData';\n\n/**\n * Whitelisted endpoints for SDK data loaders.\n *\n * These are the only CMS endpoints that can be called from config-based loaders.\n * This ensures SDK sites can only access safe, read-only public endpoints.\n *\n * Derived from SUPPORTED_LOADER_ENDPOINTS - the single source of truth.\n */\nexport const sdkLoaderEndpointSchema = z.enum(SUPPORTED_LOADER_ENDPOINTS);\n\n/**\n * A binding expression for dynamic loader params.\n *\n * @example\n * ```typescript\n * { $bind: { from: 'content.categoryId' } }\n * { $bind: { from: '$root.siteId' } }\n * { $bind: { from: 'content.limit', fallback: '10' } }\n * ```\n */\nexport const loaderParamBindingSchema = z.object({\n $bind: z.object({\n from: z.string().min(1, \"Binding path is required\"),\n fallback: z.string().optional(),\n }),\n});\n\n/**\n * A loader param value can be static or a binding expression.\n */\nexport const loaderParamValueSchema = z.union([\n z.string(),\n z.number(),\n z.boolean(),\n loaderParamBindingSchema,\n]);\n\n/**\n * Schema for config-based data loader.\n *\n * Config loaders execute server-side during loadPage() and are\n * restricted to whitelisted CMS endpoints.\n */\nexport const sdkConfigLoaderSchema = z.object({\n endpoint: sdkLoaderEndpointSchema,\n params: z.record(z.string(), loaderParamValueSchema),\n mode: z.enum(['server', 'client']).default('server'),\n});\n\n/**\n * Schema for the dataLoaders field on custom blocks.\n * Validates the loader configuration and limits the number of loaders.\n */\nexport const sdkDataLoadersSchema = z.record(z.string(), sdkConfigLoaderSchema)\n .refine(\n (loaders) => Object.keys(loaders).length <= 5,\n { message: \"Maximum 5 data loaders per block\" }\n )\n .optional();\n\n// ============================================================================\n// Custom Block Schema\n// ============================================================================\n\n/**\n * Schema for field select option.\n */\nexport const fieldSelectOptionSchema = z.object({\n value: z.string().min(1, \"Option value is required\"),\n label: z.string().min(1, \"Option label is required\"),\n});\n\n/**\n * Schema for per-field configuration within a block.\n */\nexport const blockFieldConfigSchema = z.object({\n options: z.array(fieldSelectOptionSchema).min(1, \"At least one option is required\").optional(),\n});\n\n/**\n * Schema for per-block field options.\n *\n * Block IDs must be either 'block.*' (system blocks) or 'custom.*' (custom blocks).\n * Field IDs can be any valid identifier string.\n */\nexport const blockFieldOptionsSchema: z.ZodType<BlockFieldOptionsMap> = z.record(\n z.string().regex(/^(block\\.|custom\\.)[a-z][a-z0-9-]*$/, {\n message: \"Block ID must be 'block.*' or 'custom.*' format\",\n }),\n z.record(\n z.string().min(1, \"Field ID is required\"),\n blockFieldConfigSchema\n )\n).optional() as z.ZodType<BlockFieldOptionsMap>;\n\n// ============================================================================\n// Block Field Extensions Schema\n// ============================================================================\n\n/**\n * Schema for block field extension configuration.\n *\n * Validates additional fields to be appended to a built-in block.\n * Includes refinement to ensure required fields have defaultValue.\n *\n * Note: Explicit type annotation required due to recursive fieldSchema complexity.\n */\nexport const blockFieldExtensionSchema: z.ZodType<BlockFieldExtension> = z.object({\n fields: fieldSchema.array().min(1, \"At least one field is required\"),\n}).refine(\n (data) => {\n // All required fields must have a defaultValue\n return data.fields.every((field: FieldDefinition) => {\n if (!field.required) return true;\n return field.defaultValue !== undefined;\n });\n },\n {\n message: \"Required fields must have a defaultValue to support existing blocks\",\n }\n) as z.ZodType<BlockFieldExtension>;\n\n/**\n * Schema for block field extensions map.\n *\n * Block IDs must be system blocks (e.g., 'block.bodyText', 'block.hero').\n * Custom blocks ('custom.*') should define their fields directly, not via extensions.\n */\nexport const blockFieldExtensionsSchema: z.ZodType<BlockFieldExtensionsMap | undefined> = z.record(\n z.string().regex(/^block\\.[a-z][a-zA-Z0-9]*$/, {\n message: \"Block ID must be 'block.*' format (system blocks only)\",\n }),\n blockFieldExtensionSchema\n).optional() as z.ZodType<BlockFieldExtensionsMap | undefined>;\n\n/**\n * Validates that extended field IDs don't conflict with existing block fields.\n *\n * This validation should be called during config push to provide clear error messages.\n * Returns an array of conflict errors, or empty array if valid.\n *\n * @example\n * ```typescript\n * const conflicts = validateFieldIdConflicts(config.blockFieldExtensions);\n * if (conflicts.length > 0) {\n * throw new Error(conflicts.map(c => c.message).join('\\n'));\n * }\n * ```\n */\nexport function validateFieldIdConflicts(\n blockFieldExtensions?: BlockFieldExtensionsMap | null\n): { blockId: string; fieldId: string; message: string }[] {\n if (!blockFieldExtensions) return [];\n\n const conflicts: { blockId: string; fieldId: string; message: string }[] = [];\n\n for (const [blockId, extension] of Object.entries(blockFieldExtensions)) {\n const definition = getBlockDefinition(blockId);\n if (!definition) {\n conflicts.push({\n blockId,\n fieldId: '',\n message: `Unknown block type: ${blockId}`,\n });\n continue;\n }\n\n // Get all existing field IDs from the block manifest\n const existingFieldIds = new Set<string>();\n const collectFieldIds = (fields: FieldDefinition[] | undefined) => {\n if (!fields) return;\n for (const field of fields) {\n existingFieldIds.add(field.id);\n // Also collect nested field IDs from groups, modals, repeaters, tab groups\n if (field.type === 'group' || field.type === 'modal') {\n collectFieldIds(field.schema?.fields);\n } else if (field.type === 'repeater' && field.schema?.fields) {\n collectFieldIds(field.schema.fields);\n } else if (field.type === 'tabGroup') {\n for (const tab of field.tabs ?? []) {\n collectFieldIds(tab.fields);\n }\n }\n }\n };\n collectFieldIds(definition.manifest.fields);\n\n // Check for conflicts\n for (const field of extension.fields) {\n if (existingFieldIds.has(field.id)) {\n conflicts.push({\n blockId,\n fieldId: field.id,\n message: `Field ID \"${field.id}\" conflicts with existing field in ${blockId}`,\n });\n }\n }\n }\n\n return conflicts;\n}\n\n/**\n * Schema for SDK custom block definitions.\n *\n * Validates custom blocks defined in riverbank.config.ts.\n * Reuses fieldSchema from @riverbankcms/blocks for field validation.\n *\n * Note: Explicit type annotation required due to recursive fieldSchema complexity.\n */\nexport const sdkCustomBlockSchema: z.ZodType<SdkCustomBlock> = z.object({\n // Block ID must start with 'custom.'\n id: z.string()\n .min(8) // 'custom.' + at least 1 char\n .regex(/^custom\\.[a-z][a-z0-9-]*$/, {\n message: \"Block ID must start with 'custom.' followed by lowercase letters, numbers, or hyphens\",\n }),\n title: z.string().min(1, \"Title is required\"),\n titleSource: z.string().optional(),\n description: z.string().optional(),\n category: blockCategoryEnum,\n icon: z.string().optional(),\n tags: z.array(z.string()).optional(),\n // Reuse the exact field schema from @riverbankcms/blocks - all field types supported\n fields: fieldSchema.array().min(1, \"Custom blocks must have at least one field\"),\n // Data loaders for CMS endpoints\n dataLoaders: sdkDataLoadersSchema,\n}).refine(\n // Validate titleSource references a valid field if provided\n (data) => {\n if (!data.titleSource) return true;\n return data.fields.some(f => f.id === data.titleSource);\n },\n {\n message: \"titleSource must reference a valid field ID\",\n path: [\"titleSource\"],\n }\n) as z.ZodType<SdkCustomBlock>;\n\n/**\n * Schema for the complete SDK site configuration.\n *\n * Use this schema to validate configuration fetched from SDK sites\n * before storing in the database.\n *\n * @example\n * ```typescript\n * import { riverbankSiteConfigSchema } from '@riverbankcms/sdk/config/validation';\n *\n * const rawConfig = await response.json();\n * const config = riverbankSiteConfigSchema.parse(rawConfig);\n * ```\n */\nexport const riverbankSiteConfigSchema: z.ZodType<RiverbankSiteConfig> = z.object({\n siteId: z.string().uuid(),\n theme: sdkThemeConfigSchema.optional(),\n styles: siteStyleConfigSchema,\n customBlocks: z.array(sdkCustomBlockSchema)\n .max(20, \"Maximum 20 custom blocks per site\")\n .refine(\n // Ensure unique block IDs\n (blocks) => {\n const ids = blocks.map(b => b.id);\n return ids.length === new Set(ids).size;\n },\n { message: \"Block IDs must be unique\" }\n )\n .optional(),\n blockFieldOptions: blockFieldOptionsSchema,\n blockFieldExtensions: blockFieldExtensionsSchema,\n content: contentConfigSchema.optional(),\n}).strict() as z.ZodType<RiverbankSiteConfig>;\n\n/**\n * Type inferred from the validation schema.\n * This should match the RiverbankSiteConfig type from ./types.ts\n */\nexport type ValidatedRiverbankSiteConfig = z.infer<typeof riverbankSiteConfigSchema>;\n\n/**\n * Type for a validated SDK custom block.\n */\nexport type ValidatedSdkCustomBlock = z.infer<typeof sdkCustomBlockSchema>;\n\n// ============================================================================\n// Compile-time type assertions\n//\n// These assertions ensure the Zod schemas stay in sync with the TypeScript types.\n// If the schema output diverges from the expected type, TypeScript will error here.\n// ============================================================================\n\n/** Asserts sdkCustomBlockSchema output matches SdkCustomBlock */\ntype _AssertSdkCustomBlockSchema = z.infer<typeof sdkCustomBlockSchema> extends SdkCustomBlock\n ? SdkCustomBlock extends z.infer<typeof sdkCustomBlockSchema>\n ? true\n : never\n : never;\n\n/** Asserts riverbankSiteConfigSchema output matches RiverbankSiteConfig */\ntype _AssertRiverbankSiteConfigSchema = z.infer<typeof riverbankSiteConfigSchema> extends RiverbankSiteConfig\n ? RiverbankSiteConfig extends z.infer<typeof riverbankSiteConfigSchema>\n ? true\n : never\n : never;\n\n// These assignments will fail to compile if the types don't match\nconst _checkSdkCustomBlock: _AssertSdkCustomBlockSchema = true;\nconst _checkRiverbankSiteConfig: _AssertRiverbankSiteConfigSchema = true;\n\n// Prevent unused variable warnings\nvoid _checkSdkCustomBlock;\nvoid _checkRiverbankSiteConfig;\n","/**\n * SDK Content Config Validation\n *\n * Zod schemas for validating SDK content configuration.\n */\n\nimport { z } from 'zod';\nimport { SYSTEM_BLOCK_KINDS } from '../types';\nimport type { ContentConfig, ContentTypeConfig } from './content-types';\n\n// Use looseObject for data fields that can contain arbitrary values\nconst jsonDataSchema = z.record(z.string(), z.any());\n\n// ============================================================================\n// Content Type Schema\n// ============================================================================\n\n/**\n * Content type key validation.\n * Must be lowercase, start with a letter, and contain only letters, numbers, and hyphens.\n */\nconst contentTypeKeySchema = z\n .string()\n .min(1)\n .regex(\n /^[a-z][a-z0-9-]*$/,\n 'Key must be lowercase, start with a letter, and contain only letters, numbers, and hyphens'\n );\n\n/**\n * Content type config schema.\n */\nexport const contentTypeConfigSchema: z.ZodType<ContentTypeConfig> = z\n .object({\n key: contentTypeKeySchema,\n name: z.string().min(1),\n description: z.string().optional(),\n hasPages: z.boolean(),\n routePattern: z.string().optional(),\n // Fields are validated as any[] here - deep field validation happens in @riverbankcms/blocks\n fields: z.array(z.any()).min(1, 'At least one field is required'),\n titleField: z.string().optional(),\n })\n .refine(\n (data) => {\n // If hasPages is true, routePattern must be provided and contain {slug}\n if (data.hasPages) {\n return data.routePattern && data.routePattern.includes('{slug}');\n }\n return true;\n },\n { message: 'routePattern with {slug} is required when hasPages is true' }\n )\n .refine(\n (data) => {\n // If hasPages is false, routePattern must NOT be defined\n if (!data.hasPages && data.routePattern) {\n return false;\n }\n return true;\n },\n { message: 'routePattern must not be defined when hasPages is false' }\n ) as z.ZodType<ContentTypeConfig>;\n\n// ============================================================================\n// Block Config Schema\n// ============================================================================\n\n/**\n * Block kind validation.\n * Must be either a system block kind (e.g., 'block.hero') or a custom block kind (e.g., 'custom.myBlock').\n */\nconst blockKindSchema = z.string().refine(\n (kind) => {\n // Check if it's a system block kind\n if ((SYSTEM_BLOCK_KINDS as readonly string[]).includes(kind)) {\n return true;\n }\n // Check if it's a custom block kind (custom.${string})\n if (kind.startsWith('custom.') && kind.length > 7) {\n return true;\n }\n return false;\n },\n {\n message:\n 'Block kind must be a system block (e.g., \"block.hero\") or a custom block (e.g., \"custom.myBlock\")',\n }\n);\n\nexport const blockConfigSchema = z.object({\n kind: blockKindSchema,\n content: jsonDataSchema,\n orderIndex: z.number().optional(),\n});\n\n// ============================================================================\n// Page Config Schema\n// ============================================================================\n\nexport const pageConfigSchema = z.object({\n identifier: z.string().min(1, 'Identifier is required'),\n title: z.string().min(1, 'Title is required'),\n path: z.string().regex(/^\\//, 'Path must start with /'),\n purpose: z.string().default('content'),\n blocks: z.array(blockConfigSchema).optional(),\n status: z.enum(['draft', 'published']).default('draft'),\n metaTitle: z.string().optional(),\n metaDescription: z.string().optional(),\n});\n\n// ============================================================================\n// Entry Config Schema\n// ============================================================================\n\nexport const entryConfigSchema = z.object({\n identifier: z.string().min(1, 'Identifier is required'),\n contentType: z.string().min(1, 'Content type is required'),\n data: jsonDataSchema,\n status: z.enum(['draft', 'published']).default('draft'),\n slug: z.string().optional(),\n title: z.string().optional(),\n summary: z.string().optional(),\n metaTitle: z.string().optional(),\n metaDescription: z.string().optional(),\n});\n\n// ============================================================================\n// Navigation Config Schema\n// ============================================================================\n\nconst pageLinkSchema = z.object({\n kind: z.literal('page'),\n identifier: z.string().min(1),\n});\n\nconst entryLinkSchema = z.object({\n kind: z.literal('entry'),\n identifier: z.string().min(1),\n});\n\nconst externalLinkSchema = z.object({\n kind: z.literal('external'),\n href: z.string().url('Invalid URL'),\n});\n\nconst dropdownLinkSchema = z.object({\n kind: z.literal('dropdown'),\n});\n\nconst navigationLinkSchema = z.discriminatedUnion('kind', [\n pageLinkSchema,\n entryLinkSchema,\n externalLinkSchema,\n dropdownLinkSchema,\n]);\n\n// Navigation item with recursive children support\nconst baseNavigationItemSchema = z.object({\n label: z.string().min(1, 'Label is required'),\n link: navigationLinkSchema,\n isCta: z.boolean().optional(),\n});\n\n// Using z.lazy for recursive type\nexport const navigationItemConfigSchema: z.ZodType<{\n label: string;\n link:\n | { kind: 'page'; identifier: string }\n | { kind: 'entry'; identifier: string }\n | { kind: 'external'; href: string }\n | { kind: 'dropdown' };\n isCta?: boolean;\n children?: Array<{\n label: string;\n link:\n | { kind: 'page'; identifier: string }\n | { kind: 'entry'; identifier: string }\n | { kind: 'external'; href: string }\n | { kind: 'dropdown' };\n isCta?: boolean;\n children?: unknown[];\n }>;\n}> = baseNavigationItemSchema.extend({\n children: z.lazy(() => z.array(navigationItemConfigSchema)).optional(),\n});\n\nexport const navigationMenuConfigSchema = z.object({\n identifier: z.string().min(1, 'Identifier is required'),\n name: z.string().min(1, 'Name is required'),\n isPrimary: z.boolean().optional(),\n items: z.array(navigationItemConfigSchema),\n});\n\n// ============================================================================\n// Site Settings Schema\n// ============================================================================\n\nexport const siteSettingsConfigSchema = z.object({\n homepagePath: z.string().optional(),\n siteTitle: z.string().optional(),\n siteDescription: z.string().optional(),\n defaultTemplates: z.record(z.string(), z.string()).optional(),\n});\n\n// ============================================================================\n// Complete Content Config Schema\n// ============================================================================\n\nconst contentConfigBaseSchema = z.object({\n contentTypes: z.array(contentTypeConfigSchema).optional(),\n entries: z.array(entryConfigSchema).optional(),\n pages: z.array(pageConfigSchema).optional(),\n navigationMenus: z.array(navigationMenuConfigSchema).optional(),\n settings: siteSettingsConfigSchema.optional(),\n});\n\nexport const contentConfigSchema: z.ZodType<ContentConfig> = contentConfigBaseSchema\n .superRefine((data, ctx) => {\n // Validate unique content type keys\n if (data.contentTypes && data.contentTypes.length > 1) {\n const keys = data.contentTypes.map((ct) => ct.key);\n const duplicateKeys = keys.filter((key, i) => keys.indexOf(key) !== i);\n if (duplicateKeys.length > 0) {\n ctx.addIssue({\n code: z.ZodIssueCode.custom,\n message: `Content type keys must be unique. Duplicates: ${duplicateKeys.join(', ')}`,\n path: ['contentTypes'],\n });\n }\n }\n\n // Validate unique page identifiers\n if (data.pages && data.pages.length > 1) {\n const identifiers = data.pages.map((p) => p.identifier);\n const duplicateIds = identifiers.filter((id, i) => identifiers.indexOf(id) !== i);\n if (duplicateIds.length > 0) {\n ctx.addIssue({\n code: z.ZodIssueCode.custom,\n message: `Page identifiers must be unique. Duplicates: ${duplicateIds.join(', ')}`,\n path: ['pages'],\n });\n }\n\n // Validate unique page paths\n const paths = data.pages.map((p) => p.path);\n const duplicatePaths = paths.filter((path, i) => paths.indexOf(path) !== i);\n if (duplicatePaths.length > 0) {\n ctx.addIssue({\n code: z.ZodIssueCode.custom,\n message: `Page paths must be unique. Duplicates: ${duplicatePaths.join(', ')}`,\n path: ['pages'],\n });\n }\n }\n\n // Validate unique entry identifiers\n if (data.entries && data.entries.length > 1) {\n const identifiers = data.entries.map((e) => e.identifier);\n const duplicateIds = identifiers.filter((id, i) => identifiers.indexOf(id) !== i);\n if (duplicateIds.length > 0) {\n ctx.addIssue({\n code: z.ZodIssueCode.custom,\n message: `Entry identifiers must be unique. Duplicates: ${duplicateIds.join(', ')}`,\n path: ['entries'],\n });\n }\n }\n\n // Validate unique navigation menu identifiers\n if (data.navigationMenus && data.navigationMenus.length > 1) {\n const identifiers = data.navigationMenus.map((m) => m.identifier);\n const duplicateIds = identifiers.filter((id, i) => identifiers.indexOf(id) !== i);\n if (duplicateIds.length > 0) {\n ctx.addIssue({\n code: z.ZodIssueCode.custom,\n message: `Navigation menu identifiers must be unique. Duplicates: ${duplicateIds.join(', ')}`,\n path: ['navigationMenus'],\n });\n }\n }\n }) as z.ZodType<ContentConfig>;\n\n// ============================================================================\n// Type Exports\n// ============================================================================\n\nexport type ContentTypeConfigInput = z.input<typeof contentTypeConfigSchema>;\nexport type ContentTypeConfigOutput = z.output<typeof contentTypeConfigSchema>;\n\nexport type PageConfigInput = z.input<typeof pageConfigSchema>;\nexport type PageConfigOutput = z.output<typeof pageConfigSchema>;\n\nexport type EntryConfigInput = z.input<typeof entryConfigSchema>;\nexport type EntryConfigOutput = z.output<typeof entryConfigSchema>;\n\nexport type NavigationMenuConfigInput = z.input<typeof navigationMenuConfigSchema>;\nexport type NavigationMenuConfigOutput = z.output<typeof navigationMenuConfigSchema>;\n\nexport type ContentConfigInput = z.input<typeof contentConfigSchema>;\nexport type ContentConfigOutput = z.output<typeof contentConfigSchema>;\n"],"mappings":"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","names":["z","z"]}
|