@riverbankcms/sdk 0.3.0 → 0.4.1

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 (142) hide show
  1. package/README.md +255 -1
  2. package/dist/cli/index.js +249 -40
  3. package/dist/cli/index.js.map +1 -1
  4. package/dist/client/bookings.js +15 -1
  5. package/dist/client/bookings.js.map +1 -1
  6. package/dist/client/bookings.mjs +15 -1
  7. package/dist/client/bookings.mjs.map +1 -1
  8. package/dist/client/client.d.mts +2 -2
  9. package/dist/client/client.d.ts +2 -2
  10. package/dist/client/client.js +33 -5
  11. package/dist/client/client.js.map +1 -1
  12. package/dist/client/client.mjs +33 -5
  13. package/dist/client/client.mjs.map +1 -1
  14. package/dist/client/hooks.d.mts +2 -2
  15. package/dist/client/hooks.d.ts +2 -2
  16. package/dist/client/rendering/client.js +31 -33
  17. package/dist/client/rendering/client.js.map +1 -1
  18. package/dist/client/rendering/client.mjs +31 -33
  19. package/dist/client/rendering/client.mjs.map +1 -1
  20. package/dist/client/usePage-BcjWPXvh.d.mts +6388 -0
  21. package/dist/client/usePage-C3ZKNwY7.d.mts +6393 -0
  22. package/dist/client/usePage-CyYpOJud.d.ts +6388 -0
  23. package/dist/client/usePage-gpVaeWDy.d.ts +6393 -0
  24. package/dist/server/{Layout-CsAQ-0Fv.d.mts → Layout-B_zUr9ci.d.mts} +1 -1
  25. package/dist/server/{Layout-BM_KmCxO.d.ts → Layout-CHG77dhK.d.ts} +1 -1
  26. package/dist/server/blockKinds-B6MWzNWp.d.mts +16 -0
  27. package/dist/server/blockKinds-B6MWzNWp.d.ts +16 -0
  28. package/dist/server/{chunk-GWBMJPLH.mjs → chunk-A2FZMRDW.mjs} +3 -2
  29. package/dist/server/chunk-A2FZMRDW.mjs.map +1 -0
  30. package/dist/server/chunk-BOHTTHY5.mjs +406 -0
  31. package/dist/server/chunk-BOHTTHY5.mjs.map +1 -0
  32. package/dist/server/{chunk-5R4NMVXA.js → chunk-BUCJWG6G.js} +5 -5
  33. package/dist/server/chunk-BUCJWG6G.js.map +1 -0
  34. package/dist/server/chunk-D2QLTPUJ.mjs +33 -0
  35. package/dist/server/chunk-D2QLTPUJ.mjs.map +1 -0
  36. package/dist/server/chunk-GERCMTPQ.js +33 -0
  37. package/dist/server/chunk-GERCMTPQ.js.map +1 -0
  38. package/dist/server/{chunk-SF63XAX7.js → chunk-IT5ICP43.js} +24 -4
  39. package/dist/server/chunk-IT5ICP43.js.map +1 -0
  40. package/dist/server/{chunk-QFFQTOY3.mjs → chunk-N3PX76AP.mjs} +11 -3
  41. package/dist/server/{chunk-QFFQTOY3.mjs.map → chunk-N3PX76AP.mjs.map} +1 -1
  42. package/dist/server/{chunk-O5DC7MYW.mjs → chunk-NKXS4TBK.mjs} +24 -4
  43. package/dist/server/chunk-NKXS4TBK.mjs.map +1 -0
  44. package/dist/server/{chunk-INWKF3IC.js → chunk-P6CDRJN3.js} +8 -8
  45. package/dist/server/{chunk-INWKF3IC.js.map → chunk-P6CDRJN3.js.map} +1 -1
  46. package/dist/server/{chunk-UFVCBGBY.js → chunk-R5B6IOFQ.js} +11 -3
  47. package/dist/server/chunk-R5B6IOFQ.js.map +1 -0
  48. package/dist/server/{chunk-FK64TZBT.mjs → chunk-SFQ7VF3G.mjs} +2 -2
  49. package/dist/server/{chunk-62ZJI564.js → chunk-SWPHIUVE.js} +7 -6
  50. package/dist/server/chunk-SWPHIUVE.js.map +1 -0
  51. package/dist/server/{chunk-PN3CHDVX.mjs → chunk-XK2YIISA.mjs} +2 -2
  52. package/dist/server/chunk-XK2YIISA.mjs.map +1 -0
  53. package/dist/server/{chunk-YXDDFG3N.js → chunk-Y7347JMZ.js} +3 -2
  54. package/dist/server/chunk-Y7347JMZ.js.map +1 -0
  55. package/dist/server/{chunk-JTAERCX2.mjs → chunk-ZEAJW6T3.mjs} +5 -4
  56. package/dist/server/chunk-ZEAJW6T3.mjs.map +1 -0
  57. package/dist/server/chunk-ZIM53VP6.js +406 -0
  58. package/dist/server/chunk-ZIM53VP6.js.map +1 -0
  59. package/dist/server/{components-CI3JiOYA.d.mts → components-Bo3LPpVb.d.mts} +3 -3
  60. package/dist/server/{components-DJBLu_yc.d.ts → components-ClFs4PUa.d.ts} +3 -3
  61. package/dist/server/components.d.mts +7 -6
  62. package/dist/server/components.d.ts +7 -6
  63. package/dist/server/components.js +3 -3
  64. package/dist/server/components.mjs +2 -2
  65. package/dist/server/config-validation.d.mts +5 -300
  66. package/dist/server/config-validation.d.ts +5 -300
  67. package/dist/server/config-validation.js +6 -3
  68. package/dist/server/config-validation.js.map +1 -1
  69. package/dist/server/config-validation.mjs +5 -2
  70. package/dist/server/config.d.mts +43 -4
  71. package/dist/server/config.d.ts +43 -4
  72. package/dist/server/config.js +27 -3
  73. package/dist/server/config.js.map +1 -1
  74. package/dist/server/config.mjs +26 -2
  75. package/dist/server/config.mjs.map +1 -1
  76. package/dist/server/data.d.mts +8 -5
  77. package/dist/server/data.d.ts +8 -5
  78. package/dist/server/data.js +2 -2
  79. package/dist/server/data.mjs +1 -1
  80. package/dist/server/{index-Dus2gkY6.d.ts → index-DbSfrRA0.d.ts} +1 -1
  81. package/dist/server/{index-DoX3ELQn.d.mts → index-Dj7VKH34.d.mts} +1 -1
  82. package/dist/server/index.d.mts +5 -4
  83. package/dist/server/index.d.ts +5 -4
  84. package/dist/server/index.js +6 -2
  85. package/dist/server/index.js.map +1 -1
  86. package/dist/server/index.mjs +5 -1
  87. package/dist/server/{loadContent-CdXDGsJM.d.ts → loadContent-C2SwqmXy.d.ts} +4 -4
  88. package/dist/server/{loadContent-v2n6pOlO.d.mts → loadContent-C_FipaAC.d.mts} +4 -4
  89. package/dist/server/loadPage-DUHBXDEW.js +11 -0
  90. package/dist/server/{loadPage-3ECPF426.js.map → loadPage-DUHBXDEW.js.map} +1 -1
  91. package/dist/server/{loadPage-LW273NYO.mjs → loadPage-LYVKY3WZ.mjs} +3 -3
  92. package/dist/server/{loadPage-en10WQrt.d.mts → loadPage-mavT3Jae.d.mts} +22 -3
  93. package/dist/server/{loadPage-bejlajm9.d.ts → loadPage-naVvoua8.d.ts} +22 -3
  94. package/dist/server/metadata.d.mts +5 -4
  95. package/dist/server/metadata.d.ts +5 -4
  96. package/dist/server/navigation.d.mts +2 -2
  97. package/dist/server/navigation.d.ts +2 -2
  98. package/dist/server/rendering/server.d.mts +8 -7
  99. package/dist/server/rendering/server.d.ts +8 -7
  100. package/dist/server/rendering/server.js +4 -4
  101. package/dist/server/rendering/server.mjs +3 -3
  102. package/dist/server/rendering.d.mts +9 -8
  103. package/dist/server/rendering.d.ts +9 -8
  104. package/dist/server/rendering.js +6 -6
  105. package/dist/server/rendering.mjs +5 -5
  106. package/dist/server/routing.d.mts +5 -4
  107. package/dist/server/routing.d.ts +5 -4
  108. package/dist/server/routing.js +1 -1
  109. package/dist/server/routing.mjs +1 -1
  110. package/dist/server/server.d.mts +7 -6
  111. package/dist/server/server.d.ts +7 -6
  112. package/dist/server/server.js +4 -4
  113. package/dist/server/server.mjs +3 -3
  114. package/dist/server/theme-bridge.js +7 -7
  115. package/dist/server/theme-bridge.mjs +1 -1
  116. package/dist/server/{types-Cc7lyPkN.d.ts → types-5XdVD2J1.d.ts} +2 -0
  117. package/dist/server/{types-nVerjjdv.d.mts → types-BA-J9K8r.d.mts} +2 -0
  118. package/dist/server/{types-CLusapsM.d.mts → types-BC9eB2KH.d.mts} +65 -17
  119. package/dist/server/{types-Ls6BkLKg.d.ts → types-CAnC529E.d.ts} +65 -17
  120. package/dist/server/{types-D-rqOU5I.d.ts → types-CMqVHYLG.d.ts} +264 -2
  121. package/dist/server/{types-Bq3520hK.d.mts → types-CYfHxUhe.d.mts} +264 -2
  122. package/dist/server/{types-_nDnPHpv.d.mts → types-CbagRQ_7.d.mts} +19 -1
  123. package/dist/server/{types-_nDnPHpv.d.ts → types-DuQCNVV0.d.ts} +19 -1
  124. package/dist/server/validation-C7W2Fe0i.d.ts +459 -0
  125. package/dist/server/validation-hg1sqhrt.d.mts +459 -0
  126. package/package.json +2 -1
  127. package/dist/server/chunk-5R4NMVXA.js.map +0 -1
  128. package/dist/server/chunk-62ZJI564.js.map +0 -1
  129. package/dist/server/chunk-BPKYRPCQ.mjs +0 -215
  130. package/dist/server/chunk-BPKYRPCQ.mjs.map +0 -1
  131. package/dist/server/chunk-GWBMJPLH.mjs.map +0 -1
  132. package/dist/server/chunk-JTAERCX2.mjs.map +0 -1
  133. package/dist/server/chunk-O5DC7MYW.mjs.map +0 -1
  134. package/dist/server/chunk-PN3CHDVX.mjs.map +0 -1
  135. package/dist/server/chunk-SF63XAX7.js.map +0 -1
  136. package/dist/server/chunk-UFVCBGBY.js.map +0 -1
  137. package/dist/server/chunk-XLVL5WPH.js +0 -215
  138. package/dist/server/chunk-XLVL5WPH.js.map +0 -1
  139. package/dist/server/chunk-YXDDFG3N.js.map +0 -1
  140. package/dist/server/loadPage-3ECPF426.js +0 -11
  141. /package/dist/server/{chunk-FK64TZBT.mjs.map → chunk-SFQ7VF3G.mjs.map} +0 -0
  142. /package/dist/server/{loadPage-LW273NYO.mjs.map → loadPage-LYVKY3WZ.mjs.map} +0 -0
package/README.md CHANGED
@@ -12,6 +12,7 @@ A lightweight TypeScript SDK for consuming Riverbank CMS content in React Server
12
12
  - ✅ **Type-safe API client** with caching
13
13
  - ✅ **React Server Components** compatible
14
14
  - ✅ **Custom blocks** - Define site-specific blocks with full CMS editing support
15
+ - ✅ **Content scaffolding** - Define content types, pages, entries, and navigation in code
15
16
 
16
17
  ## Installation
17
18
 
@@ -1879,6 +1880,259 @@ import {
1879
1880
  } from '@riverbankcms/sdk/data';
1880
1881
  ```
1881
1882
 
1883
+ ## Content Configuration (SDK Content Scaffolding)
1884
+
1885
+ Define content types, pages, entries, navigation menus, and site settings in code. When you push your SDK config, this content is automatically synced to the CMS database.
1886
+
1887
+ ### Quick Start
1888
+
1889
+ Create a `content.config.ts` file alongside your `riverbank.config.ts`:
1890
+
1891
+ ```typescript
1892
+ // content.config.ts
1893
+ import { defineContentConfig } from '@riverbankcms/sdk/config';
1894
+
1895
+ export const contentConfig = defineContentConfig({
1896
+ // Content type definitions
1897
+ contentTypes: [
1898
+ {
1899
+ key: 'testimonial',
1900
+ name: 'Testimonial',
1901
+ hasPages: false,
1902
+ fields: [
1903
+ { id: 'quote', type: 'richText', label: 'Quote', required: true },
1904
+ { id: 'author', type: 'text', label: 'Author Name', required: true, multiline: false },
1905
+ ],
1906
+ },
1907
+ ],
1908
+
1909
+ // Page definitions
1910
+ pages: [
1911
+ {
1912
+ identifier: 'home',
1913
+ path: '/',
1914
+ title: 'Home',
1915
+ status: 'published',
1916
+ },
1917
+ ],
1918
+
1919
+ // Content entries
1920
+ entries: [
1921
+ {
1922
+ identifier: 'testimonial-1',
1923
+ contentType: 'testimonial',
1924
+ title: 'First Testimonial',
1925
+ status: 'published',
1926
+ data: {
1927
+ quote: '<p>Great product!</p>',
1928
+ author: 'Jane Doe',
1929
+ },
1930
+ },
1931
+ ],
1932
+
1933
+ // Navigation menus
1934
+ navigationMenus: [
1935
+ {
1936
+ identifier: 'main',
1937
+ name: 'Main Navigation',
1938
+ isPrimary: true,
1939
+ items: [
1940
+ { label: 'Home', link: { kind: 'page', identifier: 'home' } },
1941
+ ],
1942
+ },
1943
+ ],
1944
+
1945
+ // Site settings
1946
+ settings: {
1947
+ homepagePath: '/',
1948
+ siteTitle: 'My Site',
1949
+ siteDescription: 'Welcome to my site',
1950
+ },
1951
+ });
1952
+ ```
1953
+
1954
+ Then import it in your main config:
1955
+
1956
+ ```typescript
1957
+ // riverbank.config.ts
1958
+ import { defineConfig } from '@riverbankcms/sdk/config';
1959
+ import { contentConfig } from './content.config';
1960
+
1961
+ export default defineConfig({
1962
+ siteId: 'your-site-id',
1963
+ theme: { /* ... */ },
1964
+ content: contentConfig,
1965
+ });
1966
+ ```
1967
+
1968
+ ### Content Types
1969
+
1970
+ Define reusable content structures:
1971
+
1972
+ ```typescript
1973
+ contentTypes: [
1974
+ {
1975
+ key: 'blog-post', // Unique identifier
1976
+ name: 'Blog Post', // Display name
1977
+ description: 'Blog articles',
1978
+ hasPages: true, // Whether entries are routable
1979
+ routePattern: '/blog/{slug}', // URL pattern (required if hasPages)
1980
+ fields: [
1981
+ { id: 'body', type: 'richText', label: 'Content', required: true },
1982
+ { id: 'author', type: 'text', label: 'Author', required: false, multiline: false },
1983
+ { id: 'featuredImage', type: 'media', label: 'Featured Image', mediaKinds: ['image'] },
1984
+ ],
1985
+ titleField: 'title', // Field to use as entry title
1986
+ },
1987
+ ]
1988
+ ```
1989
+
1990
+ ### Pages
1991
+
1992
+ Define pages with optional initial blocks:
1993
+
1994
+ ```typescript
1995
+ pages: [
1996
+ {
1997
+ identifier: 'about', // Required, unique per site
1998
+ path: '/about', // URL path
1999
+ title: 'About Us',
2000
+ purpose: 'content', // 'content' | 'landing' | etc.
2001
+ status: 'draft', // 'draft' | 'published'
2002
+ metaTitle: 'About Us | My Site',
2003
+ metaDescription: 'Learn more about our company',
2004
+ blocks: [
2005
+ {
2006
+ kind: 'block.hero',
2007
+ content: { headline: 'About Us', subheadline: 'Our Story' },
2008
+ orderIndex: 0,
2009
+ },
2010
+ ],
2011
+ },
2012
+ ]
2013
+ ```
2014
+
2015
+ ### Content Entries
2016
+
2017
+ Create content entries for your content types:
2018
+
2019
+ ```typescript
2020
+ entries: [
2021
+ {
2022
+ identifier: 'john-smith', // Required, unique per site
2023
+ contentType: 'team-member', // Content type key
2024
+ title: 'John Smith',
2025
+ slug: 'john-smith', // For routable entries (defaults to identifier)
2026
+ status: 'published',
2027
+ data: {
2028
+ bio: '<p>Our founder...</p>',
2029
+ role: 'CEO',
2030
+ },
2031
+ metaTitle: 'John Smith | Team',
2032
+ metaDescription: 'Meet John Smith, our CEO',
2033
+ },
2034
+ ]
2035
+ ```
2036
+
2037
+ ### Navigation Menus
2038
+
2039
+ Define navigation structure with page/entry references:
2040
+
2041
+ ```typescript
2042
+ navigationMenus: [
2043
+ {
2044
+ identifier: 'main',
2045
+ name: 'Main Navigation',
2046
+ isPrimary: true,
2047
+ items: [
2048
+ // Link to a page by identifier
2049
+ { label: 'Home', link: { kind: 'page', identifier: 'home' } },
2050
+
2051
+ // Link to a routable content entry
2052
+ { label: 'About John', link: { kind: 'entry', identifier: 'john-smith' } },
2053
+
2054
+ // External link
2055
+ { label: 'GitHub', link: { kind: 'external', href: 'https://github.com' } },
2056
+
2057
+ // Dropdown with children
2058
+ {
2059
+ label: 'Resources',
2060
+ link: { kind: 'dropdown' },
2061
+ children: [
2062
+ { label: 'Blog', link: { kind: 'page', identifier: 'blog' } },
2063
+ { label: 'Docs', link: { kind: 'external', href: 'https://docs.example.com' } },
2064
+ ],
2065
+ },
2066
+ ],
2067
+ },
2068
+ ]
2069
+ ```
2070
+
2071
+ ### Site Settings
2072
+
2073
+ Configure site-wide settings:
2074
+
2075
+ ```typescript
2076
+ settings: {
2077
+ homepagePath: '/', // Path to homepage
2078
+ siteTitle: 'My Site', // Site title for metadata
2079
+ siteDescription: 'Welcome', // Site description for metadata
2080
+ }
2081
+ ```
2082
+
2083
+ ### Sync Behavior
2084
+
2085
+ When you run `riverbank push`:
2086
+
2087
+ 1. **Creates** new content if the identifier doesn't exist
2088
+ 2. **Updates** existing content if it hasn't been modified in the dashboard
2089
+ 3. **Skips** content that was edited in the dashboard (preserves manual changes)
2090
+ 4. **Never deletes** - removing from config doesn't delete from CMS
2091
+
2092
+ This "overwrite if unchanged" behavior ensures:
2093
+ - SDK-defined content can be version controlled
2094
+ - Dashboard edits are never lost
2095
+ - Teams can safely iterate on config
2096
+
2097
+ ### Sync Order
2098
+
2099
+ Content syncs in dependency order:
2100
+
2101
+ 1. Content Types (no dependencies)
2102
+ 2. Site Settings (no dependencies)
2103
+ 3. Pages (needed for navigation references)
2104
+ 4. Content Entries (depend on content types)
2105
+ 5. Navigation Menus (reference pages and entries)
2106
+
2107
+ ### Validation
2108
+
2109
+ The SDK validates your content config on push:
2110
+
2111
+ - Unique identifiers per entity type
2112
+ - Valid content type references
2113
+ - Valid field definitions
2114
+ - Navigation references resolve to existing pages/entries
2115
+ - Path uniqueness for pages
2116
+
2117
+ ### Types
2118
+
2119
+ ```typescript
2120
+ import {
2121
+ // Helper function
2122
+ defineContentConfig,
2123
+
2124
+ // Types
2125
+ type ContentConfig,
2126
+ type ContentTypeConfig,
2127
+ type PageConfig,
2128
+ type EntryConfig,
2129
+ type BlockConfig,
2130
+ type NavigationMenuConfig,
2131
+ type NavigationItemConfig,
2132
+ type SiteSettingsConfig,
2133
+ } from '@riverbankcms/sdk/config';
2134
+ ```
2135
+
1882
2136
  ## Additional Exports
1883
2137
 
1884
2138
  - `@riverbankcms/sdk/rendering` - Low-level rendering components
@@ -1889,4 +2143,4 @@ import {
1889
2143
  - `@riverbankcms/sdk/metadata` - Metadata generation helpers
1890
2144
  - `@riverbankcms/sdk/routing` - Route resolution helpers
1891
2145
  - `@riverbankcms/sdk/analytics` - Analytics tracking helpers
1892
- - `@riverbankcms/sdk/config` - Site configuration utilities (includes `defineConfig`, `RiverbankSiteConfig`, `BlockFieldOptionsMap`, `FieldSelectOption`, etc.)
2146
+ - `@riverbankcms/sdk/config` - Site configuration utilities (includes `defineConfig`, `defineContentConfig`, `RiverbankSiteConfig`, `ContentConfig`, etc.)
package/dist/cli/index.js CHANGED
@@ -1,6 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  'use strict';
3
3
 
4
+ var dotenv = require('dotenv');
4
5
  var commander = require('commander');
5
6
  var zod = require('zod');
6
7
  var jiti = require('jiti');
@@ -685,7 +686,7 @@ function sectionContainer(children, opts) {
685
686
  var DEFAULT_SECTION_SPACING = "medium";
686
687
 
687
688
  // ../blocks/src/system/node/fragments/styledSection.ts
688
- function styledSection(config) {
689
+ function styledSection(config2) {
689
690
  const {
690
691
  children,
691
692
  baseClass = "px-6",
@@ -693,7 +694,7 @@ function styledSection(config) {
693
694
  background = "background/base",
694
695
  bindFrom = "_sectionStyles",
695
696
  imageClassName = "absolute -z-10"
696
- } = config;
697
+ } = config2;
697
698
  const backgroundNodes = backgroundLayer(`${bindFrom}.background`, {
698
699
  imageClassName
699
700
  });
@@ -834,8 +835,8 @@ var FragmentConfigError = class extends Error {
834
835
  this.name = "FragmentConfigError";
835
836
  }
836
837
  };
837
- function defineFragment(config) {
838
- const parsed = fragmentConfigSchema.parse(config);
838
+ function defineFragment(config2) {
839
+ const parsed = fragmentConfigSchema.parse(config2);
839
840
  validateFieldDefinitions(parsed.fields, parsed.id);
840
841
  return {
841
842
  ...parsed,
@@ -1042,26 +1043,26 @@ var FragmentCompositionError = class extends Error {
1042
1043
  }
1043
1044
  };
1044
1045
  var DOT_SCOPE_REGEX = /^[A-Za-z0-9_.-]*$/;
1045
- function materializeFragment(config) {
1046
- const scope = normalizeScope(config.scope);
1047
- const fields4 = scopeFragmentFields(config.fragment, scope);
1048
- const layout = scopeFragmentLayout(config.fragment, scope);
1049
- const fieldPriority = config.fieldPriority ?? 0;
1046
+ function materializeFragment(config2) {
1047
+ const scope = normalizeScope(config2.scope);
1048
+ const fields4 = scopeFragmentFields(config2.fragment, scope);
1049
+ const layout = scopeFragmentLayout(config2.fragment, scope);
1050
+ const fieldPriority = config2.fieldPriority ?? 0;
1050
1051
  let data;
1051
- if (config.fragment.data) {
1052
- const key = config.dataKey ?? config.fragment.data.key;
1052
+ if (config2.fragment.data) {
1053
+ const key = config2.dataKey ?? config2.fragment.data.key;
1053
1054
  if (!key || typeof key !== "string") {
1054
1055
  throw new FragmentCompositionError(
1055
- `Fragment "${config.fragment.id}" requires a data key to compose.`
1056
+ `Fragment "${config2.fragment.id}" requires a data key to compose.`
1056
1057
  );
1057
1058
  }
1058
1059
  data = {
1059
1060
  key,
1060
- loader: config.fragment.data.loader ? { ...config.fragment.data.loader } : void 0
1061
+ loader: config2.fragment.data.loader ? { ...config2.fragment.data.loader } : void 0
1061
1062
  };
1062
1063
  }
1063
1064
  return {
1064
- fragment: config.fragment,
1065
+ fragment: config2.fragment,
1065
1066
  scope,
1066
1067
  fields: fields4,
1067
1068
  layout,
@@ -2845,13 +2846,13 @@ function sectionStylesField(options = {}) {
2845
2846
  }
2846
2847
 
2847
2848
  // ../blocks/src/system/defineBlock.ts
2848
- function createBlockManifest(config) {
2849
- const composition2 = config.fragments ? composeFragments(config.fragments) : { fields: []};
2849
+ function createBlockManifest(config2) {
2850
+ const composition2 = config2.fragments ? composeFragments(config2.fragments) : { fields: []};
2850
2851
  const allFields = [
2851
2852
  ...composition2.fields,
2852
- ...config.additionalFields ?? []
2853
+ ...config2.additionalFields ?? []
2853
2854
  ];
2854
- if (!config.skipSectionStyles) {
2855
+ if (!config2.skipSectionStyles) {
2855
2856
  allFields.push(
2856
2857
  sectionStylesField({
2857
2858
  id: "_sectionStyles",
@@ -2860,36 +2861,36 @@ function createBlockManifest(config) {
2860
2861
  );
2861
2862
  }
2862
2863
  const fields4 = fieldSchema.array().parse(allFields);
2863
- const layout = config.layout;
2864
- const variants = config.variants;
2865
- let behaviours = config.behaviours;
2866
- if (!behaviours && config.paletteHidden !== void 0) {
2864
+ const layout = config2.layout;
2865
+ const variants = config2.variants;
2866
+ let behaviours = config2.behaviours;
2867
+ if (!behaviours && config2.paletteHidden !== void 0) {
2867
2868
  behaviours = {
2868
2869
  supportsThemeSwitching: true,
2869
2870
  inlineEditing: true,
2870
2871
  animation: true,
2871
- paletteHidden: config.paletteHidden
2872
+ paletteHidden: config2.paletteHidden
2872
2873
  };
2873
2874
  }
2874
2875
  const manifest = {
2875
- name: config.id,
2876
+ name: config2.id,
2876
2877
  version: "0.1.0",
2877
- title: config.title,
2878
- titleSource: config.titleSource,
2879
- description: config.description ?? "",
2880
- component: config.component ?? deriveComponentName(config.id),
2878
+ title: config2.title,
2879
+ titleSource: config2.titleSource,
2880
+ description: config2.description ?? "",
2881
+ component: config2.component ?? deriveComponentName(config2.id),
2881
2882
  fields: fields4,
2882
2883
  slots: [],
2883
2884
  // Always empty
2884
- styleTokens: config.styleTokens,
2885
+ styleTokens: config2.styleTokens,
2885
2886
  behaviours,
2886
- category: config.category,
2887
- contentTypes: config.contentTypes,
2888
- tags: config.tags ?? [],
2889
- icon: config.icon ?? "Box",
2887
+ category: config2.category,
2888
+ contentTypes: config2.contentTypes,
2889
+ tags: config2.tags ?? [],
2890
+ icon: config2.icon ?? "Box",
2890
2891
  layout,
2891
2892
  variants,
2892
- defaultVariant: config.defaultVariant
2893
+ defaultVariant: config2.defaultVariant
2893
2894
  };
2894
2895
  return augmentManifest(manifest);
2895
2896
  }
@@ -4840,6 +4841,211 @@ function getBlockDefinition(name) {
4840
4841
  return blockStore.get(name);
4841
4842
  }
4842
4843
 
4844
+ // ../blocks/src/system/constants/blockKinds.ts
4845
+ var SYSTEM_BLOCK_KINDS = [
4846
+ // Layout blocks
4847
+ "block.hero",
4848
+ "block.columns",
4849
+ "block.ctaFull",
4850
+ "block.singleButton",
4851
+ // Content blocks
4852
+ "block.bodyText",
4853
+ "block.faq",
4854
+ "block.testimonials",
4855
+ "block.embed",
4856
+ // Blog blocks
4857
+ "block.blogPost",
4858
+ "block.blogPlaceholder",
4859
+ "block.blogListing",
4860
+ // Site structure blocks
4861
+ "block.siteHeader",
4862
+ "block.siteFooter",
4863
+ // Form/booking blocks
4864
+ "block.form",
4865
+ "block.appointment-booking",
4866
+ "block.event-registration",
4867
+ // Event display blocks
4868
+ "block.event-spotlight",
4869
+ "block.event-listing",
4870
+ "block.event-calendar"
4871
+ ];
4872
+
4873
+ // src/config/content-validation.ts
4874
+ var jsonDataSchema = zod.z.record(zod.z.string(), zod.z.any());
4875
+ var contentTypeKeySchema = zod.z.string().min(1).regex(
4876
+ /^[a-z][a-z0-9-]*$/,
4877
+ "Key must be lowercase, start with a letter, and contain only letters, numbers, and hyphens"
4878
+ );
4879
+ var contentTypeConfigSchema = zod.z.object({
4880
+ key: contentTypeKeySchema,
4881
+ name: zod.z.string().min(1),
4882
+ description: zod.z.string().optional(),
4883
+ hasPages: zod.z.boolean(),
4884
+ routePattern: zod.z.string().optional(),
4885
+ // Fields are validated as any[] here - deep field validation happens in @riverbankcms/blocks
4886
+ fields: zod.z.array(zod.z.any()).min(1, "At least one field is required"),
4887
+ titleField: zod.z.string().optional()
4888
+ }).refine(
4889
+ (data) => {
4890
+ if (data.hasPages) {
4891
+ return data.routePattern && data.routePattern.includes("{slug}");
4892
+ }
4893
+ return true;
4894
+ },
4895
+ { message: "routePattern with {slug} is required when hasPages is true" }
4896
+ ).refine(
4897
+ (data) => {
4898
+ if (!data.hasPages && data.routePattern) {
4899
+ return false;
4900
+ }
4901
+ return true;
4902
+ },
4903
+ { message: "routePattern must not be defined when hasPages is false" }
4904
+ );
4905
+ var blockKindSchema = zod.z.string().refine(
4906
+ (kind) => {
4907
+ if (SYSTEM_BLOCK_KINDS.includes(kind)) {
4908
+ return true;
4909
+ }
4910
+ if (kind.startsWith("custom.") && kind.length > 7) {
4911
+ return true;
4912
+ }
4913
+ return false;
4914
+ },
4915
+ {
4916
+ message: 'Block kind must be a system block (e.g., "block.hero") or a custom block (e.g., "custom.myBlock")'
4917
+ }
4918
+ );
4919
+ var blockConfigSchema = zod.z.object({
4920
+ kind: blockKindSchema,
4921
+ content: jsonDataSchema,
4922
+ orderIndex: zod.z.number().optional()
4923
+ });
4924
+ var pageConfigSchema = zod.z.object({
4925
+ identifier: zod.z.string().min(1, "Identifier is required"),
4926
+ title: zod.z.string().min(1, "Title is required"),
4927
+ path: zod.z.string().regex(/^\//, "Path must start with /"),
4928
+ purpose: zod.z.string().default("content"),
4929
+ blocks: zod.z.array(blockConfigSchema).optional(),
4930
+ status: zod.z.enum(["draft", "published"]).default("draft"),
4931
+ metaTitle: zod.z.string().optional(),
4932
+ metaDescription: zod.z.string().optional()
4933
+ });
4934
+ var entryConfigSchema = zod.z.object({
4935
+ identifier: zod.z.string().min(1, "Identifier is required"),
4936
+ contentType: zod.z.string().min(1, "Content type is required"),
4937
+ data: jsonDataSchema,
4938
+ status: zod.z.enum(["draft", "published"]).default("draft"),
4939
+ slug: zod.z.string().optional(),
4940
+ title: zod.z.string().optional(),
4941
+ summary: zod.z.string().optional(),
4942
+ metaTitle: zod.z.string().optional(),
4943
+ metaDescription: zod.z.string().optional()
4944
+ });
4945
+ var pageLinkSchema = zod.z.object({
4946
+ kind: zod.z.literal("page"),
4947
+ identifier: zod.z.string().min(1)
4948
+ });
4949
+ var entryLinkSchema = zod.z.object({
4950
+ kind: zod.z.literal("entry"),
4951
+ identifier: zod.z.string().min(1)
4952
+ });
4953
+ var externalLinkSchema = zod.z.object({
4954
+ kind: zod.z.literal("external"),
4955
+ href: zod.z.string().url("Invalid URL")
4956
+ });
4957
+ var dropdownLinkSchema = zod.z.object({
4958
+ kind: zod.z.literal("dropdown")
4959
+ });
4960
+ var navigationLinkSchema = zod.z.discriminatedUnion("kind", [
4961
+ pageLinkSchema,
4962
+ entryLinkSchema,
4963
+ externalLinkSchema,
4964
+ dropdownLinkSchema
4965
+ ]);
4966
+ var baseNavigationItemSchema = zod.z.object({
4967
+ label: zod.z.string().min(1, "Label is required"),
4968
+ link: navigationLinkSchema,
4969
+ isCta: zod.z.boolean().optional()
4970
+ });
4971
+ var navigationItemConfigSchema = baseNavigationItemSchema.extend({
4972
+ children: zod.z.lazy(() => zod.z.array(navigationItemConfigSchema)).optional()
4973
+ });
4974
+ var navigationMenuConfigSchema = zod.z.object({
4975
+ identifier: zod.z.string().min(1, "Identifier is required"),
4976
+ name: zod.z.string().min(1, "Name is required"),
4977
+ isPrimary: zod.z.boolean().optional(),
4978
+ items: zod.z.array(navigationItemConfigSchema)
4979
+ });
4980
+ var siteSettingsConfigSchema = zod.z.object({
4981
+ homepagePath: zod.z.string().optional(),
4982
+ siteTitle: zod.z.string().optional(),
4983
+ siteDescription: zod.z.string().optional(),
4984
+ defaultTemplates: zod.z.record(zod.z.string(), zod.z.string()).optional()
4985
+ });
4986
+ var contentConfigBaseSchema = zod.z.object({
4987
+ contentTypes: zod.z.array(contentTypeConfigSchema).optional(),
4988
+ entries: zod.z.array(entryConfigSchema).optional(),
4989
+ pages: zod.z.array(pageConfigSchema).optional(),
4990
+ navigationMenus: zod.z.array(navigationMenuConfigSchema).optional(),
4991
+ settings: siteSettingsConfigSchema.optional()
4992
+ });
4993
+ var contentConfigSchema = contentConfigBaseSchema.superRefine((data, ctx) => {
4994
+ if (data.contentTypes && data.contentTypes.length > 1) {
4995
+ const keys = data.contentTypes.map((ct) => ct.key);
4996
+ const duplicateKeys = keys.filter((key, i) => keys.indexOf(key) !== i);
4997
+ if (duplicateKeys.length > 0) {
4998
+ ctx.addIssue({
4999
+ code: zod.z.ZodIssueCode.custom,
5000
+ message: `Content type keys must be unique. Duplicates: ${duplicateKeys.join(", ")}`,
5001
+ path: ["contentTypes"]
5002
+ });
5003
+ }
5004
+ }
5005
+ if (data.pages && data.pages.length > 1) {
5006
+ const identifiers = data.pages.map((p) => p.identifier);
5007
+ const duplicateIds = identifiers.filter((id, i) => identifiers.indexOf(id) !== i);
5008
+ if (duplicateIds.length > 0) {
5009
+ ctx.addIssue({
5010
+ code: zod.z.ZodIssueCode.custom,
5011
+ message: `Page identifiers must be unique. Duplicates: ${duplicateIds.join(", ")}`,
5012
+ path: ["pages"]
5013
+ });
5014
+ }
5015
+ const paths = data.pages.map((p) => p.path);
5016
+ const duplicatePaths = paths.filter((path, i) => paths.indexOf(path) !== i);
5017
+ if (duplicatePaths.length > 0) {
5018
+ ctx.addIssue({
5019
+ code: zod.z.ZodIssueCode.custom,
5020
+ message: `Page paths must be unique. Duplicates: ${duplicatePaths.join(", ")}`,
5021
+ path: ["pages"]
5022
+ });
5023
+ }
5024
+ }
5025
+ if (data.entries && data.entries.length > 1) {
5026
+ const identifiers = data.entries.map((e) => e.identifier);
5027
+ const duplicateIds = identifiers.filter((id, i) => identifiers.indexOf(id) !== i);
5028
+ if (duplicateIds.length > 0) {
5029
+ ctx.addIssue({
5030
+ code: zod.z.ZodIssueCode.custom,
5031
+ message: `Entry identifiers must be unique. Duplicates: ${duplicateIds.join(", ")}`,
5032
+ path: ["entries"]
5033
+ });
5034
+ }
5035
+ }
5036
+ if (data.navigationMenus && data.navigationMenus.length > 1) {
5037
+ const identifiers = data.navigationMenus.map((m) => m.identifier);
5038
+ const duplicateIds = identifiers.filter((id, i) => identifiers.indexOf(id) !== i);
5039
+ if (duplicateIds.length > 0) {
5040
+ ctx.addIssue({
5041
+ code: zod.z.ZodIssueCode.custom,
5042
+ message: `Navigation menu identifiers must be unique. Duplicates: ${duplicateIds.join(", ")}`,
5043
+ path: ["navigationMenus"]
5044
+ });
5045
+ }
5046
+ }
5047
+ });
5048
+
4843
5049
  // src/data/prefetchBlockData.ts
4844
5050
  var SUPPORTED_LOADER_ENDPOINTS = [
4845
5051
  "listPublishedEntries",
@@ -5026,7 +5232,8 @@ var riverbankSiteConfigSchema = zod.z.object({
5026
5232
  { message: "Block IDs must be unique" }
5027
5233
  ).optional(),
5028
5234
  blockFieldOptions: blockFieldOptionsSchema,
5029
- blockFieldExtensions: blockFieldExtensionsSchema
5235
+ blockFieldExtensions: blockFieldExtensionsSchema,
5236
+ content: contentConfigSchema.optional()
5030
5237
  }).strict();
5031
5238
  var DEFAULT_CONFIG_FILENAME = "riverbank.config.ts";
5032
5239
  async function loadConfigFile(configPath) {
@@ -5045,11 +5252,11 @@ Create a riverbank.config.ts file or specify a path with --config`
5045
5252
  });
5046
5253
  try {
5047
5254
  const configModule = await jiti$1.import(resolvedPath);
5048
- const config = configModule.default ?? configModule.config ?? configModule;
5049
- if (!config || typeof config !== "object") {
5255
+ const config2 = configModule.default ?? configModule.config ?? configModule;
5256
+ if (!config2 || typeof config2 !== "object") {
5050
5257
  throw new Error("Config file must export a configuration object");
5051
5258
  }
5052
- return config;
5259
+ return config2;
5053
5260
  } catch (error) {
5054
5261
  if (error instanceof Error) {
5055
5262
  throw new Error(`Failed to load config: ${error.message}`);
@@ -5069,7 +5276,7 @@ function resolveConfigPath(configPath) {
5069
5276
  }
5070
5277
 
5071
5278
  // src/cli/push-config.ts
5072
- async function pushToDashboard(dashboardUrl, siteId, apiKey, config) {
5279
+ async function pushToDashboard(dashboardUrl, siteId, apiKey, config2) {
5073
5280
  const pushUrl = `${dashboardUrl}/api/sites/${siteId}/sdk-config`;
5074
5281
  console.log(`Pushing config to ${pushUrl}...`);
5075
5282
  let response;
@@ -5080,7 +5287,7 @@ async function pushToDashboard(dashboardUrl, siteId, apiKey, config) {
5080
5287
  "Content-Type": "application/json",
5081
5288
  "X-API-Key": apiKey
5082
5289
  },
5083
- body: JSON.stringify({ config }),
5290
+ body: JSON.stringify({ config: config2 }),
5084
5291
  signal: AbortSignal.timeout(3e4)
5085
5292
  });
5086
5293
  } catch (error) {
@@ -5150,6 +5357,8 @@ var pushConfigCommand = new commander.Command("push-config").description("Push S
5150
5357
  });
5151
5358
 
5152
5359
  // src/cli/index.ts
5360
+ dotenv.config({ path: ".env.local" });
5361
+ dotenv.config({ path: ".env" });
5153
5362
  var program = new commander.Command();
5154
5363
  program.name("riverbankcms").description("Builder SDK CLI tools").version("0.1.0");
5155
5364
  program.addCommand(pushConfigCommand);