@riverbankcms/sdk 0.151.0 → 0.152.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 (226) hide show
  1. package/CHANGELOG.md +37 -0
  2. package/dist/{EventEmbeddedCheckoutStep-BBHbM22D.mjs → EventEmbeddedCheckoutStep-DVQXWFm6.mjs} +10 -9
  3. package/dist/{PreviewEditorSidebar-BHplI9HE.mjs → PreviewEditorSidebar-Dr4NJi3Q.mjs} +4 -3
  4. package/dist/{PreviewEditorUI-DuyN8nsD.mjs → PreviewEditorUI-DSZXQbyh.mjs} +5 -4
  5. package/dist/{SdkPreviewModeRuntime-CR5ypwxJ.mjs → SdkPreviewModeRuntime-Dl4GyMc3.mjs} +124 -53
  6. package/dist/{SiteChromeCustomizeContext-D5t6joF_.mjs → SiteChromeCustomizeContext-DgBe-FlM.mjs} +20418 -32298
  7. package/dist/ThemedButton-B8ViBnLK.mjs +16350 -0
  8. package/dist/_dts/api/src/bookingOperations.d.ts +5 -3
  9. package/dist/_dts/api/src/commercePortalEndpoints.d.ts +4 -0
  10. package/dist/_dts/api/src/endpoints.d.ts +7 -0
  11. package/dist/_dts/api/src/index.d.ts +1 -1
  12. package/dist/_dts/api/src/navigation/types.d.ts +7 -0
  13. package/dist/_dts/api/src/navigation.d.ts +1 -1
  14. package/dist/_dts/api/src/publicAppointmentBookingContracts.d.ts +10 -1
  15. package/dist/_dts/api/src/siteAssets.d.ts +1 -0
  16. package/dist/_dts/blocks/src/definitions.d.ts +1 -1
  17. package/dist/_dts/blocks/src/index.d.ts +1 -0
  18. package/dist/_dts/blocks/src/pageRenderPreparation.d.ts +1 -0
  19. package/dist/_dts/blocks/src/pageRendererContracts.d.ts +1 -1
  20. package/dist/_dts/blocks/src/primitives/content/utils/sanitizeHref.d.ts +1 -1
  21. package/dist/_dts/blocks/src/system/blocks/event-registration.d.ts +4 -4
  22. package/dist/_dts/blocks/src/system/blocks/events/shared/schemas.d.ts +16 -0
  23. package/dist/_dts/blocks/src/system/blocks/events/shared/types.d.ts +5 -0
  24. package/dist/_dts/blocks/src/system/blocks/offerings-preview/model.d.ts +2 -0
  25. package/dist/_dts/blocks/src/system/helpers/normalizeMailingListBlockContent.d.ts +2 -0
  26. package/dist/_dts/blocks/src/system/registry.d.ts +14 -2
  27. package/dist/_dts/blocks/src/system/runtime/api/client.d.ts +0 -5
  28. package/dist/_dts/blocks/src/system/runtime/api/index.d.ts +2 -0
  29. package/dist/_dts/blocks/src/system/runtime/api/schemas/events.d.ts +971 -0
  30. package/dist/_dts/blocks/src/system/runtime/api/schemas/waitlist-claims.d.ts +42 -0
  31. package/dist/_dts/blocks/src/system/runtime/api/types/appointments.d.ts +10 -2
  32. package/dist/_dts/blocks/src/system/runtime/api/types/events.d.ts +10 -136
  33. package/dist/_dts/blocks/src/system/runtime/components/registry.client.d.ts +48 -6
  34. package/dist/_dts/blocks/src/system/runtime/components/runtime-backed-blocks.d.ts +24 -3
  35. package/dist/_dts/blocks/src/system/runtime/hooks/useBookingSubmission.state.d.ts +2 -0
  36. package/dist/_dts/blocks/src/system/runtime/hooks/useHeadlessBookingFlow.d.ts +3 -1
  37. package/dist/_dts/blocks/src/system/runtime/hooks/useHeadlessBookingFlow.state.d.ts +1 -1
  38. package/dist/_dts/blocks/src/system/runtime/nodes/booking-form.headless.state.d.ts +23 -0
  39. package/dist/_dts/blocks/src/system/runtime/nodes/booking-form.state.d.ts +6 -2
  40. package/dist/_dts/blocks/src/system/runtime/nodes/carousel.client.hooks.d.ts +6 -0
  41. package/dist/_dts/blocks/src/system/runtime/nodes/carousel.client.state.d.ts +7 -0
  42. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/BookingEntryViews.d.ts +42 -0
  43. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/EventEmbeddedCheckoutStep.d.ts +4 -1
  44. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/EventRegistrationBookingLayer.d.ts +19 -9
  45. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/EventRegistrationPayment.d.ts +4 -2
  46. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/EventRegistrationSurface.d.ts +24 -9
  47. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/EventRegistrationWizardContent.d.ts +4 -2
  48. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/EventSelectionStep.d.ts +5 -4
  49. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/GuestDetailsSection.d.ts +1 -1
  50. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/PaymentStatusStep.d.ts +2 -0
  51. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/SingleTicketEntitlementNote.d.ts +11 -0
  52. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/SuccessStep.d.ts +4 -0
  53. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/TicketSelectionStep.d.ts +5 -0
  54. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/VerifyingTimeoutStep.d.ts +2 -0
  55. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/bookingCardDisplay.d.ts +31 -0
  56. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/bookingEntry.d.ts +154 -0
  57. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/bookingFocus.d.ts +12 -0
  58. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/event-registration.headless.contract.d.ts +2 -2
  59. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/event-registration.headless.planning.d.ts +2 -2
  60. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/eventSourceRecovery.d.ts +9 -0
  61. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/finishedBooking.d.ts +77 -0
  62. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/index.d.ts +0 -1
  63. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/offeringNoun.d.ts +12 -0
  64. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/paymentResponse.d.ts +2 -1
  65. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/registrationCompletion.d.ts +33 -1
  66. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/registrationDerivations.d.ts +11 -0
  67. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/registrationDisplay.d.ts +31 -0
  68. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/registrationDraft.state.d.ts +60 -0
  69. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/registrationErrorRecovery.d.ts +1 -1
  70. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/registrationEventAdapter.d.ts +3 -0
  71. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/registrationStateMachine.d.ts +58 -10
  72. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/registrationSurface.d.ts +17 -0
  73. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/types.d.ts +23 -7
  74. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/useBookingBarFacts.d.ts +12 -0
  75. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/useEventRegistrationRecoveryRequest.d.ts +3 -0
  76. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/useEventRegistrationStripeReturn.d.ts +4 -0
  77. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/useEventRegistrationSubmit.d.ts +4 -11
  78. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/useEventRegistrationWizard.d.ts +1563 -31
  79. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/useEventSourceRecovery.d.ts +2 -12
  80. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/usePaymentStatusRecovery.d.ts +147 -4
  81. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/utils.d.ts +50 -2
  82. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/waitlistClaimRestoration.d.ts +6 -5
  83. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/wizardSteps.d.ts +85 -15
  84. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration.interactive.d.ts +53 -8
  85. package/dist/_dts/blocks/src/system/runtime/nodes/events/shared/MonthLookView.d.ts +10 -3
  86. package/dist/_dts/blocks/src/system/runtime/nodes/events/shared/eventCollectionContract.d.ts +16 -0
  87. package/dist/_dts/blocks/src/system/runtime/shared/bookingPaymentOptionCopy.d.ts +5 -1
  88. package/dist/_dts/blocks/src/system/runtime/shared/bookingPaymentOptions.d.ts +37 -1
  89. package/dist/_dts/blocks/src/system/runtime/shared/components/ModalShell.d.ts +9 -2
  90. package/dist/_dts/blocks/src/system/runtime/shared/components/PaymentChoiceRows.d.ts +28 -0
  91. package/dist/_dts/blocks/src/system/runtime/shared/components/StepIndicator.d.ts +6 -8
  92. package/dist/_dts/blocks/src/system/runtime/shared/components/index.d.ts +2 -2
  93. package/dist/_dts/blocks/src/system/runtime/shared/index.d.ts +2 -2
  94. package/dist/_dts/blocks/src/system/runtime/shared/useWaitlistClaim.d.ts +9 -12
  95. package/dist/_dts/blocks/src/system/runtime/shared/useWaitlistClaim.state.d.ts +15 -0
  96. package/dist/_dts/blocks/src/system/runtime/shared/utils/themePortalRoot.d.ts +10 -15
  97. package/dist/_dts/blocks/src/system/transforms/registry/index.d.ts +1 -0
  98. package/dist/_dts/blocks/src/system/transforms/registry/links.d.ts +3 -1
  99. package/dist/_dts/commerce/src/purchase/bookingInstalmentSchedule.d.ts +2 -0
  100. package/dist/_dts/commerce/src/purchase/bookingPaymentSelection.d.ts +6 -0
  101. package/dist/_dts/commerce/src/purchase/confirmationEmailPayment.d.ts +14 -14
  102. package/dist/_dts/commerce/src/purchase/confirmationPayment.d.ts +65 -0
  103. package/dist/_dts/commerce/src/purchase/confirmationPaymentSchema.d.ts +66 -0
  104. package/dist/_dts/commerce/src/purchase/index.d.ts +2 -0
  105. package/dist/_dts/commerce/src/purchase/orderPaymentState.d.ts +8 -0
  106. package/dist/_dts/content-model/src/templates/defaults.d.ts +2 -2
  107. package/dist/_dts/core/src/appointment-booking-terms.d.ts +20 -0
  108. package/dist/_dts/core/src/booking-payment-schemas.d.ts +70 -0
  109. package/dist/_dts/core/src/branded.d.ts +0 -2
  110. package/dist/_dts/core/src/demo-claim-access.d.ts +1 -1
  111. package/dist/_dts/core/src/demo-claim-readiness.d.ts +18 -0
  112. package/dist/_dts/core/src/demo-lifecycle-status.d.ts +1 -1
  113. package/dist/_dts/core/src/demo-warm-pool-target.d.ts +1 -1
  114. package/dist/_dts/core/src/index.d.ts +4 -0
  115. package/dist/_dts/core/src/mailing-list-targeting-availability.d.ts +68 -0
  116. package/dist/_dts/core/src/mailing-list-targeting.d.ts +2 -0
  117. package/dist/_dts/core/src/ticket-quantity.d.ts +13 -0
  118. package/dist/_dts/db/src/generated/supabase/database.types.d.ts +51 -32
  119. package/dist/_dts/editor/src/chrome/chromeLayers.d.ts +7 -0
  120. package/dist/_dts/editor/src/index.d.ts +1 -0
  121. package/dist/_dts/editor-blocks/src/widgets/link/LinkAddressInput.d.ts +8 -0
  122. package/dist/_dts/editor-blocks/src/widgets/link/LinkContentList.d.ts +12 -0
  123. package/dist/_dts/editor-blocks/src/widgets/link/LinkPicker.d.ts +5 -1
  124. package/dist/_dts/editor-blocks/src/widgets/link/LinkPickerDialog.d.ts +3 -3
  125. package/dist/_dts/editor-blocks/src/widgets/link/LinkSummary.d.ts +8 -2
  126. package/dist/_dts/editor-blocks/src/widgets/link/LinkWidget.d.ts +4 -3
  127. package/dist/_dts/editor-blocks/src/widgets/link/LinkWidgetContext.d.ts +2 -1
  128. package/dist/_dts/editor-blocks/src/widgets/link/draft.d.ts +44 -0
  129. package/dist/_dts/editor-blocks/src/widgets/link/presentation.d.ts +13 -0
  130. package/dist/_dts/editor-blocks/src/widgets/link/types.d.ts +14 -0
  131. package/dist/_dts/editor-blocks/src/widgets/link/useRoutableContent.d.ts +2 -0
  132. package/dist/_dts/editor-blocks/src/widgets/link/utils.d.ts +5 -4
  133. package/dist/_dts/editor-blocks/src/widgets/mailing-list-targeting/MailingListTargetingField.d.ts +1 -1
  134. package/dist/_dts/editor-blocks/src/widgets/mailing-list-targeting/targetingState.d.ts +13 -8
  135. package/dist/_dts/media-core/src/fileStorage.d.ts +4 -3
  136. package/dist/_dts/media-core/src/index.d.ts +1 -0
  137. package/dist/_dts/media-core/src/storageObjectKey.d.ts +12 -0
  138. package/dist/_dts/media-react/src/hooks/useSupabaseMediaUploader.d.ts +1 -1
  139. package/dist/_dts/media-react/src/internal/supabaseResumableUpload.d.ts +13 -0
  140. package/dist/_dts/preview-next/src/client/preview/previewModeRootAttribute.d.ts +11 -0
  141. package/dist/_dts/sdk/src/rendering/blocks-page-renderer.d.ts +0 -1
  142. package/dist/_dts/sdk/src/rendering/image.d.ts +10 -0
  143. package/dist/_dts/sdk/src/rendering/index.d.ts +3 -1
  144. package/dist/_dts/sdk/src/rendering/link.d.ts +9 -0
  145. package/dist/_dts/sdk/src/rendering/overrides.d.ts +1 -0
  146. package/dist/_dts/sdk/src/version.d.ts +1 -1
  147. package/dist/_dts/theme-core/src/blocks/opaquePanels.d.ts +3 -3
  148. package/dist/_dts/theme-core/src/generated/containerResponsiveThemeCss.d.ts +1 -1
  149. package/dist/client/BookingMailingListNote-CBuMKN0w.mjs +386 -0
  150. package/dist/client/{CardListItem-ZDLYh6dL.mjs → CardListItem-Dc9XTXhB.mjs} +3 -4
  151. package/dist/client/CheckIcon-CpyXr-Kg.mjs +35 -0
  152. package/dist/client/{CheckboxChoice-M-3HycLw.mjs → CheckboxChoice-CIqx_o0E.mjs} +1 -1
  153. package/dist/client/{DemoBookingPortalAccess-BMtvPTWE.mjs → DemoBookingPortalAccess-D3VQ_kLj.mjs} +85 -85
  154. package/dist/client/{EventCombined.client-DUm_DOxW.mjs → EventCombined.client-BHGEKise.mjs} +8 -9
  155. package/dist/client/{EventEmbeddedCheckoutStep-DsNNzgRm.mjs → EventEmbeddedCheckoutStep-Bo1Cm23R.mjs} +11 -10
  156. package/dist/client/{EventListing.client-DjNYGUVm.mjs → EventListing.client-D5OQgVPr.mjs} +2 -2
  157. package/dist/client/{LeafletStaticMap-DuFiOHq_.mjs → LeafletStaticMap-CLL5cWrr.mjs} +1 -1
  158. package/dist/client/{ModalShell-CqkBvBIC.mjs → ModalShell-BXzucfR6.mjs} +83 -12
  159. package/dist/client/{RichText-LjPCUGGi.mjs → RichText-BoDmnopf.mjs} +6 -3
  160. package/dist/client/{ThemedButton-DZKjtaDD.mjs → ThemedButton-Ciltp0IK.mjs} +2 -2
  161. package/dist/client/{accordion-Qe--SILD.mjs → accordion-DOkRS64-.mjs} +1 -1
  162. package/dist/client/aspectTransforms-D-TnDdq4.mjs +93 -0
  163. package/dist/client/{bookingForm-3BCl4g_f.mjs → bookingForm-wlMVwo9h.mjs} +289 -149
  164. package/dist/client/bookings.mjs +12212 -10832
  165. package/dist/client/{carousel-h2YjKOS2.mjs → carousel-CGWk-Z0u.mjs} +2 -2
  166. package/dist/client/{carousel.client-BLrd4hmY.mjs → carousel.client-kX5mN1Eg.mjs} +46 -41
  167. package/dist/client/{cart-5NeiigVd.mjs → cart-OBBPlaH4.mjs} +3 -3
  168. package/dist/client/{checkout-G4UWjOEw.mjs → checkout-Bmw2WjQX.mjs} +3 -3
  169. package/dist/client/client.mjs +9541 -6039
  170. package/dist/client/{colorStyles-DTunOThj.mjs → colorStyles-ECDNPE_P.mjs} +23 -86
  171. package/dist/client/{context-GK4-AVAh.mjs → context-BI77UFAn.mjs} +275 -10
  172. package/dist/client/{eventCalendar-Prw3Yo_L.mjs → eventCalendar-qkej2Edn.mjs} +19 -17
  173. package/dist/client/{eventCombined-BUugi6__.mjs → eventCombined-DApKHvat.mjs} +4 -4
  174. package/dist/client/{eventDetails-CBHUda-R.mjs → eventDetails-BQyUDPH0.mjs} +3 -4
  175. package/dist/client/{eventListing-CQeG45Dg.mjs → eventListing-CeAnshlZ.mjs} +4 -4
  176. package/dist/client/{eventRegistration-Dif3p3oJ.mjs → eventRegistration-C3A3NyBO.mjs} +2209 -2144
  177. package/dist/client/{form-Cf0WMtfG.mjs → form-DW8rU7jO.mjs} +6 -6
  178. package/dist/client/{headerSection-Bqdr3JWp.mjs → headerSection-CkIhsoNm.mjs} +9 -9
  179. package/dist/client/hooks.mjs +1289 -380
  180. package/dist/client/{locationMap-BVvPou4O.mjs → locationMap-D4O8wbli.mjs} +4 -4
  181. package/dist/client/{media-47fcnhzD.mjs → media-C5K_0xPd.mjs} +2 -2
  182. package/dist/client/{newsletterForm-Cx-1dnic.mjs → newsletterForm-B7IxSPuz.mjs} +5 -5
  183. package/dist/client/{productDetail-DaUMaJYk.mjs → productDetail-Bqxpwy5N.mjs} +3 -3
  184. package/dist/client/{productList-BKQlWEKE.mjs → productList-u9Lz8V15.mjs} +3 -3
  185. package/dist/client/{propsDecoder-DO5iXphQ.mjs → propsDecoder-Bf1jucUp.mjs} +17333 -14489
  186. package/dist/client/rendering/client.mjs +58216 -55133
  187. package/dist/client/rendering/islands.mjs +19 -19
  188. package/dist/client/rendering.mjs +9099 -5636
  189. package/dist/client/{routeIslandApiConfig-BNm0Z3-g.mjs → routeIslandApiConfig-BgP-aPMl.mjs} +1 -1
  190. package/dist/client/{shop-D7ytcFBC.mjs → shop-B_eykR-_.mjs} +16 -14
  191. package/dist/client/{shop-commerce-D68-hHy2.mjs → shop-commerce-CT0APkFV.mjs} +9 -9
  192. package/dist/client/{shop-commerce-store-Cu7X2lyu.mjs → shop-commerce-store-DcvZxdEK.mjs} +1 -1
  193. package/dist/client/{shopShared-D12rxE69.mjs → shopShared-BVJOiUiJ.mjs} +5 -5
  194. package/dist/client/themePortalRoot-DS92SRVZ.mjs +47 -0
  195. package/dist/client/{TerminalPanel-DOTSUCO0.mjs → useDiscountCodeUi-DPmYDunz.mjs} +60 -77
  196. package/dist/client/{useEventPagination-jgKa67KV.mjs → useEventPagination-8-U55vH5.mjs} +7 -8
  197. package/dist/preview-next/before-render.mjs +7 -0
  198. package/dist/preview-next/client/runtime.mjs +3 -3
  199. package/dist/preview-next/keepAlive.mjs +7 -0
  200. package/dist/preview-next/middleware.mjs +7 -0
  201. package/dist/{sdk-runtime-4cCWO_3i.mjs → sdk-runtime-C5jPdSrk.mjs} +1 -1
  202. package/dist/server/components.mjs +3138 -1373
  203. package/dist/server/config-validation.mjs +1395 -486
  204. package/dist/server/config.mjs +1395 -486
  205. package/dist/server/data.mjs +1289 -380
  206. package/dist/server/index.mjs +8 -1
  207. package/dist/server/next.mjs +3362 -1538
  208. package/dist/server/page-converter.mjs +1357 -453
  209. package/dist/server/prebuild.mjs +1 -1
  210. package/dist/server/rendering/server.mjs +2335 -1044
  211. package/dist/server/rendering.mjs +3152 -1370
  212. package/dist/server/routing.mjs +1437 -490
  213. package/dist/server/server.mjs +1297 -381
  214. package/dist/server/theme-bridge.mjs +1135 -795
  215. package/dist/{spinner-tSdG9YvG.mjs → spinner-B-ab2rJp.mjs} +1 -1
  216. package/dist/styles/index.css +1088 -721
  217. package/package.json +1 -1
  218. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/OrderSummary.d.ts +0 -15
  219. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/useWaitlistClaimRestoration.d.ts +0 -22
  220. package/dist/_dts/blocks/src/system/runtime/shared/components/SelectableOptionCard.d.ts +0 -30
  221. package/dist/client/BookingMailingListNote-DlDSs5Vm.mjs +0 -567
  222. package/dist/client/pricing-CMD0kADd.mjs +0 -314
  223. package/dist/client/result-DRW7JWXz.mjs +0 -30
  224. package/dist/client/scrollToBlock-C86BYkyV.mjs +0 -17
  225. package/dist/client/themePortalRoot-AGs4cE7t.mjs +0 -30
  226. package/dist/spinner-BL3kW9Uh.mjs +0 -803
@@ -1975,6 +1975,29 @@ function canonicalEventPricingDisplay(pricing) {
1975
1975
  default: return assertNever$6(pricing);
1976
1976
  }
1977
1977
  }
1978
+ /**
1979
+ * The single renderer for a precomputed booking price `display` ADT. This is
1980
+ * the one place booking price variants map to user-facing strings — every call
1981
+ * site (booking entry, events calendar, event/course details, listings, portal)
1982
+ * should route through here so labels never drift. Adding a variant to the ADT
1983
+ * breaks compilation here (via `assertNever`) until it is handled.
1984
+ */
1985
+ function formatBookingPriceDisplay(display, options = {}) {
1986
+ switch (display.kind) {
1987
+ case "free": return "Free";
1988
+ case "exact": return formatCurrencyAmount(display.amount, display.currency, options);
1989
+ case "from": return `From ${formatCurrencyAmount(display.amount, display.currency, options)}`;
1990
+ case "range": return `${formatCurrencyAmount(display.minAmount, display.currency, options)} - ${formatCurrencyAmount(display.maxAmount, display.currency, options)}`;
1991
+ case "mixed": return display.paidToAmount > display.paidFromAmount ? `Free / ${formatCurrencyAmount(display.paidFromAmount, display.currency, options)} - ${formatCurrencyAmount(display.paidToAmount, display.currency, options)}` : `Free / ${formatCurrencyAmount(display.paidFromAmount, display.currency, options)}`;
1992
+ case "unavailable": return "Pricing unavailable";
1993
+ case "external": return display.label;
1994
+ default: return assertNever$6(display);
1995
+ }
1996
+ }
1997
+ /** Render an event's price directly from its pricing value. */
1998
+ function formatEventPricing(pricing, options = {}) {
1999
+ return formatBookingPriceDisplay(publicEventPricingDisplay(pricing), options);
2000
+ }
1978
2001
  //#endregion
1979
2002
  //#region ../core/src/event-page-surface-subject.ts
1980
2003
  function createEventSeriesPageSurfaceSubject(eventSeriesId, bookingScope, eventDestinationMode) {
@@ -2118,6 +2141,7 @@ function parseBookingTimeZone(timeZone) {
2118
2141
  }
2119
2142
  //#endregion
2120
2143
  //#region ../core/src/booking-date-time.ts
2144
+ const DEFAULT_LOCALE = "en-US";
2121
2145
  const FALLBACK_TIMEZONE = "UTC";
2122
2146
  const START_OF_DAY = {
2123
2147
  hour: 0,
@@ -2240,6 +2264,34 @@ function toScheduledBookingDateTime(input) {
2240
2264
  if (result.ok) return result.value;
2241
2265
  throw new Error(formatParseError(result));
2242
2266
  }
2267
+ function formatBookingInstant(isoString, options) {
2268
+ const date = new Date(isoString);
2269
+ if (Number.isNaN(date.getTime())) return isoString;
2270
+ return getCachedDateTimeFormatter(options.locale ?? DEFAULT_LOCALE, getSafeTimeZone(options.timeZone), options.format).format(date);
2271
+ }
2272
+ function formatBookingDate(input, format = {
2273
+ weekday: "long",
2274
+ year: "numeric",
2275
+ month: "long",
2276
+ day: "numeric"
2277
+ }) {
2278
+ return formatBookingInstant(input.startsAt, {
2279
+ locale: input.locale,
2280
+ timeZone: input.timeZone,
2281
+ format
2282
+ });
2283
+ }
2284
+ function formatBookingTime(input, format = {
2285
+ hour: "numeric",
2286
+ minute: "2-digit",
2287
+ hour12: true
2288
+ }) {
2289
+ return formatBookingInstant(input.startsAt, {
2290
+ locale: input.locale,
2291
+ timeZone: input.timeZone,
2292
+ format
2293
+ });
2294
+ }
2243
2295
  function getBookingLocalDateKey(input) {
2244
2296
  return formatDateKey$1(getLocalScheduleDate(new Date(input.startsAt), input.timeZone));
2245
2297
  }
@@ -2501,6 +2553,14 @@ function formError(field) {
2501
2553
  }
2502
2554
  //#endregion
2503
2555
  //#region ../core/src/mailing-list-targeting.ts
2556
+ const DEFAULT_MAILING_LIST_TARGETING = { mode: "default" };
2557
+ function mailingListTargetingChoiceIds(targeting) {
2558
+ switch (targeting.provider) {
2559
+ case "native": return targeting.selection.tag === "fixed" ? targeting.selection.topicIds : targeting.selection.offeredTopicIds;
2560
+ case "brevo": return targeting.selection.tag === "fixed" ? targeting.selection.listIds : targeting.selection.offeredListIds;
2561
+ case "mailchimp": return targeting.selection.tag === "fixed" ? targeting.selection.interestIds : targeting.selection.offeredInterestIds;
2562
+ }
2563
+ }
2504
2564
  function planMailingListChoicePresentation(targeting) {
2505
2565
  if (targeting.mode === "default" || targeting.selection.tag === "fixed") return { tag: "hidden" };
2506
2566
  switch (targeting.provider) {
@@ -2540,6 +2600,103 @@ function planMailingListChoicePresentation(targeting) {
2540
2600
  }
2541
2601
  }
2542
2602
  //#endregion
2603
+ //#region ../core/src/mailing-list-targeting-availability.ts
2604
+ function resolveMailingListDefaultDestination(provider) {
2605
+ switch (provider.provider) {
2606
+ case "native": return provider.topics.find((item) => item.id === provider.defaultTopicId) ?? null;
2607
+ case "brevo": return provider.lists.find((item) => item.id === provider.defaultListId) ?? null;
2608
+ case "mailchimp": return provider.audiences.find((item) => item.id === provider.defaultAudienceId) ?? null;
2609
+ }
2610
+ }
2611
+ function assessMailingListTargeting(context, targeting) {
2612
+ if (context?.tag !== "configured") return {
2613
+ tag: "unavailable",
2614
+ reason: "setup_missing",
2615
+ targeting
2616
+ };
2617
+ const catalog = context.provider;
2618
+ if (targeting.mode === "custom") return assessCustomTargeting(catalog, targeting);
2619
+ const destination = resolveMailingListDefaultDestination(catalog);
2620
+ return destination === null ? {
2621
+ tag: "unavailable",
2622
+ reason: "default_missing",
2623
+ targeting
2624
+ } : {
2625
+ tag: "available",
2626
+ value: {
2627
+ mode: "default",
2628
+ targeting,
2629
+ provider: catalog.provider,
2630
+ destination
2631
+ }
2632
+ };
2633
+ }
2634
+ function assessCustomTargeting(catalog, targeting) {
2635
+ switch (targeting.provider) {
2636
+ case "native": return catalog.provider === "native" ? assessMatchedChoices({
2637
+ provider: "native",
2638
+ catalog,
2639
+ targeting
2640
+ }, catalog.topics) : {
2641
+ tag: "unavailable",
2642
+ reason: "provider_mismatch",
2643
+ targeting
2644
+ };
2645
+ case "brevo": return catalog.provider === "brevo" ? assessMatchedChoices({
2646
+ provider: "brevo",
2647
+ catalog,
2648
+ targeting
2649
+ }, catalog.lists) : {
2650
+ tag: "unavailable",
2651
+ reason: "provider_mismatch",
2652
+ targeting
2653
+ };
2654
+ case "mailchimp": {
2655
+ if (catalog.provider !== "mailchimp") return {
2656
+ tag: "unavailable",
2657
+ reason: "provider_mismatch",
2658
+ targeting
2659
+ };
2660
+ const audience = catalog.audiences.find((item) => item.id === targeting.audienceId);
2661
+ return audience === void 0 ? {
2662
+ tag: "unavailable",
2663
+ reason: "audience_missing",
2664
+ value: {
2665
+ mode: "custom",
2666
+ provider: "mailchimp",
2667
+ catalog,
2668
+ targeting
2669
+ }
2670
+ } : assessMatchedChoices({
2671
+ provider: "mailchimp",
2672
+ catalog,
2673
+ targeting,
2674
+ audience
2675
+ }, audience.interests);
2676
+ }
2677
+ }
2678
+ }
2679
+ function assessMatchedChoices(matched, choices) {
2680
+ const selected = new Set(mailingListTargetingChoiceIds(matched.targeting));
2681
+ const available = new Set(choices.map((item) => item.id));
2682
+ const missingIds = [...selected].filter((id) => !available.has(id));
2683
+ const value = {
2684
+ mode: "custom",
2685
+ ...matched,
2686
+ choices,
2687
+ selectedChoices: choices.filter((item) => selected.has(item.id))
2688
+ };
2689
+ return missingIds.length > 0 ? {
2690
+ tag: "unavailable",
2691
+ reason: "choices_missing",
2692
+ missingIds,
2693
+ value
2694
+ } : {
2695
+ tag: "available",
2696
+ value
2697
+ };
2698
+ }
2699
+ //#endregion
2543
2700
  //#region ../core/src/mailing-list-runtime.ts
2544
2701
  function parseMailingListRuntimeContext(value) {
2545
2702
  if (!isRecord$15(value)) return invalidRuntimeContext();
@@ -2604,57 +2761,47 @@ function parseBrevoRuntimeProvider(value) {
2604
2761
  function parseMailchimpRuntimeProvider(value) {
2605
2762
  if (!Array.isArray(value.audiences)) return null;
2606
2763
  const defaultAudienceId = parseMailchimpAudienceId(value.defaultAudienceId);
2607
- if (defaultAudienceId === null) return null;
2608
- const audiences = [];
2609
- for (const audience of value.audiences) {
2610
- if (!isRecord$15(audience)) return null;
2611
- const id = parseMailchimpAudienceId(audience.id);
2612
- const name = parseName(audience.name);
2613
- if (id === null || name === null || !Array.isArray(audience.interests)) return null;
2614
- const interests = parseMailchimpInterests(audience.interests);
2615
- if (interests === null) return null;
2616
- audiences.push({
2617
- id,
2618
- name,
2619
- interests
2620
- });
2621
- }
2622
- return {
2764
+ const audiences = Array.from(value.audiences, parseMailchimpAudience);
2765
+ return defaultAudienceId !== null && audiences.every((audience) => audience !== null) ? {
2623
2766
  provider: "mailchimp",
2624
2767
  defaultAudienceId,
2625
2768
  audiences
2626
- };
2769
+ } : null;
2627
2770
  }
2628
- function parseMailchimpInterests(value) {
2629
- const interests = [];
2630
- for (const interest of value) {
2631
- if (!isRecord$15(interest)) return null;
2632
- const id = parseMailchimpInterestId(interest.id);
2633
- const name = parseName(interest.name);
2634
- const categoryName = parseName(interest.categoryName);
2635
- if (id === null || name === null || categoryName === null) return null;
2636
- interests.push({
2637
- id,
2638
- name,
2639
- categoryName
2640
- });
2641
- }
2642
- return interests;
2771
+ function parseMailchimpAudience(value) {
2772
+ if (!isRecord$15(value) || !Array.isArray(value.interests)) return null;
2773
+ const id = parseMailchimpAudienceId(value.id);
2774
+ const name = parseName(value.name);
2775
+ const interests = Array.from(value.interests, parseMailchimpInterest);
2776
+ return id !== null && name !== null && interests.every((interest) => interest !== null) ? {
2777
+ id,
2778
+ name,
2779
+ interests
2780
+ } : null;
2781
+ }
2782
+ function parseMailchimpInterest(value) {
2783
+ if (!isRecord$15(value)) return null;
2784
+ const id = parseMailchimpInterestId(value.id);
2785
+ const name = parseName(value.name);
2786
+ const categoryName = parseName(value.categoryName);
2787
+ return id !== null && name !== null && categoryName !== null ? {
2788
+ id,
2789
+ name,
2790
+ categoryName
2791
+ } : null;
2643
2792
  }
2644
2793
  function parseNamedItems(value, parseId) {
2645
2794
  if (!Array.isArray(value)) return null;
2646
- const items = [];
2647
- for (const item of value) {
2795
+ const items = Array.from(value, (item) => {
2648
2796
  if (!isRecord$15(item)) return null;
2649
2797
  const id = parseId(item.id);
2650
2798
  const name = parseName(item.name);
2651
- if (id === null || name === null) return null;
2652
- items.push({
2799
+ return id !== null && name !== null ? {
2653
2800
  id,
2654
2801
  name
2655
- });
2656
- }
2657
- return items;
2802
+ } : null;
2803
+ });
2804
+ return items.every((item) => item !== null) ? items : null;
2658
2805
  }
2659
2806
  function parseName(value) {
2660
2807
  if (typeof value !== "string") return null;
@@ -2670,6 +2817,7 @@ function invalidRuntimeContext() {
2670
2817
  //#endregion
2671
2818
  //#region ../core/src/mailing-list-targeting-parser.ts
2672
2819
  function parseMailingListPlacementTargeting(value) {
2820
+ if (value == null) return ok$1(DEFAULT_MAILING_LIST_TARGETING);
2673
2821
  if (!isRecord$14(value)) return targetingError("shape");
2674
2822
  if (value.mode === "default") return ok$1({ mode: "default" });
2675
2823
  if (value.mode !== "custom") return targetingError("mode");
@@ -2768,14 +2916,11 @@ function parseNonEmptyUniqueIds(value, parseId) {
2768
2916
  }
2769
2917
  function parseUniqueIds(value, parseId) {
2770
2918
  if (!Array.isArray(value)) return targetingError("targets");
2771
- const parsed = [];
2772
- for (const item of value) {
2919
+ return Array.from(value).reduce((parsed, item) => {
2920
+ if (!parsed.ok) return parsed;
2773
2921
  const id = parseId(item);
2774
- if (id === null) return targetingError("targets");
2775
- if (parsed.includes(id)) return targetingError("duplicate_target");
2776
- parsed.push(id);
2777
- }
2778
- return ok$1(parsed);
2922
+ return id === null ? targetingError("targets") : parsed.value.includes(id) ? targetingError("duplicate_target") : ok$1([...parsed.value, id]);
2923
+ }, ok$1([]));
2779
2924
  }
2780
2925
  function isRecord$14(value) {
2781
2926
  return typeof value === "object" && value !== null && !Array.isArray(value);
@@ -3207,6 +3352,45 @@ function resolveSiteVerticalBookingConfig(rawConfig) {
3207
3352
  };
3208
3353
  }
3209
3354
  //#endregion
3355
+ //#region ../core/src/event-preset-vocabulary.ts
3356
+ const EVENT_PRESET_VOCABULARY = {
3357
+ event: {
3358
+ label: "Event",
3359
+ pluralLabel: "Events",
3360
+ description: "Any event, repeating or manually dated, that does not fit the other categories."
3361
+ },
3362
+ class: {
3363
+ label: "Class",
3364
+ pluralLabel: "Classes",
3365
+ description: "Regular scheduled classes and group sessions."
3366
+ },
3367
+ course: {
3368
+ label: "Course",
3369
+ pluralLabel: "Courses",
3370
+ description: "A fixed set of dates that people book together."
3371
+ },
3372
+ term: {
3373
+ label: "Term",
3374
+ pluralLabel: "Terms",
3375
+ description: "A fixed term of sessions that people book together."
3376
+ },
3377
+ workshop: {
3378
+ label: "Workshop",
3379
+ pluralLabel: "Workshops",
3380
+ description: "One-off workshops, day sessions, or special events."
3381
+ },
3382
+ retreat: {
3383
+ label: "Retreat",
3384
+ pluralLabel: "Retreats",
3385
+ description: "Multi-day or residential immersive experiences."
3386
+ },
3387
+ performance: {
3388
+ label: "Performance",
3389
+ pluralLabel: "Performances",
3390
+ description: "Audience-facing shows, concerts, recitals, or showcases."
3391
+ }
3392
+ };
3393
+ //#endregion
3210
3394
  //#region ../api/src/publicEventEndpointDefinitions.ts
3211
3395
  const PUBLIC_EVENT_ENDPOINT_DEFINITIONS = defineEndpointDefinitions({
3212
3396
  checkPublicEventRegistrationNextStep: {
@@ -9457,6 +9641,13 @@ const API_ENDPOINTS$1 = {
9457
9641
  auth: "public",
9458
9642
  responseKind: "json"
9459
9643
  },
9644
+ demoClaimReadiness: {
9645
+ path: "/demo/claims/{claimId}/readiness",
9646
+ method: "GET",
9647
+ auth: "user",
9648
+ responseKind: "json",
9649
+ errors: []
9650
+ },
9460
9651
  demoClaimMagicLink: {
9461
9652
  path: "/demo/auth/send-magic-link",
9462
9653
  method: "POST",
@@ -10362,7 +10553,7 @@ var SimpleCache = class {
10362
10553
  * 1. This constant
10363
10554
  * 2. The "version" field in package.json
10364
10555
  */
10365
- const SDK_VERSION = "0.151.0";
10556
+ const SDK_VERSION = "0.152.0";
10366
10557
  //#endregion
10367
10558
  //#region src/client/error.ts
10368
10559
  let _Symbol$for;
@@ -40015,6 +40206,15 @@ const formBlockDefinition = {
40015
40206
  runtime: formBlockRuntime
40016
40207
  };
40017
40208
  //#endregion
40209
+ //#region ../blocks/src/system/helpers/normalizeMailingListBlockContent.ts
40210
+ /** Fill the declared default without admitting or rewriting invalid custom settings. */
40211
+ function normalizeMailingListBlockContent(record) {
40212
+ return record.targeting == null ? {
40213
+ ...record,
40214
+ targeting: DEFAULT_MAILING_LIST_TARGETING
40215
+ } : record;
40216
+ }
40217
+ //#endregion
40018
40218
  //#region ../blocks/src/system/blocks/contact-newsletter.ts
40019
40219
  const contactNewsletterLayout = styledSection({
40020
40220
  children: sectionContainer([el("div", { className: "contact-newsletter-grid" }, [el("div", { className: "contact-newsletter-column contact-newsletter-contact" }, [el("div", { className: "contact-newsletter-copy" }, [text({
@@ -40178,6 +40378,10 @@ const contactNewsletterBlockDefinition = {
40178
40378
  ],
40179
40379
  icon: "MailPlus"
40180
40380
  }),
40381
+ contentNormalizer: {
40382
+ fieldIds: ["targeting"],
40383
+ normalize: normalizeMailingListBlockContent
40384
+ },
40181
40385
  dataSchemas: { form: formDataSchema.optional() },
40182
40386
  dataLoaders: { form: {
40183
40387
  endpoint: "getPublicFormById",
@@ -43866,6 +44070,14 @@ bookingPaymentOptionSchemas.flexibleBalanceConfig;
43866
44070
  bookingPaymentOptionSchemas.paymentCollectionOption;
43867
44071
  const bookingPaymentTermsSchema = bookingPaymentOptionSchemas.paymentTerms;
43868
44072
  bookingPaymentOptionSchemas.paymentOptionsChoice;
44073
+ z.strictObject({
44074
+ currency: z.string().trim().length(3),
44075
+ totalCents: nonNegativeIntegerSchema,
44076
+ dueNowCents: nonNegativeIntegerSchema,
44077
+ laterPaymentAmountsCents: z.array(nonNegativeIntegerSchema),
44078
+ paymentOption: z.union([createBookingPaymentOptionSchemas("strict").paymentCollectionOption, z.strictObject({ kind: z.literal("not_required") })]),
44079
+ promisedDiscountId: z.string().min(1).nullable().optional()
44080
+ });
43869
44081
  //#endregion
43870
44082
  //#region ../blocks/src/system/blocks/shared/bookingPaymentTerms.ts
43871
44083
  const blockBookingPaymentTermsSchema = bookingPaymentTermsSchema;
@@ -44106,6 +44318,10 @@ const publicEventBaseSchema = z.object({
44106
44318
  eventDestinationMode: eventDestinationModeSchema.optional(),
44107
44319
  scheduleScope: eventScheduleScopeSchema$2.optional(),
44108
44320
  recurrence: publicEventRecurrenceSchema.optional(),
44321
+ scheduleSummary: z.object({
44322
+ recurrenceDescription: z.string(),
44323
+ startTime: z.string().regex(/^(?:[01]\d|2[0-3]):[0-5]\d$/).nullable()
44324
+ }).nullable().optional(),
44109
44325
  id: z.string(),
44110
44326
  occurrenceId: z.string(),
44111
44327
  seriesId: z.string(),
@@ -44516,68 +44732,267 @@ const occurrenceContextSchema = z.object({
44516
44732
  overrides: z.record(z.string(), z.unknown()).nullable().optional()
44517
44733
  }).nullable();
44518
44734
  //#endregion
44519
- //#region ../blocks/src/system/runtime/api/readAuthority.ts
44520
- const browserReadAuthoritySchema = z.discriminatedUnion("kind", [z.object({ kind: z.literal("published") }).strict(), z.object({
44521
- kind: z.literal("preview"),
44522
- token: z.string().min(1)
44523
- }).strict()]);
44524
- const unsupportedEventRegistrationPageSurfaceSubjectSchema = z.object({
44525
- kind: z.literal("unsupported"),
44526
- reason: z.enum([
44527
- "unsupportedSubject",
44528
- "invalidEventSeriesId",
44529
- "invalidEventSeriesBookingScope",
44530
- "invalidEventOfferingId",
44531
- "invalidEventOfferingBookingScope",
44532
- "invalidEventOfferingCatalogue"
44533
- ])
44534
- });
44535
- const eventRegistrationPageSurfaceSubjectSchema = z.unknown().transform((value, ctx) => {
44536
- const unsupported = unsupportedEventRegistrationPageSurfaceSubjectSchema.safeParse(value);
44537
- if (unsupported.success) return unsupported.data;
44538
- const parsed = parseEventPageSurfaceSubject(value);
44539
- if (parsed.ok) return parsed.value;
44540
- ctx.addIssue({
44541
- code: z.ZodIssueCode.custom,
44542
- message: `Invalid event page-surface subject (${parsed.error.kind})`
44543
- });
44544
- return z.NEVER;
44545
- });
44546
- function resolveEventRegistrationScope(context) {
44547
- const occurrenceSeriesId = context.occurrenceContext?.seriesId;
44548
- if (occurrenceSeriesId) return {
44549
- kind: "occurrenceSeries",
44550
- seriesId: occurrenceSeriesId
44735
+ //#region ../blocks/src/system/runtime/nodes/events/domain/capacity.ts
44736
+ /**
44737
+ * Compute remaining spots for an event from `availableSpots` with a fallback
44738
+ * to `capacity - registeredCount`. Returns `{ available: null, isFull: false }`
44739
+ * for uncapped events so call sites can render no capacity indicator at all.
44740
+ *
44741
+ * The single source of truth for capacity math across event cards, agenda
44742
+ * rows, and timetable cells — keep wording variations in the call sites.
44743
+ */
44744
+ function getEventAvailability(event) {
44745
+ if (event.capacity == null) return {
44746
+ available: null,
44747
+ isFull: false
44551
44748
  };
44552
- if (context.subject?.kind === "eventSeries") return {
44553
- kind: "pageSurfaceSeries",
44554
- seriesId: context.subject.eventSeriesId
44749
+ const available = event.availableSpots ?? event.capacity - event.registeredCount;
44750
+ if (available <= 0) return {
44751
+ available: 0,
44752
+ isFull: true
44555
44753
  };
44556
- if (context.subject?.kind === "eventOffering") return {
44557
- kind: "pageSurfaceOffering",
44558
- offeringId: context.subject.eventOfferingId,
44559
- seriesIds: context.subject.catalogueEventSeriesIds
44754
+ return {
44755
+ available,
44756
+ isFull: false
44560
44757
  };
44561
- if (context.subject?.kind === "unsupported") return {
44562
- kind: "unsupportedPageSurfaceSubject",
44563
- reason: context.subject.reason
44758
+ }
44759
+ /**
44760
+ * Pick the capacity row a booking decision should read: the selected
44761
+ * attendance mode's availability when the visitor has chosen one and the
44762
+ * event publishes it, otherwise the event-level aggregate.
44763
+ *
44764
+ * The mode row is used wholesale. A mode with `capacity: null` on a capped
44765
+ * event is uncapped *by design*: the registration write path applies the
44766
+ * same rule via `getEffectiveOccurrenceCapacityForAttendanceMode`, so falling
44767
+ * back to the event's `availableSpots` here would block bookings the server
44768
+ * accepts. (Waitlist claims are still occurrence-scoped; see issue 1377.)
44769
+ */
44770
+ function selectEventCapacitySource(event, attendanceMode) {
44771
+ if (!attendanceMode) return event;
44772
+ return (event.attendanceModes ?? []).find((mode) => mode.mode === attendanceMode) ?? event;
44773
+ }
44774
+ /**
44775
+ * Whether the visitor's next step is the waitlist: the source is full *and*
44776
+ * a waitlist is open. The one place this is decided — cards, rows, the
44777
+ * wizard's steps and its action bar all route through here so they agree,
44778
+ * including for producers that omit `availableSpots` and rely on the
44779
+ * `capacity - registeredCount` fallback.
44780
+ */
44781
+ function canJoinEventWaitlist(event, options) {
44782
+ const source = selectEventCapacitySource(event, options?.attendanceMode);
44783
+ return getEventAvailability(source).isFull && source.waitlistEnabled === true;
44784
+ }
44785
+ /**
44786
+ * Remaining places at or below which an event reads as scarce and the exact
44787
+ * count is worth showing. Above it, no number is shown at all: "147 places
44788
+ * left" is not something a visitor acts on, and it is the figure most likely
44789
+ * to be stale on a cached page.
44790
+ *
44791
+ * max( min(3, ceil(capacity * 0.25)), ceil(capacity * 0.05) )
44792
+ *
44793
+ * The 25% arm stops small groups shouting early (3 of 4 is not urgency); the
44794
+ * 5% floor stops large events staying silent until three remain.
44795
+ * capacity 4 -> 1, 12 -> 3, 40 -> 3, 100 -> 5, 200 -> 10.
44796
+ */
44797
+ function lowCapacityThreshold(capacity) {
44798
+ return Math.max(Math.min(3, Math.ceil(capacity * .25)), Math.ceil(capacity * .05));
44799
+ }
44800
+ function getRemainingPlaces(source) {
44801
+ const { available, isFull } = getEventAvailability(source);
44802
+ if (available === null || source.capacity === null) return { kind: "unlimited" };
44803
+ if (isFull) return {
44804
+ kind: "full",
44805
+ waitlist: canJoinEventWaitlist(source)
44564
44806
  };
44565
- return { kind: "unscoped" };
44807
+ if (available <= lowCapacityThreshold(source.capacity)) return {
44808
+ kind: "low",
44809
+ remaining: available
44810
+ };
44811
+ return { kind: "open" };
44566
44812
  }
44567
- function resolveEventRegistrationScopedEvents(events, context) {
44568
- const scope = resolveEventRegistrationScope(context);
44569
- switch (scope.kind) {
44570
- case "occurrenceSeries":
44571
- case "pageSurfaceSeries": return events.filter((event) => event.seriesId === scope.seriesId);
44572
- case "pageSurfaceOffering": {
44573
- const seriesIds = new Set(scope.seriesIds);
44574
- return events.filter((event) => seriesIds.has(event.seriesId));
44575
- }
44576
- case "unsupportedPageSurfaceSubject": return [];
44577
- case "unscoped": return [...events];
44813
+ /**
44814
+ * The one place a remaining-places count becomes text. Returns `null` when
44815
+ * nothing should be shown: uncapped events, and capped events above the
44816
+ * scarcity threshold. Surfaces that need their own full-state wording pass
44817
+ * it in rather than re-deriving the count.
44818
+ */
44819
+ function formatRemainingPlaces(source, wording = {}) {
44820
+ const remaining = getRemainingPlaces(source);
44821
+ switch (remaining.kind) {
44822
+ case "unlimited":
44823
+ case "open": return null;
44824
+ case "low": return formatPlacesLeft(remaining.remaining, wording.compact === true);
44825
+ case "full": return remaining.waitlist ? wording.waitlist ?? "Waitlist" : wording.full ?? "Full";
44826
+ }
44827
+ }
44828
+ function formatPlacesLeft(count, compact) {
44829
+ if (compact) return `${count} left`;
44830
+ return count === 1 ? "1 place left" : `${count} places left`;
44831
+ }
44832
+ function formatTicketTypeRemaining(remainingCapacity) {
44833
+ if (remainingCapacity === null || remainingCapacity > 3) return null;
44834
+ if (remainingCapacity <= 0) return "None left for this ticket type";
44835
+ return `${formatPlacesLeft(remainingCapacity, false)} for this ticket type`;
44836
+ }
44837
+ /**
44838
+ * Which capacity figure wins when they disagree.
44839
+ *
44840
+ * For a hybrid event that publishes both attendance-mode rows, **the mode
44841
+ * rows are authoritative and the event-level aggregate is only a summary**.
44842
+ * The registration write path admits or refuses per mode
44843
+ * (`getEffectiveOccurrenceCapacityForAttendanceMode` on the server), so the
44844
+ * aggregate can read full while a mode still has room, and can read open
44845
+ * while the mode the visitor wants is full. Believing the aggregate on a
44846
+ * card is how a "places available" event turns into a dead end at step two.
44847
+ *
44848
+ * Everything else — single-mode events, events that publish no rows, and the
44849
+ * aggregate-only helpers {@link getEventAvailability} /
44850
+ * {@link getRemainingPlaces} that existing callers rely on — keeps reading
44851
+ * the aggregate, unchanged.
44852
+ */
44853
+ function findHybridModeRows(event) {
44854
+ if (event.deliveryMode !== "hybrid") return null;
44855
+ const rows = event.attendanceModes ?? [];
44856
+ const inPerson = rows.find((row) => row.mode === "in_person");
44857
+ const online = rows.find((row) => row.mode === "online");
44858
+ return inPerson && online ? {
44859
+ inPerson,
44860
+ online
44861
+ } : null;
44862
+ }
44863
+ /** Openness rank; higher means more room. A full mode with a waitlist beats one without. */
44864
+ function remainingPlacesRank(remaining) {
44865
+ switch (remaining.kind) {
44866
+ case "unlimited": return 4;
44867
+ case "open": return 3;
44868
+ case "low": return 2;
44869
+ case "full": return remaining.waitlist ? 1 : 0;
44870
+ }
44871
+ }
44872
+ function isMoreOpen(candidate, than) {
44873
+ const rank = remainingPlacesRank(candidate);
44874
+ const otherRank = remainingPlacesRank(than);
44875
+ if (rank !== otherRank) return rank > otherRank;
44876
+ return candidate.kind === "low" && than.kind === "low" ? candidate.remaining > than.remaining : false;
44877
+ }
44878
+ /** True when `candidate` has less room left than `than`. */
44879
+ function isScarcer(candidate, than) {
44880
+ return isMoreOpen(than, candidate);
44881
+ }
44882
+ const ATTENDANCE_MODE_LABEL = {
44883
+ in_person: "In person",
44884
+ online: "Online"
44885
+ };
44886
+ /** Wording for a mode with room to spare, where the count is deliberately withheld. */
44887
+ const PLACES_AVAILABLE = "Places available";
44888
+ /**
44889
+ * The browse-surface label for an event's remaining places. Single-mode
44890
+ * events get exactly the aggregate label {@link formatRemainingPlaces}
44891
+ * produces, and so does a hybrid event whose two modes tell the same story.
44892
+ * When the modes differ, both sides are named: "available" on a card is a
44893
+ * promise the registration wizard would otherwise have to break.
44894
+ *
44895
+ * Hybrid examples, in person full with 3 online places left:
44896
+ * default -> "Online: 3 places left · In person: Full"
44897
+ * { full: 'Sold out' } -> "Online: 3 places left · In person: Sold out"
44898
+ * { compact: true } -> "Online: 3 left"
44899
+ */
44900
+ function formatEventRemainingPlaces(event, wording = {}) {
44901
+ const rows = findHybridModeRows(event);
44902
+ if (!rows) return formatRemainingPlaces(event, wording);
44903
+ const inPerson = formatRemainingPlaces(rows.inPerson, wording);
44904
+ const online = formatRemainingPlaces(rows.online, wording);
44905
+ if (inPerson === online) return inPerson;
44906
+ if (wording.compact === true) return formatCompactHybridPlaces(rows, wording);
44907
+ return formatBothHybridSides(rows, {
44908
+ inPerson,
44909
+ online
44910
+ });
44911
+ }
44912
+ /**
44913
+ * Both sides, the one with room first: that is the half a visitor can act on,
44914
+ * and it should not arrive after a full stop's worth of bad news.
44915
+ */
44916
+ function formatBothHybridSides(rows, labels) {
44917
+ return (isMoreOpen(getRemainingPlaces(rows.online), getRemainingPlaces(rows.inPerson)) ? [["online", labels.online], ["in_person", labels.inPerson]] : [["in_person", labels.inPerson], ["online", labels.online]]).map(([mode, label]) => namedModePlaces(mode, label)).join(" · ");
44918
+ }
44919
+ function namedModePlaces(mode, label) {
44920
+ return `${ATTENDANCE_MODE_LABEL[mode]}: ${label ?? PLACES_AVAILABLE}`;
44921
+ }
44922
+ /**
44923
+ * Pills and timetable cells have room for one short phrase, so they name the
44924
+ * single mode that carries news rather than listing both: the scarce side
44925
+ * when there is a count to show, and otherwise the side that has closed —
44926
+ * "In person: Full", never "Online only", which reads as a statement about
44927
+ * how the event is delivered rather than about its remaining places.
44928
+ */
44929
+ function formatCompactHybridPlaces(rows, wording) {
44930
+ const inPerson = getRemainingPlaces(rows.inPerson);
44931
+ const online = getRemainingPlaces(rows.online);
44932
+ if (inPerson.kind === "full" && online.kind === "full") return formatRemainingPlaces(inPerson.waitlist ? rows.inPerson : rows.online, wording);
44933
+ if (inPerson.kind === "full" || online.kind === "full") {
44934
+ const fullSide = inPerson.kind === "full" ? "in_person" : "online";
44935
+ const openSide = inPerson.kind === "full" ? "online" : "in_person";
44936
+ const openLabel = formatRemainingPlaces(rowFor(rows, openSide), wording);
44937
+ return openLabel !== null ? namedModePlaces(openSide, openLabel) : namedModePlaces(fullSide, formatRemainingPlaces(rowFor(rows, fullSide), wording));
44578
44938
  }
44939
+ const scarcerSide = isScarcer(online, inPerson) ? "online" : "in_person";
44940
+ return namedModePlaces(scarcerSide, formatRemainingPlaces(rowFor(rows, scarcerSide), wording));
44941
+ }
44942
+ function rowFor(rows, mode) {
44943
+ return mode === "online" ? rows.online : rows.inPerson;
44579
44944
  }
44580
44945
  //#endregion
44946
+ //#region ../blocks/src/system/runtime/nodes/event-registration/registrationDisplay.ts
44947
+ const registrationDisplaySchema = z.object({
44948
+ venue: z.enum(["shown", "hidden"]),
44949
+ placesLeft: z.enum(["shown", "hidden"])
44950
+ });
44951
+ function resolveRegistrationDisplay(source) {
44952
+ return {
44953
+ venue: source.showVenue === false ? "hidden" : "shown",
44954
+ placesLeft: source.showCapacity === false ? "hidden" : "shown"
44955
+ };
44956
+ }
44957
+ /** Venue stays in domain data; views receive only the permitted presentation. */
44958
+ function shownVenue(event, display) {
44959
+ return display.venue === "shown" ? event.venue?.name ?? null : null;
44960
+ }
44961
+ /** Full/waitlist explanations remain visible even when optional counts are hidden. */
44962
+ function placesLeftLabel(source, display, wording = {}) {
44963
+ if ("kind" in source) return ticketPlacesLabel(source.remaining, display);
44964
+ if (source.deliveryMode === "hybrid" && source.attendanceModes?.length) return hybridPlacesLabel(source, display, wording);
44965
+ return capacityPlacesLabel(source, display, wording);
44966
+ }
44967
+ function capacityPlacesLabel(source, display, wording) {
44968
+ const remaining = getRemainingPlaces(source);
44969
+ if (display.placesLeft === "hidden" && remaining.kind !== "full") return null;
44970
+ switch (remaining.kind) {
44971
+ case "full": return formatRemainingPlaces(source, wording);
44972
+ case "low": return formatRemainingPlaces(source, wording);
44973
+ case "unlimited": return wording.unlimited ?? null;
44974
+ case "open": return wording.open ?? null;
44975
+ default: return assertNever$6(remaining);
44976
+ }
44977
+ }
44978
+ function hybridPlacesLabel(event, display, wording) {
44979
+ if (display.placesLeft === "shown") return formatEventRemainingPlaces(event, wording);
44980
+ return (event.attendanceModes ?? []).flatMap((mode) => {
44981
+ const label = placesLeftLabel(mode, display, wording);
44982
+ return label ? [`${mode.mode === "online" ? "Online" : "In person"}: ${label}`] : [];
44983
+ }).join(" · ") || null;
44984
+ }
44985
+ function ticketPlacesLabel(remaining, display) {
44986
+ if (remaining !== null && remaining > 0 && display.placesLeft === "hidden") return null;
44987
+ return formatTicketTypeRemaining(remaining);
44988
+ }
44989
+ //#endregion
44990
+ //#region ../blocks/src/system/runtime/api/readAuthority.ts
44991
+ const browserReadAuthoritySchema = z.discriminatedUnion("kind", [z.object({ kind: z.literal("published") }).strict(), z.object({
44992
+ kind: z.literal("preview"),
44993
+ token: z.string().min(1)
44994
+ }).strict()]);
44995
+ //#endregion
44581
44996
  //#region ../calendar/src/grid.ts
44582
44997
  const MONTH_NAMES = [
44583
44998
  "January",
@@ -44664,6 +45079,11 @@ function buildCalendarViewport(input) {
44664
45079
  //#endregion
44665
45080
  //#region ../blocks/src/system/runtime/nodes/events/domain/dates.ts
44666
45081
  /**
45082
+ * Date and time presentation for events: the single place an event's
45083
+ * timestamps become strings or date buckets. Calendar-grid primitives are
45084
+ * re-exported from @riverbankcms/calendar so block code has one import.
45085
+ */
45086
+ /**
44667
45087
  * The English locale a site's public dates are written in, read from the
44668
45088
  * site's own time zone (Will, 2026-09-26: "date style from site time zone").
44669
45089
  *
@@ -44683,6 +45103,28 @@ function eventDateLocale(siteTimeZone) {
44683
45103
  if (siteTimeZone === "Pacific/Auckland" || siteTimeZone === "Pacific/Chatham") return "en-NZ";
44684
45104
  return "en-US";
44685
45105
  }
45106
+ function formatEventCompactDateRail(isoTimestamp, options, letterCase = "capitals") {
45107
+ const locale = options?.locale ?? "en-US";
45108
+ const dateTime = toScheduledBookingDateTime({
45109
+ startsAt: isoTimestamp,
45110
+ timeZone: toBookingTimeZone(options?.timeZone ?? "UTC"),
45111
+ locale
45112
+ });
45113
+ const cased = (value) => letterCase === "capitals" ? value.toLocaleUpperCase(locale) : value;
45114
+ const weekday = cased(formatBookingDate(dateTime, { weekday: "short" }));
45115
+ const month = cased(formatBookingDate(dateTime, { month: "short" }).slice(0, 3));
45116
+ return {
45117
+ weekday,
45118
+ day: formatBookingDate(dateTime, { day: "numeric" }),
45119
+ month,
45120
+ label: formatBookingDate(dateTime, {
45121
+ weekday: "long",
45122
+ year: "numeric",
45123
+ month: "long",
45124
+ day: "numeric"
45125
+ })
45126
+ };
45127
+ }
44686
45128
  //#endregion
44687
45129
  //#region ../blocks/src/system/runtime/nodes/events/domain/destination.ts
44688
45130
  function isRecord$8(value) {
@@ -44702,6 +45144,15 @@ function resolveRuntimeEventDestination(event) {
44702
45144
  };
44703
45145
  }
44704
45146
  //#endregion
45147
+ //#region ../blocks/src/system/runtime/nodes/events/domain/pricing.ts
45148
+ /**
45149
+ * Format an event's price directly from its pricing value (without the full
45150
+ * event). Same output as {@link formatEventPrice}.
45151
+ */
45152
+ function formatEventPriceDisplay(pricing) {
45153
+ return formatEventPricing(pricing, { decimals: "auto" });
45154
+ }
45155
+ //#endregion
44705
45156
  //#region ../blocks/src/system/runtime/nodes/events/domain/listWeek.ts
44706
45157
  /** Civil weeks use the site zone, including weeks containing a clock change. */
44707
45158
  function buildEventListWeek(anchor, startOfWeek, timeZone) {
@@ -44736,52 +45187,6 @@ function eventListWindowEvents(events, window) {
44736
45187
  });
44737
45188
  }
44738
45189
  //#endregion
44739
- //#region ../blocks/src/system/runtime/nodes/shared/contract-schemas.ts
44740
- const previewStageSchema = z.enum(["preview", "published"]);
44741
- const registrationButtonVariantSchema = blockButtonRoleSchema;
44742
- const eventButtonVariantSchema = blockButtonRoleSchema;
44743
- const eventLayoutSchema = z.enum(eventCalendarListLayoutValues);
44744
- const eventCalendarDisplayModeSchema = z.enum(eventCalendarDisplayModeValues);
44745
- const eventCalendarDefaultViewSchema = z.enum(eventCalendarDefaultViewValues);
44746
- const columnOptionSchema = z.enum([
44747
- "2",
44748
- "3",
44749
- "4"
44750
- ]);
44751
- const weekStartSchema = z.enum(["sunday", "monday"]);
44752
- const filterAutoShowSchema = z.enum([
44753
- "auto",
44754
- "always",
44755
- "never"
44756
- ]);
44757
- const calendarTabViewsSchema = z.enum([
44758
- "both",
44759
- "month",
44760
- "week"
44761
- ]);
44762
- //#endregion
44763
- //#region ../blocks/src/system/runtime/nodes/event-registration/wizardSteps.ts
44764
- /**
44765
- * Determine which wizard steps to show based on context and payment requirements.
44766
- *
44767
- * @param hasPreSelectedContext - Whether event is pre-selected (skips event selection)
44768
- * @param needsPaymentStep - Whether user must complete payment method selection
44769
- * @returns Array of wizard steps to display
44770
- */
44771
- function getWizardStepsForContext(hasPreSelectedContext, needsPaymentStep, paymentActive = false) {
44772
- const baseSteps = hasPreSelectedContext ? ["select-tickets", "enter-details"] : [
44773
- "select-event",
44774
- "select-tickets",
44775
- "enter-details"
44776
- ];
44777
- if (!needsPaymentStep) return baseSteps;
44778
- return paymentActive ? [
44779
- ...baseSteps,
44780
- "select-payment",
44781
- "payment"
44782
- ] : [...baseSteps, "select-payment"];
44783
- }
44784
- //#endregion
44785
45190
  //#region ../blocks/src/system/runtime/nodes/event-registration/registrationDerivations.ts
44786
45191
  function eventMatchesOccurrenceId(event, occurrenceId) {
44787
45192
  return event.registrationSubject.kind === "occurrence" && (event.registrationSubject.occurrenceId === occurrenceId || event.selectionId === occurrenceId);
@@ -44805,6 +45210,424 @@ function resolveEventRequiresPayment(input) {
44805
45210
  return publicEventPricingType(input.event.pricing) === "paid";
44806
45211
  }
44807
45212
  //#endregion
45213
+ //#region ../blocks/src/system/runtime/nodes/event-registration/utils.ts
45214
+ /**
45215
+ * The locale a site's booking dates are written in: the one the site's time
45216
+ * zone places it in (`eventDateLocale`, shared with event cards), or British
45217
+ * English when the site names no zone.
45218
+ */
45219
+ function bookingDateLocale(siteTimeZone) {
45220
+ return siteTimeZone ? eventDateLocale(siteTimeZone) : "en-GB";
45221
+ }
45222
+ function bookingInstant(startsAt, input) {
45223
+ return toScheduledBookingDateTime({
45224
+ startsAt,
45225
+ timeZone: toBookingTimeZone(input.timeZone),
45226
+ locale: bookingDateLocale(input.siteTimeZone)
45227
+ });
45228
+ }
45229
+ /** "7:00pm": a twelve-hour clock with a lowercase, unspaced day period. */
45230
+ function bookingClock(startsAt, input) {
45231
+ return formatBookingTime(bookingInstant(startsAt, input), {
45232
+ hour: "numeric",
45233
+ minute: "2-digit",
45234
+ hour12: true
45235
+ }).replace(/\s*([ap])\.?\s?m\.?$/i, (_match, period) => `${period.toLowerCase()}m`);
45236
+ }
45237
+ /** "7:00pm – 8:15pm", or just the start when there's no distinct end. */
45238
+ function clockRange(input) {
45239
+ const start = bookingClock(input.startsAt, input);
45240
+ const end = input.endsAt ? bookingClock(input.endsAt, input) : null;
45241
+ return end && end !== start ? `${start} – ${end}` : start;
45242
+ }
45243
+ /** A stored local clock has no date or DST offset; UTC preserves its digits. */
45244
+ function formatBookingScheduleSummary(event) {
45245
+ const summary = event.scheduleSummary;
45246
+ if (!summary) return null;
45247
+ const time = summary.startTime ? formatBookingWhen({
45248
+ startsAt: `1970-01-01T${summary.startTime}:00Z`,
45249
+ timeZone: "UTC",
45250
+ siteTimeZone: event.siteTimeZone
45251
+ }, "time") : null;
45252
+ return [summary.recurrenceDescription, time].filter(Boolean).join(" · ");
45253
+ }
45254
+ /** The one booking date formatter; every variant derives from it. */
45255
+ function formatBookingWhen(input, variant) {
45256
+ const instant = bookingInstant(input.startsAt, input);
45257
+ const day = formatBookingDate(instant, {
45258
+ weekday: "short",
45259
+ day: "numeric",
45260
+ month: "short"
45261
+ });
45262
+ switch (variant) {
45263
+ case "day": return day;
45264
+ case "dayLong": return formatBookingDate(instant, {
45265
+ weekday: "long",
45266
+ day: "numeric",
45267
+ month: "long"
45268
+ });
45269
+ case "month": return formatBookingDate(instant, { month: "long" });
45270
+ case "monthYear": return formatBookingDate(instant, {
45271
+ month: "long",
45272
+ year: "numeric"
45273
+ });
45274
+ case "time": return bookingClock(input.startsAt, input);
45275
+ case "timeRange": return clockRange(input);
45276
+ case "short": return `${day}, ${bookingClock(input.startsAt, input)}`;
45277
+ case "long": return `${day} · ${clockRange(input)}`;
45278
+ }
45279
+ }
45280
+ /** A single instant in the short form, e.g. "Mon 5 Oct, 7:00pm". */
45281
+ function formatEventDateTime(isoTimestamp, options) {
45282
+ return formatBookingWhen({
45283
+ startsAt: isoTimestamp,
45284
+ timeZone: options?.timeZone,
45285
+ siteTimeZone: options?.siteTimeZone
45286
+ }, "short");
45287
+ }
45288
+ /** The title an occurrence was given on its own, when it overrides the event's. */
45289
+ function occurrenceTitleOverride(occurrence) {
45290
+ const title = occurrence?.overrides?.title;
45291
+ return typeof title === "string" && title.trim() ? title.trim() : null;
45292
+ }
45293
+ /** The event as this page names it: an occurrence's own title wins. */
45294
+ function withOccurrenceTitle(event, occurrence) {
45295
+ const title = occurrenceTitleOverride(occurrence);
45296
+ return event && title ? {
45297
+ ...event,
45298
+ title
45299
+ } : event;
45300
+ }
45301
+ function courseSchedule(sessions, siteTimeZone) {
45302
+ const first = sessions[0];
45303
+ if (!first) return null;
45304
+ const last = sessions[sessions.length - 1] ?? first;
45305
+ const firstLabel = formatEventDateTime(first.startsAt, {
45306
+ timeZone: first.timeZone,
45307
+ siteTimeZone
45308
+ });
45309
+ if (last.startsAt === first.startsAt) return `${sessions.length} session${sessions.length === 1 ? "" : "s"} · ${firstLabel}`;
45310
+ const lastLabel = formatEventDateTime(last.startsAt, {
45311
+ timeZone: last.timeZone,
45312
+ siteTimeZone
45313
+ });
45314
+ return `${sessions.length} sessions · ${firstLabel} – ${lastLabel}`;
45315
+ }
45316
+ /**
45317
+ * When a booking happens, in one line: the occurrence's date and time, or a
45318
+ * course's session count and first and last dates. `short` by default (bar,
45319
+ * confirmation); `long` adds the end time (card, panel header).
45320
+ */
45321
+ function formatRegistrationSchedule(event, occurrence, variant = "short") {
45322
+ if (event.registrationSubject.kind === "wholeSeries") return courseSchedule(event.registrationSubject.sessions, event.siteTimeZone);
45323
+ const input = scheduleInput(event, occurrence ?? null);
45324
+ return input ? formatBookingWhen(input, variant) : null;
45325
+ }
45326
+ /** The occurrence's own times when the page names one, otherwise the event's. */
45327
+ function scheduleInput(event, occurrence) {
45328
+ const times = occurrence?.startsAt ? {
45329
+ startsAt: occurrence.startsAt,
45330
+ endsAt: occurrence.endsAt ?? null
45331
+ } : {
45332
+ startsAt: event.startsAt,
45333
+ endsAt: event.endsAt
45334
+ };
45335
+ if (!times.startsAt) return null;
45336
+ return {
45337
+ ...times,
45338
+ timeZone: occurrence?.timeZone ?? event.timeZone ?? null,
45339
+ siteTimeZone: event.siteTimeZone
45340
+ };
45341
+ }
45342
+ //#endregion
45343
+ //#region ../blocks/src/system/runtime/nodes/event-registration/wizardSteps.ts
45344
+ function registrationRoute(input) {
45345
+ const base = input.chooseEvent ? [
45346
+ "select-event",
45347
+ "select-tickets",
45348
+ "enter-details"
45349
+ ] : ["select-tickets", "enter-details"];
45350
+ switch (input.payment) {
45351
+ case "none": return base;
45352
+ case "options": return [...base, "select-payment"];
45353
+ case "optionsAndPayment": return [
45354
+ ...base,
45355
+ "select-payment",
45356
+ "payment"
45357
+ ];
45358
+ }
45359
+ }
45360
+ const REGISTRATION_STEP_LABELS = [
45361
+ "Event",
45362
+ "Date",
45363
+ "Tickets",
45364
+ "Places",
45365
+ "Your details",
45366
+ "How you’ll pay",
45367
+ "Payment"
45368
+ ];
45369
+ /** Several dates of one series are a date choice; anything else is an event choice. */
45370
+ function getEventSelectionMode(events) {
45371
+ const firstSeriesId = events[0]?.seriesId;
45372
+ return events.length > 1 && firstSeriesId && events.every((event) => event.seriesId === firstSeriesId) ? "date" : "event";
45373
+ }
45374
+ /** The label facts for a surface: what the visitor chooses from, and what they book. */
45375
+ function registrationStepLabelContext(scopedEvents, bookedEvent) {
45376
+ return {
45377
+ eventSelection: getEventSelectionMode(scopedEvents),
45378
+ booksWholeSeries: bookedEvent?.registrationSubject.kind === "wholeSeries"
45379
+ };
45380
+ }
45381
+ /** One label owner for the live progress and the server placeholder. */
45382
+ function registrationStepLabel(step, context) {
45383
+ switch (step) {
45384
+ case "select-event": return context.eventSelection === "date" ? "Date" : "Event";
45385
+ case "select-tickets": return context.booksWholeSeries ? "Places" : "Tickets";
45386
+ case "enter-details": return "Your details";
45387
+ case "select-payment": return "How you’ll pay";
45388
+ case "payment": return "Payment";
45389
+ }
45390
+ }
45391
+ function bookingButtonText(buttonText) {
45392
+ return buttonText?.trim() || "Book your place";
45393
+ }
45394
+ function placesLabel(event, input, compact) {
45395
+ return placesLeftLabel(selectEventCapacitySource(event, input.attendanceMode), input.display, { compact });
45396
+ }
45397
+ function isFull(event, mode) {
45398
+ return formatRemainingPlaces(selectEventCapacitySource(event, mode), {
45399
+ full: "full",
45400
+ waitlist: "full"
45401
+ }) === "full";
45402
+ }
45403
+ function priceLabel(event) {
45404
+ return formatEventPriceDisplay(event.pricing).trim() || null;
45405
+ }
45406
+ /** The lowest price a display can mean; null when it names no paid amount. */
45407
+ function lowestPaidAmount(display) {
45408
+ switch (display.kind) {
45409
+ case "exact":
45410
+ case "from": return display.amount;
45411
+ case "range": return display.minAmount;
45412
+ case "mixed": return display.paidFromAmount;
45413
+ case "free":
45414
+ case "unavailable":
45415
+ case "external": return null;
45416
+ }
45417
+ }
45418
+ /**
45419
+ * One price for a set of dates: the shared label when every date costs the
45420
+ * same, otherwise "From" the lowest paid amount, through the one formatter.
45421
+ */
45422
+ function seriesPriceLabel(events) {
45423
+ const firstEvent = events[0];
45424
+ if (!firstEvent) return null;
45425
+ const displays = events.map((event) => publicEventPricingDisplay(event.pricing));
45426
+ const labels = new Set(displays.map((display) => formatBookingPriceDisplay(display, { decimals: "auto" })));
45427
+ if (labels.size === 1) return [...labels][0] || null;
45428
+ const lowest = displays.flatMap((display) => {
45429
+ const amount = lowestPaidAmount(display);
45430
+ return amount === null || !("currency" in display) ? [] : [{
45431
+ amount,
45432
+ currency: display.currency
45433
+ }];
45434
+ }).reduce((best, candidate) => best === null || candidate.amount < best.amount ? candidate : best, null);
45435
+ return lowest ? formatBookingPriceDisplay({
45436
+ kind: "from",
45437
+ amount: lowest.amount,
45438
+ currency: lowest.currency
45439
+ }, { decimals: "auto" }) : priceLabel(firstEvent);
45440
+ }
45441
+ function dateTile(event) {
45442
+ if (event.registrationSubject.kind === "wholeSeries" || !event.startsAt) return null;
45443
+ const rail = formatEventCompactDateRail(event.startsAt, { timeZone: event.timeZone });
45444
+ return {
45445
+ month: rail.month,
45446
+ day: rail.day
45447
+ };
45448
+ }
45449
+ /** "Mon 5 Oct": the compact date the chips and the bar share. */
45450
+ function formatBookingShortDate(event) {
45451
+ return formatBookingWhen(event, "day");
45452
+ }
45453
+ function byStart(left, right) {
45454
+ return Date.parse(left.startsAt) - Date.parse(right.startsAt);
45455
+ }
45456
+ function singleEntry(event, input) {
45457
+ const isCourse = event.registrationSubject.kind === "wholeSeries";
45458
+ return {
45459
+ kind: "single",
45460
+ title: event.title,
45461
+ when: formatRegistrationSchedule(event, null, "long"),
45462
+ shortWhen: formatRegistrationSchedule(event),
45463
+ venue: shownVenue(event, input.display),
45464
+ price: priceLabel(event),
45465
+ places: placesLabel(event, input, false),
45466
+ tile: dateTile(event),
45467
+ action: isCourse ? `Book the ${input.noun.label}` : bookingButtonText(input.buttonText)
45468
+ };
45469
+ }
45470
+ function chooseDateEntry(input) {
45471
+ const ordered = [...input.events].sort(byStart);
45472
+ const first = ordered[0];
45473
+ const shown = ordered.slice(0, 4);
45474
+ return {
45475
+ kind: "chooseDate",
45476
+ title: first?.title ?? "",
45477
+ schedule: (first ? formatBookingScheduleSummary(first) : null) ?? (ordered.length === 1 ? "1 date" : `${ordered.length} dates`),
45478
+ venue: first ? shownVenue(first, input.display) : null,
45479
+ price: seriesPriceLabel(ordered),
45480
+ nextDate: first ? formatBookingShortDate(first) : null,
45481
+ dateCount: ordered.length,
45482
+ dates: shown.map((event) => ({
45483
+ selectionId: event.selectionId,
45484
+ label: formatBookingShortDate(event),
45485
+ places: placesLabel(event, input, true),
45486
+ full: isFull(event, input.attendanceMode)
45487
+ })),
45488
+ moreCount: Math.max(0, ordered.length - shown.length),
45489
+ action: "Choose a date"
45490
+ };
45491
+ }
45492
+ /** The card before a booking starts; the server renders this too. */
45493
+ function idleBookingEntry(input) {
45494
+ if (input.event) return singleEntry(input.event, input);
45495
+ const only = input.events.length === 1 ? input.events[0] : void 0;
45496
+ if (only) return singleEntry(only, input);
45497
+ if (getEventSelectionMode(input.events) === "date") return chooseDateEntry(input);
45498
+ return {
45499
+ kind: "chooseEvent",
45500
+ title: input.events[0]?.title ?? "",
45501
+ count: input.events.length,
45502
+ noun: input.noun.pluralLabel,
45503
+ action: "Choose an event"
45504
+ };
45505
+ }
45506
+ //#endregion
45507
+ //#region ../blocks/src/system/blocks/offerings-preview/model.ts
45508
+ const OFFERINGS_PREVIEW_DEFAULT_BUTTON_TEXT = "Book";
45509
+ const OFFERINGS_PREVIEW_DEFAULT_EMPTY_MESSAGE = "Nothing scheduled just yet.";
45510
+ const OFFERINGS_PREVIEW_EVENT_PRESET_DEFINITIONS = [
45511
+ {
45512
+ preset: "class",
45513
+ sourceKey: "eventPreset:class",
45514
+ dataKey: "classEvents",
45515
+ label: "Classes"
45516
+ },
45517
+ {
45518
+ preset: "workshop",
45519
+ sourceKey: "eventPreset:workshop",
45520
+ dataKey: "workshopEvents",
45521
+ label: "Workshops"
45522
+ },
45523
+ {
45524
+ preset: "retreat",
45525
+ sourceKey: "eventPreset:retreat",
45526
+ dataKey: "retreatEvents",
45527
+ label: "Retreats"
45528
+ },
45529
+ {
45530
+ preset: "performance",
45531
+ sourceKey: "eventPreset:performance",
45532
+ dataKey: "performanceEvents",
45533
+ label: "Performances"
45534
+ }
45535
+ ];
45536
+ const OFFERINGS_PREVIEW_EVENT_PRESETS = OFFERINGS_PREVIEW_EVENT_PRESET_DEFINITIONS.map((definition) => definition.preset);
45537
+ const OFFERINGS_PREVIEW_AUTHORABLE_SOURCE_KEYS = [...OFFERINGS_PREVIEW_EVENT_PRESET_DEFINITIONS.map((definition) => definition.sourceKey), "course"];
45538
+ const OFFERINGS_PREVIEW_SOURCE_KEYS = OFFERINGS_PREVIEW_AUTHORABLE_SOURCE_KEYS;
45539
+ Object.fromEntries(OFFERINGS_PREVIEW_EVENT_PRESET_DEFINITIONS.map((definition) => [definition.sourceKey, {
45540
+ kind: "eventPreset",
45541
+ preset: definition.preset
45542
+ }]));
45543
+ function isOfferingsPreviewSourceKey(value) {
45544
+ return typeof value === "string" && OFFERINGS_PREVIEW_SOURCE_KEYS.includes(value);
45545
+ }
45546
+ function normalizeOfferingsPreviewGroupOrder(value) {
45547
+ if (value == null) return { kind: "legacy" };
45548
+ if (value.length === 0) return { kind: "default" };
45549
+ const keys = uniqueOfferingsPreviewSourceKeys(value);
45550
+ return keys.length > 0 ? {
45551
+ kind: "custom",
45552
+ keys
45553
+ } : { kind: "default" };
45554
+ }
45555
+ function inferLegacyOfferingsPreviewGroupOrder(overrides) {
45556
+ const keys = uniqueOfferingsPreviewSourceKeys((overrides ?? []).map((override) => override.sourceKey));
45557
+ return keys.length >= 2 ? keys : [];
45558
+ }
45559
+ function normalizeOfferingsPreviewContentForWrite(value) {
45560
+ if (!isRecord$7(value)) return null;
45561
+ const groupOrderValue = value.groupOrder;
45562
+ if (Array.isArray(groupOrderValue)) {
45563
+ const normalized = normalizeOfferingsPreviewGroupOrder(groupOrderValue);
45564
+ return {
45565
+ ...value,
45566
+ groupOrder: normalized.kind === "custom" ? normalized.keys : []
45567
+ };
45568
+ }
45569
+ if (groupOrderValue != null) return {
45570
+ ...value,
45571
+ groupOrder: []
45572
+ };
45573
+ const legacyOrder = inferLegacyOfferingsPreviewGroupOrder(Array.isArray(value.groupOverrides) ? value.groupOverrides.filter(isRecord$7) : []);
45574
+ return legacyOrder.length > 0 ? {
45575
+ ...value,
45576
+ groupOrder: legacyOrder
45577
+ } : value;
45578
+ }
45579
+ /** Which offerings a site preset row names; one owner for "the course preset means whole-series runs". */
45580
+ function sourceFromPresetSummary(preset) {
45581
+ if (preset.engine === "event_series" && preset.canonicalBehaviour === "course") return { kind: "wholeSeries" };
45582
+ if (preset.engine !== "event_series") return null;
45583
+ if (isOfferingsPreviewEventPreset(preset.canonicalBehaviour)) return {
45584
+ kind: "eventPreset",
45585
+ preset: preset.canonicalBehaviour
45586
+ };
45587
+ return null;
45588
+ }
45589
+ function uniqueOfferingsPreviewSourceKeys(values) {
45590
+ const keys = [];
45591
+ for (const value of values) {
45592
+ if (!isOfferingsPreviewSourceKey(value)) continue;
45593
+ if (!keys.includes(value)) keys.push(value);
45594
+ }
45595
+ return keys;
45596
+ }
45597
+ function isRecord$7(value) {
45598
+ return typeof value === "object" && value !== null && !Array.isArray(value);
45599
+ }
45600
+ function isOfferingsPreviewEventPreset(value) {
45601
+ return typeof value === "string" && OFFERINGS_PREVIEW_EVENT_PRESETS.includes(value);
45602
+ }
45603
+ //#endregion
45604
+ //#region ../blocks/src/system/runtime/nodes/event-registration/offeringNoun.ts
45605
+ function offeringKindForEvent(event) {
45606
+ return event.registrationSubject.kind === "wholeSeries" ? "course" : event.eventPreset ?? "event";
45607
+ }
45608
+ function presetKind(preset) {
45609
+ const source = sourceFromPresetSummary(preset);
45610
+ if (!source) return null;
45611
+ return source.kind === "wholeSeries" ? "course" : source.preset;
45612
+ }
45613
+ /**
45614
+ * What the site calls this kind of offering, in lower case for use mid-sentence
45615
+ * ("Book the course", "3 workshops"). Site preset labels win; the canonical
45616
+ * name stands in when the site has none.
45617
+ */
45618
+ function offeringNounForEvent(event, presets) {
45619
+ if (!event) return lowerCased(EVENT_PRESET_VOCABULARY.event);
45620
+ const kind = offeringKindForEvent(event);
45621
+ const preset = presets?.find((candidate) => presetKind(candidate) === kind);
45622
+ return lowerCased(preset?.label.trim() && preset.pluralLabel.trim() ? preset : EVENT_PRESET_VOCABULARY[kind]);
45623
+ }
45624
+ function lowerCased(noun) {
45625
+ return {
45626
+ label: noun.label.trim().toLocaleLowerCase(),
45627
+ pluralLabel: noun.pluralLabel.trim().toLocaleLowerCase()
45628
+ };
45629
+ }
45630
+ //#endregion
44808
45631
  //#region ../blocks/src/system/runtime/nodes/event-registration/registrationEventAdapter.ts
44809
45632
  function toRegistrationEvent(event) {
44810
45633
  return {
@@ -44849,6 +45672,181 @@ function toWholeSeriesRegistrationEvents(events) {
44849
45672
  return groupPublicEventsByRun(events).map(toWholeSeriesRegistrationEvent).filter((event) => event !== null);
44850
45673
  }
44851
45674
  //#endregion
45675
+ //#region ../blocks/src/system/runtime/nodes/event-registration/bookingCardDisplay.ts
45676
+ /**
45677
+ * The booking card the server renders on CMS pages (issue 1581), and the
45678
+ * offering noun it resolves for the wizard. Kept beside the island contract
45679
+ * so the server and the client agree on one card.
45680
+ */
45681
+ const bookingHostSchema = z.enum(["overlay", "inPlace"]);
45682
+ const tileSchema = z.object({
45683
+ month: z.string(),
45684
+ day: z.string()
45685
+ }).nullable();
45686
+ const idleBookingEntrySchema = z.discriminatedUnion("kind", [
45687
+ z.object({
45688
+ kind: z.literal("single"),
45689
+ title: z.string(),
45690
+ when: z.string().nullable(),
45691
+ shortWhen: z.string().nullable(),
45692
+ venue: z.string().nullable(),
45693
+ price: z.string().nullable(),
45694
+ places: z.string().nullable(),
45695
+ tile: tileSchema,
45696
+ action: z.string()
45697
+ }),
45698
+ z.object({
45699
+ kind: z.literal("chooseDate"),
45700
+ title: z.string(),
45701
+ schedule: z.string(),
45702
+ venue: z.string().nullable(),
45703
+ price: z.string().nullable(),
45704
+ nextDate: z.string().nullable(),
45705
+ dateCount: z.number(),
45706
+ dates: z.array(z.object({
45707
+ selectionId: z.string(),
45708
+ label: z.string(),
45709
+ places: z.string().nullable(),
45710
+ full: z.boolean()
45711
+ })),
45712
+ moreCount: z.number(),
45713
+ action: z.string()
45714
+ }),
45715
+ z.object({
45716
+ kind: z.literal("chooseEvent"),
45717
+ title: z.string(),
45718
+ count: z.number(),
45719
+ noun: z.string(),
45720
+ action: z.string()
45721
+ })
45722
+ ]);
45723
+ function toScopedEventRegistrationEvents(scopedSourceEvents) {
45724
+ return [...toRegistrationEvents(scopedSourceEvents), ...toWholeSeriesRegistrationEvents(scopedSourceEvents)];
45725
+ }
45726
+ function scopedRegistrationEvents(source, scopedSourceEvents) {
45727
+ const scopedEvents = toScopedEventRegistrationEvents(scopedSourceEvents);
45728
+ return {
45729
+ scopedEvents,
45730
+ preSelected: findPreSelectedEvent({
45731
+ scopedEvents,
45732
+ occurrenceContext: source.occurrenceContext
45733
+ })
45734
+ };
45735
+ }
45736
+ function nounFor(scoped, presets) {
45737
+ return offeringNounForEvent(scoped.preSelected ?? scoped.scopedEvents[0] ?? null, presets);
45738
+ }
45739
+ /** What the site calls the offering this block books, from its preset rows. */
45740
+ function resolveOfferingNoun(source, scopedSourceEvents) {
45741
+ return nounFor(scopedRegistrationEvents(source, scopedSourceEvents), source.bookingOfferingPresets);
45742
+ }
45743
+ function idleEntry(source, scopedSourceEvents, display) {
45744
+ const scoped = scopedRegistrationEvents(source, scopedSourceEvents);
45745
+ if (scoped.scopedEvents.length === 0) return null;
45746
+ return idleBookingEntry({
45747
+ events: scoped.scopedEvents,
45748
+ event: withOccurrenceTitle(scoped.preSelected, source.occurrenceContext),
45749
+ display,
45750
+ buttonText: source.buttonText,
45751
+ noun: nounFor(scoped, source.bookingOfferingPresets),
45752
+ attendanceMode: null
45753
+ });
45754
+ }
45755
+ /** The server's card on CMS pages, or null where the shell still applies. */
45756
+ function bookingCardDisplay(source, scopedSourceEvents, display) {
45757
+ if ((source.host ?? "overlay") !== "overlay") return null;
45758
+ const entry = idleEntry(source, scopedSourceEvents, display);
45759
+ if (!entry) return null;
45760
+ return {
45761
+ kind: "event-registration",
45762
+ state: "booking-card",
45763
+ className: source.className ?? null,
45764
+ entry,
45765
+ buttonVariant: source.buttonVariant ?? null,
45766
+ buttonText: source.buttonText?.trim() ?? ""
45767
+ };
45768
+ }
45769
+ const unsupportedEventRegistrationPageSurfaceSubjectSchema = z.object({
45770
+ kind: z.literal("unsupported"),
45771
+ reason: z.enum([
45772
+ "unsupportedSubject",
45773
+ "invalidEventSeriesId",
45774
+ "invalidEventSeriesBookingScope",
45775
+ "invalidEventOfferingId",
45776
+ "invalidEventOfferingBookingScope",
45777
+ "invalidEventOfferingCatalogue"
45778
+ ])
45779
+ });
45780
+ const eventRegistrationPageSurfaceSubjectSchema = z.unknown().transform((value, ctx) => {
45781
+ const unsupported = unsupportedEventRegistrationPageSurfaceSubjectSchema.safeParse(value);
45782
+ if (unsupported.success) return unsupported.data;
45783
+ const parsed = parseEventPageSurfaceSubject(value);
45784
+ if (parsed.ok) return parsed.value;
45785
+ ctx.addIssue({
45786
+ code: z.ZodIssueCode.custom,
45787
+ message: `Invalid event page-surface subject (${parsed.error.kind})`
45788
+ });
45789
+ return z.NEVER;
45790
+ });
45791
+ function resolveEventRegistrationScope(context) {
45792
+ const occurrenceSeriesId = context.occurrenceContext?.seriesId;
45793
+ if (occurrenceSeriesId) return {
45794
+ kind: "occurrenceSeries",
45795
+ seriesId: occurrenceSeriesId
45796
+ };
45797
+ if (context.subject?.kind === "eventSeries") return {
45798
+ kind: "pageSurfaceSeries",
45799
+ seriesId: context.subject.eventSeriesId
45800
+ };
45801
+ if (context.subject?.kind === "eventOffering") return {
45802
+ kind: "pageSurfaceOffering",
45803
+ offeringId: context.subject.eventOfferingId,
45804
+ seriesIds: context.subject.catalogueEventSeriesIds
45805
+ };
45806
+ if (context.subject?.kind === "unsupported") return {
45807
+ kind: "unsupportedPageSurfaceSubject",
45808
+ reason: context.subject.reason
45809
+ };
45810
+ return { kind: "unscoped" };
45811
+ }
45812
+ function resolveEventRegistrationScopedEvents(events, context) {
45813
+ const scope = resolveEventRegistrationScope(context);
45814
+ switch (scope.kind) {
45815
+ case "occurrenceSeries":
45816
+ case "pageSurfaceSeries": return events.filter((event) => event.seriesId === scope.seriesId);
45817
+ case "pageSurfaceOffering": {
45818
+ const seriesIds = new Set(scope.seriesIds);
45819
+ return events.filter((event) => seriesIds.has(event.seriesId));
45820
+ }
45821
+ case "unsupportedPageSurfaceSubject": return [];
45822
+ case "unscoped": return [...events];
45823
+ }
45824
+ }
45825
+ //#endregion
45826
+ //#region ../blocks/src/system/runtime/nodes/shared/contract-schemas.ts
45827
+ const previewStageSchema = z.enum(["preview", "published"]);
45828
+ const registrationButtonVariantSchema = blockButtonRoleSchema;
45829
+ const eventButtonVariantSchema = blockButtonRoleSchema;
45830
+ const eventLayoutSchema = z.enum(eventCalendarListLayoutValues);
45831
+ const eventCalendarDisplayModeSchema = z.enum(eventCalendarDisplayModeValues);
45832
+ const eventCalendarDefaultViewSchema = z.enum(eventCalendarDefaultViewValues);
45833
+ const columnOptionSchema = z.enum([
45834
+ "2",
45835
+ "3",
45836
+ "4"
45837
+ ]);
45838
+ const weekStartSchema = z.enum(["sunday", "monday"]);
45839
+ const filterAutoShowSchema = z.enum([
45840
+ "auto",
45841
+ "always",
45842
+ "never"
45843
+ ]);
45844
+ const calendarTabViewsSchema = z.enum([
45845
+ "both",
45846
+ "month",
45847
+ "week"
45848
+ ]);
45849
+ //#endregion
44852
45850
  //#region ../blocks/src/system/runtime/nodes/event-registration/eventSourceRecovery.ts
44853
45851
  const { limit: _rawLimit, eventSurface: _rawEventSurface, scheduleScope: _rawScheduleScope, detailLevel: _rawDetailLevel, ...stringEventQueryShape } = publicEventLoaderQuerySchemaShape;
44854
45852
  const eventRecoveryRequestSchema = z.object({
@@ -44891,27 +45889,17 @@ function parseEventLoaderRecoveryRequest(value) {
44891
45889
  const EVENT_REGISTRATION_GENERIC_SHELL_STEP_LABELS = [
44892
45890
  "Date",
44893
45891
  "Tickets",
44894
- "Details"
44895
- ];
44896
- const EVENT_REGISTRATION_SHELL_STEP_LABELS = [
44897
- "Event",
44898
- "Date",
44899
- "Tickets",
44900
- "Details",
44901
- "Payment options",
44902
- "Payment"
45892
+ "Your details"
44903
45893
  ];
44904
45894
  const eventRegistrationPublicEventSchema = z.preprocess(normalizeEventRegistrationEventInput, publicEventSchema);
44905
- const eventRegistrationHydrationSchemaShape = {
45895
+ const eventRegistrationCommonSchemaShape = {
44906
45896
  demoEmailSuppressed: z.boolean().optional(),
44907
45897
  occurrenceContext: occurrenceContextSchema.optional(),
44908
45898
  subject: eventRegistrationPageSurfaceSubjectSchema.nullable().optional(),
44909
45899
  maxTickets: z.string().optional(),
44910
- showVenue: z.boolean().optional(),
44911
- showCapacity: z.boolean().optional(),
44912
45900
  successMessage: z.string().optional(),
44913
45901
  buttonVariant: registrationButtonVariantSchema.optional(),
44914
- overlayEnabled: z.boolean().optional(),
45902
+ host: bookingHostSchema.optional(),
44915
45903
  spamProtectionEnabled: z.boolean().optional(),
44916
45904
  supportEmail: z.string().optional(),
44917
45905
  sitePasses: publicPassProductCollectionSchema.nullable().optional(),
@@ -44936,32 +45924,51 @@ const eventRegistrationSourceSchema = z.object({
44936
45924
  portalToken: z.string().nullable().optional(),
44937
45925
  previewToken: z.string().nullable().optional(),
44938
45926
  readAuthority: browserReadAuthoritySchema.optional(),
44939
- ...eventRegistrationHydrationSchemaShape,
45927
+ ...eventRegistrationCommonSchemaShape,
45928
+ showVenue: z.boolean().optional(),
45929
+ showCapacity: z.boolean().optional(),
44940
45930
  events: z.array(eventRegistrationPublicEventSchema).nullable().optional(),
44941
45931
  wholeSeriesRunEvents: z.array(eventRegistrationPublicEventSchema).nullable().optional(),
44942
45932
  eventLoaderFailures: z.record(z.string(), z.unknown()).nullable().optional(),
44943
45933
  className: z.string().nullable().optional(),
44944
45934
  buttonText: z.string().nullable().optional(),
44945
45935
  maxTickets: z.string().nullable().optional(),
44946
- stage: previewStageSchema.optional()
45936
+ stage: previewStageSchema.optional(),
45937
+ bookingOfferingPresets: z.array(z.custom((value) => typeof value === "object" && value !== null && typeof value.label === "string")).nullable().optional()
44947
45938
  });
44948
- const eventRegistrationDisplaySchema = z.discriminatedUnion("state", [z.object({
44949
- kind: z.literal("event-registration"),
44950
- state: z.literal("generic-shell"),
44951
- className: z.string().nullable(),
44952
- titleText: z.string(),
44953
- bodyText: z.string(),
44954
- stepLabels: z.array(z.enum(EVENT_REGISTRATION_SHELL_STEP_LABELS)).min(1),
44955
- summaryButtonText: z.string()
44956
- }), z.object({
44957
- kind: z.literal("event-registration"),
44958
- state: z.literal("registration-closed"),
44959
- className: z.string().nullable(),
44960
- titleText: z.string(),
44961
- bodyText: z.string()
44962
- })]);
45939
+ const eventRegistrationDisplaySchema = z.discriminatedUnion("state", [
45940
+ z.object({
45941
+ kind: z.literal("event-registration"),
45942
+ state: z.literal("generic-shell"),
45943
+ className: z.string().nullable(),
45944
+ titleText: z.string(),
45945
+ bodyText: z.string(),
45946
+ stepLabels: z.array(z.enum(REGISTRATION_STEP_LABELS)).min(1),
45947
+ summaryButtonText: z.string()
45948
+ }),
45949
+ z.object({
45950
+ kind: z.literal("event-registration"),
45951
+ state: z.literal("registration-closed"),
45952
+ className: z.string().nullable(),
45953
+ titleText: z.string(),
45954
+ bodyText: z.string()
45955
+ }),
45956
+ z.object({
45957
+ kind: z.literal("event-registration"),
45958
+ state: z.literal("booking-card"),
45959
+ className: z.string().nullable(),
45960
+ entry: idleBookingEntrySchema,
45961
+ buttonVariant: registrationButtonVariantSchema.nullable(),
45962
+ buttonText: z.string()
45963
+ })
45964
+ ]);
44963
45965
  const eventRegistrationHydrationSchema = z.object({
44964
- ...eventRegistrationHydrationSchemaShape,
45966
+ ...eventRegistrationCommonSchemaShape,
45967
+ display: registrationDisplaySchema,
45968
+ offeringNoun: z.object({
45969
+ label: z.string(),
45970
+ pluralLabel: z.string()
45971
+ }).optional(),
44965
45972
  eventSource: eventRegistrationEventSourceSchema
44966
45973
  });
44967
45974
  const eventRegistrationRenderSchema = z.object({
@@ -44976,7 +45983,7 @@ z.object({
44976
45983
  readAuthority: browserReadAuthoritySchema.optional(),
44977
45984
  stage: previewStageSchema.optional()
44978
45985
  });
44979
- function buildEventRegistrationDisplayFromScopedEvents(source, scopedSourceEvents) {
45986
+ function buildEventRegistrationDisplayFromScopedEvents(source, scopedSourceEvents, display) {
44980
45987
  const selectedRun = resolveSelectedWholeSeriesRun(scopedSourceEvents);
44981
45988
  if (selectedRun && (!selectedRun.run.enrollmentOpen || toWholeSeriesRegistrationEvent(selectedRun) === null)) return {
44982
45989
  kind: "event-registration",
@@ -44985,6 +45992,8 @@ function buildEventRegistrationDisplayFromScopedEvents(source, scopedSourceEvent
44985
45992
  titleText: "Registration closed",
44986
45993
  bodyText: "This run is no longer open for bookings."
44987
45994
  };
45995
+ const card = bookingCardDisplay(source, scopedSourceEvents, display);
45996
+ if (card) return card;
44988
45997
  const stepLabels = buildEventRegistrationShellStepLabels(source, scopedSourceEvents);
44989
45998
  return {
44990
45999
  kind: "event-registration",
@@ -45001,11 +46010,14 @@ function buildEventRegistrationShellStepLabels(source, scopedSourceEvents) {
45001
46010
  if (selectedRun) {
45002
46011
  const selectedEvent = toWholeSeriesRegistrationEvent(selectedRun);
45003
46012
  if (!selectedEvent) return EVENT_REGISTRATION_GENERIC_SHELL_STEP_LABELS;
45004
- return getWizardStepsForContext(true, resolveEventRequiresPayment({
45005
- eligibility: null,
45006
- selectedTicketType: null,
45007
- event: selectedEvent
45008
- })).map((step) => shellLabelForRegistrationStep(step, [selectedEvent]));
46013
+ return shellStepLabels(registrationRoute({
46014
+ chooseEvent: false,
46015
+ payment: resolveEventRequiresPayment({
46016
+ eligibility: null,
46017
+ selectedTicketType: null,
46018
+ event: selectedEvent
46019
+ }) ? "optionsAndPayment" : "none"
46020
+ }), registrationStepLabelContext([selectedEvent], selectedEvent));
45009
46021
  }
45010
46022
  const scopedEvents = toScopedEventRegistrationEvents(scopedSourceEvents);
45011
46023
  if (scopedEvents.length === 0) return EVENT_REGISTRATION_GENERIC_SHELL_STEP_LABELS;
@@ -45014,14 +46026,15 @@ function buildEventRegistrationShellStepLabels(source, scopedSourceEvents) {
45014
46026
  occurrenceContext: source.occurrenceContext
45015
46027
  });
45016
46028
  const hasPreSelectedContext = Boolean(source.occurrenceContext || preSelectedEvent);
45017
- return getWizardStepsForContext(hasPreSelectedContext, hasPreSelectedContext && preSelectedEvent ? resolveEventRequiresPayment({
46029
+ const needsPaymentStep = hasPreSelectedContext && preSelectedEvent ? resolveEventRequiresPayment({
45018
46030
  eligibility: null,
45019
46031
  selectedTicketType: null,
45020
46032
  event: preSelectedEvent
45021
- }) : false).map((step) => shellLabelForRegistrationStep(step, scopedEvents));
45022
- }
45023
- function toScopedEventRegistrationEvents(scopedSourceEvents) {
45024
- return [...toRegistrationEvents(scopedSourceEvents), ...toWholeSeriesRegistrationEvents(scopedSourceEvents)];
46033
+ }) : false;
46034
+ return shellStepLabels(registrationRoute({
46035
+ chooseEvent: !hasPreSelectedContext,
46036
+ payment: needsPaymentStep ? "optionsAndPayment" : "none"
46037
+ }), registrationStepLabelContext(scopedEvents, preSelectedEvent));
45025
46038
  }
45026
46039
  function scopeEventRegistrationSourceEvents(source, eventSource) {
45027
46040
  return resolveEventRegistrationScopedEvents(eventSource.kind === "loaded" ? eventSource.events : [], {
@@ -45037,29 +46050,12 @@ function resolveSelectedWholeSeriesRun(scopedSourceEvents) {
45037
46050
  const runs = groupPublicEventsByRun(scopedSourceEvents);
45038
46051
  return runs.length === 1 ? runs[0] : null;
45039
46052
  }
45040
- function shellLabelForRegistrationStep(step, events) {
45041
- switch (step) {
45042
- case "select-event": return isDateSelection(events) ? "Date" : "Event";
45043
- case "select-tickets": return "Tickets";
45044
- case "enter-details": return "Details";
45045
- case "select-payment": return "Payment options";
45046
- case "payment": return "Payment";
45047
- case "error":
45048
- case "success":
45049
- case "submitting":
45050
- case "verifying-payment":
45051
- case "verifying-timeout":
45052
- case "payment-status":
45053
- case "payment-failed": return "Details";
45054
- }
45055
- }
45056
- function isDateSelection(events) {
45057
- const firstSeriesId = events[0]?.seriesId;
45058
- return Boolean(events.length > 1 && firstSeriesId && events.every((event) => event.seriesId === firstSeriesId));
46053
+ function shellStepLabels(route, context) {
46054
+ return route.map((step) => registrationStepLabel(step, context));
45059
46055
  }
45060
46056
  function buildEventRegistrationShellBodyText(stepLabels) {
45061
- if (stepLabels.includes("Payment options")) return "Preparing tickets, details, and payment options.";
45062
- if (stepLabels[0] === "Tickets") return "Preparing tickets and booking details.";
46057
+ if (stepLabels.includes("How you’ll pay")) return "Preparing tickets, details, and payment options.";
46058
+ if (stepLabels[0] === "Tickets" || stepLabels[0] === "Places") return "Preparing tickets and booking details.";
45063
46059
  return "Preparing dates, tickets, and booking details.";
45064
46060
  }
45065
46061
  const eventRegistrationBlockRuntime = defineClientOwnedBlockRuntime({
@@ -45069,6 +46065,7 @@ const eventRegistrationBlockRuntime = defineClientOwnedBlockRuntime({
45069
46065
  render: createZodCodec(eventRegistrationRenderSchema)
45070
46066
  },
45071
46067
  buildRender: (source) => {
46068
+ const display = resolveRegistrationDisplay(source);
45072
46069
  const eventSource = resolveEventRegistrationEventSource({
45073
46070
  events: source.events,
45074
46071
  wholeSeriesRunEvents: source.wholeSeriesRunEvents,
@@ -45076,7 +46073,7 @@ const eventRegistrationBlockRuntime = defineClientOwnedBlockRuntime({
45076
46073
  });
45077
46074
  const scopedSourceEvents = scopeEventRegistrationSourceEvents(source, eventSource);
45078
46075
  return {
45079
- display: buildEventRegistrationDisplayFromScopedEvents(source, scopedSourceEvents),
46076
+ display: buildEventRegistrationDisplayFromScopedEvents(source, scopedSourceEvents, display),
45080
46077
  hydration: {
45081
46078
  occurrenceContext: source.occurrenceContext ?? null,
45082
46079
  subject: source.subject ?? null,
@@ -45086,29 +46083,29 @@ const eventRegistrationBlockRuntime = defineClientOwnedBlockRuntime({
45086
46083
  } : eventSource,
45087
46084
  demoEmailSuppressed: source.demoEmailSuppressed,
45088
46085
  maxTickets: source.maxTickets ?? void 0,
45089
- showVenue: source.showVenue,
45090
- showCapacity: source.showCapacity,
46086
+ display,
45091
46087
  successMessage: source.successMessage,
45092
46088
  buttonVariant: source.buttonVariant,
45093
- overlayEnabled: source.overlayEnabled,
46089
+ host: source.host ?? "overlay",
45094
46090
  spamProtectionEnabled: source.spamProtectionEnabled,
45095
46091
  supportEmail: source.supportEmail,
45096
46092
  sitePasses: source.sitePasses ?? null,
45097
46093
  siteMemberships: source.siteMemberships ?? null,
45098
46094
  mailingList: source.mailingList,
45099
- siteName: source.siteName
46095
+ siteName: source.siteName,
46096
+ offeringNoun: resolveOfferingNoun(source, scopedSourceEvents)
45100
46097
  }
45101
46098
  };
45102
46099
  }
45103
46100
  });
45104
46101
  function normalizeEventRegistrationEventInput(value) {
45105
- if (!isRecord$7(value)) return value;
46102
+ if (!isRecord$6(value)) return value;
45106
46103
  return {
45107
46104
  ...value,
45108
46105
  destination: resolveRuntimeEventDestination(value)
45109
46106
  };
45110
46107
  }
45111
- function isRecord$7(value) {
46108
+ function isRecord$6(value) {
45112
46109
  return typeof value === "object" && value !== null && !Array.isArray(value);
45113
46110
  }
45114
46111
  const eventRegistrationBlockDefinition = {
@@ -45148,7 +46145,7 @@ const eventRegistrationBlockDefinition = {
45148
46145
  description: "Main heading for the registration form",
45149
46146
  required: false,
45150
46147
  multiline: false,
45151
- defaultValue: "Register for this event"
46148
+ defaultValue: ""
45152
46149
  },
45153
46150
  {
45154
46151
  id: "description",
@@ -45220,11 +46217,11 @@ const eventRegistrationBlockDefinition = {
45220
46217
  {
45221
46218
  id: "buttonText",
45222
46219
  type: "text",
45223
- label: "Submit button text",
45224
- description: "Text for the registration submit button",
46220
+ label: "Button text",
46221
+ description: "Text on the booking button",
45225
46222
  required: false,
45226
46223
  multiline: false,
45227
- defaultValue: "Complete Registration",
46224
+ defaultValue: "Book your place",
45228
46225
  maxLength: 40
45229
46226
  },
45230
46227
  createButtonVariantField()
@@ -45256,21 +46253,21 @@ const eventRegistrationBlockDefinition = {
45256
46253
  successMessage: { $bind: { from: "content.successMessage" } },
45257
46254
  buttonText: { $bind: {
45258
46255
  from: "content.buttonText",
45259
- fallback: "Complete Registration"
46256
+ fallback: "Book your place"
45260
46257
  } },
45261
46258
  buttonVariant: { $bind: {
45262
46259
  from: "content.buttonVariant",
45263
46260
  fallback: "primary"
45264
46261
  } },
45265
- overlayEnabled: { $bind: {
45266
- from: "$root.eventRegistrationOverlayEnabled",
45267
- fallback: false
45268
- } },
45269
46262
  events: { $bind: { from: "data.events" } },
45270
46263
  wholeSeriesRunEvents: { $bind: { from: "data.wholeSeriesRunEvents" } },
45271
46264
  loaderFailures: { $bind: { from: "$root.loaderFailures" } },
45272
46265
  mailingList: { $bind: { from: "$root.mailingList" } },
45273
46266
  siteName: { $bind: { from: "$root.siteName" } },
46267
+ bookingOfferingPresets: { $bind: {
46268
+ from: "$root.bookingOfferingPresets",
46269
+ fallback: []
46270
+ } },
45274
46271
  sitePasses: { $bind: { from: "data.sitePasses" } },
45275
46272
  siteMemberships: { $bind: { from: "data.siteMemberships" } },
45276
46273
  stage: { $bind: {
@@ -47931,7 +48928,21 @@ const eventCalendarBlockDefinition = {
47931
48928
  list: { label: "List" }
47932
48929
  }
47933
48930
  }, eventCalendarContentShape),
47934
- normalizeContent: normalizeEventCalendarContent,
48931
+ contentNormalizer: {
48932
+ fieldIds: [
48933
+ "variant",
48934
+ "displayMode",
48935
+ "listLayout",
48936
+ "defaultCalendarView",
48937
+ "eventSurface",
48938
+ "filterEventPresets",
48939
+ "scheduleScope",
48940
+ "filterCategoryIds",
48941
+ "filterStaffMemberId",
48942
+ "filterVenueId"
48943
+ ],
48944
+ normalize: normalizeEventCalendarContent
48945
+ },
47935
48946
  dataSchemas: {
47936
48947
  events: publicEventsArraySchema.optional(),
47937
48948
  calendarWindow: calendarWindowPageSchema.optional()
@@ -48404,90 +49415,6 @@ const recurringScheduleBlockDefinition = {
48404
49415
  } }
48405
49416
  };
48406
49417
  //#endregion
48407
- //#region ../blocks/src/system/blocks/offerings-preview/model.ts
48408
- const OFFERINGS_PREVIEW_DEFAULT_BUTTON_TEXT = "Book";
48409
- const OFFERINGS_PREVIEW_DEFAULT_EMPTY_MESSAGE = "Nothing scheduled just yet.";
48410
- const OFFERINGS_PREVIEW_EVENT_PRESET_DEFINITIONS = [
48411
- {
48412
- preset: "class",
48413
- sourceKey: "eventPreset:class",
48414
- dataKey: "classEvents",
48415
- label: "Classes"
48416
- },
48417
- {
48418
- preset: "workshop",
48419
- sourceKey: "eventPreset:workshop",
48420
- dataKey: "workshopEvents",
48421
- label: "Workshops"
48422
- },
48423
- {
48424
- preset: "retreat",
48425
- sourceKey: "eventPreset:retreat",
48426
- dataKey: "retreatEvents",
48427
- label: "Retreats"
48428
- },
48429
- {
48430
- preset: "performance",
48431
- sourceKey: "eventPreset:performance",
48432
- dataKey: "performanceEvents",
48433
- label: "Performances"
48434
- }
48435
- ];
48436
- OFFERINGS_PREVIEW_EVENT_PRESET_DEFINITIONS.map((definition) => definition.preset);
48437
- const OFFERINGS_PREVIEW_AUTHORABLE_SOURCE_KEYS = [...OFFERINGS_PREVIEW_EVENT_PRESET_DEFINITIONS.map((definition) => definition.sourceKey), "course"];
48438
- const OFFERINGS_PREVIEW_SOURCE_KEYS = OFFERINGS_PREVIEW_AUTHORABLE_SOURCE_KEYS;
48439
- Object.fromEntries(OFFERINGS_PREVIEW_EVENT_PRESET_DEFINITIONS.map((definition) => [definition.sourceKey, {
48440
- kind: "eventPreset",
48441
- preset: definition.preset
48442
- }]));
48443
- function isOfferingsPreviewSourceKey(value) {
48444
- return typeof value === "string" && OFFERINGS_PREVIEW_SOURCE_KEYS.includes(value);
48445
- }
48446
- function normalizeOfferingsPreviewGroupOrder(value) {
48447
- if (value == null) return { kind: "legacy" };
48448
- if (value.length === 0) return { kind: "default" };
48449
- const keys = uniqueOfferingsPreviewSourceKeys(value);
48450
- return keys.length > 0 ? {
48451
- kind: "custom",
48452
- keys
48453
- } : { kind: "default" };
48454
- }
48455
- function inferLegacyOfferingsPreviewGroupOrder(overrides) {
48456
- const keys = uniqueOfferingsPreviewSourceKeys((overrides ?? []).map((override) => override.sourceKey));
48457
- return keys.length >= 2 ? keys : [];
48458
- }
48459
- function normalizeOfferingsPreviewContentForWrite(value) {
48460
- if (!isRecord$6(value)) return null;
48461
- const groupOrderValue = value.groupOrder;
48462
- if (Array.isArray(groupOrderValue)) {
48463
- const normalized = normalizeOfferingsPreviewGroupOrder(groupOrderValue);
48464
- return {
48465
- ...value,
48466
- groupOrder: normalized.kind === "custom" ? normalized.keys : []
48467
- };
48468
- }
48469
- if (groupOrderValue != null) return {
48470
- ...value,
48471
- groupOrder: []
48472
- };
48473
- const legacyOrder = inferLegacyOfferingsPreviewGroupOrder(Array.isArray(value.groupOverrides) ? value.groupOverrides.filter(isRecord$6) : []);
48474
- return legacyOrder.length > 0 ? {
48475
- ...value,
48476
- groupOrder: legacyOrder
48477
- } : value;
48478
- }
48479
- function uniqueOfferingsPreviewSourceKeys(values) {
48480
- const keys = [];
48481
- for (const value of values) {
48482
- if (!isOfferingsPreviewSourceKey(value)) continue;
48483
- if (!keys.includes(value)) keys.push(value);
48484
- }
48485
- return keys;
48486
- }
48487
- function isRecord$6(value) {
48488
- return typeof value === "object" && value !== null && !Array.isArray(value);
48489
- }
48490
- //#endregion
48491
49418
  //#region ../blocks/src/system/blocks/offerings-preview/fields.ts
48492
49419
  const groupOverrideFields = [
48493
49420
  {
@@ -48851,7 +49778,22 @@ const offeringsPreviewEventDataSchemas = Object.fromEntries(OFFERINGS_PREVIEW_EV
48851
49778
  const offeringsPreviewEventDataLoaders = Object.fromEntries(OFFERINGS_PREVIEW_EVENT_PRESET_DEFINITIONS.map((definition) => [definition.dataKey, eventPresetLoader(definition.preset)]));
48852
49779
  const offeringsPreviewBlockDefinition = {
48853
49780
  manifest: offeringsPreviewManifest,
48854
- normalizeContent: normalizeOfferingsPreviewBlockContent,
49781
+ contentNormalizer: {
49782
+ fieldIds: [
49783
+ "displayMode",
49784
+ "heading",
49785
+ "description",
49786
+ "itemLimit",
49787
+ "groupOrder",
49788
+ "groupOverrides",
49789
+ "_cardStyles",
49790
+ "buttonVariant",
49791
+ "buttonText",
49792
+ "emptyMessage",
49793
+ "_sectionStyles"
49794
+ ],
49795
+ normalize: normalizeOfferingsPreviewBlockContent
49796
+ },
48855
49797
  dataSchemas: {
48856
49798
  ...offeringsPreviewEventDataSchemas,
48857
49799
  routeCandidates: publicRouteCandidatesSchema.optional(),
@@ -52449,11 +53391,11 @@ function buildNewsletterDisplay(source) {
52449
53391
  successMessage: DEFAULT_MAILING_LIST_SUCCESS_MESSAGE,
52450
53392
  privacyNote: null
52451
53393
  };
52452
- const targeting = source.targeting ?? { mode: "default" };
52453
- const choices = resolveRuntimeChoices(context, targeting);
53394
+ const assessment = assessMailingListTargeting(context, source.targeting ?? { mode: "default" });
53395
+ const choices = resolveRuntimeChoices(assessment);
52454
53396
  return {
52455
53397
  kind: "newsletter-form",
52456
- availability: runtimeTargetingIsAvailable(context, targeting, choices) ? "ready" : "unavailable",
53398
+ availability: assessment.tag === "available" ? "ready" : "unavailable",
52457
53399
  className: source.className ?? null,
52458
53400
  buttonLabel: form.buttonLabel,
52459
53401
  successMessage: form.successMessage,
@@ -52510,60 +53452,12 @@ function projectNewsletterFormHydrationTarget(source) {
52510
53452
  default: return assertNever$6(source);
52511
53453
  }
52512
53454
  }
52513
- function resolveRuntimeChoices(context, targeting) {
52514
- if (context?.tag !== "configured" || targeting.mode !== "custom" || targeting.selection.tag !== "visitor_choice" || context.provider.provider !== targeting.provider) return [];
52515
- switch (targeting.provider) {
52516
- case "native": {
52517
- const offered = new Set(targeting.selection.offeredTopicIds);
52518
- return context.provider.provider === "native" ? context.provider.topics.filter((item) => offered.has(item.id)).map((item) => ({
52519
- id: item.id,
52520
- label: item.name
52521
- })) : [];
52522
- }
52523
- case "brevo": {
52524
- const offered = new Set(targeting.selection.offeredListIds);
52525
- return context.provider.provider === "brevo" ? context.provider.lists.filter((item) => offered.has(item.id)).map((item) => ({
52526
- id: String(item.id),
52527
- label: item.name
52528
- })) : [];
52529
- }
52530
- case "mailchimp": {
52531
- const offered = new Set(targeting.selection.offeredInterestIds);
52532
- return (context.provider.provider === "mailchimp" ? context.provider.audiences.find((item) => item.id === targeting.audienceId) : void 0)?.interests.filter((item) => offered.has(item.id)).map((item) => ({
52533
- id: item.id,
52534
- label: item.name
52535
- })) ?? [];
52536
- }
52537
- }
52538
- }
52539
- function runtimeTargetingIsAvailable(context, targeting, choices) {
52540
- if (context?.tag !== "configured") return false;
52541
- const provider = context.provider;
52542
- if (targeting.mode === "default") switch (provider.provider) {
52543
- case "native": return provider.topics.some((item) => item.id === provider.defaultTopicId);
52544
- case "brevo": return provider.lists.some((item) => item.id === provider.defaultListId);
52545
- case "mailchimp": return provider.audiences.some((item) => item.id === provider.defaultAudienceId);
52546
- }
52547
- if (targeting.provider !== provider.provider) return false;
52548
- switch (targeting.provider) {
52549
- case "native": return provider.provider === "native" && nativeTargetIsAvailable(provider, targeting, choices);
52550
- case "brevo": return provider.provider === "brevo" && brevoTargetIsAvailable(provider, targeting, choices);
52551
- case "mailchimp": return provider.provider === "mailchimp" && mailchimpTargetIsAvailable(provider, targeting, choices);
52552
- }
52553
- }
52554
- function nativeTargetIsAvailable(provider, targeting, choices) {
52555
- const ids = targeting.selection.tag === "fixed" ? targeting.selection.topicIds : targeting.selection.offeredTopicIds;
52556
- return ids.every((id) => provider.topics.some((item) => item.id === id)) && (targeting.selection.tag !== "visitor_choice" || choices.length === ids.length);
52557
- }
52558
- function brevoTargetIsAvailable(provider, targeting, choices) {
52559
- const ids = targeting.selection.tag === "fixed" ? targeting.selection.listIds : targeting.selection.offeredListIds;
52560
- return ids.every((id) => provider.lists.some((item) => item.id === id)) && (targeting.selection.tag !== "visitor_choice" || choices.length === ids.length);
52561
- }
52562
- function mailchimpTargetIsAvailable(provider, targeting, choices) {
52563
- const audience = provider.audiences.find((item) => item.id === targeting.audienceId);
52564
- if (!audience) return false;
52565
- const ids = targeting.selection.tag === "fixed" ? targeting.selection.interestIds : targeting.selection.offeredInterestIds;
52566
- return ids.every((id) => audience.interests.some((item) => item.id === id)) && (targeting.selection.tag !== "visitor_choice" || choices.length === ids.length);
53455
+ function resolveRuntimeChoices(assessment) {
53456
+ if (!("value" in assessment) || assessment.value.mode !== "custom" || !("selectedChoices" in assessment.value) || assessment.value.targeting.selection.tag !== "visitor_choice") return [];
53457
+ return assessment.value.selectedChoices.map((item) => ({
53458
+ id: String(item.id),
53459
+ label: item.name
53460
+ }));
52567
53461
  }
52568
53462
  function parseRequiredMailingListSignupBlockId(value) {
52569
53463
  const parsed = parseMailingListBlockId(value);
@@ -52672,6 +53566,10 @@ function sanitizeNewsletterSignupPublicContent(content) {
52672
53566
  }
52673
53567
  const newsletterSignupBlockDefinition = {
52674
53568
  manifest: newsletterSignupManifest,
53569
+ contentNormalizer: {
53570
+ fieldIds: ["targeting"],
53571
+ normalize: normalizeMailingListBlockContent
53572
+ },
52675
53573
  runtime: newsletterFormBlockRuntime
52676
53574
  };
52677
53575
  //#endregion
@@ -53404,7 +54302,20 @@ const locationMapBlockDefinition = {
53404
54302
  })], { gap: "md" })
53405
54303
  })
53406
54304
  }),
53407
- normalizeContent: normalizeLocationMapContent
54305
+ contentNormalizer: {
54306
+ fieldIds: [
54307
+ "heading",
54308
+ "locationSource",
54309
+ "venueId",
54310
+ "location",
54311
+ "showAddressText",
54312
+ "showDirections",
54313
+ "directionsLabel",
54314
+ "zoom",
54315
+ "heightPx"
54316
+ ],
54317
+ normalize: normalizeLocationMapContent
54318
+ }
53408
54319
  };
53409
54320
  //#endregion
53410
54321
  //#region ../blocks/src/system/blocks/index.ts
@@ -54609,17 +55520,7 @@ const linksTransforms = [{
54609
55520
  id: "links.resolve",
54610
55521
  kind: "formatter",
54611
55522
  summary: "Resolve LinkValue objects to href strings using the route map",
54612
- run: (value, _options, context) => {
54613
- if (!value || typeof value !== "object") return null;
54614
- const link = value;
54615
- const kind = typeof link.kind === "string" ? link.kind : null;
54616
- if (kind === "internal") return resolveInternalLink(link, context);
54617
- if (kind === "page") return resolvePageLink(link, context);
54618
- if (kind === "entry") return resolveEntryLink(link, context);
54619
- if (kind === "external" || kind === "url") return resolveExternalLink(link);
54620
- if (!kind && typeof link.href === "string") return resolveExternalLink(link);
54621
- return null;
54622
- }
55523
+ run: (value, _options, context) => resolveAuthoredLinkHref(value, context)
54623
55524
  }, {
54624
55525
  id: "links.label",
54625
55526
  kind: "formatter",
@@ -54646,6 +55547,21 @@ const linksTransforms = [{
54646
55547
  return null;
54647
55548
  }
54648
55549
  }];
55550
+ function resolveAuthoredLinkHref(value, context = {}) {
55551
+ if (!value || typeof value !== "object") return null;
55552
+ const link = value;
55553
+ switch (link.kind) {
55554
+ case "internal": return resolveInternalLink(link, context);
55555
+ case "page": return resolvePageLink(link, context);
55556
+ case "entry": return resolveEntryLink(link, context);
55557
+ case "external":
55558
+ case "url": return resolveExternalLink(link);
55559
+ default: return resolvePreResolvedLink(link);
55560
+ }
55561
+ }
55562
+ function resolvePreResolvedLink(link) {
55563
+ return !(typeof link.kind === "string" ? link.kind : null) && typeof link.href === "string" ? resolveExternalLink(link) : null;
55564
+ }
54649
55565
  const eventCalendarSelectionParam = {
54650
55566
  id: "events.calendarSelectionParam",
54651
55567
  kind: "formatter",