@riverbankcms/sdk 0.135.1 → 0.137.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 (220) hide show
  1. package/CHANGELOG.md +114 -0
  2. package/README.md +4 -8
  3. package/dist/{PreviewEditorSidebar-D4p_5TdY.mjs → PreviewEditorSidebar-Bn6cONPa.mjs} +8 -8
  4. package/dist/{PreviewEditorUI-D0cRoTX1.mjs → PreviewEditorUI-LAwXZPSx.mjs} +3 -3
  5. package/dist/{PreviewQueryProvider-DcgaESkm.mjs → PreviewQueryProvider-ztQUdsNo.mjs} +1 -1
  6. package/dist/{SdkPreviewModeRuntime-BKLtM3Nz.mjs → SdkPreviewModeRuntime-BbbGsI_v.mjs} +621 -238
  7. package/dist/{SiteChromeCustomizeContext-61X2m7QV.mjs → SiteChromeCustomizeContext-BbkNSt5a.mjs} +5041 -2407
  8. package/dist/_dts/ai/src/contracts/proposals.d.ts +18 -850
  9. package/dist/_dts/ai/src/contracts/siteStyleProposalSchemas.d.ts +412 -2316
  10. package/dist/_dts/ai/src/designer/themePatch.d.ts +5 -10
  11. package/dist/_dts/api/src/accessAdmin.d.ts +1 -9
  12. package/dist/_dts/api/src/accountApi.d.ts +49 -2
  13. package/dist/_dts/api/src/contentRuntime.d.ts +7 -4
  14. package/dist/_dts/api/src/domainConnection.d.ts +41 -0
  15. package/dist/_dts/api/src/endpoints.d.ts +201 -181
  16. package/dist/_dts/api/src/index.d.ts +5 -3
  17. package/dist/_dts/api/src/media.d.ts +19 -2
  18. package/dist/_dts/api/src/publicEventEndpointDefinitions.d.ts +7 -0
  19. package/dist/_dts/api/src/publicExperience/eventEndpoints.d.ts +26 -0
  20. package/dist/_dts/api/src/sdkBlockContentAdmission.d.ts +5 -0
  21. package/dist/_dts/api/src/sdkContracts.d.ts +2 -15
  22. package/dist/_dts/api/src/sdkMediaContracts.d.ts +40 -0
  23. package/dist/_dts/api/src/sdkMediaEndpointDefinitions.d.ts +61 -0
  24. package/dist/_dts/api/src/sdkThemeWire.d.ts +21 -436
  25. package/dist/_dts/api/src/siteInfrastructure.d.ts +17 -23
  26. package/dist/_dts/api/src/siteManagementEndpoints.d.ts +7 -21
  27. package/dist/_dts/api/src/sitePlatformEndpoints.d.ts +27 -3
  28. package/dist/_dts/api/src/siteRuntimeEndpoints.d.ts +37 -17
  29. package/dist/_dts/api/src/types.d.ts +1 -1
  30. package/dist/_dts/block-form/src/blockDraftSaveability.d.ts +2 -3
  31. package/dist/_dts/block-form/src/utils/slug.d.ts +6 -0
  32. package/dist/_dts/blocks/src/PageRenderer.d.ts +13 -7
  33. package/dist/_dts/blocks/src/contracts/proof-block-islands.d.ts +48 -0
  34. package/dist/_dts/blocks/src/definitions.d.ts +14 -0
  35. package/dist/_dts/blocks/src/index.d.ts +4 -3
  36. package/dist/_dts/blocks/src/pageRenderAdmission.d.ts +49 -0
  37. package/dist/_dts/blocks/src/pageRendererBlockSupport.d.ts +75 -0
  38. package/dist/_dts/blocks/src/pageRendererBoundaries.d.ts +2 -0
  39. package/dist/_dts/blocks/src/server.d.ts +2 -2
  40. package/dist/_dts/blocks/src/system/blocks/event-registration.d.ts +1 -1
  41. package/dist/_dts/blocks/src/system/data/index.d.ts +2 -0
  42. package/dist/_dts/blocks/src/system/data/publicLoaderCachePolicy.d.ts +78 -0
  43. package/dist/_dts/blocks/src/system/manifest/fieldValidation/index.d.ts +2 -18
  44. package/dist/_dts/blocks/src/system/manifest/validation.d.ts +103 -16
  45. package/dist/_dts/blocks/src/system/node/validation-constants.d.ts +1 -1
  46. package/dist/_dts/blocks/src/system/runtime/api/client.d.ts +4 -2
  47. package/dist/_dts/blocks/src/system/runtime/api/endpoints.d.ts +8 -0
  48. package/dist/_dts/blocks/src/system/runtime/api/index.d.ts +1 -1
  49. package/dist/_dts/blocks/src/system/runtime/api/types/events.d.ts +19 -0
  50. package/dist/_dts/blocks/src/system/runtime/components/registry.client.d.ts +52 -0
  51. package/dist/_dts/blocks/src/system/runtime/components/runtime-backed-blocks.d.ts +24 -0
  52. package/dist/_dts/blocks/src/system/runtime/hooks/useCalendarEvents.d.ts +47 -21
  53. package/dist/_dts/blocks/src/system/runtime/hooks/useCalendarEvents.state.d.ts +38 -9
  54. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/ErrorStep.d.ts +3 -5
  55. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/VerifyingTimeoutStep.d.ts +3 -1
  56. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/event-registration.headless.contract.d.ts +2 -0
  57. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/event-registration.headless.planning.d.ts +1 -1
  58. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/hooks.d.ts +2 -0
  59. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/registrationErrorPresentation.d.ts +5 -0
  60. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/registrationErrorRecovery.d.ts +25 -0
  61. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/registrationWizardContext.d.ts +4 -0
  62. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/useEventRegistrationStripeReturn.d.ts +8 -3
  63. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/useEventRegistrationSubmit.d.ts +2 -0
  64. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/useEventRegistrationWizard.d.ts +5 -2
  65. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/useWaitlistClaimRestoration.d.ts +2 -0
  66. package/dist/_dts/blocks/src/system/runtime/nodes/events/EventCalendar.client.d.ts +2 -2
  67. package/dist/_dts/blocks/src/system/runtime/nodes/events/EventCalendar.interactive.d.ts +40 -0
  68. package/dist/_dts/blocks/src/system/runtime/nodes/events/EventCombined.client.d.ts +2 -0
  69. package/dist/_dts/blocks/src/system/runtime/nodes/events/EventCombined.interactive.d.ts +43 -0
  70. package/dist/_dts/blocks/src/system/runtime/nodes/events/domain/capacity.d.ts +164 -0
  71. package/dist/_dts/blocks/src/system/runtime/nodes/events/domain/cta.d.ts +29 -0
  72. package/dist/_dts/blocks/src/system/runtime/nodes/events/domain/dates.d.ts +47 -0
  73. package/dist/_dts/blocks/src/system/runtime/nodes/events/domain/index.d.ts +14 -0
  74. package/dist/_dts/blocks/src/system/runtime/nodes/events/domain/people.d.ts +6 -0
  75. package/dist/_dts/blocks/src/system/runtime/nodes/events/domain/pricing.d.ts +31 -0
  76. package/dist/_dts/blocks/src/system/runtime/nodes/events/shared/CalendarDayCell.d.ts +3 -1
  77. package/dist/_dts/blocks/src/system/runtime/nodes/events/shared/CalendarNavigationChevron.d.ts +4 -0
  78. package/dist/_dts/blocks/src/system/runtime/nodes/events/shared/DayEventsModal.d.ts +2 -1
  79. package/dist/_dts/blocks/src/system/runtime/nodes/events/shared/EventModalContext.d.ts +3 -2
  80. package/dist/_dts/blocks/src/system/runtime/nodes/events/shared/OfferingTitle.d.ts +11 -0
  81. package/dist/_dts/blocks/src/system/runtime/nodes/events/shared/WeekAgendaView.d.ts +4 -1
  82. package/dist/_dts/blocks/src/system/runtime/nodes/events/shared/WeekBoardView.d.ts +31 -3
  83. package/dist/_dts/blocks/src/system/runtime/nodes/events/shared/WeekTimetableView.d.ts +3 -1
  84. package/dist/_dts/blocks/src/system/runtime/nodes/events/shared/eventCollectionContract.d.ts +9 -1
  85. package/dist/_dts/blocks/src/system/runtime/nodes/events/shared/index.d.ts +3 -1
  86. package/dist/_dts/blocks/src/system/runtime/nodes/events/shared/listingLayout.d.ts +23 -0
  87. package/dist/_dts/blocks/src/system/runtime/nodes/events/shared/timetableModel.d.ts +4 -3
  88. package/dist/_dts/blocks/src/system/transforms/registry/layout.d.ts +0 -1
  89. package/dist/_dts/calendar/src/grid.d.ts +24 -66
  90. package/dist/_dts/calendar/src/index.d.ts +2 -2
  91. package/dist/_dts/calendar/src/timetable.d.ts +5 -9
  92. package/dist/_dts/commerce/src/purchase/allocation.d.ts +11 -0
  93. package/dist/_dts/commerce/src/purchase/event-registration-errors.d.ts +57 -0
  94. package/dist/_dts/commerce/src/purchase/index.d.ts +3 -0
  95. package/dist/_dts/commerce/src/purchase/settlement.d.ts +33 -0
  96. package/dist/_dts/commerce/src/purchase/stripeCheckoutReturnUrl.d.ts +8 -0
  97. package/dist/_dts/content-model/src/templates/defaults.d.ts +6 -0
  98. package/dist/_dts/core/src/slug.d.ts +20 -5
  99. package/dist/_dts/db/src/generated/supabase/database.types.d.ts +8 -40
  100. package/dist/_dts/db/src/index.d.ts +1 -0
  101. package/dist/_dts/db/src/utils/postgrestFilter.d.ts +2 -0
  102. package/dist/_dts/media-core/src/bulkDeletePlanning.d.ts +4 -0
  103. package/dist/_dts/media-core/src/fileDomain.d.ts +3 -0
  104. package/dist/_dts/preview-next/src/client/blocks/EditablePageRenderer.d.ts +1 -1
  105. package/dist/_dts/preview-next/src/client/preview/PreviewShell.d.ts +1 -0
  106. package/dist/_dts/preview-next/src/client/preview/PreviewShellLayout.d.ts +1 -0
  107. package/dist/_dts/preview-next/src/client/preview/SiteChromeCustomizeContext.d.ts +3 -2
  108. package/dist/_dts/preview-next/src/client/preview/headerInteractionPaletteSurfaceState.d.ts +2 -1
  109. package/dist/_dts/preview-next/src/client/preview/siteChromeBrandPreferences.d.ts +1 -1
  110. package/dist/_dts/preview-next/src/client/preview/siteChromeQuickControlHistoryState.d.ts +2 -0
  111. package/dist/_dts/preview-next/src/client/preview/siteChromeQuickControlSaveRequest.d.ts +51 -0
  112. package/dist/_dts/preview-next/src/client/preview/siteChromeSavePlan.d.ts +24 -2
  113. package/dist/_dts/preview-next/src/client/preview/siteStylePreviewPlanner.d.ts +23 -1
  114. package/dist/_dts/preview-next/src/client/preview/usePersistedHeaderPreferences.d.ts +8 -0
  115. package/dist/_dts/sdk/src/client/publicLoaderCacheAccess.d.ts +6 -0
  116. package/dist/_dts/sdk/src/public-api/contracts.d.ts +7 -2
  117. package/dist/_dts/sdk/src/rendering/blocks-page-renderer.d.ts +10 -6
  118. package/dist/_dts/sdk/src/rendering/components/Block.shared.d.ts +0 -1
  119. package/dist/_dts/sdk/src/rendering/components/pageShared.d.ts +4 -4
  120. package/dist/_dts/sdk/src/rendering/helpers/footerPageOutline.d.ts +3 -0
  121. package/dist/_dts/sdk/src/rendering/helpers/loadContent.d.ts +3 -0
  122. package/dist/_dts/sdk/src/rendering/helpers/loadPage.d.ts +1 -1
  123. package/dist/_dts/sdk/src/rendering/helpers/pageRuntimeSupport.d.ts +17 -0
  124. package/dist/_dts/sdk/src/rendering/helpers/templateContent.d.ts +9 -3
  125. package/dist/_dts/sdk/src/rendering/hooks/usePage.d.ts +1 -1
  126. package/dist/_dts/sdk/src/rendering/index.d.ts +2 -2
  127. package/dist/_dts/sdk/src/rendering/overrideResolution.d.ts +7 -4
  128. package/dist/_dts/sdk/src/version.d.ts +1 -1
  129. package/dist/_dts/theme-core/src/cards/types.d.ts +8 -8
  130. package/dist/_dts/theme-core/src/data.d.ts +12 -0
  131. package/dist/_dts/theme-core/src/generated/containerResponsiveThemeCss.d.ts +1 -1
  132. package/dist/_dts/theme-core/src/gradients/types.d.ts +5 -0
  133. package/dist/_dts/theme-core/src/header/interactionPalette.d.ts +25 -2
  134. package/dist/_dts/theme-core/src/header/legacyHeaderVariant.d.ts +2 -6
  135. package/dist/_dts/theme-core/src/mock-themes/all.d.ts +9 -810
  136. package/dist/_dts/theme-core/src/schema.d.ts +460 -1285
  137. package/dist/_dts/theme-core/src/site-styles/compilationPlan.d.ts +43 -3
  138. package/dist/_dts/theme-core/src/site-styles/curatedSiteStyles.d.ts +223 -1
  139. package/dist/_dts/theme-core/src/site-styles/headerBrandPresentation.d.ts +63 -0
  140. package/dist/_dts/theme-core/src/site-styles/headerBytesComparison.d.ts +30 -0
  141. package/dist/_dts/theme-core/src/site-styles/headerInteractionPalette.d.ts +50 -1
  142. package/dist/_dts/theme-core/src/site-styles/headerLayoutCatalog.d.ts +48 -38
  143. package/dist/_dts/theme-core/src/site-styles/headerLayoutStyles.d.ts +10 -0
  144. package/dist/_dts/theme-core/src/site-styles/headerLayoutTypes.d.ts +8 -5
  145. package/dist/_dts/theme-core/src/site-styles/headerOpeningForeground.d.ts +4 -4
  146. package/dist/_dts/theme-core/src/site-styles/headerPreferences.d.ts +162 -0
  147. package/dist/_dts/theme-core/src/site-styles/headerPreferencesPersistence.d.ts +73 -0
  148. package/dist/_dts/theme-core/src/site-styles/headerRecompile.d.ts +118 -0
  149. package/dist/_dts/theme-core/src/site-styles/headerStyleCompiler.d.ts +25 -2
  150. package/dist/_dts/theme-core/src/site-styles/headerStyleDefaults.d.ts +465 -0
  151. package/dist/_dts/theme-core/src/site-styles/headerStyleRecipe.d.ts +168 -0
  152. package/dist/_dts/theme-core/src/site-styles/index.d.ts +5 -0
  153. package/dist/_dts/theme-core/src/site-styles/previewCompiler.d.ts +1 -1
  154. package/dist/_dts/theme-core/src/site-styles/siteChromeBackgrounds.d.ts +20 -1
  155. package/dist/_dts/theme-core/src/site-styles/siteChromeNavPreferences.d.ts +43 -2
  156. package/dist/client/{BookingConsentAgreement-Bz9znbpn.mjs → BookingConsentAgreement--_fUCZe-.mjs} +2 -2
  157. package/dist/client/{DiscountCodeDisclosure-35Ru8yB9.mjs → DiscountCodeDisclosure-BMipSoDJ.mjs} +1 -1
  158. package/dist/client/{EventCombined.client-BKB0ZxRR.mjs → EventCombined.client-CwJOxn7A.mjs} +274 -278
  159. package/dist/client/{EventListing.client-Dx8I21ok.mjs → EventListing.client-eVwiylPW.mjs} +2 -2
  160. package/dist/client/{LeafletStaticMap-CEWx-Bhe.mjs → LeafletStaticMap-IKenna5u.mjs} +1 -1
  161. package/dist/client/{accordion-KIDsKTwS.mjs → accordion-MGAO3PuE.mjs} +1 -1
  162. package/dist/client/{bookingForm-D96Rmp-5.mjs → bookingForm-CudlQPUu.mjs} +5 -5
  163. package/dist/client/bookings.mjs +476 -98
  164. package/dist/client/capacity-BW00PO7s.mjs +271 -0
  165. package/dist/client/{carousel-YwUgyL66.mjs → carousel-BrbGJ2Jl.mjs} +1 -1
  166. package/dist/client/{cart-BbwuNrUT.mjs → cart-Bq5eU0xZ.mjs} +3 -3
  167. package/dist/client/{checkout-hp1ucmEY.mjs → checkout-BAFVRdrK.mjs} +3 -3
  168. package/dist/client/client.mjs +4641 -2721
  169. package/dist/client/{context-DljQn8L5.mjs → context-DCkiHVdD.mjs} +20 -5
  170. package/dist/client/dates-2RPXHQy4.mjs +264 -0
  171. package/dist/client/{eventCalendar-DU4iNLpo.mjs → eventCalendar-B0O-hPF7.mjs} +261 -126
  172. package/dist/client/{eventCombined-CZuupW8m.mjs → eventCombined-DCkDaoDI.mjs} +3 -3
  173. package/dist/client/{eventDetails-DPQWwCIi.mjs → eventDetails-D8GG2dnD.mjs} +7 -4
  174. package/dist/client/{eventListing-C7oMidwS.mjs → eventListing-3ssV_bxz.mjs} +3 -3
  175. package/dist/client/{eventRegistration-zEiunlUs.mjs → eventRegistration-bPQo3kVw.mjs} +610 -313
  176. package/dist/client/{form-D2KIyCVJ.mjs → form-qMv8RDkk.mjs} +2 -2
  177. package/dist/client/{headerSection-CK5uChUK.mjs → headerSection-CH0Sc5hX.mjs} +2 -2
  178. package/dist/client/hooks.mjs +2514 -308
  179. package/dist/client/{locationMap-DLQNH1ln.mjs → locationMap-0_xlBnNI.mjs} +2 -2
  180. package/dist/client/{newsletterForm-ZuhIG7fZ.mjs → newsletterForm-NUW7m9RI.mjs} +2 -2
  181. package/dist/client/{productDetail-7hIcGeae.mjs → productDetail-FBEOM7jW.mjs} +3 -3
  182. package/dist/client/{productList-T4Cii1ot.mjs → productList-R0OSGhnn.mjs} +3 -3
  183. package/dist/client/{propsDecoder-mB0aC_Fi.mjs → propsDecoder-pYvx_Srl.mjs} +179 -401
  184. package/dist/client/rendering/client.mjs +3496 -2223
  185. package/dist/client/rendering/islands.mjs +17 -17
  186. package/dist/client/rendering.mjs +4400 -2510
  187. package/dist/client/{scrollToBlock-Du5mHgvL.mjs → scrollToBlock-ByQv96M2.mjs} +1 -1
  188. package/dist/client/{shop-B5zAcKZn.mjs → shop-RUG9DmRE.mjs} +6 -6
  189. package/dist/client/{shop-commerce-BKBZB6l5.mjs → shop-commerce-B76Jz23B.mjs} +5 -5
  190. package/dist/client/{shop-commerce-store-Cq4qlNtQ.mjs → shop-commerce-store-QuQNdDsD.mjs} +1 -1
  191. package/dist/client/{shopShared-DYGzEs9D.mjs → shopShared-vXU1omwJ.mjs} +3 -3
  192. package/dist/client/{useEventFilters-Bphkda8m.mjs → useEventFilters-ByuU6HLA.mjs} +33 -18
  193. package/dist/preview-next/before-render.mjs +86 -51
  194. package/dist/preview-next/client/runtime.mjs +4 -4
  195. package/dist/preview-next/keepAlive.mjs +86 -51
  196. package/dist/preview-next/middleware.mjs +86 -51
  197. package/dist/{previewAuthStore-C7ksxt5b.mjs → previewAuthStore-L_cKW8Sb.mjs} +86 -51
  198. package/dist/{sdk-runtime-BbIC8G4J.mjs → sdk-runtime-sUMr5d70.mjs} +2 -2
  199. package/dist/{separator-CadhAtI7.mjs → separator-BIHkie5i.mjs} +1 -1
  200. package/dist/server/components.mjs +4397 -2960
  201. package/dist/server/config-validation.mjs +354 -145
  202. package/dist/server/config.mjs +354 -145
  203. package/dist/server/data.mjs +354 -144
  204. package/dist/server/index.mjs +201 -55
  205. package/dist/server/manifest.mjs +0 -1
  206. package/dist/server/next.mjs +4046 -2611
  207. package/dist/server/page-converter.mjs +225 -122
  208. package/dist/server/prebuild.mjs +1 -1
  209. package/dist/server/rendering/server.mjs +4105 -2636
  210. package/dist/server/rendering.mjs +4522 -3087
  211. package/dist/server/routing.mjs +3289 -1637
  212. package/dist/server/server.mjs +23175 -21079
  213. package/dist/server/theme-bridge.mjs +708 -272
  214. package/dist/server/theme.mjs +29 -25
  215. package/dist/styles/index.css +624 -181
  216. package/package.json +1 -1
  217. package/dist/_dts/blocks/src/system/runtime/nodes/events/shared/eventCapacity.d.ts +0 -48
  218. package/dist/_dts/blocks/src/system/runtime/nodes/events/shared/pricing.d.ts +0 -17
  219. package/dist/_dts/blocks/src/system/runtime/nodes/events/shared/utils.d.ts +0 -100
  220. /package/dist/_dts/blocks/src/system/runtime/nodes/events/{shared → domain}/destination.d.ts +0 -0
@@ -1200,6 +1200,13 @@ const PUBLIC_WHOLE_SERIES_ENDPOINT_DEFINITIONS = defineEndpointDefinitions({
1200
1200
  //#endregion
1201
1201
  //#region ../api/src/publicEventEndpointDefinitions.ts
1202
1202
  const PUBLIC_EVENT_ENDPOINT_DEFINITIONS = defineEndpointDefinitions({
1203
+ resolvePublicEventCheckoutReturn: {
1204
+ path: "/public/sites/{siteId}/events/checkout-return",
1205
+ method: "POST",
1206
+ tags: [TAG_TEMPLATES.publicEvents, "event-checkout"],
1207
+ auth: "public",
1208
+ responseKind: "json"
1209
+ },
1203
1210
  getPublicEventRegistrationOptions: {
1204
1211
  path: "/public/sites/{siteId}/events/registration-options",
1205
1212
  method: "POST",
@@ -1584,6 +1591,83 @@ const SDK_ENDPOINT_DEFINITIONS = defineEndpointDefinitions({
1584
1591
  errors: ["validation:invalid_input", "server:internal_error"]
1585
1592
  }
1586
1593
  });
1594
+ //#endregion
1595
+ //#region ../api/src/sdkMediaEndpointDefinitions.ts
1596
+ const SDK_MEDIA_ENDPOINT_DEFINITIONS = {
1597
+ sdkMediaExists: {
1598
+ path: "/sdk/{siteId}/media/exists",
1599
+ method: "POST",
1600
+ errors: ["validation:invalid_input"],
1601
+ auth: "service",
1602
+ responseKind: "json"
1603
+ },
1604
+ sdkMediaFindByChecksum: {
1605
+ path: "/sdk/{siteId}/media/find-by-checksum",
1606
+ method: "POST",
1607
+ errors: ["server:internal_error"],
1608
+ auth: "service",
1609
+ responseKind: "json"
1610
+ },
1611
+ sdkMediaResolve: {
1612
+ path: "/sdk/{siteId}/media/resolve",
1613
+ method: "POST",
1614
+ errors: ["server:internal_error"],
1615
+ auth: "service",
1616
+ responseKind: "json"
1617
+ },
1618
+ sdkMediaList: {
1619
+ path: "/sdk/{siteId}/media",
1620
+ method: "GET",
1621
+ auth: "service",
1622
+ responseKind: "json"
1623
+ },
1624
+ sdkMediaDownload: {
1625
+ path: "/sdk/{siteId}/media/{identifier}/download",
1626
+ method: "GET",
1627
+ auth: "service",
1628
+ responseKind: "json"
1629
+ },
1630
+ sdkMediaDeletePreview: {
1631
+ path: "/sdk/{siteId}/media/{identifier}/delete-preview",
1632
+ method: "POST",
1633
+ auth: "service",
1634
+ responseKind: "json"
1635
+ },
1636
+ sdkMediaDeleteApply: {
1637
+ path: "/sdk/{siteId}/media/{identifier}",
1638
+ method: "DELETE",
1639
+ auth: "service",
1640
+ responseKind: "json"
1641
+ },
1642
+ sdkMediaUploadUrl: {
1643
+ path: "/sdk/{siteId}/media/upload-url",
1644
+ method: "POST",
1645
+ errors: [
1646
+ "resource:not_found",
1647
+ "validation:invalid_input",
1648
+ "resource:conflict",
1649
+ "validation:payload_too_large",
1650
+ "billing:plan_limit_exceeded",
1651
+ "server:internal_error"
1652
+ ],
1653
+ auth: "service",
1654
+ responseKind: "json"
1655
+ },
1656
+ sdkMediaUploadFinalize: {
1657
+ path: "/sdk/{siteId}/media/upload",
1658
+ method: "POST",
1659
+ errors: [
1660
+ "resource:not_found",
1661
+ "validation:invalid_input",
1662
+ "resource:conflict",
1663
+ "validation:payload_too_large",
1664
+ "billing:plan_limit_exceeded",
1665
+ "server:internal_error"
1666
+ ],
1667
+ auth: "service",
1668
+ responseKind: "json"
1669
+ }
1670
+ };
1587
1671
  ({
1588
1672
  ...ACCOUNT_ENDPOINT_DEFINITIONS,
1589
1673
  ...PUBLIC_EVENT_ENDPOINT_DEFINITIONS,
@@ -1592,7 +1676,7 @@ const SDK_ENDPOINT_DEFINITIONS = defineEndpointDefinitions({
1592
1676
  ...NEWSLETTER_ENDPOINT_DEFINITIONS,
1593
1677
  ...APPOINTMENT_ENDPOINT_DEFINITIONS,
1594
1678
  ...EVENT_SERIES_REGISTRATION_ENDPOINT_DEFINITIONS
1595
- }, TAG_TEMPLATES.siteCollection, TAG_TEMPLATES.siteById, TAG_TEMPLATES.siteBySlug, TAG_TEMPLATES.siteByDomain, TAG_TEMPLATES.siteCollection, TAG_TEMPLATES.siteById, TAG_TEMPLATES.siteBySlug, TAG_TEMPLATES.siteByDomain, TAG_TEMPLATES.siteCollection, TAG_TEMPLATES.pageSurfaceById, TAG_TEMPLATES.siteCollection, TAG_TEMPLATES.pageSurfaceById, TAG_TEMPLATES.siteCollection, TAG_TEMPLATES.siteRoutableContent, TAG_TEMPLATES.pageSurfaceById, TAG_TEMPLATES.siteCollection, TAG_TEMPLATES.siteRoutableContent, TAG_TEMPLATES.pageSurfaceById, TAG_TEMPLATES.workflowRunById, TAG_TEMPLATES.siteCollection, TAG_TEMPLATES.siteRoutableContent, TAG_TEMPLATES.routeById, TAG_TEMPLATES.site, TAG_TEMPLATES.siteSettings, TAG_TEMPLATES.siteCollection, TAG_TEMPLATES.site, TAG_TEMPLATES.siteSettings, TAG_TEMPLATES.siteCollection, TAG_TEMPLATES.site, TAG_TEMPLATES.siteSettings, TAG_TEMPLATES.siteCollection, TAG_TEMPLATES.site, TAG_TEMPLATES.site, TAG_TEMPLATES.siteSettings, TAG_TEMPLATES.site, TAG_TEMPLATES.siteSettings, TAG_TEMPLATES.site, TAG_TEMPLATES.siteSettings, TAG_TEMPLATES.site, TAG_TEMPLATES.siteSettings, TAG_TEMPLATES.site, TAG_TEMPLATES.siteSettings, TAG_TEMPLATES.site, TAG_TEMPLATES.siteSettings, TAG_TEMPLATES.site, TAG_TEMPLATES.siteSettings, TAG_TEMPLATES.site, TAG_TEMPLATES.siteSettings, TAG_TEMPLATES.site, TAG_TEMPLATES.siteSettings, TAG_TEMPLATES.site, TAG_TEMPLATES.siteSettings, TAG_TEMPLATES.site, TAG_TEMPLATES.siteSettings, TAG_TEMPLATES.site, TAG_TEMPLATES.siteSettings, TAG_TEMPLATES.site, TAG_TEMPLATES.siteSettings, TAG_TEMPLATES.site, TAG_TEMPLATES.siteSettings, TAG_TEMPLATES.site, TAG_TEMPLATES.siteSettings, TAG_TEMPLATES.site, TAG_TEMPLATES.siteSettings, TAG_TEMPLATES.site, TAG_TEMPLATES.siteSettings, TAG_TEMPLATES.site, TAG_TEMPLATES.siteSettings, TAG_TEMPLATES.site, TAG_TEMPLATES.siteSettings, TAG_TEMPLATES.site, TAG_TEMPLATES.siteSettings, TAG_TEMPLATES.site, TAG_TEMPLATES.siteSettings, TAG_TEMPLATES.site, TAG_TEMPLATES.siteSettings, TAG_TEMPLATES.site, TAG_TEMPLATES.siteSettings, TAG_TEMPLATES.site, TAG_TEMPLATES.siteSettings, TAG_TEMPLATES.site, TAG_TEMPLATES.siteSettings, TAG_TEMPLATES.site, TAG_TEMPLATES.siteSettings, TAG_TEMPLATES.siteCollection, TAG_TEMPLATES.siteNavigation, TAG_TEMPLATES.siteCollection, TAG_TEMPLATES.siteNavigation, TAG_TEMPLATES.siteCollection, TAG_TEMPLATES.siteNavigation, TAG_TEMPLATES.navigationMenuById, TAG_TEMPLATES.siteCollection, TAG_TEMPLATES.siteNavigation, TAG_TEMPLATES.navigationMenuById, TAG_TEMPLATES.siteCollection, TAG_TEMPLATES.siteNavigation, TAG_TEMPLATES.navigationMenuById, TAG_TEMPLATES.siteCollection, TAG_TEMPLATES.siteNavigation, TAG_TEMPLATES.navigationMenuById, TAG_TEMPLATES.siteCollection, TAG_TEMPLATES.siteNavigation, TAG_TEMPLATES.navigationMenuById, TAG_TEMPLATES.navigationItemById, TAG_TEMPLATES.siteCollection, TAG_TEMPLATES.siteNavigation, TAG_TEMPLATES.navigationMenuById, TAG_TEMPLATES.navigationItemById, TAG_TEMPLATES.siteCollection, TAG_TEMPLATES.siteNavigation, TAG_TEMPLATES.navigationMenuById, TAG_TEMPLATES.siteRoutableContent, TAG_TEMPLATES.siteRoutableContent, TAG_TEMPLATES.siteRoutableContent, TAG_TEMPLATES.siteSettings, TAG_TEMPLATES.siteCollection, TAG_TEMPLATES.siteTheme, TAG_TEMPLATES.siteCollection, TAG_TEMPLATES.siteTheme, TAG_TEMPLATES.siteCollection, TAG_TEMPLATES.siteTheme, TAG_TEMPLATES.siteCollection, TAG_TEMPLATES.pageSurfaceById, TAG_TEMPLATES.siteCollection, TAG_TEMPLATES.pageSurfaceById, TAG_TEMPLATES.siteCollection, TAG_TEMPLATES.pageSurfaceById, TAG_TEMPLATES.siteCollection, TAG_TEMPLATES.pageSurfaceById, TAG_TEMPLATES.siteRoutableContent, TAG_TEMPLATES.pageById, TAG_TEMPLATES.siteRoutableContent, TAG_TEMPLATES.pageById, TAG_TEMPLATES.siteRoutableContent, TAG_TEMPLATES.pageById, TAG_TEMPLATES.siteRoutableContent, TAG_TEMPLATES.pageById, TAG_TEMPLATES.siteRoutableContent, TAG_TEMPLATES.pageById, TAG_TEMPLATES.siteRoutableContent, TAG_TEMPLATES.pageById, TAG_TEMPLATES.siteRoutableContent, TAG_TEMPLATES.pageById, TAG_TEMPLATES.siteRoutableContent, TAG_TEMPLATES.pageById, TAG_TEMPLATES.siteRoutableContent, TAG_TEMPLATES.pageById, TAG_TEMPLATES.siteRoutableContent, TAG_TEMPLATES.siteRoutableContent, TAG_TEMPLATES.siteRedirects, TAG_TEMPLATES.siteRouteOutcomes, TAG_TEMPLATES.siteRoutableContent, TAG_TEMPLATES.siteRedirects, TAG_TEMPLATES.siteRouteOutcomes, TAG_TEMPLATES.siteRoutableContent, TAG_TEMPLATES.siteForms, TAG_TEMPLATES.siteForms, TAG_TEMPLATES.siteForms, TAG_TEMPLATES.siteForms, TAG_TEMPLATES.siteForms, TAG_TEMPLATES.siteForms, TAG_TEMPLATES.formSubmissionsBySlug, TAG_TEMPLATES.siteForms, TAG_TEMPLATES.formSubmissionsBySlug, TAG_TEMPLATES.siteForms, TAG_TEMPLATES.formSubmissionsBySlug, TAG_TEMPLATES.formById, TAG_TEMPLATES.siteForms, TAG_TEMPLATES.publicEvents, TAG_TEMPLATES.publicEventFilters, { ...PUBLIC_WHOLE_SERIES_ENDPOINT_DEFINITIONS }, TAG_TEMPLATES.publicTeamMembers, TAG_TEMPLATES.publicWholeSeriesOfferings, TAG_TEMPLATES.publicEvents, TAG_TEMPLATES.publicPasses, TAG_TEMPLATES.publicMemberships, TAG_TEMPLATES.publicProducts, TAG_TEMPLATES.publicShop, TAG_TEMPLATES.publicCheckout, TAG_TEMPLATES.siteRoutableContent, TAG_TEMPLATES.publicEvents, TAG_TEMPLATES.publicEvents, TAG_TEMPLATES.publicEvents, TAG_TEMPLATES.publicEvents, TAG_TEMPLATES.publicEvents, { ...EVENT_MANAGEMENT_ENDPOINT_DEFINITIONS }, { ...SDK_ENDPOINT_DEFINITIONS }, { ...SDK_ENTRY_READ_ENDPOINTS };
1679
+ }, TAG_TEMPLATES.siteCollection, TAG_TEMPLATES.siteById, TAG_TEMPLATES.siteBySlug, TAG_TEMPLATES.siteByDomain, TAG_TEMPLATES.siteCollection, TAG_TEMPLATES.siteById, TAG_TEMPLATES.siteBySlug, TAG_TEMPLATES.siteByDomain, TAG_TEMPLATES.siteCollection, TAG_TEMPLATES.pageSurfaceById, TAG_TEMPLATES.siteCollection, TAG_TEMPLATES.pageSurfaceById, TAG_TEMPLATES.siteCollection, TAG_TEMPLATES.siteRoutableContent, TAG_TEMPLATES.pageSurfaceById, TAG_TEMPLATES.siteCollection, TAG_TEMPLATES.siteRoutableContent, TAG_TEMPLATES.pageSurfaceById, TAG_TEMPLATES.workflowRunById, TAG_TEMPLATES.siteCollection, TAG_TEMPLATES.siteRoutableContent, TAG_TEMPLATES.routeById, TAG_TEMPLATES.site, TAG_TEMPLATES.siteSettings, TAG_TEMPLATES.siteCollection, TAG_TEMPLATES.site, TAG_TEMPLATES.siteSettings, TAG_TEMPLATES.siteCollection, TAG_TEMPLATES.site, TAG_TEMPLATES.siteSettings, TAG_TEMPLATES.siteCollection, TAG_TEMPLATES.site, TAG_TEMPLATES.site, TAG_TEMPLATES.siteSettings, TAG_TEMPLATES.site, TAG_TEMPLATES.siteSettings, TAG_TEMPLATES.site, TAG_TEMPLATES.siteSettings, TAG_TEMPLATES.site, TAG_TEMPLATES.siteSettings, TAG_TEMPLATES.site, TAG_TEMPLATES.siteSettings, TAG_TEMPLATES.site, TAG_TEMPLATES.siteSettings, TAG_TEMPLATES.site, TAG_TEMPLATES.siteSettings, TAG_TEMPLATES.site, TAG_TEMPLATES.siteSettings, TAG_TEMPLATES.site, TAG_TEMPLATES.siteSettings, TAG_TEMPLATES.site, TAG_TEMPLATES.siteSettings, TAG_TEMPLATES.site, TAG_TEMPLATES.siteSettings, TAG_TEMPLATES.site, TAG_TEMPLATES.siteSettings, TAG_TEMPLATES.site, TAG_TEMPLATES.siteSettings, TAG_TEMPLATES.site, TAG_TEMPLATES.siteSettings, TAG_TEMPLATES.site, TAG_TEMPLATES.siteSettings, TAG_TEMPLATES.site, TAG_TEMPLATES.siteSettings, TAG_TEMPLATES.site, TAG_TEMPLATES.siteSettings, TAG_TEMPLATES.site, TAG_TEMPLATES.siteSettings, TAG_TEMPLATES.site, TAG_TEMPLATES.siteSettings, TAG_TEMPLATES.site, TAG_TEMPLATES.siteSettings, TAG_TEMPLATES.site, TAG_TEMPLATES.siteSettings, TAG_TEMPLATES.site, TAG_TEMPLATES.siteSettings, TAG_TEMPLATES.site, TAG_TEMPLATES.siteSettings, TAG_TEMPLATES.site, TAG_TEMPLATES.siteSettings, TAG_TEMPLATES.site, TAG_TEMPLATES.siteSettings, TAG_TEMPLATES.site, TAG_TEMPLATES.siteSettings, TAG_TEMPLATES.siteCollection, TAG_TEMPLATES.siteNavigation, TAG_TEMPLATES.siteCollection, TAG_TEMPLATES.siteNavigation, TAG_TEMPLATES.siteCollection, TAG_TEMPLATES.siteNavigation, TAG_TEMPLATES.navigationMenuById, TAG_TEMPLATES.siteCollection, TAG_TEMPLATES.siteNavigation, TAG_TEMPLATES.navigationMenuById, TAG_TEMPLATES.siteCollection, TAG_TEMPLATES.siteNavigation, TAG_TEMPLATES.navigationMenuById, TAG_TEMPLATES.siteCollection, TAG_TEMPLATES.siteNavigation, TAG_TEMPLATES.navigationMenuById, TAG_TEMPLATES.siteCollection, TAG_TEMPLATES.siteNavigation, TAG_TEMPLATES.navigationMenuById, TAG_TEMPLATES.navigationItemById, TAG_TEMPLATES.siteCollection, TAG_TEMPLATES.siteNavigation, TAG_TEMPLATES.navigationMenuById, TAG_TEMPLATES.navigationItemById, TAG_TEMPLATES.siteCollection, TAG_TEMPLATES.siteNavigation, TAG_TEMPLATES.navigationMenuById, TAG_TEMPLATES.siteRoutableContent, TAG_TEMPLATES.siteRoutableContent, TAG_TEMPLATES.siteRoutableContent, TAG_TEMPLATES.siteSettings, TAG_TEMPLATES.siteCollection, TAG_TEMPLATES.siteTheme, TAG_TEMPLATES.siteCollection, TAG_TEMPLATES.siteTheme, TAG_TEMPLATES.siteCollection, TAG_TEMPLATES.siteTheme, TAG_TEMPLATES.siteCollection, TAG_TEMPLATES.pageSurfaceById, TAG_TEMPLATES.siteCollection, TAG_TEMPLATES.pageSurfaceById, TAG_TEMPLATES.siteCollection, TAG_TEMPLATES.pageSurfaceById, TAG_TEMPLATES.siteCollection, TAG_TEMPLATES.pageSurfaceById, TAG_TEMPLATES.siteRoutableContent, TAG_TEMPLATES.pageById, TAG_TEMPLATES.siteRoutableContent, TAG_TEMPLATES.pageById, TAG_TEMPLATES.siteRoutableContent, TAG_TEMPLATES.pageById, TAG_TEMPLATES.siteRoutableContent, TAG_TEMPLATES.pageById, TAG_TEMPLATES.siteRoutableContent, TAG_TEMPLATES.pageById, TAG_TEMPLATES.siteRoutableContent, TAG_TEMPLATES.pageById, TAG_TEMPLATES.siteRoutableContent, TAG_TEMPLATES.pageById, TAG_TEMPLATES.siteRoutableContent, TAG_TEMPLATES.pageById, TAG_TEMPLATES.siteRoutableContent, TAG_TEMPLATES.pageById, TAG_TEMPLATES.siteRoutableContent, TAG_TEMPLATES.siteRoutableContent, TAG_TEMPLATES.siteRedirects, TAG_TEMPLATES.siteRouteOutcomes, TAG_TEMPLATES.siteRoutableContent, TAG_TEMPLATES.siteRedirects, TAG_TEMPLATES.siteRouteOutcomes, TAG_TEMPLATES.siteRoutableContent, TAG_TEMPLATES.siteForms, TAG_TEMPLATES.siteForms, TAG_TEMPLATES.siteForms, TAG_TEMPLATES.siteForms, TAG_TEMPLATES.siteForms, TAG_TEMPLATES.siteForms, TAG_TEMPLATES.formSubmissionsBySlug, TAG_TEMPLATES.siteForms, TAG_TEMPLATES.formSubmissionsBySlug, TAG_TEMPLATES.siteForms, TAG_TEMPLATES.formSubmissionsBySlug, TAG_TEMPLATES.formById, TAG_TEMPLATES.siteForms, TAG_TEMPLATES.publicEvents, TAG_TEMPLATES.publicEventFilters, { ...PUBLIC_WHOLE_SERIES_ENDPOINT_DEFINITIONS }, TAG_TEMPLATES.publicTeamMembers, TAG_TEMPLATES.publicWholeSeriesOfferings, TAG_TEMPLATES.publicEvents, TAG_TEMPLATES.publicPasses, TAG_TEMPLATES.publicMemberships, TAG_TEMPLATES.publicProducts, TAG_TEMPLATES.publicShop, TAG_TEMPLATES.publicCheckout, TAG_TEMPLATES.siteRoutableContent, TAG_TEMPLATES.publicEvents, TAG_TEMPLATES.publicEvents, TAG_TEMPLATES.publicEvents, TAG_TEMPLATES.publicEvents, TAG_TEMPLATES.publicEvents, { ...EVENT_MANAGEMENT_ENDPOINT_DEFINITIONS }, { ...SDK_MEDIA_ENDPOINT_DEFINITIONS }, { ...SDK_ENDPOINT_DEFINITIONS }, { ...SDK_ENTRY_READ_ENDPOINTS };
1596
1680
  z.object({ refreshToken: z.string().min(1).max(4096) }).strict();
1597
1681
  z.object({
1598
1682
  accessToken: z.string().min(1).max(4096),
@@ -1709,7 +1793,7 @@ const parseUserId = (value) => parseUuidBrand(value, unsafeAsUserId);
1709
1793
  const parseEventSeriesId = (value) => parseUuidBrand(value, unsafeAsEventSeriesId);
1710
1794
  //#endregion
1711
1795
  //#region ../core/src/assert-never.ts
1712
- function assertNever$2(value, message = "Unexpected variant") {
1796
+ function assertNever$3(value, message = "Unexpected variant") {
1713
1797
  throw new Error(`${message}: ${String(value)}`);
1714
1798
  }
1715
1799
  //#endregion
@@ -1798,7 +1882,7 @@ function createEventSeriesPageSurfaceSubject(eventSeriesId, bookingScope) {
1798
1882
  };
1799
1883
  }
1800
1884
  function parseEventPageSurfaceSubject(value) {
1801
- if (!isRecord$10(value)) return err({ kind: "notObject" });
1885
+ if (!isRecord$14(value)) return err({ kind: "notObject" });
1802
1886
  switch (value.kind) {
1803
1887
  case "eventSeries": {
1804
1888
  const eventSeriesId = parseEventSeriesId(value.eventSeriesId);
@@ -1843,7 +1927,7 @@ function parseEventPageSurfaceSubject(value) {
1843
1927
  });
1844
1928
  }
1845
1929
  }
1846
- function isRecord$10(value) {
1930
+ function isRecord$14(value) {
1847
1931
  return typeof value === "object" && value !== null && !Array.isArray(value);
1848
1932
  }
1849
1933
  //#endregion
@@ -1931,6 +2015,9 @@ function getZonedDateTimeParts(date, timeZone) {
1931
2015
  function formatDateKey(date) {
1932
2016
  return `${String(date.year).padStart(4, "0")}-${String(date.month).padStart(2, "0")}-${String(date.day).padStart(2, "0")}`;
1933
2017
  }
2018
+ function toBookingTimeZone(timeZone) {
2019
+ return getSafeTimeZone(timeZone);
2020
+ }
1934
2021
  function parseUtcIsoDateTimeString(value) {
1935
2022
  const normalized = value.trim();
1936
2023
  const hasUtcSuffix = normalized.endsWith("Z");
@@ -2438,7 +2525,7 @@ const entryLinkSchema$1 = z.object({
2438
2525
  contentType: z.string().min(1),
2439
2526
  identifier: z.string().min(1)
2440
2527
  }).passthrough();
2441
- z.union([
2528
+ const linkSchema = z.union([
2442
2529
  internalLinkSchema,
2443
2530
  externalLinkSchema$1,
2444
2531
  customLinkSchema$1,
@@ -8446,6 +8533,11 @@ const gradientConfigSchema = z.object({
8446
8533
  position: z.string().optional(),
8447
8534
  stops: z.array(gradientStopSchema).min(2)
8448
8535
  });
8536
+ const gradientsSchema = z.object({
8537
+ button: z.string().min(1).nullable().optional(),
8538
+ hero: z.string().min(1).nullable().optional(),
8539
+ background: z.string().min(1).nullable().optional()
8540
+ });
8449
8541
  //#endregion
8450
8542
  //#region ../theme-core/src/interactive/baseSchema.ts
8451
8543
  /**
@@ -8889,7 +8981,7 @@ function asDesignSystemTemplateId(value) {
8889
8981
  function makeBoundaryId(previousBlockId, nextBlockId) {
8890
8982
  return asDesignBoundaryId(`boundary:${encodeURIComponent(previousBlockId)}:${encodeURIComponent(nextBlockId)}`);
8891
8983
  }
8892
- function assertNever$1(value, message = "Unhandled Theme V2 variant") {
8984
+ function assertNever$2(value, message = "Unhandled Theme V2 variant") {
8893
8985
  throw new Error(`${message}: ${String(value)}`);
8894
8986
  }
8895
8987
  function brandNonEmptyString(value, label) {
@@ -9146,12 +9238,8 @@ const legacyHeaderVariantValues = Object.keys(legacyHeaderVariants);
9146
9238
  * `@riverbankcms/api` is `z.input<typeof themeSchema>`, so a widened input here
9147
9239
  * would leak straight into a public type.
9148
9240
  *
9149
- * Not every read of a persisted theme goes through the schema:
9150
- * `resolveRuntimeTheme` in
9151
- * `apps/dashboard/src/server/services/sites/runtimeSiteResponse.ts` returns the
9152
- * raw database JSON asserted as `Theme` with no parse, so a theme still
9153
- * carrying a legacy spelling reaches the theme editor un-aliased. That gap
9154
- * predates this alias and is harmless while persisted usage is zero.
9241
+ * Persisted runtime themes are admitted through `parseThemeForPersistence`
9242
+ * before reaching consumers, so legacy spellings normalize at that boundary.
9155
9243
  */
9156
9244
  const headerVariantWithLegacyAliasSchema = z.enum([...headerVariantValues, ...legacyHeaderVariantValues]).transform((value) => value in legacyHeaderVariants ? legacyHeaderVariants[value] : value);
9157
9245
  const BREAKPOINT_LG = 1024;
@@ -9203,11 +9291,30 @@ const headerContainerSchema = z.object({
9203
9291
  tint: z.string().nullable().optional(),
9204
9292
  opacity: z.number().min(0).max(1).default(.12)
9205
9293
  }).optional();
9206
- const headerInteractionPaletteSchema = z.enum([
9294
+ //#endregion
9295
+ //#region ../theme-core/src/header/interactionPalette.ts
9296
+ /**
9297
+ * The palettes that fill the nav link with a colour on hover and focus. Each
9298
+ * one names a background/foreground token pair.
9299
+ */
9300
+ const headerInteractionPillPaletteValues = [
9207
9301
  "primary",
9208
9302
  "secondary",
9209
9303
  "accent"
9210
- ]);
9304
+ ];
9305
+ /**
9306
+ * "No pill": the nav link keeps whatever hover treatment its recipe compiled,
9307
+ * and the palette step writes nothing at all (#1318 Phase 4c decision 2).
9308
+ *
9309
+ * It exists because "a colour change on hover, no filled background" was not
9310
+ * expressible in the vocabulary, and it is what
9311
+ * `anna-raven-therapy-and-yoga` and `kirtan-lucy` have rendered since before
9312
+ * the palette existed. Without it those two rows can only be classified as a
9313
+ * conscious visual change.
9314
+ */
9315
+ const headerInteractionQuietPalette = "quiet";
9316
+ const headerInteractionPaletteValues = [...headerInteractionPillPaletteValues, headerInteractionQuietPalette];
9317
+ const headerInteractionPaletteSchema = z.enum(headerInteractionPaletteValues);
9211
9318
  //#endregion
9212
9319
  //#region ../theme-core/src/boxBackgroundSchema.ts
9213
9320
  const boxBackgroundOverlaySchema = z.preprocess(normalizeAuthoringOpacity, z.discriminatedUnion("type", [
@@ -9265,7 +9372,7 @@ const boxBackgroundSchema = z.discriminatedUnion("type", [
9265
9372
  })
9266
9373
  ]);
9267
9374
  function normalizeAuthoringOpacity(value) {
9268
- if (!isRecord$9(value)) return value;
9375
+ if (!isRecord$13(value)) return value;
9269
9376
  return {
9270
9377
  ...value,
9271
9378
  opacity: normalizeOpacity(value.opacity)
@@ -9274,7 +9381,7 @@ function normalizeAuthoringOpacity(value) {
9274
9381
  function normalizeOpacity(value) {
9275
9382
  return typeof value === "number" && value > 1 && value <= 100 ? value / 100 : value;
9276
9383
  }
9277
- function isRecord$9(value) {
9384
+ function isRecord$13(value) {
9278
9385
  return typeof value === "object" && value !== null && !Array.isArray(value);
9279
9386
  }
9280
9387
  //#endregion
@@ -9589,11 +9696,12 @@ const inputStyle = z.object({
9589
9696
  ]),
9590
9697
  label: z.enum(["inside", "above"])
9591
9698
  });
9592
- const headerPositioning = z.enum([
9699
+ const headerPositioningValues = [
9593
9700
  "static",
9594
9701
  "sticky",
9595
9702
  "fixed"
9596
- ]);
9703
+ ];
9704
+ const headerPositioning = z.enum(headerPositioningValues);
9597
9705
  const headerNavStyle = z.enum([
9598
9706
  "minimal",
9599
9707
  "underline",
@@ -9610,7 +9718,6 @@ const navFontWeight = z.enum([
9610
9718
  "bold"
9611
9719
  ]);
9612
9720
  const headerMaxWidth = z.enum(["container", "full"]);
9613
- const headerBarFrame = z.enum(["full-bleed", "inset-shell"]);
9614
9721
  const headerBorderSchema = componentBorderSchema.extend({ position: z.enum([
9615
9722
  "bottom",
9616
9723
  "top",
@@ -9639,12 +9746,6 @@ const headerLogoTextSchema = z.object({
9639
9746
  ]).optional(),
9640
9747
  hideOnShrink: z.boolean().optional()
9641
9748
  }).optional();
9642
- const navEffectsSchema = z.object({
9643
- underlineGradient: z.boolean().default(false),
9644
- glow: z.boolean().default(false),
9645
- glowColor: z.string().optional(),
9646
- neumorphic: z.boolean().default(false)
9647
- }).optional();
9648
9749
  const dropdownStyleSchema = z.object({
9649
9750
  background: themeColorTokenSchema.default("surface"),
9650
9751
  textColor: themeColorTokenSchema.default("text"),
@@ -9735,10 +9836,8 @@ const headerSchema = z.object({
9735
9836
  variant: headerVariantWithLegacyAliasSchema,
9736
9837
  positioning: headerPositioning,
9737
9838
  shrinkOnScroll: z.boolean(),
9738
- maxWidth: headerMaxWidth,
9739
- barFrame: headerBarFrame.optional(),
9839
+ maxWidth: headerMaxWidth.default("container"),
9740
9840
  scrollTreatment: headerScrollTreatmentSchema,
9741
- logoOverride: mediaSchema.nullable().optional(),
9742
9841
  background: headerBackgroundSchema.default({
9743
9842
  type: "color",
9744
9843
  color: "surface"
@@ -9750,13 +9849,11 @@ const headerSchema = z.object({
9750
9849
  navFontRole: z.enum(["body", "heading"]).optional(),
9751
9850
  navTextSize: navLinkStyleSchema.shape.textSize,
9752
9851
  interactionPalette: headerInteractionPaletteSchema.optional(),
9753
- navPlacement: z.enum(["end", "center"]).optional(),
9754
9852
  container: headerContainerSchema,
9755
9853
  border: headerBorderSchema,
9756
9854
  shadow: componentShadowSchema.optional(),
9757
9855
  logo: logoStyleSchema,
9758
9856
  logoText: headerLogoTextSchema,
9759
- navEffects: navEffectsSchema,
9760
9857
  navContainer: navContainerSchema,
9761
9858
  navLinkStyle: navLinkStyleSchema.optional(),
9762
9859
  ctaGap: headerCtaGapSchema.optional(),
@@ -9938,11 +10035,6 @@ const bodyTextSchema = z.object({ intro: z.object({
9938
10035
  paragraphSize: bodyTextIntroTypographySizeSchema.optional(),
9939
10036
  headingSize: bodyTextIntroTypographySizeSchema.optional()
9940
10037
  }).optional() }).optional();
9941
- const gradientsSchema = z.object({
9942
- button: z.string().min(1).nullable().optional(),
9943
- hero: z.string().min(1).nullable().optional(),
9944
- background: z.string().min(1).nullable().optional()
9945
- });
9946
10038
  /**
9947
10039
  * Testimonial quote-mark treatment, owned by the site style (analogous to the
9948
10040
  * accordion icon). `style` is the coarse selector; the optional fields fine-tune
@@ -10925,6 +11017,12 @@ function cardRoleFromLegacyVocabulary(value) {
10925
11017
  */
10926
11018
  const cardRoleWithLegacyAliasSchema = z.preprocess((value) => cardRoleFromLegacyVocabulary(value) ?? value, cardRoleSchema);
10927
11019
  //#endregion
11020
+ //#region ../theme-core/src/site-styles/compilationPlan.ts
11021
+ const defaultHeaderBrandPreferences = {
11022
+ brandDisplay: "auto",
11023
+ logoSize: "auto"
11024
+ };
11025
+ //#endregion
10928
11026
  //#region ../theme-core/src/site-styles/sectionTransitions.ts
10929
11027
  function boundaryRuntimeMetrics(family, size, heightExpression) {
10930
11028
  return {
@@ -11851,27 +11949,6 @@ function asHeaderStyleId(value) {
11851
11949
  return value;
11852
11950
  }
11853
11951
  //#endregion
11854
- //#region ../theme-core/src/site-styles/headerInteractionPalette.ts
11855
- const clearSplitInteractionPalette = defineLookOptionGroup({
11856
- id: "interaction-palette",
11857
- label: "Hover colour",
11858
- defaultOptionId: "accent",
11859
- options: [
11860
- {
11861
- id: "accent",
11862
- label: "Accent"
11863
- },
11864
- {
11865
- id: "primary",
11866
- label: "Primary"
11867
- },
11868
- {
11869
- id: "secondary",
11870
- label: "Secondary"
11871
- }
11872
- ]
11873
- });
11874
- //#endregion
11875
11952
  //#region ../theme-core/src/site-styles/headerLayoutCatalog.ts
11876
11953
  const titleOnlyLogoAccommodation = {
11877
11954
  pattern: "title-only",
@@ -11991,6 +12068,12 @@ const overlayLogoAccommodations = [
11991
12068
  inlineMarkWithTitleLogoAccommodation,
11992
12069
  unbackedOverlayLogoAccommodation
11993
12070
  ];
12071
+ const allCuratedLogoAccommodations = [
12072
+ ...inlineClassicLogoAccommodations,
12073
+ ...centeredLogoAccommodations,
12074
+ ...overlayLogoAccommodations
12075
+ ];
12076
+ new Set(allCuratedLogoAccommodations.filter((option) => option.providesBacking === true).map((option) => option.pattern));
11994
12077
  const headerLayoutCatalog = defineExactCatalog()([
11995
12078
  {
11996
12079
  id: asHeaderLayoutId("classic"),
@@ -12031,43 +12114,37 @@ const headerStyleCatalog = defineExactCatalog()([
12031
12114
  id: asHeaderStyleId("minimal"),
12032
12115
  label: "Minimal",
12033
12116
  description: "Quiet surface with simple color-change navigation.",
12034
- supportedLayoutIds: supportedLayouts(["classic", "centered"]),
12035
- navTypography: "plain"
12117
+ supportedLayoutIds: supportedLayouts(["classic", "centered"])
12036
12118
  },
12037
12119
  {
12038
12120
  id: asHeaderStyleId("fine-line"),
12039
12121
  label: "Seamless",
12040
12122
  description: "Borderless surface with underline hover for a quieter page transition.",
12041
- supportedLayoutIds: supportedLayouts(["classic", "centered"]),
12042
- navTypography: "plain"
12123
+ supportedLayoutIds: supportedLayouts(["classic", "centered"])
12043
12124
  },
12044
12125
  {
12045
12126
  id: asHeaderStyleId("soft-bar"),
12046
12127
  label: "Soft Bar",
12047
12128
  description: "Soft tinted surface with gentle pill hover states.",
12048
- supportedLayoutIds: supportedLayouts(["classic", "centered"]),
12049
- navTypography: "plain"
12129
+ supportedLayoutIds: supportedLayouts(["classic", "centered"])
12050
12130
  },
12051
12131
  {
12052
12132
  id: asHeaderStyleId("pill-nav"),
12053
12133
  label: "Pill Nav",
12054
12134
  description: "A calm pill navigation rail over a neutral header.",
12055
- supportedLayoutIds: supportedLayouts(["classic", "centered"]),
12056
- navTypography: "plain"
12135
+ supportedLayoutIds: supportedLayouts(["classic", "centered"])
12057
12136
  },
12058
12137
  {
12059
12138
  id: asHeaderStyleId("brand-bar"),
12060
12139
  label: "Brand Bar",
12061
12140
  description: "Solid brand surface with high-contrast navigation.",
12062
- supportedLayoutIds: supportedLayouts(["classic", "centered"]),
12063
- navTypography: "brand-caps"
12141
+ supportedLayoutIds: supportedLayouts(["classic", "centered"])
12064
12142
  },
12065
12143
  {
12066
12144
  id: asHeaderStyleId("brand-underline"),
12067
12145
  label: "Brand Underline",
12068
12146
  description: "Brand surface with underline-grow navigation.",
12069
- supportedLayoutIds: supportedLayouts(["classic"]),
12070
- navTypography: "brand-caps"
12147
+ supportedLayoutIds: supportedLayouts(["classic"])
12071
12148
  },
12072
12149
  {
12073
12150
  id: asHeaderStyleId("brand-pill"),
@@ -12077,73 +12154,82 @@ const headerStyleCatalog = defineExactCatalog()([
12077
12154
  "classic",
12078
12155
  "centered",
12079
12156
  "floating"
12080
- ]),
12081
- navTypography: "brand-caps"
12157
+ ])
12082
12158
  },
12083
12159
  {
12084
12160
  id: asHeaderStyleId("brand-capsule"),
12085
12161
  label: "Brand Capsule",
12086
12162
  description: "Solid brand chrome designed as a floating capsule, with quiet-caps nav, a soft pill active state, and an inverted CTA pill.",
12087
- supportedLayoutIds: supportedLayouts(["classic"]),
12088
- navTypography: "quiet-caps"
12163
+ supportedLayoutIds: supportedLayouts(["classic"])
12089
12164
  },
12090
12165
  {
12091
12166
  id: asHeaderStyleId("serif-line"),
12092
12167
  label: "Serif Line",
12093
12168
  description: "Typographic logo treatment with understated underline navigation.",
12094
- supportedLayoutIds: supportedLayouts(["classic", "centered"]),
12095
- navTypography: "heading"
12169
+ supportedLayoutIds: supportedLayouts(["classic", "centered"])
12096
12170
  },
12097
12171
  {
12098
12172
  id: asHeaderStyleId("flat-tabs"),
12099
12173
  label: "Flat Tabs",
12100
12174
  description: "Flat block hover and active states with a Swiss utility feel.",
12101
- supportedLayoutIds: supportedLayouts(["classic", "centered"]),
12102
- navTypography: "quiet-caps"
12175
+ supportedLayoutIds: supportedLayouts(["classic", "centered"])
12103
12176
  },
12104
12177
  {
12105
12178
  id: asHeaderStyleId("glass"),
12106
12179
  label: "Glass",
12107
12180
  description: "Translucent chrome with frosted navigation backing.",
12108
- supportedLayoutIds: supportedLayouts(["floating"]),
12109
- navTypography: "plain"
12181
+ supportedLayoutIds: supportedLayouts(["floating"])
12110
12182
  },
12111
12183
  {
12112
12184
  id: asHeaderStyleId("minimal-surface"),
12113
12185
  label: "Clear Split",
12114
12186
  description: "Floating logo with transparent navigation and no shared shell.",
12115
- supportedLayoutIds: supportedLayouts(["floating"]),
12116
- navTypography: "plain",
12117
- openingForegroundTarget: "all",
12118
- scrollTreatment: "contrast-glass",
12119
- interactionPalette: clearSplitInteractionPalette
12187
+ supportedLayoutIds: supportedLayouts(["floating"])
12120
12188
  },
12121
12189
  {
12122
12190
  id: asHeaderStyleId("soft-shell"),
12123
12191
  label: "Soft Shell",
12124
12192
  description: "Floating rounded shell with soft pill navigation states.",
12125
- supportedLayoutIds: supportedLayouts(["floating"]),
12126
- navTypography: "plain"
12193
+ supportedLayoutIds: supportedLayouts(["floating"])
12127
12194
  },
12128
12195
  {
12129
12196
  id: asHeaderStyleId("split-glass"),
12130
12197
  label: "Split Glass",
12131
12198
  description: "Logo floats independently while the navigation sits in a glass rail.",
12132
- supportedLayoutIds: supportedLayouts(["floating"]),
12133
- navTypography: "quiet-caps",
12134
- openingForegroundTarget: "brand-only"
12199
+ supportedLayoutIds: supportedLayouts(["floating"])
12135
12200
  },
12136
12201
  {
12137
12202
  id: asHeaderStyleId("split-pill"),
12138
12203
  label: "Split Pill",
12139
12204
  description: "Logo floats independently with a soft pill-backed navigation rail.",
12140
- supportedLayoutIds: supportedLayouts(["floating"]),
12141
- navTypography: "heading",
12142
- openingForegroundTarget: "brand-only"
12205
+ supportedLayoutIds: supportedLayouts(["floating"])
12143
12206
  }
12144
12207
  ]);
12145
12208
  new Map(headerLayoutCatalog.map((layout) => [layout.id, layout]));
12146
12209
  new Map(headerStyleCatalog.map((style) => [style.id, style]));
12210
+ defineLookOptionGroup({
12211
+ id: "interaction-palette",
12212
+ label: "Hover colour",
12213
+ defaultOptionId: "accent",
12214
+ options: [
12215
+ {
12216
+ id: "accent",
12217
+ label: "Accent"
12218
+ },
12219
+ {
12220
+ id: "primary",
12221
+ label: "Primary"
12222
+ },
12223
+ {
12224
+ id: "secondary",
12225
+ label: "Secondary"
12226
+ },
12227
+ {
12228
+ id: headerInteractionQuietPalette,
12229
+ label: "Text only"
12230
+ }
12231
+ ]
12232
+ });
12147
12233
  //#endregion
12148
12234
  //#region ../theme-core/src/site-styles/footerLooks.ts
12149
12235
  function asFooterLookId(value) {
@@ -14562,6 +14648,67 @@ const blockDesignFallback = {
14562
14648
  };
14563
14649
  blockDesignFallback.sectionSurface, blockDesignFallback.contentFrame, blockDesignFallback.transitionAfter, blockDesignFallback.sectionHeight, blockDesignFallback.heroLegibility, blockDesignFallback.imageSurfaceTreatment, blockDesignFallback.imageSurfaceMotion, blockDesignFallback.tileTreatment, blockDesignFallback.eventListTreatment, blockDesignFallback.eyebrowTreatment, blockDesignFallback.accordionIconTreatment, blockDesignFallback.composition;
14564
14650
  //#endregion
14651
+ //#region ../theme-core/src/site-styles/siteChromeBackgrounds.ts
14652
+ const siteChromeBackgroundColorTokens = toSiteChromeBackgroundColorTokens(backgroundAppearanceColorTokenNames);
14653
+ const quickSiteChromeBackgroundColorTokens = toSiteChromeBackgroundColorTokens(backgroundAppearanceQuickColorTokenNames);
14654
+ siteChromeBackgroundColorTokens.filter((token) => !quickSiteChromeBackgroundColorTokens.includes(token));
14655
+ function parseSiteChromeBackgroundColorToken(value) {
14656
+ const token = parseBackgroundAppearanceColorToken(value);
14657
+ return token ? token : null;
14658
+ }
14659
+ function toSiteChromeBackgroundColorTokens(tokenNames) {
14660
+ return tokenNames.flatMap((name) => {
14661
+ const token = parseSiteChromeBackgroundColorToken(name);
14662
+ return token ? [token] : [];
14663
+ });
14664
+ }
14665
+ //#endregion
14666
+ //#region ../theme-core/src/site-styles/siteChromeNavPreferences.ts
14667
+ const headerNavTypographyPreferenceValues = [
14668
+ "inherit",
14669
+ "body",
14670
+ "heading"
14671
+ ];
14672
+ const headerNavFontSizePreferenceValues = [
14673
+ "inherit",
14674
+ "sm",
14675
+ "base",
14676
+ "lg",
14677
+ "xl"
14678
+ ];
14679
+ const defaultHeaderNavPreferences = {
14680
+ typography: "inherit",
14681
+ fontSize: "inherit",
14682
+ interactionPalette: "recommended"
14683
+ };
14684
+ //#endregion
14685
+ //#region ../theme-core/src/site-styles/headerBrandPresentation.ts
14686
+ /**
14687
+ * Whether the header wears a brand presentation at all (#1318 Phase 4c).
14688
+ *
14689
+ * `applyHeaderBrandPresentation` is unconditional today: given a logo context
14690
+ * it always writes a `logo` object carrying a resolved display, size and
14691
+ * accommodation. That makes "this header has no brand presentation block" —
14692
+ * which is what several live sites persist, including one whose site *does*
14693
+ * have a logo asset — a state the preference record could not express, so a
14694
+ * recompile would silently give the site a logo treatment it has never
14695
+ * rendered.
14696
+ *
14697
+ * This is the third field in this phase with that shape, after the interaction
14698
+ * palette and the nav text size: the persisted bytes carry intent the reader
14699
+ * cannot say, so the recompile "corrects" it. The fix is the same one — widen
14700
+ * the closed vocabulary with a named member and derive it by baseline
14701
+ * comparison — and it is a *widening*, not a restyle: `'recommended'` is the
14702
+ * default and reproduces today's behaviour exactly, so no row whose bytes carry
14703
+ * a presentation compiles differently for `'plain'` existing.
14704
+ *
14705
+ * `'plain'` is deliberately not "preserve whatever bytes are there". It is a
14706
+ * constructive instruction — *write no presentation block* — so Phase 4e's
14707
+ * compiler, which builds the header from nothing, can produce it.
14708
+ */
14709
+ const headerBrandPresentationValues = ["recommended", "plain"];
14710
+ defaultHeaderBrandPreferences.brandDisplay, defaultHeaderBrandPreferences.logoSize, defaultHeaderNavPreferences.typography, defaultHeaderNavPreferences.fontSize, defaultHeaderNavPreferences.interactionPalette;
14711
+ //#endregion
14565
14712
  //#region ../theme-core/src/site-styles/generatedSchemas.ts
14566
14713
  function uniqueNonEmpty(values) {
14567
14714
  const uniqueValues = [...new Set(values)];
@@ -14630,6 +14777,56 @@ z.object({
14630
14777
  blocks: z.array(generatedPageDesignBlockIntentSchema)
14631
14778
  }).strict();
14632
14779
  //#endregion
14780
+ //#region ../theme-core/src/site-styles/headerPreferencesPersistence.ts
14781
+ const backgroundColorTokenSchema = z.unknown().transform((value, ctx) => {
14782
+ const token = parseSiteChromeBackgroundColorToken(value);
14783
+ if (token === null) {
14784
+ ctx.addIssue({
14785
+ code: "custom",
14786
+ message: "Unknown site chrome background colour token"
14787
+ });
14788
+ return z.NEVER;
14789
+ }
14790
+ return token;
14791
+ });
14792
+ const backgroundOverrideSchema = z.discriminatedUnion("kind", [z.object({ kind: z.literal("default") }).strict(), z.object({
14793
+ kind: z.literal("color"),
14794
+ token: backgroundColorTokenSchema
14795
+ }).strict()]);
14796
+ const logoTextIntentSchema = z.object({
14797
+ mobileMaxWidth: z.enum([
14798
+ "xs",
14799
+ "sm",
14800
+ "md"
14801
+ ]).optional(),
14802
+ mobileWrapLines: z.union([z.literal(1), z.literal(2)]).optional(),
14803
+ hideOnShrink: z.boolean().optional()
14804
+ }).strict();
14805
+ const structureSchema = z.object({
14806
+ positioning: z.union([z.literal("inherit"), z.enum(headerPositioningValues)]),
14807
+ shrinkOnScroll: z.union([z.literal("inherit"), z.boolean()]),
14808
+ logoText: z.union([
14809
+ z.literal("inherit"),
14810
+ z.literal("none"),
14811
+ logoTextIntentSchema
14812
+ ])
14813
+ }).strict();
14814
+ const headerPreferencesShape = {
14815
+ brandPresentation: z.enum(headerBrandPresentationValues),
14816
+ brandDisplay: z.enum(brandDisplayModeValues),
14817
+ logoSize: z.enum(logoSizePolicyValues),
14818
+ navFontRole: z.enum(headerNavTypographyPreferenceValues),
14819
+ navTextSize: z.enum(headerNavFontSizePreferenceValues),
14820
+ interactionPalette: z.union([z.literal("recommended"), z.enum(headerInteractionPaletteValues)]),
14821
+ background: backgroundOverrideSchema,
14822
+ structure: structureSchema
14823
+ };
14824
+ z.object(headerPreferencesShape).strict();
14825
+ z.object(headerPreferencesShape).omit({
14826
+ brandPresentation: true,
14827
+ structure: true
14828
+ }).strict();
14829
+ //#endregion
14633
14830
  //#region ../theme-core/src/site-styles/pageDesignAuthority.ts
14634
14831
  const nonEmptyStringSchema = z.string().min(1);
14635
14832
  const blockContentVersionIdSchema = nonEmptyStringSchema.transform(asBlockContentVersionId);
@@ -14731,21 +14928,6 @@ function collectBlockSequenceIssues(snapshots) {
14731
14928
  return issues;
14732
14929
  }
14733
14930
  //#endregion
14734
- //#region ../theme-core/src/site-styles/siteChromeBackgrounds.ts
14735
- const siteChromeBackgroundColorTokens = toSiteChromeBackgroundColorTokens(backgroundAppearanceColorTokenNames);
14736
- const quickSiteChromeBackgroundColorTokens = toSiteChromeBackgroundColorTokens(backgroundAppearanceQuickColorTokenNames);
14737
- siteChromeBackgroundColorTokens.filter((token) => !quickSiteChromeBackgroundColorTokens.includes(token));
14738
- function parseSiteChromeBackgroundColorToken(value) {
14739
- const token = parseBackgroundAppearanceColorToken(value);
14740
- return token ? token : null;
14741
- }
14742
- function toSiteChromeBackgroundColorTokens(tokenNames) {
14743
- return tokenNames.flatMap((name) => {
14744
- const token = parseSiteChromeBackgroundColorToken(name);
14745
- return token ? [token] : [];
14746
- });
14747
- }
14748
- //#endregion
14749
14931
  //#region ../theme-core/src/site-styles/tokenRecipes.ts
14750
14932
  const themeTokenRecipeOptions = {
14751
14933
  palette: [
@@ -14829,7 +15011,7 @@ function planSiteChromePlacement(positioning) {
14829
15011
  headerRootClassName: "rb-fixed rb-top-0 rb-left-0 rb-right-0",
14830
15012
  standaloneShellClassName: "fixed left-0 right-0 top-0 z-40 w-full"
14831
15013
  };
14832
- default: return assertNever$1(positioning);
15014
+ default: return assertNever$2(positioning);
14833
15015
  }
14834
15016
  }
14835
15017
  //#endregion
@@ -14896,14 +15078,9 @@ function formatKeyAsLabel(key) {
14896
15078
  return key.replace(/([A-Z])/g, " $1").replace(/[-_]/g, " ").trim().split(" ").map((word) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase()).join(" ");
14897
15079
  }
14898
15080
  //#endregion
14899
- //#region ../blocks/src/system/manifest/registry.ts
14900
- const REGISTRY_SYMBOL$1 = Symbol.for("@riverbankcms/blocks/manifest-registry");
14901
- const isBlocksTestRuntime$1 = typeof process !== "undefined" && typeof process.env !== "undefined" && process.env.RIVERBANK_BLOCKS_TEST === "1";
14902
- const globalScope$1 = globalThis;
14903
- const manifestStore = isBlocksTestRuntime$1 ? /* @__PURE__ */ new Map() : globalScope$1[REGISTRY_SYMBOL$1] ?? (globalScope$1[REGISTRY_SYMBOL$1] = /* @__PURE__ */ new Map());
14904
- function registerManifest(manifest) {
14905
- manifestStore.set(manifest.name, manifest);
14906
- return manifest;
15081
+ //#region ../media-core/src/typeGuards.ts
15082
+ function isRecord$12(value) {
15083
+ return Boolean(value) && typeof value === "object" && !Array.isArray(value);
14907
15084
  }
14908
15085
  //#endregion
14909
15086
  //#region ../media-core/src/transformRect.ts
@@ -14997,6 +15174,37 @@ function rotateNormalizedPoint(point, rotation) {
14997
15174
  }
14998
15175
  }
14999
15176
  //#endregion
15177
+ //#region ../media-core/src/mediaRefNormalizer.ts
15178
+ function looksLikeUuid(value) {
15179
+ return typeof value === "string" && /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(value);
15180
+ }
15181
+ /**
15182
+ * Strip environment-specific keys from a CMS media object.
15183
+ * @mutates Mutates the passed object in-place.
15184
+ */
15185
+ function deleteEnvSpecificKeys(obj) {
15186
+ delete obj.assetId;
15187
+ delete obj.storageBucket;
15188
+ delete obj.storagePath;
15189
+ delete obj.src;
15190
+ delete obj.mimeType;
15191
+ if (looksLikeUuid(obj.id)) delete obj.id;
15192
+ }
15193
+ //#endregion
15194
+ //#region ../media-core/src/richTextImageCollector.ts
15195
+ function walkAndCollectImageAttrs(value, out, location = "") {
15196
+ if (Array.isArray(value)) {
15197
+ value.forEach((item, index) => walkAndCollectImageAttrs(item, out, `${location}[${index}]`));
15198
+ return;
15199
+ }
15200
+ if (!isRecord$12(value)) return;
15201
+ if (value.type === "image" && isRecord$12(value.attrs)) out.push({
15202
+ attrs: value.attrs,
15203
+ location: location ? `${location}.attrs` : "attrs"
15204
+ });
15205
+ for (const [key, child] of Object.entries(value)) walkAndCollectImageAttrs(child, out, location ? `${location}.${key}` : key);
15206
+ }
15207
+ //#endregion
15000
15208
  //#region ../blocks/src/system/node/schema.ts
15001
15209
  const transformStepSchema = z.object({
15002
15210
  id: z.string().min(1, "Transform requires an identifier"),
@@ -15644,6 +15852,14 @@ function parseFieldDefinition(input) {
15644
15852
  function parseFieldDefinitions(input) {
15645
15853
  return fieldSchema.array().parse(input);
15646
15854
  }
15855
+ /**
15856
+ * Normalize legacy select fields into the closed source union before any consumer
15857
+ * branches on source semantics. Runtime-backed definitions must be explicit;
15858
+ * legacy static definitions normalize to `kind: "static"`.
15859
+ */
15860
+ function normalizeSelectSource(field) {
15861
+ return field.source ?? { kind: "static" };
15862
+ }
15647
15863
  function expectParsedSelectField(field) {
15648
15864
  if (field.type !== "select") throw new Error(`Expected select field, received ${field.type}`);
15649
15865
  return field;
@@ -15802,6 +16018,1932 @@ z.object({
15802
16018
  tags: z.array(z.string()).optional(),
15803
16019
  icon: z.string().optional()
15804
16020
  });
16021
+ const inlineTextMarkSchema = z.discriminatedUnion("kind", [
16022
+ z.object({ kind: z.literal("strong") }),
16023
+ z.object({ kind: z.literal("emphasis") }),
16024
+ z.object({
16025
+ kind: z.literal("accent"),
16026
+ tone: z.enum([
16027
+ "accent",
16028
+ "brand",
16029
+ "contrast"
16030
+ ])
16031
+ }),
16032
+ z.object({
16033
+ kind: z.literal("highlight"),
16034
+ tone: z.enum([
16035
+ "soft",
16036
+ "accent",
16037
+ "brand"
16038
+ ])
16039
+ })
16040
+ ]);
16041
+ const inlineTextRunSchema = z.object({
16042
+ text: z.string(),
16043
+ marks: z.array(inlineTextMarkSchema).optional()
16044
+ });
16045
+ z.array(inlineTextRunSchema);
16046
+ const MARK_ORDER = {
16047
+ strong: 0,
16048
+ emphasis: 1,
16049
+ accent: 2,
16050
+ highlight: 3
16051
+ };
16052
+ function plainTextToInlineText(value) {
16053
+ return normalizeInlineText(value.length > 0 ? [{ text: value }] : []);
16054
+ }
16055
+ function inlineTextToPlainText(value) {
16056
+ return value.map((run) => run.text).join("");
16057
+ }
16058
+ function parseInlineTextFromUnknown(value) {
16059
+ if (!Array.isArray(value)) return {
16060
+ ok: false,
16061
+ error: { kind: "not-array" }
16062
+ };
16063
+ const runs = [];
16064
+ for (let index = 0; index < value.length; index += 1) {
16065
+ const parsed = inlineTextRunSchema.safeParse(value[index]);
16066
+ if (!parsed.success) return {
16067
+ ok: false,
16068
+ error: {
16069
+ kind: "invalid-run",
16070
+ index
16071
+ }
16072
+ };
16073
+ runs.push(parsed.data);
16074
+ }
16075
+ return {
16076
+ ok: true,
16077
+ value: normalizeInlineText(runs)
16078
+ };
16079
+ }
16080
+ function normalizeInlineText(value) {
16081
+ const normalized = [];
16082
+ for (const run of value) {
16083
+ if (run.text.length === 0) continue;
16084
+ const marks = normalizeInlineTextMarks(run.marks ?? []);
16085
+ const nextRun = marks.length > 0 ? {
16086
+ text: run.text,
16087
+ marks
16088
+ } : { text: run.text };
16089
+ const previous = normalized[normalized.length - 1];
16090
+ if (previous && inlineTextMarksEqual(previous.marks ?? [], nextRun.marks ?? [])) {
16091
+ normalized[normalized.length - 1] = previous.marks && previous.marks.length > 0 ? {
16092
+ text: previous.text + nextRun.text,
16093
+ marks: previous.marks
16094
+ } : { text: previous.text + nextRun.text };
16095
+ continue;
16096
+ }
16097
+ normalized.push(nextRun);
16098
+ }
16099
+ return normalized;
16100
+ }
16101
+ function normalizeInlineTextMarks(marks) {
16102
+ let strong = null;
16103
+ let emphasis = null;
16104
+ let accent = null;
16105
+ let highlight = null;
16106
+ for (const mark of marks) switch (mark.kind) {
16107
+ case "strong":
16108
+ strong = { kind: "strong" };
16109
+ break;
16110
+ case "emphasis":
16111
+ emphasis = { kind: "emphasis" };
16112
+ break;
16113
+ case "accent":
16114
+ accent = {
16115
+ kind: "accent",
16116
+ tone: mark.tone
16117
+ };
16118
+ break;
16119
+ case "highlight":
16120
+ highlight = {
16121
+ kind: "highlight",
16122
+ tone: mark.tone
16123
+ };
16124
+ break;
16125
+ }
16126
+ return [
16127
+ strong,
16128
+ emphasis,
16129
+ accent,
16130
+ highlight
16131
+ ].filter((mark) => mark !== null).sort((left, right) => MARK_ORDER[left.kind] - MARK_ORDER[right.kind]);
16132
+ }
16133
+ function inlineTextMarksEqual(left, right) {
16134
+ const normalizedLeft = normalizeInlineTextMarks(left);
16135
+ const normalizedRight = normalizeInlineTextMarks(right);
16136
+ if (normalizedLeft.length !== normalizedRight.length) return false;
16137
+ return normalizedLeft.every((mark, index) => inlineTextMarkEqual(mark, normalizedRight[index]));
16138
+ }
16139
+ function inlineTextMarkEqual(left, right) {
16140
+ if (!right || left.kind !== right.kind) return false;
16141
+ switch (left.kind) {
16142
+ case "strong":
16143
+ case "emphasis": return true;
16144
+ case "accent": return right.kind === "accent" && left.tone === right.tone;
16145
+ case "highlight": return right.kind === "highlight" && left.tone === right.tone;
16146
+ }
16147
+ }
16148
+ //#endregion
16149
+ //#region ../blocks/src/system/icons/ids.ts
16150
+ const GENERATED_ICON_SET_ID_PATTERN = /^generated-icon-set:[a-z0-9][a-z0-9:_-]{0,107}$/u;
16151
+ const GENERATED_ICON_ASSET_ID_PATTERN = /^generated-icon-asset:[a-z0-9][a-z0-9:_-]{0,105}$/u;
16152
+ const SITE_ICON_STYLE_GUIDE_VERSION_ID_PATTERN = /^site-icon-style-guide:[a-f0-9]{16}$/u;
16153
+ function normalizeString(value) {
16154
+ if (typeof value !== "string") return null;
16155
+ const normalized = value.trim();
16156
+ return normalized.length > 0 ? normalized : null;
16157
+ }
16158
+ function unsafeAsGeneratedIconSetId(value) {
16159
+ return value;
16160
+ }
16161
+ function unsafeAsGeneratedIconAssetId(value) {
16162
+ return value;
16163
+ }
16164
+ function unsafeAsSiteIconStyleGuideVersionId(value) {
16165
+ return value;
16166
+ }
16167
+ function parseGeneratedIconSetId(value) {
16168
+ const normalized = normalizeString(value);
16169
+ return normalized !== null && GENERATED_ICON_SET_ID_PATTERN.test(normalized) ? unsafeAsGeneratedIconSetId(normalized) : null;
16170
+ }
16171
+ function parseGeneratedIconAssetId(value) {
16172
+ const normalized = normalizeString(value);
16173
+ return normalized !== null && GENERATED_ICON_ASSET_ID_PATTERN.test(normalized) ? unsafeAsGeneratedIconAssetId(normalized) : null;
16174
+ }
16175
+ function parseSiteIconStyleGuideVersionId(value) {
16176
+ if (typeof value !== "string") return null;
16177
+ const normalized = value.trim();
16178
+ return SITE_ICON_STYLE_GUIDE_VERSION_ID_PATTERN.test(normalized) ? unsafeAsSiteIconStyleGuideVersionId(normalized) : null;
16179
+ }
16180
+ //#endregion
16181
+ //#region ../blocks/src/system/icons/semantic.ts
16182
+ const SEMANTIC_ICON_IDS = [
16183
+ "protection-trust",
16184
+ "confidentiality",
16185
+ "care-support",
16186
+ "partnership",
16187
+ "warmth",
16188
+ "community",
16189
+ "personal-guidance",
16190
+ "conversation",
16191
+ "friendly-welcome",
16192
+ "insight-learning",
16193
+ "natural-wellbeing",
16194
+ "growth",
16195
+ "gentle-care",
16196
+ "special-touch",
16197
+ "ideas",
16198
+ "direction",
16199
+ "focus",
16200
+ "quality",
16201
+ "highlight",
16202
+ "verified-quality",
16203
+ "learning",
16204
+ "training",
16205
+ "programmes-dates",
16206
+ "place",
16207
+ "products",
16208
+ "packages",
16209
+ "time",
16210
+ "practical-service",
16211
+ "at-home",
16212
+ "worldwide",
16213
+ "professional-service"
16214
+ ];
16215
+ const featurePreset = ["feature"];
16216
+ [
16217
+ {
16218
+ id: "protection-trust",
16219
+ label: "Protection and trust",
16220
+ tags: [
16221
+ "safe",
16222
+ "safety",
16223
+ "security",
16224
+ "trust",
16225
+ "privacy",
16226
+ "confidence"
16227
+ ],
16228
+ domains: [
16229
+ "wellbeing",
16230
+ "services",
16231
+ "technology"
16232
+ ],
16233
+ tone: ["trustworthy", "practical"],
16234
+ presets: featurePreset
16235
+ },
16236
+ {
16237
+ id: "confidentiality",
16238
+ label: "Confidentiality",
16239
+ tags: [
16240
+ "confidential",
16241
+ "privacy",
16242
+ "secure",
16243
+ "protected",
16244
+ "private"
16245
+ ],
16246
+ domains: [
16247
+ "wellbeing",
16248
+ "coaching",
16249
+ "services",
16250
+ "technology"
16251
+ ],
16252
+ tone: ["trustworthy", "calm"],
16253
+ presets: featurePreset
16254
+ },
16255
+ {
16256
+ id: "care-support",
16257
+ label: "Care and support",
16258
+ tags: [
16259
+ "care",
16260
+ "support",
16261
+ "therapy",
16262
+ "kindness",
16263
+ "help",
16264
+ "compassion"
16265
+ ],
16266
+ domains: [
16267
+ "wellbeing",
16268
+ "coaching",
16269
+ "community"
16270
+ ],
16271
+ tone: ["warm", "human"],
16272
+ presets: featurePreset
16273
+ },
16274
+ {
16275
+ id: "partnership",
16276
+ label: "Partnership",
16277
+ tags: [
16278
+ "partnership",
16279
+ "together",
16280
+ "support",
16281
+ "relationship",
16282
+ "collaboration"
16283
+ ],
16284
+ domains: [
16285
+ "coaching",
16286
+ "community",
16287
+ "services"
16288
+ ],
16289
+ tone: [
16290
+ "warm",
16291
+ "human",
16292
+ "trustworthy"
16293
+ ],
16294
+ presets: featurePreset
16295
+ },
16296
+ {
16297
+ id: "warmth",
16298
+ label: "Warmth",
16299
+ tags: [
16300
+ "heart",
16301
+ "warmth",
16302
+ "care",
16303
+ "love",
16304
+ "wellbeing",
16305
+ "personal"
16306
+ ],
16307
+ domains: [
16308
+ "wellbeing",
16309
+ "community",
16310
+ "hospitality"
16311
+ ],
16312
+ tone: ["warm", "human"],
16313
+ presets: featurePreset
16314
+ },
16315
+ {
16316
+ id: "community",
16317
+ label: "Community",
16318
+ tags: [
16319
+ "community",
16320
+ "group",
16321
+ "people",
16322
+ "team",
16323
+ "members",
16324
+ "belonging"
16325
+ ],
16326
+ domains: [
16327
+ "community",
16328
+ "education",
16329
+ "events"
16330
+ ],
16331
+ tone: ["human", "warm"],
16332
+ presets: featurePreset
16333
+ },
16334
+ {
16335
+ id: "personal-guidance",
16336
+ label: "Personal guidance",
16337
+ tags: [
16338
+ "guidance",
16339
+ "personal",
16340
+ "coach",
16341
+ "expert",
16342
+ "one to one",
16343
+ "trusted"
16344
+ ],
16345
+ domains: [
16346
+ "coaching",
16347
+ "services",
16348
+ "education"
16349
+ ],
16350
+ tone: ["trustworthy", "human"],
16351
+ presets: featurePreset
16352
+ },
16353
+ {
16354
+ id: "conversation",
16355
+ label: "Conversation",
16356
+ tags: [
16357
+ "conversation",
16358
+ "talk",
16359
+ "chat",
16360
+ "communication",
16361
+ "support"
16362
+ ],
16363
+ domains: [
16364
+ "coaching",
16365
+ "community",
16366
+ "services"
16367
+ ],
16368
+ tone: ["human", "warm"],
16369
+ presets: featurePreset
16370
+ },
16371
+ {
16372
+ id: "friendly-welcome",
16373
+ label: "Friendly welcome",
16374
+ tags: [
16375
+ "friendly",
16376
+ "welcome",
16377
+ "positive",
16378
+ "happy",
16379
+ "approachable"
16380
+ ],
16381
+ domains: [
16382
+ "hospitality",
16383
+ "community",
16384
+ "services"
16385
+ ],
16386
+ tone: [
16387
+ "warm",
16388
+ "playful",
16389
+ "human"
16390
+ ],
16391
+ presets: featurePreset
16392
+ },
16393
+ {
16394
+ id: "insight-learning",
16395
+ label: "Insight and learning",
16396
+ tags: [
16397
+ "mind",
16398
+ "insight",
16399
+ "learning",
16400
+ "clarity",
16401
+ "thinking",
16402
+ "therapy"
16403
+ ],
16404
+ domains: [
16405
+ "wellbeing",
16406
+ "coaching",
16407
+ "education"
16408
+ ],
16409
+ tone: ["calm", "practical"],
16410
+ presets: featurePreset
16411
+ },
16412
+ {
16413
+ id: "natural-wellbeing",
16414
+ label: "Natural wellbeing",
16415
+ tags: [
16416
+ "natural",
16417
+ "wellbeing",
16418
+ "growth",
16419
+ "eco",
16420
+ "calm",
16421
+ "organic"
16422
+ ],
16423
+ domains: [
16424
+ "wellbeing",
16425
+ "hospitality",
16426
+ "creative"
16427
+ ],
16428
+ tone: ["calm", "warm"],
16429
+ presets: featurePreset
16430
+ },
16431
+ {
16432
+ id: "growth",
16433
+ label: "Growth",
16434
+ tags: [
16435
+ "growth",
16436
+ "new",
16437
+ "progress",
16438
+ "development",
16439
+ "nurture"
16440
+ ],
16441
+ domains: [
16442
+ "wellbeing",
16443
+ "coaching",
16444
+ "education",
16445
+ "community"
16446
+ ],
16447
+ tone: ["warm", "calm"],
16448
+ presets: featurePreset
16449
+ },
16450
+ {
16451
+ id: "gentle-care",
16452
+ label: "Gentle care",
16453
+ tags: [
16454
+ "gentle",
16455
+ "soft",
16456
+ "care",
16457
+ "wellbeing",
16458
+ "beauty",
16459
+ "calm"
16460
+ ],
16461
+ domains: [
16462
+ "wellbeing",
16463
+ "creative",
16464
+ "hospitality"
16465
+ ],
16466
+ tone: [
16467
+ "warm",
16468
+ "calm",
16469
+ "premium"
16470
+ ],
16471
+ presets: featurePreset
16472
+ },
16473
+ {
16474
+ id: "special-touch",
16475
+ label: "Special touch",
16476
+ tags: [
16477
+ "special",
16478
+ "sparkle",
16479
+ "magic",
16480
+ "polish",
16481
+ "delight",
16482
+ "creative"
16483
+ ],
16484
+ domains: [
16485
+ "creative",
16486
+ "commerce",
16487
+ "hospitality"
16488
+ ],
16489
+ tone: ["playful", "premium"],
16490
+ presets: featurePreset
16491
+ },
16492
+ {
16493
+ id: "ideas",
16494
+ label: "Ideas",
16495
+ tags: [
16496
+ "idea",
16497
+ "innovation",
16498
+ "clarity",
16499
+ "creative",
16500
+ "strategy"
16501
+ ],
16502
+ domains: [
16503
+ "coaching",
16504
+ "education",
16505
+ "creative",
16506
+ "technology"
16507
+ ],
16508
+ tone: ["practical", "playful"],
16509
+ presets: featurePreset
16510
+ },
16511
+ {
16512
+ id: "direction",
16513
+ label: "Direction",
16514
+ tags: [
16515
+ "direction",
16516
+ "navigation",
16517
+ "path",
16518
+ "journey",
16519
+ "guidance"
16520
+ ],
16521
+ domains: [
16522
+ "coaching",
16523
+ "education",
16524
+ "services"
16525
+ ],
16526
+ tone: ["trustworthy", "practical"],
16527
+ presets: featurePreset
16528
+ },
16529
+ {
16530
+ id: "focus",
16531
+ label: "Focus",
16532
+ tags: [
16533
+ "focus",
16534
+ "goal",
16535
+ "outcome",
16536
+ "aim",
16537
+ "strategy"
16538
+ ],
16539
+ domains: [
16540
+ "coaching",
16541
+ "services",
16542
+ "technology"
16543
+ ],
16544
+ tone: ["practical"],
16545
+ presets: featurePreset
16546
+ },
16547
+ {
16548
+ id: "quality",
16549
+ label: "Quality",
16550
+ tags: [
16551
+ "quality",
16552
+ "award",
16553
+ "standard",
16554
+ "achievement",
16555
+ "premium"
16556
+ ],
16557
+ domains: [
16558
+ "services",
16559
+ "education",
16560
+ "commerce"
16561
+ ],
16562
+ tone: ["premium", "trustworthy"],
16563
+ presets: featurePreset
16564
+ },
16565
+ {
16566
+ id: "highlight",
16567
+ label: "Highlight",
16568
+ tags: [
16569
+ "highlight",
16570
+ "featured",
16571
+ "favorite",
16572
+ "quality",
16573
+ "special"
16574
+ ],
16575
+ domains: [
16576
+ "creative",
16577
+ "commerce",
16578
+ "services"
16579
+ ],
16580
+ tone: ["playful", "premium"],
16581
+ presets: featurePreset
16582
+ },
16583
+ {
16584
+ id: "verified-quality",
16585
+ label: "Verified quality",
16586
+ tags: [
16587
+ "verified",
16588
+ "quality",
16589
+ "approved",
16590
+ "trusted",
16591
+ "check"
16592
+ ],
16593
+ domains: [
16594
+ "services",
16595
+ "commerce",
16596
+ "education"
16597
+ ],
16598
+ tone: ["trustworthy", "practical"],
16599
+ presets: featurePreset
16600
+ },
16601
+ {
16602
+ id: "learning",
16603
+ label: "Learning",
16604
+ tags: [
16605
+ "learning",
16606
+ "book",
16607
+ "course",
16608
+ "knowledge",
16609
+ "education"
16610
+ ],
16611
+ domains: [
16612
+ "education",
16613
+ "coaching",
16614
+ "community"
16615
+ ],
16616
+ tone: ["calm", "practical"],
16617
+ presets: featurePreset
16618
+ },
16619
+ {
16620
+ id: "training",
16621
+ label: "Training",
16622
+ tags: [
16623
+ "training",
16624
+ "course",
16625
+ "qualification",
16626
+ "school",
16627
+ "education"
16628
+ ],
16629
+ domains: ["education", "coaching"],
16630
+ tone: ["trustworthy", "practical"],
16631
+ presets: featurePreset
16632
+ },
16633
+ {
16634
+ id: "programmes-dates",
16635
+ label: "Programmes and dates",
16636
+ tags: [
16637
+ "calendar",
16638
+ "dates",
16639
+ "schedule",
16640
+ "events",
16641
+ "programme",
16642
+ "classes"
16643
+ ],
16644
+ domains: [
16645
+ "events",
16646
+ "education",
16647
+ "community"
16648
+ ],
16649
+ tone: ["practical"],
16650
+ presets: featurePreset
16651
+ },
16652
+ {
16653
+ id: "place",
16654
+ label: "Place",
16655
+ tags: [
16656
+ "place",
16657
+ "location",
16658
+ "local",
16659
+ "venue",
16660
+ "destination"
16661
+ ],
16662
+ domains: [
16663
+ "events",
16664
+ "hospitality",
16665
+ "services"
16666
+ ],
16667
+ tone: ["practical"],
16668
+ presets: featurePreset
16669
+ },
16670
+ {
16671
+ id: "products",
16672
+ label: "Products",
16673
+ tags: [
16674
+ "products",
16675
+ "shop",
16676
+ "store",
16677
+ "retail",
16678
+ "commerce"
16679
+ ],
16680
+ domains: ["commerce"],
16681
+ tone: ["practical", "premium"],
16682
+ presets: featurePreset
16683
+ },
16684
+ {
16685
+ id: "packages",
16686
+ label: "Packages",
16687
+ tags: [
16688
+ "package",
16689
+ "bundle",
16690
+ "delivery",
16691
+ "offer",
16692
+ "product"
16693
+ ],
16694
+ domains: ["commerce", "services"],
16695
+ tone: ["practical"],
16696
+ presets: featurePreset
16697
+ },
16698
+ {
16699
+ id: "time",
16700
+ label: "Time",
16701
+ tags: [
16702
+ "time",
16703
+ "duration",
16704
+ "schedule",
16705
+ "availability",
16706
+ "appointment"
16707
+ ],
16708
+ domains: [
16709
+ "events",
16710
+ "services",
16711
+ "wellbeing"
16712
+ ],
16713
+ tone: ["practical", "calm"],
16714
+ presets: featurePreset
16715
+ },
16716
+ {
16717
+ id: "practical-service",
16718
+ label: "Practical service",
16719
+ tags: [
16720
+ "service",
16721
+ "repair",
16722
+ "setup",
16723
+ "practical",
16724
+ "tools",
16725
+ "support"
16726
+ ],
16727
+ domains: ["services", "technology"],
16728
+ tone: ["practical"],
16729
+ presets: featurePreset
16730
+ },
16731
+ {
16732
+ id: "at-home",
16733
+ label: "At home",
16734
+ tags: [
16735
+ "home",
16736
+ "domestic",
16737
+ "comfort",
16738
+ "local",
16739
+ "space"
16740
+ ],
16741
+ domains: [
16742
+ "hospitality",
16743
+ "services",
16744
+ "community"
16745
+ ],
16746
+ tone: ["warm", "calm"],
16747
+ presets: featurePreset
16748
+ },
16749
+ {
16750
+ id: "worldwide",
16751
+ label: "Worldwide",
16752
+ tags: [
16753
+ "global",
16754
+ "online",
16755
+ "worldwide",
16756
+ "remote",
16757
+ "international"
16758
+ ],
16759
+ domains: [
16760
+ "technology",
16761
+ "education",
16762
+ "services"
16763
+ ],
16764
+ tone: ["practical"],
16765
+ presets: featurePreset
16766
+ },
16767
+ {
16768
+ id: "professional-service",
16769
+ label: "Professional service",
16770
+ tags: [
16771
+ "business",
16772
+ "professional",
16773
+ "consulting",
16774
+ "service",
16775
+ "work"
16776
+ ],
16777
+ domains: [
16778
+ "services",
16779
+ "technology",
16780
+ "coaching"
16781
+ ],
16782
+ tone: ["practical", "trustworthy"],
16783
+ presets: featurePreset
16784
+ }
16785
+ ].map(({ id, label }) => ({
16786
+ id,
16787
+ label
16788
+ }));
16789
+ function isSemanticIconId(value) {
16790
+ return typeof value === "string" && SEMANTIC_ICON_IDS.includes(value);
16791
+ }
16792
+ //#endregion
16793
+ //#region ../blocks/src/system/icons/value.ts
16794
+ const NO_BLOCK_ICON_VALUE = { kind: "none" };
16795
+ function isRecord$11(value) {
16796
+ return typeof value === "object" && value !== null && !Array.isArray(value);
16797
+ }
16798
+ function parseBlockIconValue(value) {
16799
+ if (isSemanticIconId(value)) return {
16800
+ kind: "semantic",
16801
+ iconId: value
16802
+ };
16803
+ if (!isRecord$11(value)) return NO_BLOCK_ICON_VALUE;
16804
+ switch (value.kind) {
16805
+ case "none": return NO_BLOCK_ICON_VALUE;
16806
+ case "semantic": return isSemanticIconId(value.iconId) ? {
16807
+ kind: "semantic",
16808
+ iconId: value.iconId
16809
+ } : NO_BLOCK_ICON_VALUE;
16810
+ case "generated": {
16811
+ const setId = parseGeneratedIconSetId(value.setId);
16812
+ const assetId = parseGeneratedIconAssetId(value.assetId);
16813
+ const styleGuideVersionId = parseSiteIconStyleGuideVersionId(value.styleGuideVersionId);
16814
+ if (setId !== null && assetId !== null && styleGuideVersionId !== null) return {
16815
+ kind: "generated",
16816
+ setId,
16817
+ assetId,
16818
+ styleGuideVersionId
16819
+ };
16820
+ return NO_BLOCK_ICON_VALUE;
16821
+ }
16822
+ default: return NO_BLOCK_ICON_VALUE;
16823
+ }
16824
+ }
16825
+ //#endregion
16826
+ //#region ../blocks/src/lib/typeGuards.ts
16827
+ /**
16828
+ * Strict record guard: excludes null, primitives, and arrays.
16829
+ */
16830
+ function isRecord$10(value) {
16831
+ return typeof value === "object" && value !== null && !Array.isArray(value);
16832
+ }
16833
+ /**
16834
+ * Non-null object guard: includes arrays for behavior parity with legacy helpers.
16835
+ *
16836
+ * NOTE: This intentionally narrows to UnknownRecord for compatibility with existing
16837
+ * callsites even though arrays pass at runtime. Follow-up cleanup should replace
16838
+ * this API with a more precise predicate type and migrate consumers.
16839
+ */
16840
+ function isObjectRecord$1(value) {
16841
+ return typeof value === "object" && value !== null;
16842
+ }
16843
+ //#endregion
16844
+ //#region ../blocks/src/system/manifest/richTextSchema.ts
16845
+ /**
16846
+ * Shared Rich Text Schema
16847
+ *
16848
+ * This module provides a single source of truth for TipTap rich text validation
16849
+ * across both the block-form editor and the block rendering system.
16850
+ *
16851
+ * Rich-text fields use one canonical root shape:
16852
+ * `{ type: 'doc', content: [...] }`.
16853
+ */
16854
+ /**
16855
+ * TipTap document node schema (recursive structure)
16856
+ *
16857
+ * Supports the full TipTap document structure including:
16858
+ * - type: Node type (e.g., 'doc', 'paragraph', 'text')
16859
+ * - content: Nested array of child nodes
16860
+ * - text: Text content for text nodes
16861
+ * - attrs: Node attributes (e.g., alignment, color)
16862
+ * - marks: Text formatting marks (e.g., bold, italic)
16863
+ */
16864
+ const TipTapMarkSchema = z.object({
16865
+ type: z.string().min(1),
16866
+ attrs: z.record(z.string(), z.unknown()).optional()
16867
+ }).strict();
16868
+ const TipTapNodeSchema = z.lazy(() => z.object({
16869
+ type: z.string().min(1),
16870
+ content: z.array(TipTapNodeSchema).optional(),
16871
+ text: z.string().optional(),
16872
+ attrs: z.record(z.string(), z.unknown()).optional(),
16873
+ marks: z.array(TipTapMarkSchema).optional()
16874
+ }).strict().superRefine((node, ctx) => {
16875
+ if (node.type === "doc") ctx.addIssue({
16876
+ code: "custom",
16877
+ path: ["type"],
16878
+ message: "Document nodes are only valid at the rich-text root"
16879
+ });
16880
+ if (node.type === "text") {
16881
+ if (node.text === void 0 || node.text.length === 0) ctx.addIssue({
16882
+ code: "custom",
16883
+ path: ["text"],
16884
+ message: "Text nodes require a non-empty text value"
16885
+ });
16886
+ if (node.content !== void 0) ctx.addIssue({
16887
+ code: "custom",
16888
+ path: ["content"],
16889
+ message: "Text nodes cannot contain child nodes"
16890
+ });
16891
+ return;
16892
+ }
16893
+ if (node.text !== void 0) ctx.addIssue({
16894
+ code: "custom",
16895
+ path: ["text"],
16896
+ message: "Only text nodes can contain a text value"
16897
+ });
16898
+ }));
16899
+ /**
16900
+ * Canonical schema for a TipTap rich-text document root.
16901
+ *
16902
+ * Stricter than {@link TipTapNodeSchema}: `type` is literally `'doc'` and
16903
+ * `content` is required (possibly empty). This is the shape every producer
16904
+ * (dummy content, editor output, migrations) must emit, and the shape every
16905
+ * non-legacy consumer can rely on.
16906
+ *
16907
+ */
16908
+ const RichTextDocSchema = z.object({
16909
+ type: z.literal("doc"),
16910
+ content: z.array(TipTapNodeSchema)
16911
+ }).strict();
16912
+ /**
16913
+ * Type guard for {@link RichTextDoc}.
16914
+ *
16915
+ * Returns `true` only for the canonical unwrapped form. The shared structural
16916
+ * contract lives in `@riverbankcms/core`; parity tests keep its dependency-free
16917
+ * guard aligned with this detailed Zod validator.
16918
+ */
16919
+ function isRichTextDoc(value) {
16920
+ return RichTextDocSchema.safeParse(value).success;
16921
+ }
16922
+ //#endregion
16923
+ //#region ../blocks/src/lib/media.ts
16924
+ const PUBLIC_DOWNLOAD_MEDIA_TYPES = [
16925
+ "document",
16926
+ "spreadsheet",
16927
+ "archive",
16928
+ "audio"
16929
+ ];
16930
+ //#endregion
16931
+ //#region ../blocks/src/system/manifest/fieldValidation/writeCanonicalization.ts
16932
+ function allGroupChildPlans(plan) {
16933
+ return plan.childSelection.kind === "all" ? plan.childSelection.fields : plan.childSelection.tabs.flatMap((tab) => tab.fields);
16934
+ }
16935
+ function selectedGroupChildPlans(plan, value) {
16936
+ if (plan.childSelection.kind === "all") return plan.childSelection.fields;
16937
+ if (!isPlainObject$3(value)) return [];
16938
+ const selectedType = value[plan.childSelection.activeTabField];
16939
+ return plan.childSelection.tabs.find((tab) => tab.typeId === selectedType)?.fields ?? [];
16940
+ }
16941
+ function canonicalizeObjectForWrite(plans, value) {
16942
+ if (!isPlainObject$3(value)) return value;
16943
+ const canonical = { ...value };
16944
+ for (const plan of plans) {
16945
+ if (!(plan.fieldId in canonical)) continue;
16946
+ canonical[plan.fieldId] = canonicalizeFieldValue(plan, canonical[plan.fieldId]);
16947
+ }
16948
+ return omitUndefined(canonical);
16949
+ }
16950
+ function canonicalizeFieldValue(plan, value) {
16951
+ switch (plan.kind) {
16952
+ case "media": return canonicalizeMedia(value);
16953
+ case "group": return plan.childSelection.kind === "discriminatedTabs" ? canonicalizeDiscriminatedGroup(plan, value) : canonicalizeObjectForWrite(plan.childSelection.fields, value);
16954
+ case "repeater": return canonicalizeRepeater(plan, value);
16955
+ case "string":
16956
+ case "number":
16957
+ case "boolean":
16958
+ case "richText":
16959
+ case "inlineText":
16960
+ case "link":
16961
+ case "select":
16962
+ case "passthrough": return value;
16963
+ }
16964
+ }
16965
+ function canonicalizeMedia(value) {
16966
+ if (!isPlainObject$3(value)) return value;
16967
+ const canonical = { ...value };
16968
+ if (mediaIdentifierSchema.safeParse(canonical.identifier).success) deleteEnvSpecificKeys(canonical);
16969
+ return canonical;
16970
+ }
16971
+ function canonicalizeRepeater(plan, value) {
16972
+ if (!isUnknownArray$1(value)) return value;
16973
+ return value.map((item) => {
16974
+ if (!isPlainObject$3(item)) return item;
16975
+ const childPlans = plan.repeatedItemVariants ? plan.repeatedItemVariants.find((variant) => variant.typeId === item._type)?.fields : plan.repeatedItemPlan;
16976
+ return childPlans ? canonicalizeObjectForWrite(childPlans, item) : item;
16977
+ });
16978
+ }
16979
+ function canonicalizeDiscriminatedGroup(plan, value) {
16980
+ if (!isPlainObject$3(value) || plan.childSelection.kind !== "discriminatedTabs") return value;
16981
+ const selection = plan.childSelection;
16982
+ const selectedPlans = selectedGroupChildPlans(plan, value);
16983
+ const selectedIds = rootFieldIds(selectedPlans);
16984
+ const allChildIds = rootFieldIds(allGroupChildPlans(plan));
16985
+ const canonical = omitUndefined(canonicalizeObjectForWrite(selectedPlans, Object.fromEntries(Object.entries(value).filter(([key]) => key === selection.activeTabField || selectedIds.has(key) || !allChildIds.has(key)))));
16986
+ if (plan.valueContract !== "boxBackground") return canonical;
16987
+ const parsed = boxBackgroundSchema.safeParse(canonical);
16988
+ return parsed.success ? parsed.data : canonical;
16989
+ }
16990
+ function rootFieldIds(plans) {
16991
+ return new Set(plans.map((child) => child.fieldId.split(".")[0] ?? child.fieldId));
16992
+ }
16993
+ function omitUndefined(value) {
16994
+ if (isUnknownArray$1(value)) return value.map(omitUndefined);
16995
+ if (!isPlainObject$3(value)) return value;
16996
+ return Object.fromEntries(Object.entries(value).filter(([, field]) => field !== void 0).map(([key, field]) => [key, omitUndefined(field)]));
16997
+ }
16998
+ function isPlainObject$3(value) {
16999
+ return typeof value === "object" && value !== null && !Array.isArray(value);
17000
+ }
17001
+ function isUnknownArray$1(value) {
17002
+ return Array.isArray(value);
17003
+ }
17004
+ //#endregion
17005
+ //#region ../blocks/src/system/manifest/fieldValidation/index.ts
17006
+ const TEL_RE = /^[+()0-9\s-]{3,}$/;
17007
+ const SLUG_PATTERN = /^(?:[a-z0-9]+(?:-[a-z0-9]+)*)(?:\/(?:[a-z0-9]+(?:-[a-z0-9]+)*))*$/;
17008
+ const ALLOWED_URL_PROTOCOLS = ["http:", "https:"];
17009
+ const DATE_PATTERN = /^(?<year>[1-9]\d{3})-(?<month>\d{2})-(?<day>\d{2})$/;
17010
+ const TIME_PATTERN = /^(?<hours>[01]\d|2[0-3]):(?<minutes>[0-5]\d)$/;
17011
+ const DATETIME_PATTERN = /^(?<date>[1-9]\d{3}-\d{2}-\d{2})T(?<time>(?:[01]\d|2[0-3]):[0-5]\d:[0-5]\d)(?:\.\d+)?(?<offset>Z|[+-](?:[01]\d|2[0-3]):[0-5]\d)$/;
17012
+ function assertNever$1(value) {
17013
+ throw new Error(`Unhandled field validation variant: ${JSON.stringify(value)}`);
17014
+ }
17015
+ function validationContext(options = {}) {
17016
+ const isDraft = options.mode === "draft";
17017
+ return {
17018
+ allowNull: options.allowNull ?? true,
17019
+ allowIncomplete: isDraft || (options.allowIncomplete ?? false)
17020
+ };
17021
+ }
17022
+ function strictValidationContext(options = {}) {
17023
+ return {
17024
+ ...validationContext(options),
17025
+ allowNull: options.allowNull ?? false
17026
+ };
17027
+ }
17028
+ function formatFieldPath(path) {
17029
+ return path.map((segment) => String(segment)).join(".");
17030
+ }
17031
+ function toZodIssuePath(path) {
17032
+ return [...path];
17033
+ }
17034
+ function parseFieldPathKey(key) {
17035
+ if (!key) return [];
17036
+ return key.split(".").map((segment) => {
17037
+ const numeric = Number(segment);
17038
+ return Number.isInteger(numeric) && String(numeric) === segment ? numeric : segment;
17039
+ });
17040
+ }
17041
+ function fieldIssueToMessage(issue) {
17042
+ switch (issue.kind) {
17043
+ case "required": return "This field is required";
17044
+ case "wrongType": return `${issue.label} needs ${issue.expected}`;
17045
+ case "tooLong": return `Use ${issue.maximum} characters or fewer`;
17046
+ case "invalidEmail": return "Enter a valid email address";
17047
+ case "invalidPhone": return "Enter a valid phone number";
17048
+ case "invalidPattern": return "Check the format and try again";
17049
+ case "invalidUrl": return "Enter a valid web address";
17050
+ case "invalidDate": return "Choose a valid date";
17051
+ case "invalidTime": return "Choose a valid time";
17052
+ case "invalidDateTime": return "Choose a valid date and time";
17053
+ case "invalidNumber": return `${issue.label} needs a number`;
17054
+ case "tooSmall": return `${issue.label} must be ${issue.minimum} or more`;
17055
+ case "tooLarge": return `${issue.label} must be ${issue.maximum} or less`;
17056
+ case "invalidSlug": return `${issue.label} can only use lowercase letters, numbers, and dashes`;
17057
+ case "invalidOption": return `Choose one of the available options for ${issue.label}`;
17058
+ case "tooFewItems": return `${issue.label} must have at least ${issue.minimum} ${pluralize(issue.itemLabel, issue.minimum)}`;
17059
+ case "tooManyItems": return `${issue.label} must have at most ${issue.maximum} ${pluralize(issue.itemLabel, issue.maximum)}`;
17060
+ case "invalidRichText": return `${issue.label} must be valid rich text`;
17061
+ case "invalidInlineText": return `${issue.label} must be valid styled text`;
17062
+ case "invalidMedia": return `${issue.label} must be a media object with an identifier`;
17063
+ case "invalidBoxBackground": return `${issue.label} must use a valid background type and matching fields`;
17064
+ case "invalidLink": return `${issue.label} must be a valid link`;
17065
+ case "invalidReference": return `${issue.label} must reference a valid item`;
17066
+ default: return assertNever$1(issue);
17067
+ }
17068
+ }
17069
+ function pluralize(label, count) {
17070
+ if (count === 1) return label;
17071
+ return label.endsWith("s") ? label : `${label}s`;
17072
+ }
17073
+ function valueContractForField(field) {
17074
+ return field.ui?.valueContract === "boxBackground" ? { valueContract: "boxBackground" } : {};
17075
+ }
17076
+ function deriveTabGroupValidationPlan(field, path, required) {
17077
+ return {
17078
+ ...makePlan("group", "object", field, path, required, requiredConstraints(required)),
17079
+ ...valueContractForField(field),
17080
+ childSelection: field.activeTabField ? {
17081
+ kind: "discriminatedTabs",
17082
+ activeTabField: field.activeTabField,
17083
+ tabs: field.tabs.map((tab) => ({
17084
+ typeId: tab.id,
17085
+ fields: deriveNestedFieldPlans(tab.fields, path)
17086
+ }))
17087
+ } : {
17088
+ kind: "all",
17089
+ fields: field.tabs.flatMap((tab) => deriveNestedFieldPlans(tab.fields, path))
17090
+ }
17091
+ };
17092
+ }
17093
+ function deriveFieldValidationPlan(field, path = [field.id]) {
17094
+ const required = Boolean(field.required);
17095
+ switch (field.type) {
17096
+ case "text": {
17097
+ const inputType = field.ui?.inputType;
17098
+ if (inputType === "number") {
17099
+ const constraints = commonConstraints(required, { kind: "numberType" });
17100
+ if (typeof field.ui?.min === "number") constraints.push({
17101
+ kind: "numberMin",
17102
+ minimum: field.ui.min
17103
+ });
17104
+ if (typeof field.ui?.max === "number") constraints.push({
17105
+ kind: "numberMax",
17106
+ maximum: field.ui.max
17107
+ });
17108
+ return makePlan("number", "number", field, path, required, constraints);
17109
+ }
17110
+ const constraints = commonConstraints(required, { kind: field.ui?.widget === "iconPicker" ? "blockIconValue" : "textType" });
17111
+ if (inputType === "email") constraints.push({ kind: "emailFormat" });
17112
+ if (inputType === "tel") constraints.push({ kind: "phoneFormat" });
17113
+ if (field.ui?.pattern) constraints.push({
17114
+ kind: "patternFormat",
17115
+ pattern: field.ui.pattern
17116
+ });
17117
+ if (typeof field.maxLength === "number") constraints.push({
17118
+ kind: "maxLength",
17119
+ maximum: field.maxLength
17120
+ });
17121
+ return makePlan("string", "string", field, path, required, constraints);
17122
+ }
17123
+ case "number": {
17124
+ const constraints = commonConstraints(required, { kind: "numberType" });
17125
+ if (typeof field.min === "number") constraints.push({
17126
+ kind: "numberMin",
17127
+ minimum: field.min
17128
+ });
17129
+ if (typeof field.max === "number") constraints.push({
17130
+ kind: "numberMax",
17131
+ maximum: field.max
17132
+ });
17133
+ return makePlan("number", "number", field, path, required, constraints);
17134
+ }
17135
+ case "slug": {
17136
+ const constraints = commonConstraints(required, { kind: "textType" }, { kind: "slugFormat" });
17137
+ if (typeof field.maxLength === "number") constraints.push({
17138
+ kind: "maxLength",
17139
+ maximum: field.maxLength
17140
+ });
17141
+ return makePlan("string", "string", field, path, required, constraints);
17142
+ }
17143
+ case "url": return makePlan("string", "string", field, path, required, commonConstraints(required, { kind: "textType" }, {
17144
+ kind: "urlFormat",
17145
+ allowRelative: Boolean(field.allowRelative)
17146
+ }));
17147
+ case "reference": return makePlan("string", "string", field, path, required, commonConstraints(required, { kind: "referenceText" }));
17148
+ case "date": return makePlan("string", "string", field, path, required, commonConstraints(required, { kind: "textType" }, { kind: "dateFormat" }));
17149
+ case "time": return makePlan("string", "string", field, path, required, commonConstraints(required, { kind: "textType" }, { kind: "timeFormat" }));
17150
+ case "datetime": return makePlan("string", "string", field, path, required, commonConstraints(required, { kind: "textType" }, { kind: "datetimeFormat" }));
17151
+ case "select": return makePlan("select", "select", field, path, required, commonConstraints(required, {
17152
+ kind: "selectOption",
17153
+ source: resolveSelectConstraintSource(field),
17154
+ multiple: Boolean(field.multiple)
17155
+ }));
17156
+ case "richText": return makePlan("richText", "object", field, path, required, commonConstraints(required, { kind: "richTextDoc" }));
17157
+ case "inlineText": {
17158
+ const constraints = commonConstraints(required, { kind: "inlineText" });
17159
+ if (typeof field.maxLength === "number") constraints.push({
17160
+ kind: "maxPlainTextLength",
17161
+ maximum: field.maxLength
17162
+ });
17163
+ return {
17164
+ ...makePlan("inlineText", "array", field, path, required, constraints),
17165
+ ...field.plainTextField ? { plainTextFallbackPath: parseFieldPathKey(field.plainTextField) } : {}
17166
+ };
17167
+ }
17168
+ case "media":
17169
+ case "file": return {
17170
+ ...makePlan("media", "object", field, path, required, commonConstraints(required, { kind: "mediaObject" })),
17171
+ allowedMediaTypes: field.type === "media" ? field.mediaKinds ?? DEFAULT_MEDIA_FIELD_KINDS : field.fileKinds ?? PUBLIC_FILE_FIELD_KINDS
17172
+ };
17173
+ case "link": return makePlan("link", "object", field, path, required, commonConstraints(required, { kind: "linkObject" }));
17174
+ case "boolean": return makePlan("boolean", "boolean", field, path, required, requiredConstraints(required));
17175
+ case "repeater": return {
17176
+ ...makePlan("repeater", "array", field, path, required, commonConstraints(required, { kind: "arrayType" })),
17177
+ ...field.polymorphic && field.itemTypes ? { repeatedItemVariants: deriveRepeatedItemVariants(field, [...path, 0]) } : { repeatedItemPlan: deriveNestedFieldPlans(getRepeaterFields(field), [...path, 0]) },
17178
+ constraints: withRepeaterItemConstraints(field, commonConstraints(required, { kind: "arrayType" }))
17179
+ };
17180
+ case "group":
17181
+ case "modal": return {
17182
+ ...makePlan("group", "object", field, path, required, requiredConstraints(required)),
17183
+ childSelection: {
17184
+ kind: "all",
17185
+ fields: deriveNestedFieldPlans(field.schema?.fields ?? [], path)
17186
+ },
17187
+ allowNullChildren: field.type === "group" && field.ui?.widget === "locationPicker"
17188
+ };
17189
+ case "tabGroup": return deriveTabGroupValidationPlan(field, path, required);
17190
+ case "presetOrCustom":
17191
+ case "contentTypeSelect":
17192
+ case "entryPicker": return makePlan("passthrough", "unknown", field, path, false, []);
17193
+ default: return assertNever$1(field);
17194
+ }
17195
+ }
17196
+ function deriveFieldValidationPlans(fields, parentPath = []) {
17197
+ return fields.map((field) => deriveFieldValidationPlan(field, [...parentPath, field.id]));
17198
+ }
17199
+ function commonConstraints(required, ...specific) {
17200
+ return required ? [{ kind: "required" }, ...specific] : [...specific];
17201
+ }
17202
+ function requiredConstraints(required) {
17203
+ return required ? [{ kind: "required" }] : [];
17204
+ }
17205
+ function makePlan(kind, valueKind, field, path, required, constraints) {
17206
+ return {
17207
+ kind,
17208
+ valueKind,
17209
+ fieldId: field.id,
17210
+ label: field.label,
17211
+ fieldType: field.type,
17212
+ path,
17213
+ required,
17214
+ visibility: fieldVisibility(field),
17215
+ constraints
17216
+ };
17217
+ }
17218
+ function fieldVisibility(field) {
17219
+ if (field.ui?.hidden) return { kind: "hidden" };
17220
+ if (field.visibleRoles && field.visibleRoles.length > 0) return {
17221
+ kind: "roleRestricted",
17222
+ roles: field.visibleRoles
17223
+ };
17224
+ return { kind: "visible" };
17225
+ }
17226
+ function withRepeaterItemConstraints(field, constraints) {
17227
+ const next = [...constraints];
17228
+ if (typeof field.minItems === "number") next.push({
17229
+ kind: "minItems",
17230
+ minimum: field.minItems
17231
+ });
17232
+ if (typeof field.maxItems === "number") next.push({
17233
+ kind: "maxItems",
17234
+ maximum: field.maxItems
17235
+ });
17236
+ return next;
17237
+ }
17238
+ function deriveRepeatedItemVariants(field, parentPath) {
17239
+ return Object.entries(field.itemTypes ?? {}).map(([typeId, itemType]) => ({
17240
+ typeId,
17241
+ fields: deriveNestedFieldPlans(getFlattenedRepeaterFields(itemType.fields), parentPath)
17242
+ }));
17243
+ }
17244
+ function deriveNestedFieldPlans(fields, parentPath) {
17245
+ return deriveFieldValidationPlans(fields, parentPath);
17246
+ }
17247
+ function getRepeaterFields(field) {
17248
+ return getFlattenedRepeaterFields(field.polymorphic && field.itemTypes ? Object.values(field.itemTypes).flatMap((itemType) => itemType.fields) : field.schema?.fields ?? []);
17249
+ }
17250
+ function getFlattenedRepeaterFields(fields) {
17251
+ return fields.flatMap((child) => {
17252
+ if (child.type === "group" && child.ui?.flattenInRepeater) return child.schema?.fields ?? [];
17253
+ return child;
17254
+ });
17255
+ }
17256
+ function resolveSelectConstraintSource(field) {
17257
+ const source = normalizeSelectSource(field);
17258
+ switch (source.kind) {
17259
+ case "static": return {
17260
+ kind: "static",
17261
+ values: field.options.map((option) => option.value)
17262
+ };
17263
+ case "site":
17264
+ case "sdk": return {
17265
+ kind: "runtime",
17266
+ source: source.kind
17267
+ };
17268
+ default: return assertNever$1(source);
17269
+ }
17270
+ }
17271
+ function normalizeStoredContentValues(plans, content, options = {}) {
17272
+ if (!content || typeof content !== "object" || Array.isArray(content)) return {};
17273
+ const planMap = new Map(plans.map((plan) => [plan.fieldId, plan]));
17274
+ const normalized = {};
17275
+ const ctx = validationContext(options);
17276
+ for (const [key, value] of Object.entries(content)) {
17277
+ const plan = planMap.get(key);
17278
+ normalized[key] = plan ? normalizeStoredFieldValueWithContext(plan, value, ctx) : value;
17279
+ }
17280
+ normalizeInlineTextFallbacks(plans, normalized, ctx);
17281
+ return normalized;
17282
+ }
17283
+ /**
17284
+ * Produces the portable representation accepted by content persistence.
17285
+ * Runtime-hydrated media fields are stripped only when a canonical identifier
17286
+ * is already present; unresolved environment-specific references remain
17287
+ * invalid and must be handled by the site-scoped media write normalizer first.
17288
+ */
17289
+ function normalizeManifestContentForWrite(plans, content, options = {}) {
17290
+ return canonicalizeObjectForWrite(plans, normalizeStoredContentValues(plans, content, options));
17291
+ }
17292
+ function normalizeStoredFieldValueWithContext(plan, value, ctx) {
17293
+ if (value === null && !plan.required) return;
17294
+ switch (plan.kind) {
17295
+ case "group": return normalizeStoredObjectChildren(selectedGroupChildPlans(plan, value), value, childValidationContext(plan, ctx));
17296
+ case "repeater": return normalizeStoredRepeaterItems(plan, value, ctx);
17297
+ case "string":
17298
+ case "number":
17299
+ case "boolean":
17300
+ case "richText":
17301
+ case "inlineText":
17302
+ case "media":
17303
+ case "link":
17304
+ case "select":
17305
+ case "passthrough": return normalizeFieldValue(plan, value, ctx);
17306
+ default: return assertNever$1(plan);
17307
+ }
17308
+ }
17309
+ function normalizeStoredObjectChildren(children, value, ctx) {
17310
+ if (!value || typeof value !== "object" || Array.isArray(value)) return value;
17311
+ const normalized = { ...value };
17312
+ for (const child of children) normalized[child.fieldId] = normalizeStoredFieldValueWithContext(child, normalized[child.fieldId], ctx);
17313
+ normalizeInlineTextFallbacks(children, normalized, ctx);
17314
+ return normalized;
17315
+ }
17316
+ function normalizeStoredRepeaterItems(plan, value, ctx) {
17317
+ if (!isUnknownArray(value)) return value;
17318
+ return value.map((item) => {
17319
+ if (!item || typeof item !== "object" || Array.isArray(item)) return item;
17320
+ const itemRecord = item;
17321
+ const fields = plan.repeatedItemVariants ? plan.repeatedItemVariants.find((variant) => variant.typeId === itemRecord._type)?.fields : plan.repeatedItemPlan;
17322
+ if (!fields) return item;
17323
+ return normalizeStoredObjectChildren(fields, itemRecord, ctx);
17324
+ });
17325
+ }
17326
+ function normalizeFieldValue(plan, value, ctx) {
17327
+ switch (plan.kind) {
17328
+ case "number": return normalizeNumberInput(value, ctx.allowNull);
17329
+ case "string": return acceptsOptionalBlankAsMissing(plan, value) ? void 0 : value;
17330
+ case "group": return normalizeObjectChildren(selectedGroupChildPlans(plan, value), value, childValidationContext(plan, ctx));
17331
+ case "repeater": return normalizeRepeaterItems(plan, value, ctx);
17332
+ case "boolean":
17333
+ case "richText":
17334
+ case "inlineText":
17335
+ case "link":
17336
+ case "select":
17337
+ case "passthrough": return value;
17338
+ case "media": return value;
17339
+ default: return assertNever$1(plan);
17340
+ }
17341
+ }
17342
+ function normalizeObjectChildren(children, value, ctx) {
17343
+ if (!value || typeof value !== "object" || Array.isArray(value)) return value;
17344
+ const normalized = { ...value };
17345
+ for (const child of children) normalized[child.fieldId] = normalizeFieldValue(child, normalized[child.fieldId], ctx);
17346
+ normalizeInlineTextFallbacks(children, normalized, ctx);
17347
+ return normalized;
17348
+ }
17349
+ function normalizeInlineTextFallbacks(plans, values, ctx) {
17350
+ for (const plan of plans) {
17351
+ if (plan.kind !== "inlineText" || !plan.plainTextFallbackPath) continue;
17352
+ if (!isMissingForPlan(plan, values[plan.fieldId]) && !(ctx.allowIncomplete && !parseInlineTextFromUnknown(values[plan.fieldId]).ok)) continue;
17353
+ const fallback = getAtFieldPath(values, plan.plainTextFallbackPath);
17354
+ if (typeof fallback !== "string" || fallback.trim().length === 0) continue;
17355
+ values[plan.fieldId] = normalizeFieldValue(plan, plainTextToInlineText(fallback), ctx);
17356
+ }
17357
+ }
17358
+ function getAtFieldPath(source, path) {
17359
+ return path.reduce((current, segment) => {
17360
+ if (!current || typeof current !== "object" || Array.isArray(current)) return void 0;
17361
+ return current[String(segment)];
17362
+ }, source);
17363
+ }
17364
+ function normalizeRepeaterItems(plan, value, ctx) {
17365
+ if (!isUnknownArray(value)) return value;
17366
+ return value.map((item) => {
17367
+ if (!item || typeof item !== "object" || Array.isArray(item)) return item;
17368
+ const itemRecord = item;
17369
+ const fields = plan.repeatedItemVariants ? plan.repeatedItemVariants.find((variant) => variant.typeId === itemRecord._type)?.fields : plan.repeatedItemPlan;
17370
+ if (!fields) return item;
17371
+ return normalizeObjectChildren(fields, itemRecord, ctx);
17372
+ });
17373
+ }
17374
+ function normalizeNumberInput(value, _allowNull) {
17375
+ if (value === "") return void 0;
17376
+ if (typeof value === "string" && value.trim() !== "") return Number(value);
17377
+ return value;
17378
+ }
17379
+ function acceptsOptionalBlankAsMissing(plan, value) {
17380
+ if (plan.required || typeof value !== "string" || value.trim() !== "") return false;
17381
+ return plan.constraints.some((constraint) => constraint.kind === "urlFormat" || constraint.kind === "slugFormat" || constraint.kind === "emailFormat" || constraint.kind === "phoneFormat" || constraint.kind === "patternFormat");
17382
+ }
17383
+ function validateNormalizedFieldValue(plan, value, ctx) {
17384
+ if (value === null && (ctx.allowNull || acceptsOptionalNullValue(plan))) return [];
17385
+ if (isMissingForPlan(plan, value)) return shouldRequireValue(plan, value, ctx) ? [requiredIssue(plan, value)] : [];
17386
+ switch (plan.kind) {
17387
+ case "string": return validateStringPlan(plan, value);
17388
+ case "number": return validateNumberPlan(plan, value);
17389
+ case "boolean": return typeof value === "boolean" ? [] : [issue(plan, "wrongType", { expected: "a true/false value" })];
17390
+ case "select": return validateSelectPlan(plan, value);
17391
+ case "richText": return isValidRichTextDoc(value) ? [] : [issue(plan, "invalidRichText")];
17392
+ case "inlineText": return validateInlineTextPlan(plan, value);
17393
+ case "media": return isPlainObject$2(value) && mediaIdentifierSchema.safeParse(value.identifier).success && (value.type === void 0 || typeof value.type === "string" && plan.allowedMediaTypes.includes(value.type)) ? [] : [issue(plan, "invalidMedia")];
17394
+ case "link": return linkSchema.safeParse(value).success ? [] : [issue(plan, "invalidLink")];
17395
+ case "repeater": return validateRepeaterPlan(plan, value, ctx);
17396
+ case "group": return validateGroupPlan(plan, value, ctx);
17397
+ case "passthrough": return [];
17398
+ default: return assertNever$1(plan);
17399
+ }
17400
+ }
17401
+ function shouldRequireValue(plan, value, ctx) {
17402
+ if (!plan.required || ctx.allowIncomplete) return false;
17403
+ if (value === null && ctx.allowNull) return false;
17404
+ return true;
17405
+ }
17406
+ function acceptsOptionalNullValue(plan) {
17407
+ return !plan.required && (plan.kind === "media" || plan.kind === "number");
17408
+ }
17409
+ function isMissingForPlan(plan, value) {
17410
+ if (value === void 0) return true;
17411
+ if (value === null) return plan.required;
17412
+ if (typeof value === "string" && value.trim() === "") return true;
17413
+ if (Array.isArray(value) && value.length === 0) return true;
17414
+ return false;
17415
+ }
17416
+ function validateStringPlan(plan, value) {
17417
+ if (plan.constraints.some((constraint) => constraint.kind === "blockIconValue")) return plan.constraints.flatMap((constraint) => validateStringConstraint(plan, constraint, value));
17418
+ if (typeof value !== "string") return [issue(plan, "wrongType", { expected: "text" })];
17419
+ return plan.constraints.flatMap((constraint) => validateStringConstraint(plan, constraint, value));
17420
+ }
17421
+ function validateStringConstraint(plan, constraint, value) {
17422
+ switch (constraint.kind) {
17423
+ case "required": return [];
17424
+ case "textType":
17425
+ case "referenceText": return typeof value === "string" ? [] : [issue(plan, "wrongType", { expected: "text" })];
17426
+ case "blockIconValue": return isValidIconPickerValue(value) ? [] : [issue(plan, "wrongType", { expected: "text or icon" })];
17427
+ case "maxLength": return typeof value === "string" && value.length > constraint.maximum ? [issue(plan, "tooLong", { maximum: constraint.maximum })] : [];
17428
+ case "emailFormat": return typeof value === "string" && isCanonicalEmailValue(value) ? [] : [issue(plan, "invalidEmail")];
17429
+ case "phoneFormat": return typeof value === "string" && isCanonicalPhoneValue(value) ? [] : [issue(plan, "invalidPhone")];
17430
+ case "patternFormat": {
17431
+ if (typeof value !== "string") return [issue(plan, "wrongType", { expected: "text" })];
17432
+ const re = compilePattern(constraint.pattern);
17433
+ return re && !re.test(value) ? [issue(plan, "invalidPattern")] : [];
17434
+ }
17435
+ case "urlFormat": return typeof value === "string" && isCanonicalUrlValue(value, { allowRelative: constraint.allowRelative }) ? [] : [issue(plan, "invalidUrl")];
17436
+ case "dateFormat": return typeof value === "string" && isValidDateString(value) ? [] : [issue(plan, "invalidDate")];
17437
+ case "timeFormat": return typeof value === "string" && isValidTimeString(value) ? [] : [issue(plan, "invalidTime")];
17438
+ case "datetimeFormat": return typeof value === "string" && isValidDateTimeInstantString(value) ? [] : [issue(plan, "invalidDateTime")];
17439
+ case "slugFormat": return typeof value === "string" && SLUG_PATTERN.test(value) ? [] : [issue(plan, "invalidSlug")];
17440
+ default: return assertNever$1(constraint);
17441
+ }
17442
+ }
17443
+ function isValidIconPickerValue(value) {
17444
+ if (typeof value === "string") return true;
17445
+ if (!value || typeof value !== "object" || Array.isArray(value)) return false;
17446
+ if (value.kind === "none") return true;
17447
+ return parseBlockIconValue(value).kind !== "none";
17448
+ }
17449
+ function validateNumberPlan(plan, value) {
17450
+ if (!isValidNumber(value)) return [issue(plan, "invalidNumber")];
17451
+ return plan.constraints.flatMap((constraint) => validateNumberConstraint(plan, constraint, Number(value)));
17452
+ }
17453
+ function validateNumberConstraint(plan, constraint, value) {
17454
+ switch (constraint.kind) {
17455
+ case "required":
17456
+ case "numberType": return [];
17457
+ case "numberMin": return value < constraint.minimum ? [issue(plan, "tooSmall", { minimum: constraint.minimum })] : [];
17458
+ case "numberMax": return value > constraint.maximum ? [issue(plan, "tooLarge", { maximum: constraint.maximum })] : [];
17459
+ default: return assertNever$1(constraint);
17460
+ }
17461
+ }
17462
+ function validateSelectPlan(plan, value) {
17463
+ const constraint = plan.constraints.find((candidate) => candidate.kind === "selectOption");
17464
+ if (!constraint || constraint.source.kind === "runtime") return [];
17465
+ return isValidSelectValue(value, constraint.source.values, constraint.multiple) ? [] : [issue(plan, "invalidOption")];
17466
+ }
17467
+ function validateRepeaterPlan(plan, value, ctx) {
17468
+ if (!Array.isArray(value)) return [issue(plan, "wrongType", { expected: "a list" })];
17469
+ return [...plan.constraints.flatMap((constraint) => {
17470
+ switch (constraint.kind) {
17471
+ case "required":
17472
+ case "arrayType": return [];
17473
+ case "minItems": return value.length < constraint.minimum ? [issue(plan, "tooFewItems", {
17474
+ minimum: constraint.minimum,
17475
+ itemLabel: "item"
17476
+ })] : [];
17477
+ case "maxItems": return value.length > constraint.maximum ? [issue(plan, "tooManyItems", {
17478
+ maximum: constraint.maximum,
17479
+ itemLabel: "item"
17480
+ })] : [];
17481
+ default: return assertNever$1(constraint);
17482
+ }
17483
+ }), ...value.flatMap((item, index) => validateRepeaterItem(plan, item, index, ctx))];
17484
+ }
17485
+ function validateRepeaterItem(plan, item, index, ctx) {
17486
+ if (!isPlainObject$2(item)) return [{
17487
+ kind: "wrongType",
17488
+ path: [...plan.path, index],
17489
+ fieldId: plan.fieldId,
17490
+ label: plan.label,
17491
+ expected: "an object"
17492
+ }];
17493
+ const itemType = item._type;
17494
+ const variant = plan.repeatedItemVariants ? plan.repeatedItemVariants.find((candidate) => candidate.typeId === itemType) : void 0;
17495
+ if (plan.repeatedItemVariants && !variant) return [{
17496
+ kind: "invalidOption",
17497
+ path: [
17498
+ ...plan.path,
17499
+ index,
17500
+ "_type"
17501
+ ],
17502
+ fieldId: "_type",
17503
+ label: `${plan.label} item type`
17504
+ }];
17505
+ const fields = variant?.fields ?? plan.repeatedItemPlan;
17506
+ if (!fields) return [];
17507
+ return fields.flatMap((childPlan) => {
17508
+ return validateNormalizedFieldValue(materializeRepeaterItemPlan(childPlan, plan.path, index), item[childPlan.fieldId], ctx);
17509
+ });
17510
+ }
17511
+ function validateGroupPlan(plan, value, ctx) {
17512
+ if (!isPlainObject$2(value)) return [issue(plan, "wrongType", { expected: "an object" })];
17513
+ if (plan.childSelection.kind === "discriminatedTabs") {
17514
+ const selectedType = value[plan.childSelection.activeTabField];
17515
+ if (selectedType !== void 0 && selectedType !== null && !plan.childSelection.tabs.some((tab) => tab.typeId === selectedType)) return [{
17516
+ kind: "invalidOption",
17517
+ path: [...plan.path, plan.childSelection.activeTabField],
17518
+ fieldId: plan.childSelection.activeTabField,
17519
+ label: `${plan.label} type`
17520
+ }];
17521
+ }
17522
+ const childCtx = childValidationContext(plan, ctx);
17523
+ const childIssues = selectedGroupChildPlans(plan, value).flatMap((childPlan) => validateNormalizedFieldValue(childPlan, value[childPlan.fieldId], childCtx));
17524
+ if (childIssues.length > 0) return childIssues;
17525
+ if (plan.valueContract === "boxBackground" && !boxBackgroundSchema.safeParse(value).success) return [issue(plan, "invalidBoxBackground")];
17526
+ return [];
17527
+ }
17528
+ function childValidationContext(plan, ctx) {
17529
+ return plan.allowNullChildren ? {
17530
+ ...ctx,
17531
+ allowNull: true
17532
+ } : ctx;
17533
+ }
17534
+ function materializeRepeaterItemPlan(plan, parentPath, index) {
17535
+ return rebaseFieldPlanPath(plan, [...parentPath, 0], [...parentPath, index]);
17536
+ }
17537
+ function rebaseFieldPlanPath(plan, fromPrefix, toPrefix) {
17538
+ const path = rebaseFieldPath(plan.path, fromPrefix, toPrefix);
17539
+ switch (plan.kind) {
17540
+ case "group": return {
17541
+ ...plan,
17542
+ path,
17543
+ childSelection: plan.childSelection.kind === "all" ? {
17544
+ kind: "all",
17545
+ fields: plan.childSelection.fields.map((childPlan) => rebaseFieldPlanPath(childPlan, fromPrefix, toPrefix))
17546
+ } : {
17547
+ ...plan.childSelection,
17548
+ tabs: plan.childSelection.tabs.map((tab) => ({
17549
+ ...tab,
17550
+ fields: tab.fields.map((childPlan) => rebaseFieldPlanPath(childPlan, fromPrefix, toPrefix))
17551
+ }))
17552
+ }
17553
+ };
17554
+ case "repeater": return {
17555
+ ...plan,
17556
+ path,
17557
+ ...plan.repeatedItemVariants ? { repeatedItemVariants: plan.repeatedItemVariants.map((variant) => ({
17558
+ ...variant,
17559
+ fields: variant.fields.map((fieldPlan) => rebaseFieldPlanPath(fieldPlan, fromPrefix, toPrefix))
17560
+ })) } : {},
17561
+ ...plan.repeatedItemPlan ? { repeatedItemPlan: plan.repeatedItemPlan.map((fieldPlan) => rebaseFieldPlanPath(fieldPlan, fromPrefix, toPrefix)) } : {}
17562
+ };
17563
+ case "string":
17564
+ case "number":
17565
+ case "boolean":
17566
+ case "richText":
17567
+ case "inlineText":
17568
+ case "media":
17569
+ case "link":
17570
+ case "select":
17571
+ case "passthrough": return {
17572
+ ...plan,
17573
+ path
17574
+ };
17575
+ default: return assertNever$1(plan);
17576
+ }
17577
+ }
17578
+ function validateInlineTextPlan(plan, value) {
17579
+ const parsed = parseInlineTextFromUnknown(value);
17580
+ if (!parsed.ok) return [issue(plan, "invalidInlineText")];
17581
+ const text = inlineTextToPlainText(parsed.value);
17582
+ const lengthIssue = plan.constraints.find((constraint) => constraint.kind === "maxPlainTextLength");
17583
+ if (lengthIssue && text.length > lengthIssue.maximum) return [issue(plan, "tooLong", { maximum: lengthIssue.maximum })];
17584
+ return [];
17585
+ }
17586
+ function rebaseFieldPath(path, fromPrefix, toPrefix) {
17587
+ if (!startsWithFieldPath(path, fromPrefix)) throw new Error(`Cannot rebase field path ${formatFieldPath(path)} from ${formatFieldPath(fromPrefix)}`);
17588
+ return [...toPrefix, ...path.slice(fromPrefix.length)];
17589
+ }
17590
+ function startsWithFieldPath(path, prefix) {
17591
+ return prefix.every((segment, index) => path[index] === segment);
17592
+ }
17593
+ function issue(plan, kind, extra) {
17594
+ return {
17595
+ kind,
17596
+ path: plan.path,
17597
+ fieldId: plan.fieldId,
17598
+ label: plan.label,
17599
+ ...extra ?? {}
17600
+ };
17601
+ }
17602
+ function requiredIssue(plan, value) {
17603
+ if (typeof value === "string") return issue(plan, "required", { emptyOrigin: "string" });
17604
+ if (Array.isArray(value)) return issue(plan, "required", { emptyOrigin: "array" });
17605
+ return issue(plan, "required");
17606
+ }
17607
+ function fieldPlanToZod(plan, options = {}) {
17608
+ const ctx = validationContext(options);
17609
+ return z.any().transform((value) => normalizeFieldValue(plan, value, ctx)).superRefine((value, zodCtx) => {
17610
+ for (const validationIssue of validateNormalizedFieldValue(plan, value, ctx)) addFieldIssueToZodContext(zodCtx, validationIssue, plan.path);
17611
+ });
17612
+ }
17613
+ function fieldPresenceForPlan(plan) {
17614
+ return plan.required ? { kind: "required" } : { kind: "optional" };
17615
+ }
17616
+ function fieldPlanToObjectPropertyZod(plan, options = {}) {
17617
+ const schema = fieldPlanToZod(plan, options);
17618
+ const presence = fieldPresenceForPlan(plan);
17619
+ switch (presence.kind) {
17620
+ case "required": return schema;
17621
+ case "optional": return schema.optional();
17622
+ default: return assertNever$1(presence);
17623
+ }
17624
+ }
17625
+ /** Validate stored field values without applying editor/write normalization. */
17626
+ function fieldPlanToStoredObjectPropertyZod(plan) {
17627
+ const ctx = strictValidationContext({ allowNull: false });
17628
+ const schema = z.any().superRefine((value, zodCtx) => {
17629
+ for (const validationIssue of validateNormalizedFieldValue(plan, value, ctx)) addFieldIssueToZodContext(zodCtx, validationIssue, plan.path);
17630
+ });
17631
+ return plan.required ? schema : schema.optional();
17632
+ }
17633
+ function addFieldIssueToZodContext(zodCtx, validationIssue, rootPath) {
17634
+ const message = fieldIssueToMessage(validationIssue);
17635
+ const path = relativeZodIssuePath(validationIssue.path, rootPath);
17636
+ const issuePath = path.length > 0 ? { path: toZodIssuePath(path) } : {};
17637
+ switch (validationIssue.kind) {
17638
+ case "required":
17639
+ if (validationIssue.emptyOrigin) {
17640
+ zodCtx.addIssue({
17641
+ ...issuePath,
17642
+ code: "too_small",
17643
+ origin: validationIssue.emptyOrigin,
17644
+ minimum: 1,
17645
+ inclusive: true,
17646
+ message
17647
+ });
17648
+ return;
17649
+ }
17650
+ zodCtx.addIssue({
17651
+ ...issuePath,
17652
+ code: "invalid_type",
17653
+ expected: "nonoptional",
17654
+ message
17655
+ });
17656
+ return;
17657
+ case "tooLong":
17658
+ zodCtx.addIssue({
17659
+ ...issuePath,
17660
+ code: "too_big",
17661
+ origin: "string",
17662
+ maximum: validationIssue.maximum,
17663
+ inclusive: true,
17664
+ message
17665
+ });
17666
+ return;
17667
+ case "invalidOption":
17668
+ zodCtx.addIssue({
17669
+ ...issuePath,
17670
+ code: "invalid_value",
17671
+ values: [],
17672
+ message
17673
+ });
17674
+ return;
17675
+ case "wrongType":
17676
+ case "invalidEmail":
17677
+ case "invalidPhone":
17678
+ case "invalidPattern":
17679
+ case "invalidUrl":
17680
+ case "invalidDate":
17681
+ case "invalidTime":
17682
+ case "invalidDateTime":
17683
+ case "invalidNumber":
17684
+ case "tooSmall":
17685
+ case "tooLarge":
17686
+ case "invalidSlug":
17687
+ case "tooFewItems":
17688
+ case "tooManyItems":
17689
+ case "invalidRichText":
17690
+ case "invalidInlineText":
17691
+ case "invalidMedia":
17692
+ case "invalidBoxBackground":
17693
+ case "invalidLink":
17694
+ case "invalidReference":
17695
+ zodCtx.addIssue({
17696
+ ...issuePath,
17697
+ code: "custom",
17698
+ message
17699
+ });
17700
+ return;
17701
+ default: assertNever$1(validationIssue);
17702
+ }
17703
+ }
17704
+ function relativeZodIssuePath(path, rootPath) {
17705
+ return startsWithFieldPath(path, rootPath) ? path.slice(rootPath.length) : path;
17706
+ }
17707
+ function compilePattern(pattern) {
17708
+ try {
17709
+ return new RegExp(pattern);
17710
+ } catch {
17711
+ return null;
17712
+ }
17713
+ }
17714
+ function isValidNumber(value) {
17715
+ return typeof value === "number" && Number.isFinite(value);
17716
+ }
17717
+ function isCanonicalEmailValue(value) {
17718
+ return z.email().safeParse(value).success;
17719
+ }
17720
+ function isCanonicalPhoneValue(value) {
17721
+ return TEL_RE.test(value);
17722
+ }
17723
+ function isCanonicalUrlValue(value, options = {}) {
17724
+ if (!value) return false;
17725
+ if (options.allowRelative === true && value.startsWith("/")) return true;
17726
+ try {
17727
+ const parsed = new URL(value);
17728
+ return ALLOWED_URL_PROTOCOLS.includes(parsed.protocol);
17729
+ } catch {
17730
+ return false;
17731
+ }
17732
+ }
17733
+ function isValidDateString(value) {
17734
+ const match = DATE_PATTERN.exec(value);
17735
+ if (!match?.groups) return false;
17736
+ return isValidCalendarDate(Number(match.groups.year), Number(match.groups.month), Number(match.groups.day));
17737
+ }
17738
+ function isValidTimeString(value) {
17739
+ return TIME_PATTERN.test(value);
17740
+ }
17741
+ function isValidDateTimeInstantString(value) {
17742
+ const match = DATETIME_PATTERN.exec(value);
17743
+ if (!match?.groups) return false;
17744
+ const datePart = match.groups.date;
17745
+ if (!datePart || !isValidDateString(datePart)) return false;
17746
+ return !Number.isNaN(Date.parse(value));
17747
+ }
17748
+ function isValidCalendarDate(year, month, day) {
17749
+ if (!Number.isInteger(year) || !Number.isInteger(month) || !Number.isInteger(day)) return false;
17750
+ if (year < 1 || month < 1 || month > 12) return false;
17751
+ return day >= 1 && day <= daysInMonth(year, month);
17752
+ }
17753
+ function daysInMonth(year, month) {
17754
+ switch (month) {
17755
+ case 2: return isLeapYear(year) ? 29 : 28;
17756
+ case 4:
17757
+ case 6:
17758
+ case 9:
17759
+ case 11: return 30;
17760
+ case 1:
17761
+ case 3:
17762
+ case 5:
17763
+ case 7:
17764
+ case 8:
17765
+ case 10:
17766
+ case 12: return 31;
17767
+ default: return 0;
17768
+ }
17769
+ }
17770
+ function isLeapYear(year) {
17771
+ return year % 4 === 0 && (year % 100 !== 0 || year % 400 === 0);
17772
+ }
17773
+ function isValidSelectValue(value, values, multiple) {
17774
+ if (multiple) return Array.isArray(value) && value.every((item) => typeof item === "string" && values.includes(item));
17775
+ return typeof value === "string" && values.includes(value);
17776
+ }
17777
+ function isEmptyValue(value) {
17778
+ if (value === null || value === void 0) return true;
17779
+ if (typeof value === "string" && value.trim() === "") return true;
17780
+ if (Array.isArray(value) && value.length === 0) return true;
17781
+ if (isEmptyRichTextDoc(value)) return true;
17782
+ return false;
17783
+ }
17784
+ function isPlainObject$2(value) {
17785
+ return Boolean(value && typeof value === "object" && !Array.isArray(value));
17786
+ }
17787
+ function isUnknownArray(value) {
17788
+ return Array.isArray(value);
17789
+ }
17790
+ function isValidRichTextDoc(value) {
17791
+ return isRichTextDoc(value);
17792
+ }
17793
+ function isEmptyRichTextDoc(value) {
17794
+ if (!isPlainObject$2(value)) return false;
17795
+ if (value.type !== "doc") return false;
17796
+ const content = value.content;
17797
+ if (!Array.isArray(content) || content.length === 0) return true;
17798
+ if (content.length !== 1) return false;
17799
+ const first = content[0];
17800
+ return first?.type === "paragraph" && (!Array.isArray(first.content) || first.content.length === 0);
17801
+ }
17802
+ //#endregion
17803
+ //#region ../blocks/src/system/manifest/registry.ts
17804
+ const REGISTRY_SYMBOL$1 = Symbol.for("@riverbankcms/blocks/manifest-registry");
17805
+ const isBlocksTestRuntime$1 = typeof process !== "undefined" && typeof process.env !== "undefined" && process.env.RIVERBANK_BLOCKS_TEST === "1";
17806
+ const globalScope$1 = globalThis;
17807
+ const manifestStore = isBlocksTestRuntime$1 ? /* @__PURE__ */ new Map() : globalScope$1[REGISTRY_SYMBOL$1] ?? (globalScope$1[REGISTRY_SYMBOL$1] = /* @__PURE__ */ new Map());
17808
+ function registerManifest(manifest) {
17809
+ manifestStore.set(manifest.name, manifest);
17810
+ return manifest;
17811
+ }
17812
+ //#endregion
17813
+ //#region ../blocks/src/system/manifest/validation.ts
17814
+ const compiledManifestContentContract = Symbol("compiledManifestContentContract");
17815
+ /**
17816
+ * Preprocess content by converting empty values to undefined.
17817
+ * Used in draft mode to allow required fields to be temporarily empty.
17818
+ */
17819
+ function preprocessEmptyValues(content) {
17820
+ if (!content || typeof content !== "object" || Array.isArray(content)) return {};
17821
+ const result = {};
17822
+ for (const [key, value] of Object.entries(content)) result[key] = isEmptyValue(value) ? void 0 : value;
17823
+ return result;
17824
+ }
17825
+ /**
17826
+ * Compiles editor-facing manifest structure into the presentation-independent
17827
+ * validation contract used by every manifest-content trust transition.
17828
+ */
17829
+ function compileManifestContentContract(manifestOrFields) {
17830
+ const plans = derivePlansFromManifestOrFields(manifestOrFields);
17831
+ const strictOptions = { allowNull: false };
17832
+ const strictStoredSchema = deriveStrictStoredZodFromPlans(plans);
17833
+ return Object.freeze({ [compiledManifestContentContract]: Object.freeze({
17834
+ plans,
17835
+ strictStoredSchema,
17836
+ writeSchema: deriveNormalizedContentZodFromPlans(plans, strictOptions, normalizeManifestContentForWrite).transform((value) => canonicalizeObjectForWrite(plans, value)),
17837
+ draftSchema: deriveNormalizedContentZodFromPlans(plans, {
17838
+ allowNull: true,
17839
+ allowIncomplete: true,
17840
+ mode: "draft"
17841
+ }, normalizeStoredContentValues),
17842
+ renderSchema: strictStoredSchema,
17843
+ findNonPortableMediaPaths: (content) => findNonPortableMediaPaths(plans, content)
17844
+ }) });
17845
+ }
17846
+ /**
17847
+ * Re-admits renderable content after a transport boundary has erased its
17848
+ * compile-time proof. Unlike write admission, this never canonicalizes or
17849
+ * grants a stored proof, so hydrated media cannot flow back to persistence.
17850
+ */
17851
+ function admitTransportedRenderableManifestContent(contract, content) {
17852
+ const parsed = contract[compiledManifestContentContract].renderSchema.safeParse(content);
17853
+ return parsed.success ? {
17854
+ ok: true,
17855
+ value: content
17856
+ } : {
17857
+ ok: false,
17858
+ error: zodAdmissionError(parsed.error)
17859
+ };
17860
+ }
17861
+ function deriveNormalizedContentZodFromPlans(plans, options, normalize) {
17862
+ const isDraft = options.mode === "draft";
17863
+ const baseSchema = deriveContentZodFromPlans(plans, options);
17864
+ if (isDraft) return z.preprocess((content) => preprocessEmptyValues(normalize(plans, content, options)), baseSchema);
17865
+ return z.preprocess((content) => normalize(plans, content, options), baseSchema);
17866
+ }
17867
+ function deriveContentZodFromPlans(plans, options) {
17868
+ const shape = {};
17869
+ for (const plan of plans) shape[plan.fieldId] = fieldPlanToObjectPropertyZod(plan, options);
17870
+ return z.object(shape).catchall(z.unknown()).passthrough();
17871
+ }
17872
+ function deriveStrictStoredZodFromPlans(plans) {
17873
+ const shape = {};
17874
+ for (const plan of plans) shape[plan.fieldId] = fieldPlanToStoredObjectPropertyZod(plan);
17875
+ return z.object(shape).catchall(z.unknown()).passthrough();
17876
+ }
17877
+ function derivePlansFromManifestOrFields(manifestOrFields) {
17878
+ return deriveFieldValidationPlans(Array.isArray(manifestOrFields) ? manifestOrFields : manifestOrFields.fields ?? []);
17879
+ }
17880
+ function zodAdmissionError(error) {
17881
+ return {
17882
+ kind: "invalid_shape",
17883
+ issues: zodAdmissionIssues(error)
17884
+ };
17885
+ }
17886
+ function zodAdmissionIssues(error) {
17887
+ return error.issues.map((issue) => ({
17888
+ code: issue.code,
17889
+ path: issue.path.map((segment) => typeof segment === "number" ? segment : String(segment)),
17890
+ message: issue.message
17891
+ }));
17892
+ }
17893
+ function findNonPortableMediaPaths(plans, content) {
17894
+ const paths = /* @__PURE__ */ new Set();
17895
+ collectNonPortablePlannedMedia(plans, content, [], paths);
17896
+ return [...paths].sort();
17897
+ }
17898
+ function collectNonPortablePlannedMedia(plans, content, parentPath, paths) {
17899
+ if (!isPlainRecord(content)) return;
17900
+ for (const plan of plans) {
17901
+ if (!(plan.fieldId in content)) continue;
17902
+ const value = content[plan.fieldId];
17903
+ const path = [...parentPath, plan.fieldId];
17904
+ switch (plan.kind) {
17905
+ case "media":
17906
+ if (isPlainRecord(value) && hasEnvironmentSpecificMediaField(value)) paths.add(formatFieldPath(path));
17907
+ break;
17908
+ case "richText":
17909
+ collectNonPortableRichTextImages(value, path, paths);
17910
+ break;
17911
+ case "group":
17912
+ collectNonPortablePlannedMedia(selectedGroupChildPlans(plan, value), value, path, paths);
17913
+ break;
17914
+ case "repeater":
17915
+ collectNonPortableRepeaterMedia(plan, value, path, paths);
17916
+ break;
17917
+ case "string":
17918
+ case "number":
17919
+ case "boolean":
17920
+ case "inlineText":
17921
+ case "link":
17922
+ case "select":
17923
+ case "passthrough": break;
17924
+ }
17925
+ }
17926
+ }
17927
+ function collectNonPortableRepeaterMedia(plan, value, path, paths) {
17928
+ if (!Array.isArray(value)) return;
17929
+ value.forEach((item, index) => {
17930
+ if (!isPlainRecord(item)) return;
17931
+ const childPlans = plan.repeatedItemVariants ? plan.repeatedItemVariants.find((variant) => variant.typeId === item._type)?.fields : plan.repeatedItemPlan;
17932
+ if (childPlans) collectNonPortablePlannedMedia(childPlans, item, [...path, index], paths);
17933
+ });
17934
+ }
17935
+ function collectNonPortableRichTextImages(value, path, paths) {
17936
+ const images = [];
17937
+ walkAndCollectImageAttrs(value, images);
17938
+ const prefix = formatFieldPath(path);
17939
+ for (const reference of images) if (hasEnvironmentSpecificMediaField(reference.attrs)) paths.add(reference.location ? `${prefix}.${reference.location}` : prefix);
17940
+ }
17941
+ function isPlainRecord(value) {
17942
+ return typeof value === "object" && value !== null && !Array.isArray(value);
17943
+ }
17944
+ function hasEnvironmentSpecificMediaField(value) {
17945
+ return "assetId" in value || "storageBucket" in value || "storagePath" in value || "src" in value || "mimeType" in value || looksLikeUuid(value.id);
17946
+ }
15805
17947
  //#endregion
15806
17948
  //#region ../blocks/src/system/designCapabilities.ts
15807
17949
  function declareBlockDesignCapability(capability) {
@@ -23536,26 +25678,6 @@ const headerLogoTextVisibleTransform = {
23536
25678
  return resolveHeaderBrandDisplay(asPartialObject(value)) !== "logo-only";
23537
25679
  }
23538
25680
  };
23539
- /**
23540
- * Selects which classic-layout nav row should render.
23541
- *
23542
- * - `'inline'` → classic variant with the historical logo-left / nav-right row
23543
- * - `'centered'`→ classic variant with a centred (flex-1) nav and a right CTA
23544
- * - `'other'` → not a classic variant (centered/floating handle it)
23545
- *
23546
- * Consumed by the site-header block's `$when` gates so the two classic rows are
23547
- * mutually exclusive without introducing a new top-level header variant.
23548
- */
23549
- const classicNavModeTransform = {
23550
- id: "layout.classicNavMode",
23551
- kind: "formatter",
23552
- summary: "Resolve which classic-layout nav row to render",
23553
- run: (value) => {
23554
- const header = asPartialObject(value);
23555
- if (header?.variant !== "classic") return "other";
23556
- return header?.navPlacement === "center" ? "centered" : "inline";
23557
- }
23558
- };
23559
25681
  const maxWidthClassTransform = {
23560
25682
  id: "layout.maxWidthClass",
23561
25683
  kind: "string",
@@ -23661,7 +25783,6 @@ const headerRootClassTransform = {
23661
25783
  if (isHeaderScrollRuntimeEnabled(content)) classes.push(config.shrink);
23662
25784
  const variant = header?.variant;
23663
25785
  if (variant && headerVariantValues.includes(variant)) classes.push(`header-variant-${variant}`);
23664
- if (header?.barFrame === "inset-shell" && variant === "classic") classes.push("header-bar-frame-inset-shell");
23665
25786
  return joinClasses(classes);
23666
25787
  }
23667
25788
  };
@@ -23869,7 +25990,6 @@ const layoutTransforms = [
23869
25990
  headerLogoClassTransform,
23870
25991
  headerLogoMediaClassTransform,
23871
25992
  headerLogoTextVisibleTransform,
23872
- classicNavModeTransform,
23873
25993
  footerRootClassTransform,
23874
25994
  headerRootStyleTransform,
23875
25995
  headerSectionBackgroundTransform,
@@ -23945,40 +26065,22 @@ const mediaTransforms = [{
23945
26065
  }
23946
26066
  }];
23947
26067
  //#endregion
23948
- //#region ../blocks/src/lib/typeGuards.ts
23949
- /**
23950
- * Strict record guard: excludes null, primitives, and arrays.
23951
- */
23952
- function isRecord$8(value) {
23953
- return typeof value === "object" && value !== null && !Array.isArray(value);
23954
- }
23955
- /**
23956
- * Non-null object guard: includes arrays for behavior parity with legacy helpers.
23957
- *
23958
- * NOTE: This intentionally narrows to UnknownRecord for compatibility with existing
23959
- * callsites even though arrays pass at runtime. Follow-up cleanup should replace
23960
- * this API with a more precise predicate type and migrate consumers.
23961
- */
23962
- function isObjectRecord(value) {
23963
- return typeof value === "object" && value !== null;
23964
- }
23965
- //#endregion
23966
26068
  //#region ../blocks/src/system/transforms/registry/content.ts
23967
26069
  const supportedRichTextTokenSchema = z.enum(["year", "site_name"]);
23968
26070
  const interpolateRichTextTokensOptionsSchema = z.object({ tokens: z.array(supportedRichTextTokenSchema).default(["year", "site_name"]) });
23969
26071
  const richTextTokenPattern = /\{\{\s*([a-zA-Z0-9_]+)\s*\}\}/g;
23970
26072
  function getSiteName(context) {
23971
26073
  const viewModel = context.viewModel;
23972
- if (!isRecord$8(viewModel)) return null;
26074
+ if (!isRecord$10(viewModel)) return null;
23973
26075
  const site = viewModel.site;
23974
- if (isRecord$8(site) && typeof site.title === "string") {
26076
+ if (isRecord$10(site) && typeof site.title === "string") {
23975
26077
  const title = site.title.trim();
23976
26078
  return title.length > 0 ? title : null;
23977
26079
  }
23978
26080
  const root = viewModel.$root;
23979
- if (isRecord$8(root)) {
26081
+ if (isRecord$10(root)) {
23980
26082
  const rootSite = root.site;
23981
- if (isRecord$8(rootSite) && typeof rootSite.title === "string") {
26083
+ if (isRecord$10(rootSite) && typeof rootSite.title === "string") {
23982
26084
  const title = rootSite.title.trim();
23983
26085
  if (title.length > 0) return title;
23984
26086
  }
@@ -24000,7 +26102,7 @@ function replaceTokenText(input, allowedTokens, tokenValues) {
24000
26102
  });
24001
26103
  }
24002
26104
  function visitRichTextNode(node, allowedTokens, tokenValues) {
24003
- if (!isRecord$8(node)) return node;
26105
+ if (!isRecord$10(node)) return node;
24004
26106
  let changed = false;
24005
26107
  const nextNode = { ...node };
24006
26108
  if (nextNode.type === "text" && typeof nextNode.text === "string") {
@@ -24021,7 +26123,7 @@ function visitRichTextNode(node, allowedTokens, tokenValues) {
24021
26123
  return changed ? nextNode : node;
24022
26124
  }
24023
26125
  function interpolateRichTextTokens(value, options, context) {
24024
- if (!isRecord$8(value)) return value;
26126
+ if (!isRecord$10(value)) return value;
24025
26127
  return visitRichTextNode(value, new Set(options.tokens), {
24026
26128
  year: String((/* @__PURE__ */ new Date()).getFullYear()),
24027
26129
  site_name: getSiteName(context)
@@ -24269,18 +26371,7 @@ const classicLayout = inline({
24269
26371
  logoRow,
24270
26372
  createNavRow(`rb-ml-auto ${desktopOnlyDisplay("flex")} rb-gap-6`),
24271
26373
  headerCta
24272
- ], whenTransformed("$root.theme.header", "layout.classicNavMode", "inline"));
24273
- const classicCenteredNavLayout = inline({
24274
- className: bindProp("$root.theme.header.maxWidth", {
24275
- transforms: pipe(tx("layout.maxWidthClass", { base: `rb-header-layout ${desktopOnlyDisplay("flex")} rb-w-full rb-items-center rb-gap-6` })),
24276
- fallback: `rb-container rb-header-layout ${desktopOnlyDisplay("flex")} rb-w-full rb-items-center rb-gap-6`
24277
- }),
24278
- align: "center"
24279
- }, [
24280
- logoRow,
24281
- createNavRow(`rb-flex-1 rb-justify-center ${desktopOnlyDisplay("flex")} rb-gap-6`, "center"),
24282
- headerCta
24283
- ], whenTransformed("$root.theme.header", "layout.classicNavMode", "centered"));
26374
+ ], when("$root.theme.header.variant", { equals: "classic" }));
24284
26375
  const centeredLayout = stack({
24285
26376
  gap: "md",
24286
26377
  align: "center",
@@ -24345,7 +26436,6 @@ const siteHeaderBlockDefinition = { manifest: createBlockManifest({
24345
26436
  }, [
24346
26437
  mobileHeaderBar,
24347
26438
  classicLayout,
24348
- classicCenteredNavLayout,
24349
26439
  centeredLayout,
24350
26440
  floatingLayout,
24351
26441
  mobileOverlay
@@ -26299,83 +28389,6 @@ const weeklySlotAvailabilityBlockDefinition = {
26299
28389
  } }
26300
28390
  };
26301
28391
  //#endregion
26302
- //#region ../blocks/src/lib/media.ts
26303
- const PUBLIC_DOWNLOAD_MEDIA_TYPES = [
26304
- "document",
26305
- "spreadsheet",
26306
- "archive",
26307
- "audio"
26308
- ];
26309
- //#endregion
26310
- //#region ../blocks/src/system/manifest/richTextSchema.ts
26311
- /**
26312
- * Shared Rich Text Schema
26313
- *
26314
- * This module provides a single source of truth for TipTap rich text validation
26315
- * across both the block-form editor and the block rendering system.
26316
- *
26317
- * Rich-text fields use one canonical root shape:
26318
- * `{ type: 'doc', content: [...] }`.
26319
- */
26320
- /**
26321
- * TipTap document node schema (recursive structure)
26322
- *
26323
- * Supports the full TipTap document structure including:
26324
- * - type: Node type (e.g., 'doc', 'paragraph', 'text')
26325
- * - content: Nested array of child nodes
26326
- * - text: Text content for text nodes
26327
- * - attrs: Node attributes (e.g., alignment, color)
26328
- * - marks: Text formatting marks (e.g., bold, italic)
26329
- */
26330
- const TipTapMarkSchema = z.object({
26331
- type: z.string().min(1),
26332
- attrs: z.record(z.string(), z.unknown()).optional()
26333
- }).strict();
26334
- const TipTapNodeSchema = z.lazy(() => z.object({
26335
- type: z.string().min(1),
26336
- content: z.array(TipTapNodeSchema).optional(),
26337
- text: z.string().optional(),
26338
- attrs: z.record(z.string(), z.unknown()).optional(),
26339
- marks: z.array(TipTapMarkSchema).optional()
26340
- }).strict().superRefine((node, ctx) => {
26341
- if (node.type === "doc") ctx.addIssue({
26342
- code: "custom",
26343
- path: ["type"],
26344
- message: "Document nodes are only valid at the rich-text root"
26345
- });
26346
- if (node.type === "text") {
26347
- if (node.text === void 0 || node.text.length === 0) ctx.addIssue({
26348
- code: "custom",
26349
- path: ["text"],
26350
- message: "Text nodes require a non-empty text value"
26351
- });
26352
- if (node.content !== void 0) ctx.addIssue({
26353
- code: "custom",
26354
- path: ["content"],
26355
- message: "Text nodes cannot contain child nodes"
26356
- });
26357
- return;
26358
- }
26359
- if (node.text !== void 0) ctx.addIssue({
26360
- code: "custom",
26361
- path: ["text"],
26362
- message: "Only text nodes can contain a text value"
26363
- });
26364
- }));
26365
- /**
26366
- * Canonical schema for a TipTap rich-text document root.
26367
- *
26368
- * Stricter than {@link TipTapNodeSchema}: `type` is literally `'doc'` and
26369
- * `content` is required (possibly empty). This is the shape every producer
26370
- * (dummy content, editor output, migrations) must emit, and the shape every
26371
- * non-legacy consumer can rely on.
26372
- *
26373
- */
26374
- const RichTextDocSchema = z.object({
26375
- type: z.literal("doc"),
26376
- content: z.array(TipTapNodeSchema)
26377
- }).strict();
26378
- //#endregion
26379
28392
  //#region ../blocks/src/system/blocks/shared/bookingPaymentTerms.ts
26380
28393
  const bookingDepositRefundPolicySchema$1 = z.enum(["refundable", "non_refundable"]);
26381
28394
  const bookingInstalmentPlanSchema$1 = z.union([
@@ -27071,7 +29084,7 @@ const eventDisplayFields = [
27071
29084
  {
27072
29085
  id: "showCapacity",
27073
29086
  type: "boolean",
27074
- label: "Show available spots",
29087
+ label: "Show available places",
27075
29088
  description: "Display remaining capacity",
27076
29089
  required: false,
27077
29090
  defaultValue: true,
@@ -27370,12 +29383,12 @@ function planEventRegistrationScope(input) {
27370
29383
  };
27371
29384
  }
27372
29385
  //#endregion
27373
- //#region ../blocks/src/system/runtime/nodes/events/shared/destination.ts
27374
- function isRecord$7(value) {
29386
+ //#region ../blocks/src/system/runtime/nodes/events/domain/destination.ts
29387
+ function isRecord$9(value) {
27375
29388
  return typeof value === "object" && value !== null && !Array.isArray(value);
27376
29389
  }
27377
29390
  function isRuntimeEventDestination(value) {
27378
- if (!isRecord$7(value) || typeof value.kind !== "string") return false;
29391
+ if (!isRecord$9(value) || typeof value.kind !== "string") return false;
27379
29392
  if (value.kind === "native") return (value.mode === "cms-managed" || value.mode === "system-managed") && typeof value.path === "string";
27380
29393
  return value.kind === "external" && typeof value.url === "string" && typeof value.label === "string";
27381
29394
  }
@@ -27388,7 +29401,7 @@ function resolveRuntimeEventDestination(event) {
27388
29401
  };
27389
29402
  }
27390
29403
  //#endregion
27391
- //#region ../blocks/src/system/runtime/nodes/events/shared/pricing.ts
29404
+ //#region ../blocks/src/system/runtime/nodes/events/domain/pricing.ts
27392
29405
  const DEFAULT_EVENT_CURRENCY = "USD";
27393
29406
  function resolveRuntimeEventPricing(event) {
27394
29407
  if (isPublicEventPricing(event.pricing)) return event.pricing;
@@ -27438,15 +29451,15 @@ function resolveRuntimeEventPricing(event) {
27438
29451
  function readCurrency(value) {
27439
29452
  return typeof value === "string" && value.trim().length > 0 ? value : DEFAULT_EVENT_CURRENCY;
27440
29453
  }
27441
- function isRecord$6(value) {
29454
+ function isRecord$8(value) {
27442
29455
  return typeof value === "object" && value !== null && !Array.isArray(value);
27443
29456
  }
27444
29457
  function isPublicEventPricing(value) {
27445
- if (!isRecord$6(value) || typeof value.kind !== "string") return false;
29458
+ if (!isRecord$8(value) || typeof value.kind !== "string") return false;
27446
29459
  return value.kind === "drop_in" || value.kind === "free_registration" || value.kind === "single_paid" || value.kind === "sliding_scale" || value.kind === "ticket_types" || value.kind === "whole_series" || value.kind === "external";
27447
29460
  }
27448
29461
  function isPublicEventTicketType(value) {
27449
- return isRecord$6(value) && typeof value.id === "string";
29462
+ return isRecord$8(value) && typeof value.id === "string";
27450
29463
  }
27451
29464
  function readTicketTypePriceDisplay(value, currency) {
27452
29465
  const display = readLegacyPriceDisplay(value, currency);
@@ -27456,7 +29469,7 @@ function readTicketTypePriceDisplay(value, currency) {
27456
29469
  } : display;
27457
29470
  }
27458
29471
  function readLegacyPriceDisplay(value, currency) {
27459
- if (!isRecord$6(value) || typeof value.kind !== "string") return {
29472
+ if (!isRecord$8(value) || typeof value.kind !== "string") return {
27460
29473
  kind: "unavailable",
27461
29474
  currency
27462
29475
  };
@@ -27508,7 +29521,7 @@ function readLegacyPriceDisplay(value, currency) {
27508
29521
  kind: "unavailable",
27509
29522
  currency: displayCurrency
27510
29523
  };
27511
- case "whole_series": return isRecord$6(value.display) ? {
29524
+ case "whole_series": return isRecord$8(value.display) ? {
27512
29525
  kind: "whole_series",
27513
29526
  display: readCoursePriceDisplay(value.display, displayCurrency)
27514
29527
  } : {
@@ -27969,14 +29982,14 @@ const eventRegistrationBlockRuntime = defineClientOwnedBlockRuntime({
27969
29982
  }
27970
29983
  });
27971
29984
  function normalizeEventRegistrationEventInput(value) {
27972
- if (!isRecord$5(value)) return value;
29985
+ if (!isRecord$7(value)) return value;
27973
29986
  return {
27974
29987
  ...value,
27975
29988
  destination: resolveRuntimeEventDestination(value),
27976
29989
  pricing: resolveRuntimeEventPricing(value)
27977
29990
  };
27978
29991
  }
27979
- function isRecord$5(value) {
29992
+ function isRecord$7(value) {
27980
29993
  return typeof value === "object" && value !== null && !Array.isArray(value);
27981
29994
  }
27982
29995
  const eventRegistrationBlockDefinition = {
@@ -28068,7 +30081,7 @@ const eventRegistrationBlockDefinition = {
28068
30081
  {
28069
30082
  id: "showCapacity",
28070
30083
  type: "boolean",
28071
- label: "Show available spots",
30084
+ label: "Show available places",
28072
30085
  description: "Display remaining capacity",
28073
30086
  required: false,
28074
30087
  defaultValue: true,
@@ -28629,7 +30642,11 @@ const eventSpotlightBlockDefinition = {
28629
30642
  } }
28630
30643
  };
28631
30644
  //#endregion
28632
- //#region ../blocks/src/system/runtime/nodes/events/shared/utils.ts
30645
+ //#region ../blocks/src/system/runtime/nodes/events/shared/listingLayout.ts
30646
+ /**
30647
+ * Layout classes for event listing containers and cards. Presentation
30648
+ * plumbing rather than event domain logic, so it lives beside the components.
30649
+ */
28633
30650
  /**
28634
30651
  * Column class mappings for grid layouts, derived from the canonical
28635
30652
  * responsive column ladder (see gridUserColumnLadder).
@@ -28750,7 +30767,8 @@ const eventCollectionSourceSchemaShape = {
28750
30767
  buttonVariant: eventButtonVariantSchema.optional(),
28751
30768
  buttonText: z.string().optional(),
28752
30769
  emptyMessage: z.string().optional(),
28753
- className: z.string().optional()
30770
+ className: z.string().optional(),
30771
+ bookingTimeZone: z.string().optional()
28754
30772
  };
28755
30773
  const eventCollectionDisplaySchemaShape = {
28756
30774
  events: z.array(publicEventSchema),
@@ -28785,7 +30803,15 @@ const eventCollectionHydrationSchema = z.object({
28785
30803
  prefetchedEvents: z.boolean(),
28786
30804
  eventSurface: eventSurfaceScopeSchema$1,
28787
30805
  filterEventPresets: z.string().nullable(),
28788
- scheduleScope: eventScheduleScopeSchema$1
30806
+ scheduleScope: eventScheduleScopeSchema$1,
30807
+ calendarContext: z.object({
30808
+ timeZone: z.string().transform(toBookingTimeZone),
30809
+ initialToday: z.object({
30810
+ year: z.number().int(),
30811
+ month: z.number().int().min(1).max(12),
30812
+ day: z.number().int().min(1).max(31)
30813
+ })
30814
+ })
28789
30815
  });
28790
30816
  function buildEventCollectionDisplay(source) {
28791
30817
  return {
@@ -28830,14 +30856,19 @@ function buildEventCollectionPresentationDisplay(source) {
28830
30856
  emptyMessage: source.emptyMessage ?? "No upcoming events scheduled."
28831
30857
  };
28832
30858
  }
28833
- function buildEventCollectionHydration(source) {
30859
+ function buildEventCollectionHydration(source, now = /* @__PURE__ */ new Date()) {
30860
+ const timeZone = toBookingTimeZone(source.bookingTimeZone);
28834
30861
  return {
28835
30862
  siteId: source.siteId,
28836
30863
  stage: source.stage,
28837
30864
  prefetchedEvents: source.events != null,
28838
30865
  eventSurface: normalizeEventSurfaceScope(source.eventSurface),
28839
30866
  filterEventPresets: normalizeEventSurfacePresetsCsv(source.filterEventPresets),
28840
- scheduleScope: normalizeEventScheduleScope(source.scheduleScope)
30867
+ scheduleScope: normalizeEventScheduleScope(source.scheduleScope),
30868
+ calendarContext: {
30869
+ timeZone,
30870
+ initialToday: getLocalScheduleDate(now, timeZone)
30871
+ }
28841
30872
  };
28842
30873
  }
28843
30874
  //#endregion
@@ -29947,7 +31978,7 @@ const eventCalendarAdditionalFields = [
29947
31978
  {
29948
31979
  id: "showCapacity",
29949
31980
  type: "boolean",
29950
- label: "Show available spots",
31981
+ label: "Show available places",
29951
31982
  description: "Display remaining capacity on event cards and timetable cells",
29952
31983
  required: false,
29953
31984
  defaultValue: true,
@@ -30828,7 +32859,7 @@ function inferLegacyOfferingsPreviewGroupOrder(overrides) {
30828
32859
  return keys.length >= 2 ? keys : [];
30829
32860
  }
30830
32861
  function normalizeOfferingsPreviewContentForWrite(value) {
30831
- if (!isRecord$4(value)) return null;
32862
+ if (!isRecord$6(value)) return null;
30832
32863
  const groupOrderValue = value.groupOrder;
30833
32864
  if (Array.isArray(groupOrderValue)) {
30834
32865
  const normalized = normalizeOfferingsPreviewGroupOrder(groupOrderValue);
@@ -30841,7 +32872,7 @@ function normalizeOfferingsPreviewContentForWrite(value) {
30841
32872
  ...value,
30842
32873
  groupOrder: []
30843
32874
  };
30844
- const legacyOrder = inferLegacyOfferingsPreviewGroupOrder(Array.isArray(value.groupOverrides) ? value.groupOverrides.filter(isRecord$4) : []);
32875
+ const legacyOrder = inferLegacyOfferingsPreviewGroupOrder(Array.isArray(value.groupOverrides) ? value.groupOverrides.filter(isRecord$6) : []);
30845
32876
  return legacyOrder.length > 0 ? {
30846
32877
  ...value,
30847
32878
  groupOrder: legacyOrder
@@ -30855,7 +32886,7 @@ function uniqueOfferingsPreviewSourceKeys(values) {
30855
32886
  }
30856
32887
  return keys;
30857
32888
  }
30858
- function isRecord$4(value) {
32889
+ function isRecord$6(value) {
30859
32890
  return typeof value === "object" && value !== null && !Array.isArray(value);
30860
32891
  }
30861
32892
  //#endregion
@@ -31233,7 +33264,7 @@ function isOfferingsPreviewItemLimit(value) {
31233
33264
  return value === "1" || value === "2" || value === "3" || value === "4" || value === "5" || value === "6" || value === null;
31234
33265
  }
31235
33266
  function normalizeOfferingsPreviewGroupOverride(value) {
31236
- if (!isRecord$3(value)) return [];
33267
+ if (!isRecord$5(value)) return [];
31237
33268
  const override = {};
31238
33269
  if (isOfferingsPreviewSourceKey(value.sourceKey) || value.sourceKey === null) override.sourceKey = value.sourceKey;
31239
33270
  if (typeof value.title === "string" || value.title === null) override.title = value.title;
@@ -31256,7 +33287,7 @@ function normalizeSectionStylesContent(value) {
31256
33287
  }
31257
33288
  function normalizeBoxStylesContent(value) {
31258
33289
  if (value === null) return null;
31259
- if (!isRecord$3(value)) return void 0;
33290
+ if (!isRecord$5(value)) return void 0;
31260
33291
  const { background: _background, ...rest } = value;
31261
33292
  const background = parseBackgroundContent(value.background);
31262
33293
  return {
@@ -31264,7 +33295,7 @@ function normalizeBoxStylesContent(value) {
31264
33295
  ...background ? { background } : {}
31265
33296
  };
31266
33297
  }
31267
- function isRecord$3(value) {
33298
+ function isRecord$5(value) {
31268
33299
  return typeof value === "object" && value !== null && !Array.isArray(value);
31269
33300
  }
31270
33301
  const embedManifest = createBlockManifest({
@@ -34485,6 +36516,23 @@ function deriveNewsletterSignupRuntimeSource(values) {
34485
36516
  tags: parseTrimmedString(normalized.tags)
34486
36517
  };
34487
36518
  }
36519
+ function sanitizeNewsletterSignupPublicContent(content) {
36520
+ if (!isRecord$4(content)) return content;
36521
+ const provider = parseNewsletterSignupDestinationProvider(content.destinationProvider) ?? getProviderFromExplicitDestination(content.destination);
36522
+ const { destination: _destination, destinationProvider: _destinationProvider, mailchimpAudienceId: _mailchimpAudienceId, mailchimpTags: _mailchimpTags, listId, tags, ...publicContent } = content;
36523
+ const mutablePublicContent = { ...publicContent };
36524
+ if (provider !== "mailchimp") {
36525
+ if ("listId" in content) mutablePublicContent.listId = listId;
36526
+ if ("tags" in content) mutablePublicContent.tags = tags;
36527
+ }
36528
+ return mutablePublicContent;
36529
+ }
36530
+ function getProviderFromExplicitDestination(destination) {
36531
+ if (!isRecord$4(destination) || typeof destination.tag !== "string") return null;
36532
+ if (destination.tag === "riverbank_list") return "riverbank";
36533
+ if (destination.tag === "mailchimp_audience") return "mailchimp";
36534
+ return null;
36535
+ }
34488
36536
  const BLOCK_ID_PATTERN = /^[A-Za-z0-9._:-]{1,200}$/;
34489
36537
  const SIGNED_DESTINATION_REF_PATTERN = /^[A-Za-z0-9._~-]{16,1000}$/;
34490
36538
  function parseTrimmedString(value) {
@@ -34492,6 +36540,9 @@ function parseTrimmedString(value) {
34492
36540
  const trimmed = value.trim();
34493
36541
  return trimmed.length > 0 ? trimmed : null;
34494
36542
  }
36543
+ function isRecord$4(value) {
36544
+ return typeof value === "object" && value !== null;
36545
+ }
34495
36546
  //#endregion
34496
36547
  //#region ../blocks/src/system/runtime/nodes/newsletter-form.types.ts
34497
36548
  /**
@@ -34727,7 +36778,7 @@ function projectNewsletterFormHydrationTarget(source) {
34727
36778
  tag: "legacy-direct-list",
34728
36779
  listId: source.listId
34729
36780
  };
34730
- default: return assertNever$2(source);
36781
+ default: return assertNever$3(source);
34731
36782
  }
34732
36783
  }
34733
36784
  function parseRequiredNewsletterSignupBlockId(value) {
@@ -36905,7 +38956,7 @@ function isNonProductionEnv() {
36905
38956
  //#region ../blocks/src/system/data/linkedContentEntries.ts
36906
38957
  const LINKED_CONTENT_ENTRY_META_KEY = "__rbLinkedContentEntry";
36907
38958
  function coerceLinkedContentEntriesPayload(payload) {
36908
- if (!isRecord$2(payload)) return payload;
38959
+ if (!isRecord$3(payload)) return payload;
36909
38960
  const response = payload;
36910
38961
  if (!Array.isArray(response.entries)) return payload;
36911
38962
  const contentType = parsePublicContentTypeMeta(response.contentType);
@@ -36913,12 +38964,12 @@ function coerceLinkedContentEntriesPayload(payload) {
36913
38964
  return response.entries.map((entry) => enrichLinkedContentEntry(entry, contentType));
36914
38965
  }
36915
38966
  function enrichLinkedContentEntry(entry, contentType) {
36916
- if (!isRecord$2(entry)) return entry;
38967
+ if (!isRecord$3(entry)) return entry;
36917
38968
  const entryId = typeof entry.id === "string" ? entry.id : null;
36918
38969
  if (!entryId) return entry;
36919
38970
  const title = typeof entry.title === "string" && entry.title.trim().length > 0 ? entry.title : entryId;
36920
38971
  const optimisticHash = typeof entry.optimisticHash === "string" && entry.optimisticHash.length > 0 ? entry.optimisticHash : null;
36921
- const content = isRecord$2(entry.content) ? entry.content : {};
38972
+ const content = isRecord$3(entry.content) ? entry.content : {};
36922
38973
  const meta = {
36923
38974
  kind: "linked-content-entry",
36924
38975
  entryId,
@@ -37001,7 +39052,7 @@ function resolveLinkedContentEditSupport(contentType) {
37001
39052
  kind: "unsupported-domain-backed",
37002
39053
  reason: metadata.reason
37003
39054
  };
37004
- default: return assertNever$2(metadata);
39055
+ default: return assertNever$3(metadata);
37005
39056
  }
37006
39057
  }
37007
39058
  function contentTypeKeyToLabel(key) {
@@ -37011,7 +39062,7 @@ function capitalize(value) {
37011
39062
  return value.charAt(0).toUpperCase() + value.slice(1).toLowerCase();
37012
39063
  }
37013
39064
  function parsePublicContentTypeMeta(value) {
37014
- if (!isRecord$2(value)) return null;
39065
+ if (!isRecord$3(value)) return null;
37015
39066
  if (typeof value.key !== "string" || value.key.trim().length === 0) return null;
37016
39067
  return {
37017
39068
  key: value.key,
@@ -37021,7 +39072,7 @@ function parsePublicContentTypeMeta(value) {
37021
39072
  };
37022
39073
  }
37023
39074
  function parsePublicContentTypeLinkedEntryEditing(value) {
37024
- if (!isRecord$2(value) || typeof value.kind !== "string") return null;
39075
+ if (!isRecord$3(value) || typeof value.kind !== "string") return null;
37025
39076
  switch (value.kind) {
37026
39077
  case "generic": return { kind: "generic" };
37027
39078
  case "unsupported-domain-backed": return {
@@ -37032,7 +39083,7 @@ function parsePublicContentTypeLinkedEntryEditing(value) {
37032
39083
  }
37033
39084
  }
37034
39085
  function parsePublicContentTypeFieldMeta(value) {
37035
- if (!isRecord$2(value)) return [];
39086
+ if (!isRecord$3(value)) return [];
37036
39087
  if (typeof value.id !== "string" || typeof value.type !== "string") return [];
37037
39088
  return [{
37038
39089
  id: value.id,
@@ -37046,7 +39097,7 @@ function parseStringArray(value) {
37046
39097
  if (!Array.isArray(value)) return null;
37047
39098
  return value.filter((item) => typeof item === "string");
37048
39099
  }
37049
- function isRecord$2(value) {
39100
+ function isRecord$3(value) {
37050
39101
  return typeof value === "object" && value !== null && !Array.isArray(value);
37051
39102
  }
37052
39103
  //#endregion
@@ -37067,10 +39118,10 @@ function normalizeDataLoaders(def) {
37067
39118
  */
37068
39119
  function normalizeContent(block) {
37069
39120
  const source = block.draftContent ?? block.content ?? block.publishedContent ?? {};
37070
- if (!isObjectRecord(source)) return {};
39121
+ if (!isObjectRecord$1(source)) return {};
37071
39122
  if ("data" in source) {
37072
39123
  const data = source.data;
37073
- if (isObjectRecord(data)) return data;
39124
+ if (isObjectRecord$1(data)) return data;
37074
39125
  if (data === null || data === void 0) return {};
37075
39126
  }
37076
39127
  return source;
@@ -37253,7 +39304,7 @@ const SUPPORTED_LOADER_ENDPOINTS$1 = [
37253
39304
  //#endregion
37254
39305
  //#region ../blocks/src/system/data/normalizeLoaderParams.ts
37255
39306
  function toStringParams(value) {
37256
- if (!isObjectRecord(value)) return {};
39307
+ if (!isObjectRecord$1(value)) return {};
37257
39308
  const out = {};
37258
39309
  for (const [k, v] of Object.entries(value)) if (typeof v === "string") out[k] = v;
37259
39310
  return out;
@@ -37676,6 +39727,50 @@ z.object({
37676
39727
  title: z.string(),
37677
39728
  slug: z.string().min(1)
37678
39729
  });
39730
+ z.object({
39731
+ signedUrl: z.string().url(),
39732
+ token: z.string().min(1),
39733
+ storagePath: z.string().min(1)
39734
+ }).strict();
39735
+ const backupManifestSchema = z.object({
39736
+ siteId: z.string().min(1),
39737
+ siteSlug: z.string().min(1),
39738
+ siteTitle: z.string(),
39739
+ exportedAt: z.string().min(1),
39740
+ tableCount: z.number().int().nonnegative(),
39741
+ totalRecords: z.number().int().nonnegative(),
39742
+ pageCount: z.number().int().nonnegative(),
39743
+ blockCount: z.number().int().nonnegative(),
39744
+ contentEntryCount: z.number().int().nonnegative(),
39745
+ formCount: z.number().int().nonnegative(),
39746
+ navigationMenuCount: z.number().int().nonnegative(),
39747
+ mediaCount: z.number().int().nonnegative(),
39748
+ mediaBytes: z.number().int().nonnegative()
39749
+ }).strict();
39750
+ z.object({
39751
+ ok: z.literal(true),
39752
+ manifest: backupManifestSchema,
39753
+ storagePath: z.string().min(1)
39754
+ }).strict();
39755
+ z.object({
39756
+ ok: z.literal(true),
39757
+ siteId: brandedIdSchema(parseSiteId, "site id"),
39758
+ slug: z.string().min(1),
39759
+ title: z.string(),
39760
+ counts: z.record(z.string(), z.object({
39761
+ created: z.number().int().nonnegative(),
39762
+ updated: z.number().int().nonnegative(),
39763
+ skipped: z.number().int().nonnegative(),
39764
+ failed: z.number().int().nonnegative()
39765
+ }).strict()),
39766
+ warnings: z.array(z.string()),
39767
+ errors: z.array(z.object({
39768
+ table: z.string(),
39769
+ id: z.string(),
39770
+ error: z.string()
39771
+ }).strict())
39772
+ }).strict();
39773
+ z.object({ deleted: z.boolean() }).strict();
37679
39774
  const apiKeyIdSchema = brandedIdSchema(parseApiKeyId, "API key id");
37680
39775
  const standardKeyIdsSchema = z.object({
37681
39776
  live: apiKeyIdSchema.optional(),
@@ -37851,7 +39946,7 @@ function describeSiteVerticalConfigIssue(issue) {
37851
39946
  case "duplicate_preset_id":
37852
39947
  case "duplicate_canonical_behaviour":
37853
39948
  case "future_preset_selected": return issue.code;
37854
- default: return assertNever$2(issue);
39949
+ default: return assertNever$3(issue);
37855
39950
  }
37856
39951
  }
37857
39952
  const VerticalIdSchema = z.string().trim().transform((value, ctx) => {
@@ -37892,7 +39987,7 @@ z.object({
37892
39987
  });
37893
39988
  //#endregion
37894
39989
  //#region ../api/src/utils/isRecord.ts
37895
- function isRecord$1(value) {
39990
+ function isRecord$2(value) {
37896
39991
  return typeof value === "object" && value !== null && !Array.isArray(value);
37897
39992
  }
37898
39993
  //#endregion
@@ -38141,7 +40236,7 @@ function sanitizeMarks(marks) {
38141
40236
  if (!Array.isArray(marks) || marks.length === 0) return;
38142
40237
  const sanitized = marks.flatMap((mark) => {
38143
40238
  if (mark.type === "bold" || mark.type === "strong" || mark.type === "italic" || mark.type === "em") return [{ type: mark.type }];
38144
- if (mark.type === "link" && isRecord$1(mark.attrs) && typeof mark.attrs.href === "string") {
40239
+ if (mark.type === "link" && isRecord$2(mark.attrs) && typeof mark.attrs.href === "string") {
38145
40240
  const attrs = { href: mark.attrs.href };
38146
40241
  if (typeof mark.attrs.target === "string" && mark.attrs.target.trim().length > 0) attrs.target = mark.attrs.target;
38147
40242
  if (typeof mark.attrs.rel === "string" && mark.attrs.rel.trim().length > 0) attrs.rel = mark.attrs.rel;
@@ -38336,7 +40431,7 @@ const ENTRY_REF_KEYS = [
38336
40431
  "entryId",
38337
40432
  "slug"
38338
40433
  ];
38339
- function isRecord(value) {
40434
+ function isRecord$1(value) {
38340
40435
  return Boolean(value) && typeof value === "object" && !Array.isArray(value);
38341
40436
  }
38342
40437
  function extractManualEntryRef(item) {
@@ -38344,7 +40439,7 @@ function extractManualEntryRef(item) {
38344
40439
  const trimmed = item.trim();
38345
40440
  return trimmed.length > 0 ? trimmed : null;
38346
40441
  }
38347
- if (!isRecord(item)) return null;
40442
+ if (!isRecord$1(item)) return null;
38348
40443
  for (const key of ENTRY_REF_KEYS) {
38349
40444
  const raw = item[key];
38350
40445
  if (typeof raw === "string" && raw.trim().length > 0) return raw.trim();
@@ -38649,6 +40744,126 @@ function requireSdkTheme(theme, siteId, reason = "cms_theme_absent") {
38649
40744
  return theme;
38650
40745
  }
38651
40746
  //#endregion
40747
+ //#region src/rendering/helpers/siteCustomBlocks.ts
40748
+ /**
40749
+ * Prefer the DB-backed custom block manifest list (`site.customBlocks`) when available,
40750
+ * but fall back to SDK config custom blocks (`site.sdkConfig.customBlocks`) for SDK-mode sites.
40751
+ *
40752
+ * This avoids subtle DX bugs where one source is populated but the renderer/prefetch layer
40753
+ * is looking at the other.
40754
+ */
40755
+ function resolveSiteCustomBlocks(site) {
40756
+ const dbBlocks = site.customBlocks;
40757
+ if (Array.isArray(dbBlocks) && dbBlocks.length > 0) return dbBlocks;
40758
+ const sdkBlocks = site.sdkConfig?.customBlocks;
40759
+ if (Array.isArray(sdkBlocks) && sdkBlocks.length > 0) return sdkBlocks;
40760
+ }
40761
+ //#endregion
40762
+ //#region src/rendering/helpers/pageRuntimeSupport.ts
40763
+ function convertPageResponseBlock(block) {
40764
+ if (!isRecord(block)) throw new Error("Invalid block format in page API response");
40765
+ if (typeof block.id !== "string" && block.id !== null) throw new Error(`Invalid block id in page: expected string or null, got ${typeof block.id}`);
40766
+ if (typeof block.kind !== "string") throw new Error(`Invalid block kind in page: expected string, got ${typeof block.kind}`);
40767
+ if (typeof block.purpose !== "string") throw new Error(`Invalid block purpose in page: expected string, got ${typeof block.purpose}`);
40768
+ const admission = readInvalidBlockAdmission(block.admission);
40769
+ return {
40770
+ id: block.id,
40771
+ kind: block.kind,
40772
+ purpose: block.purpose,
40773
+ content: sanitizeSdkRuntimeBlockContent(block.kind, readContentRecord(block.content) ?? {}),
40774
+ draftContent: sanitizeNullableSdkRuntimeBlockContent(block.kind, readNestedContentRecord(block.draftContent)),
40775
+ ...admission ? { admission } : {}
40776
+ };
40777
+ }
40778
+ function readInvalidBlockAdmission(value) {
40779
+ if (!isRecord(value) || value.kind !== "invalid" || !isRecord(value.error)) return null;
40780
+ if (value.error.kind !== "invalid_manifest_content" && value.error.kind !== "manifest_unavailable") return null;
40781
+ return value;
40782
+ }
40783
+ function buildPageOutline(input) {
40784
+ return {
40785
+ name: input.name,
40786
+ path: input.path ?? input.fallbackPath,
40787
+ purpose: input.purpose,
40788
+ blocks: input.blocks.map(convertPageResponseBlock)
40789
+ };
40790
+ }
40791
+ function buildRenderablePageOutline(input) {
40792
+ const page = buildPageOutline(input);
40793
+ return {
40794
+ ...page,
40795
+ blocks: page.blocks.map((block) => admitSdkRuntimePageBlock(block, input.customBlocks))
40796
+ };
40797
+ }
40798
+ function projectRenderablePageOutline(page) {
40799
+ return {
40800
+ ...page,
40801
+ blocks: page.blocks.flatMap((block) => block.admission.kind === "renderable" ? [{
40802
+ id: block.id,
40803
+ kind: block.kind,
40804
+ purpose: block.purpose,
40805
+ content: block.admission.content,
40806
+ ...block.bindings ? { bindings: block.bindings } : {}
40807
+ }] : [])
40808
+ };
40809
+ }
40810
+ function admitSdkRuntimePageBlock(block, customBlocks) {
40811
+ const base = {
40812
+ id: block.id,
40813
+ kind: block.kind,
40814
+ purpose: block.purpose,
40815
+ ...block.bindings ? { bindings: block.bindings } : {}
40816
+ };
40817
+ const transportedAdmission = readInvalidBlockAdmission(block.admission);
40818
+ if (transportedAdmission) return invalidRenderableBlock(base, transportedAdmission.error);
40819
+ const manifest = resolveSdkBlockManifest(block.kind, customBlocks);
40820
+ if (!manifest) return invalidRenderableBlock(base, { kind: "manifest_unavailable" });
40821
+ const content = block.content ?? {};
40822
+ const renderable = admitTransportedRenderableManifestContent(compileManifestContentContract(manifest), content);
40823
+ if (!renderable.ok) return invalidRenderableBlock(base, {
40824
+ kind: "invalid_manifest_content",
40825
+ error: renderable.error
40826
+ });
40827
+ return {
40828
+ ...base,
40829
+ admission: {
40830
+ kind: "renderable",
40831
+ content: renderable.value
40832
+ }
40833
+ };
40834
+ }
40835
+ function resolveSdkBlockManifest(blockKind, customBlocks) {
40836
+ const customBlock = customBlocks?.find((candidate) => candidate.id === blockKind);
40837
+ return customBlock ? parseFieldDefinitions(customBlock.fields) : getBlockDefinition(blockKind)?.manifest ?? null;
40838
+ }
40839
+ function invalidRenderableBlock(block, error) {
40840
+ return {
40841
+ ...block,
40842
+ admission: {
40843
+ kind: "invalid",
40844
+ error
40845
+ }
40846
+ };
40847
+ }
40848
+ function isRecord(value) {
40849
+ return Boolean(value) && typeof value === "object" && !Array.isArray(value);
40850
+ }
40851
+ function readContentRecord(value) {
40852
+ return isRecord(value) ? value : void 0;
40853
+ }
40854
+ function readNestedContentRecord(value) {
40855
+ if (!isRecord(value)) return null;
40856
+ return readContentRecord(value.data) ?? null;
40857
+ }
40858
+ function sanitizeSdkRuntimeBlockContent(blockKind, content) {
40859
+ if (blockKind !== "block.newsletter-signup") return content;
40860
+ const sanitized = sanitizeNewsletterSignupPublicContent(content);
40861
+ return isRecord(sanitized) ? sanitized : {};
40862
+ }
40863
+ function sanitizeNullableSdkRuntimeBlockContent(blockKind, content) {
40864
+ return content ? sanitizeSdkRuntimeBlockContent(blockKind, content) : null;
40865
+ }
40866
+ //#endregion
38652
40867
  //#region src/rendering/hooks/usePage.ts
38653
40868
  /**
38654
40869
  * Client-side React hook to fetch page data.
@@ -38748,24 +40963,15 @@ function usePage(params) {
38748
40963
  const { page: pageData } = pageResponse;
38749
40964
  const canonicalPagePath = pageData.path ?? path;
38750
40965
  const mediaAssets = Array.isArray(pageResponse.mediaAssets) && pageResponse.mediaAssets.length > 0 ? toMediaAssetSidecar(pageResponse.mediaAssets) : void 0;
38751
- const blocks = pageData.blocks.map((block) => {
38752
- if (!block || typeof block !== "object") throw new Error("Invalid block format in API response");
38753
- if (typeof block.id !== "string" && block.id !== null) throw new Error(`Invalid block id: expected string or null, got ${typeof block.id}`);
38754
- if (typeof block.kind !== "string") throw new Error(`Invalid block kind: expected string, got ${typeof block.kind}`);
38755
- if (typeof block.purpose !== "string") throw new Error(`Invalid block purpose: expected string, got ${typeof block.purpose}`);
38756
- return {
38757
- id: block.id,
38758
- kind: block.kind,
38759
- purpose: block.purpose
38760
- };
38761
- });
38762
- const pageOutline = {
40966
+ const pageOutline = buildRenderablePageOutline({
38763
40967
  name: pageData.name,
38764
40968
  path: canonicalPagePath,
40969
+ fallbackPath: path,
38765
40970
  purpose: pageData.purpose,
38766
- blocks
38767
- };
38768
- const resolvedData = await prefetchBlockData(pageOutline, {
40971
+ blocks: pageData.blocks,
40972
+ customBlocks: resolveSiteCustomBlocks(site)
40973
+ });
40974
+ const resolvedData = await prefetchBlockData(projectRenderablePageOutline(pageOutline), {
38769
40975
  siteId,
38770
40976
  pageId: pageId ?? pageData.id,
38771
40977
  previewStage: preview ? "preview" : "published"