@riverbankcms/sdk 0.8.1 → 0.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +298 -2
- package/dist/cli/index.js +1996 -618
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/init-docs/content/agents-section.md +11 -0
- package/dist/cli/init-docs/content/cli-reference.md +15 -1
- package/dist/cli/init-docs/content/workflow-add-block.md +7 -0
- package/dist/cli/init-docs/content/workflow-block-extensions.md +361 -0
- package/dist/cli/init-docs/content/workflow-cmsify-page.md +357 -0
- package/dist/cli/init-docs/content/workflow-content-types.md +328 -0
- package/dist/cli/init-docs/content/workflow-create-page.md +9 -0
- package/dist/cli/init-docs/content/workflow-custom-block.md +446 -0
- package/dist/client/client.d.mts +2 -2
- package/dist/client/client.d.ts +2 -2
- package/dist/client/client.js +262 -17
- package/dist/client/client.js.map +1 -1
- package/dist/client/client.mjs +262 -17
- package/dist/client/client.mjs.map +1 -1
- package/dist/client/hooks.d.mts +2 -2
- package/dist/client/hooks.d.ts +2 -2
- package/dist/client/hooks.js +6 -6
- package/dist/client/hooks.js.map +1 -1
- package/dist/client/hooks.mjs +6 -6
- package/dist/client/hooks.mjs.map +1 -1
- package/dist/client/rendering/client.js +29 -6
- package/dist/client/rendering/client.js.map +1 -1
- package/dist/client/rendering/client.mjs +29 -6
- package/dist/client/rendering/client.mjs.map +1 -1
- package/dist/client/usePage--LiGLbVz.d.mts +7195 -0
- package/dist/client/usePage-BwQJlxpe.d.mts +7218 -0
- package/dist/client/usePage-Ds-ow1-d.d.ts +7195 -0
- package/dist/client/usePage-Duc2GC-H.d.ts +7218 -0
- package/dist/client/usePage-DyzrgxqR.d.mts +7215 -0
- package/dist/client/usePage-lTWkuVMZ.d.ts +7215 -0
- package/dist/server/{Layout-CZ-kxKfl.d.ts → Layout-BHGokJmV.d.ts} +1 -1
- package/dist/server/{Layout-ESG8zvrk.d.mts → Layout-CXkMcTR4.d.mts} +1 -1
- package/dist/server/chunk-274Y2CUE.js +341 -0
- package/dist/server/chunk-274Y2CUE.js.map +1 -0
- package/dist/server/{chunk-IJTJH4J3.js → chunk-2WL52ZOE.js} +8 -8
- package/dist/server/{chunk-IJTJH4J3.js.map → chunk-2WL52ZOE.js.map} +1 -1
- package/dist/server/{chunk-DAXWU3S3.js → chunk-5HGVBSWA.js} +9 -9
- package/dist/server/{chunk-DAXWU3S3.js.map → chunk-5HGVBSWA.js.map} +1 -1
- package/dist/server/chunk-7WJGJY3B.js +7 -0
- package/dist/server/chunk-7WJGJY3B.js.map +1 -0
- package/dist/server/chunk-AGAOKSPY.mjs +22 -0
- package/dist/server/chunk-AGAOKSPY.mjs.map +1 -0
- package/dist/server/{chunk-A3UZ2LDH.mjs → chunk-BOYBN4KN.mjs} +3 -3
- package/dist/server/chunk-BOYBN4KN.mjs.map +1 -0
- package/dist/server/{chunk-FUFPKTSI.mjs → chunk-CKZDJBMC.mjs} +33 -9
- package/dist/server/chunk-CKZDJBMC.mjs.map +1 -0
- package/dist/server/{chunk-PGZJUNCY.mjs → chunk-E4R5ILRE.mjs} +3 -3
- package/dist/server/{chunk-MFNWLB5G.js → chunk-EC2AA2IP.js} +275 -297
- package/dist/server/chunk-EC2AA2IP.js.map +1 -0
- package/dist/server/{chunk-HE3RTUDX.js → chunk-F4U4LC5D.js} +8 -8
- package/dist/server/{chunk-HE3RTUDX.js.map → chunk-F4U4LC5D.js.map} +1 -1
- package/dist/server/{chunk-T5PAA22U.mjs → chunk-H44G72AB.mjs} +2 -2
- package/dist/server/{chunk-KGORQCHF.js → chunk-JVLQDZTZ.js} +6 -6
- package/dist/server/{chunk-KGORQCHF.js.map → chunk-JVLQDZTZ.js.map} +1 -1
- package/dist/server/{chunk-ADD3O2QO.mjs → chunk-KKUR3PDT.mjs} +4 -4
- package/dist/server/chunk-NTG7XP3E.js +264 -0
- package/dist/server/chunk-NTG7XP3E.js.map +1 -0
- package/dist/server/{chunk-TR7MSLWL.mjs → chunk-OSTUHBFE.mjs} +3 -3
- package/dist/server/chunk-PAHSKNY5.mjs +264 -0
- package/dist/server/chunk-PAHSKNY5.mjs.map +1 -0
- package/dist/server/chunk-PSN6HXUD.js +22 -0
- package/dist/server/chunk-PSN6HXUD.js.map +1 -0
- package/dist/server/{chunk-GRFFJUCO.mjs → chunk-QS6ZTLLB.mjs} +242 -264
- package/dist/server/chunk-QS6ZTLLB.mjs.map +1 -0
- package/dist/server/{chunk-K44OPKLA.js → chunk-R6T3Z4W5.js} +3 -3
- package/dist/server/{chunk-K44OPKLA.js.map → chunk-R6T3Z4W5.js.map} +1 -1
- package/dist/server/{chunk-KDCVCDW6.js → chunk-RIROJYPX.js} +4 -4
- package/dist/server/{chunk-KDCVCDW6.js.map → chunk-RIROJYPX.js.map} +1 -1
- package/dist/server/chunk-SVEQVEA5.mjs +341 -0
- package/dist/server/chunk-SVEQVEA5.mjs.map +1 -0
- package/dist/server/{chunk-HDHY4236.mjs → chunk-TBN35TGI.mjs} +6 -6
- package/dist/server/{chunk-HDHY4236.mjs.map → chunk-TBN35TGI.mjs.map} +1 -1
- package/dist/server/{chunk-5GCSRTIU.mjs → chunk-TBX6CXBM.mjs} +2 -2
- package/dist/server/{chunk-6ERSDFTY.js → chunk-U2F4BWKW.js} +3 -3
- package/dist/server/chunk-U2F4BWKW.js.map +1 -0
- package/dist/server/chunk-WWGVFOLS.mjs +7 -0
- package/dist/server/chunk-WWGVFOLS.mjs.map +1 -0
- package/dist/server/{chunk-TLZHVGTL.js → chunk-X4REO3S7.js} +4 -4
- package/dist/server/{chunk-TLZHVGTL.js.map → chunk-X4REO3S7.js.map} +1 -1
- package/dist/server/{chunk-BNHK7YOC.mjs → chunk-YUD7ONZG.mjs} +2 -2
- package/dist/server/{chunk-F2NDLDDA.js → chunk-ZJXFRSTC.js} +92 -68
- package/dist/server/chunk-ZJXFRSTC.js.map +1 -0
- package/dist/server/{components-iEDvl2Yw.d.mts → components-Bqn4xmR6.d.mts} +74 -5
- package/dist/server/{components-CE48wJM1.d.ts → components-C7j9yzAt.d.ts} +74 -5
- package/dist/server/components.d.mts +5 -5
- package/dist/server/components.d.ts +5 -5
- package/dist/server/components.js +7 -5
- package/dist/server/components.js.map +1 -1
- package/dist/server/components.mjs +6 -4
- package/dist/server/config-validation.js +5 -5
- package/dist/server/config-validation.mjs +4 -4
- package/dist/server/config.js +5 -5
- package/dist/server/config.mjs +4 -4
- package/dist/server/data.d.mts +2 -2
- package/dist/server/data.d.ts +2 -2
- package/dist/server/data.js +3 -3
- package/dist/server/data.mjs +2 -2
- package/dist/server/{index-BHLK2mgQ.d.ts → index-Bns_1a4N.d.ts} +1 -1
- package/dist/server/{index-DTBg8eXj.d.ts → index-CHp2kyp0.d.ts} +2 -2
- package/dist/server/{index-Cgvb5fVQ.d.mts → index-CPDT8kn9.d.mts} +1 -1
- package/dist/server/{index-BrH_NIRO.d.mts → index-Cm9nMPkf.d.mts} +2 -2
- package/dist/server/index.d.mts +193 -215
- package/dist/server/index.d.ts +193 -215
- package/dist/server/index.js +9 -288
- package/dist/server/index.js.map +1 -1
- package/dist/server/index.mjs +10 -289
- package/dist/server/index.mjs.map +1 -1
- package/dist/server/{loadContent-BUK6IVJf.d.ts → loadContent-DD7J5_WO.d.ts} +3 -3
- package/dist/server/{loadContent-au9Weoy0.d.mts → loadContent-DTEgYI-l.d.mts} +3 -3
- package/dist/server/{loadPage-DiHEl8BA.d.mts → loadPage-B578Xg2W.d.mts} +2 -2
- package/dist/server/{loadPage-JOIbF7ih.d.ts → loadPage-Dkiimbsg.d.ts} +2 -2
- package/dist/server/loadPage-IBX7FXGH.mjs +11 -0
- package/dist/server/loadPage-KG74OG4V.js +11 -0
- package/dist/server/{loadPage-CMHYAW2J.js.map → loadPage-KG74OG4V.js.map} +1 -1
- package/dist/server/metadata.d.mts +4 -4
- package/dist/server/metadata.d.ts +4 -4
- package/dist/server/navigation.d.mts +2 -2
- package/dist/server/navigation.d.ts +2 -2
- package/dist/server/next.d.mts +5 -5
- package/dist/server/next.d.ts +5 -5
- package/dist/server/next.js +17 -14
- package/dist/server/next.js.map +1 -1
- package/dist/server/next.mjs +9 -6
- package/dist/server/next.mjs.map +1 -1
- package/dist/server/prebuild-loader.d.mts +87 -0
- package/dist/server/prebuild-loader.d.ts +87 -0
- package/dist/server/prebuild-loader.js +15 -0
- package/dist/server/prebuild-loader.js.map +1 -0
- package/dist/server/prebuild-loader.mjs +15 -0
- package/dist/server/prebuild-loader.mjs.map +1 -0
- package/dist/server/prebuild-types.d.mts +201 -0
- package/dist/server/prebuild-types.d.ts +201 -0
- package/dist/server/prebuild-types.js +1 -0
- package/dist/server/prebuild-types.js.map +1 -0
- package/dist/server/prebuild-types.mjs +1 -0
- package/dist/server/prebuild-types.mjs.map +1 -0
- package/dist/server/prebuild.d.mts +46 -0
- package/dist/server/prebuild.d.ts +46 -0
- package/dist/server/prebuild.js +10 -0
- package/dist/server/prebuild.js.map +1 -0
- package/dist/server/prebuild.mjs +10 -0
- package/dist/server/prebuild.mjs.map +1 -0
- package/dist/server/rendering/server.d.mts +4 -4
- package/dist/server/rendering/server.d.ts +4 -4
- package/dist/server/rendering/server.js +7 -7
- package/dist/server/rendering/server.mjs +6 -6
- package/dist/server/rendering.d.mts +8 -8
- package/dist/server/rendering.d.ts +8 -8
- package/dist/server/rendering.js +11 -9
- package/dist/server/rendering.js.map +1 -1
- package/dist/server/rendering.mjs +10 -8
- package/dist/server/routing.d.mts +3 -3
- package/dist/server/routing.d.ts +3 -3
- package/dist/server/routing.js +1 -1
- package/dist/server/routing.mjs +1 -1
- package/dist/server/server.d.mts +5 -5
- package/dist/server/server.d.ts +5 -5
- package/dist/server/server.js +9 -6
- package/dist/server/server.js.map +1 -1
- package/dist/server/server.mjs +8 -5
- package/dist/server/theme-bridge.js +8 -8
- package/dist/server/theme-bridge.mjs +2 -2
- package/dist/server/{types-BAM1kcGA.d.mts → types-B6P_iaDz.d.mts} +295 -1
- package/dist/server/{types-_SNCu2ZZ.d.ts → types-C4jfCjaP.d.ts} +295 -1
- package/dist/server/{types-DDNKxQXw.d.mts → types-CSvCkmYi.d.mts} +12 -3
- package/dist/server/{types-CmBB0Osp.d.ts → types-gKcrQV09.d.ts} +12 -3
- package/dist/styles/index.css +419 -0
- package/package.json +17 -3
- package/dist/server/chunk-6ERSDFTY.js.map +0 -1
- package/dist/server/chunk-A3UZ2LDH.mjs.map +0 -1
- package/dist/server/chunk-F2NDLDDA.js.map +0 -1
- package/dist/server/chunk-FUFPKTSI.mjs.map +0 -1
- package/dist/server/chunk-GRFFJUCO.mjs.map +0 -1
- package/dist/server/chunk-MFNWLB5G.js.map +0 -1
- package/dist/server/loadPage-AWYZ2QA2.mjs +0 -11
- package/dist/server/loadPage-CMHYAW2J.js +0 -11
- package/src/styles/index.css +0 -10
- /package/dist/server/{chunk-PGZJUNCY.mjs.map → chunk-E4R5ILRE.mjs.map} +0 -0
- /package/dist/server/{chunk-T5PAA22U.mjs.map → chunk-H44G72AB.mjs.map} +0 -0
- /package/dist/server/{chunk-ADD3O2QO.mjs.map → chunk-KKUR3PDT.mjs.map} +0 -0
- /package/dist/server/{chunk-TR7MSLWL.mjs.map → chunk-OSTUHBFE.mjs.map} +0 -0
- /package/dist/server/{chunk-5GCSRTIU.mjs.map → chunk-TBX6CXBM.mjs.map} +0 -0
- /package/dist/server/{chunk-BNHK7YOC.mjs.map → chunk-YUD7ONZG.mjs.map} +0 -0
- /package/dist/server/{loadPage-AWYZ2QA2.mjs.map → loadPage-IBX7FXGH.mjs.map} +0 -0
package/dist/server/next.js
CHANGED
|
@@ -7,18 +7,21 @@ var _chunkBNQV3PXPjs = require('./chunk-BNQV3PXP.js');
|
|
|
7
7
|
var _chunk2IZ6S225js = require('./chunk-2IZ6S225.js');
|
|
8
8
|
|
|
9
9
|
|
|
10
|
-
var
|
|
10
|
+
var _chunkEC2AA2IPjs = require('./chunk-EC2AA2IP.js');
|
|
11
|
+
require('./chunk-7WJGJY3B.js');
|
|
12
|
+
require('./chunk-NTG7XP3E.js');
|
|
13
|
+
require('./chunk-PSN6HXUD.js');
|
|
11
14
|
|
|
12
15
|
|
|
13
16
|
|
|
14
17
|
|
|
15
|
-
var
|
|
18
|
+
var _chunkRIROJYPXjs = require('./chunk-RIROJYPX.js');
|
|
16
19
|
|
|
17
20
|
|
|
18
|
-
var
|
|
19
|
-
require('./chunk-
|
|
20
|
-
require('./chunk-
|
|
21
|
-
require('./chunk-
|
|
21
|
+
var _chunkX4REO3S7js = require('./chunk-X4REO3S7.js');
|
|
22
|
+
require('./chunk-ZJXFRSTC.js');
|
|
23
|
+
require('./chunk-2WL52ZOE.js');
|
|
24
|
+
require('./chunk-U2F4BWKW.js');
|
|
22
25
|
require('./chunk-Z6ZWNWWR.js');
|
|
23
26
|
|
|
24
27
|
// src/next/catch-all.tsx
|
|
@@ -62,7 +65,7 @@ function createCatchAllPage(options) {
|
|
|
62
65
|
const client = getClient();
|
|
63
66
|
let content;
|
|
64
67
|
try {
|
|
65
|
-
content = await
|
|
68
|
+
content = await _chunkRIROJYPXjs.loadContent.call(void 0, {
|
|
66
69
|
client,
|
|
67
70
|
siteId: config.siteId,
|
|
68
71
|
path,
|
|
@@ -79,9 +82,9 @@ function createCatchAllPage(options) {
|
|
|
79
82
|
}
|
|
80
83
|
}
|
|
81
84
|
let rendered;
|
|
82
|
-
if (
|
|
85
|
+
if (_chunkRIROJYPXjs.isPageContent.call(void 0, content)) {
|
|
83
86
|
rendered = /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
|
|
84
|
-
|
|
87
|
+
_chunkX4REO3S7js.Page,
|
|
85
88
|
{
|
|
86
89
|
page: content.page,
|
|
87
90
|
theme: content.theme,
|
|
@@ -92,12 +95,12 @@ function createCatchAllPage(options) {
|
|
|
92
95
|
supabaseUrl: content.supabaseUrl
|
|
93
96
|
}
|
|
94
97
|
);
|
|
95
|
-
} else if (
|
|
98
|
+
} else if (_chunkRIROJYPXjs.isEntryContent.call(void 0, content)) {
|
|
96
99
|
if (!content.templatePage) {
|
|
97
100
|
return _navigation.notFound.call(void 0, );
|
|
98
101
|
}
|
|
99
102
|
rendered = /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
|
|
100
|
-
|
|
103
|
+
_chunkX4REO3S7js.Page,
|
|
101
104
|
{
|
|
102
105
|
page: content.templatePage,
|
|
103
106
|
theme: content.theme,
|
|
@@ -123,7 +126,7 @@ function createCatchAllPage(options) {
|
|
|
123
126
|
]);
|
|
124
127
|
const path = slugToPath(slug);
|
|
125
128
|
try {
|
|
126
|
-
const content = await
|
|
129
|
+
const content = await _chunkRIROJYPXjs.loadContent.call(void 0, {
|
|
127
130
|
client: getClient(),
|
|
128
131
|
siteId: config.siteId,
|
|
129
132
|
path,
|
|
@@ -138,7 +141,7 @@ function createCatchAllPage(options) {
|
|
|
138
141
|
"[createCatchAllPage] No siteUrl configured. OpenGraph and Twitter tags will have relative URLs which may cause social sharing previews to fail. Set siteUrl option, or config.liveUrl/config.previewUrl in your riverbank.config."
|
|
139
142
|
);
|
|
140
143
|
}
|
|
141
|
-
const page =
|
|
144
|
+
const page = _chunkRIROJYPXjs.isPageContent.call(void 0, content) ? content.page : _chunkRIROJYPXjs.isEntryContent.call(void 0, content) ? {
|
|
142
145
|
name: _nullishCoalesce(content.entry.metaTitle, () => ( content.entry.title)),
|
|
143
146
|
purpose: _nullishCoalesce(content.entry.metaDescription, () => ( void 0))
|
|
144
147
|
} : { name: "Page" };
|
|
@@ -198,7 +201,7 @@ async function generateAllStaticParams() {
|
|
|
198
201
|
);
|
|
199
202
|
}
|
|
200
203
|
const { apiKey, siteId, baseUrl } = envResult.config;
|
|
201
|
-
const client =
|
|
204
|
+
const client = _chunkEC2AA2IPjs.createRiverbankClient.call(void 0, {
|
|
202
205
|
apiKey,
|
|
203
206
|
baseUrl,
|
|
204
207
|
// Disable caching for build-time fetches
|
package/dist/server/next.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["/Users/will/Projects/Business/cms/builder/packages/sdk/dist/server/next.js","../../src/next/catch-all.tsx","../../src/rendering/hooks/usePage.ts","../../src/rendering/hooks/useContent.ts","../../src/next/static-params.ts","../../src/next/index.ts"],"names":["Page"],"mappings":"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","file":"/Users/will/Projects/Business/cms/builder/packages/sdk/dist/server/next.js","sourcesContent":[null,"/**\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';\nimport type { ApiKeyType } from '../client/types';\n\n/**\n * Detect preview mode from client's API key type.\n *\n * The client automatically detects its key type from the prefix:\n * - bld_live_sk_* / bld_test_sk_* → content key → preview: false\n * - bld_preview_sk_* → preview key → preview: true\n *\n * This eliminates the need for separate env vars and ensures\n * the preview mode always matches the API key type.\n */\nfunction detectPreviewFromClient(getClient: () => { getKeyType: () => ApiKeyType }): boolean {\n const client = getClient();\n const keyType = client.getKeyType();\n\n if (keyType === 'preview') {\n return true;\n }\n\n if (keyType === 'unknown') {\n console.warn(\n '[createCatchAllPage] Could not detect API key type from prefix. ' +\n 'Expected bld_live_sk_*, bld_test_sk_*, or bld_preview_sk_*. ' +\n 'Defaulting to published content (preview: false).'\n );\n }\n\n return false;\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 (automatically from API key type)\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 preview: previewOption,\n blockOverrides,\n siteUrl,\n beforeRender,\n customMetadata,\n wrapper: Wrapper,\n } = options;\n\n // Resolve preview mode once: use explicit option if provided, otherwise detect from client key type\n const preview = previewOption ?? detectPreviewFromClient(getClient);\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 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 sdkConfig={content.sdkConfig}\n supabaseUrl={content.supabaseUrl}\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 sdkConfig={content.sdkConfig}\n supabaseUrl={content.supabaseUrl}\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 // Note: uses `preview` from outer scope (resolved once at factory creation)\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, baseUrl } = envResult.config;\n\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 try {\n const routes = await client.getAllPublishedRoutes({ siteId });\n return routes.items.map((route) => ({\n slug: pathToSlugArray(route.path),\n }));\n } catch (error) {\n // Log error but don't fail the build - allow ISR fallback\n console.error(\n '[Riverbank] generateAllStaticParams failed to fetch routes:',\n error instanceof Error ? error.message : error\n );\n // Return at least the homepage so the build doesn't fail\n return [{ slug: [] }];\n }\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"]}
|
|
1
|
+
{"version":3,"sources":["/Users/will/Projects/Business/cms/builder/packages/sdk/dist/server/next.js","../../src/next/catch-all.tsx","../../src/rendering/hooks/usePage.ts","../../src/rendering/hooks/useContent.ts","../../src/next/static-params.ts","../../src/next/index.ts"],"names":["Page"],"mappings":"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","file":"/Users/will/Projects/Business/cms/builder/packages/sdk/dist/server/next.js","sourcesContent":[null,"/**\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';\nimport type { ApiKeyType } from '../client/types';\n\n/**\n * Detect preview mode from client's API key type.\n *\n * The client automatically detects its key type from the prefix:\n * - bld_live_sk_* / bld_test_sk_* → content key → preview: false\n * - bld_preview_sk_* → preview key → preview: true\n *\n * This eliminates the need for separate env vars and ensures\n * the preview mode always matches the API key type.\n */\nfunction detectPreviewFromClient(getClient: () => { getKeyType: () => ApiKeyType }): boolean {\n const client = getClient();\n const keyType = client.getKeyType();\n\n if (keyType === 'preview') {\n return true;\n }\n\n if (keyType === 'unknown') {\n console.warn(\n '[createCatchAllPage] Could not detect API key type from prefix. ' +\n 'Expected bld_live_sk_*, bld_test_sk_*, or bld_preview_sk_*. ' +\n 'Defaulting to published content (preview: false).'\n );\n }\n\n return false;\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 (automatically from API key type)\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 preview: previewOption,\n blockOverrides,\n siteUrl,\n beforeRender,\n customMetadata,\n wrapper: Wrapper,\n } = options;\n\n // Resolve preview mode once: use explicit option if provided, otherwise detect from client key type\n const preview = previewOption ?? detectPreviewFromClient(getClient);\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 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 sdkConfig={content.sdkConfig}\n supabaseUrl={content.supabaseUrl}\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 sdkConfig={content.sdkConfig}\n supabaseUrl={content.supabaseUrl}\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 // Note: uses `preview` from outer scope (resolved once at factory creation)\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, baseUrl } = envResult.config;\n\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 try {\n const routes = await client.getAllPublishedRoutes({ siteId });\n return routes.items.map((route) => ({\n slug: pathToSlugArray(route.path),\n }));\n } catch (error) {\n // Log error but don't fail the build - allow ISR fallback\n console.error(\n '[Riverbank] generateAllStaticParams failed to fetch routes:',\n error instanceof Error ? error.message : error\n );\n // Return at least the homepage so the build doesn't fail\n return [{ slug: [] }];\n }\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"]}
|
package/dist/server/next.mjs
CHANGED
|
@@ -7,18 +7,21 @@ import {
|
|
|
7
7
|
} from "./chunk-PPHZV6YD.mjs";
|
|
8
8
|
import {
|
|
9
9
|
createRiverbankClient
|
|
10
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-QS6ZTLLB.mjs";
|
|
11
|
+
import "./chunk-WWGVFOLS.mjs";
|
|
12
|
+
import "./chunk-PAHSKNY5.mjs";
|
|
13
|
+
import "./chunk-AGAOKSPY.mjs";
|
|
11
14
|
import {
|
|
12
15
|
isEntryContent,
|
|
13
16
|
isPageContent,
|
|
14
17
|
loadContent
|
|
15
|
-
} from "./chunk-
|
|
18
|
+
} from "./chunk-TBX6CXBM.mjs";
|
|
16
19
|
import {
|
|
17
20
|
Page
|
|
18
|
-
} from "./chunk-
|
|
19
|
-
import "./chunk-
|
|
20
|
-
import "./chunk-
|
|
21
|
-
import "./chunk-
|
|
21
|
+
} from "./chunk-H44G72AB.mjs";
|
|
22
|
+
import "./chunk-CKZDJBMC.mjs";
|
|
23
|
+
import "./chunk-TBN35TGI.mjs";
|
|
24
|
+
import "./chunk-BOYBN4KN.mjs";
|
|
22
25
|
import "./chunk-WMJKH4XE.mjs";
|
|
23
26
|
|
|
24
27
|
// src/next/catch-all.tsx
|
package/dist/server/next.mjs.map
CHANGED
|
@@ -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';\nimport type { ApiKeyType } from '../client/types';\n\n/**\n * Detect preview mode from client's API key type.\n *\n * The client automatically detects its key type from the prefix:\n * - bld_live_sk_* / bld_test_sk_* → content key → preview: false\n * - bld_preview_sk_* → preview key → preview: true\n *\n * This eliminates the need for separate env vars and ensures\n * the preview mode always matches the API key type.\n */\nfunction detectPreviewFromClient(getClient: () => { getKeyType: () => ApiKeyType }): boolean {\n const client = getClient();\n const keyType = client.getKeyType();\n\n if (keyType === 'preview') {\n return true;\n }\n\n if (keyType === 'unknown') {\n console.warn(\n '[createCatchAllPage] Could not detect API key type from prefix. ' +\n 'Expected bld_live_sk_*, bld_test_sk_*, or bld_preview_sk_*. ' +\n 'Defaulting to published content (preview: false).'\n );\n }\n\n return false;\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 (automatically from API key type)\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 preview: previewOption,\n blockOverrides,\n siteUrl,\n beforeRender,\n customMetadata,\n wrapper: Wrapper,\n } = options;\n\n // Resolve preview mode once: use explicit option if provided, otherwise detect from client key type\n const preview = previewOption ?? detectPreviewFromClient(getClient);\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 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 sdkConfig={content.sdkConfig}\n supabaseUrl={content.supabaseUrl}\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 sdkConfig={content.sdkConfig}\n supabaseUrl={content.supabaseUrl}\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 // Note: uses `preview` from outer scope (resolved once at factory creation)\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, baseUrl } = envResult.config;\n\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 try {\n const routes = await client.getAllPublishedRoutes({ siteId });\n return routes.items.map((route) => ({\n slug: pathToSlugArray(route.path),\n }));\n } catch (error) {\n // Log error but don't fail the build - allow ISR fallback\n console.error(\n '[Riverbank] generateAllStaticParams failed to fetch routes:',\n error instanceof Error ? error.message : error\n );\n // Return at least the homepage so the build doesn't fail\n return [{ slug: [] }];\n }\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":["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';\nimport type { ApiKeyType } from '../client/types';\n\n/**\n * Detect preview mode from client's API key type.\n *\n * The client automatically detects its key type from the prefix:\n * - bld_live_sk_* / bld_test_sk_* → content key → preview: false\n * - bld_preview_sk_* → preview key → preview: true\n *\n * This eliminates the need for separate env vars and ensures\n * the preview mode always matches the API key type.\n */\nfunction detectPreviewFromClient(getClient: () => { getKeyType: () => ApiKeyType }): boolean {\n const client = getClient();\n const keyType = client.getKeyType();\n\n if (keyType === 'preview') {\n return true;\n }\n\n if (keyType === 'unknown') {\n console.warn(\n '[createCatchAllPage] Could not detect API key type from prefix. ' +\n 'Expected bld_live_sk_*, bld_test_sk_*, or bld_preview_sk_*. ' +\n 'Defaulting to published content (preview: false).'\n );\n }\n\n return false;\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 (automatically from API key type)\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 preview: previewOption,\n blockOverrides,\n siteUrl,\n beforeRender,\n customMetadata,\n wrapper: Wrapper,\n } = options;\n\n // Resolve preview mode once: use explicit option if provided, otherwise detect from client key type\n const preview = previewOption ?? detectPreviewFromClient(getClient);\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 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 sdkConfig={content.sdkConfig}\n supabaseUrl={content.supabaseUrl}\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 sdkConfig={content.sdkConfig}\n supabaseUrl={content.supabaseUrl}\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 // Note: uses `preview` from outer scope (resolved once at factory creation)\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, baseUrl } = envResult.config;\n\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 try {\n const routes = await client.getAllPublishedRoutes({ siteId });\n return routes.items.map((route) => ({\n slug: pathToSlugArray(route.path),\n }));\n } catch (error) {\n // Log error but don't fail the build - allow ISR fallback\n console.error(\n '[Riverbank] generateAllStaticParams failed to fetch routes:',\n error instanceof Error ? error.message : error\n );\n // Return at least the homepage so the build doesn't fail\n return [{ slug: [] }];\n }\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":["Page","useState","useEffect"]}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { NavigationCacheFile, FormsCacheFile, PrebuildManifest } from './prebuild-types.mjs';
|
|
2
|
+
import { S as SiteResponse, P as PageResponse, G as GetEntriesBaseParams, E as EntriesResponse, c as EntriesResponseWithMeta } from './types-CSvCkmYi.mjs';
|
|
3
|
+
import './types-B6P_iaDz.mjs';
|
|
4
|
+
import '@riverbankcms/ai';
|
|
5
|
+
import './schema-DYtW0zEu.mjs';
|
|
6
|
+
import 'zod';
|
|
7
|
+
import './link-DjxLyC82.mjs';
|
|
8
|
+
import '@riverbankcms/media-storage-supabase';
|
|
9
|
+
import '@riverbankcms/db';
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Prebuild cache loader
|
|
13
|
+
*
|
|
14
|
+
* Loads content from the prebuild cache at runtime as a last-resort fallback.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
interface PrebuildLoaderConfig {
|
|
18
|
+
/** Path to prebuild cache directory */
|
|
19
|
+
prebuildDir: string;
|
|
20
|
+
/** Maximum age in seconds for prebuild cache */
|
|
21
|
+
maxPrebuildAgeSec?: number;
|
|
22
|
+
}
|
|
23
|
+
interface LoadResult<T> {
|
|
24
|
+
/** Loaded data */
|
|
25
|
+
data: T;
|
|
26
|
+
/** Age of prebuild in seconds */
|
|
27
|
+
prebuildAgeSec: number;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Check if we're running in a Node.js environment with filesystem access.
|
|
31
|
+
* Edge runtimes (Vercel Edge, Cloudflare Workers) don't have fs access.
|
|
32
|
+
*/
|
|
33
|
+
declare function canUsePrebuild(): boolean;
|
|
34
|
+
/**
|
|
35
|
+
* Loader for reading data from the prebuild cache.
|
|
36
|
+
*
|
|
37
|
+
* Used as a last-resort fallback when the CMS is unavailable and
|
|
38
|
+
* there's no cached data.
|
|
39
|
+
*/
|
|
40
|
+
declare class PrebuildLoader {
|
|
41
|
+
private readonly prebuildDir;
|
|
42
|
+
private readonly maxPrebuildAgeSec;
|
|
43
|
+
constructor(config: PrebuildLoaderConfig);
|
|
44
|
+
/**
|
|
45
|
+
* Check if prebuild is available and not expired.
|
|
46
|
+
*/
|
|
47
|
+
isAvailable(): boolean;
|
|
48
|
+
/**
|
|
49
|
+
* Load site data from prebuild cache.
|
|
50
|
+
*/
|
|
51
|
+
loadSite(siteId: string): LoadResult<SiteResponse> | null;
|
|
52
|
+
/**
|
|
53
|
+
* Load page data from prebuild cache.
|
|
54
|
+
*/
|
|
55
|
+
loadPage(siteId: string, pagePath: string): LoadResult<PageResponse> | null;
|
|
56
|
+
/**
|
|
57
|
+
* Load entries from prebuild cache with runtime filtering.
|
|
58
|
+
*
|
|
59
|
+
* The prebuild stores ALL entries for each content type.
|
|
60
|
+
* Filtering, ordering, and pagination are applied at runtime.
|
|
61
|
+
*/
|
|
62
|
+
loadEntries(siteId: string, params: GetEntriesBaseParams & {
|
|
63
|
+
includeMeta?: boolean;
|
|
64
|
+
}): LoadResult<EntriesResponse | EntriesResponseWithMeta> | null;
|
|
65
|
+
/**
|
|
66
|
+
* Load navigation data from prebuild cache.
|
|
67
|
+
*/
|
|
68
|
+
loadNavigation(): LoadResult<NavigationCacheFile['menus']> | null;
|
|
69
|
+
/**
|
|
70
|
+
* Load forms data from prebuild cache.
|
|
71
|
+
*/
|
|
72
|
+
loadForms(): LoadResult<FormsCacheFile['forms']> | null;
|
|
73
|
+
/**
|
|
74
|
+
* Get the manifest for inspection.
|
|
75
|
+
*/
|
|
76
|
+
getManifest(): PrebuildManifest | null;
|
|
77
|
+
/**
|
|
78
|
+
* Clear the cached manifest (for testing).
|
|
79
|
+
*/
|
|
80
|
+
clearCache(): void;
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Create a prebuild loader instance.
|
|
84
|
+
*/
|
|
85
|
+
declare function createPrebuildLoader(config: PrebuildLoaderConfig): PrebuildLoader;
|
|
86
|
+
|
|
87
|
+
export { type LoadResult, PrebuildLoader, type PrebuildLoaderConfig, canUsePrebuild, createPrebuildLoader };
|