@riverbankcms/sdk 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1892 -0
- package/dist/cli/index.js +327 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/client/analytics.d.mts +103 -0
- package/dist/client/analytics.d.ts +103 -0
- package/dist/client/analytics.js +197 -0
- package/dist/client/analytics.js.map +1 -0
- package/dist/client/analytics.mjs +169 -0
- package/dist/client/analytics.mjs.map +1 -0
- package/dist/client/bookings.d.mts +89 -0
- package/dist/client/bookings.d.ts +89 -0
- package/dist/client/bookings.js +34 -0
- package/dist/client/bookings.js.map +1 -0
- package/dist/client/bookings.mjs +11 -0
- package/dist/client/bookings.mjs.map +1 -0
- package/dist/client/client.d.mts +195 -0
- package/dist/client/client.d.ts +195 -0
- package/dist/client/client.js +606 -0
- package/dist/client/client.js.map +1 -0
- package/dist/client/client.mjs +572 -0
- package/dist/client/client.mjs.map +1 -0
- package/dist/client/hooks.d.mts +71 -0
- package/dist/client/hooks.d.ts +71 -0
- package/dist/client/hooks.js +264 -0
- package/dist/client/hooks.js.map +1 -0
- package/dist/client/hooks.mjs +235 -0
- package/dist/client/hooks.mjs.map +1 -0
- package/dist/client/rendering/client.d.mts +1 -0
- package/dist/client/rendering/client.d.ts +1 -0
- package/dist/client/rendering/client.js +33 -0
- package/dist/client/rendering/client.js.map +1 -0
- package/dist/client/rendering/client.mjs +8 -0
- package/dist/client/rendering/client.mjs.map +1 -0
- package/dist/client/usePage-BvKAa3Zw.d.mts +366 -0
- package/dist/client/usePage-BvKAa3Zw.d.ts +366 -0
- package/dist/server/chunk-2RW5HAQQ.mjs +86 -0
- package/dist/server/chunk-2RW5HAQQ.mjs.map +1 -0
- package/dist/server/chunk-3KKZVGH4.mjs +179 -0
- package/dist/server/chunk-3KKZVGH4.mjs.map +1 -0
- package/dist/server/chunk-4Z3GPTCS.js +179 -0
- package/dist/server/chunk-4Z3GPTCS.js.map +1 -0
- package/dist/server/chunk-4Z5FBFRL.mjs +211 -0
- package/dist/server/chunk-4Z5FBFRL.mjs.map +1 -0
- package/dist/server/chunk-ADREPXFU.js +86 -0
- package/dist/server/chunk-ADREPXFU.js.map +1 -0
- package/dist/server/chunk-F472SMKX.js +140 -0
- package/dist/server/chunk-F472SMKX.js.map +1 -0
- package/dist/server/chunk-GWBMJPLH.mjs +57 -0
- package/dist/server/chunk-GWBMJPLH.mjs.map +1 -0
- package/dist/server/chunk-JB4LIEFS.js +85 -0
- package/dist/server/chunk-JB4LIEFS.js.map +1 -0
- package/dist/server/chunk-PEAXKTDU.mjs +140 -0
- package/dist/server/chunk-PEAXKTDU.mjs.map +1 -0
- package/dist/server/chunk-QQ6U4QX6.js +120 -0
- package/dist/server/chunk-QQ6U4QX6.js.map +1 -0
- package/dist/server/chunk-R5YGLRUG.mjs +122 -0
- package/dist/server/chunk-R5YGLRUG.mjs.map +1 -0
- package/dist/server/chunk-SW7LE4M3.js +211 -0
- package/dist/server/chunk-SW7LE4M3.js.map +1 -0
- package/dist/server/chunk-W3K7LVPS.mjs +120 -0
- package/dist/server/chunk-W3K7LVPS.mjs.map +1 -0
- package/dist/server/chunk-WKG57P2H.mjs +85 -0
- package/dist/server/chunk-WKG57P2H.mjs.map +1 -0
- package/dist/server/chunk-YHEZMVTS.js +122 -0
- package/dist/server/chunk-YHEZMVTS.js.map +1 -0
- package/dist/server/chunk-YXDDFG3N.js +57 -0
- package/dist/server/chunk-YXDDFG3N.js.map +1 -0
- package/dist/server/components.d.mts +49 -0
- package/dist/server/components.d.ts +49 -0
- package/dist/server/components.js +22 -0
- package/dist/server/components.js.map +1 -0
- package/dist/server/components.mjs +22 -0
- package/dist/server/components.mjs.map +1 -0
- package/dist/server/config-validation.d.mts +300 -0
- package/dist/server/config-validation.d.ts +300 -0
- package/dist/server/config-validation.js +50 -0
- package/dist/server/config-validation.js.map +1 -0
- package/dist/server/config-validation.mjs +50 -0
- package/dist/server/config-validation.mjs.map +1 -0
- package/dist/server/config.d.mts +38 -0
- package/dist/server/config.d.ts +38 -0
- package/dist/server/config.js +44 -0
- package/dist/server/config.js.map +1 -0
- package/dist/server/config.mjs +44 -0
- package/dist/server/config.mjs.map +1 -0
- package/dist/server/data.d.mts +108 -0
- package/dist/server/data.d.ts +108 -0
- package/dist/server/data.js +15 -0
- package/dist/server/data.js.map +1 -0
- package/dist/server/data.mjs +15 -0
- package/dist/server/data.mjs.map +1 -0
- package/dist/server/index-B0yI_V6Z.d.mts +18 -0
- package/dist/server/index-C6M0Wfjq.d.ts +18 -0
- package/dist/server/index.d.mts +5 -0
- package/dist/server/index.d.ts +5 -0
- package/dist/server/index.js +12 -0
- package/dist/server/index.js.map +1 -0
- package/dist/server/index.mjs +12 -0
- package/dist/server/index.mjs.map +1 -0
- package/dist/server/loadContent-CJcbYF3J.d.ts +152 -0
- package/dist/server/loadContent-zhlL4YSE.d.mts +152 -0
- package/dist/server/loadPage-BYmVMk0V.d.ts +216 -0
- package/dist/server/loadPage-CCf15nt8.d.mts +216 -0
- package/dist/server/loadPage-DVH3DW6E.js +9 -0
- package/dist/server/loadPage-DVH3DW6E.js.map +1 -0
- package/dist/server/loadPage-PHQZ6XQZ.mjs +9 -0
- package/dist/server/loadPage-PHQZ6XQZ.mjs.map +1 -0
- package/dist/server/metadata.d.mts +135 -0
- package/dist/server/metadata.d.ts +135 -0
- package/dist/server/metadata.js +68 -0
- package/dist/server/metadata.js.map +1 -0
- package/dist/server/metadata.mjs +68 -0
- package/dist/server/metadata.mjs.map +1 -0
- package/dist/server/rendering/server.d.mts +83 -0
- package/dist/server/rendering/server.d.ts +83 -0
- package/dist/server/rendering/server.js +14 -0
- package/dist/server/rendering/server.js.map +1 -0
- package/dist/server/rendering/server.mjs +14 -0
- package/dist/server/rendering/server.mjs.map +1 -0
- package/dist/server/rendering.d.mts +12 -0
- package/dist/server/rendering.d.ts +12 -0
- package/dist/server/rendering.js +40 -0
- package/dist/server/rendering.js.map +1 -0
- package/dist/server/rendering.mjs +40 -0
- package/dist/server/rendering.mjs.map +1 -0
- package/dist/server/routing.d.mts +115 -0
- package/dist/server/routing.d.ts +115 -0
- package/dist/server/routing.js +57 -0
- package/dist/server/routing.js.map +1 -0
- package/dist/server/routing.mjs +57 -0
- package/dist/server/routing.mjs.map +1 -0
- package/dist/server/server.d.mts +9 -0
- package/dist/server/server.d.ts +9 -0
- package/dist/server/server.js +21 -0
- package/dist/server/server.js.map +1 -0
- package/dist/server/server.mjs +21 -0
- package/dist/server/server.mjs.map +1 -0
- package/dist/server/theme-bridge.d.mts +232 -0
- package/dist/server/theme-bridge.d.ts +232 -0
- package/dist/server/theme-bridge.js +231 -0
- package/dist/server/theme-bridge.js.map +1 -0
- package/dist/server/theme-bridge.mjs +231 -0
- package/dist/server/theme-bridge.mjs.map +1 -0
- package/dist/server/theme.d.mts +40 -0
- package/dist/server/theme.d.ts +40 -0
- package/dist/server/theme.js +17 -0
- package/dist/server/theme.js.map +1 -0
- package/dist/server/theme.mjs +17 -0
- package/dist/server/theme.mjs.map +1 -0
- package/dist/server/types-BCeqWtI2.d.mts +333 -0
- package/dist/server/types-BCeqWtI2.d.ts +333 -0
- package/dist/server/types-Bbo01M7P.d.mts +76 -0
- package/dist/server/types-Bbo01M7P.d.ts +76 -0
- package/dist/server/types-C6gmRHLe.d.mts +150 -0
- package/dist/server/types-C6gmRHLe.d.ts +150 -0
- package/package.json +147 -0
- package/src/styles/index.css +10 -0
|
@@ -0,0 +1,140 @@
|
|
|
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; }
|
|
2
|
+
|
|
3
|
+
var _chunkQQ6U4QX6js = require('./chunk-QQ6U4QX6.js');
|
|
4
|
+
|
|
5
|
+
// src/rendering/helpers/loadContent.ts
|
|
6
|
+
function isPageContent(result) {
|
|
7
|
+
return result.type === "page";
|
|
8
|
+
}
|
|
9
|
+
function isEntryContent(result) {
|
|
10
|
+
return result.type === "entry";
|
|
11
|
+
}
|
|
12
|
+
async function loadContent(params) {
|
|
13
|
+
const { client, siteId, path, preview = false } = params;
|
|
14
|
+
const [site, contentResponse] = await Promise.all([
|
|
15
|
+
client.getSite({ id: siteId }),
|
|
16
|
+
client.getPage({ siteId, path, preview })
|
|
17
|
+
]);
|
|
18
|
+
if (isEntryResponse(contentResponse)) {
|
|
19
|
+
const entryData = contentResponse.entry;
|
|
20
|
+
const entry = {
|
|
21
|
+
id: entryData.id,
|
|
22
|
+
type: entryData.type,
|
|
23
|
+
title: entryData.title,
|
|
24
|
+
slug: entryData.slug,
|
|
25
|
+
path: entryData.path,
|
|
26
|
+
status: entryData.status,
|
|
27
|
+
publishAt: entryData.publishAt,
|
|
28
|
+
// Use draft content in preview mode, otherwise use published content
|
|
29
|
+
content: preview ? _nullishCoalesce(entryData.draftContent, () => ( entryData.content)) : entryData.content,
|
|
30
|
+
metaTitle: preview ? _nullishCoalesce(entryData.draftMetaTitle, () => ( entryData.metaTitle)) : entryData.metaTitle,
|
|
31
|
+
metaDescription: preview ? _nullishCoalesce(entryData.draftMetaDescription, () => ( entryData.metaDescription)) : entryData.metaDescription,
|
|
32
|
+
createdAt: entryData.createdAt,
|
|
33
|
+
updatedAt: entryData.updatedAt
|
|
34
|
+
};
|
|
35
|
+
const { templatePage, resolvedData: resolvedData2 } = await processEntryTemplate(
|
|
36
|
+
contentResponse.templates,
|
|
37
|
+
entry,
|
|
38
|
+
{ siteId, preview },
|
|
39
|
+
client
|
|
40
|
+
);
|
|
41
|
+
return {
|
|
42
|
+
type: "entry",
|
|
43
|
+
entry,
|
|
44
|
+
templatePage,
|
|
45
|
+
resolvedData: resolvedData2,
|
|
46
|
+
dataContext: { contentEntry: entry.content },
|
|
47
|
+
theme: site.theme,
|
|
48
|
+
siteId
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
const { page: pageData } = contentResponse;
|
|
52
|
+
const blocks = pageData.blocks.map((block) => validateAndConvertBlock(block, "page"));
|
|
53
|
+
const pageOutline = {
|
|
54
|
+
name: pageData.name,
|
|
55
|
+
path: pageData.path,
|
|
56
|
+
purpose: pageData.purpose,
|
|
57
|
+
blocks
|
|
58
|
+
};
|
|
59
|
+
const resolvedData = await _chunkQQ6U4QX6js.prefetchBlockData.call(void 0,
|
|
60
|
+
pageOutline,
|
|
61
|
+
{
|
|
62
|
+
siteId,
|
|
63
|
+
pageId: pageData.id,
|
|
64
|
+
previewStage: preview ? "preview" : "published"
|
|
65
|
+
},
|
|
66
|
+
client
|
|
67
|
+
);
|
|
68
|
+
return {
|
|
69
|
+
type: "page",
|
|
70
|
+
page: pageOutline,
|
|
71
|
+
theme: site.theme,
|
|
72
|
+
siteId,
|
|
73
|
+
resolvedData
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
function isEntryResponse(response) {
|
|
77
|
+
return "type" in response && response.type === "entry";
|
|
78
|
+
}
|
|
79
|
+
function validateAndConvertBlock(block, source) {
|
|
80
|
+
if (!block || typeof block !== "object") {
|
|
81
|
+
throw new Error(`Invalid block format in ${source} API response`);
|
|
82
|
+
}
|
|
83
|
+
const blockRecord = block;
|
|
84
|
+
const kindField = source === "template" ? "blockKind" : "kind";
|
|
85
|
+
const kindValue = blockRecord[kindField];
|
|
86
|
+
if (typeof blockRecord.id !== "string" && blockRecord.id !== null) {
|
|
87
|
+
throw new Error(`Invalid block id in ${source}: expected string or null, got ${typeof blockRecord.id}`);
|
|
88
|
+
}
|
|
89
|
+
if (typeof kindValue !== "string") {
|
|
90
|
+
throw new Error(`Invalid block ${kindField} in ${source}: expected string, got ${typeof kindValue}`);
|
|
91
|
+
}
|
|
92
|
+
if (source === "page") {
|
|
93
|
+
if (typeof blockRecord.purpose !== "string") {
|
|
94
|
+
throw new Error(`Invalid block purpose in ${source}: expected string, got ${typeof blockRecord.purpose}`);
|
|
95
|
+
}
|
|
96
|
+
return {
|
|
97
|
+
id: blockRecord.id,
|
|
98
|
+
kind: kindValue,
|
|
99
|
+
purpose: blockRecord.purpose
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
const scope = blockRecord.scope;
|
|
103
|
+
const content = _nullishCoalesce(blockRecord.content, () => ( {}));
|
|
104
|
+
return {
|
|
105
|
+
id: blockRecord.id,
|
|
106
|
+
kind: kindValue,
|
|
107
|
+
purpose: scope === "entry" ? "entry-content" : "template-layout",
|
|
108
|
+
content
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
async function processEntryTemplate(templates, entry, context, client) {
|
|
112
|
+
const template = _optionalChain([templates, 'optionalAccess', _ => _[0]]);
|
|
113
|
+
if (!template || !_optionalChain([template, 'access', _2 => _2.blocks, 'optionalAccess', _3 => _3.length])) {
|
|
114
|
+
return { templatePage: null, resolvedData: {} };
|
|
115
|
+
}
|
|
116
|
+
const blocks = template.blocks.map((block) => validateAndConvertBlock(block, "template"));
|
|
117
|
+
const templatePage = {
|
|
118
|
+
name: template.name || "Entry Template",
|
|
119
|
+
path: entry.path || "/",
|
|
120
|
+
purpose: "entry-template",
|
|
121
|
+
blocks
|
|
122
|
+
};
|
|
123
|
+
const resolvedData = await _chunkQQ6U4QX6js.prefetchBlockData.call(void 0,
|
|
124
|
+
templatePage,
|
|
125
|
+
{
|
|
126
|
+
siteId: context.siteId,
|
|
127
|
+
pageId: template.id,
|
|
128
|
+
previewStage: context.preview ? "preview" : "published"
|
|
129
|
+
},
|
|
130
|
+
client
|
|
131
|
+
);
|
|
132
|
+
return { templatePage, resolvedData };
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
exports.isPageContent = isPageContent; exports.isEntryContent = isEntryContent; exports.loadContent = loadContent;
|
|
140
|
+
//# sourceMappingURL=chunk-F472SMKX.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["/Users/will/Projects/Business/cms/builder/packages/sdk/dist/server/chunk-F472SMKX.js","../../src/rendering/helpers/loadContent.ts"],"names":["resolvedData"],"mappings":"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","file":"/Users/will/Projects/Business/cms/builder/packages/sdk/dist/server/chunk-F472SMKX.js","sourcesContent":[null,"/**\n * Server-side helper to fetch content (page or entry) by path.\n *\n * Use this for dynamic routing where a path could resolve to either\n * a page or a content entry.\n */\n\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';\n\nexport type LoadContentParams = {\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 * Content entry data returned when a path resolves to an entry\n */\nexport type ContentEntryData = {\n id: string;\n /** Content type key (e.g., 'blog-post', 'product') */\n type: string | null;\n title: string;\n slug: string | null;\n path: string | null;\n status: string;\n publishAt: string | null;\n /** The raw content fields - use these to render your own UI */\n content: Record<string, unknown>;\n metaTitle: string | null;\n metaDescription: string | null;\n createdAt: string;\n updatedAt: string;\n};\n\n/**\n * Result when path resolves to a page\n */\nexport type PageContentResult = {\n type: 'page';\n /** Page outline ready for rendering with <Page> component */\n page: PageProps['page'];\n /** Site theme for styling */\n theme: Theme;\n /** Site ID */\n siteId: string;\n /** Pre-fetched block data for data loaders */\n resolvedData: ResolvedBlockData;\n};\n\n/**\n * Result when path resolves to a content entry\n */\nexport type EntryContentResult = {\n type: 'entry';\n /** Raw entry data - render this however you want */\n entry: ContentEntryData;\n /** Template page for rendering the entry (if available) */\n templatePage: PageProps['page'] | null;\n /** Pre-fetched block data for template page data loaders */\n resolvedData: ResolvedBlockData;\n /** Data context for template blocks (includes entry content for bindings) */\n dataContext: { contentEntry: Record<string, unknown> };\n /** Site theme for styling (useful if rendering with SDK components) */\n theme: Theme;\n /** Site ID */\n siteId: string;\n};\n\n/**\n * Discriminated union result from loadContent\n */\nexport type LoadContentResult = PageContentResult | EntryContentResult;\n\n/**\n * Type guard to check if result is a page\n */\nexport function isPageContent(result: LoadContentResult): result is PageContentResult {\n return result.type === 'page';\n}\n\n/**\n * Type guard to check if result is an entry\n */\nexport function isEntryContent(result: LoadContentResult): result is EntryContentResult {\n return result.type === 'entry';\n}\n\n/**\n * Server-side helper to fetch content by path.\n *\n * Returns a discriminated union - either page data (ready for `<Page>` component)\n * or raw entry data (for custom rendering).\n *\n * @example Dynamic routing with both pages and entries\n * ```tsx\n * import { loadContent, Page, isPageContent } from '@riverbankcms/sdk';\n *\n * export default async function DynamicRoute({ params }) {\n * const content = await loadContent({\n * client,\n * siteId: 'site-123',\n * path: `/${params.slug?.join('/') || ''}`,\n * });\n *\n * if (isPageContent(content)) {\n * return <Page {...content} />;\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 = await loadContent({ client, siteId, path });\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 * default:\n * return <GenericEntry entry={content.entry} />;\n * }\n * }\n *\n * return <Page {...content} />;\n * ```\n *\n * @example Preview mode for draft content\n * ```tsx\n * const content = await loadContent({\n * client,\n * siteId,\n * path,\n * preview: true, // Fetches draft content for both pages and entries\n * });\n * ```\n */\nexport async function loadContent(params: LoadContentParams): Promise<LoadContentResult> {\n const { client, siteId, path, preview = false } = params;\n\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 // 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 // Use draft content in preview mode, otherwise use published content\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 // Process template if available (uses first template from content type)\n const { templatePage, resolvedData } = await processEntryTemplate(\n contentResponse.templates as Template[] | undefined,\n entry,\n { siteId, preview },\n client\n );\n\n return {\n type: 'entry',\n entry,\n templatePage,\n resolvedData,\n dataContext: { contentEntry: entry.content },\n theme: site.theme,\n siteId,\n };\n }\n\n // Handle page response\n const { page: pageData } = contentResponse;\n\n // Convert API response blocks to PageOutline format with validation\n const blocks = pageData.blocks.map((block) => validateAndConvertBlock(block, 'page'));\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 return {\n type: 'page',\n page: pageOutline,\n theme: site.theme,\n siteId,\n resolvedData,\n };\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/**\n * Validates and converts a raw block from API response to PageOutline block format.\n * Used for both page blocks and template blocks to ensure consistent validation.\n */\nfunction validateAndConvertBlock(\n block: unknown,\n source: 'page' | 'template'\n): { id: string | null; kind: string; purpose: string; content?: Record<string, unknown> } {\n if (!block || typeof block !== 'object') {\n throw new Error(`Invalid block format in ${source} API response`);\n }\n\n const blockRecord = block as Record<string, unknown>;\n\n // Template blocks use 'blockKind', page blocks use 'kind'\n const kindField = source === 'template' ? 'blockKind' : 'kind';\n const kindValue = blockRecord[kindField];\n\n if (typeof blockRecord.id !== 'string' && blockRecord.id !== null) {\n throw new Error(`Invalid block id in ${source}: expected string or null, got ${typeof blockRecord.id}`);\n }\n if (typeof kindValue !== 'string') {\n throw new Error(`Invalid block ${kindField} in ${source}: expected string, got ${typeof kindValue}`);\n }\n\n // Template blocks derive purpose from scope, page blocks have explicit purpose\n if (source === 'page') {\n if (typeof blockRecord.purpose !== 'string') {\n throw new Error(`Invalid block purpose in ${source}: expected string, got ${typeof blockRecord.purpose}`);\n }\n return {\n id: blockRecord.id as string | null,\n kind: kindValue,\n purpose: blockRecord.purpose,\n };\n }\n\n // Template block: derive purpose from scope, include content\n const scope = blockRecord.scope as 'entry' | 'template' | undefined;\n const content = (blockRecord.content as Record<string, unknown> | null) ?? {};\n\n return {\n id: blockRecord.id as string | null,\n kind: kindValue,\n purpose: scope === 'entry' ? 'entry-content' : 'template-layout',\n content,\n };\n}\n\n/** Template block structure from API response */\ntype TemplateBlock = {\n id: string;\n blockKind: string;\n scope: 'entry' | 'template';\n content: Record<string, unknown> | null;\n};\n\n/** Template structure from API response */\ntype Template = {\n id: string;\n name: string;\n templateKey: string;\n blocks: TemplateBlock[];\n};\n\n/**\n * Processes an entry's template into a PageOutline format and prefetches block data.\n * Returns null templatePage if no valid template with blocks is available.\n */\nasync function processEntryTemplate(\n templates: Template[] | undefined,\n entry: ContentEntryData,\n context: { siteId: string; preview: boolean },\n client: RiverbankClient\n): Promise<{ templatePage: PageProps['page'] | null; resolvedData: ResolvedBlockData }> {\n const template = templates?.[0];\n\n // Templates without blocks are treated as \"no template\" - the entry should be\n // rendered with custom UI rather than an empty template page\n if (!template || !template.blocks?.length) {\n return { templatePage: null, resolvedData: {} };\n }\n\n // Convert template blocks to PageOutline format with validation\n const blocks = template.blocks.map((block) => validateAndConvertBlock(block, 'template'));\n\n const templatePage: PageProps['page'] = {\n name: template.name || 'Entry Template',\n path: entry.path || '/',\n purpose: 'entry-template',\n blocks,\n };\n\n // Prefetch block data for template\n const resolvedData = await prefetchBlockData(\n templatePage,\n {\n siteId: context.siteId,\n pageId: template.id,\n previewStage: context.preview ? 'preview' : 'published',\n },\n client\n );\n\n return { templatePage, resolvedData };\n}\n"]}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
// src/data/executeCodeLoaders.ts
|
|
2
|
+
async function executeCodeLoaders(page, context, overrides) {
|
|
3
|
+
const results = {};
|
|
4
|
+
const tasks = [];
|
|
5
|
+
for (const block of page.blocks) {
|
|
6
|
+
const blockId = block.id;
|
|
7
|
+
if (!blockId) continue;
|
|
8
|
+
const blockLoaders = overrides[block.kind];
|
|
9
|
+
if (!blockLoaders) continue;
|
|
10
|
+
const content = block.draftContent ?? block.content ?? {};
|
|
11
|
+
for (const [key, loaderFn] of Object.entries(blockLoaders)) {
|
|
12
|
+
tasks.push(
|
|
13
|
+
(async () => {
|
|
14
|
+
try {
|
|
15
|
+
const loaderContext = {
|
|
16
|
+
siteId: context.siteId ?? "",
|
|
17
|
+
pageId: context.pageId ?? "",
|
|
18
|
+
blockId,
|
|
19
|
+
blockKind: block.kind,
|
|
20
|
+
content,
|
|
21
|
+
previewStage: context.previewStage ?? "published"
|
|
22
|
+
};
|
|
23
|
+
const data = await loaderFn(loaderContext);
|
|
24
|
+
if (!results[blockId]) {
|
|
25
|
+
results[blockId] = {};
|
|
26
|
+
}
|
|
27
|
+
results[blockId][key] = data;
|
|
28
|
+
} catch (error) {
|
|
29
|
+
console.warn("[executeCodeLoaders] Loader failed:", {
|
|
30
|
+
blockKind: block.kind,
|
|
31
|
+
loaderKey: key,
|
|
32
|
+
error
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
})()
|
|
36
|
+
);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
await Promise.all(tasks);
|
|
40
|
+
return results;
|
|
41
|
+
}
|
|
42
|
+
function mergeLoaderResults(configResults, codeResults) {
|
|
43
|
+
const merged = { ...configResults };
|
|
44
|
+
for (const [blockId, loaderData] of Object.entries(codeResults)) {
|
|
45
|
+
if (!merged[blockId]) {
|
|
46
|
+
merged[blockId] = {};
|
|
47
|
+
}
|
|
48
|
+
merged[blockId] = { ...merged[blockId], ...loaderData };
|
|
49
|
+
}
|
|
50
|
+
return merged;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export {
|
|
54
|
+
executeCodeLoaders,
|
|
55
|
+
mergeLoaderResults
|
|
56
|
+
};
|
|
57
|
+
//# sourceMappingURL=chunk-GWBMJPLH.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/data/executeCodeLoaders.ts"],"sourcesContent":["/**\n * Execute code-based data loaders for SDK custom blocks.\n *\n * Code loaders are user-defined async functions that can fetch data\n * from any API (not just whitelisted CMS endpoints). They run\n * server-side during loadPage().\n */\n\nimport type { PageOutline } from '@riverbankcms/blocks';\nimport type { PrefetchContext, ResolvedBlockData } from '@riverbankcms/blocks/system/data';\nimport type { DataLoaderContext, DataLoaderOverrides } from './types';\n\n/**\n * Execute code-based data loaders for blocks on a page.\n *\n * Iterates through page blocks and executes any registered loader\n * functions for matching block kinds. Runs all loaders in parallel.\n *\n * @param page - Page outline with blocks to process\n * @param context - Prefetch context (siteId, pageId, previewStage)\n * @param overrides - Map of block kinds to loader functions\n * @returns Resolved data keyed by blockId, then loader key\n *\n * @example\n * ```typescript\n * const codeData = await executeCodeLoaders(page, context, {\n * 'custom.featured-products': {\n * products: async (ctx) => fetchProducts(ctx.content.categoryId),\n * },\n * });\n * // codeData: { 'block-123': { products: [...] } }\n * ```\n */\nexport async function executeCodeLoaders(\n page: PageOutline,\n context: PrefetchContext,\n overrides: DataLoaderOverrides,\n): Promise<ResolvedBlockData> {\n const results: ResolvedBlockData = {};\n const tasks: Promise<void>[] = [];\n\n for (const block of page.blocks) {\n const blockId = block.id;\n if (!blockId) continue;\n\n // Look up loaders for this block kind\n const blockLoaders = overrides[block.kind];\n if (!blockLoaders) continue;\n\n // Get block content (prefer draft in preview mode)\n const content = (\n (block as { draftContent?: unknown }).draftContent ??\n (block as { content?: unknown }).content ??\n {}\n ) as Record<string, unknown>;\n\n // Execute each loader for this block\n for (const [key, loaderFn] of Object.entries(blockLoaders)) {\n tasks.push(\n (async () => {\n try {\n const loaderContext: DataLoaderContext = {\n siteId: context.siteId ?? '',\n pageId: context.pageId ?? '',\n blockId,\n blockKind: block.kind,\n content,\n previewStage: context.previewStage ?? 'published',\n };\n\n const data = await loaderFn(loaderContext);\n\n if (!results[blockId]) {\n results[blockId] = {};\n }\n results[blockId]![key] = data;\n } catch (error) {\n // Log but don't throw - data loading is best-effort\n // SDK users should handle missing data gracefully in components\n console.warn('[executeCodeLoaders] Loader failed:', {\n blockKind: block.kind,\n loaderKey: key,\n error,\n });\n }\n })(),\n );\n }\n }\n\n await Promise.all(tasks);\n return results;\n}\n\n/**\n * Merge resolved data from config loaders and code loaders.\n *\n * Code loader results take precedence on key conflicts.\n *\n * @param configResults - Data from config-based loaders (CMS endpoints)\n * @param codeResults - Data from code-based loaders (external APIs)\n * @returns Merged data with code results overwriting config on conflicts\n */\nexport function mergeLoaderResults(\n configResults: ResolvedBlockData,\n codeResults: ResolvedBlockData,\n): ResolvedBlockData {\n const merged: ResolvedBlockData = { ...configResults };\n\n for (const [blockId, loaderData] of Object.entries(codeResults)) {\n if (!merged[blockId]) {\n merged[blockId] = {};\n }\n // Code loaders take precedence on key conflicts\n merged[blockId] = { ...merged[blockId], ...loaderData };\n }\n\n return merged;\n}\n"],"mappings":";AAiCA,eAAsB,mBACpB,MACA,SACA,WAC4B;AAC5B,QAAM,UAA6B,CAAC;AACpC,QAAM,QAAyB,CAAC;AAEhC,aAAW,SAAS,KAAK,QAAQ;AAC/B,UAAM,UAAU,MAAM;AACtB,QAAI,CAAC,QAAS;AAGd,UAAM,eAAe,UAAU,MAAM,IAAI;AACzC,QAAI,CAAC,aAAc;AAGnB,UAAM,UACH,MAAqC,gBACrC,MAAgC,WACjC,CAAC;AAIH,eAAW,CAAC,KAAK,QAAQ,KAAK,OAAO,QAAQ,YAAY,GAAG;AAC1D,YAAM;AAAA,SACH,YAAY;AACX,cAAI;AACF,kBAAM,gBAAmC;AAAA,cACvC,QAAQ,QAAQ,UAAU;AAAA,cAC1B,QAAQ,QAAQ,UAAU;AAAA,cAC1B;AAAA,cACA,WAAW,MAAM;AAAA,cACjB;AAAA,cACA,cAAc,QAAQ,gBAAgB;AAAA,YACxC;AAEA,kBAAM,OAAO,MAAM,SAAS,aAAa;AAEzC,gBAAI,CAAC,QAAQ,OAAO,GAAG;AACrB,sBAAQ,OAAO,IAAI,CAAC;AAAA,YACtB;AACA,oBAAQ,OAAO,EAAG,GAAG,IAAI;AAAA,UAC3B,SAAS,OAAO;AAGd,oBAAQ,KAAK,uCAAuC;AAAA,cAClD,WAAW,MAAM;AAAA,cACjB,WAAW;AAAA,cACX;AAAA,YACF,CAAC;AAAA,UACH;AAAA,QACF,GAAG;AAAA,MACL;AAAA,IACF;AAAA,EACF;AAEA,QAAM,QAAQ,IAAI,KAAK;AACvB,SAAO;AACT;AAWO,SAAS,mBACd,eACA,aACmB;AACnB,QAAM,SAA4B,EAAE,GAAG,cAAc;AAErD,aAAW,CAAC,SAAS,UAAU,KAAK,OAAO,QAAQ,WAAW,GAAG;AAC/D,QAAI,CAAC,OAAO,OAAO,GAAG;AACpB,aAAO,OAAO,IAAI,CAAC;AAAA,IACrB;AAEA,WAAO,OAAO,IAAI,EAAE,GAAG,OAAO,OAAO,GAAG,GAAG,WAAW;AAAA,EACxD;AAEA,SAAO;AACT;","names":[]}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
"use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } }
|
|
2
|
+
|
|
3
|
+
var _chunkQQ6U4QX6js = require('./chunk-QQ6U4QX6.js');
|
|
4
|
+
|
|
5
|
+
// src/rendering/components/Block.tsx
|
|
6
|
+
var _blocks = require('@riverbankcms/blocks');
|
|
7
|
+
var _jsxruntime = require('react/jsx-runtime');
|
|
8
|
+
async function Block({
|
|
9
|
+
blockKind,
|
|
10
|
+
blockId,
|
|
11
|
+
content,
|
|
12
|
+
theme,
|
|
13
|
+
siteId,
|
|
14
|
+
pageId,
|
|
15
|
+
previewStage,
|
|
16
|
+
client,
|
|
17
|
+
usePlaceholders: _usePlaceholders = false,
|
|
18
|
+
override: OverrideComponent
|
|
19
|
+
}) {
|
|
20
|
+
const definition = _blocks.getBlockDefinition.call(void 0, blockKind);
|
|
21
|
+
if (!definition && !OverrideComponent) {
|
|
22
|
+
return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { className: "p-4 border border-red-300 bg-red-50 rounded", children: /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "p", { className: "text-red-800 font-semibold", children: [
|
|
23
|
+
"Unknown block type: ",
|
|
24
|
+
blockKind
|
|
25
|
+
] }) });
|
|
26
|
+
}
|
|
27
|
+
const themeRuntime = _blocks.buildThemeRuntime.call(void 0, theme);
|
|
28
|
+
let resolvedData;
|
|
29
|
+
if (client && blockId && definition) {
|
|
30
|
+
const pageOutline = {
|
|
31
|
+
name: "Single Block",
|
|
32
|
+
path: "/block",
|
|
33
|
+
purpose: "Block preview",
|
|
34
|
+
blocks: [{
|
|
35
|
+
id: blockId,
|
|
36
|
+
kind: blockKind,
|
|
37
|
+
purpose: "preview"
|
|
38
|
+
}]
|
|
39
|
+
};
|
|
40
|
+
const allResolvedData = await _chunkQQ6U4QX6js.prefetchBlockData.call(void 0,
|
|
41
|
+
pageOutline,
|
|
42
|
+
{ siteId, pageId, previewStage },
|
|
43
|
+
client
|
|
44
|
+
);
|
|
45
|
+
resolvedData = allResolvedData[blockId];
|
|
46
|
+
}
|
|
47
|
+
if (OverrideComponent) {
|
|
48
|
+
return /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
|
|
49
|
+
OverrideComponent,
|
|
50
|
+
{
|
|
51
|
+
content,
|
|
52
|
+
theme: themeRuntime.tokens,
|
|
53
|
+
themeConfig: theme,
|
|
54
|
+
data: resolvedData,
|
|
55
|
+
blockId: _nullishCoalesce(blockId, () => ( null)),
|
|
56
|
+
blockKind
|
|
57
|
+
}
|
|
58
|
+
);
|
|
59
|
+
}
|
|
60
|
+
if (!definition) {
|
|
61
|
+
return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { className: "p-4 border border-red-300 bg-red-50 rounded", children: /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "p", { className: "text-red-800 font-semibold", children: [
|
|
62
|
+
"Unknown block type: ",
|
|
63
|
+
blockKind
|
|
64
|
+
] }) });
|
|
65
|
+
}
|
|
66
|
+
const BlockComponent = _blocks.makeDefaultBlockComponent.call(void 0, { manifest: definition.manifest });
|
|
67
|
+
const registry = _blocks.getDefaultComponentRegistry.call(void 0, );
|
|
68
|
+
return /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
|
|
69
|
+
BlockComponent,
|
|
70
|
+
{
|
|
71
|
+
blockId: _nullishCoalesce(blockId, () => ( void 0)),
|
|
72
|
+
blockKind,
|
|
73
|
+
theme: themeRuntime.tokens,
|
|
74
|
+
themeConfig: theme,
|
|
75
|
+
content,
|
|
76
|
+
data: resolvedData,
|
|
77
|
+
registry
|
|
78
|
+
}
|
|
79
|
+
);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
exports.Block = Block;
|
|
85
|
+
//# sourceMappingURL=chunk-JB4LIEFS.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["/Users/will/Projects/Business/cms/builder/packages/sdk/dist/server/chunk-JB4LIEFS.js","../../src/rendering/components/Block.tsx"],"names":[],"mappings":"AAAA;AACE;AACF,sDAA4B;AAC5B;AACA;ACGA,8CAA8G;AAsGxG,+CAAA;AAhBN,MAAA,SAAsB,KAAA,CAAM;AAAA,EAC1B,SAAA;AAAA,EACA,OAAA;AAAA,EACA,OAAA;AAAA,EACA,KAAA;AAAA,EACA,MAAA;AAAA,EACA,MAAA;AAAA,EACA,YAAA;AAAA,EACA,MAAA;AAAA,EACA,eAAA,EAAiB,iBAAA,EAAmB,KAAA;AAAA,EACpC,QAAA,EAAU;AACZ,CAAA,EAAe;AAEb,EAAA,MAAM,WAAA,EAAa,wCAAA,SAA4B,CAAA;AAC/C,EAAA,GAAA,CAAI,CAAC,WAAA,GAAc,CAAC,iBAAA,EAAmB;AACrC,IAAA,uBACE,6BAAA,KAAC,EAAA,EAAI,SAAA,EAAU,6CAAA,EACb,QAAA,kBAAA,8BAAA,GAAC,EAAA,EAAE,SAAA,EAAU,4BAAA,EAA6B,QAAA,EAAA;AAAA,MAAA,sBAAA;AAAA,MAAqB;AAAA,IAAA,EAAA,CAAU,EAAA,CAC3E,CAAA;AAAA,EAEJ;AAGA,EAAA,MAAM,aAAA,EAAe,uCAAA,KAAuB,CAAA;AAG5C,EAAA,IAAI,YAAA;AACJ,EAAA,GAAA,CAAI,OAAA,GAAU,QAAA,GAAW,UAAA,EAAY;AACnC,IAAA,MAAM,YAAA,EAAc;AAAA,MAClB,IAAA,EAAM,cAAA;AAAA,MACN,IAAA,EAAM,QAAA;AAAA,MACN,OAAA,EAAS,eAAA;AAAA,MACT,MAAA,EAAQ,CAAC;AAAA,QACP,EAAA,EAAI,OAAA;AAAA,QACJ,IAAA,EAAM,SAAA;AAAA,QACN,OAAA,EAAS;AAAA,MACX,CAAC;AAAA,IACH,CAAA;AAEA,IAAA,MAAM,gBAAA,EAAkB,MAAM,gDAAA;AAAA,MAC5B,WAAA;AAAA,MACA,EAAE,MAAA,EAAQ,MAAA,EAAQ,aAAa,CAAA;AAAA,MAC/B;AAAA,IACF,CAAA;AAEA,IAAA,aAAA,EAAe,eAAA,CAAgB,OAAO,CAAA;AAAA,EACxC;AAGA,EAAA,GAAA,CAAI,iBAAA,EAAmB;AACrB,IAAA,uBACE,6BAAA;AAAA,MAAC,iBAAA;AAAA,MAAA;AAAA,QACC,OAAA;AAAA,QACA,KAAA,EAAO,YAAA,CAAa,MAAA;AAAA,QACpB,WAAA,EAAa,KAAA;AAAA,QACb,IAAA,EAAM,YAAA;AAAA,QACN,OAAA,mBAAS,OAAA,UAAW,MAAA;AAAA,QACpB;AAAA,MAAA;AAAA,IACF,CAAA;AAAA,EAEJ;AAGA,EAAA,GAAA,CAAI,CAAC,UAAA,EAAY;AACf,IAAA,uBACE,6BAAA,KAAC,EAAA,EAAI,SAAA,EAAU,6CAAA,EACb,QAAA,kBAAA,8BAAA,GAAC,EAAA,EAAE,SAAA,EAAU,4BAAA,EAA6B,QAAA,EAAA;AAAA,MAAA,sBAAA;AAAA,MAAqB;AAAA,IAAA,EAAA,CAAU,EAAA,CAC3E,CAAA;AAAA,EAEJ;AAGA,EAAA,MAAM,eAAA,EAAiB,+CAAA,EAA4B,QAAA,EAAU,UAAA,CAAW,SAAS,CAAC,CAAA;AAGlF,EAAA,MAAM,SAAA,EAAW,iDAAA,CAA4B;AAE7C,EAAA,uBACE,6BAAA;AAAA,IAAC,cAAA;AAAA,IAAA;AAAA,MACC,OAAA,mBAAS,OAAA,UAAW,KAAA,GAAA;AAAA,MACpB,SAAA;AAAA,MACA,KAAA,EAAO,YAAA,CAAa,MAAA;AAAA,MACpB,WAAA,EAAa,KAAA;AAAA,MACb,OAAA;AAAA,MACA,IAAA,EAAM,YAAA;AAAA,MACN;AAAA,IAAA;AAAA,EACF,CAAA;AAEJ;ADrGA;AACA;AACE;AACF,sBAAC","file":"/Users/will/Projects/Business/cms/builder/packages/sdk/dist/server/chunk-JB4LIEFS.js","sourcesContent":[null,"/**\n * Block component for rendering individual CMS blocks\n *\n * Renders a single block with its content and data.\n */\n\nimport * as React from 'react';\nimport { getBlockDefinition, makeDefaultBlockComponent, buildThemeRuntime, getDefaultComponentRegistry } from '@riverbankcms/blocks';\nimport type { Theme } from '@riverbankcms/blocks';\nimport type { RiverbankClient } from '../../client/types';\nimport { prefetchBlockData } from '../../data/prefetchBlockData';\n\n/**\n * Override component for custom block rendering.\n * Receives the same props as default block components.\n */\nexport type BlockOverrideComponent = React.ComponentType<{\n content: Record<string, unknown>;\n theme?: Record<string, unknown>;\n themeConfig?: Theme;\n data?: Record<string, unknown>;\n blockId?: string | null;\n blockKind?: string;\n}>;\n\nexport type BlockProps = {\n // Block identification\n blockKind: string;\n blockId?: string | null;\n\n // Block content\n content: Record<string, unknown>;\n\n // Required styling\n theme: Theme;\n siteId: string;\n\n // Optional data context for loaders\n pageId?: string;\n previewStage?: 'published' | 'preview';\n client?: RiverbankClient;\n\n // Optional customization\n usePlaceholders?: boolean;\n\n /**\n * Custom component to override default block rendering.\n * When provided, renders this component instead of the manifest-based default.\n * This is SSR-safe - no context or hooks required.\n *\n * @example\n * ```tsx\n * <Block\n * blockKind=\"block.hero\"\n * content={heroContent}\n * theme={theme}\n * siteId={siteId}\n * override={MyCustomHero}\n * />\n * ```\n */\n override?: BlockOverrideComponent;\n};\n\n/**\n * Renders a single CMS block with optional data loading.\n *\n * Use this component when you want to render individual blocks outside of a full page context,\n * or when mixing CMS blocks with custom JSX.\n *\n * @example Basic usage\n * ```tsx\n * <Block\n * blockKind=\"block.hero\"\n * content={{ headline: 'Welcome', subheadline: 'To our site' }}\n * theme={theme}\n * siteId=\"site-123\"\n * />\n * ```\n *\n * @example With data loading\n * ```tsx\n * <Block\n * blockKind=\"block.blog-listing\"\n * blockId=\"block-456\"\n * content={blockContent}\n * theme={theme}\n * siteId=\"site-123\"\n * pageId=\"page-789\"\n * client={client}\n * />\n * ```\n */\nexport async function Block({\n blockKind,\n blockId,\n content,\n theme,\n siteId,\n pageId,\n previewStage,\n client,\n usePlaceholders: _usePlaceholders = false,\n override: OverrideComponent,\n}: BlockProps) {\n // Get block definition (needed for data loaders even if using override)\n const definition = getBlockDefinition(blockKind);\n if (!definition && !OverrideComponent) {\n return (\n <div className=\"p-4 border border-red-300 bg-red-50 rounded\">\n <p className=\"text-red-800 font-semibold\">Unknown block type: {blockKind}</p>\n </div>\n );\n }\n\n // Build theme tokens\n const themeRuntime = buildThemeRuntime(theme);\n\n // Prefetch block data if client is provided and block has an ID\n let resolvedData: Record<string, unknown> | undefined;\n if (client && blockId && definition) {\n const pageOutline = {\n name: 'Single Block',\n path: '/block',\n purpose: 'Block preview',\n blocks: [{\n id: blockId,\n kind: blockKind,\n purpose: 'preview',\n }],\n };\n\n const allResolvedData = await prefetchBlockData(\n pageOutline,\n { siteId, pageId, previewStage },\n client\n );\n\n resolvedData = allResolvedData[blockId];\n }\n\n // If override component provided, use it instead of manifest-based rendering\n if (OverrideComponent) {\n return (\n <OverrideComponent\n content={content}\n theme={themeRuntime.tokens}\n themeConfig={theme}\n data={resolvedData}\n blockId={blockId ?? null}\n blockKind={blockKind}\n />\n );\n }\n\n // Fallback to manifest-based rendering\n if (!definition) {\n return (\n <div className=\"p-4 border border-red-300 bg-red-50 rounded\">\n <p className=\"text-red-800 font-semibold\">Unknown block type: {blockKind}</p>\n </div>\n );\n }\n\n // Create block component from manifest\n const BlockComponent = makeDefaultBlockComponent({ manifest: definition.manifest });\n\n // Get default component registry\n const registry = getDefaultComponentRegistry();\n\n return (\n <BlockComponent\n blockId={blockId ?? undefined}\n blockKind={blockKind}\n theme={themeRuntime.tokens}\n themeConfig={theme}\n content={content}\n data={resolvedData}\n registry={registry}\n />\n );\n}\n"]}
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
import {
|
|
2
|
+
prefetchBlockData
|
|
3
|
+
} from "./chunk-W3K7LVPS.mjs";
|
|
4
|
+
|
|
5
|
+
// src/rendering/helpers/loadContent.ts
|
|
6
|
+
function isPageContent(result) {
|
|
7
|
+
return result.type === "page";
|
|
8
|
+
}
|
|
9
|
+
function isEntryContent(result) {
|
|
10
|
+
return result.type === "entry";
|
|
11
|
+
}
|
|
12
|
+
async function loadContent(params) {
|
|
13
|
+
const { client, siteId, path, preview = false } = params;
|
|
14
|
+
const [site, contentResponse] = await Promise.all([
|
|
15
|
+
client.getSite({ id: siteId }),
|
|
16
|
+
client.getPage({ siteId, path, preview })
|
|
17
|
+
]);
|
|
18
|
+
if (isEntryResponse(contentResponse)) {
|
|
19
|
+
const entryData = contentResponse.entry;
|
|
20
|
+
const entry = {
|
|
21
|
+
id: entryData.id,
|
|
22
|
+
type: entryData.type,
|
|
23
|
+
title: entryData.title,
|
|
24
|
+
slug: entryData.slug,
|
|
25
|
+
path: entryData.path,
|
|
26
|
+
status: entryData.status,
|
|
27
|
+
publishAt: entryData.publishAt,
|
|
28
|
+
// Use draft content in preview mode, otherwise use published content
|
|
29
|
+
content: preview ? entryData.draftContent ?? entryData.content : entryData.content,
|
|
30
|
+
metaTitle: preview ? entryData.draftMetaTitle ?? entryData.metaTitle : entryData.metaTitle,
|
|
31
|
+
metaDescription: preview ? entryData.draftMetaDescription ?? entryData.metaDescription : entryData.metaDescription,
|
|
32
|
+
createdAt: entryData.createdAt,
|
|
33
|
+
updatedAt: entryData.updatedAt
|
|
34
|
+
};
|
|
35
|
+
const { templatePage, resolvedData: resolvedData2 } = await processEntryTemplate(
|
|
36
|
+
contentResponse.templates,
|
|
37
|
+
entry,
|
|
38
|
+
{ siteId, preview },
|
|
39
|
+
client
|
|
40
|
+
);
|
|
41
|
+
return {
|
|
42
|
+
type: "entry",
|
|
43
|
+
entry,
|
|
44
|
+
templatePage,
|
|
45
|
+
resolvedData: resolvedData2,
|
|
46
|
+
dataContext: { contentEntry: entry.content },
|
|
47
|
+
theme: site.theme,
|
|
48
|
+
siteId
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
const { page: pageData } = contentResponse;
|
|
52
|
+
const blocks = pageData.blocks.map((block) => validateAndConvertBlock(block, "page"));
|
|
53
|
+
const pageOutline = {
|
|
54
|
+
name: pageData.name,
|
|
55
|
+
path: pageData.path,
|
|
56
|
+
purpose: pageData.purpose,
|
|
57
|
+
blocks
|
|
58
|
+
};
|
|
59
|
+
const resolvedData = await prefetchBlockData(
|
|
60
|
+
pageOutline,
|
|
61
|
+
{
|
|
62
|
+
siteId,
|
|
63
|
+
pageId: pageData.id,
|
|
64
|
+
previewStage: preview ? "preview" : "published"
|
|
65
|
+
},
|
|
66
|
+
client
|
|
67
|
+
);
|
|
68
|
+
return {
|
|
69
|
+
type: "page",
|
|
70
|
+
page: pageOutline,
|
|
71
|
+
theme: site.theme,
|
|
72
|
+
siteId,
|
|
73
|
+
resolvedData
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
function isEntryResponse(response) {
|
|
77
|
+
return "type" in response && response.type === "entry";
|
|
78
|
+
}
|
|
79
|
+
function validateAndConvertBlock(block, source) {
|
|
80
|
+
if (!block || typeof block !== "object") {
|
|
81
|
+
throw new Error(`Invalid block format in ${source} API response`);
|
|
82
|
+
}
|
|
83
|
+
const blockRecord = block;
|
|
84
|
+
const kindField = source === "template" ? "blockKind" : "kind";
|
|
85
|
+
const kindValue = blockRecord[kindField];
|
|
86
|
+
if (typeof blockRecord.id !== "string" && blockRecord.id !== null) {
|
|
87
|
+
throw new Error(`Invalid block id in ${source}: expected string or null, got ${typeof blockRecord.id}`);
|
|
88
|
+
}
|
|
89
|
+
if (typeof kindValue !== "string") {
|
|
90
|
+
throw new Error(`Invalid block ${kindField} in ${source}: expected string, got ${typeof kindValue}`);
|
|
91
|
+
}
|
|
92
|
+
if (source === "page") {
|
|
93
|
+
if (typeof blockRecord.purpose !== "string") {
|
|
94
|
+
throw new Error(`Invalid block purpose in ${source}: expected string, got ${typeof blockRecord.purpose}`);
|
|
95
|
+
}
|
|
96
|
+
return {
|
|
97
|
+
id: blockRecord.id,
|
|
98
|
+
kind: kindValue,
|
|
99
|
+
purpose: blockRecord.purpose
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
const scope = blockRecord.scope;
|
|
103
|
+
const content = blockRecord.content ?? {};
|
|
104
|
+
return {
|
|
105
|
+
id: blockRecord.id,
|
|
106
|
+
kind: kindValue,
|
|
107
|
+
purpose: scope === "entry" ? "entry-content" : "template-layout",
|
|
108
|
+
content
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
async function processEntryTemplate(templates, entry, context, client) {
|
|
112
|
+
const template = templates?.[0];
|
|
113
|
+
if (!template || !template.blocks?.length) {
|
|
114
|
+
return { templatePage: null, resolvedData: {} };
|
|
115
|
+
}
|
|
116
|
+
const blocks = template.blocks.map((block) => validateAndConvertBlock(block, "template"));
|
|
117
|
+
const templatePage = {
|
|
118
|
+
name: template.name || "Entry Template",
|
|
119
|
+
path: entry.path || "/",
|
|
120
|
+
purpose: "entry-template",
|
|
121
|
+
blocks
|
|
122
|
+
};
|
|
123
|
+
const resolvedData = await prefetchBlockData(
|
|
124
|
+
templatePage,
|
|
125
|
+
{
|
|
126
|
+
siteId: context.siteId,
|
|
127
|
+
pageId: template.id,
|
|
128
|
+
previewStage: context.preview ? "preview" : "published"
|
|
129
|
+
},
|
|
130
|
+
client
|
|
131
|
+
);
|
|
132
|
+
return { templatePage, resolvedData };
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
export {
|
|
136
|
+
isPageContent,
|
|
137
|
+
isEntryContent,
|
|
138
|
+
loadContent
|
|
139
|
+
};
|
|
140
|
+
//# sourceMappingURL=chunk-PEAXKTDU.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/rendering/helpers/loadContent.ts"],"sourcesContent":["/**\n * Server-side helper to fetch content (page or entry) by path.\n *\n * Use this for dynamic routing where a path could resolve to either\n * a page or a content entry.\n */\n\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';\n\nexport type LoadContentParams = {\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 * Content entry data returned when a path resolves to an entry\n */\nexport type ContentEntryData = {\n id: string;\n /** Content type key (e.g., 'blog-post', 'product') */\n type: string | null;\n title: string;\n slug: string | null;\n path: string | null;\n status: string;\n publishAt: string | null;\n /** The raw content fields - use these to render your own UI */\n content: Record<string, unknown>;\n metaTitle: string | null;\n metaDescription: string | null;\n createdAt: string;\n updatedAt: string;\n};\n\n/**\n * Result when path resolves to a page\n */\nexport type PageContentResult = {\n type: 'page';\n /** Page outline ready for rendering with <Page> component */\n page: PageProps['page'];\n /** Site theme for styling */\n theme: Theme;\n /** Site ID */\n siteId: string;\n /** Pre-fetched block data for data loaders */\n resolvedData: ResolvedBlockData;\n};\n\n/**\n * Result when path resolves to a content entry\n */\nexport type EntryContentResult = {\n type: 'entry';\n /** Raw entry data - render this however you want */\n entry: ContentEntryData;\n /** Template page for rendering the entry (if available) */\n templatePage: PageProps['page'] | null;\n /** Pre-fetched block data for template page data loaders */\n resolvedData: ResolvedBlockData;\n /** Data context for template blocks (includes entry content for bindings) */\n dataContext: { contentEntry: Record<string, unknown> };\n /** Site theme for styling (useful if rendering with SDK components) */\n theme: Theme;\n /** Site ID */\n siteId: string;\n};\n\n/**\n * Discriminated union result from loadContent\n */\nexport type LoadContentResult = PageContentResult | EntryContentResult;\n\n/**\n * Type guard to check if result is a page\n */\nexport function isPageContent(result: LoadContentResult): result is PageContentResult {\n return result.type === 'page';\n}\n\n/**\n * Type guard to check if result is an entry\n */\nexport function isEntryContent(result: LoadContentResult): result is EntryContentResult {\n return result.type === 'entry';\n}\n\n/**\n * Server-side helper to fetch content by path.\n *\n * Returns a discriminated union - either page data (ready for `<Page>` component)\n * or raw entry data (for custom rendering).\n *\n * @example Dynamic routing with both pages and entries\n * ```tsx\n * import { loadContent, Page, isPageContent } from '@riverbankcms/sdk';\n *\n * export default async function DynamicRoute({ params }) {\n * const content = await loadContent({\n * client,\n * siteId: 'site-123',\n * path: `/${params.slug?.join('/') || ''}`,\n * });\n *\n * if (isPageContent(content)) {\n * return <Page {...content} />;\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 = await loadContent({ client, siteId, path });\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 * default:\n * return <GenericEntry entry={content.entry} />;\n * }\n * }\n *\n * return <Page {...content} />;\n * ```\n *\n * @example Preview mode for draft content\n * ```tsx\n * const content = await loadContent({\n * client,\n * siteId,\n * path,\n * preview: true, // Fetches draft content for both pages and entries\n * });\n * ```\n */\nexport async function loadContent(params: LoadContentParams): Promise<LoadContentResult> {\n const { client, siteId, path, preview = false } = params;\n\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 // 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 // Use draft content in preview mode, otherwise use published content\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 // Process template if available (uses first template from content type)\n const { templatePage, resolvedData } = await processEntryTemplate(\n contentResponse.templates as Template[] | undefined,\n entry,\n { siteId, preview },\n client\n );\n\n return {\n type: 'entry',\n entry,\n templatePage,\n resolvedData,\n dataContext: { contentEntry: entry.content },\n theme: site.theme,\n siteId,\n };\n }\n\n // Handle page response\n const { page: pageData } = contentResponse;\n\n // Convert API response blocks to PageOutline format with validation\n const blocks = pageData.blocks.map((block) => validateAndConvertBlock(block, 'page'));\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 return {\n type: 'page',\n page: pageOutline,\n theme: site.theme,\n siteId,\n resolvedData,\n };\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/**\n * Validates and converts a raw block from API response to PageOutline block format.\n * Used for both page blocks and template blocks to ensure consistent validation.\n */\nfunction validateAndConvertBlock(\n block: unknown,\n source: 'page' | 'template'\n): { id: string | null; kind: string; purpose: string; content?: Record<string, unknown> } {\n if (!block || typeof block !== 'object') {\n throw new Error(`Invalid block format in ${source} API response`);\n }\n\n const blockRecord = block as Record<string, unknown>;\n\n // Template blocks use 'blockKind', page blocks use 'kind'\n const kindField = source === 'template' ? 'blockKind' : 'kind';\n const kindValue = blockRecord[kindField];\n\n if (typeof blockRecord.id !== 'string' && blockRecord.id !== null) {\n throw new Error(`Invalid block id in ${source}: expected string or null, got ${typeof blockRecord.id}`);\n }\n if (typeof kindValue !== 'string') {\n throw new Error(`Invalid block ${kindField} in ${source}: expected string, got ${typeof kindValue}`);\n }\n\n // Template blocks derive purpose from scope, page blocks have explicit purpose\n if (source === 'page') {\n if (typeof blockRecord.purpose !== 'string') {\n throw new Error(`Invalid block purpose in ${source}: expected string, got ${typeof blockRecord.purpose}`);\n }\n return {\n id: blockRecord.id as string | null,\n kind: kindValue,\n purpose: blockRecord.purpose,\n };\n }\n\n // Template block: derive purpose from scope, include content\n const scope = blockRecord.scope as 'entry' | 'template' | undefined;\n const content = (blockRecord.content as Record<string, unknown> | null) ?? {};\n\n return {\n id: blockRecord.id as string | null,\n kind: kindValue,\n purpose: scope === 'entry' ? 'entry-content' : 'template-layout',\n content,\n };\n}\n\n/** Template block structure from API response */\ntype TemplateBlock = {\n id: string;\n blockKind: string;\n scope: 'entry' | 'template';\n content: Record<string, unknown> | null;\n};\n\n/** Template structure from API response */\ntype Template = {\n id: string;\n name: string;\n templateKey: string;\n blocks: TemplateBlock[];\n};\n\n/**\n * Processes an entry's template into a PageOutline format and prefetches block data.\n * Returns null templatePage if no valid template with blocks is available.\n */\nasync function processEntryTemplate(\n templates: Template[] | undefined,\n entry: ContentEntryData,\n context: { siteId: string; preview: boolean },\n client: RiverbankClient\n): Promise<{ templatePage: PageProps['page'] | null; resolvedData: ResolvedBlockData }> {\n const template = templates?.[0];\n\n // Templates without blocks are treated as \"no template\" - the entry should be\n // rendered with custom UI rather than an empty template page\n if (!template || !template.blocks?.length) {\n return { templatePage: null, resolvedData: {} };\n }\n\n // Convert template blocks to PageOutline format with validation\n const blocks = template.blocks.map((block) => validateAndConvertBlock(block, 'template'));\n\n const templatePage: PageProps['page'] = {\n name: template.name || 'Entry Template',\n path: entry.path || '/',\n purpose: 'entry-template',\n blocks,\n };\n\n // Prefetch block data for template\n const resolvedData = await prefetchBlockData(\n templatePage,\n {\n siteId: context.siteId,\n pageId: template.id,\n previewStage: context.preview ? 'preview' : 'published',\n },\n client\n );\n\n return { templatePage, resolvedData };\n}\n"],"mappings":"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","names":["resolvedData"]}
|
|
@@ -0,0 +1,120 @@
|
|
|
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/data/prefetchBlockData.ts
|
|
2
|
+
var _data = require('@riverbankcms/blocks/system/data');
|
|
3
|
+
var SUPPORTED_LOADER_ENDPOINTS = [
|
|
4
|
+
"listPublishedEntries",
|
|
5
|
+
"getPublishedEntryPreview",
|
|
6
|
+
"listPublicEvents",
|
|
7
|
+
"getPublicFormById",
|
|
8
|
+
"getPublicBookingServices"
|
|
9
|
+
];
|
|
10
|
+
async function prefetchBlockData(page, context, client, options) {
|
|
11
|
+
const { customBlocks } = _nullishCoalesce(options, () => ( {}));
|
|
12
|
+
const customBlockMap = new Map(
|
|
13
|
+
(_nullishCoalesce(customBlocks, () => ( []))).map((block) => [block.id, block])
|
|
14
|
+
);
|
|
15
|
+
return _data.prefetchBlockData.call(void 0, page, context, {
|
|
16
|
+
apiClient: async ({ endpoint, params }) => {
|
|
17
|
+
if (!isSupportedEndpoint(endpoint)) {
|
|
18
|
+
throw new Error(
|
|
19
|
+
`Unsupported loader endpoint: ${endpoint}. SDK only supports: ${SUPPORTED_LOADER_ENDPOINTS.join(", ")}`
|
|
20
|
+
);
|
|
21
|
+
}
|
|
22
|
+
switch (endpoint) {
|
|
23
|
+
case "listPublishedEntries": {
|
|
24
|
+
const { siteId, type, orderBy, limit, stage, mode, entryIds } = _nullishCoalesce(params, () => ( {}));
|
|
25
|
+
if (!siteId || !type) {
|
|
26
|
+
throw new Error("listPublishedEntries requires siteId and type params");
|
|
27
|
+
}
|
|
28
|
+
const parsedLimit = typeof limit === "string" ? Number.parseInt(limit, 10) : typeof limit === "number" ? limit : void 0;
|
|
29
|
+
const order = orderBy === "newest" || orderBy === "oldest" || orderBy === "title" || orderBy === "order" ? orderBy : void 0;
|
|
30
|
+
let parsedEntryIds;
|
|
31
|
+
if (mode === "manual" && Array.isArray(entryIds)) {
|
|
32
|
+
parsedEntryIds = entryIds.map((item) => {
|
|
33
|
+
if (typeof item === "object" && item !== null && "entryId" in item) {
|
|
34
|
+
return item.entryId;
|
|
35
|
+
}
|
|
36
|
+
if (typeof item === "string") {
|
|
37
|
+
return item;
|
|
38
|
+
}
|
|
39
|
+
return null;
|
|
40
|
+
}).filter((id) => id !== null);
|
|
41
|
+
}
|
|
42
|
+
return await client.getEntries({
|
|
43
|
+
siteId,
|
|
44
|
+
contentType: type,
|
|
45
|
+
limit: parsedLimit,
|
|
46
|
+
order,
|
|
47
|
+
preview: stage === "preview",
|
|
48
|
+
// Manual mode - pass entry IDs for hydration
|
|
49
|
+
mode: mode === "manual" ? "manual" : void 0,
|
|
50
|
+
entryIds: parsedEntryIds
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
case "getPublishedEntryPreview": {
|
|
54
|
+
const { siteId, type, slug } = _nullishCoalesce(params, () => ( {}));
|
|
55
|
+
if (!siteId || !type || !slug) {
|
|
56
|
+
throw new Error("getPublishedEntryPreview requires siteId, type, and slug params");
|
|
57
|
+
}
|
|
58
|
+
return await client.getEntry({ siteId, contentType: type, slug });
|
|
59
|
+
}
|
|
60
|
+
case "listPublicEvents": {
|
|
61
|
+
const { siteId, limit, from, to, stage } = _nullishCoalesce(params, () => ( {}));
|
|
62
|
+
if (!siteId) {
|
|
63
|
+
throw new Error("listPublicEvents requires siteId param");
|
|
64
|
+
}
|
|
65
|
+
const parsedLimit = typeof limit === "string" ? Number.parseInt(limit, 10) : typeof limit === "number" ? limit : void 0;
|
|
66
|
+
return await client.listPublicEvents({ siteId, limit: parsedLimit, from, to, stage });
|
|
67
|
+
}
|
|
68
|
+
case "getPublicFormById": {
|
|
69
|
+
const { formId } = _nullishCoalesce(params, () => ( {}));
|
|
70
|
+
if (!formId) {
|
|
71
|
+
throw new Error("getPublicFormById requires formId param");
|
|
72
|
+
}
|
|
73
|
+
return await client.getPublicFormById({ formId });
|
|
74
|
+
}
|
|
75
|
+
case "getPublicBookingServices": {
|
|
76
|
+
const { siteId, ids } = _nullishCoalesce(params, () => ( {}));
|
|
77
|
+
if (!siteId) {
|
|
78
|
+
throw new Error("getPublicBookingServices requires siteId param");
|
|
79
|
+
}
|
|
80
|
+
return await client.getPublicBookingServices({ siteId, ids });
|
|
81
|
+
}
|
|
82
|
+
default: {
|
|
83
|
+
const _exhaustive = endpoint;
|
|
84
|
+
throw new Error(`Unhandled endpoint: ${_exhaustive}`);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
},
|
|
88
|
+
isValidEndpoint: isSupportedEndpoint,
|
|
89
|
+
onError: (error, { block, loader }) => {
|
|
90
|
+
console.warn("[prefetchBlockData] failed to prefetch block data", {
|
|
91
|
+
block,
|
|
92
|
+
loader,
|
|
93
|
+
error
|
|
94
|
+
});
|
|
95
|
+
},
|
|
96
|
+
// Provide custom block loader lookup for SDK custom blocks
|
|
97
|
+
getCustomBlockLoaders: (blockKind) => {
|
|
98
|
+
const customBlock = customBlockMap.get(blockKind);
|
|
99
|
+
if (!_optionalChain([customBlock, 'optionalAccess', _ => _.dataLoaders])) return void 0;
|
|
100
|
+
const loaders = {};
|
|
101
|
+
for (const [key, loader] of Object.entries(customBlock.dataLoaders)) {
|
|
102
|
+
loaders[key] = {
|
|
103
|
+
endpoint: loader.endpoint,
|
|
104
|
+
params: loader.params,
|
|
105
|
+
mode: loader.mode
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
return loaders;
|
|
109
|
+
}
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
function isSupportedEndpoint(endpoint) {
|
|
113
|
+
return SUPPORTED_LOADER_ENDPOINTS.includes(endpoint);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
exports.SUPPORTED_LOADER_ENDPOINTS = SUPPORTED_LOADER_ENDPOINTS; exports.prefetchBlockData = prefetchBlockData;
|
|
120
|
+
//# sourceMappingURL=chunk-QQ6U4QX6.js.map
|