@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
@@ -322,6 +322,29 @@ function canonicalEventPricingDisplay(pricing) {
322
322
  default: return assertNever$3(pricing);
323
323
  }
324
324
  }
325
+ /**
326
+ * The single renderer for a precomputed booking price `display` ADT. This is
327
+ * the one place booking price variants map to user-facing strings — every call
328
+ * site (booking entry, events calendar, event/course details, listings, portal)
329
+ * should route through here so labels never drift. Adding a variant to the ADT
330
+ * breaks compilation here (via `assertNever`) until it is handled.
331
+ */
332
+ function formatBookingPriceDisplay(display, options = {}) {
333
+ switch (display.kind) {
334
+ case "free": return "Free";
335
+ case "exact": return formatCurrencyAmount(display.amount, display.currency, options);
336
+ case "from": return `From ${formatCurrencyAmount(display.amount, display.currency, options)}`;
337
+ case "range": return `${formatCurrencyAmount(display.minAmount, display.currency, options)} - ${formatCurrencyAmount(display.maxAmount, display.currency, options)}`;
338
+ 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)}`;
339
+ case "unavailable": return "Pricing unavailable";
340
+ case "external": return display.label;
341
+ default: return assertNever$3(display);
342
+ }
343
+ }
344
+ /** Render an event's price directly from its pricing value. */
345
+ function formatEventPricing(pricing, options = {}) {
346
+ return formatBookingPriceDisplay(publicEventPricingDisplay(pricing), options);
347
+ }
325
348
  //#endregion
326
349
  //#region ../core/src/event-page-surface-subject.ts
327
350
  function createEventSeriesPageSurfaceSubject(eventSeriesId, bookingScope, eventDestinationMode) {
@@ -465,6 +488,7 @@ function parseBookingTimeZone(timeZone) {
465
488
  }
466
489
  //#endregion
467
490
  //#region ../core/src/booking-date-time.ts
491
+ const DEFAULT_LOCALE = "en-US";
468
492
  const FALLBACK_TIMEZONE = "UTC";
469
493
  const START_OF_DAY = {
470
494
  hour: 0,
@@ -587,6 +611,34 @@ function toScheduledBookingDateTime(input) {
587
611
  if (result.ok) return result.value;
588
612
  throw new Error(formatParseError(result));
589
613
  }
614
+ function formatBookingInstant(isoString, options) {
615
+ const date = new Date(isoString);
616
+ if (Number.isNaN(date.getTime())) return isoString;
617
+ return getCachedDateTimeFormatter(options.locale ?? DEFAULT_LOCALE, getSafeTimeZone(options.timeZone), options.format).format(date);
618
+ }
619
+ function formatBookingDate(input, format = {
620
+ weekday: "long",
621
+ year: "numeric",
622
+ month: "long",
623
+ day: "numeric"
624
+ }) {
625
+ return formatBookingInstant(input.startsAt, {
626
+ locale: input.locale,
627
+ timeZone: input.timeZone,
628
+ format
629
+ });
630
+ }
631
+ function formatBookingTime(input, format = {
632
+ hour: "numeric",
633
+ minute: "2-digit",
634
+ hour12: true
635
+ }) {
636
+ return formatBookingInstant(input.startsAt, {
637
+ locale: input.locale,
638
+ timeZone: input.timeZone,
639
+ format
640
+ });
641
+ }
590
642
  function getBookingLocalDateKey(input) {
591
643
  return formatDateKey(getLocalScheduleDate(new Date(input.startsAt), input.timeZone));
592
644
  }
@@ -828,6 +880,14 @@ function formError(field) {
828
880
  }
829
881
  //#endregion
830
882
  //#region ../core/src/mailing-list-targeting.ts
883
+ const DEFAULT_MAILING_LIST_TARGETING = { mode: "default" };
884
+ function mailingListTargetingChoiceIds(targeting) {
885
+ switch (targeting.provider) {
886
+ case "native": return targeting.selection.tag === "fixed" ? targeting.selection.topicIds : targeting.selection.offeredTopicIds;
887
+ case "brevo": return targeting.selection.tag === "fixed" ? targeting.selection.listIds : targeting.selection.offeredListIds;
888
+ case "mailchimp": return targeting.selection.tag === "fixed" ? targeting.selection.interestIds : targeting.selection.offeredInterestIds;
889
+ }
890
+ }
831
891
  function planMailingListChoicePresentation(targeting) {
832
892
  if (targeting.mode === "default" || targeting.selection.tag === "fixed") return { tag: "hidden" };
833
893
  switch (targeting.provider) {
@@ -867,6 +927,103 @@ function planMailingListChoicePresentation(targeting) {
867
927
  }
868
928
  }
869
929
  //#endregion
930
+ //#region ../core/src/mailing-list-targeting-availability.ts
931
+ function resolveMailingListDefaultDestination(provider) {
932
+ switch (provider.provider) {
933
+ case "native": return provider.topics.find((item) => item.id === provider.defaultTopicId) ?? null;
934
+ case "brevo": return provider.lists.find((item) => item.id === provider.defaultListId) ?? null;
935
+ case "mailchimp": return provider.audiences.find((item) => item.id === provider.defaultAudienceId) ?? null;
936
+ }
937
+ }
938
+ function assessMailingListTargeting(context, targeting) {
939
+ if (context?.tag !== "configured") return {
940
+ tag: "unavailable",
941
+ reason: "setup_missing",
942
+ targeting
943
+ };
944
+ const catalog = context.provider;
945
+ if (targeting.mode === "custom") return assessCustomTargeting(catalog, targeting);
946
+ const destination = resolveMailingListDefaultDestination(catalog);
947
+ return destination === null ? {
948
+ tag: "unavailable",
949
+ reason: "default_missing",
950
+ targeting
951
+ } : {
952
+ tag: "available",
953
+ value: {
954
+ mode: "default",
955
+ targeting,
956
+ provider: catalog.provider,
957
+ destination
958
+ }
959
+ };
960
+ }
961
+ function assessCustomTargeting(catalog, targeting) {
962
+ switch (targeting.provider) {
963
+ case "native": return catalog.provider === "native" ? assessMatchedChoices({
964
+ provider: "native",
965
+ catalog,
966
+ targeting
967
+ }, catalog.topics) : {
968
+ tag: "unavailable",
969
+ reason: "provider_mismatch",
970
+ targeting
971
+ };
972
+ case "brevo": return catalog.provider === "brevo" ? assessMatchedChoices({
973
+ provider: "brevo",
974
+ catalog,
975
+ targeting
976
+ }, catalog.lists) : {
977
+ tag: "unavailable",
978
+ reason: "provider_mismatch",
979
+ targeting
980
+ };
981
+ case "mailchimp": {
982
+ if (catalog.provider !== "mailchimp") return {
983
+ tag: "unavailable",
984
+ reason: "provider_mismatch",
985
+ targeting
986
+ };
987
+ const audience = catalog.audiences.find((item) => item.id === targeting.audienceId);
988
+ return audience === void 0 ? {
989
+ tag: "unavailable",
990
+ reason: "audience_missing",
991
+ value: {
992
+ mode: "custom",
993
+ provider: "mailchimp",
994
+ catalog,
995
+ targeting
996
+ }
997
+ } : assessMatchedChoices({
998
+ provider: "mailchimp",
999
+ catalog,
1000
+ targeting,
1001
+ audience
1002
+ }, audience.interests);
1003
+ }
1004
+ }
1005
+ }
1006
+ function assessMatchedChoices(matched, choices) {
1007
+ const selected = new Set(mailingListTargetingChoiceIds(matched.targeting));
1008
+ const available = new Set(choices.map((item) => item.id));
1009
+ const missingIds = [...selected].filter((id) => !available.has(id));
1010
+ const value = {
1011
+ mode: "custom",
1012
+ ...matched,
1013
+ choices,
1014
+ selectedChoices: choices.filter((item) => selected.has(item.id))
1015
+ };
1016
+ return missingIds.length > 0 ? {
1017
+ tag: "unavailable",
1018
+ reason: "choices_missing",
1019
+ missingIds,
1020
+ value
1021
+ } : {
1022
+ tag: "available",
1023
+ value
1024
+ };
1025
+ }
1026
+ //#endregion
870
1027
  //#region ../core/src/mailing-list-runtime.ts
871
1028
  function parseMailingListRuntimeContext(value) {
872
1029
  if (!isRecord$10(value)) return invalidRuntimeContext();
@@ -931,57 +1088,47 @@ function parseBrevoRuntimeProvider(value) {
931
1088
  function parseMailchimpRuntimeProvider(value) {
932
1089
  if (!Array.isArray(value.audiences)) return null;
933
1090
  const defaultAudienceId = parseMailchimpAudienceId(value.defaultAudienceId);
934
- if (defaultAudienceId === null) return null;
935
- const audiences = [];
936
- for (const audience of value.audiences) {
937
- if (!isRecord$10(audience)) return null;
938
- const id = parseMailchimpAudienceId(audience.id);
939
- const name = parseName(audience.name);
940
- if (id === null || name === null || !Array.isArray(audience.interests)) return null;
941
- const interests = parseMailchimpInterests(audience.interests);
942
- if (interests === null) return null;
943
- audiences.push({
944
- id,
945
- name,
946
- interests
947
- });
948
- }
949
- return {
1091
+ const audiences = Array.from(value.audiences, parseMailchimpAudience);
1092
+ return defaultAudienceId !== null && audiences.every((audience) => audience !== null) ? {
950
1093
  provider: "mailchimp",
951
1094
  defaultAudienceId,
952
1095
  audiences
953
- };
1096
+ } : null;
954
1097
  }
955
- function parseMailchimpInterests(value) {
956
- const interests = [];
957
- for (const interest of value) {
958
- if (!isRecord$10(interest)) return null;
959
- const id = parseMailchimpInterestId(interest.id);
960
- const name = parseName(interest.name);
961
- const categoryName = parseName(interest.categoryName);
962
- if (id === null || name === null || categoryName === null) return null;
963
- interests.push({
964
- id,
965
- name,
966
- categoryName
967
- });
968
- }
969
- return interests;
1098
+ function parseMailchimpAudience(value) {
1099
+ if (!isRecord$10(value) || !Array.isArray(value.interests)) return null;
1100
+ const id = parseMailchimpAudienceId(value.id);
1101
+ const name = parseName(value.name);
1102
+ const interests = Array.from(value.interests, parseMailchimpInterest);
1103
+ return id !== null && name !== null && interests.every((interest) => interest !== null) ? {
1104
+ id,
1105
+ name,
1106
+ interests
1107
+ } : null;
1108
+ }
1109
+ function parseMailchimpInterest(value) {
1110
+ if (!isRecord$10(value)) return null;
1111
+ const id = parseMailchimpInterestId(value.id);
1112
+ const name = parseName(value.name);
1113
+ const categoryName = parseName(value.categoryName);
1114
+ return id !== null && name !== null && categoryName !== null ? {
1115
+ id,
1116
+ name,
1117
+ categoryName
1118
+ } : null;
970
1119
  }
971
1120
  function parseNamedItems(value, parseId) {
972
1121
  if (!Array.isArray(value)) return null;
973
- const items = [];
974
- for (const item of value) {
1122
+ const items = Array.from(value, (item) => {
975
1123
  if (!isRecord$10(item)) return null;
976
1124
  const id = parseId(item.id);
977
1125
  const name = parseName(item.name);
978
- if (id === null || name === null) return null;
979
- items.push({
1126
+ return id !== null && name !== null ? {
980
1127
  id,
981
1128
  name
982
- });
983
- }
984
- return items;
1129
+ } : null;
1130
+ });
1131
+ return items.every((item) => item !== null) ? items : null;
985
1132
  }
986
1133
  function parseName(value) {
987
1134
  if (typeof value !== "string") return null;
@@ -997,6 +1144,7 @@ function invalidRuntimeContext() {
997
1144
  //#endregion
998
1145
  //#region ../core/src/mailing-list-targeting-parser.ts
999
1146
  function parseMailingListPlacementTargeting(value) {
1147
+ if (value == null) return ok(DEFAULT_MAILING_LIST_TARGETING);
1000
1148
  if (!isRecord$9(value)) return targetingError("shape");
1001
1149
  if (value.mode === "default") return ok({ mode: "default" });
1002
1150
  if (value.mode !== "custom") return targetingError("mode");
@@ -1095,14 +1243,11 @@ function parseNonEmptyUniqueIds(value, parseId) {
1095
1243
  }
1096
1244
  function parseUniqueIds(value, parseId) {
1097
1245
  if (!Array.isArray(value)) return targetingError("targets");
1098
- const parsed = [];
1099
- for (const item of value) {
1246
+ return Array.from(value).reduce((parsed, item) => {
1247
+ if (!parsed.ok) return parsed;
1100
1248
  const id = parseId(item);
1101
- if (id === null) return targetingError("targets");
1102
- if (parsed.includes(id)) return targetingError("duplicate_target");
1103
- parsed.push(id);
1104
- }
1105
- return ok(parsed);
1249
+ return id === null ? targetingError("targets") : parsed.value.includes(id) ? targetingError("duplicate_target") : ok([...parsed.value, id]);
1250
+ }, ok([]));
1106
1251
  }
1107
1252
  function isRecord$9(value) {
1108
1253
  return typeof value === "object" && value !== null && !Array.isArray(value);
@@ -1531,6 +1676,45 @@ function resolveSiteVerticalBookingConfig(rawConfig) {
1531
1676
  };
1532
1677
  }
1533
1678
  //#endregion
1679
+ //#region ../core/src/event-preset-vocabulary.ts
1680
+ const EVENT_PRESET_VOCABULARY = {
1681
+ event: {
1682
+ label: "Event",
1683
+ pluralLabel: "Events",
1684
+ description: "Any event, repeating or manually dated, that does not fit the other categories."
1685
+ },
1686
+ class: {
1687
+ label: "Class",
1688
+ pluralLabel: "Classes",
1689
+ description: "Regular scheduled classes and group sessions."
1690
+ },
1691
+ course: {
1692
+ label: "Course",
1693
+ pluralLabel: "Courses",
1694
+ description: "A fixed set of dates that people book together."
1695
+ },
1696
+ term: {
1697
+ label: "Term",
1698
+ pluralLabel: "Terms",
1699
+ description: "A fixed term of sessions that people book together."
1700
+ },
1701
+ workshop: {
1702
+ label: "Workshop",
1703
+ pluralLabel: "Workshops",
1704
+ description: "One-off workshops, day sessions, or special events."
1705
+ },
1706
+ retreat: {
1707
+ label: "Retreat",
1708
+ pluralLabel: "Retreats",
1709
+ description: "Multi-day or residential immersive experiences."
1710
+ },
1711
+ performance: {
1712
+ label: "Performance",
1713
+ pluralLabel: "Performances",
1714
+ description: "Audience-facing shows, concerts, recitals, or showcases."
1715
+ }
1716
+ };
1717
+ //#endregion
1534
1718
  //#region ../media-core/src/transformRect.ts
1535
1719
  function isValidTransformRectStructure(rect) {
1536
1720
  if (!Array.isArray(rect) || rect.length !== 4) return false;
@@ -12157,6 +12341,14 @@ bookingPaymentOptionSchemas.flexibleBalanceConfig;
12157
12341
  bookingPaymentOptionSchemas.paymentCollectionOption;
12158
12342
  const bookingPaymentTermsSchema = bookingPaymentOptionSchemas.paymentTerms;
12159
12343
  bookingPaymentOptionSchemas.paymentOptionsChoice;
12344
+ z.strictObject({
12345
+ currency: z.string().trim().length(3),
12346
+ totalCents: nonNegativeIntegerSchema,
12347
+ dueNowCents: nonNegativeIntegerSchema,
12348
+ laterPaymentAmountsCents: z.array(nonNegativeIntegerSchema),
12349
+ paymentOption: z.union([createBookingPaymentOptionSchemas("strict").paymentCollectionOption, z.strictObject({ kind: z.literal("not_required") })]),
12350
+ promisedDiscountId: z.string().min(1).nullable().optional()
12351
+ });
12160
12352
  //#endregion
12161
12353
  //#region ../blocks/src/system/blocks/shared/bookingPaymentTerms.ts
12162
12354
  const blockBookingPaymentTermsSchema = bookingPaymentTermsSchema;
@@ -12397,6 +12589,10 @@ const publicEventBaseSchema = z.object({
12397
12589
  eventDestinationMode: eventDestinationModeSchema.optional(),
12398
12590
  scheduleScope: eventScheduleScopeSchema$2.optional(),
12399
12591
  recurrence: publicEventRecurrenceSchema.optional(),
12592
+ scheduleSummary: z.object({
12593
+ recurrenceDescription: z.string(),
12594
+ startTime: z.string().regex(/^(?:[01]\d|2[0-3]):[0-5]\d$/).nullable()
12595
+ }).nullable().optional(),
12400
12596
  id: z.string(),
12401
12597
  occurrenceId: z.string(),
12402
12598
  seriesId: z.string(),
@@ -31978,6 +32174,15 @@ const formBlockDefinition = {
31978
32174
  runtime: formBlockRuntime
31979
32175
  };
31980
32176
  //#endregion
32177
+ //#region ../blocks/src/system/helpers/normalizeMailingListBlockContent.ts
32178
+ /** Fill the declared default without admitting or rewriting invalid custom settings. */
32179
+ function normalizeMailingListBlockContent(record) {
32180
+ return record.targeting == null ? {
32181
+ ...record,
32182
+ targeting: DEFAULT_MAILING_LIST_TARGETING
32183
+ } : record;
32184
+ }
32185
+ //#endregion
31981
32186
  //#region ../blocks/src/system/blocks/contact-newsletter.ts
31982
32187
  const contactNewsletterLayout = styledSection({
31983
32188
  children: sectionContainer([el("div", { className: "contact-newsletter-grid" }, [el("div", { className: "contact-newsletter-column contact-newsletter-contact" }, [el("div", { className: "contact-newsletter-copy" }, [text({
@@ -32141,6 +32346,10 @@ const contactNewsletterBlockDefinition = {
32141
32346
  ],
32142
32347
  icon: "MailPlus"
32143
32348
  }),
32349
+ contentNormalizer: {
32350
+ fieldIds: ["targeting"],
32351
+ normalize: normalizeMailingListBlockContent
32352
+ },
32144
32353
  dataSchemas: { form: formDataSchema.optional() },
32145
32354
  dataLoaders: { form: {
32146
32355
  endpoint: "getPublicFormById",
@@ -36619,70 +36828,274 @@ const occurrenceContextSchema = z.object({
36619
36828
  overrides: z.record(z.string(), z.unknown()).nullable().optional()
36620
36829
  }).nullable();
36621
36830
  //#endregion
36622
- //#region ../blocks/src/system/runtime/api/readAuthority.ts
36623
- const browserReadAuthoritySchema = z.discriminatedUnion("kind", [z.object({ kind: z.literal("published") }).strict(), z.object({
36624
- kind: z.literal("preview"),
36625
- token: z.string().min(1)
36626
- }).strict()]);
36627
- const unsupportedEventRegistrationPageSurfaceSubjectSchema = z.object({
36628
- kind: z.literal("unsupported"),
36629
- reason: z.enum([
36630
- "unsupportedSubject",
36631
- "invalidEventSeriesId",
36632
- "invalidEventSeriesBookingScope",
36633
- "invalidEventOfferingId",
36634
- "invalidEventOfferingBookingScope",
36635
- "invalidEventOfferingCatalogue"
36636
- ])
36637
- });
36638
- const eventRegistrationPageSurfaceSubjectSchema = z.unknown().transform((value, ctx) => {
36639
- const unsupported = unsupportedEventRegistrationPageSurfaceSubjectSchema.safeParse(value);
36640
- if (unsupported.success) return unsupported.data;
36641
- const parsed = parseEventPageSurfaceSubject(value);
36642
- if (parsed.ok) return parsed.value;
36643
- ctx.addIssue({
36644
- code: z.ZodIssueCode.custom,
36645
- message: `Invalid event page-surface subject (${parsed.error.kind})`
36646
- });
36647
- return z.NEVER;
36648
- });
36649
- function resolveEventRegistrationScope(context) {
36650
- const occurrenceSeriesId = context.occurrenceContext?.seriesId;
36651
- if (occurrenceSeriesId) return {
36652
- kind: "occurrenceSeries",
36653
- seriesId: occurrenceSeriesId
36831
+ //#region ../blocks/src/system/runtime/nodes/events/domain/capacity.ts
36832
+ /**
36833
+ * Compute remaining spots for an event from `availableSpots` with a fallback
36834
+ * to `capacity - registeredCount`. Returns `{ available: null, isFull: false }`
36835
+ * for uncapped events so call sites can render no capacity indicator at all.
36836
+ *
36837
+ * The single source of truth for capacity math across event cards, agenda
36838
+ * rows, and timetable cells — keep wording variations in the call sites.
36839
+ */
36840
+ function getEventAvailability(event) {
36841
+ if (event.capacity == null) return {
36842
+ available: null,
36843
+ isFull: false
36654
36844
  };
36655
- if (context.subject?.kind === "eventSeries") return {
36656
- kind: "pageSurfaceSeries",
36657
- seriesId: context.subject.eventSeriesId
36845
+ const available = event.availableSpots ?? event.capacity - event.registeredCount;
36846
+ if (available <= 0) return {
36847
+ available: 0,
36848
+ isFull: true
36658
36849
  };
36659
- if (context.subject?.kind === "eventOffering") return {
36660
- kind: "pageSurfaceOffering",
36661
- offeringId: context.subject.eventOfferingId,
36662
- seriesIds: context.subject.catalogueEventSeriesIds
36850
+ return {
36851
+ available,
36852
+ isFull: false
36663
36853
  };
36664
- if (context.subject?.kind === "unsupported") return {
36665
- kind: "unsupportedPageSurfaceSubject",
36666
- reason: context.subject.reason
36854
+ }
36855
+ /**
36856
+ * Pick the capacity row a booking decision should read: the selected
36857
+ * attendance mode's availability when the visitor has chosen one and the
36858
+ * event publishes it, otherwise the event-level aggregate.
36859
+ *
36860
+ * The mode row is used wholesale. A mode with `capacity: null` on a capped
36861
+ * event is uncapped *by design*: the registration write path applies the
36862
+ * same rule via `getEffectiveOccurrenceCapacityForAttendanceMode`, so falling
36863
+ * back to the event's `availableSpots` here would block bookings the server
36864
+ * accepts. (Waitlist claims are still occurrence-scoped; see issue 1377.)
36865
+ */
36866
+ function selectEventCapacitySource(event, attendanceMode) {
36867
+ if (!attendanceMode) return event;
36868
+ return (event.attendanceModes ?? []).find((mode) => mode.mode === attendanceMode) ?? event;
36869
+ }
36870
+ /**
36871
+ * Whether the visitor's next step is the waitlist: the source is full *and*
36872
+ * a waitlist is open. The one place this is decided — cards, rows, the
36873
+ * wizard's steps and its action bar all route through here so they agree,
36874
+ * including for producers that omit `availableSpots` and rely on the
36875
+ * `capacity - registeredCount` fallback.
36876
+ */
36877
+ function canJoinEventWaitlist(event, options) {
36878
+ const source = selectEventCapacitySource(event, options?.attendanceMode);
36879
+ return getEventAvailability(source).isFull && source.waitlistEnabled === true;
36880
+ }
36881
+ /**
36882
+ * Remaining places at or below which an event reads as scarce and the exact
36883
+ * count is worth showing. Above it, no number is shown at all: "147 places
36884
+ * left" is not something a visitor acts on, and it is the figure most likely
36885
+ * to be stale on a cached page.
36886
+ *
36887
+ * max( min(3, ceil(capacity * 0.25)), ceil(capacity * 0.05) )
36888
+ *
36889
+ * The 25% arm stops small groups shouting early (3 of 4 is not urgency); the
36890
+ * 5% floor stops large events staying silent until three remain.
36891
+ * capacity 4 -> 1, 12 -> 3, 40 -> 3, 100 -> 5, 200 -> 10.
36892
+ */
36893
+ function lowCapacityThreshold(capacity) {
36894
+ return Math.max(Math.min(3, Math.ceil(capacity * .25)), Math.ceil(capacity * .05));
36895
+ }
36896
+ function getRemainingPlaces(source) {
36897
+ const { available, isFull } = getEventAvailability(source);
36898
+ if (available === null || source.capacity === null) return { kind: "unlimited" };
36899
+ if (isFull) return {
36900
+ kind: "full",
36901
+ waitlist: canJoinEventWaitlist(source)
36667
36902
  };
36668
- return { kind: "unscoped" };
36903
+ if (available <= lowCapacityThreshold(source.capacity)) return {
36904
+ kind: "low",
36905
+ remaining: available
36906
+ };
36907
+ return { kind: "open" };
36669
36908
  }
36670
- function resolveEventRegistrationScopedEvents(events, context) {
36671
- const scope = resolveEventRegistrationScope(context);
36672
- switch (scope.kind) {
36673
- case "occurrenceSeries":
36674
- case "pageSurfaceSeries": return events.filter((event) => event.seriesId === scope.seriesId);
36675
- case "pageSurfaceOffering": {
36676
- const seriesIds = new Set(scope.seriesIds);
36677
- return events.filter((event) => seriesIds.has(event.seriesId));
36678
- }
36679
- case "unsupportedPageSurfaceSubject": return [];
36680
- case "unscoped": return [...events];
36909
+ /**
36910
+ * The one place a remaining-places count becomes text. Returns `null` when
36911
+ * nothing should be shown: uncapped events, and capped events above the
36912
+ * scarcity threshold. Surfaces that need their own full-state wording pass
36913
+ * it in rather than re-deriving the count.
36914
+ */
36915
+ function formatRemainingPlaces(source, wording = {}) {
36916
+ const remaining = getRemainingPlaces(source);
36917
+ switch (remaining.kind) {
36918
+ case "unlimited":
36919
+ case "open": return null;
36920
+ case "low": return formatPlacesLeft(remaining.remaining, wording.compact === true);
36921
+ case "full": return remaining.waitlist ? wording.waitlist ?? "Waitlist" : wording.full ?? "Full";
36922
+ }
36923
+ }
36924
+ function formatPlacesLeft(count, compact) {
36925
+ if (compact) return `${count} left`;
36926
+ return count === 1 ? "1 place left" : `${count} places left`;
36927
+ }
36928
+ function formatTicketTypeRemaining(remainingCapacity) {
36929
+ if (remainingCapacity === null || remainingCapacity > 3) return null;
36930
+ if (remainingCapacity <= 0) return "None left for this ticket type";
36931
+ return `${formatPlacesLeft(remainingCapacity, false)} for this ticket type`;
36932
+ }
36933
+ /**
36934
+ * Which capacity figure wins when they disagree.
36935
+ *
36936
+ * For a hybrid event that publishes both attendance-mode rows, **the mode
36937
+ * rows are authoritative and the event-level aggregate is only a summary**.
36938
+ * The registration write path admits or refuses per mode
36939
+ * (`getEffectiveOccurrenceCapacityForAttendanceMode` on the server), so the
36940
+ * aggregate can read full while a mode still has room, and can read open
36941
+ * while the mode the visitor wants is full. Believing the aggregate on a
36942
+ * card is how a "places available" event turns into a dead end at step two.
36943
+ *
36944
+ * Everything else — single-mode events, events that publish no rows, and the
36945
+ * aggregate-only helpers {@link getEventAvailability} /
36946
+ * {@link getRemainingPlaces} that existing callers rely on — keeps reading
36947
+ * the aggregate, unchanged.
36948
+ */
36949
+ function findHybridModeRows(event) {
36950
+ if (event.deliveryMode !== "hybrid") return null;
36951
+ const rows = event.attendanceModes ?? [];
36952
+ const inPerson = rows.find((row) => row.mode === "in_person");
36953
+ const online = rows.find((row) => row.mode === "online");
36954
+ return inPerson && online ? {
36955
+ inPerson,
36956
+ online
36957
+ } : null;
36958
+ }
36959
+ /** Openness rank; higher means more room. A full mode with a waitlist beats one without. */
36960
+ function remainingPlacesRank(remaining) {
36961
+ switch (remaining.kind) {
36962
+ case "unlimited": return 4;
36963
+ case "open": return 3;
36964
+ case "low": return 2;
36965
+ case "full": return remaining.waitlist ? 1 : 0;
36966
+ }
36967
+ }
36968
+ function isMoreOpen(candidate, than) {
36969
+ const rank = remainingPlacesRank(candidate);
36970
+ const otherRank = remainingPlacesRank(than);
36971
+ if (rank !== otherRank) return rank > otherRank;
36972
+ return candidate.kind === "low" && than.kind === "low" ? candidate.remaining > than.remaining : false;
36973
+ }
36974
+ /** True when `candidate` has less room left than `than`. */
36975
+ function isScarcer(candidate, than) {
36976
+ return isMoreOpen(than, candidate);
36977
+ }
36978
+ const ATTENDANCE_MODE_LABEL = {
36979
+ in_person: "In person",
36980
+ online: "Online"
36981
+ };
36982
+ /** Wording for a mode with room to spare, where the count is deliberately withheld. */
36983
+ const PLACES_AVAILABLE = "Places available";
36984
+ /**
36985
+ * The browse-surface label for an event's remaining places. Single-mode
36986
+ * events get exactly the aggregate label {@link formatRemainingPlaces}
36987
+ * produces, and so does a hybrid event whose two modes tell the same story.
36988
+ * When the modes differ, both sides are named: "available" on a card is a
36989
+ * promise the registration wizard would otherwise have to break.
36990
+ *
36991
+ * Hybrid examples, in person full with 3 online places left:
36992
+ * default -> "Online: 3 places left · In person: Full"
36993
+ * { full: 'Sold out' } -> "Online: 3 places left · In person: Sold out"
36994
+ * { compact: true } -> "Online: 3 left"
36995
+ */
36996
+ function formatEventRemainingPlaces(event, wording = {}) {
36997
+ const rows = findHybridModeRows(event);
36998
+ if (!rows) return formatRemainingPlaces(event, wording);
36999
+ const inPerson = formatRemainingPlaces(rows.inPerson, wording);
37000
+ const online = formatRemainingPlaces(rows.online, wording);
37001
+ if (inPerson === online) return inPerson;
37002
+ if (wording.compact === true) return formatCompactHybridPlaces(rows, wording);
37003
+ return formatBothHybridSides(rows, {
37004
+ inPerson,
37005
+ online
37006
+ });
37007
+ }
37008
+ /**
37009
+ * Both sides, the one with room first: that is the half a visitor can act on,
37010
+ * and it should not arrive after a full stop's worth of bad news.
37011
+ */
37012
+ function formatBothHybridSides(rows, labels) {
37013
+ 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(" · ");
37014
+ }
37015
+ function namedModePlaces(mode, label) {
37016
+ return `${ATTENDANCE_MODE_LABEL[mode]}: ${label ?? PLACES_AVAILABLE}`;
37017
+ }
37018
+ /**
37019
+ * Pills and timetable cells have room for one short phrase, so they name the
37020
+ * single mode that carries news rather than listing both: the scarce side
37021
+ * when there is a count to show, and otherwise the side that has closed —
37022
+ * "In person: Full", never "Online only", which reads as a statement about
37023
+ * how the event is delivered rather than about its remaining places.
37024
+ */
37025
+ function formatCompactHybridPlaces(rows, wording) {
37026
+ const inPerson = getRemainingPlaces(rows.inPerson);
37027
+ const online = getRemainingPlaces(rows.online);
37028
+ if (inPerson.kind === "full" && online.kind === "full") return formatRemainingPlaces(inPerson.waitlist ? rows.inPerson : rows.online, wording);
37029
+ if (inPerson.kind === "full" || online.kind === "full") {
37030
+ const fullSide = inPerson.kind === "full" ? "in_person" : "online";
37031
+ const openSide = inPerson.kind === "full" ? "online" : "in_person";
37032
+ const openLabel = formatRemainingPlaces(rowFor(rows, openSide), wording);
37033
+ return openLabel !== null ? namedModePlaces(openSide, openLabel) : namedModePlaces(fullSide, formatRemainingPlaces(rowFor(rows, fullSide), wording));
36681
37034
  }
37035
+ const scarcerSide = isScarcer(online, inPerson) ? "online" : "in_person";
37036
+ return namedModePlaces(scarcerSide, formatRemainingPlaces(rowFor(rows, scarcerSide), wording));
37037
+ }
37038
+ function rowFor(rows, mode) {
37039
+ return mode === "online" ? rows.online : rows.inPerson;
36682
37040
  }
36683
37041
  //#endregion
37042
+ //#region ../blocks/src/system/runtime/nodes/event-registration/registrationDisplay.ts
37043
+ const registrationDisplaySchema = z.object({
37044
+ venue: z.enum(["shown", "hidden"]),
37045
+ placesLeft: z.enum(["shown", "hidden"])
37046
+ });
37047
+ function resolveRegistrationDisplay(source) {
37048
+ return {
37049
+ venue: source.showVenue === false ? "hidden" : "shown",
37050
+ placesLeft: source.showCapacity === false ? "hidden" : "shown"
37051
+ };
37052
+ }
37053
+ /** Venue stays in domain data; views receive only the permitted presentation. */
37054
+ function shownVenue(event, display) {
37055
+ return display.venue === "shown" ? event.venue?.name ?? null : null;
37056
+ }
37057
+ /** Full/waitlist explanations remain visible even when optional counts are hidden. */
37058
+ function placesLeftLabel(source, display, wording = {}) {
37059
+ if ("kind" in source) return ticketPlacesLabel(source.remaining, display);
37060
+ if (source.deliveryMode === "hybrid" && source.attendanceModes?.length) return hybridPlacesLabel(source, display, wording);
37061
+ return capacityPlacesLabel(source, display, wording);
37062
+ }
37063
+ function capacityPlacesLabel(source, display, wording) {
37064
+ const remaining = getRemainingPlaces(source);
37065
+ if (display.placesLeft === "hidden" && remaining.kind !== "full") return null;
37066
+ switch (remaining.kind) {
37067
+ case "full": return formatRemainingPlaces(source, wording);
37068
+ case "low": return formatRemainingPlaces(source, wording);
37069
+ case "unlimited": return wording.unlimited ?? null;
37070
+ case "open": return wording.open ?? null;
37071
+ default: return assertNever$3(remaining);
37072
+ }
37073
+ }
37074
+ function hybridPlacesLabel(event, display, wording) {
37075
+ if (display.placesLeft === "shown") return formatEventRemainingPlaces(event, wording);
37076
+ return (event.attendanceModes ?? []).flatMap((mode) => {
37077
+ const label = placesLeftLabel(mode, display, wording);
37078
+ return label ? [`${mode.mode === "online" ? "Online" : "In person"}: ${label}`] : [];
37079
+ }).join(" · ") || null;
37080
+ }
37081
+ function ticketPlacesLabel(remaining, display) {
37082
+ if (remaining !== null && remaining > 0 && display.placesLeft === "hidden") return null;
37083
+ return formatTicketTypeRemaining(remaining);
37084
+ }
37085
+ //#endregion
37086
+ //#region ../blocks/src/system/runtime/api/readAuthority.ts
37087
+ const browserReadAuthoritySchema = z.discriminatedUnion("kind", [z.object({ kind: z.literal("published") }).strict(), z.object({
37088
+ kind: z.literal("preview"),
37089
+ token: z.string().min(1)
37090
+ }).strict()]);
37091
+ //#endregion
36684
37092
  //#region ../blocks/src/system/runtime/nodes/events/domain/dates.ts
36685
37093
  /**
37094
+ * Date and time presentation for events: the single place an event's
37095
+ * timestamps become strings or date buckets. Calendar-grid primitives are
37096
+ * re-exported from @riverbankcms/calendar so block code has one import.
37097
+ */
37098
+ /**
36686
37099
  * The English locale a site's public dates are written in, read from the
36687
37100
  * site's own time zone (Will, 2026-09-26: "date style from site time zone").
36688
37101
  *
@@ -36702,6 +37115,28 @@ function eventDateLocale(siteTimeZone) {
36702
37115
  if (siteTimeZone === "Pacific/Auckland" || siteTimeZone === "Pacific/Chatham") return "en-NZ";
36703
37116
  return "en-US";
36704
37117
  }
37118
+ function formatEventCompactDateRail(isoTimestamp, options, letterCase = "capitals") {
37119
+ const locale = options?.locale ?? "en-US";
37120
+ const dateTime = toScheduledBookingDateTime({
37121
+ startsAt: isoTimestamp,
37122
+ timeZone: toBookingTimeZone(options?.timeZone ?? "UTC"),
37123
+ locale
37124
+ });
37125
+ const cased = (value) => letterCase === "capitals" ? value.toLocaleUpperCase(locale) : value;
37126
+ const weekday = cased(formatBookingDate(dateTime, { weekday: "short" }));
37127
+ const month = cased(formatBookingDate(dateTime, { month: "short" }).slice(0, 3));
37128
+ return {
37129
+ weekday,
37130
+ day: formatBookingDate(dateTime, { day: "numeric" }),
37131
+ month,
37132
+ label: formatBookingDate(dateTime, {
37133
+ weekday: "long",
37134
+ year: "numeric",
37135
+ month: "long",
37136
+ day: "numeric"
37137
+ })
37138
+ };
37139
+ }
36705
37140
  //#endregion
36706
37141
  //#region ../blocks/src/system/runtime/nodes/events/domain/destination.ts
36707
37142
  function isRecord$6(value) {
@@ -36721,6 +37156,15 @@ function resolveRuntimeEventDestination(event) {
36721
37156
  };
36722
37157
  }
36723
37158
  //#endregion
37159
+ //#region ../blocks/src/system/runtime/nodes/events/domain/pricing.ts
37160
+ /**
37161
+ * Format an event's price directly from its pricing value (without the full
37162
+ * event). Same output as {@link formatEventPrice}.
37163
+ */
37164
+ function formatEventPriceDisplay(pricing) {
37165
+ return formatEventPricing(pricing, { decimals: "auto" });
37166
+ }
37167
+ //#endregion
36724
37168
  //#region ../blocks/src/system/runtime/nodes/events/domain/listWeek.ts
36725
37169
  /** Civil weeks use the site zone, including weeks containing a clock change. */
36726
37170
  function buildEventListWeek(anchor, startOfWeek, timeZone) {
@@ -36755,52 +37199,6 @@ function eventListWindowEvents(events, window) {
36755
37199
  });
36756
37200
  }
36757
37201
  //#endregion
36758
- //#region ../blocks/src/system/runtime/nodes/shared/contract-schemas.ts
36759
- const previewStageSchema = z.enum(["preview", "published"]);
36760
- const registrationButtonVariantSchema = blockButtonRoleSchema;
36761
- const eventButtonVariantSchema = blockButtonRoleSchema;
36762
- const eventLayoutSchema = z.enum(eventCalendarListLayoutValues);
36763
- const eventCalendarDisplayModeSchema = z.enum(eventCalendarDisplayModeValues);
36764
- const eventCalendarDefaultViewSchema = z.enum(eventCalendarDefaultViewValues);
36765
- const columnOptionSchema = z.enum([
36766
- "2",
36767
- "3",
36768
- "4"
36769
- ]);
36770
- const weekStartSchema = z.enum(["sunday", "monday"]);
36771
- const filterAutoShowSchema = z.enum([
36772
- "auto",
36773
- "always",
36774
- "never"
36775
- ]);
36776
- const calendarTabViewsSchema = z.enum([
36777
- "both",
36778
- "month",
36779
- "week"
36780
- ]);
36781
- //#endregion
36782
- //#region ../blocks/src/system/runtime/nodes/event-registration/wizardSteps.ts
36783
- /**
36784
- * Determine which wizard steps to show based on context and payment requirements.
36785
- *
36786
- * @param hasPreSelectedContext - Whether event is pre-selected (skips event selection)
36787
- * @param needsPaymentStep - Whether user must complete payment method selection
36788
- * @returns Array of wizard steps to display
36789
- */
36790
- function getWizardStepsForContext(hasPreSelectedContext, needsPaymentStep, paymentActive = false) {
36791
- const baseSteps = hasPreSelectedContext ? ["select-tickets", "enter-details"] : [
36792
- "select-event",
36793
- "select-tickets",
36794
- "enter-details"
36795
- ];
36796
- if (!needsPaymentStep) return baseSteps;
36797
- return paymentActive ? [
36798
- ...baseSteps,
36799
- "select-payment",
36800
- "payment"
36801
- ] : [...baseSteps, "select-payment"];
36802
- }
36803
- //#endregion
36804
37202
  //#region ../blocks/src/system/runtime/nodes/event-registration/registrationDerivations.ts
36805
37203
  function eventMatchesOccurrenceId(event, occurrenceId) {
36806
37204
  return event.registrationSubject.kind === "occurrence" && (event.registrationSubject.occurrenceId === occurrenceId || event.selectionId === occurrenceId);
@@ -36824,6 +37222,424 @@ function resolveEventRequiresPayment(input) {
36824
37222
  return publicEventPricingType(input.event.pricing) === "paid";
36825
37223
  }
36826
37224
  //#endregion
37225
+ //#region ../blocks/src/system/runtime/nodes/event-registration/utils.ts
37226
+ /**
37227
+ * The locale a site's booking dates are written in: the one the site's time
37228
+ * zone places it in (`eventDateLocale`, shared with event cards), or British
37229
+ * English when the site names no zone.
37230
+ */
37231
+ function bookingDateLocale(siteTimeZone) {
37232
+ return siteTimeZone ? eventDateLocale(siteTimeZone) : "en-GB";
37233
+ }
37234
+ function bookingInstant(startsAt, input) {
37235
+ return toScheduledBookingDateTime({
37236
+ startsAt,
37237
+ timeZone: toBookingTimeZone(input.timeZone),
37238
+ locale: bookingDateLocale(input.siteTimeZone)
37239
+ });
37240
+ }
37241
+ /** "7:00pm": a twelve-hour clock with a lowercase, unspaced day period. */
37242
+ function bookingClock(startsAt, input) {
37243
+ return formatBookingTime(bookingInstant(startsAt, input), {
37244
+ hour: "numeric",
37245
+ minute: "2-digit",
37246
+ hour12: true
37247
+ }).replace(/\s*([ap])\.?\s?m\.?$/i, (_match, period) => `${period.toLowerCase()}m`);
37248
+ }
37249
+ /** "7:00pm – 8:15pm", or just the start when there's no distinct end. */
37250
+ function clockRange(input) {
37251
+ const start = bookingClock(input.startsAt, input);
37252
+ const end = input.endsAt ? bookingClock(input.endsAt, input) : null;
37253
+ return end && end !== start ? `${start} – ${end}` : start;
37254
+ }
37255
+ /** A stored local clock has no date or DST offset; UTC preserves its digits. */
37256
+ function formatBookingScheduleSummary(event) {
37257
+ const summary = event.scheduleSummary;
37258
+ if (!summary) return null;
37259
+ const time = summary.startTime ? formatBookingWhen({
37260
+ startsAt: `1970-01-01T${summary.startTime}:00Z`,
37261
+ timeZone: "UTC",
37262
+ siteTimeZone: event.siteTimeZone
37263
+ }, "time") : null;
37264
+ return [summary.recurrenceDescription, time].filter(Boolean).join(" · ");
37265
+ }
37266
+ /** The one booking date formatter; every variant derives from it. */
37267
+ function formatBookingWhen(input, variant) {
37268
+ const instant = bookingInstant(input.startsAt, input);
37269
+ const day = formatBookingDate(instant, {
37270
+ weekday: "short",
37271
+ day: "numeric",
37272
+ month: "short"
37273
+ });
37274
+ switch (variant) {
37275
+ case "day": return day;
37276
+ case "dayLong": return formatBookingDate(instant, {
37277
+ weekday: "long",
37278
+ day: "numeric",
37279
+ month: "long"
37280
+ });
37281
+ case "month": return formatBookingDate(instant, { month: "long" });
37282
+ case "monthYear": return formatBookingDate(instant, {
37283
+ month: "long",
37284
+ year: "numeric"
37285
+ });
37286
+ case "time": return bookingClock(input.startsAt, input);
37287
+ case "timeRange": return clockRange(input);
37288
+ case "short": return `${day}, ${bookingClock(input.startsAt, input)}`;
37289
+ case "long": return `${day} · ${clockRange(input)}`;
37290
+ }
37291
+ }
37292
+ /** A single instant in the short form, e.g. "Mon 5 Oct, 7:00pm". */
37293
+ function formatEventDateTime(isoTimestamp, options) {
37294
+ return formatBookingWhen({
37295
+ startsAt: isoTimestamp,
37296
+ timeZone: options?.timeZone,
37297
+ siteTimeZone: options?.siteTimeZone
37298
+ }, "short");
37299
+ }
37300
+ /** The title an occurrence was given on its own, when it overrides the event's. */
37301
+ function occurrenceTitleOverride(occurrence) {
37302
+ const title = occurrence?.overrides?.title;
37303
+ return typeof title === "string" && title.trim() ? title.trim() : null;
37304
+ }
37305
+ /** The event as this page names it: an occurrence's own title wins. */
37306
+ function withOccurrenceTitle(event, occurrence) {
37307
+ const title = occurrenceTitleOverride(occurrence);
37308
+ return event && title ? {
37309
+ ...event,
37310
+ title
37311
+ } : event;
37312
+ }
37313
+ function courseSchedule(sessions, siteTimeZone) {
37314
+ const first = sessions[0];
37315
+ if (!first) return null;
37316
+ const last = sessions[sessions.length - 1] ?? first;
37317
+ const firstLabel = formatEventDateTime(first.startsAt, {
37318
+ timeZone: first.timeZone,
37319
+ siteTimeZone
37320
+ });
37321
+ if (last.startsAt === first.startsAt) return `${sessions.length} session${sessions.length === 1 ? "" : "s"} · ${firstLabel}`;
37322
+ const lastLabel = formatEventDateTime(last.startsAt, {
37323
+ timeZone: last.timeZone,
37324
+ siteTimeZone
37325
+ });
37326
+ return `${sessions.length} sessions · ${firstLabel} – ${lastLabel}`;
37327
+ }
37328
+ /**
37329
+ * When a booking happens, in one line: the occurrence's date and time, or a
37330
+ * course's session count and first and last dates. `short` by default (bar,
37331
+ * confirmation); `long` adds the end time (card, panel header).
37332
+ */
37333
+ function formatRegistrationSchedule(event, occurrence, variant = "short") {
37334
+ if (event.registrationSubject.kind === "wholeSeries") return courseSchedule(event.registrationSubject.sessions, event.siteTimeZone);
37335
+ const input = scheduleInput(event, occurrence ?? null);
37336
+ return input ? formatBookingWhen(input, variant) : null;
37337
+ }
37338
+ /** The occurrence's own times when the page names one, otherwise the event's. */
37339
+ function scheduleInput(event, occurrence) {
37340
+ const times = occurrence?.startsAt ? {
37341
+ startsAt: occurrence.startsAt,
37342
+ endsAt: occurrence.endsAt ?? null
37343
+ } : {
37344
+ startsAt: event.startsAt,
37345
+ endsAt: event.endsAt
37346
+ };
37347
+ if (!times.startsAt) return null;
37348
+ return {
37349
+ ...times,
37350
+ timeZone: occurrence?.timeZone ?? event.timeZone ?? null,
37351
+ siteTimeZone: event.siteTimeZone
37352
+ };
37353
+ }
37354
+ //#endregion
37355
+ //#region ../blocks/src/system/runtime/nodes/event-registration/wizardSteps.ts
37356
+ function registrationRoute(input) {
37357
+ const base = input.chooseEvent ? [
37358
+ "select-event",
37359
+ "select-tickets",
37360
+ "enter-details"
37361
+ ] : ["select-tickets", "enter-details"];
37362
+ switch (input.payment) {
37363
+ case "none": return base;
37364
+ case "options": return [...base, "select-payment"];
37365
+ case "optionsAndPayment": return [
37366
+ ...base,
37367
+ "select-payment",
37368
+ "payment"
37369
+ ];
37370
+ }
37371
+ }
37372
+ const REGISTRATION_STEP_LABELS = [
37373
+ "Event",
37374
+ "Date",
37375
+ "Tickets",
37376
+ "Places",
37377
+ "Your details",
37378
+ "How you’ll pay",
37379
+ "Payment"
37380
+ ];
37381
+ /** Several dates of one series are a date choice; anything else is an event choice. */
37382
+ function getEventSelectionMode(events) {
37383
+ const firstSeriesId = events[0]?.seriesId;
37384
+ return events.length > 1 && firstSeriesId && events.every((event) => event.seriesId === firstSeriesId) ? "date" : "event";
37385
+ }
37386
+ /** The label facts for a surface: what the visitor chooses from, and what they book. */
37387
+ function registrationStepLabelContext(scopedEvents, bookedEvent) {
37388
+ return {
37389
+ eventSelection: getEventSelectionMode(scopedEvents),
37390
+ booksWholeSeries: bookedEvent?.registrationSubject.kind === "wholeSeries"
37391
+ };
37392
+ }
37393
+ /** One label owner for the live progress and the server placeholder. */
37394
+ function registrationStepLabel(step, context) {
37395
+ switch (step) {
37396
+ case "select-event": return context.eventSelection === "date" ? "Date" : "Event";
37397
+ case "select-tickets": return context.booksWholeSeries ? "Places" : "Tickets";
37398
+ case "enter-details": return "Your details";
37399
+ case "select-payment": return "How you’ll pay";
37400
+ case "payment": return "Payment";
37401
+ }
37402
+ }
37403
+ function bookingButtonText(buttonText) {
37404
+ return buttonText?.trim() || "Book your place";
37405
+ }
37406
+ function placesLabel(event, input, compact) {
37407
+ return placesLeftLabel(selectEventCapacitySource(event, input.attendanceMode), input.display, { compact });
37408
+ }
37409
+ function isFull(event, mode) {
37410
+ return formatRemainingPlaces(selectEventCapacitySource(event, mode), {
37411
+ full: "full",
37412
+ waitlist: "full"
37413
+ }) === "full";
37414
+ }
37415
+ function priceLabel(event) {
37416
+ return formatEventPriceDisplay(event.pricing).trim() || null;
37417
+ }
37418
+ /** The lowest price a display can mean; null when it names no paid amount. */
37419
+ function lowestPaidAmount(display) {
37420
+ switch (display.kind) {
37421
+ case "exact":
37422
+ case "from": return display.amount;
37423
+ case "range": return display.minAmount;
37424
+ case "mixed": return display.paidFromAmount;
37425
+ case "free":
37426
+ case "unavailable":
37427
+ case "external": return null;
37428
+ }
37429
+ }
37430
+ /**
37431
+ * One price for a set of dates: the shared label when every date costs the
37432
+ * same, otherwise "From" the lowest paid amount, through the one formatter.
37433
+ */
37434
+ function seriesPriceLabel(events) {
37435
+ const firstEvent = events[0];
37436
+ if (!firstEvent) return null;
37437
+ const displays = events.map((event) => publicEventPricingDisplay(event.pricing));
37438
+ const labels = new Set(displays.map((display) => formatBookingPriceDisplay(display, { decimals: "auto" })));
37439
+ if (labels.size === 1) return [...labels][0] || null;
37440
+ const lowest = displays.flatMap((display) => {
37441
+ const amount = lowestPaidAmount(display);
37442
+ return amount === null || !("currency" in display) ? [] : [{
37443
+ amount,
37444
+ currency: display.currency
37445
+ }];
37446
+ }).reduce((best, candidate) => best === null || candidate.amount < best.amount ? candidate : best, null);
37447
+ return lowest ? formatBookingPriceDisplay({
37448
+ kind: "from",
37449
+ amount: lowest.amount,
37450
+ currency: lowest.currency
37451
+ }, { decimals: "auto" }) : priceLabel(firstEvent);
37452
+ }
37453
+ function dateTile(event) {
37454
+ if (event.registrationSubject.kind === "wholeSeries" || !event.startsAt) return null;
37455
+ const rail = formatEventCompactDateRail(event.startsAt, { timeZone: event.timeZone });
37456
+ return {
37457
+ month: rail.month,
37458
+ day: rail.day
37459
+ };
37460
+ }
37461
+ /** "Mon 5 Oct": the compact date the chips and the bar share. */
37462
+ function formatBookingShortDate(event) {
37463
+ return formatBookingWhen(event, "day");
37464
+ }
37465
+ function byStart(left, right) {
37466
+ return Date.parse(left.startsAt) - Date.parse(right.startsAt);
37467
+ }
37468
+ function singleEntry(event, input) {
37469
+ const isCourse = event.registrationSubject.kind === "wholeSeries";
37470
+ return {
37471
+ kind: "single",
37472
+ title: event.title,
37473
+ when: formatRegistrationSchedule(event, null, "long"),
37474
+ shortWhen: formatRegistrationSchedule(event),
37475
+ venue: shownVenue(event, input.display),
37476
+ price: priceLabel(event),
37477
+ places: placesLabel(event, input, false),
37478
+ tile: dateTile(event),
37479
+ action: isCourse ? `Book the ${input.noun.label}` : bookingButtonText(input.buttonText)
37480
+ };
37481
+ }
37482
+ function chooseDateEntry(input) {
37483
+ const ordered = [...input.events].sort(byStart);
37484
+ const first = ordered[0];
37485
+ const shown = ordered.slice(0, 4);
37486
+ return {
37487
+ kind: "chooseDate",
37488
+ title: first?.title ?? "",
37489
+ schedule: (first ? formatBookingScheduleSummary(first) : null) ?? (ordered.length === 1 ? "1 date" : `${ordered.length} dates`),
37490
+ venue: first ? shownVenue(first, input.display) : null,
37491
+ price: seriesPriceLabel(ordered),
37492
+ nextDate: first ? formatBookingShortDate(first) : null,
37493
+ dateCount: ordered.length,
37494
+ dates: shown.map((event) => ({
37495
+ selectionId: event.selectionId,
37496
+ label: formatBookingShortDate(event),
37497
+ places: placesLabel(event, input, true),
37498
+ full: isFull(event, input.attendanceMode)
37499
+ })),
37500
+ moreCount: Math.max(0, ordered.length - shown.length),
37501
+ action: "Choose a date"
37502
+ };
37503
+ }
37504
+ /** The card before a booking starts; the server renders this too. */
37505
+ function idleBookingEntry(input) {
37506
+ if (input.event) return singleEntry(input.event, input);
37507
+ const only = input.events.length === 1 ? input.events[0] : void 0;
37508
+ if (only) return singleEntry(only, input);
37509
+ if (getEventSelectionMode(input.events) === "date") return chooseDateEntry(input);
37510
+ return {
37511
+ kind: "chooseEvent",
37512
+ title: input.events[0]?.title ?? "",
37513
+ count: input.events.length,
37514
+ noun: input.noun.pluralLabel,
37515
+ action: "Choose an event"
37516
+ };
37517
+ }
37518
+ //#endregion
37519
+ //#region ../blocks/src/system/blocks/offerings-preview/model.ts
37520
+ const OFFERINGS_PREVIEW_DEFAULT_BUTTON_TEXT = "Book";
37521
+ const OFFERINGS_PREVIEW_DEFAULT_EMPTY_MESSAGE = "Nothing scheduled just yet.";
37522
+ const OFFERINGS_PREVIEW_EVENT_PRESET_DEFINITIONS = [
37523
+ {
37524
+ preset: "class",
37525
+ sourceKey: "eventPreset:class",
37526
+ dataKey: "classEvents",
37527
+ label: "Classes"
37528
+ },
37529
+ {
37530
+ preset: "workshop",
37531
+ sourceKey: "eventPreset:workshop",
37532
+ dataKey: "workshopEvents",
37533
+ label: "Workshops"
37534
+ },
37535
+ {
37536
+ preset: "retreat",
37537
+ sourceKey: "eventPreset:retreat",
37538
+ dataKey: "retreatEvents",
37539
+ label: "Retreats"
37540
+ },
37541
+ {
37542
+ preset: "performance",
37543
+ sourceKey: "eventPreset:performance",
37544
+ dataKey: "performanceEvents",
37545
+ label: "Performances"
37546
+ }
37547
+ ];
37548
+ const OFFERINGS_PREVIEW_EVENT_PRESETS = OFFERINGS_PREVIEW_EVENT_PRESET_DEFINITIONS.map((definition) => definition.preset);
37549
+ const OFFERINGS_PREVIEW_AUTHORABLE_SOURCE_KEYS = [...OFFERINGS_PREVIEW_EVENT_PRESET_DEFINITIONS.map((definition) => definition.sourceKey), "course"];
37550
+ const OFFERINGS_PREVIEW_SOURCE_KEYS = OFFERINGS_PREVIEW_AUTHORABLE_SOURCE_KEYS;
37551
+ Object.fromEntries(OFFERINGS_PREVIEW_EVENT_PRESET_DEFINITIONS.map((definition) => [definition.sourceKey, {
37552
+ kind: "eventPreset",
37553
+ preset: definition.preset
37554
+ }]));
37555
+ function isOfferingsPreviewSourceKey(value) {
37556
+ return typeof value === "string" && OFFERINGS_PREVIEW_SOURCE_KEYS.includes(value);
37557
+ }
37558
+ function normalizeOfferingsPreviewGroupOrder(value) {
37559
+ if (value == null) return { kind: "legacy" };
37560
+ if (value.length === 0) return { kind: "default" };
37561
+ const keys = uniqueOfferingsPreviewSourceKeys(value);
37562
+ return keys.length > 0 ? {
37563
+ kind: "custom",
37564
+ keys
37565
+ } : { kind: "default" };
37566
+ }
37567
+ function inferLegacyOfferingsPreviewGroupOrder(overrides) {
37568
+ const keys = uniqueOfferingsPreviewSourceKeys((overrides ?? []).map((override) => override.sourceKey));
37569
+ return keys.length >= 2 ? keys : [];
37570
+ }
37571
+ function normalizeOfferingsPreviewContentForWrite(value) {
37572
+ if (!isRecord$5(value)) return null;
37573
+ const groupOrderValue = value.groupOrder;
37574
+ if (Array.isArray(groupOrderValue)) {
37575
+ const normalized = normalizeOfferingsPreviewGroupOrder(groupOrderValue);
37576
+ return {
37577
+ ...value,
37578
+ groupOrder: normalized.kind === "custom" ? normalized.keys : []
37579
+ };
37580
+ }
37581
+ if (groupOrderValue != null) return {
37582
+ ...value,
37583
+ groupOrder: []
37584
+ };
37585
+ const legacyOrder = inferLegacyOfferingsPreviewGroupOrder(Array.isArray(value.groupOverrides) ? value.groupOverrides.filter(isRecord$5) : []);
37586
+ return legacyOrder.length > 0 ? {
37587
+ ...value,
37588
+ groupOrder: legacyOrder
37589
+ } : value;
37590
+ }
37591
+ /** Which offerings a site preset row names; one owner for "the course preset means whole-series runs". */
37592
+ function sourceFromPresetSummary(preset) {
37593
+ if (preset.engine === "event_series" && preset.canonicalBehaviour === "course") return { kind: "wholeSeries" };
37594
+ if (preset.engine !== "event_series") return null;
37595
+ if (isOfferingsPreviewEventPreset(preset.canonicalBehaviour)) return {
37596
+ kind: "eventPreset",
37597
+ preset: preset.canonicalBehaviour
37598
+ };
37599
+ return null;
37600
+ }
37601
+ function uniqueOfferingsPreviewSourceKeys(values) {
37602
+ const keys = [];
37603
+ for (const value of values) {
37604
+ if (!isOfferingsPreviewSourceKey(value)) continue;
37605
+ if (!keys.includes(value)) keys.push(value);
37606
+ }
37607
+ return keys;
37608
+ }
37609
+ function isRecord$5(value) {
37610
+ return typeof value === "object" && value !== null && !Array.isArray(value);
37611
+ }
37612
+ function isOfferingsPreviewEventPreset(value) {
37613
+ return typeof value === "string" && OFFERINGS_PREVIEW_EVENT_PRESETS.includes(value);
37614
+ }
37615
+ //#endregion
37616
+ //#region ../blocks/src/system/runtime/nodes/event-registration/offeringNoun.ts
37617
+ function offeringKindForEvent(event) {
37618
+ return event.registrationSubject.kind === "wholeSeries" ? "course" : event.eventPreset ?? "event";
37619
+ }
37620
+ function presetKind(preset) {
37621
+ const source = sourceFromPresetSummary(preset);
37622
+ if (!source) return null;
37623
+ return source.kind === "wholeSeries" ? "course" : source.preset;
37624
+ }
37625
+ /**
37626
+ * What the site calls this kind of offering, in lower case for use mid-sentence
37627
+ * ("Book the course", "3 workshops"). Site preset labels win; the canonical
37628
+ * name stands in when the site has none.
37629
+ */
37630
+ function offeringNounForEvent(event, presets) {
37631
+ if (!event) return lowerCased(EVENT_PRESET_VOCABULARY.event);
37632
+ const kind = offeringKindForEvent(event);
37633
+ const preset = presets?.find((candidate) => presetKind(candidate) === kind);
37634
+ return lowerCased(preset?.label.trim() && preset.pluralLabel.trim() ? preset : EVENT_PRESET_VOCABULARY[kind]);
37635
+ }
37636
+ function lowerCased(noun) {
37637
+ return {
37638
+ label: noun.label.trim().toLocaleLowerCase(),
37639
+ pluralLabel: noun.pluralLabel.trim().toLocaleLowerCase()
37640
+ };
37641
+ }
37642
+ //#endregion
36827
37643
  //#region ../blocks/src/system/runtime/nodes/event-registration/registrationEventAdapter.ts
36828
37644
  function toRegistrationEvent(event) {
36829
37645
  return {
@@ -36868,6 +37684,181 @@ function toWholeSeriesRegistrationEvents(events) {
36868
37684
  return groupPublicEventsByRun(events).map(toWholeSeriesRegistrationEvent).filter((event) => event !== null);
36869
37685
  }
36870
37686
  //#endregion
37687
+ //#region ../blocks/src/system/runtime/nodes/event-registration/bookingCardDisplay.ts
37688
+ /**
37689
+ * The booking card the server renders on CMS pages (issue 1581), and the
37690
+ * offering noun it resolves for the wizard. Kept beside the island contract
37691
+ * so the server and the client agree on one card.
37692
+ */
37693
+ const bookingHostSchema = z.enum(["overlay", "inPlace"]);
37694
+ const tileSchema = z.object({
37695
+ month: z.string(),
37696
+ day: z.string()
37697
+ }).nullable();
37698
+ const idleBookingEntrySchema = z.discriminatedUnion("kind", [
37699
+ z.object({
37700
+ kind: z.literal("single"),
37701
+ title: z.string(),
37702
+ when: z.string().nullable(),
37703
+ shortWhen: z.string().nullable(),
37704
+ venue: z.string().nullable(),
37705
+ price: z.string().nullable(),
37706
+ places: z.string().nullable(),
37707
+ tile: tileSchema,
37708
+ action: z.string()
37709
+ }),
37710
+ z.object({
37711
+ kind: z.literal("chooseDate"),
37712
+ title: z.string(),
37713
+ schedule: z.string(),
37714
+ venue: z.string().nullable(),
37715
+ price: z.string().nullable(),
37716
+ nextDate: z.string().nullable(),
37717
+ dateCount: z.number(),
37718
+ dates: z.array(z.object({
37719
+ selectionId: z.string(),
37720
+ label: z.string(),
37721
+ places: z.string().nullable(),
37722
+ full: z.boolean()
37723
+ })),
37724
+ moreCount: z.number(),
37725
+ action: z.string()
37726
+ }),
37727
+ z.object({
37728
+ kind: z.literal("chooseEvent"),
37729
+ title: z.string(),
37730
+ count: z.number(),
37731
+ noun: z.string(),
37732
+ action: z.string()
37733
+ })
37734
+ ]);
37735
+ function toScopedEventRegistrationEvents(scopedSourceEvents) {
37736
+ return [...toRegistrationEvents(scopedSourceEvents), ...toWholeSeriesRegistrationEvents(scopedSourceEvents)];
37737
+ }
37738
+ function scopedRegistrationEvents(source, scopedSourceEvents) {
37739
+ const scopedEvents = toScopedEventRegistrationEvents(scopedSourceEvents);
37740
+ return {
37741
+ scopedEvents,
37742
+ preSelected: findPreSelectedEvent({
37743
+ scopedEvents,
37744
+ occurrenceContext: source.occurrenceContext
37745
+ })
37746
+ };
37747
+ }
37748
+ function nounFor(scoped, presets) {
37749
+ return offeringNounForEvent(scoped.preSelected ?? scoped.scopedEvents[0] ?? null, presets);
37750
+ }
37751
+ /** What the site calls the offering this block books, from its preset rows. */
37752
+ function resolveOfferingNoun(source, scopedSourceEvents) {
37753
+ return nounFor(scopedRegistrationEvents(source, scopedSourceEvents), source.bookingOfferingPresets);
37754
+ }
37755
+ function idleEntry(source, scopedSourceEvents, display) {
37756
+ const scoped = scopedRegistrationEvents(source, scopedSourceEvents);
37757
+ if (scoped.scopedEvents.length === 0) return null;
37758
+ return idleBookingEntry({
37759
+ events: scoped.scopedEvents,
37760
+ event: withOccurrenceTitle(scoped.preSelected, source.occurrenceContext),
37761
+ display,
37762
+ buttonText: source.buttonText,
37763
+ noun: nounFor(scoped, source.bookingOfferingPresets),
37764
+ attendanceMode: null
37765
+ });
37766
+ }
37767
+ /** The server's card on CMS pages, or null where the shell still applies. */
37768
+ function bookingCardDisplay(source, scopedSourceEvents, display) {
37769
+ if ((source.host ?? "overlay") !== "overlay") return null;
37770
+ const entry = idleEntry(source, scopedSourceEvents, display);
37771
+ if (!entry) return null;
37772
+ return {
37773
+ kind: "event-registration",
37774
+ state: "booking-card",
37775
+ className: source.className ?? null,
37776
+ entry,
37777
+ buttonVariant: source.buttonVariant ?? null,
37778
+ buttonText: source.buttonText?.trim() ?? ""
37779
+ };
37780
+ }
37781
+ const unsupportedEventRegistrationPageSurfaceSubjectSchema = z.object({
37782
+ kind: z.literal("unsupported"),
37783
+ reason: z.enum([
37784
+ "unsupportedSubject",
37785
+ "invalidEventSeriesId",
37786
+ "invalidEventSeriesBookingScope",
37787
+ "invalidEventOfferingId",
37788
+ "invalidEventOfferingBookingScope",
37789
+ "invalidEventOfferingCatalogue"
37790
+ ])
37791
+ });
37792
+ const eventRegistrationPageSurfaceSubjectSchema = z.unknown().transform((value, ctx) => {
37793
+ const unsupported = unsupportedEventRegistrationPageSurfaceSubjectSchema.safeParse(value);
37794
+ if (unsupported.success) return unsupported.data;
37795
+ const parsed = parseEventPageSurfaceSubject(value);
37796
+ if (parsed.ok) return parsed.value;
37797
+ ctx.addIssue({
37798
+ code: z.ZodIssueCode.custom,
37799
+ message: `Invalid event page-surface subject (${parsed.error.kind})`
37800
+ });
37801
+ return z.NEVER;
37802
+ });
37803
+ function resolveEventRegistrationScope(context) {
37804
+ const occurrenceSeriesId = context.occurrenceContext?.seriesId;
37805
+ if (occurrenceSeriesId) return {
37806
+ kind: "occurrenceSeries",
37807
+ seriesId: occurrenceSeriesId
37808
+ };
37809
+ if (context.subject?.kind === "eventSeries") return {
37810
+ kind: "pageSurfaceSeries",
37811
+ seriesId: context.subject.eventSeriesId
37812
+ };
37813
+ if (context.subject?.kind === "eventOffering") return {
37814
+ kind: "pageSurfaceOffering",
37815
+ offeringId: context.subject.eventOfferingId,
37816
+ seriesIds: context.subject.catalogueEventSeriesIds
37817
+ };
37818
+ if (context.subject?.kind === "unsupported") return {
37819
+ kind: "unsupportedPageSurfaceSubject",
37820
+ reason: context.subject.reason
37821
+ };
37822
+ return { kind: "unscoped" };
37823
+ }
37824
+ function resolveEventRegistrationScopedEvents(events, context) {
37825
+ const scope = resolveEventRegistrationScope(context);
37826
+ switch (scope.kind) {
37827
+ case "occurrenceSeries":
37828
+ case "pageSurfaceSeries": return events.filter((event) => event.seriesId === scope.seriesId);
37829
+ case "pageSurfaceOffering": {
37830
+ const seriesIds = new Set(scope.seriesIds);
37831
+ return events.filter((event) => seriesIds.has(event.seriesId));
37832
+ }
37833
+ case "unsupportedPageSurfaceSubject": return [];
37834
+ case "unscoped": return [...events];
37835
+ }
37836
+ }
37837
+ //#endregion
37838
+ //#region ../blocks/src/system/runtime/nodes/shared/contract-schemas.ts
37839
+ const previewStageSchema = z.enum(["preview", "published"]);
37840
+ const registrationButtonVariantSchema = blockButtonRoleSchema;
37841
+ const eventButtonVariantSchema = blockButtonRoleSchema;
37842
+ const eventLayoutSchema = z.enum(eventCalendarListLayoutValues);
37843
+ const eventCalendarDisplayModeSchema = z.enum(eventCalendarDisplayModeValues);
37844
+ const eventCalendarDefaultViewSchema = z.enum(eventCalendarDefaultViewValues);
37845
+ const columnOptionSchema = z.enum([
37846
+ "2",
37847
+ "3",
37848
+ "4"
37849
+ ]);
37850
+ const weekStartSchema = z.enum(["sunday", "monday"]);
37851
+ const filterAutoShowSchema = z.enum([
37852
+ "auto",
37853
+ "always",
37854
+ "never"
37855
+ ]);
37856
+ const calendarTabViewsSchema = z.enum([
37857
+ "both",
37858
+ "month",
37859
+ "week"
37860
+ ]);
37861
+ //#endregion
36871
37862
  //#region ../blocks/src/system/runtime/nodes/event-registration/eventSourceRecovery.ts
36872
37863
  const { limit: _rawLimit, eventSurface: _rawEventSurface, scheduleScope: _rawScheduleScope, detailLevel: _rawDetailLevel, ...stringEventQueryShape } = publicEventLoaderQuerySchemaShape;
36873
37864
  const eventRecoveryRequestSchema = z.object({
@@ -36910,27 +37901,17 @@ function parseEventLoaderRecoveryRequest(value) {
36910
37901
  const EVENT_REGISTRATION_GENERIC_SHELL_STEP_LABELS = [
36911
37902
  "Date",
36912
37903
  "Tickets",
36913
- "Details"
36914
- ];
36915
- const EVENT_REGISTRATION_SHELL_STEP_LABELS = [
36916
- "Event",
36917
- "Date",
36918
- "Tickets",
36919
- "Details",
36920
- "Payment options",
36921
- "Payment"
37904
+ "Your details"
36922
37905
  ];
36923
37906
  const eventRegistrationPublicEventSchema = z.preprocess(normalizeEventRegistrationEventInput, publicEventSchema);
36924
- const eventRegistrationHydrationSchemaShape = {
37907
+ const eventRegistrationCommonSchemaShape = {
36925
37908
  demoEmailSuppressed: z.boolean().optional(),
36926
37909
  occurrenceContext: occurrenceContextSchema.optional(),
36927
37910
  subject: eventRegistrationPageSurfaceSubjectSchema.nullable().optional(),
36928
37911
  maxTickets: z.string().optional(),
36929
- showVenue: z.boolean().optional(),
36930
- showCapacity: z.boolean().optional(),
36931
37912
  successMessage: z.string().optional(),
36932
37913
  buttonVariant: registrationButtonVariantSchema.optional(),
36933
- overlayEnabled: z.boolean().optional(),
37914
+ host: bookingHostSchema.optional(),
36934
37915
  spamProtectionEnabled: z.boolean().optional(),
36935
37916
  supportEmail: z.string().optional(),
36936
37917
  sitePasses: publicPassProductCollectionSchema.nullable().optional(),
@@ -36955,32 +37936,51 @@ const eventRegistrationSourceSchema = z.object({
36955
37936
  portalToken: z.string().nullable().optional(),
36956
37937
  previewToken: z.string().nullable().optional(),
36957
37938
  readAuthority: browserReadAuthoritySchema.optional(),
36958
- ...eventRegistrationHydrationSchemaShape,
37939
+ ...eventRegistrationCommonSchemaShape,
37940
+ showVenue: z.boolean().optional(),
37941
+ showCapacity: z.boolean().optional(),
36959
37942
  events: z.array(eventRegistrationPublicEventSchema).nullable().optional(),
36960
37943
  wholeSeriesRunEvents: z.array(eventRegistrationPublicEventSchema).nullable().optional(),
36961
37944
  eventLoaderFailures: z.record(z.string(), z.unknown()).nullable().optional(),
36962
37945
  className: z.string().nullable().optional(),
36963
37946
  buttonText: z.string().nullable().optional(),
36964
37947
  maxTickets: z.string().nullable().optional(),
36965
- stage: previewStageSchema.optional()
37948
+ stage: previewStageSchema.optional(),
37949
+ bookingOfferingPresets: z.array(z.custom((value) => typeof value === "object" && value !== null && typeof value.label === "string")).nullable().optional()
36966
37950
  });
36967
- const eventRegistrationDisplaySchema = z.discriminatedUnion("state", [z.object({
36968
- kind: z.literal("event-registration"),
36969
- state: z.literal("generic-shell"),
36970
- className: z.string().nullable(),
36971
- titleText: z.string(),
36972
- bodyText: z.string(),
36973
- stepLabels: z.array(z.enum(EVENT_REGISTRATION_SHELL_STEP_LABELS)).min(1),
36974
- summaryButtonText: z.string()
36975
- }), z.object({
36976
- kind: z.literal("event-registration"),
36977
- state: z.literal("registration-closed"),
36978
- className: z.string().nullable(),
36979
- titleText: z.string(),
36980
- bodyText: z.string()
36981
- })]);
37951
+ const eventRegistrationDisplaySchema = z.discriminatedUnion("state", [
37952
+ z.object({
37953
+ kind: z.literal("event-registration"),
37954
+ state: z.literal("generic-shell"),
37955
+ className: z.string().nullable(),
37956
+ titleText: z.string(),
37957
+ bodyText: z.string(),
37958
+ stepLabels: z.array(z.enum(REGISTRATION_STEP_LABELS)).min(1),
37959
+ summaryButtonText: z.string()
37960
+ }),
37961
+ z.object({
37962
+ kind: z.literal("event-registration"),
37963
+ state: z.literal("registration-closed"),
37964
+ className: z.string().nullable(),
37965
+ titleText: z.string(),
37966
+ bodyText: z.string()
37967
+ }),
37968
+ z.object({
37969
+ kind: z.literal("event-registration"),
37970
+ state: z.literal("booking-card"),
37971
+ className: z.string().nullable(),
37972
+ entry: idleBookingEntrySchema,
37973
+ buttonVariant: registrationButtonVariantSchema.nullable(),
37974
+ buttonText: z.string()
37975
+ })
37976
+ ]);
36982
37977
  const eventRegistrationHydrationSchema = z.object({
36983
- ...eventRegistrationHydrationSchemaShape,
37978
+ ...eventRegistrationCommonSchemaShape,
37979
+ display: registrationDisplaySchema,
37980
+ offeringNoun: z.object({
37981
+ label: z.string(),
37982
+ pluralLabel: z.string()
37983
+ }).optional(),
36984
37984
  eventSource: eventRegistrationEventSourceSchema
36985
37985
  });
36986
37986
  const eventRegistrationRenderSchema = z.object({
@@ -36995,7 +37995,7 @@ z.object({
36995
37995
  readAuthority: browserReadAuthoritySchema.optional(),
36996
37996
  stage: previewStageSchema.optional()
36997
37997
  });
36998
- function buildEventRegistrationDisplayFromScopedEvents(source, scopedSourceEvents) {
37998
+ function buildEventRegistrationDisplayFromScopedEvents(source, scopedSourceEvents, display) {
36999
37999
  const selectedRun = resolveSelectedWholeSeriesRun(scopedSourceEvents);
37000
38000
  if (selectedRun && (!selectedRun.run.enrollmentOpen || toWholeSeriesRegistrationEvent(selectedRun) === null)) return {
37001
38001
  kind: "event-registration",
@@ -37004,6 +38004,8 @@ function buildEventRegistrationDisplayFromScopedEvents(source, scopedSourceEvent
37004
38004
  titleText: "Registration closed",
37005
38005
  bodyText: "This run is no longer open for bookings."
37006
38006
  };
38007
+ const card = bookingCardDisplay(source, scopedSourceEvents, display);
38008
+ if (card) return card;
37007
38009
  const stepLabels = buildEventRegistrationShellStepLabels(source, scopedSourceEvents);
37008
38010
  return {
37009
38011
  kind: "event-registration",
@@ -37020,11 +38022,14 @@ function buildEventRegistrationShellStepLabels(source, scopedSourceEvents) {
37020
38022
  if (selectedRun) {
37021
38023
  const selectedEvent = toWholeSeriesRegistrationEvent(selectedRun);
37022
38024
  if (!selectedEvent) return EVENT_REGISTRATION_GENERIC_SHELL_STEP_LABELS;
37023
- return getWizardStepsForContext(true, resolveEventRequiresPayment({
37024
- eligibility: null,
37025
- selectedTicketType: null,
37026
- event: selectedEvent
37027
- })).map((step) => shellLabelForRegistrationStep(step, [selectedEvent]));
38025
+ return shellStepLabels(registrationRoute({
38026
+ chooseEvent: false,
38027
+ payment: resolveEventRequiresPayment({
38028
+ eligibility: null,
38029
+ selectedTicketType: null,
38030
+ event: selectedEvent
38031
+ }) ? "optionsAndPayment" : "none"
38032
+ }), registrationStepLabelContext([selectedEvent], selectedEvent));
37028
38033
  }
37029
38034
  const scopedEvents = toScopedEventRegistrationEvents(scopedSourceEvents);
37030
38035
  if (scopedEvents.length === 0) return EVENT_REGISTRATION_GENERIC_SHELL_STEP_LABELS;
@@ -37033,14 +38038,15 @@ function buildEventRegistrationShellStepLabels(source, scopedSourceEvents) {
37033
38038
  occurrenceContext: source.occurrenceContext
37034
38039
  });
37035
38040
  const hasPreSelectedContext = Boolean(source.occurrenceContext || preSelectedEvent);
37036
- return getWizardStepsForContext(hasPreSelectedContext, hasPreSelectedContext && preSelectedEvent ? resolveEventRequiresPayment({
38041
+ const needsPaymentStep = hasPreSelectedContext && preSelectedEvent ? resolveEventRequiresPayment({
37037
38042
  eligibility: null,
37038
38043
  selectedTicketType: null,
37039
38044
  event: preSelectedEvent
37040
- }) : false).map((step) => shellLabelForRegistrationStep(step, scopedEvents));
37041
- }
37042
- function toScopedEventRegistrationEvents(scopedSourceEvents) {
37043
- return [...toRegistrationEvents(scopedSourceEvents), ...toWholeSeriesRegistrationEvents(scopedSourceEvents)];
38045
+ }) : false;
38046
+ return shellStepLabels(registrationRoute({
38047
+ chooseEvent: !hasPreSelectedContext,
38048
+ payment: needsPaymentStep ? "optionsAndPayment" : "none"
38049
+ }), registrationStepLabelContext(scopedEvents, preSelectedEvent));
37044
38050
  }
37045
38051
  function scopeEventRegistrationSourceEvents(source, eventSource) {
37046
38052
  return resolveEventRegistrationScopedEvents(eventSource.kind === "loaded" ? eventSource.events : [], {
@@ -37056,29 +38062,12 @@ function resolveSelectedWholeSeriesRun(scopedSourceEvents) {
37056
38062
  const runs = groupPublicEventsByRun(scopedSourceEvents);
37057
38063
  return runs.length === 1 ? runs[0] : null;
37058
38064
  }
37059
- function shellLabelForRegistrationStep(step, events) {
37060
- switch (step) {
37061
- case "select-event": return isDateSelection(events) ? "Date" : "Event";
37062
- case "select-tickets": return "Tickets";
37063
- case "enter-details": return "Details";
37064
- case "select-payment": return "Payment options";
37065
- case "payment": return "Payment";
37066
- case "error":
37067
- case "success":
37068
- case "submitting":
37069
- case "verifying-payment":
37070
- case "verifying-timeout":
37071
- case "payment-status":
37072
- case "payment-failed": return "Details";
37073
- }
37074
- }
37075
- function isDateSelection(events) {
37076
- const firstSeriesId = events[0]?.seriesId;
37077
- return Boolean(events.length > 1 && firstSeriesId && events.every((event) => event.seriesId === firstSeriesId));
38065
+ function shellStepLabels(route, context) {
38066
+ return route.map((step) => registrationStepLabel(step, context));
37078
38067
  }
37079
38068
  function buildEventRegistrationShellBodyText(stepLabels) {
37080
- if (stepLabels.includes("Payment options")) return "Preparing tickets, details, and payment options.";
37081
- if (stepLabels[0] === "Tickets") return "Preparing tickets and booking details.";
38069
+ if (stepLabels.includes("How you’ll pay")) return "Preparing tickets, details, and payment options.";
38070
+ if (stepLabels[0] === "Tickets" || stepLabels[0] === "Places") return "Preparing tickets and booking details.";
37082
38071
  return "Preparing dates, tickets, and booking details.";
37083
38072
  }
37084
38073
  const eventRegistrationBlockRuntime = defineClientOwnedBlockRuntime({
@@ -37088,6 +38077,7 @@ const eventRegistrationBlockRuntime = defineClientOwnedBlockRuntime({
37088
38077
  render: createZodCodec(eventRegistrationRenderSchema)
37089
38078
  },
37090
38079
  buildRender: (source) => {
38080
+ const display = resolveRegistrationDisplay(source);
37091
38081
  const eventSource = resolveEventRegistrationEventSource({
37092
38082
  events: source.events,
37093
38083
  wholeSeriesRunEvents: source.wholeSeriesRunEvents,
@@ -37095,7 +38085,7 @@ const eventRegistrationBlockRuntime = defineClientOwnedBlockRuntime({
37095
38085
  });
37096
38086
  const scopedSourceEvents = scopeEventRegistrationSourceEvents(source, eventSource);
37097
38087
  return {
37098
- display: buildEventRegistrationDisplayFromScopedEvents(source, scopedSourceEvents),
38088
+ display: buildEventRegistrationDisplayFromScopedEvents(source, scopedSourceEvents, display),
37099
38089
  hydration: {
37100
38090
  occurrenceContext: source.occurrenceContext ?? null,
37101
38091
  subject: source.subject ?? null,
@@ -37105,29 +38095,29 @@ const eventRegistrationBlockRuntime = defineClientOwnedBlockRuntime({
37105
38095
  } : eventSource,
37106
38096
  demoEmailSuppressed: source.demoEmailSuppressed,
37107
38097
  maxTickets: source.maxTickets ?? void 0,
37108
- showVenue: source.showVenue,
37109
- showCapacity: source.showCapacity,
38098
+ display,
37110
38099
  successMessage: source.successMessage,
37111
38100
  buttonVariant: source.buttonVariant,
37112
- overlayEnabled: source.overlayEnabled,
38101
+ host: source.host ?? "overlay",
37113
38102
  spamProtectionEnabled: source.spamProtectionEnabled,
37114
38103
  supportEmail: source.supportEmail,
37115
38104
  sitePasses: source.sitePasses ?? null,
37116
38105
  siteMemberships: source.siteMemberships ?? null,
37117
38106
  mailingList: source.mailingList,
37118
- siteName: source.siteName
38107
+ siteName: source.siteName,
38108
+ offeringNoun: resolveOfferingNoun(source, scopedSourceEvents)
37119
38109
  }
37120
38110
  };
37121
38111
  }
37122
38112
  });
37123
38113
  function normalizeEventRegistrationEventInput(value) {
37124
- if (!isRecord$5(value)) return value;
38114
+ if (!isRecord$4(value)) return value;
37125
38115
  return {
37126
38116
  ...value,
37127
38117
  destination: resolveRuntimeEventDestination(value)
37128
38118
  };
37129
38119
  }
37130
- function isRecord$5(value) {
38120
+ function isRecord$4(value) {
37131
38121
  return typeof value === "object" && value !== null && !Array.isArray(value);
37132
38122
  }
37133
38123
  const eventRegistrationBlockDefinition = {
@@ -37167,7 +38157,7 @@ const eventRegistrationBlockDefinition = {
37167
38157
  description: "Main heading for the registration form",
37168
38158
  required: false,
37169
38159
  multiline: false,
37170
- defaultValue: "Register for this event"
38160
+ defaultValue: ""
37171
38161
  },
37172
38162
  {
37173
38163
  id: "description",
@@ -37239,11 +38229,11 @@ const eventRegistrationBlockDefinition = {
37239
38229
  {
37240
38230
  id: "buttonText",
37241
38231
  type: "text",
37242
- label: "Submit button text",
37243
- description: "Text for the registration submit button",
38232
+ label: "Button text",
38233
+ description: "Text on the booking button",
37244
38234
  required: false,
37245
38235
  multiline: false,
37246
- defaultValue: "Complete Registration",
38236
+ defaultValue: "Book your place",
37247
38237
  maxLength: 40
37248
38238
  },
37249
38239
  createButtonVariantField()
@@ -37275,21 +38265,21 @@ const eventRegistrationBlockDefinition = {
37275
38265
  successMessage: { $bind: { from: "content.successMessage" } },
37276
38266
  buttonText: { $bind: {
37277
38267
  from: "content.buttonText",
37278
- fallback: "Complete Registration"
38268
+ fallback: "Book your place"
37279
38269
  } },
37280
38270
  buttonVariant: { $bind: {
37281
38271
  from: "content.buttonVariant",
37282
38272
  fallback: "primary"
37283
38273
  } },
37284
- overlayEnabled: { $bind: {
37285
- from: "$root.eventRegistrationOverlayEnabled",
37286
- fallback: false
37287
- } },
37288
38274
  events: { $bind: { from: "data.events" } },
37289
38275
  wholeSeriesRunEvents: { $bind: { from: "data.wholeSeriesRunEvents" } },
37290
38276
  loaderFailures: { $bind: { from: "$root.loaderFailures" } },
37291
38277
  mailingList: { $bind: { from: "$root.mailingList" } },
37292
38278
  siteName: { $bind: { from: "$root.siteName" } },
38279
+ bookingOfferingPresets: { $bind: {
38280
+ from: "$root.bookingOfferingPresets",
38281
+ fallback: []
38282
+ } },
37293
38283
  sitePasses: { $bind: { from: "data.sitePasses" } },
37294
38284
  siteMemberships: { $bind: { from: "data.siteMemberships" } },
37295
38285
  stage: { $bind: {
@@ -39859,7 +40849,21 @@ const eventCalendarBlockDefinition = {
39859
40849
  list: { label: "List" }
39860
40850
  }
39861
40851
  }, eventCalendarContentShape),
39862
- normalizeContent: normalizeEventCalendarContent,
40852
+ contentNormalizer: {
40853
+ fieldIds: [
40854
+ "variant",
40855
+ "displayMode",
40856
+ "listLayout",
40857
+ "defaultCalendarView",
40858
+ "eventSurface",
40859
+ "filterEventPresets",
40860
+ "scheduleScope",
40861
+ "filterCategoryIds",
40862
+ "filterStaffMemberId",
40863
+ "filterVenueId"
40864
+ ],
40865
+ normalize: normalizeEventCalendarContent
40866
+ },
39863
40867
  dataSchemas: {
39864
40868
  events: publicEventsArraySchema.optional(),
39865
40869
  calendarWindow: calendarWindowPageSchema.optional()
@@ -40332,90 +41336,6 @@ const recurringScheduleBlockDefinition = {
40332
41336
  } }
40333
41337
  };
40334
41338
  //#endregion
40335
- //#region ../blocks/src/system/blocks/offerings-preview/model.ts
40336
- const OFFERINGS_PREVIEW_DEFAULT_BUTTON_TEXT = "Book";
40337
- const OFFERINGS_PREVIEW_DEFAULT_EMPTY_MESSAGE = "Nothing scheduled just yet.";
40338
- const OFFERINGS_PREVIEW_EVENT_PRESET_DEFINITIONS = [
40339
- {
40340
- preset: "class",
40341
- sourceKey: "eventPreset:class",
40342
- dataKey: "classEvents",
40343
- label: "Classes"
40344
- },
40345
- {
40346
- preset: "workshop",
40347
- sourceKey: "eventPreset:workshop",
40348
- dataKey: "workshopEvents",
40349
- label: "Workshops"
40350
- },
40351
- {
40352
- preset: "retreat",
40353
- sourceKey: "eventPreset:retreat",
40354
- dataKey: "retreatEvents",
40355
- label: "Retreats"
40356
- },
40357
- {
40358
- preset: "performance",
40359
- sourceKey: "eventPreset:performance",
40360
- dataKey: "performanceEvents",
40361
- label: "Performances"
40362
- }
40363
- ];
40364
- OFFERINGS_PREVIEW_EVENT_PRESET_DEFINITIONS.map((definition) => definition.preset);
40365
- const OFFERINGS_PREVIEW_AUTHORABLE_SOURCE_KEYS = [...OFFERINGS_PREVIEW_EVENT_PRESET_DEFINITIONS.map((definition) => definition.sourceKey), "course"];
40366
- const OFFERINGS_PREVIEW_SOURCE_KEYS = OFFERINGS_PREVIEW_AUTHORABLE_SOURCE_KEYS;
40367
- Object.fromEntries(OFFERINGS_PREVIEW_EVENT_PRESET_DEFINITIONS.map((definition) => [definition.sourceKey, {
40368
- kind: "eventPreset",
40369
- preset: definition.preset
40370
- }]));
40371
- function isOfferingsPreviewSourceKey(value) {
40372
- return typeof value === "string" && OFFERINGS_PREVIEW_SOURCE_KEYS.includes(value);
40373
- }
40374
- function normalizeOfferingsPreviewGroupOrder(value) {
40375
- if (value == null) return { kind: "legacy" };
40376
- if (value.length === 0) return { kind: "default" };
40377
- const keys = uniqueOfferingsPreviewSourceKeys(value);
40378
- return keys.length > 0 ? {
40379
- kind: "custom",
40380
- keys
40381
- } : { kind: "default" };
40382
- }
40383
- function inferLegacyOfferingsPreviewGroupOrder(overrides) {
40384
- const keys = uniqueOfferingsPreviewSourceKeys((overrides ?? []).map((override) => override.sourceKey));
40385
- return keys.length >= 2 ? keys : [];
40386
- }
40387
- function normalizeOfferingsPreviewContentForWrite(value) {
40388
- if (!isRecord$4(value)) return null;
40389
- const groupOrderValue = value.groupOrder;
40390
- if (Array.isArray(groupOrderValue)) {
40391
- const normalized = normalizeOfferingsPreviewGroupOrder(groupOrderValue);
40392
- return {
40393
- ...value,
40394
- groupOrder: normalized.kind === "custom" ? normalized.keys : []
40395
- };
40396
- }
40397
- if (groupOrderValue != null) return {
40398
- ...value,
40399
- groupOrder: []
40400
- };
40401
- const legacyOrder = inferLegacyOfferingsPreviewGroupOrder(Array.isArray(value.groupOverrides) ? value.groupOverrides.filter(isRecord$4) : []);
40402
- return legacyOrder.length > 0 ? {
40403
- ...value,
40404
- groupOrder: legacyOrder
40405
- } : value;
40406
- }
40407
- function uniqueOfferingsPreviewSourceKeys(values) {
40408
- const keys = [];
40409
- for (const value of values) {
40410
- if (!isOfferingsPreviewSourceKey(value)) continue;
40411
- if (!keys.includes(value)) keys.push(value);
40412
- }
40413
- return keys;
40414
- }
40415
- function isRecord$4(value) {
40416
- return typeof value === "object" && value !== null && !Array.isArray(value);
40417
- }
40418
- //#endregion
40419
41339
  //#region ../blocks/src/system/blocks/offerings-preview/fields.ts
40420
41340
  const groupOverrideFields = [
40421
41341
  {
@@ -40779,7 +41699,22 @@ const offeringsPreviewEventDataSchemas = Object.fromEntries(OFFERINGS_PREVIEW_EV
40779
41699
  const offeringsPreviewEventDataLoaders = Object.fromEntries(OFFERINGS_PREVIEW_EVENT_PRESET_DEFINITIONS.map((definition) => [definition.dataKey, eventPresetLoader(definition.preset)]));
40780
41700
  const offeringsPreviewBlockDefinition = {
40781
41701
  manifest: offeringsPreviewManifest,
40782
- normalizeContent: normalizeOfferingsPreviewBlockContent,
41702
+ contentNormalizer: {
41703
+ fieldIds: [
41704
+ "displayMode",
41705
+ "heading",
41706
+ "description",
41707
+ "itemLimit",
41708
+ "groupOrder",
41709
+ "groupOverrides",
41710
+ "_cardStyles",
41711
+ "buttonVariant",
41712
+ "buttonText",
41713
+ "emptyMessage",
41714
+ "_sectionStyles"
41715
+ ],
41716
+ normalize: normalizeOfferingsPreviewBlockContent
41717
+ },
40783
41718
  dataSchemas: {
40784
41719
  ...offeringsPreviewEventDataSchemas,
40785
41720
  routeCandidates: publicRouteCandidatesSchema.optional(),
@@ -44377,11 +45312,11 @@ function buildNewsletterDisplay(source) {
44377
45312
  successMessage: DEFAULT_MAILING_LIST_SUCCESS_MESSAGE,
44378
45313
  privacyNote: null
44379
45314
  };
44380
- const targeting = source.targeting ?? { mode: "default" };
44381
- const choices = resolveRuntimeChoices(context, targeting);
45315
+ const assessment = assessMailingListTargeting(context, source.targeting ?? { mode: "default" });
45316
+ const choices = resolveRuntimeChoices(assessment);
44382
45317
  return {
44383
45318
  kind: "newsletter-form",
44384
- availability: runtimeTargetingIsAvailable(context, targeting, choices) ? "ready" : "unavailable",
45319
+ availability: assessment.tag === "available" ? "ready" : "unavailable",
44385
45320
  className: source.className ?? null,
44386
45321
  buttonLabel: form.buttonLabel,
44387
45322
  successMessage: form.successMessage,
@@ -44438,60 +45373,12 @@ function projectNewsletterFormHydrationTarget(source) {
44438
45373
  default: return assertNever$3(source);
44439
45374
  }
44440
45375
  }
44441
- function resolveRuntimeChoices(context, targeting) {
44442
- if (context?.tag !== "configured" || targeting.mode !== "custom" || targeting.selection.tag !== "visitor_choice" || context.provider.provider !== targeting.provider) return [];
44443
- switch (targeting.provider) {
44444
- case "native": {
44445
- const offered = new Set(targeting.selection.offeredTopicIds);
44446
- return context.provider.provider === "native" ? context.provider.topics.filter((item) => offered.has(item.id)).map((item) => ({
44447
- id: item.id,
44448
- label: item.name
44449
- })) : [];
44450
- }
44451
- case "brevo": {
44452
- const offered = new Set(targeting.selection.offeredListIds);
44453
- return context.provider.provider === "brevo" ? context.provider.lists.filter((item) => offered.has(item.id)).map((item) => ({
44454
- id: String(item.id),
44455
- label: item.name
44456
- })) : [];
44457
- }
44458
- case "mailchimp": {
44459
- const offered = new Set(targeting.selection.offeredInterestIds);
44460
- 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) => ({
44461
- id: item.id,
44462
- label: item.name
44463
- })) ?? [];
44464
- }
44465
- }
44466
- }
44467
- function runtimeTargetingIsAvailable(context, targeting, choices) {
44468
- if (context?.tag !== "configured") return false;
44469
- const provider = context.provider;
44470
- if (targeting.mode === "default") switch (provider.provider) {
44471
- case "native": return provider.topics.some((item) => item.id === provider.defaultTopicId);
44472
- case "brevo": return provider.lists.some((item) => item.id === provider.defaultListId);
44473
- case "mailchimp": return provider.audiences.some((item) => item.id === provider.defaultAudienceId);
44474
- }
44475
- if (targeting.provider !== provider.provider) return false;
44476
- switch (targeting.provider) {
44477
- case "native": return provider.provider === "native" && nativeTargetIsAvailable(provider, targeting, choices);
44478
- case "brevo": return provider.provider === "brevo" && brevoTargetIsAvailable(provider, targeting, choices);
44479
- case "mailchimp": return provider.provider === "mailchimp" && mailchimpTargetIsAvailable(provider, targeting, choices);
44480
- }
44481
- }
44482
- function nativeTargetIsAvailable(provider, targeting, choices) {
44483
- const ids = targeting.selection.tag === "fixed" ? targeting.selection.topicIds : targeting.selection.offeredTopicIds;
44484
- return ids.every((id) => provider.topics.some((item) => item.id === id)) && (targeting.selection.tag !== "visitor_choice" || choices.length === ids.length);
44485
- }
44486
- function brevoTargetIsAvailable(provider, targeting, choices) {
44487
- const ids = targeting.selection.tag === "fixed" ? targeting.selection.listIds : targeting.selection.offeredListIds;
44488
- return ids.every((id) => provider.lists.some((item) => item.id === id)) && (targeting.selection.tag !== "visitor_choice" || choices.length === ids.length);
44489
- }
44490
- function mailchimpTargetIsAvailable(provider, targeting, choices) {
44491
- const audience = provider.audiences.find((item) => item.id === targeting.audienceId);
44492
- if (!audience) return false;
44493
- const ids = targeting.selection.tag === "fixed" ? targeting.selection.interestIds : targeting.selection.offeredInterestIds;
44494
- return ids.every((id) => audience.interests.some((item) => item.id === id)) && (targeting.selection.tag !== "visitor_choice" || choices.length === ids.length);
45376
+ function resolveRuntimeChoices(assessment) {
45377
+ if (!("value" in assessment) || assessment.value.mode !== "custom" || !("selectedChoices" in assessment.value) || assessment.value.targeting.selection.tag !== "visitor_choice") return [];
45378
+ return assessment.value.selectedChoices.map((item) => ({
45379
+ id: String(item.id),
45380
+ label: item.name
45381
+ }));
44495
45382
  }
44496
45383
  function parseRequiredMailingListSignupBlockId(value) {
44497
45384
  const parsed = parseMailingListBlockId(value);
@@ -44595,6 +45482,10 @@ const newsletterSignupManifest = createBlockManifest({
44595
45482
  [...(newsletterSignupManifest.fields ?? []).map((field) => field.id)];
44596
45483
  const newsletterSignupBlockDefinition = {
44597
45484
  manifest: newsletterSignupManifest,
45485
+ contentNormalizer: {
45486
+ fieldIds: ["targeting"],
45487
+ normalize: normalizeMailingListBlockContent
45488
+ },
44598
45489
  runtime: newsletterFormBlockRuntime
44599
45490
  };
44600
45491
  //#endregion
@@ -45327,7 +46218,20 @@ const locationMapBlockDefinition = {
45327
46218
  })], { gap: "md" })
45328
46219
  })
45329
46220
  }),
45330
- normalizeContent: normalizeLocationMapContent
46221
+ contentNormalizer: {
46222
+ fieldIds: [
46223
+ "heading",
46224
+ "locationSource",
46225
+ "venueId",
46226
+ "location",
46227
+ "showAddressText",
46228
+ "showDirections",
46229
+ "directionsLabel",
46230
+ "zoom",
46231
+ "heightPx"
46232
+ ],
46233
+ normalize: normalizeLocationMapContent
46234
+ }
45331
46235
  };
45332
46236
  //#endregion
45333
46237
  //#region ../blocks/src/system/blocks/index.ts
@@ -46532,17 +47436,7 @@ const linksTransforms = [{
46532
47436
  id: "links.resolve",
46533
47437
  kind: "formatter",
46534
47438
  summary: "Resolve LinkValue objects to href strings using the route map",
46535
- run: (value, _options, context) => {
46536
- if (!value || typeof value !== "object") return null;
46537
- const link = value;
46538
- const kind = typeof link.kind === "string" ? link.kind : null;
46539
- if (kind === "internal") return resolveInternalLink(link, context);
46540
- if (kind === "page") return resolvePageLink(link, context);
46541
- if (kind === "entry") return resolveEntryLink(link, context);
46542
- if (kind === "external" || kind === "url") return resolveExternalLink(link);
46543
- if (!kind && typeof link.href === "string") return resolveExternalLink(link);
46544
- return null;
46545
- }
47439
+ run: (value, _options, context) => resolveAuthoredLinkHref(value, context)
46546
47440
  }, {
46547
47441
  id: "links.label",
46548
47442
  kind: "formatter",
@@ -46569,6 +47463,21 @@ const linksTransforms = [{
46569
47463
  return null;
46570
47464
  }
46571
47465
  }];
47466
+ function resolveAuthoredLinkHref(value, context = {}) {
47467
+ if (!value || typeof value !== "object") return null;
47468
+ const link = value;
47469
+ switch (link.kind) {
47470
+ case "internal": return resolveInternalLink(link, context);
47471
+ case "page": return resolvePageLink(link, context);
47472
+ case "entry": return resolveEntryLink(link, context);
47473
+ case "external":
47474
+ case "url": return resolveExternalLink(link);
47475
+ default: return resolvePreResolvedLink(link);
47476
+ }
47477
+ }
47478
+ function resolvePreResolvedLink(link) {
47479
+ return !(typeof link.kind === "string" ? link.kind : null) && typeof link.href === "string" ? resolveExternalLink(link) : null;
47480
+ }
46572
47481
  const eventCalendarSelectionParam = {
46573
47482
  id: "events.calendarSelectionParam",
46574
47483
  kind: "formatter",