@riverbankcms/sdk 0.6.1 → 0.7.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +175 -0
- package/dist/cli/index.js +42 -95
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/init-docs/content/agents-section.md +50 -0
- package/dist/cli/init-docs/content/cli-reference.md +574 -0
- package/dist/cli/init-docs/content/content-management.md +384 -0
- package/dist/cli/init-docs/content/context-brand.md +125 -0
- package/dist/cli/init-docs/content/context-brief.md +77 -0
- package/dist/cli/init-docs/content/context-knowledge.md +111 -0
- package/dist/cli/init-docs/content/getting-started.md +130 -0
- package/dist/cli/init-docs/content/site-workflows-readme.md +96 -0
- package/dist/cli/init-docs/content/workflow-add-block.md +228 -0
- package/dist/cli/init-docs/content/workflow-create-page.md +193 -0
- package/dist/cli/init-docs/content/workflow-publish.md +280 -0
- package/dist/client/client.d.mts +2 -2
- package/dist/client/client.d.ts +2 -2
- package/dist/client/client.js +536 -71
- package/dist/client/client.js.map +1 -1
- package/dist/client/client.mjs +536 -78
- 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/usePage-C9tJpuKa.d.mts +6813 -0
- package/dist/client/usePage-Db9kzA41.d.ts +6813 -0
- package/dist/server/{Layout-wBtJLTVX.d.ts → Layout-Ce7PU9I5.d.ts} +1 -1
- package/dist/server/{Layout-B7cvis7r.d.mts → Layout-WllR8Zug.d.mts} +1 -1
- package/dist/server/{chunk-I7ZR2WO3.mjs → chunk-5JT452F2.mjs} +537 -76
- package/dist/server/chunk-5JT452F2.mjs.map +1 -0
- package/dist/server/{chunk-Z5ZA6Q4D.mjs → chunk-74XUVNOO.mjs} +5 -3
- package/dist/server/chunk-74XUVNOO.mjs.map +1 -0
- package/dist/server/{chunk-3B364WO2.js → chunk-7BVRA5MY.js} +4 -49
- package/dist/server/chunk-7BVRA5MY.js.map +1 -0
- package/dist/server/chunk-AET56TQX.mjs +45 -0
- package/dist/server/chunk-AET56TQX.mjs.map +1 -0
- package/dist/server/chunk-ARNCLSQT.mjs +52 -0
- package/dist/server/chunk-ARNCLSQT.mjs.map +1 -0
- package/dist/server/chunk-BNQV3PXP.js +69 -0
- package/dist/server/chunk-BNQV3PXP.js.map +1 -0
- package/dist/server/{chunk-IVHIQFJH.js → chunk-HMENX4Y7.js} +543 -82
- package/dist/server/chunk-HMENX4Y7.js.map +1 -0
- package/dist/server/{chunk-I2D7KOEA.js → chunk-JWRNMNWI.js} +5 -3
- package/dist/server/chunk-JWRNMNWI.js.map +1 -0
- package/dist/server/chunk-LQUKXIW7.mjs +13 -0
- package/dist/server/chunk-LQUKXIW7.mjs.map +1 -0
- package/dist/server/{chunk-XXFF4RVR.mjs → chunk-RBJFXNDM.mjs} +1 -46
- package/dist/server/chunk-RBJFXNDM.mjs.map +1 -0
- package/dist/server/chunk-SWYWZT3L.mjs +69 -0
- package/dist/server/chunk-SWYWZT3L.mjs.map +1 -0
- package/dist/server/chunk-T26N3P26.js +52 -0
- package/dist/server/chunk-T26N3P26.js.map +1 -0
- package/dist/server/chunk-VODFQMUW.js +45 -0
- package/dist/server/chunk-VODFQMUW.js.map +1 -0
- package/dist/server/chunk-WYNEYDXO.js +13 -0
- package/dist/server/chunk-WYNEYDXO.js.map +1 -0
- package/dist/server/{components-CICSJyp_.d.ts → components--LT61IKp.d.ts} +2 -2
- package/dist/server/{components-CMMwDXTW.d.mts → components-RPzRQve6.d.mts} +2 -2
- package/dist/server/components.d.mts +4 -4
- package/dist/server/components.d.ts +4 -4
- package/dist/server/components.js +5 -4
- package/dist/server/components.js.map +1 -1
- package/dist/server/components.mjs +5 -4
- package/dist/server/config-validation.js +0 -1
- package/dist/server/config-validation.js.map +1 -1
- package/dist/server/config-validation.mjs +0 -1
- package/dist/server/config.js +0 -1
- package/dist/server/config.js.map +1 -1
- package/dist/server/config.mjs +0 -1
- package/dist/server/config.mjs.map +1 -1
- package/dist/server/data.d.mts +1 -1
- package/dist/server/data.d.ts +1 -1
- package/dist/server/data.js +0 -1
- package/dist/server/data.js.map +1 -1
- package/dist/server/data.mjs +0 -1
- package/dist/server/env.d.mts +23 -0
- package/dist/server/env.d.ts +23 -0
- package/dist/server/env.js +7 -0
- package/dist/server/env.js.map +1 -0
- package/dist/server/env.mjs +7 -0
- package/dist/server/index-BL66CU6d.d.mts +130 -0
- package/dist/server/{index-Bucs6UqG.d.mts → index-Bkva0WAj.d.mts} +1 -1
- package/dist/server/index-CJk9iQQW.d.ts +130 -0
- package/dist/server/{index-Cp7tJuRt.d.ts → index-CSBWKA3r.d.ts} +1 -1
- package/dist/server/index.d.mts +3 -3
- package/dist/server/index.d.ts +3 -3
- package/dist/server/index.js +2 -3
- package/dist/server/index.js.map +1 -1
- package/dist/server/index.mjs +1 -2
- package/dist/server/index.mjs.map +1 -1
- package/dist/server/{loadContent-Buvmudee.d.ts → loadContent-CXUWMuzY.d.ts} +11 -3
- package/dist/server/{loadContent-BS-3wesN.d.mts → loadContent-F_tAS0Nl.d.mts} +11 -3
- package/dist/server/{loadPage-IDGVDFBB.js → loadPage-6I7F6GRF.js} +1 -2
- package/dist/server/loadPage-6I7F6GRF.js.map +1 -0
- package/dist/server/{loadPage-B8mQUUSo.d.mts → loadPage-CxlYLe5K.d.mts} +1 -1
- package/dist/server/{loadPage-DNQTTRHL.mjs → loadPage-JI2SML4M.mjs} +1 -2
- package/dist/server/{loadPage-DP3nrHBi.d.ts → loadPage-i2r-X5b9.d.ts} +1 -1
- package/dist/server/metadata.d.mts +8 -135
- package/dist/server/metadata.d.ts +8 -135
- package/dist/server/metadata.js +4 -65
- package/dist/server/metadata.js.map +1 -1
- package/dist/server/metadata.mjs +4 -65
- package/dist/server/metadata.mjs.map +1 -1
- package/dist/server/navigation.js +0 -1
- package/dist/server/navigation.js.map +1 -1
- package/dist/server/navigation.mjs +0 -1
- package/dist/server/next/revalidate.d.mts +66 -0
- package/dist/server/next/revalidate.d.ts +66 -0
- package/dist/server/next/revalidate.js +59 -0
- package/dist/server/next/revalidate.js.map +1 -0
- package/dist/server/next/revalidate.mjs +59 -0
- package/dist/server/next/revalidate.mjs.map +1 -0
- package/dist/server/next/tags.d.mts +78 -0
- package/dist/server/next/tags.d.ts +78 -0
- package/dist/server/next/tags.js +34 -0
- package/dist/server/next/tags.js.map +1 -0
- package/dist/server/next/tags.mjs +34 -0
- package/dist/server/next/tags.mjs.map +1 -0
- package/dist/server/next.d.mts +432 -0
- package/dist/server/next.d.ts +432 -0
- package/dist/server/next.js +217 -0
- package/dist/server/next.js.map +1 -0
- package/dist/server/next.mjs +217 -0
- package/dist/server/next.mjs.map +1 -0
- package/dist/server/rendering/server.d.mts +3 -3
- package/dist/server/rendering/server.d.ts +3 -3
- package/dist/server/rendering/server.js +5 -4
- package/dist/server/rendering/server.js.map +1 -1
- package/dist/server/rendering/server.mjs +5 -4
- package/dist/server/rendering.d.mts +6 -6
- package/dist/server/rendering.d.ts +6 -6
- package/dist/server/rendering.js +7 -6
- package/dist/server/rendering.js.map +1 -1
- package/dist/server/rendering.mjs +9 -8
- package/dist/server/routing.d.mts +2 -2
- package/dist/server/routing.d.ts +2 -2
- package/dist/server/routing.js +2 -4
- package/dist/server/routing.js.map +1 -1
- package/dist/server/routing.mjs +1 -3
- package/dist/server/routing.mjs.map +1 -1
- package/dist/server/server.d.mts +4 -4
- package/dist/server/server.d.ts +4 -4
- package/dist/server/server.js +4 -5
- package/dist/server/server.js.map +1 -1
- package/dist/server/server.mjs +5 -6
- package/dist/server/theme-bridge.js +0 -1
- package/dist/server/theme-bridge.js.map +1 -1
- package/dist/server/theme-bridge.mjs +0 -1
- package/dist/server/theme-bridge.mjs.map +1 -1
- package/dist/server/theme.js +1 -3
- package/dist/server/theme.js.map +1 -1
- package/dist/server/theme.mjs +0 -2
- package/dist/server/theme.mjs.map +1 -1
- package/dist/server/{types-BvcJU7zk.d.ts → types-DnkRh0UL.d.ts} +118 -9
- package/dist/server/{types-1cLz0vnq.d.mts → types-MF2AWoKv.d.mts} +118 -9
- package/dist/server/webhooks.d.mts +75 -0
- package/dist/server/webhooks.d.ts +75 -0
- package/dist/server/webhooks.js +11 -0
- package/dist/server/webhooks.js.map +1 -0
- package/dist/server/webhooks.mjs +11 -0
- package/dist/server/webhooks.mjs.map +1 -0
- package/package.json +33 -1
- package/dist/server/chunk-3B364WO2.js.map +0 -1
- package/dist/server/chunk-BJTO5JO5.mjs +0 -11
- package/dist/server/chunk-DGUM43GV.js +0 -11
- package/dist/server/chunk-DGUM43GV.js.map +0 -1
- package/dist/server/chunk-I2D7KOEA.js.map +0 -1
- package/dist/server/chunk-I7ZR2WO3.mjs.map +0 -1
- package/dist/server/chunk-IVHIQFJH.js.map +0 -1
- package/dist/server/chunk-XXFF4RVR.mjs.map +0 -1
- package/dist/server/chunk-Z5ZA6Q4D.mjs.map +0 -1
- package/dist/server/loadPage-IDGVDFBB.js.map +0 -1
- /package/dist/server/{chunk-BJTO5JO5.mjs.map → env.mjs.map} +0 -0
- /package/dist/server/{loadPage-DNQTTRHL.mjs.map → loadPage-JI2SML4M.mjs.map} +0 -0
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
// src/webhooks/verify.ts
|
|
2
|
+
import crypto from "crypto";
|
|
3
|
+
import { z } from "zod";
|
|
4
|
+
var WebhookPayloadSchema = z.object({
|
|
5
|
+
/** Event type (e.g., 'page.published', 'entry.published') */
|
|
6
|
+
event: z.string(),
|
|
7
|
+
/** ISO timestamp of when the event occurred */
|
|
8
|
+
timestamp: z.string(),
|
|
9
|
+
/** Site ID that triggered the event */
|
|
10
|
+
siteId: z.string(),
|
|
11
|
+
/** Event-specific data (varies by event type) */
|
|
12
|
+
data: z.record(z.string(), z.any()),
|
|
13
|
+
/** Cache invalidation tags (for tag-based revalidation) */
|
|
14
|
+
tags: z.array(z.string()).optional()
|
|
15
|
+
});
|
|
16
|
+
function verifyWebhookSignature(payload, signature, secret) {
|
|
17
|
+
const expectedSignature = crypto.createHmac("sha256", secret).update(payload).digest("hex");
|
|
18
|
+
if (signature.length !== expectedSignature.length) {
|
|
19
|
+
return false;
|
|
20
|
+
}
|
|
21
|
+
return crypto.timingSafeEqual(
|
|
22
|
+
Buffer.from(signature),
|
|
23
|
+
Buffer.from(expectedSignature)
|
|
24
|
+
);
|
|
25
|
+
}
|
|
26
|
+
function parseWebhookPayload(body) {
|
|
27
|
+
let parsed;
|
|
28
|
+
try {
|
|
29
|
+
parsed = JSON.parse(body);
|
|
30
|
+
} catch {
|
|
31
|
+
return { success: false, error: "Invalid JSON" };
|
|
32
|
+
}
|
|
33
|
+
const result = WebhookPayloadSchema.safeParse(parsed);
|
|
34
|
+
if (!result.success) {
|
|
35
|
+
return { success: false, error: `Invalid payload: ${result.error.message}` };
|
|
36
|
+
}
|
|
37
|
+
return { success: true, payload: result.data };
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export {
|
|
41
|
+
WebhookPayloadSchema,
|
|
42
|
+
verifyWebhookSignature,
|
|
43
|
+
parseWebhookPayload
|
|
44
|
+
};
|
|
45
|
+
//# sourceMappingURL=chunk-AET56TQX.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/webhooks/verify.ts"],"sourcesContent":["/**\n * Webhook verification utilities for Riverbank SDK\n *\n * Provides framework-agnostic webhook signature verification and payload parsing.\n */\n\nimport crypto from 'crypto';\nimport { z } from 'zod';\n\n/**\n * Zod schema for webhook payload validation.\n */\nexport const WebhookPayloadSchema = z.object({\n /** Event type (e.g., 'page.published', 'entry.published') */\n event: z.string(),\n /** ISO timestamp of when the event occurred */\n timestamp: z.string(),\n /** Site ID that triggered the event */\n siteId: z.string(),\n /** Event-specific data (varies by event type) */\n data: z.record(z.string(), z.any()),\n /** Cache invalidation tags (for tag-based revalidation) */\n tags: z.array(z.string()).optional(),\n});\n\n/**\n * Webhook payload structure sent by the CMS.\n */\nexport type WebhookPayload = z.infer<typeof WebhookPayloadSchema>;\n\n/**\n * Verify a webhook signature using HMAC-SHA256.\n *\n * Uses timing-safe comparison to prevent timing attacks.\n *\n * @param payload - The raw request body as a string\n * @param signature - The X-Riverbank-Signature header value\n * @param secret - The webhook signing secret (RIVERBANK_WEBHOOK_SECRET)\n * @returns true if signature is valid, false otherwise\n *\n * @example\n * ```ts\n * const body = await request.text();\n * const signature = request.headers.get('x-riverbank-signature');\n * const secret = process.env.RIVERBANK_WEBHOOK_SECRET;\n *\n * if (!verifyWebhookSignature(body, signature, secret)) {\n * return new Response('Invalid signature', { status: 401 });\n * }\n * ```\n */\nexport function verifyWebhookSignature(\n payload: string,\n signature: string,\n secret: string\n): boolean {\n const expectedSignature = crypto\n .createHmac('sha256', secret)\n .update(payload)\n .digest('hex');\n\n // Length check before timing-safe comparison\n // timingSafeEqual throws if lengths differ\n if (signature.length !== expectedSignature.length) {\n return false;\n }\n\n // Timing-safe comparison to prevent timing attacks\n return crypto.timingSafeEqual(\n Buffer.from(signature),\n Buffer.from(expectedSignature)\n );\n}\n\n/**\n * Result type for webhook payload parsing.\n */\nexport type ParseWebhookResult =\n | { success: true; payload: WebhookPayload }\n | { success: false; error: string };\n\n/**\n * Parse and validate a webhook payload from the request body.\n *\n * Uses Zod schema validation to ensure the payload has the expected structure.\n *\n * @param body - The raw request body as a string\n * @returns A result object with either the validated payload or an error message\n *\n * @example\n * ```ts\n * const body = await request.text();\n * const result = parseWebhookPayload(body);\n * if (!result.success) {\n * return new Response(result.error, { status: 400 });\n * }\n * console.log(`Received ${result.payload.event} for site ${result.payload.siteId}`);\n * ```\n */\nexport function parseWebhookPayload(body: string): ParseWebhookResult {\n let parsed: unknown;\n try {\n parsed = JSON.parse(body);\n } catch {\n return { success: false, error: 'Invalid JSON' };\n }\n\n const result = WebhookPayloadSchema.safeParse(parsed);\n if (!result.success) {\n return { success: false, error: `Invalid payload: ${result.error.message}` };\n }\n\n return { success: true, payload: result.data };\n}\n"],"mappings":";AAMA,OAAO,YAAY;AACnB,SAAS,SAAS;AAKX,IAAM,uBAAuB,EAAE,OAAO;AAAA;AAAA,EAE3C,OAAO,EAAE,OAAO;AAAA;AAAA,EAEhB,WAAW,EAAE,OAAO;AAAA;AAAA,EAEpB,QAAQ,EAAE,OAAO;AAAA;AAAA,EAEjB,MAAM,EAAE,OAAO,EAAE,OAAO,GAAG,EAAE,IAAI,CAAC;AAAA;AAAA,EAElC,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,SAAS;AACrC,CAAC;AA4BM,SAAS,uBACd,SACA,WACA,QACS;AACT,QAAM,oBAAoB,OACvB,WAAW,UAAU,MAAM,EAC3B,OAAO,OAAO,EACd,OAAO,KAAK;AAIf,MAAI,UAAU,WAAW,kBAAkB,QAAQ;AACjD,WAAO;AAAA,EACT;AAGA,SAAO,OAAO;AAAA,IACZ,OAAO,KAAK,SAAS;AAAA,IACrB,OAAO,KAAK,iBAAiB;AAAA,EAC/B;AACF;AA2BO,SAAS,oBAAoB,MAAkC;AACpE,MAAI;AACJ,MAAI;AACF,aAAS,KAAK,MAAM,IAAI;AAAA,EAC1B,QAAQ;AACN,WAAO,EAAE,SAAS,OAAO,OAAO,eAAe;AAAA,EACjD;AAEA,QAAM,SAAS,qBAAqB,UAAU,MAAM;AACpD,MAAI,CAAC,OAAO,SAAS;AACnB,WAAO,EAAE,SAAS,OAAO,OAAO,oBAAoB,OAAO,MAAM,OAAO,GAAG;AAAA,EAC7E;AAEA,SAAO,EAAE,SAAS,MAAM,SAAS,OAAO,KAAK;AAC/C;","names":[]}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import {
|
|
2
|
+
PageRenderer,
|
|
3
|
+
buildThemeRuntime
|
|
4
|
+
} from "./chunk-LNOUXALA.mjs";
|
|
5
|
+
|
|
6
|
+
// src/rendering/components/Page.tsx
|
|
7
|
+
import { jsx } from "react/jsx-runtime";
|
|
8
|
+
function Page({
|
|
9
|
+
page,
|
|
10
|
+
theme,
|
|
11
|
+
themeTokens: providedTokens,
|
|
12
|
+
siteId,
|
|
13
|
+
resolvedData,
|
|
14
|
+
routeMap,
|
|
15
|
+
wrapBlock,
|
|
16
|
+
registry,
|
|
17
|
+
usePlaceholders = false,
|
|
18
|
+
blockOverrides,
|
|
19
|
+
sdkConfig,
|
|
20
|
+
supabaseUrl,
|
|
21
|
+
dataContext
|
|
22
|
+
}) {
|
|
23
|
+
const baseTokens = providedTokens ?? buildThemeRuntime(theme).tokens;
|
|
24
|
+
const themeTokens = sdkConfig?.theme?.palette ? { ...baseTokens, palette: { ...baseTokens.palette, ...sdkConfig.theme.palette } } : baseTokens;
|
|
25
|
+
return /* @__PURE__ */ jsx(
|
|
26
|
+
PageRenderer,
|
|
27
|
+
{
|
|
28
|
+
theme,
|
|
29
|
+
page,
|
|
30
|
+
themeTokens,
|
|
31
|
+
usePlaceholders,
|
|
32
|
+
dataContext: {
|
|
33
|
+
siteId,
|
|
34
|
+
resolvedData,
|
|
35
|
+
routes: routeMap,
|
|
36
|
+
occurrenceContext: dataContext?.occurrenceContext ?? null,
|
|
37
|
+
contentEntry: dataContext?.contentEntry ?? null,
|
|
38
|
+
supabaseUrl
|
|
39
|
+
},
|
|
40
|
+
routeMap,
|
|
41
|
+
wrapBlock,
|
|
42
|
+
registry,
|
|
43
|
+
blockOverrides,
|
|
44
|
+
sdkConfig
|
|
45
|
+
}
|
|
46
|
+
);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export {
|
|
50
|
+
Page
|
|
51
|
+
};
|
|
52
|
+
//# sourceMappingURL=chunk-ARNCLSQT.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/rendering/components/Page.tsx"],"sourcesContent":["/**\n * Pure Page renderer component for Riverbank CMS.\n *\n * This component expects all data to be provided via props.\n * For data fetching, use:\n * - Server-side: `await loadPage({ client, siteId, path })`\n * - Client-side: `usePage({ client, siteId, path })`\n */\n\nimport { PageRenderer, buildThemeRuntime } from '@riverbankcms/blocks';\nimport type { PageOutline, RouteMap, Theme, ThemeTokens, BlockOverrides, OccurrenceContextData } from '@riverbankcms/blocks';\nimport type { ResolvedBlockData } from '../../data';\nimport type { RuntimeSdkConfig } from '../helpers/loadPage';\n\n// Re-export OccurrenceContextData for SDK consumers\nexport type { OccurrenceContextData };\n\nexport type PageProps = {\n // Required data (must be provided by caller)\n page: PageOutline;\n theme: Theme;\n siteId: string;\n\n // Optional data\n themeTokens?: ThemeTokens; // If not provided, will be built from theme\n resolvedData?: ResolvedBlockData; // Pre-fetched block data\n routeMap?: RouteMap;\n /**\n * SDK site configuration containing theme palette overrides.\n * When provided, the SDK palette tokens are merged into the theme tokens,\n * allowing blocks to use SDK-defined color tokens for section backgrounds.\n */\n sdkConfig?: RuntimeSdkConfig | null;\n /**\n * Supabase storage URL for direct image access.\n * SDK sites receive this from the API instead of requiring NEXT_PUBLIC_SUPABASE_URL env var.\n */\n supabaseUrl?: string;\n\n /**\n * Additional context data for content entry pages.\n * Used to pass occurrence context and content entry data to blocks.\n */\n dataContext?: {\n /** Occurrence context for event pages (from URL like /events/yoga/2025-01-15) */\n occurrenceContext?: OccurrenceContextData | null;\n /** Content entry data for template pages */\n contentEntry?: Record<string, unknown> | null;\n };\n\n // Customization\n wrapBlock?: (blockId: string, rendered: React.ReactNode) => React.ReactNode;\n registry?: Parameters<typeof PageRenderer>[0]['registry'];\n usePlaceholders?: boolean;\n /**\n * Custom components to override default block rendering.\n * Keys can be full block kind (\"block.hero\") or short form (\"hero\").\n *\n * This is SSR-safe - no context or hooks required.\n *\n * @example\n * ```tsx\n * <Page\n * {...pageData}\n * blockOverrides={{\n * 'hero': MyCustomHero,\n * 'block.testimonials': MyCustomTestimonials,\n * }}\n * />\n * ```\n */\n blockOverrides?: BlockOverrides;\n};\n\n/**\n * Pure renderer for Riverbank CMS pages.\n *\n * This component expects all data to be provided via props.\n * For data fetching, use:\n * - Server-side: `await loadPage({ client, siteId, path })`\n * - Client-side: `usePage({ client, siteId, path })`\n *\n * @example Server-side (Next.js App Router)\n * ```tsx\n * import { createRiverbankClient } from '@riverbankcms/sdk';\n * import { loadPage, Page } from '@riverbankcms/sdk/rendering';\n *\n * const client = createRiverbankClient({ apiKey, baseUrl });\n *\n * export default async function PageRoute({ params }) {\n * const pageData = await loadPage({\n * client,\n * siteId: 'site-id',\n * path: `/${params.slug}`,\n * });\n *\n * return <Page {...pageData} />;\n * }\n * ```\n *\n * @example Client-side\n * ```tsx\n * import { createRiverbankClient } from '@riverbankcms/sdk';\n * import { usePage, Page } from '@riverbankcms/sdk/rendering';\n *\n * const client = createRiverbankClient({ apiKey, baseUrl });\n *\n * function MyPage({ path }) {\n * const pageData = usePage({ client, siteId: 'site-id', path });\n *\n * if (pageData.loading) return <LoadingState />;\n * if (pageData.error) return <ErrorState error={pageData.error} />;\n * if (!pageData.page) return <NotFound />;\n *\n * return <Page {...pageData} />;\n * }\n * ```\n */\nexport function Page({\n page,\n theme,\n themeTokens: providedTokens,\n siteId,\n resolvedData,\n routeMap,\n wrapBlock,\n registry,\n usePlaceholders = false,\n blockOverrides,\n sdkConfig,\n supabaseUrl,\n dataContext,\n}: PageProps) {\n // Build theme tokens if not provided\n const baseTokens = providedTokens ?? buildThemeRuntime(theme).tokens;\n\n // Merge SDK palette tokens into theme tokens\n // This allows blocks to resolve SDK-defined color tokens (e.g., 'primary' -> '#6d28d9')\n const themeTokens = sdkConfig?.theme?.palette\n ? { ...baseTokens, palette: { ...baseTokens.palette, ...sdkConfig.theme.palette } }\n : baseTokens;\n\n return (\n <PageRenderer\n theme={theme}\n page={page}\n themeTokens={themeTokens}\n usePlaceholders={usePlaceholders}\n dataContext={{\n siteId,\n resolvedData,\n routes: routeMap,\n occurrenceContext: dataContext?.occurrenceContext ?? null,\n contentEntry: dataContext?.contentEntry ?? null,\n supabaseUrl,\n }}\n routeMap={routeMap}\n wrapBlock={wrapBlock}\n registry={registry}\n blockOverrides={blockOverrides}\n sdkConfig={sdkConfig}\n />\n );\n}\n"],"mappings":";;;;;;AA+II;AAzBG,SAAS,KAAK;AAAA,EACnB;AAAA,EACA;AAAA,EACA,aAAa;AAAA,EACb;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,kBAAkB;AAAA,EAClB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAAc;AAEZ,QAAM,aAAa,kBAAkB,kBAAkB,KAAK,EAAE;AAI9D,QAAM,cAAc,WAAW,OAAO,UAClC,EAAE,GAAG,YAAY,SAAS,EAAE,GAAG,WAAW,SAAS,GAAG,UAAU,MAAM,QAAQ,EAAE,IAChF;AAEJ,SACE;AAAA,IAAC;AAAA;AAAA,MACC;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA,aAAa;AAAA,QACX;AAAA,QACA;AAAA,QACA,QAAQ;AAAA,QACR,mBAAmB,aAAa,qBAAqB;AAAA,QACrD,cAAc,aAAa,gBAAgB;AAAA,QAC3C;AAAA,MACF;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA;AAAA,EACF;AAEJ;","names":[]}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
"use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } } function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; }// src/metadata/generatePageMetadata.ts
|
|
2
|
+
function generatePageMetadata(input) {
|
|
3
|
+
const { page, site, path, siteUrl, overrides, googleSiteVerification } = input;
|
|
4
|
+
const pageTitle = _nullishCoalesce(_optionalChain([overrides, 'optionalAccess', _ => _.title]), () => ( page.name));
|
|
5
|
+
const fullTitle = pageTitle === site.title ? pageTitle : `${pageTitle} | ${site.title}`;
|
|
6
|
+
const description = _nullishCoalesce(_optionalChain([overrides, 'optionalAccess', _2 => _2.description]), () => ( page.purpose));
|
|
7
|
+
const canonicalUrl = _nullishCoalesce(_optionalChain([overrides, 'optionalAccess', _3 => _3.canonicalUrl]), () => ( `${siteUrl}${path}`));
|
|
8
|
+
const fullUrl = `${siteUrl}${path}`;
|
|
9
|
+
const metadata = {
|
|
10
|
+
title: fullTitle,
|
|
11
|
+
description: _nullishCoalesce(description, () => ( void 0)),
|
|
12
|
+
alternates: {
|
|
13
|
+
canonical: canonicalUrl
|
|
14
|
+
},
|
|
15
|
+
openGraph: {
|
|
16
|
+
title: pageTitle,
|
|
17
|
+
description: _nullishCoalesce(description, () => ( void 0)),
|
|
18
|
+
url: fullUrl,
|
|
19
|
+
siteName: site.title,
|
|
20
|
+
type: "website",
|
|
21
|
+
..._optionalChain([overrides, 'optionalAccess', _4 => _4.ogImage]) ? {
|
|
22
|
+
images: [
|
|
23
|
+
{
|
|
24
|
+
url: overrides.ogImage,
|
|
25
|
+
alt: pageTitle
|
|
26
|
+
}
|
|
27
|
+
]
|
|
28
|
+
} : {}
|
|
29
|
+
},
|
|
30
|
+
twitter: {
|
|
31
|
+
card: "summary_large_image",
|
|
32
|
+
title: pageTitle,
|
|
33
|
+
description: _nullishCoalesce(description, () => ( void 0)),
|
|
34
|
+
..._optionalChain([overrides, 'optionalAccess', _5 => _5.ogImage]) ? {
|
|
35
|
+
images: [overrides.ogImage]
|
|
36
|
+
} : {}
|
|
37
|
+
}
|
|
38
|
+
};
|
|
39
|
+
if (_optionalChain([overrides, 'optionalAccess', _6 => _6.robots])) {
|
|
40
|
+
metadata.robots = {
|
|
41
|
+
index: _nullishCoalesce(overrides.robots.index, () => ( true)),
|
|
42
|
+
follow: _nullishCoalesce(overrides.robots.follow, () => ( true))
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
if (googleSiteVerification) {
|
|
46
|
+
metadata.verification = {
|
|
47
|
+
google: googleSiteVerification
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
return metadata;
|
|
51
|
+
}
|
|
52
|
+
function generatePreviewMetadata(input) {
|
|
53
|
+
return generatePageMetadata({
|
|
54
|
+
...input,
|
|
55
|
+
overrides: {
|
|
56
|
+
...input.overrides,
|
|
57
|
+
robots: {
|
|
58
|
+
index: false,
|
|
59
|
+
follow: false
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
exports.generatePageMetadata = generatePageMetadata; exports.generatePreviewMetadata = generatePreviewMetadata;
|
|
69
|
+
//# sourceMappingURL=chunk-BNQV3PXP.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["/Users/will/Projects/Business/cms/builder/packages/sdk/dist/server/chunk-BNQV3PXP.js","../../src/metadata/generatePageMetadata.ts"],"names":[],"mappings":"AAAA;ACgHO,SAAS,oBAAA,CAAqB,KAAA,EAAoC;AACvE,EAAA,MAAM,EAAE,IAAA,EAAM,IAAA,EAAM,IAAA,EAAM,OAAA,EAAS,SAAA,EAAW,uBAAuB,EAAA,EAAI,KAAA;AAGzE,EAAA,MAAM,UAAA,mCAAY,SAAA,2BAAW,OAAA,UAAS,IAAA,CAAK,MAAA;AAC3C,EAAA,MAAM,UAAA,EAAY,UAAA,IAAc,IAAA,CAAK,MAAA,EAAQ,UAAA,EAAY,CAAA,EAAA;AAGN,EAAA;AAGO,EAAA;AACzB,EAAA;AAGN,EAAA;AAClB,IAAA;AACqB,IAAA;AAChB,IAAA;AACC,MAAA;AACb,IAAA;AACW,IAAA;AACF,MAAA;AACqB,MAAA;AACvB,MAAA;AACU,MAAA;AACT,MAAA;AAEF,MAAA;AACU,QAAA;AACN,UAAA;AACiB,YAAA;AACV,YAAA;AACP,UAAA;AACF,QAAA;AAED,MAAA;AACP,IAAA;AACS,IAAA;AACD,MAAA;AACC,MAAA;AACqB,MAAA;AAExB,MAAA;AAC4B,QAAA;AAE3B,MAAA;AACP,IAAA;AACF,EAAA;AAGuB,EAAA;AACH,IAAA;AACiB,MAAA;AACE,MAAA;AACrC,IAAA;AACF,EAAA;AAG4B,EAAA;AACF,IAAA;AACd,MAAA;AACV,IAAA;AACF,EAAA;AAEO,EAAA;AACT;AAuB4E;AAC9C,EAAA;AACvB,IAAA;AACQ,IAAA;AACA,MAAA;AACD,MAAA;AACC,QAAA;AACC,QAAA;AACV,MAAA;AACF,IAAA;AACD,EAAA;AACH;ADrJ4D;AACA;AACA;AACA;AACA","file":"/Users/will/Projects/Business/cms/builder/packages/sdk/dist/server/chunk-BNQV3PXP.js","sourcesContent":[null,"/**\n * Metadata generation helper for Next.js pages\n *\n * Generates SEO-optimized metadata from Builder page data for use in\n * Next.js generateMetadata() functions.\n */\n\nimport type { LoadPageResult } from '../rendering/helpers/loadPage';\nimport type { SiteResponse } from '../client/types';\n\n/**\n * Next.js Metadata type\n * Simplified version to avoid requiring Next.js as a dependency\n */\nexport type Metadata = {\n title?: string;\n description?: string;\n alternates?: {\n canonical?: string;\n };\n openGraph?: {\n title?: string;\n description?: string;\n url?: string;\n siteName?: string;\n type?: string;\n images?: Array<{\n url: string;\n alt?: string;\n }>;\n };\n twitter?: {\n card?: string;\n title?: string;\n description?: string;\n images?: string[];\n };\n robots?: {\n index?: boolean;\n follow?: boolean;\n };\n verification?: {\n google?: string;\n };\n};\n\nexport type PageMetadataInput = {\n /**\n * Page data from loadPage() or custom page object\n */\n page: LoadPageResult['page'] | {\n name: string;\n purpose?: string;\n };\n\n /**\n * Site data from client.getSite()\n */\n site: SiteResponse['site'];\n\n /**\n * Current URL path (e.g., '/about', '/blog/post-1')\n */\n path: string;\n\n /**\n * Full site URL for canonical and OG URLs\n * Example: 'https://example.com'\n */\n siteUrl: string;\n\n /**\n * Optional custom metadata overrides\n */\n overrides?: {\n title?: string;\n description?: string;\n ogImage?: string;\n canonicalUrl?: string;\n robots?: {\n index?: boolean;\n follow?: boolean;\n };\n };\n\n /**\n * Optional Google site verification token\n */\n googleSiteVerification?: string;\n};\n\n/**\n * Generate Next.js Metadata object from Builder page data\n *\n * @example\n * ```tsx\n * import { generatePageMetadata } from '@riverbankcms/sdk/metadata';\n * import { loadPage } from '@riverbankcms/sdk';\n *\n * export async function generateMetadata({ params }) {\n * const pageData = await loadPage({ client, siteId, path: params.slug });\n * const siteData = await client.getSite({ id: siteId });\n *\n * return generatePageMetadata({\n * page: pageData.page,\n * site: siteData.site,\n * path: params.slug,\n * siteUrl: 'https://example.com',\n * });\n * }\n * ```\n */\nexport function generatePageMetadata(input: PageMetadataInput): Metadata {\n const { page, site, path, siteUrl, overrides, googleSiteVerification } = input;\n\n // Build page title\n const pageTitle = overrides?.title ?? page.name;\n const fullTitle = pageTitle === site.title ? pageTitle : `${pageTitle} | ${site.title}`;\n\n // Use page purpose as description fallback\n const description = overrides?.description ?? page.purpose;\n\n // Build full URLs\n const canonicalUrl = overrides?.canonicalUrl ?? `${siteUrl}${path}`;\n const fullUrl = `${siteUrl}${path}`;\n\n // Build metadata object\n const metadata: Metadata = {\n title: fullTitle,\n description: description ?? undefined,\n alternates: {\n canonical: canonicalUrl,\n },\n openGraph: {\n title: pageTitle,\n description: description ?? undefined,\n url: fullUrl,\n siteName: site.title,\n type: 'website',\n ...(overrides?.ogImage\n ? {\n images: [\n {\n url: overrides.ogImage,\n alt: pageTitle,\n },\n ],\n }\n : {}),\n },\n twitter: {\n card: 'summary_large_image',\n title: pageTitle,\n description: description ?? undefined,\n ...(overrides?.ogImage\n ? {\n images: [overrides.ogImage],\n }\n : {}),\n },\n };\n\n // Add robots meta if specified\n if (overrides?.robots) {\n metadata.robots = {\n index: overrides.robots.index ?? true,\n follow: overrides.robots.follow ?? true,\n };\n }\n\n // Add Google site verification if provided\n if (googleSiteVerification) {\n metadata.verification = {\n google: googleSiteVerification,\n };\n }\n\n return metadata;\n}\n\n/**\n * Generate metadata for preview/staging environments\n *\n * This helper adds noindex/nofollow robots tags to prevent search engines\n * from indexing preview or staging URLs.\n *\n * @example\n * ```tsx\n * export async function generateMetadata({ params }) {\n * const pageData = await loadPage({ client, siteId, path: params.slug });\n * const isPreview = process.env.VERCEL_ENV !== 'production';\n *\n * return generatePreviewMetadata({\n * page: pageData.page,\n * site: siteData.site,\n * path: params.slug,\n * siteUrl: 'https://example.com',\n * });\n * }\n * ```\n */\nexport function generatePreviewMetadata(input: PageMetadataInput): Metadata {\n return generatePageMetadata({\n ...input,\n overrides: {\n ...input.overrides,\n robots: {\n index: false,\n follow: false,\n },\n },\n });\n}\n"]}
|