@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.
Files changed (157) hide show
  1. package/README.md +1892 -0
  2. package/dist/cli/index.js +327 -0
  3. package/dist/cli/index.js.map +1 -0
  4. package/dist/client/analytics.d.mts +103 -0
  5. package/dist/client/analytics.d.ts +103 -0
  6. package/dist/client/analytics.js +197 -0
  7. package/dist/client/analytics.js.map +1 -0
  8. package/dist/client/analytics.mjs +169 -0
  9. package/dist/client/analytics.mjs.map +1 -0
  10. package/dist/client/bookings.d.mts +89 -0
  11. package/dist/client/bookings.d.ts +89 -0
  12. package/dist/client/bookings.js +34 -0
  13. package/dist/client/bookings.js.map +1 -0
  14. package/dist/client/bookings.mjs +11 -0
  15. package/dist/client/bookings.mjs.map +1 -0
  16. package/dist/client/client.d.mts +195 -0
  17. package/dist/client/client.d.ts +195 -0
  18. package/dist/client/client.js +606 -0
  19. package/dist/client/client.js.map +1 -0
  20. package/dist/client/client.mjs +572 -0
  21. package/dist/client/client.mjs.map +1 -0
  22. package/dist/client/hooks.d.mts +71 -0
  23. package/dist/client/hooks.d.ts +71 -0
  24. package/dist/client/hooks.js +264 -0
  25. package/dist/client/hooks.js.map +1 -0
  26. package/dist/client/hooks.mjs +235 -0
  27. package/dist/client/hooks.mjs.map +1 -0
  28. package/dist/client/rendering/client.d.mts +1 -0
  29. package/dist/client/rendering/client.d.ts +1 -0
  30. package/dist/client/rendering/client.js +33 -0
  31. package/dist/client/rendering/client.js.map +1 -0
  32. package/dist/client/rendering/client.mjs +8 -0
  33. package/dist/client/rendering/client.mjs.map +1 -0
  34. package/dist/client/usePage-BvKAa3Zw.d.mts +366 -0
  35. package/dist/client/usePage-BvKAa3Zw.d.ts +366 -0
  36. package/dist/server/chunk-2RW5HAQQ.mjs +86 -0
  37. package/dist/server/chunk-2RW5HAQQ.mjs.map +1 -0
  38. package/dist/server/chunk-3KKZVGH4.mjs +179 -0
  39. package/dist/server/chunk-3KKZVGH4.mjs.map +1 -0
  40. package/dist/server/chunk-4Z3GPTCS.js +179 -0
  41. package/dist/server/chunk-4Z3GPTCS.js.map +1 -0
  42. package/dist/server/chunk-4Z5FBFRL.mjs +211 -0
  43. package/dist/server/chunk-4Z5FBFRL.mjs.map +1 -0
  44. package/dist/server/chunk-ADREPXFU.js +86 -0
  45. package/dist/server/chunk-ADREPXFU.js.map +1 -0
  46. package/dist/server/chunk-F472SMKX.js +140 -0
  47. package/dist/server/chunk-F472SMKX.js.map +1 -0
  48. package/dist/server/chunk-GWBMJPLH.mjs +57 -0
  49. package/dist/server/chunk-GWBMJPLH.mjs.map +1 -0
  50. package/dist/server/chunk-JB4LIEFS.js +85 -0
  51. package/dist/server/chunk-JB4LIEFS.js.map +1 -0
  52. package/dist/server/chunk-PEAXKTDU.mjs +140 -0
  53. package/dist/server/chunk-PEAXKTDU.mjs.map +1 -0
  54. package/dist/server/chunk-QQ6U4QX6.js +120 -0
  55. package/dist/server/chunk-QQ6U4QX6.js.map +1 -0
  56. package/dist/server/chunk-R5YGLRUG.mjs +122 -0
  57. package/dist/server/chunk-R5YGLRUG.mjs.map +1 -0
  58. package/dist/server/chunk-SW7LE4M3.js +211 -0
  59. package/dist/server/chunk-SW7LE4M3.js.map +1 -0
  60. package/dist/server/chunk-W3K7LVPS.mjs +120 -0
  61. package/dist/server/chunk-W3K7LVPS.mjs.map +1 -0
  62. package/dist/server/chunk-WKG57P2H.mjs +85 -0
  63. package/dist/server/chunk-WKG57P2H.mjs.map +1 -0
  64. package/dist/server/chunk-YHEZMVTS.js +122 -0
  65. package/dist/server/chunk-YHEZMVTS.js.map +1 -0
  66. package/dist/server/chunk-YXDDFG3N.js +57 -0
  67. package/dist/server/chunk-YXDDFG3N.js.map +1 -0
  68. package/dist/server/components.d.mts +49 -0
  69. package/dist/server/components.d.ts +49 -0
  70. package/dist/server/components.js +22 -0
  71. package/dist/server/components.js.map +1 -0
  72. package/dist/server/components.mjs +22 -0
  73. package/dist/server/components.mjs.map +1 -0
  74. package/dist/server/config-validation.d.mts +300 -0
  75. package/dist/server/config-validation.d.ts +300 -0
  76. package/dist/server/config-validation.js +50 -0
  77. package/dist/server/config-validation.js.map +1 -0
  78. package/dist/server/config-validation.mjs +50 -0
  79. package/dist/server/config-validation.mjs.map +1 -0
  80. package/dist/server/config.d.mts +38 -0
  81. package/dist/server/config.d.ts +38 -0
  82. package/dist/server/config.js +44 -0
  83. package/dist/server/config.js.map +1 -0
  84. package/dist/server/config.mjs +44 -0
  85. package/dist/server/config.mjs.map +1 -0
  86. package/dist/server/data.d.mts +108 -0
  87. package/dist/server/data.d.ts +108 -0
  88. package/dist/server/data.js +15 -0
  89. package/dist/server/data.js.map +1 -0
  90. package/dist/server/data.mjs +15 -0
  91. package/dist/server/data.mjs.map +1 -0
  92. package/dist/server/index-B0yI_V6Z.d.mts +18 -0
  93. package/dist/server/index-C6M0Wfjq.d.ts +18 -0
  94. package/dist/server/index.d.mts +5 -0
  95. package/dist/server/index.d.ts +5 -0
  96. package/dist/server/index.js +12 -0
  97. package/dist/server/index.js.map +1 -0
  98. package/dist/server/index.mjs +12 -0
  99. package/dist/server/index.mjs.map +1 -0
  100. package/dist/server/loadContent-CJcbYF3J.d.ts +152 -0
  101. package/dist/server/loadContent-zhlL4YSE.d.mts +152 -0
  102. package/dist/server/loadPage-BYmVMk0V.d.ts +216 -0
  103. package/dist/server/loadPage-CCf15nt8.d.mts +216 -0
  104. package/dist/server/loadPage-DVH3DW6E.js +9 -0
  105. package/dist/server/loadPage-DVH3DW6E.js.map +1 -0
  106. package/dist/server/loadPage-PHQZ6XQZ.mjs +9 -0
  107. package/dist/server/loadPage-PHQZ6XQZ.mjs.map +1 -0
  108. package/dist/server/metadata.d.mts +135 -0
  109. package/dist/server/metadata.d.ts +135 -0
  110. package/dist/server/metadata.js +68 -0
  111. package/dist/server/metadata.js.map +1 -0
  112. package/dist/server/metadata.mjs +68 -0
  113. package/dist/server/metadata.mjs.map +1 -0
  114. package/dist/server/rendering/server.d.mts +83 -0
  115. package/dist/server/rendering/server.d.ts +83 -0
  116. package/dist/server/rendering/server.js +14 -0
  117. package/dist/server/rendering/server.js.map +1 -0
  118. package/dist/server/rendering/server.mjs +14 -0
  119. package/dist/server/rendering/server.mjs.map +1 -0
  120. package/dist/server/rendering.d.mts +12 -0
  121. package/dist/server/rendering.d.ts +12 -0
  122. package/dist/server/rendering.js +40 -0
  123. package/dist/server/rendering.js.map +1 -0
  124. package/dist/server/rendering.mjs +40 -0
  125. package/dist/server/rendering.mjs.map +1 -0
  126. package/dist/server/routing.d.mts +115 -0
  127. package/dist/server/routing.d.ts +115 -0
  128. package/dist/server/routing.js +57 -0
  129. package/dist/server/routing.js.map +1 -0
  130. package/dist/server/routing.mjs +57 -0
  131. package/dist/server/routing.mjs.map +1 -0
  132. package/dist/server/server.d.mts +9 -0
  133. package/dist/server/server.d.ts +9 -0
  134. package/dist/server/server.js +21 -0
  135. package/dist/server/server.js.map +1 -0
  136. package/dist/server/server.mjs +21 -0
  137. package/dist/server/server.mjs.map +1 -0
  138. package/dist/server/theme-bridge.d.mts +232 -0
  139. package/dist/server/theme-bridge.d.ts +232 -0
  140. package/dist/server/theme-bridge.js +231 -0
  141. package/dist/server/theme-bridge.js.map +1 -0
  142. package/dist/server/theme-bridge.mjs +231 -0
  143. package/dist/server/theme-bridge.mjs.map +1 -0
  144. package/dist/server/theme.d.mts +40 -0
  145. package/dist/server/theme.d.ts +40 -0
  146. package/dist/server/theme.js +17 -0
  147. package/dist/server/theme.js.map +1 -0
  148. package/dist/server/theme.mjs +17 -0
  149. package/dist/server/theme.mjs.map +1 -0
  150. package/dist/server/types-BCeqWtI2.d.mts +333 -0
  151. package/dist/server/types-BCeqWtI2.d.ts +333 -0
  152. package/dist/server/types-Bbo01M7P.d.mts +76 -0
  153. package/dist/server/types-Bbo01M7P.d.ts +76 -0
  154. package/dist/server/types-C6gmRHLe.d.mts +150 -0
  155. package/dist/server/types-C6gmRHLe.d.ts +150 -0
  156. package/package.json +147 -0
  157. package/src/styles/index.css +10 -0
@@ -0,0 +1,366 @@
1
+ import { APIEndpoints } from '@riverbankcms/api';
2
+ import * as react_jsx_runtime from 'react/jsx-runtime';
3
+ import { PageOutline, Theme, ThemeTokens, RouteMap, OccurrenceContextData, PageRenderer, BlockOverrides } from '@riverbankcms/blocks';
4
+ import { ResolvedBlockData } from '@riverbankcms/blocks/system/data';
5
+
6
+ type SiteResponse = NonNullable<APIEndpoints['getSite']['response']>;
7
+ type PageResponse = NonNullable<APIEndpoints['getContentByPath']['response']>;
8
+ type EntriesResponse = NonNullable<APIEndpoints['listPublishedEntries']['response']>;
9
+ type EntryResponse = NonNullable<APIEndpoints['getPublishedEntryPreview']['response']>;
10
+ type PublicFormResponse = NonNullable<APIEndpoints['getPublicFormById']['response']>;
11
+ type PublicBookingServicesResponse = NonNullable<APIEndpoints['getPublicBookingServices']['response']>;
12
+ type PublicEventsResponse = NonNullable<APIEndpoints['listPublicEvents']['response']>;
13
+ /**
14
+ * Configuration for creating a Riverbank CMS client
15
+ */
16
+ interface RiverbankClientConfig {
17
+ /**
18
+ * API key for authentication (format: bld_live_... or bld_test_...)
19
+ */
20
+ apiKey: string;
21
+ /**
22
+ * Base URL for the Riverbank CMS API (required; e.g. https://dashboard.example.com/api)
23
+ */
24
+ baseUrl: string;
25
+ /**
26
+ * Cache configuration
27
+ */
28
+ cache?: {
29
+ /**
30
+ * Enable caching (default: true)
31
+ */
32
+ enabled?: boolean;
33
+ /**
34
+ * Time-to-live for cached responses in seconds
35
+ * @default 300 (5 minutes)
36
+ */
37
+ ttl?: number;
38
+ /**
39
+ * Maximum number of cached responses
40
+ * @default 100
41
+ */
42
+ maxSize?: number;
43
+ };
44
+ }
45
+ /**
46
+ * Riverbank CMS client interface
47
+ */
48
+ interface RiverbankClient {
49
+ /**
50
+ * Fetch site data by slug, domain, or ID
51
+ */
52
+ getSite(params: {
53
+ slug?: string;
54
+ domain?: string;
55
+ id?: string;
56
+ }): Promise<SiteResponse>;
57
+ /**
58
+ * Fetch a specific page by path
59
+ *
60
+ * @param params.preview - If true, returns draft content instead of published content (requires API key with site access)
61
+ */
62
+ getPage(params: {
63
+ siteId: string;
64
+ path: string;
65
+ preview?: boolean;
66
+ }): Promise<PageResponse>;
67
+ /**
68
+ * Fetch published content entries with optional pagination, sorting, and preview mode
69
+ *
70
+ * @param params.siteId - The site ID
71
+ * @param params.contentType - The content type key (e.g., 'blog-post', 'product')
72
+ * @param params.limit - Maximum number of entries to return
73
+ * @param params.order - Sort order: 'newest' (published_at desc) or 'oldest' (published_at asc)
74
+ * @param params.preview - If true, includes draft entries (requires API key with site access)
75
+ * @param params.mode - 'query' for automatic fetching, 'manual' for specific entry IDs
76
+ * @param params.entryIds - Array of entry IDs to fetch (only used when mode is 'manual')
77
+ *
78
+ * @example
79
+ * ```ts
80
+ * // Fetch latest 10 blog posts
81
+ * const entries = await client.getEntries({
82
+ * siteId: 'site-id',
83
+ * contentType: 'blog-post',
84
+ * limit: 10,
85
+ * order: 'newest',
86
+ * });
87
+ *
88
+ * // Fetch draft entries for preview
89
+ * const drafts = await client.getEntries({
90
+ * siteId: 'site-id',
91
+ * contentType: 'blog-post',
92
+ * preview: true,
93
+ * });
94
+ *
95
+ * // Fetch specific entries by ID (manual mode)
96
+ * const specific = await client.getEntries({
97
+ * siteId: 'site-id',
98
+ * contentType: 'blog-post',
99
+ * mode: 'manual',
100
+ * entryIds: ['uuid-1', 'uuid-2'],
101
+ * });
102
+ * ```
103
+ */
104
+ getEntries(params: {
105
+ siteId: string;
106
+ contentType: string;
107
+ limit?: number;
108
+ /** Sort order for entries. 'order' uses custom ordering (default behavior). */
109
+ order?: 'newest' | 'oldest' | 'title' | 'order';
110
+ preview?: boolean;
111
+ /** Selection mode: 'query' for automatic, 'manual' for specific entry IDs */
112
+ mode?: 'query' | 'manual';
113
+ /** Entry IDs to fetch when mode is 'manual' */
114
+ entryIds?: string[];
115
+ }): Promise<EntriesResponse>;
116
+ /**
117
+ * Fetch a specific content entry by slug
118
+ */
119
+ getEntry(params: {
120
+ siteId: string;
121
+ contentType: string;
122
+ slug: string;
123
+ }): Promise<EntryResponse>;
124
+ /**
125
+ * Fetch a public form definition by ID
126
+ */
127
+ getPublicFormById(params: {
128
+ formId: string;
129
+ }): Promise<PublicFormResponse>;
130
+ /**
131
+ * Fetch public booking services for a site
132
+ */
133
+ getPublicBookingServices(params: {
134
+ siteId: string;
135
+ ids?: string;
136
+ }): Promise<PublicBookingServicesResponse>;
137
+ /**
138
+ * List public events for a site (optionally time-filtered)
139
+ */
140
+ listPublicEvents(params: {
141
+ siteId: string;
142
+ limit?: number;
143
+ from?: string;
144
+ to?: string;
145
+ stage?: string;
146
+ }): Promise<PublicEventsResponse>;
147
+ /**
148
+ * Force clear the cache
149
+ */
150
+ clearCache(): void;
151
+ }
152
+
153
+ /**
154
+ * Server-side helper to fetch all data needed for <Page> component.
155
+ *
156
+ * Use this in server components, getServerSideProps, or API routes.
157
+ */
158
+
159
+ /**
160
+ * SDK config from API response (without siteId which is stripped at storage).
161
+ * This is the runtime representation - for defining configs, use RiverbankSiteConfig.
162
+ */
163
+ type RuntimeSdkConfig = NonNullable<SiteResponse['sdkConfig']>;
164
+
165
+ type PageProps = {
166
+ page: PageOutline;
167
+ theme: Theme;
168
+ siteId: string;
169
+ themeTokens?: ThemeTokens;
170
+ resolvedData?: ResolvedBlockData;
171
+ routeMap?: RouteMap;
172
+ /**
173
+ * SDK site configuration containing theme palette overrides.
174
+ * When provided, the SDK palette tokens are merged into the theme tokens,
175
+ * allowing blocks to use SDK-defined color tokens for section backgrounds.
176
+ */
177
+ sdkConfig?: RuntimeSdkConfig | null;
178
+ /**
179
+ * Additional context data for content entry pages.
180
+ * Used to pass occurrence context and content entry data to blocks.
181
+ */
182
+ dataContext?: {
183
+ /** Occurrence context for event pages (from URL like /events/yoga/2025-01-15) */
184
+ occurrenceContext?: OccurrenceContextData | null;
185
+ /** Content entry data for template pages */
186
+ contentEntry?: Record<string, unknown> | null;
187
+ };
188
+ wrapBlock?: (blockId: string, rendered: React.ReactNode) => React.ReactNode;
189
+ registry?: Parameters<typeof PageRenderer>[0]['registry'];
190
+ usePlaceholders?: boolean;
191
+ /**
192
+ * Custom components to override default block rendering.
193
+ * Keys can be full block kind ("block.hero") or short form ("hero").
194
+ *
195
+ * This is SSR-safe - no context or hooks required.
196
+ *
197
+ * @example
198
+ * ```tsx
199
+ * <Page
200
+ * {...pageData}
201
+ * blockOverrides={{
202
+ * 'hero': MyCustomHero,
203
+ * 'block.testimonials': MyCustomTestimonials,
204
+ * }}
205
+ * />
206
+ * ```
207
+ */
208
+ blockOverrides?: BlockOverrides;
209
+ };
210
+ /**
211
+ * Pure renderer for Riverbank CMS pages.
212
+ *
213
+ * This component expects all data to be provided via props.
214
+ * For data fetching, use:
215
+ * - Server-side: `await loadPage({ client, siteId, path })`
216
+ * - Client-side: `usePage({ client, siteId, path })`
217
+ *
218
+ * @example Server-side (Next.js App Router)
219
+ * ```tsx
220
+ * import { createRiverbankClient } from '@riverbankcms/sdk';
221
+ * import { loadPage, Page } from '@riverbankcms/sdk/rendering';
222
+ *
223
+ * const client = createRiverbankClient({ apiKey, baseUrl });
224
+ *
225
+ * export default async function PageRoute({ params }) {
226
+ * const pageData = await loadPage({
227
+ * client,
228
+ * siteId: 'site-id',
229
+ * path: `/${params.slug}`,
230
+ * });
231
+ *
232
+ * return <Page {...pageData} />;
233
+ * }
234
+ * ```
235
+ *
236
+ * @example Client-side
237
+ * ```tsx
238
+ * import { createRiverbankClient } from '@riverbankcms/sdk';
239
+ * import { usePage, Page } from '@riverbankcms/sdk/rendering';
240
+ *
241
+ * const client = createRiverbankClient({ apiKey, baseUrl });
242
+ *
243
+ * function MyPage({ path }) {
244
+ * const pageData = usePage({ client, siteId: 'site-id', path });
245
+ *
246
+ * if (pageData.loading) return <LoadingState />;
247
+ * if (pageData.error) return <ErrorState error={pageData.error} />;
248
+ * if (!pageData.page) return <NotFound />;
249
+ *
250
+ * return <Page {...pageData} />;
251
+ * }
252
+ * ```
253
+ */
254
+ declare function Page({ page, theme, themeTokens: providedTokens, siteId, resolvedData, routeMap, wrapBlock, registry, usePlaceholders, blockOverrides, sdkConfig, dataContext, }: PageProps): react_jsx_runtime.JSX.Element;
255
+
256
+ /**
257
+ * Client-side React hook to fetch page data.
258
+ *
259
+ * Use this in client components for dynamic page loading.
260
+ */
261
+
262
+ type UsePageParams = {
263
+ client: RiverbankClient;
264
+ siteId: string;
265
+ path: string;
266
+ pageId?: string;
267
+ /**
268
+ * If true, fetches draft/unpublished content instead of published content.
269
+ * This affects both the page structure and block data loaders.
270
+ * Requires API key with site access.
271
+ *
272
+ * @default false
273
+ */
274
+ preview?: boolean;
275
+ };
276
+ type UsePageResult = {
277
+ loading: true;
278
+ error: null;
279
+ page: null;
280
+ theme: null;
281
+ siteId: string;
282
+ resolvedData: null;
283
+ sdkConfig: null;
284
+ } | {
285
+ loading: false;
286
+ error: Error;
287
+ page: null;
288
+ theme: null;
289
+ siteId: string;
290
+ resolvedData: null;
291
+ sdkConfig: null;
292
+ } | {
293
+ loading: false;
294
+ error: null;
295
+ sdkConfig: RuntimeSdkConfig | null;
296
+ } & Omit<PageProps, 'registry' | 'wrapBlock' | 'usePlaceholders' | 'sdkConfig'>;
297
+ /**
298
+ * Client-side React hook to fetch all data needed for <Page> component.
299
+ *
300
+ * Fetches site data, page data, and prefetches block data loaders.
301
+ * Returns loading and error states for proper UI handling.
302
+ *
303
+ * IMPORTANT: The client object should be stable across renders to avoid
304
+ * unnecessary re-fetches. Create it outside your component or use useMemo:
305
+ *
306
+ * ```tsx
307
+ * // ✅ Good - stable reference
308
+ * const client = useMemo(
309
+ * () => createRiverbankClient({ apiKey, baseUrl }),
310
+ * [apiKey, baseUrl]
311
+ * );
312
+ *
313
+ * // ❌ Bad - new client on every render (causes infinite loops)
314
+ * const client = createRiverbankClient({ apiKey, baseUrl });
315
+ * ```
316
+ *
317
+ * @example Basic usage
318
+ * ```tsx
319
+ * import { createRiverbankClient } from '@riverbankcms/sdk';
320
+ * import { usePage, Page } from '@riverbankcms/sdk/rendering';
321
+ *
322
+ * const client = createRiverbankClient({
323
+ * apiKey: process.env.NEXT_PUBLIC_RIVERBANK_API_KEY!,
324
+ * baseUrl: process.env.NEXT_PUBLIC_DASHBOARD_URL + '/api',
325
+ * });
326
+ *
327
+ * function MyPage({ path }: { path: string }) {
328
+ * const pageData = usePage({ client, siteId: 'site-123', path });
329
+ *
330
+ * if (pageData.loading) {
331
+ * return <div>Loading...</div>;
332
+ * }
333
+ *
334
+ * if (pageData.error) {
335
+ * return <div>Error: {pageData.error.message}</div>;
336
+ * }
337
+ *
338
+ * return <Page {...pageData} />;
339
+ * }
340
+ * ```
341
+ *
342
+ * @example With custom loading/error states
343
+ * ```tsx
344
+ * function MyPage({ path }: { path: string }) {
345
+ * const pageData = usePage({ client, siteId: 'site-123', path });
346
+ *
347
+ * if (pageData.loading) {
348
+ * return <Skeleton />;
349
+ * }
350
+ *
351
+ * if (pageData.error) {
352
+ * return (
353
+ * <ErrorBoundary
354
+ * error={pageData.error}
355
+ * onRetry={() => window.location.reload()}
356
+ * />
357
+ * );
358
+ * }
359
+ *
360
+ * return <Page {...pageData} />;
361
+ * }
362
+ * ```
363
+ */
364
+ declare function usePage(params: UsePageParams): UsePageResult;
365
+
366
+ export { type PageResponse as P, type RiverbankClient as R, type SiteResponse as S, type UsePageParams as U, type UsePageResult as a, type PageProps as b, type RiverbankClientConfig as c, Page as d, usePage as u };
@@ -0,0 +1,86 @@
1
+ import {
2
+ executeCodeLoaders,
3
+ mergeLoaderResults
4
+ } from "./chunk-GWBMJPLH.mjs";
5
+ import {
6
+ prefetchBlockData
7
+ } from "./chunk-W3K7LVPS.mjs";
8
+
9
+ // src/rendering/helpers/loadPage.ts
10
+ async function loadPage(params) {
11
+ const { client, siteId, path, pageId, preview = false, dataLoaderOverrides } = params;
12
+ const [site, pageResponse] = await Promise.all([
13
+ client.getSite({ id: siteId }),
14
+ client.getPage({ siteId, path, preview })
15
+ ]);
16
+ if ("entry" in pageResponse) {
17
+ throw new Error(
18
+ "This path resolves to a content entry, not a page. Use loadContent() instead, which handles both pages and entries. For entries, loadContent() returns the raw entry data for custom rendering."
19
+ );
20
+ }
21
+ const { page: pageData } = pageResponse;
22
+ const blocks = pageData.blocks.map((block) => {
23
+ if (!block || typeof block !== "object") {
24
+ throw new Error("Invalid block format in API response");
25
+ }
26
+ if (typeof block.id !== "string" && block.id !== null) {
27
+ throw new Error(`Invalid block id: expected string or null, got ${typeof block.id}`);
28
+ }
29
+ if (typeof block.kind !== "string") {
30
+ throw new Error(`Invalid block kind: expected string, got ${typeof block.kind}`);
31
+ }
32
+ if (typeof block.purpose !== "string") {
33
+ throw new Error(`Invalid block purpose: expected string, got ${typeof block.purpose}`);
34
+ }
35
+ const typedBlock = block;
36
+ return {
37
+ id: typedBlock.id,
38
+ kind: typedBlock.kind,
39
+ purpose: typedBlock.purpose,
40
+ // Include content for PageRenderer's getRenderableContent()
41
+ content: typedBlock.content ?? {},
42
+ // Include draftContent if available (for preview mode)
43
+ draftContent: typedBlock.draftContent?.data ?? null
44
+ };
45
+ });
46
+ const pageOutline = {
47
+ name: pageData.name,
48
+ path: pageData.path,
49
+ purpose: pageData.purpose,
50
+ blocks
51
+ };
52
+ const prefetchContext = {
53
+ siteId,
54
+ pageId: pageId ?? pageData.id,
55
+ previewStage: preview ? "preview" : "published"
56
+ };
57
+ const configData = await prefetchBlockData(
58
+ pageOutline,
59
+ prefetchContext,
60
+ client,
61
+ {
62
+ // Pass custom blocks so their config-based loaders are discovered
63
+ customBlocks: site.sdkConfig?.customBlocks
64
+ }
65
+ );
66
+ let resolvedData = configData;
67
+ if (dataLoaderOverrides && Object.keys(dataLoaderOverrides).length > 0) {
68
+ const codeData = await executeCodeLoaders(pageOutline, prefetchContext, dataLoaderOverrides);
69
+ resolvedData = mergeLoaderResults(configData, codeData);
70
+ }
71
+ return {
72
+ page: pageOutline,
73
+ theme: site.theme,
74
+ sdkConfig: site.sdkConfig ?? null,
75
+ siteId,
76
+ resolvedData
77
+ // Note: routeMap is optional and can be built from site data if needed for internal links.
78
+ // Consumers can construct it from site.pages and pass explicitly via Page component props.
79
+ // Example: const routeMap = site.pages.reduce((map, p) => ({ ...map, [p.id]: p.path }), {});
80
+ };
81
+ }
82
+
83
+ export {
84
+ loadPage
85
+ };
86
+ //# sourceMappingURL=chunk-2RW5HAQQ.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/rendering/helpers/loadPage.ts"],"sourcesContent":["/**\n * Server-side helper to fetch all data needed for <Page> component.\n *\n * Use this in server components, getServerSideProps, or API routes.\n */\n\nimport type { RiverbankClient, SiteResponse } from '../../client/types';\nimport type { PageProps } from '../components/Page';\nimport { prefetchBlockData } from '../../data/prefetchBlockData';\nimport { executeCodeLoaders, mergeLoaderResults } from '../../data/executeCodeLoaders';\nimport type { DataLoaderOverrides } from '../../data/types';\n\n/**\n * SDK config from API response (without siteId which is stripped at storage).\n * This is the runtime representation - for defining configs, use RiverbankSiteConfig.\n */\nexport type RuntimeSdkConfig = NonNullable<SiteResponse['sdkConfig']>;\n\nexport type LoadPageParams = {\n client: RiverbankClient;\n siteId: string;\n path: string;\n pageId?: string;\n /**\n * If true, fetches draft/unpublished content instead of published content.\n * This affects both the page structure and block data loaders.\n * Requires API key with site access.\n *\n * @default false\n */\n preview?: boolean;\n /**\n * Code-based data loaders for custom blocks.\n *\n * Use this to fetch data from external APIs (not just whitelisted CMS endpoints).\n * Keys are block kinds (e.g., 'custom.featured-products').\n *\n * Config-based loaders (defined in riverbank.config.ts) run first.\n * Code loaders run second and take precedence on key conflicts.\n *\n * @example\n * ```typescript\n * const pageData = await loadPage({\n * client,\n * siteId: 'site-123',\n * path: '/',\n * dataLoaderOverrides: {\n * 'custom.featured-products': {\n * products: async (ctx) => {\n * const res = await fetch(`https://api.shop.com/products?category=${ctx.content.categoryId}`);\n * return res.json();\n * },\n * },\n * },\n * });\n * ```\n */\n dataLoaderOverrides?: DataLoaderOverrides;\n};\n\nexport type LoadPageResult = Omit<PageProps, 'registry' | 'wrapBlock' | 'usePlaceholders' | 'blockOverrides'> & {\n /**\n * SDK site configuration, if available.\n * Contains SDK-defined theme palette, section backgrounds, and style options.\n */\n sdkConfig: RuntimeSdkConfig | null;\n};\n\n/**\n * Server-side helper to fetch all data needed for <Page> component.\n *\n * Fetches site data, page data, and prefetches block data loaders in parallel.\n *\n * @example Next.js App Router (published content)\n * ```tsx\n * import { createRiverbankClient } from '@riverbankcms/sdk';\n * import { loadPage, Page } from '@riverbankcms/sdk/rendering';\n *\n * const client = createRiverbankClient({\n * apiKey: process.env.RIVERBANK_API_KEY!,\n * baseUrl: process.env.NEXT_PUBLIC_DASHBOARD_URL + '/api',\n * });\n *\n * export default async function PageRoute({ params }) {\n * const pageData = await loadPage({\n * client,\n * siteId: 'site-123',\n * path: `/${params.slug || ''}`,\n * });\n *\n * return <Page {...pageData} />;\n * }\n * ```\n *\n * @example Next.js App Router (preview/draft content)\n * ```tsx\n * export default async function PreviewRoute({ params, searchParams }) {\n * const pageData = await loadPage({\n * client,\n * siteId: searchParams.siteId,\n * path: `/${params.slug || ''}`,\n * preview: true, // Fetch draft content\n * });\n *\n * return <Page {...pageData} />;\n * }\n * ```\n *\n * @example Next.js Pages Router (getServerSideProps)\n * ```tsx\n * export async function getServerSideProps({ params }) {\n * const pageData = await loadPage({\n * client,\n * siteId: 'site-123',\n * path: `/${params.slug || ''}`,\n * });\n *\n * return { props: pageData };\n * }\n *\n * export default function PageRoute(props) {\n * return <Page {...props} />;\n * }\n * ```\n */\nexport async function loadPage(params: LoadPageParams): Promise<LoadPageResult> {\n const { client, siteId, path, pageId, preview = false, dataLoaderOverrides } = params;\n\n // Fetch site and page data in parallel\n const [site, pageResponse] = await Promise.all([\n client.getSite({ id: siteId }),\n client.getPage({ siteId, path, preview }),\n ]);\n\n // Extract page data (getContentByPath can return page or entry)\n if ('entry' in pageResponse) {\n throw new Error(\n 'This path resolves to a content entry, not a page. ' +\n 'Use loadContent() instead, which handles both pages and entries. ' +\n 'For entries, loadContent() returns the raw entry data for custom rendering.'\n );\n }\n\n const { page: pageData } = pageResponse;\n\n // Convert API response blocks to PageOutline format with content\n // API returns blocks with full content - PageRenderer needs the content field for rendering\n const blocks = pageData.blocks.map((block) => {\n if (!block || typeof block !== 'object') {\n throw new Error('Invalid block format in API response');\n }\n if (typeof block.id !== 'string' && block.id !== null) {\n throw new Error(`Invalid block id: expected string or null, got ${typeof block.id}`);\n }\n if (typeof block.kind !== 'string') {\n throw new Error(`Invalid block kind: expected string, got ${typeof block.kind}`);\n }\n if (typeof block.purpose !== 'string') {\n throw new Error(`Invalid block purpose: expected string, got ${typeof block.purpose}`);\n }\n\n // Include content for rendering\n // API provides `content` (active content for the requested stage)\n // and optionally `draftContent` for preview mode\n const typedBlock = block as {\n id: string | null;\n kind: string;\n purpose: string;\n content?: Record<string, unknown>;\n draftContent?: { data: Record<string, unknown> } | null;\n };\n\n return {\n id: typedBlock.id,\n kind: typedBlock.kind,\n purpose: typedBlock.purpose,\n // Include content for PageRenderer's getRenderableContent()\n content: typedBlock.content ?? {},\n // Include draftContent if available (for preview mode)\n draftContent: typedBlock.draftContent?.data ?? null,\n };\n });\n\n const pageOutline = {\n name: pageData.name,\n path: pageData.path,\n purpose: pageData.purpose,\n blocks,\n };\n\n // Build prefetch context\n const prefetchContext: { siteId: string; pageId: string; previewStage: 'published' | 'preview' } = {\n siteId,\n pageId: pageId ?? pageData.id,\n previewStage: preview ? 'preview' : 'published',\n };\n\n // Prefetch block data loaders (config-based loaders for CMS endpoints)\n const configData = await prefetchBlockData(\n pageOutline,\n prefetchContext,\n client,\n {\n // Pass custom blocks so their config-based loaders are discovered\n customBlocks: site.sdkConfig?.customBlocks,\n }\n );\n\n // Execute code-based loaders (external APIs) and merge results\n let resolvedData = configData;\n if (dataLoaderOverrides && Object.keys(dataLoaderOverrides).length > 0) {\n const codeData = await executeCodeLoaders(pageOutline, prefetchContext, dataLoaderOverrides);\n resolvedData = mergeLoaderResults(configData, codeData);\n }\n\n return {\n page: pageOutline,\n theme: site.theme,\n sdkConfig: site.sdkConfig ?? null,\n siteId,\n resolvedData,\n // Note: routeMap is optional and can be built from site data if needed for internal links.\n // Consumers can construct it from site.pages and pass explicitly via Page component props.\n // Example: const routeMap = site.pages.reduce((map, p) => ({ ...map, [p.id]: p.path }), {});\n };\n}\n"],"mappings":";;;;;;;;;AA6HA,eAAsB,SAAS,QAAiD;AAC9E,QAAM,EAAE,QAAQ,QAAQ,MAAM,QAAQ,UAAU,OAAO,oBAAoB,IAAI;AAG/E,QAAM,CAAC,MAAM,YAAY,IAAI,MAAM,QAAQ,IAAI;AAAA,IAC7C,OAAO,QAAQ,EAAE,IAAI,OAAO,CAAC;AAAA,IAC7B,OAAO,QAAQ,EAAE,QAAQ,MAAM,QAAQ,CAAC;AAAA,EAC1C,CAAC;AAGD,MAAI,WAAW,cAAc;AAC3B,UAAM,IAAI;AAAA,MACR;AAAA,IAGF;AAAA,EACF;AAEA,QAAM,EAAE,MAAM,SAAS,IAAI;AAI3B,QAAM,SAAS,SAAS,OAAO,IAAI,CAAC,UAAU;AAC5C,QAAI,CAAC,SAAS,OAAO,UAAU,UAAU;AACvC,YAAM,IAAI,MAAM,sCAAsC;AAAA,IACxD;AACA,QAAI,OAAO,MAAM,OAAO,YAAY,MAAM,OAAO,MAAM;AACrD,YAAM,IAAI,MAAM,kDAAkD,OAAO,MAAM,EAAE,EAAE;AAAA,IACrF;AACA,QAAI,OAAO,MAAM,SAAS,UAAU;AAClC,YAAM,IAAI,MAAM,4CAA4C,OAAO,MAAM,IAAI,EAAE;AAAA,IACjF;AACA,QAAI,OAAO,MAAM,YAAY,UAAU;AACrC,YAAM,IAAI,MAAM,+CAA+C,OAAO,MAAM,OAAO,EAAE;AAAA,IACvF;AAKA,UAAM,aAAa;AAQnB,WAAO;AAAA,MACL,IAAI,WAAW;AAAA,MACf,MAAM,WAAW;AAAA,MACjB,SAAS,WAAW;AAAA;AAAA,MAEpB,SAAS,WAAW,WAAW,CAAC;AAAA;AAAA,MAEhC,cAAc,WAAW,cAAc,QAAQ;AAAA,IACjD;AAAA,EACF,CAAC;AAED,QAAM,cAAc;AAAA,IAClB,MAAM,SAAS;AAAA,IACf,MAAM,SAAS;AAAA,IACf,SAAS,SAAS;AAAA,IAClB;AAAA,EACF;AAGA,QAAM,kBAA6F;AAAA,IACjG;AAAA,IACA,QAAQ,UAAU,SAAS;AAAA,IAC3B,cAAc,UAAU,YAAY;AAAA,EACtC;AAGA,QAAM,aAAa,MAAM;AAAA,IACvB;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA;AAAA,MAEE,cAAc,KAAK,WAAW;AAAA,IAChC;AAAA,EACF;AAGA,MAAI,eAAe;AACnB,MAAI,uBAAuB,OAAO,KAAK,mBAAmB,EAAE,SAAS,GAAG;AACtE,UAAM,WAAW,MAAM,mBAAmB,aAAa,iBAAiB,mBAAmB;AAC3F,mBAAe,mBAAmB,YAAY,QAAQ;AAAA,EACxD;AAEA,SAAO;AAAA,IACL,MAAM;AAAA,IACN,OAAO,KAAK;AAAA,IACZ,WAAW,KAAK,aAAa;AAAA,IAC7B;AAAA,IACA;AAAA;AAAA;AAAA;AAAA,EAIF;AACF;","names":[]}
@@ -0,0 +1,179 @@
1
+ // src/client/index.ts
2
+ import { createBearerAPIClient } from "@riverbankcms/api";
3
+
4
+ // src/client/cache.ts
5
+ var SimpleCache = class {
6
+ constructor(options = {}) {
7
+ this.cache = /* @__PURE__ */ new Map();
8
+ this.maxSize = options.maxSize ?? 100;
9
+ this.ttl = options.ttl ?? 3e5;
10
+ }
11
+ get(key) {
12
+ const entry = this.cache.get(key);
13
+ if (!entry) return void 0;
14
+ if (Date.now() > entry.expires) {
15
+ this.cache.delete(key);
16
+ return void 0;
17
+ }
18
+ return entry.value;
19
+ }
20
+ set(key, value) {
21
+ if (this.cache.size >= this.maxSize) {
22
+ const firstKey = this.cache.keys().next().value;
23
+ if (firstKey) {
24
+ this.cache.delete(firstKey);
25
+ }
26
+ }
27
+ this.cache.set(key, {
28
+ value,
29
+ expires: Date.now() + this.ttl
30
+ });
31
+ }
32
+ clear() {
33
+ this.cache.clear();
34
+ }
35
+ has(key) {
36
+ return this.get(key) !== void 0;
37
+ }
38
+ };
39
+
40
+ // src/client/index.ts
41
+ function createRiverbankClient(config) {
42
+ if (!config.baseUrl) {
43
+ throw new Error(
44
+ "baseUrl is required when creating a Builder client. Expected format: https://dashboard.example.com/api (must include /api path)"
45
+ );
46
+ }
47
+ if (!config.baseUrl.endsWith("/api")) {
48
+ throw new Error(
49
+ `baseUrl must end with '/api'. Received: ${config.baseUrl}. Expected format: https://dashboard.example.com/api`
50
+ );
51
+ }
52
+ const cacheEnabled = config.cache?.enabled ?? true;
53
+ const cacheTTL = (config.cache?.ttl ?? 300) * 1e3;
54
+ const cacheMaxSize = config.cache?.maxSize ?? 100;
55
+ const apiClient = createBearerAPIClient(config.apiKey, config.baseUrl);
56
+ const cache = new SimpleCache({
57
+ maxSize: cacheMaxSize,
58
+ ttl: cacheTTL
59
+ });
60
+ async function cachedFetch(cacheKey, fetcher, options) {
61
+ if (cacheEnabled && !options?.force) {
62
+ const cached = cache.get(cacheKey);
63
+ if (cached !== void 0) {
64
+ return cached;
65
+ }
66
+ }
67
+ const data = await fetcher();
68
+ if (cacheEnabled) {
69
+ cache.set(cacheKey, data);
70
+ }
71
+ return data;
72
+ }
73
+ return {
74
+ async getSite(params) {
75
+ const { slug, domain, id } = params;
76
+ if (!slug && !domain && !id) {
77
+ throw new Error(
78
+ `getSite() requires at least one identifier: slug, domain, or id. Received: ${JSON.stringify(params)}`
79
+ );
80
+ }
81
+ const cacheKey = `site:${slug || domain || id}`;
82
+ return cachedFetch(cacheKey, async () => {
83
+ const apiParams = {};
84
+ if (params.slug) apiParams.slug = params.slug;
85
+ if (params.domain) apiParams.domain = params.domain;
86
+ if (params.id) apiParams.id = params.id;
87
+ return await apiClient({ endpoint: "getSite", params: apiParams });
88
+ });
89
+ },
90
+ async getPage(params) {
91
+ const { siteId, path, preview = false } = params;
92
+ const cacheKey = `page:${siteId}:${path}:${preview}`;
93
+ return cachedFetch(cacheKey, async () => {
94
+ return await apiClient({ endpoint: "getContentByPath", params: { siteId }, body: { path, preview } });
95
+ });
96
+ },
97
+ async getEntries(params) {
98
+ const { siteId, contentType, limit, order, preview = false, mode, entryIds } = params;
99
+ const entryIdsCacheKey = mode === "manual" && entryIds?.length ? entryIds.join(",") : "";
100
+ const cacheKey = `entries:${siteId}:${contentType}:${limit ?? ""}:${order ?? ""}:${preview}:${mode ?? ""}:${entryIdsCacheKey}`;
101
+ return cachedFetch(cacheKey, async () => {
102
+ const apiParams = {
103
+ siteId,
104
+ type: contentType
105
+ };
106
+ if (typeof limit === "number") {
107
+ apiParams.limit = String(limit);
108
+ }
109
+ if (order === "newest") {
110
+ apiParams.order = "published_at.desc";
111
+ } else if (order === "oldest") {
112
+ apiParams.order = "published_at.asc";
113
+ } else if (order === "title") {
114
+ apiParams.order = "title.asc";
115
+ }
116
+ if (preview) {
117
+ apiParams.stage = "preview";
118
+ }
119
+ if (mode === "manual" && entryIds?.length) {
120
+ apiParams.mode = "manual";
121
+ apiParams.entryIds = JSON.stringify(entryIds);
122
+ }
123
+ return await apiClient({ endpoint: "listPublishedEntries", params: apiParams });
124
+ });
125
+ },
126
+ async getEntry(params) {
127
+ const { siteId, contentType, slug } = params;
128
+ const cacheKey = `entry:${siteId}:${contentType}:${slug}`;
129
+ return cachedFetch(cacheKey, async () => {
130
+ return await apiClient({ endpoint: "getPublishedEntryPreview", params: { siteId, type: contentType, slug } });
131
+ });
132
+ },
133
+ async getPublicFormById(params) {
134
+ const { formId } = params;
135
+ if (!formId) {
136
+ throw new Error("getPublicFormById() requires formId");
137
+ }
138
+ const cacheKey = `public-form:${formId}`;
139
+ return cachedFetch(cacheKey, async () => {
140
+ return await apiClient({ endpoint: "getPublicFormById", params: { formId } });
141
+ });
142
+ },
143
+ async getPublicBookingServices(params) {
144
+ const { siteId, ids } = params;
145
+ if (!siteId) {
146
+ throw new Error("getPublicBookingServices() requires siteId");
147
+ }
148
+ const cacheKey = `public-booking-services:${siteId}:${ids ?? ""}`;
149
+ return cachedFetch(cacheKey, async () => {
150
+ const apiParams = { siteId };
151
+ if (ids) apiParams.ids = ids;
152
+ return await apiClient({ endpoint: "getPublicBookingServices", params: apiParams });
153
+ });
154
+ },
155
+ async listPublicEvents(params) {
156
+ const { siteId, limit, from, to, stage } = params;
157
+ if (!siteId) {
158
+ throw new Error("listPublicEvents() requires siteId");
159
+ }
160
+ const cacheKey = `public-events:${siteId}:${limit ?? ""}:${from ?? ""}:${to ?? ""}:${stage ?? ""}`;
161
+ return cachedFetch(cacheKey, async () => {
162
+ const apiParams = { siteId };
163
+ if (typeof limit === "number") apiParams.limit = String(limit);
164
+ if (from) apiParams.from = from;
165
+ if (to) apiParams.to = to;
166
+ if (stage) apiParams.stage = stage;
167
+ return await apiClient({ endpoint: "listPublicEvents", params: apiParams });
168
+ });
169
+ },
170
+ clearCache() {
171
+ cache.clear();
172
+ }
173
+ };
174
+ }
175
+
176
+ export {
177
+ createRiverbankClient
178
+ };
179
+ //# sourceMappingURL=chunk-3KKZVGH4.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/client/index.ts","../../src/client/cache.ts"],"sourcesContent":["import { createBearerAPIClient } from '@riverbankcms/api';\nimport type {\n RiverbankClient,\n RiverbankClientConfig,\n PublicBookingServicesResponse,\n PublicEventsResponse,\n PublicFormResponse,\n} from './types';\nimport { SimpleCache } from './cache';\n\n/**\n * Create a Riverbank CMS client for fetching content\n *\n * @example\n * ```ts\n * const client = createRiverbankClient({\n * apiKey: 'bld_live_sk_...',\n * baseUrl: 'https://dashboard.example.com/api',\n * });\n *\n * const site = await client.getSite({ slug: 'my-site' });\n * ```\n */\nexport function createRiverbankClient(config: RiverbankClientConfig): RiverbankClient {\n if (!config.baseUrl) {\n throw new Error(\n 'baseUrl is required when creating a Builder client. ' +\n 'Expected format: https://dashboard.example.com/api (must include /api path)'\n );\n }\n\n // Validate baseUrl format\n if (!config.baseUrl.endsWith('/api')) {\n throw new Error(\n `baseUrl must end with '/api'. Received: ${config.baseUrl}. ` +\n 'Expected format: https://dashboard.example.com/api'\n );\n }\n\n const cacheEnabled = config.cache?.enabled ?? true;\n const cacheTTL = (config.cache?.ttl ?? 300) * 1000; // Convert seconds to milliseconds\n const cacheMaxSize = config.cache?.maxSize ?? 100;\n\n // Create internal API client with Bearer token auth\n const apiClient = createBearerAPIClient(config.apiKey, config.baseUrl);\n\n // Create cache instance\n const cache = new SimpleCache<unknown>({\n maxSize: cacheMaxSize,\n ttl: cacheTTL,\n });\n\n /**\n * Helper to cache API calls\n */\n async function cachedFetch<T>(\n cacheKey: string,\n fetcher: () => Promise<T>,\n options?: { force?: boolean }\n ): Promise<T> {\n // Check cache unless force is true\n if (cacheEnabled && !options?.force) {\n const cached = cache.get(cacheKey) as T | undefined;\n if (cached !== undefined) {\n return cached;\n }\n }\n\n // Fetch fresh data\n const data = await fetcher();\n\n // Store in cache\n if (cacheEnabled) {\n cache.set(cacheKey, data);\n }\n\n return data;\n }\n\n return {\n async getSite(params) {\n const { slug, domain, id } = params;\n\n if (!slug && !domain && !id) {\n throw new Error(\n 'getSite() requires at least one identifier: slug, domain, or id. ' +\n `Received: ${JSON.stringify(params)}`\n );\n }\n\n const cacheKey = `site:${slug || domain || id}`;\n\n return cachedFetch(cacheKey, async () => {\n // Convert params to string record for API client\n const apiParams: Record<string, string> = {};\n if (params.slug) apiParams.slug = params.slug;\n if (params.domain) apiParams.domain = params.domain;\n if (params.id) apiParams.id = params.id;\n return await apiClient({ endpoint: 'getSite', params: apiParams });\n });\n },\n\n async getPage(params) {\n const { siteId, path, preview = false } = params;\n const cacheKey = `page:${siteId}:${path}:${preview}`;\n\n return cachedFetch(cacheKey, async () => {\n return await apiClient({ endpoint: 'getContentByPath', params: { siteId }, body: { path, preview } });\n });\n },\n\n async getEntries(params) {\n const { siteId, contentType, limit, order, preview = false, mode, entryIds } = params;\n\n // Include all params in cache key to ensure different queries are cached separately\n const entryIdsCacheKey = mode === 'manual' && entryIds?.length ? entryIds.join(',') : '';\n const cacheKey = `entries:${siteId}:${contentType}:${limit ?? ''}:${order ?? ''}:${preview}:${mode ?? ''}:${entryIdsCacheKey}`;\n\n return cachedFetch(cacheKey, async () => {\n // Build API params\n const apiParams: Record<string, string> = {\n siteId,\n type: contentType,\n };\n\n // Add optional pagination\n if (typeof limit === 'number') {\n apiParams.limit = String(limit);\n }\n\n // Convert user-friendly order to API format\n // 'order' means custom ordering - we don't pass an order param (API default)\n if (order === 'newest') {\n apiParams.order = 'published_at.desc';\n } else if (order === 'oldest') {\n apiParams.order = 'published_at.asc';\n } else if (order === 'title') {\n apiParams.order = 'title.asc';\n }\n // 'order' or undefined: don't set order param, use API default\n\n // Add preview stage if enabled\n if (preview) {\n apiParams.stage = 'preview';\n }\n\n // Manual mode - pass entry IDs for hydration\n if (mode === 'manual' && entryIds?.length) {\n apiParams.mode = 'manual';\n apiParams.entryIds = JSON.stringify(entryIds);\n }\n\n return await apiClient({ endpoint: 'listPublishedEntries', params: apiParams });\n });\n },\n\n async getEntry(params) {\n const { siteId, contentType, slug } = params;\n const cacheKey = `entry:${siteId}:${contentType}:${slug}`;\n\n return cachedFetch(cacheKey, async () => {\n return await apiClient({ endpoint: 'getPublishedEntryPreview', params: { siteId, type: contentType, slug } });\n });\n },\n\n async getPublicFormById(params) {\n const { formId } = params;\n if (!formId) {\n throw new Error('getPublicFormById() requires formId');\n }\n const cacheKey = `public-form:${formId}`;\n return cachedFetch(cacheKey, async () => {\n return await apiClient({ endpoint: 'getPublicFormById', params: { formId } }) as PublicFormResponse;\n });\n },\n\n async getPublicBookingServices(params) {\n const { siteId, ids } = params;\n if (!siteId) {\n throw new Error('getPublicBookingServices() requires siteId');\n }\n const cacheKey = `public-booking-services:${siteId}:${ids ?? ''}`;\n return cachedFetch(cacheKey, async () => {\n const apiParams: Record<string, string> = { siteId };\n if (ids) apiParams.ids = ids;\n return await apiClient({ endpoint: 'getPublicBookingServices', params: apiParams }) as PublicBookingServicesResponse;\n });\n },\n\n async listPublicEvents(params) {\n const { siteId, limit, from, to, stage } = params;\n if (!siteId) {\n throw new Error('listPublicEvents() requires siteId');\n }\n const cacheKey = `public-events:${siteId}:${limit ?? ''}:${from ?? ''}:${to ?? ''}:${stage ?? ''}`;\n return cachedFetch(cacheKey, async () => {\n const apiParams: Record<string, string> = { siteId };\n if (typeof limit === 'number') apiParams.limit = String(limit);\n if (from) apiParams.from = from;\n if (to) apiParams.to = to;\n if (stage) apiParams.stage = stage;\n return await apiClient({ endpoint: 'listPublicEvents', params: apiParams }) as PublicEventsResponse;\n });\n },\n\n clearCache() {\n cache.clear();\n },\n };\n}\n\n// Re-export types\nexport type { RiverbankClient, RiverbankClientConfig } from './types';\n","/**\n * Simple in-memory cache with TTL support\n */\nexport class SimpleCache<T> {\n private cache = new Map<string, { value: T; expires: number }>();\n private maxSize: number;\n private ttl: number;\n\n constructor(options: { maxSize?: number; ttl?: number } = {}) {\n this.maxSize = options.maxSize ?? 100;\n this.ttl = options.ttl ?? 300000; // 5 minutes in milliseconds\n }\n\n get(key: string): T | undefined {\n const entry = this.cache.get(key);\n if (!entry) return undefined;\n\n // Check if expired\n if (Date.now() > entry.expires) {\n this.cache.delete(key);\n return undefined;\n }\n\n return entry.value;\n }\n\n set(key: string, value: T): void {\n // Enforce max size with simple FIFO eviction\n if (this.cache.size >= this.maxSize) {\n const firstKey = this.cache.keys().next().value;\n if (firstKey) {\n this.cache.delete(firstKey);\n }\n }\n\n this.cache.set(key, {\n value,\n expires: Date.now() + this.ttl,\n });\n }\n\n clear(): void {\n this.cache.clear();\n }\n\n has(key: string): boolean {\n return this.get(key) !== undefined;\n }\n}\n"],"mappings":"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","names":[]}