@riverbankcms/sdk 0.7.2 → 0.7.4
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 +54 -0
- package/dist/client/bookings.d.mts +2 -0
- package/dist/client/bookings.d.ts +2 -0
- package/dist/client/bookings.js +2956 -104
- package/dist/client/bookings.js.map +1 -1
- package/dist/client/bookings.mjs +2929 -70
- package/dist/client/bookings.mjs.map +1 -1
- package/dist/client/client.d.mts +2 -2
- package/dist/client/client.d.ts +2 -2
- package/dist/client/client.js +80 -11
- package/dist/client/client.js.map +1 -1
- package/dist/client/client.mjs +87 -11
- 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/rendering/client.js +3070 -259
- package/dist/client/rendering/client.js.map +1 -1
- package/dist/client/rendering/client.mjs +3212 -395
- package/dist/client/rendering/client.mjs.map +1 -1
- package/dist/client/spam-protection.d.mts +55 -0
- package/dist/client/spam-protection.d.ts +55 -0
- package/dist/client/spam-protection.js +2915 -0
- package/dist/client/spam-protection.js.map +1 -0
- package/dist/client/spam-protection.mjs +2893 -0
- package/dist/client/spam-protection.mjs.map +1 -0
- package/dist/client/{usePage-Db9kzA41.d.ts → usePage-BYmJCCm1.d.ts} +14 -2
- package/dist/client/{usePage-C9tJpuKa.d.mts → usePage-DZtrWajy.d.mts} +14 -2
- package/dist/server/{Layout-Ce7PU9I5.d.ts → Layout-Yluyb6sK.d.ts} +1 -1
- package/dist/server/{Layout-WllR8Zug.d.mts → Layout-qWLdVm5-.d.mts} +1 -1
- package/dist/server/chunk-2IZ6S225.js +122 -0
- package/dist/server/chunk-2IZ6S225.js.map +1 -0
- package/dist/server/chunk-4CV4JOE5.js +27 -0
- package/dist/server/chunk-4CV4JOE5.js.map +1 -0
- package/dist/server/{chunk-AET56TQX.mjs → chunk-5LRR64Y6.mjs} +32 -5
- package/dist/server/chunk-5LRR64Y6.mjs.map +1 -0
- package/dist/server/{chunk-HMENX4Y7.js → chunk-6Z4MQG47.js} +370 -49
- package/dist/server/chunk-6Z4MQG47.js.map +1 -0
- package/dist/server/{chunk-5JT452F2.mjs → chunk-JNU7ZS2V.mjs} +340 -19
- package/dist/server/chunk-JNU7ZS2V.mjs.map +1 -0
- package/dist/server/{chunk-VODFQMUW.js → chunk-NBTRDLCM.js} +32 -5
- package/dist/server/chunk-NBTRDLCM.js.map +1 -0
- package/dist/server/chunk-NFEGQTCC.mjs +27 -0
- package/dist/server/chunk-PPHZV6YD.mjs +122 -0
- package/dist/server/chunk-PPHZV6YD.mjs.map +1 -0
- package/dist/server/{components-RPzRQve6.d.mts → components-DNHfSCML.d.mts} +3 -3
- package/dist/server/{components--LT61IKp.d.ts → components-Di5ME6He.d.ts} +3 -3
- package/dist/server/components.d.mts +5 -5
- package/dist/server/components.d.ts +5 -5
- package/dist/server/components.js +1 -0
- package/dist/server/components.js.map +1 -1
- package/dist/server/components.mjs +1 -0
- package/dist/server/config-validation.js +1 -0
- package/dist/server/config-validation.js.map +1 -1
- package/dist/server/config-validation.mjs +1 -0
- package/dist/server/config.js +1 -0
- package/dist/server/config.js.map +1 -1
- package/dist/server/config.mjs +1 -0
- package/dist/server/config.mjs.map +1 -1
- package/dist/server/data.d.mts +2 -2
- package/dist/server/data.d.ts +2 -2
- package/dist/server/data.js +1 -0
- package/dist/server/data.js.map +1 -1
- package/dist/server/data.mjs +1 -0
- package/dist/server/env.d.mts +91 -5
- package/dist/server/env.d.ts +91 -5
- package/dist/server/env.js +9 -2
- package/dist/server/env.js.map +1 -1
- package/dist/server/env.mjs +8 -1
- package/dist/server/{index-BL66CU6d.d.mts → index--Oyunk_B.d.mts} +2 -2
- package/dist/server/{index-CJk9iQQW.d.ts → index-C9Ra8dza.d.ts} +2 -2
- package/dist/server/{index-Bkva0WAj.d.mts → index-Clm3skz_.d.mts} +1 -1
- package/dist/server/{index-CSBWKA3r.d.ts → index-DLvNddi-.d.ts} +1 -1
- package/dist/server/index.d.mts +215 -4
- package/dist/server/index.d.ts +215 -4
- package/dist/server/index.js +301 -3
- package/dist/server/index.js.map +1 -1
- package/dist/server/index.mjs +301 -3
- package/dist/server/index.mjs.map +1 -1
- package/dist/server/{loadContent-CXUWMuzY.d.ts → loadContent-D7LQwI0o.d.ts} +3 -3
- package/dist/server/{loadContent-F_tAS0Nl.d.mts → loadContent-DVfuBLiZ.d.mts} +3 -3
- package/dist/server/{loadPage-6I7F6GRF.js → loadPage-AXNAERDS.js} +2 -1
- package/dist/server/loadPage-AXNAERDS.js.map +1 -0
- package/dist/server/{loadPage-i2r-X5b9.d.ts → loadPage-BmYJCe_V.d.ts} +2 -2
- package/dist/server/{loadPage-CxlYLe5K.d.mts → loadPage-BucnLHmE.d.mts} +2 -2
- package/dist/server/{loadPage-JI2SML4M.mjs → loadPage-XR7ORQ2E.mjs} +2 -1
- package/dist/server/loadPage-XR7ORQ2E.mjs.map +1 -0
- package/dist/server/metadata.d.mts +4 -4
- package/dist/server/metadata.d.ts +4 -4
- package/dist/server/metadata.js +1 -0
- package/dist/server/metadata.js.map +1 -1
- package/dist/server/metadata.mjs +1 -0
- package/dist/server/navigation.d.mts +2 -2
- package/dist/server/navigation.d.ts +2 -2
- package/dist/server/navigation.js +1 -0
- package/dist/server/navigation.js.map +1 -1
- package/dist/server/navigation.mjs +1 -0
- package/dist/server/next/revalidate.js +5 -4
- package/dist/server/next/revalidate.js.map +1 -1
- package/dist/server/next/revalidate.mjs +3 -2
- package/dist/server/next/revalidate.mjs.map +1 -1
- package/dist/server/next/tags.d.mts +3 -0
- package/dist/server/next/tags.d.ts +3 -0
- package/dist/server/next/tags.js +3 -1
- package/dist/server/next/tags.js.map +1 -1
- package/dist/server/next/tags.mjs +2 -0
- package/dist/server/next/tags.mjs.map +1 -1
- package/dist/server/next.d.mts +5 -5
- package/dist/server/next.d.ts +5 -5
- package/dist/server/next.js +5 -4
- package/dist/server/next.js.map +1 -1
- package/dist/server/next.mjs +3 -2
- package/dist/server/next.mjs.map +1 -1
- package/dist/server/rendering/server.d.mts +4 -4
- package/dist/server/rendering/server.d.ts +4 -4
- package/dist/server/rendering/server.js +1 -0
- package/dist/server/rendering/server.js.map +1 -1
- package/dist/server/rendering/server.mjs +1 -0
- package/dist/server/rendering.d.mts +7 -7
- package/dist/server/rendering.d.ts +7 -7
- package/dist/server/rendering.js +1 -0
- package/dist/server/rendering.js.map +1 -1
- package/dist/server/rendering.mjs +1 -0
- package/dist/server/routing.d.mts +3 -3
- package/dist/server/routing.d.ts +3 -3
- package/dist/server/routing.js +4 -2
- package/dist/server/routing.js.map +1 -1
- package/dist/server/routing.mjs +3 -1
- package/dist/server/routing.mjs.map +1 -1
- package/dist/server/server.d.mts +5 -5
- package/dist/server/server.d.ts +5 -5
- package/dist/server/server.js +3 -2
- package/dist/server/server.js.map +1 -1
- package/dist/server/server.mjs +2 -1
- package/dist/server/theme-bridge.js +1 -0
- package/dist/server/theme-bridge.js.map +1 -1
- package/dist/server/theme-bridge.mjs +1 -0
- package/dist/server/theme-bridge.mjs.map +1 -1
- package/dist/server/theme.js +3 -1
- package/dist/server/theme.js.map +1 -1
- package/dist/server/theme.mjs +2 -0
- package/dist/server/theme.mjs.map +1 -1
- package/dist/server/{types-DnkRh0UL.d.ts → types-BRQyLrQU.d.ts} +14 -2
- package/dist/server/{types-Dsu9wsUh.d.mts → types-BSV6Vc-P.d.mts} +2 -2
- package/dist/server/{types-MF2AWoKv.d.mts → types-C-LShyIg.d.mts} +14 -2
- package/dist/server/{types-CVykEqXN.d.ts → types-Dt98DeYa.d.ts} +2 -2
- package/dist/server/webhooks.d.mts +8 -2
- package/dist/server/webhooks.d.ts +8 -2
- package/dist/server/webhooks.js +3 -2
- package/dist/server/webhooks.js.map +1 -1
- package/dist/server/webhooks.mjs +2 -1
- package/package.json +7 -1
- package/dist/client/resolver-BhueZVxZ.d.mts +0 -61
- package/dist/client/resolver-BhueZVxZ.d.ts +0 -61
- package/dist/client/usePage--fGlyrgj.d.mts +0 -6439
- package/dist/client/usePage-BBcFCxOU.d.ts +0 -6297
- package/dist/client/usePage-BC8Q2E3t.d.mts +0 -6431
- package/dist/client/usePage-BTPnCuWC.d.mts +0 -6511
- package/dist/client/usePage-BXjk8BhD.d.mts +0 -6704
- package/dist/client/usePage-BafOS9UT.d.mts +0 -6512
- package/dist/client/usePage-BcjWPXvh.d.mts +0 -6388
- package/dist/client/usePage-BiOReg0_.d.ts +0 -6704
- package/dist/client/usePage-Bnx-kA6x.d.mts +0 -6670
- package/dist/client/usePage-BvKAa3Zw.d.mts +0 -366
- package/dist/client/usePage-BvKAa3Zw.d.ts +0 -366
- package/dist/client/usePage-BydHcMYB.d.mts +0 -6297
- package/dist/client/usePage-C3ZKNwY7.d.mts +0 -6393
- package/dist/client/usePage-CE7X5NcN.d.ts +0 -6439
- package/dist/client/usePage-CHEybPMD.d.ts +0 -6429
- package/dist/client/usePage-CrKw1H6Y.d.ts +0 -6338
- package/dist/client/usePage-CyYpOJud.d.ts +0 -6388
- package/dist/client/usePage-D4fxZbRR.d.mts +0 -6429
- package/dist/client/usePage-DMI8ImsU.d.mts +0 -6338
- package/dist/client/usePage-DoPI6b8V.d.ts +0 -6511
- package/dist/client/usePage-DpRNZUtP.d.ts +0 -6431
- package/dist/client/usePage-QNWArrVO.d.ts +0 -6670
- package/dist/client/usePage-fBgPB6Oq.d.ts +0 -6512
- package/dist/client/usePage-gpVaeWDy.d.ts +0 -6393
- package/dist/server/chunk-5JT452F2.mjs.map +0 -1
- package/dist/server/chunk-AET56TQX.mjs.map +0 -1
- package/dist/server/chunk-HMENX4Y7.js.map +0 -1
- package/dist/server/chunk-LQUKXIW7.mjs +0 -13
- package/dist/server/chunk-LQUKXIW7.mjs.map +0 -1
- package/dist/server/chunk-VODFQMUW.js.map +0 -1
- package/dist/server/chunk-WYNEYDXO.js +0 -13
- package/dist/server/chunk-WYNEYDXO.js.map +0 -1
- package/dist/server/loadPage-6I7F6GRF.js.map +0 -1
- /package/dist/server/{loadPage-JI2SML4M.mjs.map → chunk-NFEGQTCC.mjs.map} +0 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["/Users/will/Projects/Business/cms/builder/packages/sdk/dist/server/next/revalidate.js","../../../src/next/revalidate.ts"],"names":[],"mappings":"AAAA;AACE;AACA;AACF,uDAA6B;AAC7B;AACA;
|
|
1
|
+
{"version":3,"sources":["/Users/will/Projects/Business/cms/builder/packages/sdk/dist/server/next/revalidate.js","../../../src/next/revalidate.ts"],"names":[],"mappings":"AAAA;AACE;AACA;AACF,uDAA6B;AAC7B,gCAA6B;AAC7B;AACA;ACoBA,mCAA8C;AA2CvC,SAAS,uBAAA,CACd,QAAA,EAAoC,CAAC,CAAA,EACI;AACzC,EAAA,OAAO,MAAA,SAAe,uBAAA,CAAwB,OAAA,EAAqC;AAEjF,IAAA,MAAM,OAAA,mBAAS,OAAA,CAAQ,MAAA,UAAU,OAAA,CAAQ,GAAA,CAAI,0BAAA;AAE7C,IAAA,GAAA,CAAI,CAAC,MAAA,EAAQ;AACX,MAAA,OAAA,CAAQ,KAAA,CAAM,qDAAqD,CAAA;AACnE,MAAA,OAAO,QAAA,CAAS,IAAA;AAAA,QACd,EAAE,KAAA,EAAO,gCAAgC,CAAA;AAAA,QACzC,EAAE,MAAA,EAAQ,IAAI;AAAA,MAChB,CAAA;AAAA,IACF;AAGA,IAAA,MAAM,KAAA,EAAO,MAAM,OAAA,CAAQ,IAAA,CAAK,CAAA;AAChC,IAAA,MAAM,UAAA,EAAY,OAAA,CAAQ,OAAA,CAAQ,GAAA,CAAI,uBAAuB,CAAA;AAG7D,IAAA,GAAA,CAAI,CAAC,UAAA,GAAa,CAAC,qDAAA,IAAuB,EAAM,SAAA,EAAW,MAAM,CAAA,EAAG;AAClE,MAAA,OAAO,QAAA,CAAS,IAAA,CAAK,EAAE,KAAA,EAAO,oBAAoB,CAAA,EAAG,EAAE,MAAA,EAAQ,IAAI,CAAC,CAAA;AAAA,IACtE;AAGA,IAAA,MAAM,YAAA,EAAc,kDAAA,IAAwB,CAAA;AAC5C,IAAA,GAAA,CAAI,CAAC,WAAA,CAAY,OAAA,EAAS;AACxB,MAAA,OAAO,QAAA,CAAS,IAAA,CAAK,EAAE,KAAA,EAAO,WAAA,CAAY,MAAM,CAAA,EAAG,EAAE,MAAA,EAAQ,IAAI,CAAC,CAAA;AAAA,IACpE;AACA,IAAA,MAAM,QAAA,EAAU,WAAA,CAAY,OAAA;AAC5B,IAAA,MAAM,YAAA,EAAwB,CAAC,CAAA;AAG/B,IAAA,GAAA,iBAAI,OAAA,mBAAQ,IAAA,6BAAM,QAAA,EAAQ;AACxB,MAAA,IAAA,CAAA,MAAW,IAAA,GAAO,OAAA,CAAQ,IAAA,EAAM;AAC9B,QAAA,kCAAA,GAAiB,CAAA;AACjB,QAAA,WAAA,CAAY,IAAA,CAAK,CAAA,IAAA,EAAO,GAAG,CAAA,CAAA;AAC7B,MAAA;AACF,IAAA;AAG4B,IAAA;AAClB,IAAA;AACW,MAAA;AACU,MAAA;AAC/B,IAAA;AAG4B,IAAA;AAClB,MAAA;AACsB,QAAA;AAC9B,MAAA;AACF,IAAA;AAG0B,IAAA;AACM,MAAA;AAChC,IAAA;AAEqB,IAAA;AACN,MAAA;AACN,MAAA;AACQ,MAAA;AACK,MAAA;AACrB,IAAA;AACH,EAAA;AACF;AD/EoC;AACA;AACA","file":"/Users/will/Projects/Business/cms/builder/packages/sdk/dist/server/next/revalidate.js","sourcesContent":[null,"/**\n * Next.js revalidation handler for webhook-based cache invalidation\n *\n * This module provides a pre-built API route handler for processing\n * CMS webhooks and revalidating Next.js cache entries.\n *\n * @example One-line setup in consumer site\n * ```ts\n * // app/api/revalidate/route.ts\n * import { createRevalidateHandler } from '@riverbankcms/sdk/next/revalidate';\n * export const POST = createRevalidateHandler();\n * ```\n *\n * @example With custom options\n * ```ts\n * // app/api/revalidate/route.ts\n * import { createRevalidateHandler } from '@riverbankcms/sdk/next/revalidate';\n *\n * export const POST = createRevalidateHandler({\n * onRevalidate: (payload, items) => {\n * console.log(`Revalidated ${items.length} items for ${payload.event}`);\n * },\n * });\n * ```\n */\n\nimport { revalidateTag, revalidatePath } from 'next/cache';\nimport {\n verifyWebhookSignature,\n parseWebhookPayload,\n type WebhookPayload,\n} from '../webhooks/verify';\n\n/**\n * Options for the revalidation handler.\n */\nexport interface RevalidateHandlerOptions {\n /**\n * Override the webhook secret. Defaults to RIVERBANK_WEBHOOK_SECRET env var.\n */\n secret?: string;\n\n /**\n * Callback called after successful revalidation.\n * Useful for logging or analytics.\n */\n onRevalidate?: (payload: WebhookPayload, revalidatedItems: string[]) => void;\n}\n\n/**\n * Create a Next.js API route handler for webhook revalidation.\n *\n * The handler:\n * 1. Verifies the webhook signature using HMAC-SHA256\n * 2. Calls `revalidateTag()` for each tag in the payload\n * 3. Calls `revalidatePath()` if a path is included in the data\n * 4. Returns a JSON response with the revalidation results\n *\n * @param options - Configuration options\n * @returns A Next.js Route Handler function\n *\n * @example\n * ```ts\n * // app/api/revalidate/route.ts\n * import { createRevalidateHandler } from '@riverbankcms/sdk/next/revalidate';\n *\n * export const POST = createRevalidateHandler();\n * ```\n */\nexport function createRevalidateHandler(\n options: RevalidateHandlerOptions = {}\n): (request: Request) => Promise<Response> {\n return async function handleRevalidateRequest(request: Request): Promise<Response> {\n // Get webhook secret\n const secret = options.secret ?? process.env.RIVERBANK_WEBHOOK_SECRET;\n\n if (!secret) {\n console.error('[Riverbank] RIVERBANK_WEBHOOK_SECRET not configured');\n return Response.json(\n { error: 'Webhook secret not configured' },\n { status: 500 }\n );\n }\n\n // Read request body\n const body = await request.text();\n const signature = request.headers.get('x-riverbank-signature');\n\n // Verify signature\n if (!signature || !verifyWebhookSignature(body, signature, secret)) {\n return Response.json({ error: 'Invalid signature' }, { status: 401 });\n }\n\n // Parse and validate payload\n const parseResult = parseWebhookPayload(body);\n if (!parseResult.success) {\n return Response.json({ error: parseResult.error }, { status: 400 });\n }\n const payload = parseResult.payload;\n const revalidated: string[] = [];\n\n // Tag-based revalidation (preferred)\n if (payload.tags?.length) {\n for (const tag of payload.tags) {\n revalidateTag(tag);\n revalidated.push(`tag:${tag}`);\n }\n }\n\n // Path-based fallback (from data.path if present)\n const path = typeof payload.data?.path === 'string' ? payload.data.path : undefined;\n if (path) {\n revalidatePath(path);\n revalidated.push(`path:${path}`);\n }\n\n // Log revalidation\n if (revalidated.length > 0) {\n console.log(\n `[Riverbank] Revalidated: ${revalidated.join(', ')} (event: ${payload.event})`\n );\n }\n\n // Call optional callback\n if (options.onRevalidate) {\n options.onRevalidate(payload, revalidated);\n }\n\n return Response.json({\n revalidated: true,\n items: revalidated,\n event: payload.event,\n timestamp: Date.now(),\n });\n };\n}\n"]}
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import {
|
|
2
2
|
parseWebhookPayload,
|
|
3
3
|
verifyWebhookSignature
|
|
4
|
-
} from "../chunk-
|
|
4
|
+
} from "../chunk-5LRR64Y6.mjs";
|
|
5
|
+
import "../chunk-NFEGQTCC.mjs";
|
|
5
6
|
|
|
6
7
|
// src/next/revalidate.ts
|
|
7
8
|
import { revalidateTag, revalidatePath } from "next/cache";
|
|
@@ -32,7 +33,7 @@ function createRevalidateHandler(options = {}) {
|
|
|
32
33
|
revalidated.push(`tag:${tag}`);
|
|
33
34
|
}
|
|
34
35
|
}
|
|
35
|
-
const path = payload.data?.path;
|
|
36
|
+
const path = typeof payload.data?.path === "string" ? payload.data.path : void 0;
|
|
36
37
|
if (path) {
|
|
37
38
|
revalidatePath(path);
|
|
38
39
|
revalidated.push(`path:${path}`);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/next/revalidate.ts"],"sourcesContent":["/**\n * Next.js revalidation handler for webhook-based cache invalidation\n *\n * This module provides a pre-built API route handler for processing\n * CMS webhooks and revalidating Next.js cache entries.\n *\n * @example One-line setup in consumer site\n * ```ts\n * // app/api/revalidate/route.ts\n * import { createRevalidateHandler } from '@riverbankcms/sdk/next/revalidate';\n * export const POST = createRevalidateHandler();\n * ```\n *\n * @example With custom options\n * ```ts\n * // app/api/revalidate/route.ts\n * import { createRevalidateHandler } from '@riverbankcms/sdk/next/revalidate';\n *\n * export const POST = createRevalidateHandler({\n * onRevalidate: (payload, items) => {\n * console.log(`Revalidated ${items.length} items for ${payload.event}`);\n * },\n * });\n * ```\n */\n\nimport { revalidateTag, revalidatePath } from 'next/cache';\nimport {\n verifyWebhookSignature,\n parseWebhookPayload,\n type WebhookPayload,\n} from '../webhooks/verify';\n\n/**\n * Options for the revalidation handler.\n */\nexport interface RevalidateHandlerOptions {\n /**\n * Override the webhook secret. Defaults to RIVERBANK_WEBHOOK_SECRET env var.\n */\n secret?: string;\n\n /**\n * Callback called after successful revalidation.\n * Useful for logging or analytics.\n */\n onRevalidate?: (payload: WebhookPayload, revalidatedItems: string[]) => void;\n}\n\n/**\n * Create a Next.js API route handler for webhook revalidation.\n *\n * The handler:\n * 1. Verifies the webhook signature using HMAC-SHA256\n * 2. Calls `revalidateTag()` for each tag in the payload\n * 3. Calls `revalidatePath()` if a path is included in the data\n * 4. Returns a JSON response with the revalidation results\n *\n * @param options - Configuration options\n * @returns A Next.js Route Handler function\n *\n * @example\n * ```ts\n * // app/api/revalidate/route.ts\n * import { createRevalidateHandler } from '@riverbankcms/sdk/next/revalidate';\n *\n * export const POST = createRevalidateHandler();\n * ```\n */\nexport function createRevalidateHandler(\n options: RevalidateHandlerOptions = {}\n): (request: Request) => Promise<Response> {\n return async function handleRevalidateRequest(request: Request): Promise<Response> {\n // Get webhook secret\n const secret = options.secret ?? process.env.RIVERBANK_WEBHOOK_SECRET;\n\n if (!secret) {\n console.error('[Riverbank] RIVERBANK_WEBHOOK_SECRET not configured');\n return Response.json(\n { error: 'Webhook secret not configured' },\n { status: 500 }\n );\n }\n\n // Read request body\n const body = await request.text();\n const signature = request.headers.get('x-riverbank-signature');\n\n // Verify signature\n if (!signature || !verifyWebhookSignature(body, signature, secret)) {\n return Response.json({ error: 'Invalid signature' }, { status: 401 });\n }\n\n // Parse and validate payload\n const parseResult = parseWebhookPayload(body);\n if (!parseResult.success) {\n return Response.json({ error: parseResult.error }, { status: 400 });\n }\n const payload = parseResult.payload;\n const revalidated: string[] = [];\n\n // Tag-based revalidation (preferred)\n if (payload.tags?.length) {\n for (const tag of payload.tags) {\n revalidateTag(tag);\n revalidated.push(`tag:${tag}`);\n }\n }\n\n // Path-based fallback (from data.path if present)\n const path = payload.data?.path
|
|
1
|
+
{"version":3,"sources":["../../../src/next/revalidate.ts"],"sourcesContent":["/**\n * Next.js revalidation handler for webhook-based cache invalidation\n *\n * This module provides a pre-built API route handler for processing\n * CMS webhooks and revalidating Next.js cache entries.\n *\n * @example One-line setup in consumer site\n * ```ts\n * // app/api/revalidate/route.ts\n * import { createRevalidateHandler } from '@riverbankcms/sdk/next/revalidate';\n * export const POST = createRevalidateHandler();\n * ```\n *\n * @example With custom options\n * ```ts\n * // app/api/revalidate/route.ts\n * import { createRevalidateHandler } from '@riverbankcms/sdk/next/revalidate';\n *\n * export const POST = createRevalidateHandler({\n * onRevalidate: (payload, items) => {\n * console.log(`Revalidated ${items.length} items for ${payload.event}`);\n * },\n * });\n * ```\n */\n\nimport { revalidateTag, revalidatePath } from 'next/cache';\nimport {\n verifyWebhookSignature,\n parseWebhookPayload,\n type WebhookPayload,\n} from '../webhooks/verify';\n\n/**\n * Options for the revalidation handler.\n */\nexport interface RevalidateHandlerOptions {\n /**\n * Override the webhook secret. Defaults to RIVERBANK_WEBHOOK_SECRET env var.\n */\n secret?: string;\n\n /**\n * Callback called after successful revalidation.\n * Useful for logging or analytics.\n */\n onRevalidate?: (payload: WebhookPayload, revalidatedItems: string[]) => void;\n}\n\n/**\n * Create a Next.js API route handler for webhook revalidation.\n *\n * The handler:\n * 1. Verifies the webhook signature using HMAC-SHA256\n * 2. Calls `revalidateTag()` for each tag in the payload\n * 3. Calls `revalidatePath()` if a path is included in the data\n * 4. Returns a JSON response with the revalidation results\n *\n * @param options - Configuration options\n * @returns A Next.js Route Handler function\n *\n * @example\n * ```ts\n * // app/api/revalidate/route.ts\n * import { createRevalidateHandler } from '@riverbankcms/sdk/next/revalidate';\n *\n * export const POST = createRevalidateHandler();\n * ```\n */\nexport function createRevalidateHandler(\n options: RevalidateHandlerOptions = {}\n): (request: Request) => Promise<Response> {\n return async function handleRevalidateRequest(request: Request): Promise<Response> {\n // Get webhook secret\n const secret = options.secret ?? process.env.RIVERBANK_WEBHOOK_SECRET;\n\n if (!secret) {\n console.error('[Riverbank] RIVERBANK_WEBHOOK_SECRET not configured');\n return Response.json(\n { error: 'Webhook secret not configured' },\n { status: 500 }\n );\n }\n\n // Read request body\n const body = await request.text();\n const signature = request.headers.get('x-riverbank-signature');\n\n // Verify signature\n if (!signature || !verifyWebhookSignature(body, signature, secret)) {\n return Response.json({ error: 'Invalid signature' }, { status: 401 });\n }\n\n // Parse and validate payload\n const parseResult = parseWebhookPayload(body);\n if (!parseResult.success) {\n return Response.json({ error: parseResult.error }, { status: 400 });\n }\n const payload = parseResult.payload;\n const revalidated: string[] = [];\n\n // Tag-based revalidation (preferred)\n if (payload.tags?.length) {\n for (const tag of payload.tags) {\n revalidateTag(tag);\n revalidated.push(`tag:${tag}`);\n }\n }\n\n // Path-based fallback (from data.path if present)\n const path = typeof payload.data?.path === 'string' ? payload.data.path : undefined;\n if (path) {\n revalidatePath(path);\n revalidated.push(`path:${path}`);\n }\n\n // Log revalidation\n if (revalidated.length > 0) {\n console.log(\n `[Riverbank] Revalidated: ${revalidated.join(', ')} (event: ${payload.event})`\n );\n }\n\n // Call optional callback\n if (options.onRevalidate) {\n options.onRevalidate(payload, revalidated);\n }\n\n return Response.json({\n revalidated: true,\n items: revalidated,\n event: payload.event,\n timestamp: Date.now(),\n });\n };\n}\n"],"mappings":";;;;;;;AA0BA,SAAS,eAAe,sBAAsB;AA2CvC,SAAS,wBACd,UAAoC,CAAC,GACI;AACzC,SAAO,eAAe,wBAAwB,SAAqC;AAEjF,UAAM,SAAS,QAAQ,UAAU,QAAQ,IAAI;AAE7C,QAAI,CAAC,QAAQ;AACX,cAAQ,MAAM,qDAAqD;AACnE,aAAO,SAAS;AAAA,QACd,EAAE,OAAO,gCAAgC;AAAA,QACzC,EAAE,QAAQ,IAAI;AAAA,MAChB;AAAA,IACF;AAGA,UAAM,OAAO,MAAM,QAAQ,KAAK;AAChC,UAAM,YAAY,QAAQ,QAAQ,IAAI,uBAAuB;AAG7D,QAAI,CAAC,aAAa,CAAC,uBAAuB,MAAM,WAAW,MAAM,GAAG;AAClE,aAAO,SAAS,KAAK,EAAE,OAAO,oBAAoB,GAAG,EAAE,QAAQ,IAAI,CAAC;AAAA,IACtE;AAGA,UAAM,cAAc,oBAAoB,IAAI;AAC5C,QAAI,CAAC,YAAY,SAAS;AACxB,aAAO,SAAS,KAAK,EAAE,OAAO,YAAY,MAAM,GAAG,EAAE,QAAQ,IAAI,CAAC;AAAA,IACpE;AACA,UAAM,UAAU,YAAY;AAC5B,UAAM,cAAwB,CAAC;AAG/B,QAAI,QAAQ,MAAM,QAAQ;AACxB,iBAAW,OAAO,QAAQ,MAAM;AAC9B,sBAAc,GAAG;AACjB,oBAAY,KAAK,OAAO,GAAG,EAAE;AAAA,MAC/B;AAAA,IACF;AAGA,UAAM,OAAO,OAAO,QAAQ,MAAM,SAAS,WAAW,QAAQ,KAAK,OAAO;AAC1E,QAAI,MAAM;AACR,qBAAe,IAAI;AACnB,kBAAY,KAAK,QAAQ,IAAI,EAAE;AAAA,IACjC;AAGA,QAAI,YAAY,SAAS,GAAG;AAC1B,cAAQ;AAAA,QACN,4BAA4B,YAAY,KAAK,IAAI,CAAC,YAAY,QAAQ,KAAK;AAAA,MAC7E;AAAA,IACF;AAGA,QAAI,QAAQ,cAAc;AACxB,cAAQ,aAAa,SAAS,WAAW;AAAA,IAC3C;AAEA,WAAO,SAAS,KAAK;AAAA,MACnB,aAAa;AAAA,MACb,OAAO;AAAA,MACP,OAAO,QAAQ;AAAA,MACf,WAAW,KAAK,IAAI;AAAA,IACtB,CAAC;AAAA,EACH;AACF;","names":[]}
|
|
@@ -26,6 +26,9 @@
|
|
|
26
26
|
/**
|
|
27
27
|
* Global tag that invalidates ALL Riverbank data.
|
|
28
28
|
* Revalidating this tag will refresh everything.
|
|
29
|
+
*
|
|
30
|
+
* NOTE: This constant is also defined in apps/dashboard/src/lib/webhooks/dispatch.ts.
|
|
31
|
+
* If you change this value, update both locations.
|
|
29
32
|
*/
|
|
30
33
|
declare const RIVERBANK_GLOBAL_TAG = "riverbank";
|
|
31
34
|
/**
|
|
@@ -26,6 +26,9 @@
|
|
|
26
26
|
/**
|
|
27
27
|
* Global tag that invalidates ALL Riverbank data.
|
|
28
28
|
* Revalidating this tag will refresh everything.
|
|
29
|
+
*
|
|
30
|
+
* NOTE: This constant is also defined in apps/dashboard/src/lib/webhooks/dispatch.ts.
|
|
31
|
+
* If you change this value, update both locations.
|
|
29
32
|
*/
|
|
30
33
|
declare const RIVERBANK_GLOBAL_TAG = "riverbank";
|
|
31
34
|
/**
|
package/dist/server/next/tags.js
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports, "__esModule", {value: true})
|
|
1
|
+
"use strict";Object.defineProperty(exports, "__esModule", {value: true});require('../chunk-4CV4JOE5.js');
|
|
2
|
+
|
|
3
|
+
// src/next/tags.ts
|
|
2
4
|
var RIVERBANK_GLOBAL_TAG = "riverbank";
|
|
3
5
|
function getSiteTags() {
|
|
4
6
|
return [RIVERBANK_GLOBAL_TAG, "riverbank:site"];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["/Users/will/Projects/Business/cms/builder/packages/sdk/dist/server/next/tags.js","../../../src/next/tags.ts"],"names":[],"mappings":"AAAA;
|
|
1
|
+
{"version":3,"sources":["/Users/will/Projects/Business/cms/builder/packages/sdk/dist/server/next/tags.js","../../../src/next/tags.ts"],"names":[],"mappings":"AAAA,yGAA6B;AAC7B;AACA;AC+BO,IAAM,qBAAA,EAAuB,WAAA;AAO7B,SAAS,WAAA,CAAA,EAAwB;AACtC,EAAA,OAAO,CAAC,oBAAA,EAAsB,gBAAgB,CAAA;AAChD;AAcO,SAAS,WAAA,CAAY,MAAA,EAA2B;AACrD,EAAA,MAAM,KAAA,EAAO,CAAC,oBAAA,EAAsB,iBAAiB,CAAA;AACrD,EAAA,GAAA,CAAI,MAAA,EAAQ;AACV,IAAA,IAAA,CAAK,IAAA,CAAK,CAAA,eAAA,EAAkB,MAAM,CAAA,CAAA;AACpC,EAAA;AACO,EAAA;AACT;AAesD;AAChB,EAAA;AACvB,EAAA;AACkB,IAAA;AAC/B,EAAA;AACO,EAAA;AACT;AAc8D;AACxB,EAAA;AACvB,EAAA;AACuB,IAAA;AACpC,EAAA;AACO,EAAA;AACT;AD3EuC;AACA;AACA;AACA;AACA;AACA;AACA","file":"/Users/will/Projects/Business/cms/builder/packages/sdk/dist/server/next/tags.js","sourcesContent":[null,"/**\n * Tag generation helpers for Next.js cache invalidation\n *\n * These helpers generate consistent cache tags for use with Next.js\n * `unstable_cache` and `revalidateTag` APIs.\n *\n * Tag naming convention:\n * - `riverbank` - Global tag that invalidates ALL Riverbank data\n * - `riverbank:<resource>` - Collection tag (e.g., `riverbank:pages`)\n * - `riverbank:<resource>:<id>` - Specific resource tag (e.g., `riverbank:page:page-123`)\n *\n * @example\n * ```tsx\n * // Consumer site: lib/cms-fetchers.ts\n * import { unstable_cache } from 'next/cache';\n * import { loadPage } from '@riverbankcms/sdk';\n * import { getPageTags } from '@riverbankcms/sdk/next/tags';\n *\n * export const getCachedPage = unstable_cache(\n * async (path: string) => loadPage({ client, siteId, path }),\n * ['riverbank-page'],\n * { tags: getPageTags(), revalidate: 300 }\n * );\n * ```\n */\n\n/**\n * Global tag that invalidates ALL Riverbank data.\n * Revalidating this tag will refresh everything.\n *\n * NOTE: This constant is also defined in apps/dashboard/src/lib/webhooks/dispatch.ts.\n * If you change this value, update both locations.\n */\nexport const RIVERBANK_GLOBAL_TAG = 'riverbank';\n\n/**\n * Generate tags for site-level data (settings, theme).\n *\n * @returns Array of tags: ['riverbank', 'riverbank:site']\n */\nexport function getSiteTags(): string[] {\n return [RIVERBANK_GLOBAL_TAG, 'riverbank:site'];\n}\n\n/**\n * Generate tags for page data.\n *\n * @param pageId - Optional specific page ID to include\n * @returns Array of tags including global, pages collection, and optionally page-specific\n *\n * @example\n * ```ts\n * getPageTags() // ['riverbank', 'riverbank:pages']\n * getPageTags('page-123') // ['riverbank', 'riverbank:pages', 'riverbank:page:page-123']\n * ```\n */\nexport function getPageTags(pageId?: string): string[] {\n const tags = [RIVERBANK_GLOBAL_TAG, 'riverbank:pages'];\n if (pageId) {\n tags.push(`riverbank:page:${pageId}`);\n }\n return tags;\n}\n\n/**\n * Generate tags for content entries.\n *\n * @param contentTypeSlug - The content type slug (e.g., 'blog-post', 'event')\n * @param entryId - Optional specific entry ID to include\n * @returns Array of tags including global, content-type, and optionally entry-specific\n *\n * @example\n * ```ts\n * getEntryTags('blog-post') // ['riverbank', 'riverbank:content-type:blog-post']\n * getEntryTags('blog-post', 'entry-456') // ['riverbank', 'riverbank:content-type:blog-post', 'riverbank:entry:entry-456']\n * ```\n */\nexport function getEntryTags(contentTypeSlug: string, entryId?: string): string[] {\n const tags = [RIVERBANK_GLOBAL_TAG, `riverbank:content-type:${contentTypeSlug}`];\n if (entryId) {\n tags.push(`riverbank:entry:${entryId}`);\n }\n return tags;\n}\n\n/**\n * Generate tags for navigation menus.\n *\n * @param menuKey - Optional specific menu key to include\n * @returns Array of tags including global, navigation collection, and optionally menu-specific\n *\n * @example\n * ```ts\n * getNavigationTags() // ['riverbank', 'riverbank:navigation']\n * getNavigationTags('main') // ['riverbank', 'riverbank:navigation', 'riverbank:navigation:main']\n * ```\n */\nexport function getNavigationTags(menuKey?: string): string[] {\n const tags = [RIVERBANK_GLOBAL_TAG, 'riverbank:navigation'];\n if (menuKey) {\n tags.push(`riverbank:navigation:${menuKey}`);\n }\n return tags;\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/next/tags.ts"],"sourcesContent":["/**\n * Tag generation helpers for Next.js cache invalidation\n *\n * These helpers generate consistent cache tags for use with Next.js\n * `unstable_cache` and `revalidateTag` APIs.\n *\n * Tag naming convention:\n * - `riverbank` - Global tag that invalidates ALL Riverbank data\n * - `riverbank:<resource>` - Collection tag (e.g., `riverbank:pages`)\n * - `riverbank:<resource>:<id>` - Specific resource tag (e.g., `riverbank:page:page-123`)\n *\n * @example\n * ```tsx\n * // Consumer site: lib/cms-fetchers.ts\n * import { unstable_cache } from 'next/cache';\n * import { loadPage } from '@riverbankcms/sdk';\n * import { getPageTags } from '@riverbankcms/sdk/next/tags';\n *\n * export const getCachedPage = unstable_cache(\n * async (path: string) => loadPage({ client, siteId, path }),\n * ['riverbank-page'],\n * { tags: getPageTags(), revalidate: 300 }\n * );\n * ```\n */\n\n/**\n * Global tag that invalidates ALL Riverbank data.\n * Revalidating this tag will refresh everything.\n */\nexport const RIVERBANK_GLOBAL_TAG = 'riverbank';\n\n/**\n * Generate tags for site-level data (settings, theme).\n *\n * @returns Array of tags: ['riverbank', 'riverbank:site']\n */\nexport function getSiteTags(): string[] {\n return [RIVERBANK_GLOBAL_TAG, 'riverbank:site'];\n}\n\n/**\n * Generate tags for page data.\n *\n * @param pageId - Optional specific page ID to include\n * @returns Array of tags including global, pages collection, and optionally page-specific\n *\n * @example\n * ```ts\n * getPageTags() // ['riverbank', 'riverbank:pages']\n * getPageTags('page-123') // ['riverbank', 'riverbank:pages', 'riverbank:page:page-123']\n * ```\n */\nexport function getPageTags(pageId?: string): string[] {\n const tags = [RIVERBANK_GLOBAL_TAG, 'riverbank:pages'];\n if (pageId) {\n tags.push(`riverbank:page:${pageId}`);\n }\n return tags;\n}\n\n/**\n * Generate tags for content entries.\n *\n * @param contentTypeSlug - The content type slug (e.g., 'blog-post', 'event')\n * @param entryId - Optional specific entry ID to include\n * @returns Array of tags including global, content-type, and optionally entry-specific\n *\n * @example\n * ```ts\n * getEntryTags('blog-post') // ['riverbank', 'riverbank:content-type:blog-post']\n * getEntryTags('blog-post', 'entry-456') // ['riverbank', 'riverbank:content-type:blog-post', 'riverbank:entry:entry-456']\n * ```\n */\nexport function getEntryTags(contentTypeSlug: string, entryId?: string): string[] {\n const tags = [RIVERBANK_GLOBAL_TAG, `riverbank:content-type:${contentTypeSlug}`];\n if (entryId) {\n tags.push(`riverbank:entry:${entryId}`);\n }\n return tags;\n}\n\n/**\n * Generate tags for navigation menus.\n *\n * @param menuKey - Optional specific menu key to include\n * @returns Array of tags including global, navigation collection, and optionally menu-specific\n *\n * @example\n * ```ts\n * getNavigationTags() // ['riverbank', 'riverbank:navigation']\n * getNavigationTags('main') // ['riverbank', 'riverbank:navigation', 'riverbank:navigation:main']\n * ```\n */\nexport function getNavigationTags(menuKey?: string): string[] {\n const tags = [RIVERBANK_GLOBAL_TAG, 'riverbank:navigation'];\n if (menuKey) {\n tags.push(`riverbank:navigation:${menuKey}`);\n }\n return tags;\n}\n"],"mappings":"
|
|
1
|
+
{"version":3,"sources":["../../../src/next/tags.ts"],"sourcesContent":["/**\n * Tag generation helpers for Next.js cache invalidation\n *\n * These helpers generate consistent cache tags for use with Next.js\n * `unstable_cache` and `revalidateTag` APIs.\n *\n * Tag naming convention:\n * - `riverbank` - Global tag that invalidates ALL Riverbank data\n * - `riverbank:<resource>` - Collection tag (e.g., `riverbank:pages`)\n * - `riverbank:<resource>:<id>` - Specific resource tag (e.g., `riverbank:page:page-123`)\n *\n * @example\n * ```tsx\n * // Consumer site: lib/cms-fetchers.ts\n * import { unstable_cache } from 'next/cache';\n * import { loadPage } from '@riverbankcms/sdk';\n * import { getPageTags } from '@riverbankcms/sdk/next/tags';\n *\n * export const getCachedPage = unstable_cache(\n * async (path: string) => loadPage({ client, siteId, path }),\n * ['riverbank-page'],\n * { tags: getPageTags(), revalidate: 300 }\n * );\n * ```\n */\n\n/**\n * Global tag that invalidates ALL Riverbank data.\n * Revalidating this tag will refresh everything.\n *\n * NOTE: This constant is also defined in apps/dashboard/src/lib/webhooks/dispatch.ts.\n * If you change this value, update both locations.\n */\nexport const RIVERBANK_GLOBAL_TAG = 'riverbank';\n\n/**\n * Generate tags for site-level data (settings, theme).\n *\n * @returns Array of tags: ['riverbank', 'riverbank:site']\n */\nexport function getSiteTags(): string[] {\n return [RIVERBANK_GLOBAL_TAG, 'riverbank:site'];\n}\n\n/**\n * Generate tags for page data.\n *\n * @param pageId - Optional specific page ID to include\n * @returns Array of tags including global, pages collection, and optionally page-specific\n *\n * @example\n * ```ts\n * getPageTags() // ['riverbank', 'riverbank:pages']\n * getPageTags('page-123') // ['riverbank', 'riverbank:pages', 'riverbank:page:page-123']\n * ```\n */\nexport function getPageTags(pageId?: string): string[] {\n const tags = [RIVERBANK_GLOBAL_TAG, 'riverbank:pages'];\n if (pageId) {\n tags.push(`riverbank:page:${pageId}`);\n }\n return tags;\n}\n\n/**\n * Generate tags for content entries.\n *\n * @param contentTypeSlug - The content type slug (e.g., 'blog-post', 'event')\n * @param entryId - Optional specific entry ID to include\n * @returns Array of tags including global, content-type, and optionally entry-specific\n *\n * @example\n * ```ts\n * getEntryTags('blog-post') // ['riverbank', 'riverbank:content-type:blog-post']\n * getEntryTags('blog-post', 'entry-456') // ['riverbank', 'riverbank:content-type:blog-post', 'riverbank:entry:entry-456']\n * ```\n */\nexport function getEntryTags(contentTypeSlug: string, entryId?: string): string[] {\n const tags = [RIVERBANK_GLOBAL_TAG, `riverbank:content-type:${contentTypeSlug}`];\n if (entryId) {\n tags.push(`riverbank:entry:${entryId}`);\n }\n return tags;\n}\n\n/**\n * Generate tags for navigation menus.\n *\n * @param menuKey - Optional specific menu key to include\n * @returns Array of tags including global, navigation collection, and optionally menu-specific\n *\n * @example\n * ```ts\n * getNavigationTags() // ['riverbank', 'riverbank:navigation']\n * getNavigationTags('main') // ['riverbank', 'riverbank:navigation', 'riverbank:navigation:main']\n * ```\n */\nexport function getNavigationTags(menuKey?: string): string[] {\n const tags = [RIVERBANK_GLOBAL_TAG, 'riverbank:navigation'];\n if (menuKey) {\n tags.push(`riverbank:navigation:${menuKey}`);\n }\n return tags;\n}\n"],"mappings":";;;AAiCO,IAAM,uBAAuB;AAO7B,SAAS,cAAwB;AACtC,SAAO,CAAC,sBAAsB,gBAAgB;AAChD;AAcO,SAAS,YAAY,QAA2B;AACrD,QAAM,OAAO,CAAC,sBAAsB,iBAAiB;AACrD,MAAI,QAAQ;AACV,SAAK,KAAK,kBAAkB,MAAM,EAAE;AAAA,EACtC;AACA,SAAO;AACT;AAeO,SAAS,aAAa,iBAAyB,SAA4B;AAChF,QAAM,OAAO,CAAC,sBAAsB,0BAA0B,eAAe,EAAE;AAC/E,MAAI,SAAS;AACX,SAAK,KAAK,mBAAmB,OAAO,EAAE;AAAA,EACxC;AACA,SAAO;AACT;AAcO,SAAS,kBAAkB,SAA4B;AAC5D,QAAM,OAAO,CAAC,sBAAsB,sBAAsB;AAC1D,MAAI,SAAS;AACX,SAAK,KAAK,wBAAwB,OAAO,EAAE;AAAA,EAC7C;AACA,SAAO;AACT;","names":[]}
|
package/dist/server/next.d.mts
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { ReactNode, ComponentType } from 'react';
|
|
2
|
-
import { R as RiverbankClient } from './types-
|
|
2
|
+
import { R as RiverbankClient } from './types-C-LShyIg.mjs';
|
|
3
3
|
import { R as RiverbankSiteConfig } from './types-BjgZt8xJ.mjs';
|
|
4
|
-
import { B as BlockOverrides } from './loadPage-
|
|
4
|
+
import { B as BlockOverrides } from './loadPage-BucnLHmE.mjs';
|
|
5
5
|
import './schema-Z6-afHJG.mjs';
|
|
6
|
-
import { M as Metadata } from './index
|
|
7
|
-
import { b as LoadContentResult } from './loadContent-
|
|
8
|
-
import './types-
|
|
6
|
+
import { M as Metadata } from './index--Oyunk_B.mjs';
|
|
7
|
+
import { b as LoadContentResult } from './loadContent-DVfuBLiZ.mjs';
|
|
8
|
+
import './types-BSV6Vc-P.mjs';
|
|
9
9
|
import '@riverbankcms/ai';
|
|
10
10
|
import 'zod';
|
|
11
11
|
import './link-DjxLyC82.mjs';
|
package/dist/server/next.d.ts
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { ReactNode, ComponentType } from 'react';
|
|
2
|
-
import { R as RiverbankClient } from './types-
|
|
2
|
+
import { R as RiverbankClient } from './types-BRQyLrQU.js';
|
|
3
3
|
import { R as RiverbankSiteConfig } from './types-DLBhEPSt.js';
|
|
4
|
-
import { B as BlockOverrides } from './loadPage-
|
|
4
|
+
import { B as BlockOverrides } from './loadPage-BmYJCe_V.js';
|
|
5
5
|
import './schema-Z6-afHJG.js';
|
|
6
|
-
import { M as Metadata } from './index-
|
|
7
|
-
import { b as LoadContentResult } from './loadContent-
|
|
8
|
-
import './types-
|
|
6
|
+
import { M as Metadata } from './index-C9Ra8dza.js';
|
|
7
|
+
import { b as LoadContentResult } from './loadContent-D7LQwI0o.js';
|
|
8
|
+
import './types-Dt98DeYa.js';
|
|
9
9
|
import '@riverbankcms/ai';
|
|
10
10
|
import 'zod';
|
|
11
11
|
import './link-DjxLyC82.js';
|
package/dist/server/next.js
CHANGED
|
@@ -4,10 +4,10 @@
|
|
|
4
4
|
var _chunkBNQV3PXPjs = require('./chunk-BNQV3PXP.js');
|
|
5
5
|
|
|
6
6
|
|
|
7
|
-
var
|
|
7
|
+
var _chunk2IZ6S225js = require('./chunk-2IZ6S225.js');
|
|
8
8
|
|
|
9
9
|
|
|
10
|
-
var
|
|
10
|
+
var _chunk6Z4MQG47js = require('./chunk-6Z4MQG47.js');
|
|
11
11
|
|
|
12
12
|
|
|
13
13
|
|
|
@@ -19,6 +19,7 @@ var _chunkT26N3P26js = require('./chunk-T26N3P26.js');
|
|
|
19
19
|
require('./chunk-7UPVCT3K.js');
|
|
20
20
|
require('./chunk-RVDS7VSP.js');
|
|
21
21
|
require('./chunk-YYO3RIFO.js');
|
|
22
|
+
require('./chunk-4CV4JOE5.js');
|
|
22
23
|
|
|
23
24
|
// src/next/catch-all.tsx
|
|
24
25
|
var _navigation = require('next/navigation');
|
|
@@ -183,7 +184,7 @@ async function generateAllStaticParams() {
|
|
|
183
184
|
);
|
|
184
185
|
}
|
|
185
186
|
const { apiKey, siteId: _siteId, baseUrl } = envResult.config;
|
|
186
|
-
const _client =
|
|
187
|
+
const _client = _chunk6Z4MQG47js.createRiverbankClient.call(void 0, {
|
|
187
188
|
apiKey,
|
|
188
189
|
baseUrl,
|
|
189
190
|
// Disable caching for build-time fetches
|
|
@@ -200,7 +201,7 @@ async function generateAllStaticParams() {
|
|
|
200
201
|
// src/next/index.ts
|
|
201
202
|
var ISR_REVALIDATE_SECONDS = 300;
|
|
202
203
|
function getISRConfig() {
|
|
203
|
-
const preview =
|
|
204
|
+
const preview = _chunk2IZ6S225js.isPreviewMode.call(void 0, );
|
|
204
205
|
return {
|
|
205
206
|
revalidate: preview ? 0 : ISR_REVALIDATE_SECONDS,
|
|
206
207
|
isPreview: preview
|
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":["isPreviewMode","Page"],"mappings":"AAAA;AACE;AACA;AACF,sDAA4B;AAC5B;AACE;AACF,sDAA4B;AAC5B;AACE;AACF,sDAA4B;AAC5B;AACE;AACA;AACA;AACF,sDAA4B;AAC5B;AACE;AACF,sDAA4B;AAC5B,+BAA4B;AAC5B,+BAA4B;AAC5B,+BAA4B;AAC5B;AACA;ACGA,6CAAyB;AAqJA,+CAAA;AAtHzB,SAASA,cAAAA,CAAAA,EAAyB;AAChC,EAAA,OAAO,OAAA,CAAQ,GAAA,CAAI,uBAAA,IAA2B,MAAA;AAChD;AAaA,SAAS,UAAA,CAAW,IAAA,EAAyB;AAC3C,EAAA,GAAA,CAAI,CAAC,KAAA,GAAQ,IAAA,CAAK,OAAA,IAAW,CAAA,EAAG,OAAO,GAAA;AACvC,EAAA,OAAO,IAAA,EAAM,IAAA,CAAK,IAAA,CAAK,GAAG,CAAA;AAC5B;AAuDO,SAAS,kBAAA,CACd,OAAA,EAC0B;AAC1B,EAAA,MAAM;AAAA,IACJ,SAAA;AAAA,IACA,MAAA;AAAA,IACA,cAAA;AAAA,IACA,OAAA;AAAA,IACA,YAAA;AAAA,IACA,cAAA;AAAA,IACA,OAAA,EAAS;AAAA,EACX,EAAA,EAAI,OAAA;AAKJ,EAAA,MAAA,SAAeC,KAAAA,CAAK,EAAE,MAAA,EAAQ,YAAA,EAAc,oBAAoB,CAAA,EAAgD;AAC9G,IAAA,MAAM,CAAC,EAAE,KAAK,CAAA,EAAG,YAAY,EAAA,EAAI,MAAM,OAAA,CAAQ,GAAA,CAAI;AAAA,MACjD,MAAA;AAAA,uBACA,mBAAA,UAAuB,OAAA,CAAQ,OAAA,CAAQ,CAAC,CAAC;AAAA,IAC3C,CAAC,CAAA;AACD,IAAA,MAAM,KAAA,EAAO,UAAA,CAAW,IAAI,CAAA;AAC5B,IAAA,MAAM,QAAA,EAAUD,cAAAA,CAAc,CAAA;AAC9B,IAAA,MAAM,OAAA,EAAS,SAAA,CAAU,CAAA;AAGzB,IAAA,IAAI,OAAA;AACJ,IAAA,IAAI;AACF,MAAA,QAAA,EAAU,MAAM,0CAAA;AAAY,QAC1B,MAAA;AAAA,QACA,MAAA,EAAQ,MAAA,CAAO,MAAA;AAAA,QACf,IAAA;AAAA,QACA;AAAA,MACF,CAAC,CAAA;AAAA,IACH,EAAA,MAAA,CAAS,KAAA,EAAO;AAGd,MAAA,OAAA,CAAQ,KAAA,CAAM,uDAAA,EAAyD,IAAA,EAAM,KAAK,CAAA;AAClF,MAAA,OAAO,kCAAA,CAAS;AAAA,IAClB;AAGA,IAAA,GAAA,CAAI,YAAA,EAAc;AAChB,MAAA,MAAM,YAAA,EAAc,MAAM,YAAA,CAAa,EAAE,OAAA,EAAS,IAAA,EAAM,OAAA,EAAS,aAAa,CAAC,CAAA;AAC/E,MAAA,GAAA,CAAI,YAAA,IAAgB,IAAA,EAAM;AACxB,QAAA,OAAO,QAAA,kBAAU,6BAAA,OAAC,EAAA,EAAS,QAAA,EAAA,YAAA,CAAY,EAAA,EAAa,WAAA;AAAA,MACtD;AAAA,IACF;AAEA,IAAA,IAAI,QAAA;AAGJ,IAAA,GAAA,CAAI,4CAAA,OAAqB,CAAA,EAAG;AAC1B,MAAA,SAAA,kBACE,6BAAA;AAAA,QAAC,qBAAA;AAAA,QAAA;AAAA,UACC,IAAA,EAAM,OAAA,CAAQ,IAAA;AAAA,UACd,KAAA,EAAO,OAAA,CAAQ,KAAA;AAAA,UACf,MAAA,EAAQ,OAAA,CAAQ,MAAA;AAAA,UAChB,YAAA,EAAc,OAAA,CAAQ,YAAA;AAAA,UACtB;AAAA,QAAA;AAAA,MACF,CAAA;AAAA,IAEJ,EAAA,KAAA,GAAA,CAES,6CAAA,OAAsB,CAAA,EAAG;AAEhC,MAAA,GAAA,CAAI,CAAC,OAAA,CAAQ,YAAA,EAAc;AACzB,QAAA,OAAO,kCAAA,CAAS;AAAA,MAClB;AAEA,MAAA,SAAA,kBACE,6BAAA;AAAA,QAAC,qBAAA;AAAA,QAAA;AAAA,UACC,IAAA,EAAM,OAAA,CAAQ,YAAA;AAAA,UACd,KAAA,EAAO,OAAA,CAAQ,KAAA;AAAA,UACf,MAAA,EAAQ,OAAA,CAAQ,MAAA;AAAA,UAChB,YAAA,EAAc,OAAA,CAAQ,YAAA;AAAA,UACtB,cAAA;AAAA,UACA,WAAA,EAAa;AAAA,YACX,YAAA,EAAc,OAAA,CAAQ,WAAA,CAAY;AAAA,UACpC;AAAA,QAAA;AAAA,MACF,CAAA;AAAA,IAEJ,EAAA,KAEK;AACH,MAAA,OAAO,kCAAA,CAAS;AAAA,IAClB;AAEA,IAAA,OAAO,QAAA,kBAAU,6BAAA,OAAC,EAAA,EAAS,QAAA,EAAA,SAAA,CAAS,EAAA,EAAa,QAAA;AAAA,EACnD;AAKA,EAAA,MAAA,SAAe,kBAAA,CAAmB,EAAE,MAAA,EAAQ,YAAA,EAAc,oBAAoB,CAAA,EAAyC;AACrH,IAAA,MAAM,CAAC,EAAE,KAAK,CAAA,EAAG,YAAY,EAAA,EAAI,MAAM,OAAA,CAAQ,GAAA,CAAI;AAAA,MACjD,MAAA;AAAA,uBACA,mBAAA,UAAuB,OAAA,CAAQ,OAAA,CAAQ,CAAC,CAAC;AAAA,IAC3C,CAAC,CAAA;AACD,IAAA,MAAM,KAAA,EAAO,UAAA,CAAW,IAAI,CAAA;AAC5B,IAAA,MAAM,QAAA,EAAUA,cAAAA,CAAc,CAAA;AAE9B,IAAA,IAAI;AACF,MAAA,MAAM,QAAA,EAAU,MAAM,0CAAA;AAAY,QAChC,MAAA,EAAQ,SAAA,CAAU,CAAA;AAAA,QAClB,MAAA,EAAQ,MAAA,CAAO,MAAA;AAAA,QACf,IAAA;AAAA,QACA;AAAA,MACF,CAAC,CAAA;AAGD,MAAA,GAAA,CAAI,cAAA,EAAgB;AAClB,QAAA,OAAO,cAAA,CAAe,EAAE,OAAA,EAAS,IAAA,EAAM,OAAA,EAAS,aAAa,CAAC,CAAA;AAAA,MAChE;AAGA,MAAA,MAAM,YAAA,oCACJ,OAAA,UAAA,CAAY,QAAA,EAAU,MAAA,CAAO,WAAA,EAAa,MAAA,CAAO,OAAA,GAAA,UAAY,IAAA;AAG/D,MAAA,GAAA,CAAI,CAAC,WAAA,EAAa;AAChB,QAAA,OAAA,CAAQ,IAAA;AAAA,UACN;AAAA,QAEF,CAAA;AAAA,MACF;AAIA,MAAA,MAAM,KAAA,EAAO,4CAAA,OAAqB,EAAA,EAC9B,OAAA,CAAQ,KAAA,EACR,6CAAA,OAAsB,EAAA,EACpB;AAAA,QACE,IAAA,mBAAM,OAAA,CAAQ,KAAA,CAAM,SAAA,UAAa,OAAA,CAAQ,KAAA,CAAM,OAAA;AAAA,QAC/C,OAAA,mBAAS,OAAA,CAAQ,KAAA,CAAM,eAAA,UAAmB,KAAA;AAAA,MAC5C,EAAA,EACA,EAAE,IAAA,EAAM,OAAO,CAAA;AAGrB,MAAA,MAAM,kBAAA,EAAoB,QAAA,EACtB,yCAAA,EACA,qCAAA;AAGJ,MAAA,OAAO,iBAAA,CAAkB;AAAA,QACvB,IAAA;AAAA,QACA,IAAA,EAAM,OAAA,CAAQ,IAAA;AAAA,QACd,IAAA;AAAA,QACA,OAAA,EAAS;AAAA,MACX,CAAC,CAAA;AAAA,IACH,EAAA,MAAA,CAAS,KAAA,EAAO;AAEd,MAAA,OAAA,CAAQ,KAAA,CAAM,4DAAA,EAA8D,IAAA,EAAM,KAAK,CAAA;AAEvF,MAAA,OAAO;AAAA,QACL,KAAA,EAAO,gBAAA;AAAA,QACP,MAAA,EAAQ,EAAE,KAAA,EAAO,KAAA,EAAO,MAAA,EAAQ,MAAM;AAAA,MACxC,CAAA;AAAA,IACF;AAAA,EACF;AAEA,EAAA,OAAO;AAAA,IACL,IAAA,EAAAC,KAAAA;AAAA,IACA,gBAAA,EAAkB;AAAA,EACpB,CAAA;AACF;ADjJA;AACA;AEhJA,8BAAoC;AFkJpC;AACA;AGlJA;AHoJA;AACA;AI3HO,SAAS,uBAAA,CAAA,EAAiD;AAC/D,EAAA,MAAM,OAAA,EAAS,OAAA,CAAQ,GAAA,CAAI,iBAAA;AAC3B,EAAA,MAAM,OAAA,EAAS,OAAA,CAAQ,GAAA,CAAI,iBAAA;AAC3B,EAAA,MAAM,QAAA,EAAU,OAAA,CAAQ,GAAA,CAAI,yBAAA;AAE5B,EAAA,MAAM,YAAA,EAAwB,CAAC,CAAA;AAE/B,EAAA,GAAA,CAAI,CAAC,MAAA,EAAQ,WAAA,CAAY,IAAA,CAAK,mBAAmB,CAAA;AACjD,EAAA,GAAA,CAAI,CAAC,MAAA,EAAQ,WAAA,CAAY,IAAA,CAAK,mBAAmB,CAAA;AACjD,EAAA,GAAA,CAAI,CAAC,OAAA,EAAS,WAAA,CAAY,IAAA,CAAK,2BAA2B,CAAA;AAE1D,EAAA,GAAA,CAAI,WAAA,CAAY,OAAA,EAAS,CAAA,EAAG;AAC1B,IAAA,OAAO,EAAE,KAAA,EAAO,KAAA,EAAO,YAAY,CAAA;AAAA,EACrC;AAGA,EAAA,OAAO;AAAA,IACL,KAAA,EAAO,IAAA;AAAA,IACP,MAAA,EAAQ,EAAE,MAAA,EAAiB,MAAA,EAAiB,QAAkB;AAAA,EAChE,CAAA;AACF;AAeO,SAAS,eAAA,CAAgB,IAAA,EAAwB;AACtD,EAAA,GAAA,CAAI,KAAA,IAAS,GAAA,EAAK,OAAO,CAAC,CAAA;AAC1B,EAAA,OAAO,IAAA,CAAK,KAAA,CAAM,CAAC,CAAA,CAAE,KAAA,CAAM,GAAG,CAAA;AAChC;AA+BA,MAAA,SAAsB,uBAAA,CAAA,EAAyD;AAC7E,EAAA,MAAM,UAAA,EAAY,uBAAA,CAAwB,CAAA;AAE1C,EAAA,GAAA,CAAI,CAAC,SAAA,CAAU,KAAA,EAAO;AACpB,IAAA,MAAM,IAAI,KAAA;AAAA,MACR,CAAA,uDAAA,EAA0D,SAAA,CAAU,WAAA,CAAY,IAAA,CAAK,IAAI,CAAC,CAAA,oEAAA;AAAA,IAE5F,CAAA;AAAA,EACF;AAEA,EAAA,MAAM,EAAE,MAAA,EAAQ,MAAA,EAAQ,OAAA,EAAS,QAAQ,EAAA,EAAI,SAAA,CAAU,MAAA;AAGvD,EAAA,MAAM,QAAA,EAAU,oDAAA;AAAsB,IACpC,MAAA;AAAA,IACA,OAAA;AAAA;AAAA,IAEA,KAAA,EAAO,EAAE,OAAA,EAAS,MAAM,CAAA;AAAA;AAAA,IAExB,UAAA,EAAY,EAAE,OAAA,EAAS,MAAM;AAAA,EAC/B,CAAC,CAAA;AAeD,EAAA,OAAA,CAAQ,IAAA;AAAA,IACN;AAAA,EAEF,CAAA;AAGA,EAAA,OAAO,CAAC,EAAE,IAAA,EAAM,CAAC,EAAE,CAAC,CAAA;AACtB;AJsDA;AACA;AKxIO,IAAM,uBAAA,EAAyB,GAAA;AA8B/B,SAAS,YAAA,CAAA,EAA0B;AACxC,EAAA,MAAM,QAAA,EAAU,4CAAA,CAAc;AAC9B,EAAA,OAAO;AAAA,IACL,UAAA,EAAY,QAAA,EAAU,EAAA,EAAI,sBAAA;AAAA,IAC1B,SAAA,EAAW;AAAA,EACb,CAAA;AACF;AL6GA;AACE;AACA;AACA;AACA;AACA;AACA;AACF,+SAAC","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';\n\n/**\n * Detect preview mode from environment variable.\n *\n * Sites can enable preview mode by setting:\n * ```\n * RIVERBANK_PREVIEW_MODE=true\n * ```\n *\n * This is typically set in preview/staging environments to fetch\n * draft content instead of published content.\n */\nfunction isPreviewMode(): boolean {\n return process.env.RIVERBANK_PREVIEW_MODE === 'true';\n}\n\n/**\n * Convert URL slug segments to a path string.\n *\n * @example\n * ```\n * slugToPath(undefined) // '/'\n * slugToPath([]) // '/'\n * slugToPath(['about']) // '/about'\n * slugToPath(['blog', 'post']) // '/blog/post'\n * ```\n */\nfunction slugToPath(slug?: string[]): string {\n if (!slug || slug.length === 0) return '/';\n return '/' + slug.join('/');\n}\n\n/**\n * Factory function to create a Next.js catch-all page component and metadata generator.\n *\n * This provides a simple, opinionated setup for rendering Riverbank CMS content\n * in external SDK sites. It handles:\n *\n * - Page and content entry routing\n * - Preview mode detection (via `RIVERBANK_PREVIEW_MODE` env var)\n * - SEO metadata generation\n * - Block overrides for custom components\n * - 404 handling for missing content\n *\n * ## Escape Hatches\n *\n * For customization beyond the defaults, use these options:\n *\n * - `beforeRender`: Intercept before rendering (maintenance mode, access control)\n * - `customMetadata`: Full control over SEO metadata\n * - `wrapper`: Wrap all content (analytics, error boundaries)\n *\n * For maximum control, use the lower-level helpers directly:\n * - `loadContent()` from `@riverbankcms/sdk/rendering`\n * - `Page` component from `@riverbankcms/sdk/rendering`\n * - `generatePageMetadata()` from `@riverbankcms/sdk/metadata`\n *\n * @param options - Configuration options\n * @returns Object with `Page` component and `generateMetadata` function\n *\n * @example Basic usage\n * ```tsx\n * const { Page, generateMetadata } = createCatchAllPage({\n * getClient: getRiverbankClient,\n * config,\n * });\n *\n * export default Page;\n * export { generateMetadata };\n * ```\n *\n * @example With maintenance mode\n * ```tsx\n * const { Page, generateMetadata } = createCatchAllPage({\n * getClient,\n * config,\n * beforeRender: async () => {\n * if (process.env.MAINTENANCE_MODE === 'true') {\n * return <MaintenancePage />;\n * }\n * return null;\n * },\n * });\n * ```\n */\nexport function createCatchAllPage(\n options: CreateCatchAllPageOptions\n): CreateCatchAllPageResult {\n const {\n getClient,\n config,\n blockOverrides,\n siteUrl,\n beforeRender,\n customMetadata,\n wrapper: Wrapper,\n } = options;\n\n /**\n * Main page component that loads and renders CMS content.\n */\n async function Page({ params, searchParams: searchParamsPromise }: CatchAllPageProps): Promise<React.ReactNode> {\n const [{ slug }, searchParams] = await Promise.all([\n params,\n searchParamsPromise ?? Promise.resolve({}),\n ]);\n const path = slugToPath(slug);\n const preview = isPreviewMode();\n const client = getClient();\n\n // Load content (page or entry)\n let content: LoadContentResult;\n try {\n content = await loadContent({\n client,\n siteId: config.siteId,\n path,\n preview,\n });\n } catch (error) {\n // Log the error for debugging - could be a network error, auth failure, etc.\n // We treat all errors as \"not found\" since the page can't be rendered\n console.debug('[createCatchAllPage] Failed to load content for path:', path, error);\n return notFound();\n }\n\n // ESCAPE HATCH: beforeRender for maintenance mode, access control, etc.\n if (beforeRender) {\n const intercepted = await beforeRender({ content, path, preview, searchParams });\n if (intercepted !== null) {\n return Wrapper ? <Wrapper>{intercepted}</Wrapper> : intercepted;\n }\n }\n\n let rendered: React.ReactNode;\n\n // Render page content\n if (isPageContent(content)) {\n rendered = (\n <PageComponent\n page={content.page}\n theme={content.theme}\n siteId={content.siteId}\n resolvedData={content.resolvedData}\n blockOverrides={blockOverrides}\n />\n );\n }\n // Render entry content with template\n else if (isEntryContent(content)) {\n // Entries without template pages should 404\n if (!content.templatePage) {\n return notFound();\n }\n\n rendered = (\n <PageComponent\n page={content.templatePage}\n theme={content.theme}\n siteId={content.siteId}\n resolvedData={content.resolvedData}\n blockOverrides={blockOverrides}\n dataContext={{\n contentEntry: content.dataContext.contentEntry,\n }}\n />\n );\n }\n // Unexpected content type - should never happen\n else {\n return notFound();\n }\n\n return Wrapper ? <Wrapper>{rendered}</Wrapper> : rendered;\n }\n\n /**\n * Generate SEO metadata for the page.\n */\n async function generateMetadataFn({ params, searchParams: searchParamsPromise }: CatchAllPageProps): Promise<Metadata> {\n const [{ slug }, searchParams] = await Promise.all([\n params,\n searchParamsPromise ?? Promise.resolve({}),\n ]);\n const path = slugToPath(slug);\n const preview = isPreviewMode();\n\n try {\n const content = await loadContent({\n client: getClient(),\n siteId: config.siteId,\n path,\n preview,\n });\n\n // ESCAPE HATCH: Custom metadata generation\n if (customMetadata) {\n return customMetadata({ content, path, preview, searchParams });\n }\n\n // Resolve site URL (use config URLs as fallback)\n const resolvedUrl =\n siteUrl ?? (preview ? config.previewUrl : config.liveUrl) ?? '';\n\n // Warn if no site URL is configured - OG/Twitter tags require absolute URLs\n if (!resolvedUrl) {\n console.warn(\n '[createCatchAllPage] No siteUrl configured. OpenGraph and Twitter tags will have relative URLs which may cause social sharing previews to fail. ' +\n 'Set siteUrl option, or config.liveUrl/config.previewUrl in your riverbank.config.'\n );\n }\n\n // Build page object for metadata\n // For entries, we use metaTitle/metaDescription; purpose is used as description fallback\n const page = isPageContent(content)\n ? content.page\n : isEntryContent(content)\n ? {\n name: content.entry.metaTitle ?? content.entry.title,\n purpose: content.entry.metaDescription ?? undefined,\n }\n : { name: 'Page' };\n\n // Use preview metadata (noindex) in preview mode\n const metadataGenerator = preview\n ? generatePreviewMetadata\n : generatePageMetadata;\n\n // Use site data from loadContent result (no duplicate API call needed)\n return metadataGenerator({\n page,\n site: content.site,\n path,\n siteUrl: resolvedUrl,\n });\n } catch (error) {\n // Log the error for debugging purposes\n console.debug('[createCatchAllPage] Failed to generate metadata for path:', path, error);\n // Return minimal metadata on error - page will handle the actual 404\n return {\n title: 'Page Not Found',\n robots: { index: false, follow: false },\n };\n }\n }\n\n return {\n Page,\n generateMetadata: generateMetadataFn,\n };\n}\n","/**\n * Client-side React hook to fetch page data.\n *\n * Use this in client components for dynamic page loading.\n */\n\nimport { useState, useEffect } from 'react';\nimport type { RiverbankClient } from '../../client/types';\nimport type { PageProps } from '../components/Page';\nimport type { RuntimeSdkConfig } from '../helpers/loadPage';\nimport { prefetchBlockData } from '../../data/prefetchBlockData';\n\nexport type UsePageParams = {\n client: RiverbankClient;\n siteId: string;\n path: string;\n pageId?: string;\n /**\n * If true, fetches draft/unpublished content instead of published content.\n * This affects both the page structure and block data loaders.\n * Requires API key with site access.\n *\n * @default false\n */\n preview?: boolean;\n};\n\nexport type UsePageResult =\n | { loading: true; error: null; page: null; theme: null; siteId: string; resolvedData: null; sdkConfig: null }\n | { loading: false; error: Error; page: null; theme: null; siteId: string; resolvedData: null; sdkConfig: null }\n | { loading: false; error: null; sdkConfig: RuntimeSdkConfig | null } & Omit<PageProps, 'registry' | 'wrapBlock' | 'usePlaceholders' | 'sdkConfig'>;\n\n/**\n * Client-side React hook to fetch all data needed for <Page> component.\n *\n * Fetches site data, page data, and prefetches block data loaders.\n * Returns loading and error states for proper UI handling.\n *\n * IMPORTANT: The client object should be stable across renders to avoid\n * unnecessary re-fetches. Create it outside your component or use useMemo:\n *\n * ```tsx\n * // ✅ Good - stable reference\n * const client = useMemo(\n * () => createRiverbankClient({ apiKey, baseUrl }),\n * [apiKey, baseUrl]\n * );\n *\n * // ❌ Bad - new client on every render (causes infinite loops)\n * const client = createRiverbankClient({ apiKey, baseUrl });\n * ```\n *\n * @example Basic usage\n * ```tsx\n * import { createRiverbankClient } from '@riverbankcms/sdk';\n * import { usePage, Page } from '@riverbankcms/sdk/rendering';\n *\n * const client = createRiverbankClient({\n * apiKey: process.env.NEXT_PUBLIC_RIVERBANK_API_KEY!,\n * baseUrl: process.env.NEXT_PUBLIC_DASHBOARD_URL + '/api',\n * });\n *\n * function MyPage({ path }: { path: string }) {\n * const pageData = usePage({ client, siteId: 'site-123', path });\n *\n * if (pageData.loading) {\n * return <div>Loading...</div>;\n * }\n *\n * if (pageData.error) {\n * return <div>Error: {pageData.error.message}</div>;\n * }\n *\n * return <Page {...pageData} />;\n * }\n * ```\n *\n * @example With custom loading/error states\n * ```tsx\n * function MyPage({ path }: { path: string }) {\n * const pageData = usePage({ client, siteId: 'site-123', path });\n *\n * if (pageData.loading) {\n * return <Skeleton />;\n * }\n *\n * if (pageData.error) {\n * return (\n * <ErrorBoundary\n * error={pageData.error}\n * onRetry={() => window.location.reload()}\n * />\n * );\n * }\n *\n * return <Page {...pageData} />;\n * }\n * ```\n */\nexport function usePage(params: UsePageParams): UsePageResult {\n const { client, siteId, path, pageId, preview = false } = params;\n\n const [result, setResult] = useState<UsePageResult>({\n loading: true,\n error: null,\n page: null,\n theme: null,\n siteId,\n resolvedData: null,\n sdkConfig: null,\n });\n\n useEffect(() => {\n let cancelled = false;\n\n async function fetchPage() {\n try {\n // Fetch site and page data in parallel\n const [site, pageResponse] = await Promise.all([\n client.getSite({ id: siteId }),\n client.getPage({ siteId, path, preview }),\n ]);\n\n // If component unmounted, don't update state\n if (cancelled) return;\n\n // Extract page data (getContentByPath can return page or entry)\n if ('entry' in pageResponse) {\n throw new Error(\n 'This path resolves to a content entry, not a page. ' +\n 'Use useContent() instead, which handles both pages and entries. ' +\n 'For entries, useContent() returns the raw entry data for custom rendering.'\n );\n }\n\n const { page: pageData } = pageResponse;\n\n // Convert API response blocks to PageOutline format\n // API returns blocks with full content, but PageOutline only needs structure\n const blocks = pageData.blocks.map((block) => {\n if (!block || typeof block !== 'object') {\n throw new Error('Invalid block format in API response');\n }\n if (typeof block.id !== 'string' && block.id !== null) {\n throw new Error(`Invalid block id: expected string or null, got ${typeof block.id}`);\n }\n if (typeof block.kind !== 'string') {\n throw new Error(`Invalid block kind: expected string, got ${typeof block.kind}`);\n }\n if (typeof block.purpose !== 'string') {\n throw new Error(`Invalid block purpose: expected string, got ${typeof block.purpose}`);\n }\n return {\n id: block.id,\n kind: block.kind,\n purpose: block.purpose,\n };\n });\n\n const pageOutline = {\n name: pageData.name,\n path: pageData.path,\n purpose: pageData.purpose,\n blocks,\n };\n\n // Prefetch block data loaders\n const resolvedData = await prefetchBlockData(\n pageOutline,\n {\n siteId,\n pageId: pageId ?? pageData.id,\n previewStage: preview ? 'preview' : 'published',\n },\n client\n );\n\n // If component unmounted, don't update state\n if (cancelled) return;\n\n setResult({\n loading: false,\n error: null,\n page: pageOutline,\n theme: site.theme,\n siteId,\n resolvedData,\n sdkConfig: site.sdkConfig ?? null,\n });\n } catch (error) {\n if (cancelled) return;\n\n setResult({\n loading: false,\n error: error instanceof Error ? error : new Error(String(error)),\n page: null,\n theme: null,\n siteId,\n resolvedData: null,\n sdkConfig: null,\n });\n }\n }\n\n fetchPage();\n\n return () => {\n cancelled = true;\n };\n }, [client, siteId, path, pageId, preview]);\n\n return result;\n}\n","/**\n * Client-side React hook to fetch content (page or entry) by path.\n *\n * Use this in client components for dynamic routing where a path\n * could resolve to either a page or content entry.\n */\n\nimport { useState, useEffect } from 'react';\nimport type { Theme } from '@riverbankcms/blocks';\nimport type { RiverbankClient, PageResponse } from '../../client/types';\nimport type { PageProps } from '../components/Page';\nimport { prefetchBlockData } from '../../data/prefetchBlockData';\nimport type { ResolvedBlockData } from '../../data/prefetchBlockData';\nimport type { ContentEntryData } from '../helpers/loadContent';\n\nexport type UseContentParams = {\n client: RiverbankClient;\n siteId: string;\n path: string;\n /**\n * If true, fetches draft/unpublished content instead of published content.\n * This affects both pages and entries.\n * Requires API key with site access.\n *\n * @default false\n */\n preview?: boolean;\n};\n\n/**\n * Loading state\n */\ntype LoadingState = {\n loading: true;\n error: null;\n type: null;\n page: null;\n entry: null;\n theme: null;\n siteId: string;\n resolvedData: null;\n};\n\n/**\n * Error state\n */\ntype ErrorState = {\n loading: false;\n error: Error;\n type: null;\n page: null;\n entry: null;\n theme: null;\n siteId: string;\n resolvedData: null;\n};\n\n/**\n * Success state for page content\n */\ntype PageSuccessState = {\n loading: false;\n error: null;\n type: 'page';\n page: PageProps['page'];\n entry: null;\n theme: Theme;\n siteId: string;\n resolvedData: ResolvedBlockData;\n};\n\n/**\n * Success state for entry content\n */\ntype EntrySuccessState = {\n loading: false;\n error: null;\n type: 'entry';\n page: null;\n entry: ContentEntryData;\n theme: Theme;\n siteId: string;\n resolvedData: null;\n};\n\nexport type UseContentResult = LoadingState | ErrorState | PageSuccessState | EntrySuccessState;\n\n/**\n * Type guard to check if result is loading\n */\nexport function isContentLoading(result: UseContentResult): result is LoadingState {\n return result.loading === true;\n}\n\n/**\n * Type guard to check if result has an error\n */\nexport function isContentError(result: UseContentResult): result is ErrorState {\n return result.loading === false && result.error !== null;\n}\n\n/**\n * Type guard to check if result is a page\n */\nexport function isPageContentResult(result: UseContentResult): result is PageSuccessState {\n return result.loading === false && result.error === null && result.type === 'page';\n}\n\n/**\n * Type guard to check if result is an entry\n */\nexport function isEntryContentResult(result: UseContentResult): result is EntrySuccessState {\n return result.loading === false && result.error === null && result.type === 'entry';\n}\n\n/**\n * Client-side React hook to fetch content by path.\n *\n * Returns a discriminated union with loading/error states, and either\n * page data (ready for `<Page>` component) or raw entry data (for custom rendering).\n *\n * IMPORTANT: The client object should be stable across renders to avoid\n * unnecessary re-fetches. Create it outside your component or use useMemo.\n *\n * @example Dynamic routing with both pages and entries\n * ```tsx\n * \"use client\";\n *\n * import { useContent, Page, isPageContentResult } from '@riverbankcms/sdk/client';\n *\n * function DynamicPage({ path }: { path: string }) {\n * const content = useContent({ client, siteId: 'site-123', path });\n *\n * if (content.loading) return <div>Loading...</div>;\n * if (content.error) return <div>Error: {content.error.message}</div>;\n *\n * if (isPageContentResult(content)) {\n * return <Page page={content.page} theme={content.theme} siteId={content.siteId} resolvedData={content.resolvedData} />;\n * }\n *\n * // Render entry with custom UI\n * return (\n * <article>\n * <h1>{content.entry.title}</h1>\n * <div>{content.entry.content.body}</div>\n * </article>\n * );\n * }\n * ```\n *\n * @example Entry-specific rendering based on content type\n * ```tsx\n * const content = useContent({ client, siteId, path });\n *\n * if (content.loading) return <Spinner />;\n * if (content.error) return <Error error={content.error} />;\n *\n * if (content.type === 'entry') {\n * switch (content.entry.type) {\n * case 'blog-post':\n * return <BlogPost entry={content.entry} theme={content.theme} />;\n * case 'product':\n * return <ProductPage entry={content.entry} theme={content.theme} />;\n * }\n * }\n *\n * return <Page {...content} />;\n * ```\n */\nexport function useContent(params: UseContentParams): UseContentResult {\n const { client, siteId, path, preview = false } = params;\n\n const [result, setResult] = useState<UseContentResult>({\n loading: true,\n error: null,\n type: null,\n page: null,\n entry: null,\n theme: null,\n siteId,\n resolvedData: null,\n });\n\n useEffect(() => {\n let cancelled = false;\n\n async function fetchContent() {\n try {\n // Fetch site and content in parallel\n const [site, contentResponse] = await Promise.all([\n client.getSite({ id: siteId }),\n client.getPage({ siteId, path, preview }),\n ]);\n\n // If component unmounted, don't update state\n if (cancelled) return;\n\n // Check if response is an entry\n if (isEntryResponse(contentResponse)) {\n const entryData = contentResponse.entry;\n\n const entry: ContentEntryData = {\n id: entryData.id,\n type: entryData.type,\n title: entryData.title,\n slug: entryData.slug,\n path: entryData.path,\n status: entryData.status,\n publishAt: entryData.publishAt,\n content: preview\n ? (entryData.draftContent ?? entryData.content)\n : entryData.content,\n metaTitle: preview\n ? (entryData.draftMetaTitle ?? entryData.metaTitle)\n : entryData.metaTitle,\n metaDescription: preview\n ? (entryData.draftMetaDescription ?? entryData.metaDescription)\n : entryData.metaDescription,\n createdAt: entryData.createdAt,\n updatedAt: entryData.updatedAt,\n };\n\n setResult({\n loading: false,\n error: null,\n type: 'entry',\n page: null,\n entry,\n theme: site.theme,\n siteId,\n resolvedData: null,\n });\n return;\n }\n\n // Handle page response\n const { page: pageData } = contentResponse;\n\n // Convert API response blocks to PageOutline format\n const blocks = pageData.blocks.map((block) => {\n if (!block || typeof block !== 'object') {\n throw new Error('Invalid block format in API response');\n }\n if (typeof block.id !== 'string' && block.id !== null) {\n throw new Error(`Invalid block id: expected string or null, got ${typeof block.id}`);\n }\n if (typeof block.kind !== 'string') {\n throw new Error(`Invalid block kind: expected string, got ${typeof block.kind}`);\n }\n if (typeof block.purpose !== 'string') {\n throw new Error(`Invalid block purpose: expected string, got ${typeof block.purpose}`);\n }\n return {\n id: block.id,\n kind: block.kind,\n purpose: block.purpose,\n };\n });\n\n const pageOutline = {\n name: pageData.name,\n path: pageData.path,\n purpose: pageData.purpose,\n blocks,\n };\n\n // Prefetch block data loaders for pages\n const resolvedData = await prefetchBlockData(\n pageOutline,\n {\n siteId,\n pageId: pageData.id,\n previewStage: preview ? 'preview' : 'published',\n },\n client\n );\n\n // If component unmounted, don't update state\n if (cancelled) return;\n\n setResult({\n loading: false,\n error: null,\n type: 'page',\n page: pageOutline,\n entry: null,\n theme: site.theme,\n siteId,\n resolvedData,\n });\n } catch (error) {\n if (cancelled) return;\n\n setResult({\n loading: false,\n error: error instanceof Error ? error : new Error(String(error)),\n type: null,\n page: null,\n entry: null,\n theme: null,\n siteId,\n resolvedData: null,\n });\n }\n }\n\n fetchContent();\n\n return () => {\n cancelled = true;\n };\n }, [client, siteId, path, preview]);\n\n return result;\n}\n\n/**\n * Type guard to check if API response is an entry\n */\nfunction isEntryResponse(response: PageResponse): response is Extract<PageResponse, { type: 'entry' }> {\n return 'type' in response && response.type === 'entry';\n}\n","/**\n * Static params generation for Next.js SSG\n *\n * Provides helpers for generating static params from published CMS routes.\n *\n * @example\n * ```tsx\n * // app/[[...slug]]/page.tsx\n * import { generateAllStaticParams } from '@riverbankcms/sdk/next';\n *\n * export { generateAllStaticParams as generateStaticParams };\n * ```\n */\n\nimport { createRiverbankClient } from '../client';\n\n/**\n * Environment variable validation result\n */\nexport type StaticParamsEnvResult =\n | { valid: true; config: { apiKey: string; siteId: string; baseUrl: string } }\n | { valid: false; missingVars: string[] };\n\n/**\n * Validate that all required environment variables are set.\n *\n * Required env vars:\n * - RIVERBANK_API_KEY: API key for published content\n * - RIVERBANK_SITE_ID: Site ID\n * - NEXT_PUBLIC_DASHBOARD_URL: Dashboard API URL\n *\n * @returns Validation result with config or missing vars\n */\nexport function validateStaticParamsEnv(): StaticParamsEnvResult {\n const apiKey = process.env.RIVERBANK_API_KEY;\n const siteId = process.env.RIVERBANK_SITE_ID;\n const baseUrl = process.env.NEXT_PUBLIC_DASHBOARD_URL;\n\n const missingVars: string[] = [];\n\n if (!apiKey) missingVars.push('RIVERBANK_API_KEY');\n if (!siteId) missingVars.push('RIVERBANK_SITE_ID');\n if (!baseUrl) missingVars.push('NEXT_PUBLIC_DASHBOARD_URL');\n\n if (missingVars.length > 0) {\n return { valid: false, missingVars };\n }\n\n // TypeScript can't narrow through the checks above, so assert non-null\n return {\n valid: true,\n config: { apiKey: apiKey!, siteId: siteId!, baseUrl: baseUrl! },\n };\n}\n\n/**\n * Convert a route path to a Next.js slug array.\n *\n * @param path - The route path (e.g., '/about', '/blog/post')\n * @returns The slug array for Next.js catch-all route\n *\n * @example\n * ```ts\n * pathToSlugArray('/') // []\n * pathToSlugArray('/about') // ['about']\n * pathToSlugArray('/blog/post') // ['blog', 'post']\n * ```\n */\nexport function pathToSlugArray(path: string): string[] {\n if (path === '/') return [];\n return path.slice(1).split('/');\n}\n\n/**\n * Generate static params for all published routes.\n *\n * This function fetches all published routes from the CMS and converts them\n * to the static params format expected by Next.js catch-all routes.\n *\n * Requires environment variables:\n * - RIVERBANK_API_KEY: API key for published content\n * - RIVERBANK_SITE_ID: Site ID\n * - NEXT_PUBLIC_DASHBOARD_URL: Dashboard API URL\n *\n * @throws Error if required env vars are missing (prevents silent empty SSG in CI)\n * @returns Array of static params objects for Next.js\n *\n * @example\n * ```tsx\n * // app/[[...slug]]/page.tsx\n * import { generateAllStaticParams } from '@riverbankcms/sdk/next';\n *\n * export { generateAllStaticParams as generateStaticParams };\n *\n * // Or with custom logic:\n * export async function generateStaticParams() {\n * const params = await generateAllStaticParams();\n * // Filter or modify params as needed\n * return params.filter(p => !p.slug.includes('private'));\n * }\n * ```\n */\nexport async function generateAllStaticParams(): Promise<{ slug: string[] }[]> {\n const envResult = validateStaticParamsEnv();\n\n if (!envResult.valid) {\n throw new Error(\n `[Riverbank] generateAllStaticParams requires env vars: ${envResult.missingVars.join(', ')}. ` +\n `This error prevents accidentally deploying with zero static pages.`\n );\n }\n\n const { apiKey, siteId: _siteId, baseUrl } = envResult.config;\n\n // Create client (unused until getAllPublishedRoutes is implemented)\n const _client = createRiverbankClient({\n apiKey,\n baseUrl,\n // Disable caching for build-time fetches\n cache: { enabled: false },\n // Disable resilience for build-time fetches (want fast failure)\n resilience: { enabled: false },\n });\n\n // TODO: This requires adding getAllPublishedRoutes to the client\n // which requires the CMS endpoint to be updated to accept API key auth\n // and support the publishedOnly query param.\n //\n // For now, we'll use getPage to fetch the site's homepage which contains\n // the navigation data, and extract routes from there.\n //\n // In the future, implement:\n // const routes = await client.getAllPublishedRoutes({ siteId });\n // return routes.map(path => ({ slug: pathToSlugArray(path) }));\n\n // Temporary: Return empty array with warning\n // This allows the helper to be used but won't pre-render any pages\n console.warn(\n '[Riverbank] generateAllStaticParams: getAllPublishedRoutes API not yet implemented. ' +\n 'Pages will be generated on-demand with ISR.'\n );\n\n // Return at least the homepage\n return [{ slug: [] }];\n}\n","/**\n * Next.js integration helpers for Riverbank CMS SDK.\n *\n * Provides opinionated factories for common Next.js patterns, reducing\n * boilerplate while maintaining full customizability through escape hatches.\n *\n * @example Basic catch-all page\n * ```tsx\n * // src/app/[[...slug]]/page.tsx\n * import { createCatchAllPage } from '@riverbankcms/sdk/next';\n * import { getRiverbankClient } from '@/lib/builder-client';\n * import config from '@/riverbank.config';\n *\n * const { Page, generateMetadata } = createCatchAllPage({\n * getClient: getRiverbankClient,\n * config,\n * });\n *\n * export default Page;\n * export { generateMetadata };\n * ```\n *\n * @example With customization\n * ```tsx\n * const { Page, generateMetadata } = createCatchAllPage({\n * getClient,\n * config,\n * blockOverrides: { hero: MyCustomHero },\n * beforeRender: async () => {\n * if (process.env.MAINTENANCE_MODE === 'true') {\n * return <MaintenancePage />;\n * }\n * return null;\n * },\n * });\n * ```\n *\n * @packageDocumentation\n */\n\nimport { isPreviewMode } from '../env';\n\n// Catch-all page factory\nexport { createCatchAllPage } from './catch-all';\nexport type {\n CreateCatchAllPageOptions,\n CreateCatchAllPageResult,\n CatchAllPageProps,\n CatchAllContext,\n} from './types';\n\n// Static params utilities\nexport {\n generateAllStaticParams,\n validateStaticParamsEnv,\n pathToSlugArray,\n type StaticParamsEnvResult,\n} from './static-params';\n\n/**\n * ISR revalidation duration in seconds for production mode.\n * 5 minutes provides a good balance between freshness and performance.\n */\nexport const ISR_REVALIDATE_SECONDS = 300;\n\nexport interface ISRConfig {\n /**\n * Revalidation interval in seconds.\n * - 0: Dynamic rendering (no caching) - used in preview mode\n * - 300: 5 minute ISR - used in production mode\n */\n revalidate: number | false;\n\n /**\n * Whether the current environment is in preview mode.\n * Useful for conditional rendering or data fetching behavior.\n */\n isPreview: boolean;\n}\n\n/**\n * Get ISR configuration based on the current environment.\n *\n * @example\n * ```tsx\n * // app/[[...slug]]/page.tsx\n * import { getISRConfig } from '@riverbankcms/sdk/next';\n *\n * export const revalidate = getISRConfig().revalidate;\n * ```\n *\n * @returns ISR configuration with revalidate interval and preview flag\n */\nexport function getISRConfig(): ISRConfig {\n const preview = isPreviewMode();\n return {\n revalidate: preview ? 0 : ISR_REVALIDATE_SECONDS,\n isPreview: preview,\n };\n}\n"]}
|
|
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":["isPreviewMode","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';\n\n/**\n * Detect preview mode from environment variable.\n *\n * Sites can enable preview mode by setting:\n * ```\n * RIVERBANK_PREVIEW_MODE=true\n * ```\n *\n * This is typically set in preview/staging environments to fetch\n * draft content instead of published content.\n */\nfunction isPreviewMode(): boolean {\n return process.env.RIVERBANK_PREVIEW_MODE === 'true';\n}\n\n/**\n * Convert URL slug segments to a path string.\n *\n * @example\n * ```\n * slugToPath(undefined) // '/'\n * slugToPath([]) // '/'\n * slugToPath(['about']) // '/about'\n * slugToPath(['blog', 'post']) // '/blog/post'\n * ```\n */\nfunction slugToPath(slug?: string[]): string {\n if (!slug || slug.length === 0) return '/';\n return '/' + slug.join('/');\n}\n\n/**\n * Factory function to create a Next.js catch-all page component and metadata generator.\n *\n * This provides a simple, opinionated setup for rendering Riverbank CMS content\n * in external SDK sites. It handles:\n *\n * - Page and content entry routing\n * - Preview mode detection (via `RIVERBANK_PREVIEW_MODE` env var)\n * - SEO metadata generation\n * - Block overrides for custom components\n * - 404 handling for missing content\n *\n * ## Escape Hatches\n *\n * For customization beyond the defaults, use these options:\n *\n * - `beforeRender`: Intercept before rendering (maintenance mode, access control)\n * - `customMetadata`: Full control over SEO metadata\n * - `wrapper`: Wrap all content (analytics, error boundaries)\n *\n * For maximum control, use the lower-level helpers directly:\n * - `loadContent()` from `@riverbankcms/sdk/rendering`\n * - `Page` component from `@riverbankcms/sdk/rendering`\n * - `generatePageMetadata()` from `@riverbankcms/sdk/metadata`\n *\n * @param options - Configuration options\n * @returns Object with `Page` component and `generateMetadata` function\n *\n * @example Basic usage\n * ```tsx\n * const { Page, generateMetadata } = createCatchAllPage({\n * getClient: getRiverbankClient,\n * config,\n * });\n *\n * export default Page;\n * export { generateMetadata };\n * ```\n *\n * @example With maintenance mode\n * ```tsx\n * const { Page, generateMetadata } = createCatchAllPage({\n * getClient,\n * config,\n * beforeRender: async () => {\n * if (process.env.MAINTENANCE_MODE === 'true') {\n * return <MaintenancePage />;\n * }\n * return null;\n * },\n * });\n * ```\n */\nexport function createCatchAllPage(\n options: CreateCatchAllPageOptions\n): CreateCatchAllPageResult {\n const {\n getClient,\n config,\n blockOverrides,\n siteUrl,\n beforeRender,\n customMetadata,\n wrapper: Wrapper,\n } = options;\n\n /**\n * Main page component that loads and renders CMS content.\n */\n async function Page({ params, searchParams: searchParamsPromise }: CatchAllPageProps): Promise<React.ReactNode> {\n const [{ slug }, searchParams] = await Promise.all([\n params,\n searchParamsPromise ?? Promise.resolve({}),\n ]);\n const path = slugToPath(slug);\n const preview = isPreviewMode();\n const client = getClient();\n\n // Load content (page or entry)\n let content: LoadContentResult;\n try {\n content = await loadContent({\n client,\n siteId: config.siteId,\n path,\n preview,\n });\n } catch (error) {\n // Log the error for debugging - could be a network error, auth failure, etc.\n // We treat all errors as \"not found\" since the page can't be rendered\n console.debug('[createCatchAllPage] Failed to load content for path:', path, error);\n return notFound();\n }\n\n // ESCAPE HATCH: beforeRender for maintenance mode, access control, etc.\n if (beforeRender) {\n const intercepted = await beforeRender({ content, path, preview, searchParams });\n if (intercepted !== null) {\n return Wrapper ? <Wrapper>{intercepted}</Wrapper> : intercepted;\n }\n }\n\n let rendered: React.ReactNode;\n\n // Render page content\n if (isPageContent(content)) {\n rendered = (\n <PageComponent\n page={content.page}\n theme={content.theme}\n siteId={content.siteId}\n resolvedData={content.resolvedData}\n blockOverrides={blockOverrides}\n />\n );\n }\n // Render entry content with template\n else if (isEntryContent(content)) {\n // Entries without template pages should 404\n if (!content.templatePage) {\n return notFound();\n }\n\n rendered = (\n <PageComponent\n page={content.templatePage}\n theme={content.theme}\n siteId={content.siteId}\n resolvedData={content.resolvedData}\n blockOverrides={blockOverrides}\n dataContext={{\n contentEntry: content.dataContext.contentEntry,\n }}\n />\n );\n }\n // Unexpected content type - should never happen\n else {\n return notFound();\n }\n\n return Wrapper ? <Wrapper>{rendered}</Wrapper> : rendered;\n }\n\n /**\n * Generate SEO metadata for the page.\n */\n async function generateMetadataFn({ params, searchParams: searchParamsPromise }: CatchAllPageProps): Promise<Metadata> {\n const [{ slug }, searchParams] = await Promise.all([\n params,\n searchParamsPromise ?? Promise.resolve({}),\n ]);\n const path = slugToPath(slug);\n const preview = isPreviewMode();\n\n try {\n const content = await loadContent({\n client: getClient(),\n siteId: config.siteId,\n path,\n preview,\n });\n\n // ESCAPE HATCH: Custom metadata generation\n if (customMetadata) {\n return customMetadata({ content, path, preview, searchParams });\n }\n\n // Resolve site URL (use config URLs as fallback)\n const resolvedUrl =\n siteUrl ?? (preview ? config.previewUrl : config.liveUrl) ?? '';\n\n // Warn if no site URL is configured - OG/Twitter tags require absolute URLs\n if (!resolvedUrl) {\n console.warn(\n '[createCatchAllPage] No siteUrl configured. OpenGraph and Twitter tags will have relative URLs which may cause social sharing previews to fail. ' +\n 'Set siteUrl option, or config.liveUrl/config.previewUrl in your riverbank.config.'\n );\n }\n\n // Build page object for metadata\n // For entries, we use metaTitle/metaDescription; purpose is used as description fallback\n const page = isPageContent(content)\n ? content.page\n : isEntryContent(content)\n ? {\n name: content.entry.metaTitle ?? content.entry.title,\n purpose: content.entry.metaDescription ?? undefined,\n }\n : { name: 'Page' };\n\n // Use preview metadata (noindex) in preview mode\n const metadataGenerator = preview\n ? generatePreviewMetadata\n : generatePageMetadata;\n\n // Use site data from loadContent result (no duplicate API call needed)\n return metadataGenerator({\n page,\n site: content.site,\n path,\n siteUrl: resolvedUrl,\n });\n } catch (error) {\n // Log the error for debugging purposes\n console.debug('[createCatchAllPage] Failed to generate metadata for path:', path, error);\n // Return minimal metadata on error - page will handle the actual 404\n return {\n title: 'Page Not Found',\n robots: { index: false, follow: false },\n };\n }\n }\n\n return {\n Page,\n generateMetadata: generateMetadataFn,\n };\n}\n","/**\n * Client-side React hook to fetch page data.\n *\n * Use this in client components for dynamic page loading.\n */\n\nimport { useState, useEffect } from 'react';\nimport type { RiverbankClient } from '../../client/types';\nimport type { PageProps } from '../components/Page';\nimport type { RuntimeSdkConfig } from '../helpers/loadPage';\nimport { prefetchBlockData } from '../../data/prefetchBlockData';\n\nexport type UsePageParams = {\n client: RiverbankClient;\n siteId: string;\n path: string;\n pageId?: string;\n /**\n * If true, fetches draft/unpublished content instead of published content.\n * This affects both the page structure and block data loaders.\n * Requires API key with site access.\n *\n * @default false\n */\n preview?: boolean;\n};\n\nexport type UsePageResult =\n | { loading: true; error: null; page: null; theme: null; siteId: string; resolvedData: null; sdkConfig: null }\n | { loading: false; error: Error; page: null; theme: null; siteId: string; resolvedData: null; sdkConfig: null }\n | { loading: false; error: null; sdkConfig: RuntimeSdkConfig | null } & Omit<PageProps, 'registry' | 'wrapBlock' | 'usePlaceholders' | 'sdkConfig'>;\n\n/**\n * Client-side React hook to fetch all data needed for <Page> component.\n *\n * Fetches site data, page data, and prefetches block data loaders.\n * Returns loading and error states for proper UI handling.\n *\n * IMPORTANT: The client object should be stable across renders to avoid\n * unnecessary re-fetches. Create it outside your component or use useMemo:\n *\n * ```tsx\n * // ✅ Good - stable reference\n * const client = useMemo(\n * () => createRiverbankClient({ apiKey, baseUrl }),\n * [apiKey, baseUrl]\n * );\n *\n * // ❌ Bad - new client on every render (causes infinite loops)\n * const client = createRiverbankClient({ apiKey, baseUrl });\n * ```\n *\n * @example Basic usage\n * ```tsx\n * import { createRiverbankClient } from '@riverbankcms/sdk';\n * import { usePage, Page } from '@riverbankcms/sdk/rendering';\n *\n * const client = createRiverbankClient({\n * apiKey: process.env.NEXT_PUBLIC_RIVERBANK_API_KEY!,\n * baseUrl: process.env.NEXT_PUBLIC_DASHBOARD_URL + '/api',\n * });\n *\n * function MyPage({ path }: { path: string }) {\n * const pageData = usePage({ client, siteId: 'site-123', path });\n *\n * if (pageData.loading) {\n * return <div>Loading...</div>;\n * }\n *\n * if (pageData.error) {\n * return <div>Error: {pageData.error.message}</div>;\n * }\n *\n * return <Page {...pageData} />;\n * }\n * ```\n *\n * @example With custom loading/error states\n * ```tsx\n * function MyPage({ path }: { path: string }) {\n * const pageData = usePage({ client, siteId: 'site-123', path });\n *\n * if (pageData.loading) {\n * return <Skeleton />;\n * }\n *\n * if (pageData.error) {\n * return (\n * <ErrorBoundary\n * error={pageData.error}\n * onRetry={() => window.location.reload()}\n * />\n * );\n * }\n *\n * return <Page {...pageData} />;\n * }\n * ```\n */\nexport function usePage(params: UsePageParams): UsePageResult {\n const { client, siteId, path, pageId, preview = false } = params;\n\n const [result, setResult] = useState<UsePageResult>({\n loading: true,\n error: null,\n page: null,\n theme: null,\n siteId,\n resolvedData: null,\n sdkConfig: null,\n });\n\n useEffect(() => {\n let cancelled = false;\n\n async function fetchPage() {\n try {\n // Fetch site and page data in parallel\n const [site, pageResponse] = await Promise.all([\n client.getSite({ id: siteId }),\n client.getPage({ siteId, path, preview }),\n ]);\n\n // If component unmounted, don't update state\n if (cancelled) return;\n\n // Extract page data (getContentByPath can return page or entry)\n if ('entry' in pageResponse) {\n throw new Error(\n 'This path resolves to a content entry, not a page. ' +\n 'Use useContent() instead, which handles both pages and entries. ' +\n 'For entries, useContent() returns the raw entry data for custom rendering.'\n );\n }\n\n const { page: pageData } = pageResponse;\n\n // Convert API response blocks to PageOutline format\n // API returns blocks with full content, but PageOutline only needs structure\n const blocks = pageData.blocks.map((block) => {\n if (!block || typeof block !== 'object') {\n throw new Error('Invalid block format in API response');\n }\n if (typeof block.id !== 'string' && block.id !== null) {\n throw new Error(`Invalid block id: expected string or null, got ${typeof block.id}`);\n }\n if (typeof block.kind !== 'string') {\n throw new Error(`Invalid block kind: expected string, got ${typeof block.kind}`);\n }\n if (typeof block.purpose !== 'string') {\n throw new Error(`Invalid block purpose: expected string, got ${typeof block.purpose}`);\n }\n return {\n id: block.id,\n kind: block.kind,\n purpose: block.purpose,\n };\n });\n\n const pageOutline = {\n name: pageData.name,\n path: pageData.path,\n purpose: pageData.purpose,\n blocks,\n };\n\n // Prefetch block data loaders\n const resolvedData = await prefetchBlockData(\n pageOutline,\n {\n siteId,\n pageId: pageId ?? pageData.id,\n previewStage: preview ? 'preview' : 'published',\n },\n client\n );\n\n // If component unmounted, don't update state\n if (cancelled) return;\n\n setResult({\n loading: false,\n error: null,\n page: pageOutline,\n theme: site.theme,\n siteId,\n resolvedData,\n sdkConfig: site.sdkConfig ?? null,\n });\n } catch (error) {\n if (cancelled) return;\n\n setResult({\n loading: false,\n error: error instanceof Error ? error : new Error(String(error)),\n page: null,\n theme: null,\n siteId,\n resolvedData: null,\n sdkConfig: null,\n });\n }\n }\n\n fetchPage();\n\n return () => {\n cancelled = true;\n };\n }, [client, siteId, path, pageId, preview]);\n\n return result;\n}\n","/**\n * Client-side React hook to fetch content (page or entry) by path.\n *\n * Use this in client components for dynamic routing where a path\n * could resolve to either a page or content entry.\n */\n\nimport { useState, useEffect } from 'react';\nimport type { Theme } from '@riverbankcms/blocks';\nimport type { RiverbankClient, PageResponse } from '../../client/types';\nimport type { PageProps } from '../components/Page';\nimport { prefetchBlockData } from '../../data/prefetchBlockData';\nimport type { ResolvedBlockData } from '../../data/prefetchBlockData';\nimport type { ContentEntryData } from '../helpers/loadContent';\n\nexport type UseContentParams = {\n client: RiverbankClient;\n siteId: string;\n path: string;\n /**\n * If true, fetches draft/unpublished content instead of published content.\n * This affects both pages and entries.\n * Requires API key with site access.\n *\n * @default false\n */\n preview?: boolean;\n};\n\n/**\n * Loading state\n */\ntype LoadingState = {\n loading: true;\n error: null;\n type: null;\n page: null;\n entry: null;\n theme: null;\n siteId: string;\n resolvedData: null;\n};\n\n/**\n * Error state\n */\ntype ErrorState = {\n loading: false;\n error: Error;\n type: null;\n page: null;\n entry: null;\n theme: null;\n siteId: string;\n resolvedData: null;\n};\n\n/**\n * Success state for page content\n */\ntype PageSuccessState = {\n loading: false;\n error: null;\n type: 'page';\n page: PageProps['page'];\n entry: null;\n theme: Theme;\n siteId: string;\n resolvedData: ResolvedBlockData;\n};\n\n/**\n * Success state for entry content\n */\ntype EntrySuccessState = {\n loading: false;\n error: null;\n type: 'entry';\n page: null;\n entry: ContentEntryData;\n theme: Theme;\n siteId: string;\n resolvedData: null;\n};\n\nexport type UseContentResult = LoadingState | ErrorState | PageSuccessState | EntrySuccessState;\n\n/**\n * Type guard to check if result is loading\n */\nexport function isContentLoading(result: UseContentResult): result is LoadingState {\n return result.loading === true;\n}\n\n/**\n * Type guard to check if result has an error\n */\nexport function isContentError(result: UseContentResult): result is ErrorState {\n return result.loading === false && result.error !== null;\n}\n\n/**\n * Type guard to check if result is a page\n */\nexport function isPageContentResult(result: UseContentResult): result is PageSuccessState {\n return result.loading === false && result.error === null && result.type === 'page';\n}\n\n/**\n * Type guard to check if result is an entry\n */\nexport function isEntryContentResult(result: UseContentResult): result is EntrySuccessState {\n return result.loading === false && result.error === null && result.type === 'entry';\n}\n\n/**\n * Client-side React hook to fetch content by path.\n *\n * Returns a discriminated union with loading/error states, and either\n * page data (ready for `<Page>` component) or raw entry data (for custom rendering).\n *\n * IMPORTANT: The client object should be stable across renders to avoid\n * unnecessary re-fetches. Create it outside your component or use useMemo.\n *\n * @example Dynamic routing with both pages and entries\n * ```tsx\n * \"use client\";\n *\n * import { useContent, Page, isPageContentResult } from '@riverbankcms/sdk/client';\n *\n * function DynamicPage({ path }: { path: string }) {\n * const content = useContent({ client, siteId: 'site-123', path });\n *\n * if (content.loading) return <div>Loading...</div>;\n * if (content.error) return <div>Error: {content.error.message}</div>;\n *\n * if (isPageContentResult(content)) {\n * return <Page page={content.page} theme={content.theme} siteId={content.siteId} resolvedData={content.resolvedData} />;\n * }\n *\n * // Render entry with custom UI\n * return (\n * <article>\n * <h1>{content.entry.title}</h1>\n * <div>{content.entry.content.body}</div>\n * </article>\n * );\n * }\n * ```\n *\n * @example Entry-specific rendering based on content type\n * ```tsx\n * const content = useContent({ client, siteId, path });\n *\n * if (content.loading) return <Spinner />;\n * if (content.error) return <Error error={content.error} />;\n *\n * if (content.type === 'entry') {\n * switch (content.entry.type) {\n * case 'blog-post':\n * return <BlogPost entry={content.entry} theme={content.theme} />;\n * case 'product':\n * return <ProductPage entry={content.entry} theme={content.theme} />;\n * }\n * }\n *\n * return <Page {...content} />;\n * ```\n */\nexport function useContent(params: UseContentParams): UseContentResult {\n const { client, siteId, path, preview = false } = params;\n\n const [result, setResult] = useState<UseContentResult>({\n loading: true,\n error: null,\n type: null,\n page: null,\n entry: null,\n theme: null,\n siteId,\n resolvedData: null,\n });\n\n useEffect(() => {\n let cancelled = false;\n\n async function fetchContent() {\n try {\n // Fetch site and content in parallel\n const [site, contentResponse] = await Promise.all([\n client.getSite({ id: siteId }),\n client.getPage({ siteId, path, preview }),\n ]);\n\n // If component unmounted, don't update state\n if (cancelled) return;\n\n // Check if response is an entry\n if (isEntryResponse(contentResponse)) {\n const entryData = contentResponse.entry;\n\n const entry: ContentEntryData = {\n id: entryData.id,\n type: entryData.type,\n title: entryData.title,\n slug: entryData.slug,\n path: entryData.path,\n status: entryData.status,\n publishAt: entryData.publishAt,\n content: preview\n ? (entryData.draftContent ?? entryData.content)\n : entryData.content,\n metaTitle: preview\n ? (entryData.draftMetaTitle ?? entryData.metaTitle)\n : entryData.metaTitle,\n metaDescription: preview\n ? (entryData.draftMetaDescription ?? entryData.metaDescription)\n : entryData.metaDescription,\n createdAt: entryData.createdAt,\n updatedAt: entryData.updatedAt,\n };\n\n setResult({\n loading: false,\n error: null,\n type: 'entry',\n page: null,\n entry,\n theme: site.theme,\n siteId,\n resolvedData: null,\n });\n return;\n }\n\n // Handle page response\n const { page: pageData } = contentResponse;\n\n // Convert API response blocks to PageOutline format\n const blocks = pageData.blocks.map((block) => {\n if (!block || typeof block !== 'object') {\n throw new Error('Invalid block format in API response');\n }\n if (typeof block.id !== 'string' && block.id !== null) {\n throw new Error(`Invalid block id: expected string or null, got ${typeof block.id}`);\n }\n if (typeof block.kind !== 'string') {\n throw new Error(`Invalid block kind: expected string, got ${typeof block.kind}`);\n }\n if (typeof block.purpose !== 'string') {\n throw new Error(`Invalid block purpose: expected string, got ${typeof block.purpose}`);\n }\n return {\n id: block.id,\n kind: block.kind,\n purpose: block.purpose,\n };\n });\n\n const pageOutline = {\n name: pageData.name,\n path: pageData.path,\n purpose: pageData.purpose,\n blocks,\n };\n\n // Prefetch block data loaders for pages\n const resolvedData = await prefetchBlockData(\n pageOutline,\n {\n siteId,\n pageId: pageData.id,\n previewStage: preview ? 'preview' : 'published',\n },\n client\n );\n\n // If component unmounted, don't update state\n if (cancelled) return;\n\n setResult({\n loading: false,\n error: null,\n type: 'page',\n page: pageOutline,\n entry: null,\n theme: site.theme,\n siteId,\n resolvedData,\n });\n } catch (error) {\n if (cancelled) return;\n\n setResult({\n loading: false,\n error: error instanceof Error ? error : new Error(String(error)),\n type: null,\n page: null,\n entry: null,\n theme: null,\n siteId,\n resolvedData: null,\n });\n }\n }\n\n fetchContent();\n\n return () => {\n cancelled = true;\n };\n }, [client, siteId, path, preview]);\n\n return result;\n}\n\n/**\n * Type guard to check if API response is an entry\n */\nfunction isEntryResponse(response: PageResponse): response is Extract<PageResponse, { type: 'entry' }> {\n return 'type' in response && response.type === 'entry';\n}\n","/**\n * Static params generation for Next.js SSG\n *\n * Provides helpers for generating static params from published CMS routes.\n *\n * @example\n * ```tsx\n * // app/[[...slug]]/page.tsx\n * import { generateAllStaticParams } from '@riverbankcms/sdk/next';\n *\n * export { generateAllStaticParams as generateStaticParams };\n * ```\n */\n\nimport { createRiverbankClient } from '../client';\n\n/**\n * Environment variable validation result\n */\nexport type StaticParamsEnvResult =\n | { valid: true; config: { apiKey: string; siteId: string; baseUrl: string } }\n | { valid: false; missingVars: string[] };\n\n/**\n * Validate that all required environment variables are set.\n *\n * Required env vars:\n * - RIVERBANK_API_KEY: API key for published content\n * - RIVERBANK_SITE_ID: Site ID\n * - NEXT_PUBLIC_DASHBOARD_URL: Dashboard API URL\n *\n * @returns Validation result with config or missing vars\n */\nexport function validateStaticParamsEnv(): StaticParamsEnvResult {\n const apiKey = process.env.RIVERBANK_API_KEY;\n const siteId = process.env.RIVERBANK_SITE_ID;\n const baseUrl = process.env.NEXT_PUBLIC_DASHBOARD_URL;\n\n const missingVars: string[] = [];\n\n if (!apiKey) missingVars.push('RIVERBANK_API_KEY');\n if (!siteId) missingVars.push('RIVERBANK_SITE_ID');\n if (!baseUrl) missingVars.push('NEXT_PUBLIC_DASHBOARD_URL');\n\n if (missingVars.length > 0) {\n return { valid: false, missingVars };\n }\n\n // TypeScript can't narrow through the checks above, so assert non-null\n return {\n valid: true,\n config: { apiKey: apiKey!, siteId: siteId!, baseUrl: baseUrl! },\n };\n}\n\n/**\n * Convert a route path to a Next.js slug array.\n *\n * @param path - The route path (e.g., '/about', '/blog/post')\n * @returns The slug array for Next.js catch-all route\n *\n * @example\n * ```ts\n * pathToSlugArray('/') // []\n * pathToSlugArray('/about') // ['about']\n * pathToSlugArray('/blog/post') // ['blog', 'post']\n * ```\n */\nexport function pathToSlugArray(path: string): string[] {\n if (path === '/') return [];\n return path.slice(1).split('/');\n}\n\n/**\n * Generate static params for all published routes.\n *\n * This function fetches all published routes from the CMS and converts them\n * to the static params format expected by Next.js catch-all routes.\n *\n * Requires environment variables:\n * - RIVERBANK_API_KEY: API key for published content\n * - RIVERBANK_SITE_ID: Site ID\n * - NEXT_PUBLIC_DASHBOARD_URL: Dashboard API URL\n *\n * @throws Error if required env vars are missing (prevents silent empty SSG in CI)\n * @returns Array of static params objects for Next.js\n *\n * @example\n * ```tsx\n * // app/[[...slug]]/page.tsx\n * import { generateAllStaticParams } from '@riverbankcms/sdk/next';\n *\n * export { generateAllStaticParams as generateStaticParams };\n *\n * // Or with custom logic:\n * export async function generateStaticParams() {\n * const params = await generateAllStaticParams();\n * // Filter or modify params as needed\n * return params.filter(p => !p.slug.includes('private'));\n * }\n * ```\n */\nexport async function generateAllStaticParams(): Promise<{ slug: string[] }[]> {\n const envResult = validateStaticParamsEnv();\n\n if (!envResult.valid) {\n throw new Error(\n `[Riverbank] generateAllStaticParams requires env vars: ${envResult.missingVars.join(', ')}. ` +\n `This error prevents accidentally deploying with zero static pages.`\n );\n }\n\n const { apiKey, siteId: _siteId, baseUrl } = envResult.config;\n\n // Create client (unused until getAllPublishedRoutes is implemented)\n const _client = createRiverbankClient({\n apiKey,\n baseUrl,\n // Disable caching for build-time fetches\n cache: { enabled: false },\n // Disable resilience for build-time fetches (want fast failure)\n resilience: { enabled: false },\n });\n\n // TODO: This requires adding getAllPublishedRoutes to the client\n // which requires the CMS endpoint to be updated to accept API key auth\n // and support the publishedOnly query param.\n //\n // For now, we'll use getPage to fetch the site's homepage which contains\n // the navigation data, and extract routes from there.\n //\n // In the future, implement:\n // const routes = await client.getAllPublishedRoutes({ siteId });\n // return routes.map(path => ({ slug: pathToSlugArray(path) }));\n\n // Temporary: Return empty array with warning\n // This allows the helper to be used but won't pre-render any pages\n console.warn(\n '[Riverbank] generateAllStaticParams: getAllPublishedRoutes API not yet implemented. ' +\n 'Pages will be generated on-demand with ISR.'\n );\n\n // Return at least the homepage\n return [{ slug: [] }];\n}\n","/**\n * Next.js integration helpers for Riverbank CMS SDK.\n *\n * Provides opinionated factories for common Next.js patterns, reducing\n * boilerplate while maintaining full customizability through escape hatches.\n *\n * @example Basic catch-all page\n * ```tsx\n * // src/app/[[...slug]]/page.tsx\n * import { createCatchAllPage } from '@riverbankcms/sdk/next';\n * import { getRiverbankClient } from '@/lib/builder-client';\n * import config from '@/riverbank.config';\n *\n * const { Page, generateMetadata } = createCatchAllPage({\n * getClient: getRiverbankClient,\n * config,\n * });\n *\n * export default Page;\n * export { generateMetadata };\n * ```\n *\n * @example With customization\n * ```tsx\n * const { Page, generateMetadata } = createCatchAllPage({\n * getClient,\n * config,\n * blockOverrides: { hero: MyCustomHero },\n * beforeRender: async () => {\n * if (process.env.MAINTENANCE_MODE === 'true') {\n * return <MaintenancePage />;\n * }\n * return null;\n * },\n * });\n * ```\n *\n * @packageDocumentation\n */\n\nimport { isPreviewMode } from '../env';\n\n// Catch-all page factory\nexport { createCatchAllPage } from './catch-all';\nexport type {\n CreateCatchAllPageOptions,\n CreateCatchAllPageResult,\n CatchAllPageProps,\n CatchAllContext,\n} from './types';\n\n// Static params utilities\nexport {\n generateAllStaticParams,\n validateStaticParamsEnv,\n pathToSlugArray,\n type StaticParamsEnvResult,\n} from './static-params';\n\n/**\n * ISR revalidation duration in seconds for production mode.\n * 5 minutes provides a good balance between freshness and performance.\n */\nexport const ISR_REVALIDATE_SECONDS = 300;\n\nexport interface ISRConfig {\n /**\n * Revalidation interval in seconds.\n * - 0: Dynamic rendering (no caching) - used in preview mode\n * - 300: 5 minute ISR - used in production mode\n */\n revalidate: number | false;\n\n /**\n * Whether the current environment is in preview mode.\n * Useful for conditional rendering or data fetching behavior.\n */\n isPreview: boolean;\n}\n\n/**\n * Get ISR configuration based on the current environment.\n *\n * @example\n * ```tsx\n * // app/[[...slug]]/page.tsx\n * import { getISRConfig } from '@riverbankcms/sdk/next';\n *\n * export const revalidate = getISRConfig().revalidate;\n * ```\n *\n * @returns ISR configuration with revalidate interval and preview flag\n */\nexport function getISRConfig(): ISRConfig {\n const preview = isPreviewMode();\n return {\n revalidate: preview ? 0 : ISR_REVALIDATE_SECONDS,\n isPreview: preview,\n };\n}\n"]}
|
package/dist/server/next.mjs
CHANGED
|
@@ -4,10 +4,10 @@ import {
|
|
|
4
4
|
} from "./chunk-SWYWZT3L.mjs";
|
|
5
5
|
import {
|
|
6
6
|
isPreviewMode
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-PPHZV6YD.mjs";
|
|
8
8
|
import {
|
|
9
9
|
createRiverbankClient
|
|
10
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-JNU7ZS2V.mjs";
|
|
11
11
|
import {
|
|
12
12
|
isEntryContent,
|
|
13
13
|
isPageContent,
|
|
@@ -19,6 +19,7 @@ import {
|
|
|
19
19
|
import "./chunk-LNOUXALA.mjs";
|
|
20
20
|
import "./chunk-AEFWG657.mjs";
|
|
21
21
|
import "./chunk-BYBJA6SP.mjs";
|
|
22
|
+
import "./chunk-NFEGQTCC.mjs";
|
|
22
23
|
|
|
23
24
|
// src/next/catch-all.tsx
|
|
24
25
|
import { notFound } from "next/navigation";
|