@riverbankcms/sdk 0.151.0 → 0.153.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 (316) hide show
  1. package/CHANGELOG.md +52 -0
  2. package/dist/{EventEmbeddedCheckoutStep-BBHbM22D.mjs → EventEmbeddedCheckoutStep-veN-QiAE.mjs} +10 -9
  3. package/dist/{PreviewEditorSidebar-BHplI9HE.mjs → PreviewEditorSidebar-jI_4DTZY.mjs} +4 -3
  4. package/dist/{PreviewEditorUI-DuyN8nsD.mjs → PreviewEditorUI-ORkVZhfu.mjs} +5 -4
  5. package/dist/{SdkPreviewModeRuntime-CR5ypwxJ.mjs → SdkPreviewModeRuntime-DaNqevI1.mjs} +341 -164
  6. package/dist/{SiteChromeCustomizeContext-D5t6joF_.mjs → SiteChromeCustomizeContext-CgNuBHC0.mjs} +27124 -39230
  7. package/dist/ThemedButton-X2UxhW6F.mjs +16525 -0
  8. package/dist/_dts/ai/src/contracts/proposals.d.ts +52 -52
  9. package/dist/_dts/ai/src/contracts/siteStyleProposalSchemas.d.ts +2357 -7859
  10. package/dist/_dts/api/src/bookingOperations.d.ts +5 -3
  11. package/dist/_dts/api/src/commercePortalEndpoints.d.ts +4 -0
  12. package/dist/_dts/api/src/communications.d.ts +19 -5
  13. package/dist/_dts/api/src/endpoints.d.ts +7 -0
  14. package/dist/_dts/api/src/index.d.ts +1 -1
  15. package/dist/_dts/api/src/indexExtendedTypes.d.ts +1 -1
  16. package/dist/_dts/api/src/navigation/types.d.ts +7 -0
  17. package/dist/_dts/api/src/navigation.d.ts +1 -1
  18. package/dist/_dts/api/src/publicAppointmentBookingContracts.d.ts +10 -1
  19. package/dist/_dts/api/src/sdkThemeWire.d.ts +186 -1434
  20. package/dist/_dts/api/src/shared-contracts.d.ts +10 -0
  21. package/dist/_dts/api/src/siteAssets.d.ts +1 -0
  22. package/dist/_dts/api/src/siteInfrastructure.d.ts +2 -1
  23. package/dist/_dts/api/src/teamMembers.d.ts +2 -27
  24. package/dist/_dts/block-form/src/widgets/RepeaterField/components/layoutGridDomain.d.ts +3 -13
  25. package/dist/_dts/blocks/src/contracts/index.d.ts +1 -0
  26. package/dist/_dts/blocks/src/definitions.d.ts +1 -1
  27. package/dist/_dts/blocks/src/index.d.ts +2 -0
  28. package/dist/_dts/blocks/src/lib/media.d.ts +1 -0
  29. package/dist/_dts/blocks/src/lib/picsum.d.ts +2 -2
  30. package/dist/_dts/blocks/src/pageRenderPreparation.d.ts +1 -0
  31. package/dist/_dts/blocks/src/pageRendererContracts.d.ts +2 -1
  32. package/dist/_dts/blocks/src/primitives/content/utils/sanitizeHref.d.ts +1 -1
  33. package/dist/_dts/blocks/src/system/blocks/event-registration.d.ts +4 -4
  34. package/dist/_dts/blocks/src/system/blocks/events/eventSelectionFields.d.ts +1 -1
  35. package/dist/_dts/blocks/src/system/blocks/events/shared/schemas.d.ts +212 -804
  36. package/dist/_dts/blocks/src/system/blocks/events/shared/types.d.ts +7 -2
  37. package/dist/_dts/blocks/src/system/blocks/location-map.d.ts +1 -1
  38. package/dist/_dts/blocks/src/system/blocks/media-text.d.ts +45 -45
  39. package/dist/_dts/blocks/src/system/blocks/offerings-preview/model.d.ts +2 -0
  40. package/dist/_dts/blocks/src/system/blocks/shared/teamMembers.d.ts +4 -7
  41. package/dist/_dts/blocks/src/system/blocks/team-members.d.ts +2 -2
  42. package/dist/_dts/blocks/src/system/customize/backgrounds.d.ts +6 -6
  43. package/dist/_dts/blocks/src/system/fields/backgroundContent.d.ts +377 -1
  44. package/dist/_dts/blocks/src/system/helpers/backgroundMedia.d.ts +6 -2
  45. package/dist/_dts/blocks/src/system/helpers/normalizeMailingListBlockContent.d.ts +2 -0
  46. package/dist/_dts/blocks/src/system/helpers/resolveBackground.d.ts +3 -3
  47. package/dist/_dts/blocks/src/system/layout/repeaterGrid.d.ts +12 -5
  48. package/dist/_dts/blocks/src/system/manifest/fieldValidation/index.d.ts +3 -3
  49. package/dist/_dts/blocks/src/system/manifest/schema.d.ts +24 -24
  50. package/dist/_dts/blocks/src/system/manifest/validation.d.ts +2 -0
  51. package/dist/_dts/blocks/src/system/node/fragments/lookSlot.d.ts +1 -1
  52. package/dist/_dts/blocks/src/system/registry.d.ts +14 -2
  53. package/dist/_dts/blocks/src/system/runtime/api/client.d.ts +0 -5
  54. package/dist/_dts/blocks/src/system/runtime/api/index.d.ts +2 -0
  55. package/dist/_dts/blocks/src/system/runtime/api/schemas/events.d.ts +971 -0
  56. package/dist/_dts/blocks/src/system/runtime/api/schemas/waitlist-claims.d.ts +42 -0
  57. package/dist/_dts/blocks/src/system/runtime/api/types/appointments.d.ts +10 -2
  58. package/dist/_dts/blocks/src/system/runtime/api/types/events.d.ts +10 -136
  59. package/dist/_dts/blocks/src/system/runtime/components/registry.client.d.ts +48 -6
  60. package/dist/_dts/blocks/src/system/runtime/components/runtime-backed-blocks.d.ts +24 -3
  61. package/dist/_dts/blocks/src/system/runtime/hooks/useBookingFlowController.d.ts +1 -1
  62. package/dist/_dts/blocks/src/system/runtime/hooks/useBookingSubmission.state.d.ts +2 -0
  63. package/dist/_dts/blocks/src/system/runtime/hooks/useHeadlessBookingFlow.d.ts +5 -1
  64. package/dist/_dts/blocks/src/system/runtime/hooks/useHeadlessBookingFlow.state.d.ts +1 -1
  65. package/dist/_dts/blocks/src/system/runtime/lookSurfaces.d.ts +2 -2
  66. package/dist/_dts/blocks/src/system/runtime/nodes/ImageResolveContext.d.ts +1 -1
  67. package/dist/_dts/blocks/src/system/runtime/nodes/appointment-services.d.ts +50 -202
  68. package/dist/_dts/blocks/src/system/runtime/nodes/booking-form.headless.state.d.ts +51 -1
  69. package/dist/_dts/blocks/src/system/runtime/nodes/booking-form.state.d.ts +6 -2
  70. package/dist/_dts/blocks/src/system/runtime/nodes/carousel.client.hooks.d.ts +6 -0
  71. package/dist/_dts/blocks/src/system/runtime/nodes/carousel.client.state.d.ts +7 -0
  72. package/dist/_dts/blocks/src/system/runtime/nodes/event-details/lookPresentation.d.ts +2 -2
  73. package/dist/_dts/blocks/src/system/runtime/nodes/event-details/presentation.d.ts +1 -1
  74. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/BookingEntryViews.d.ts +42 -0
  75. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/EventEmbeddedCheckoutStep.d.ts +4 -1
  76. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/EventRegistrationBookingLayer.d.ts +19 -9
  77. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/EventRegistrationPayment.d.ts +4 -2
  78. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/EventRegistrationSurface.d.ts +24 -9
  79. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/EventRegistrationWizardContent.d.ts +4 -2
  80. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/EventSelectionStep.d.ts +5 -4
  81. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/GuestDetailsSection.d.ts +1 -1
  82. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/PaymentStatusStep.d.ts +2 -0
  83. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/SingleTicketEntitlementNote.d.ts +11 -0
  84. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/SuccessStep.d.ts +4 -0
  85. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/TicketSelectionStep.d.ts +5 -0
  86. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/VerifyingTimeoutStep.d.ts +2 -0
  87. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/bookingCardDisplay.d.ts +31 -0
  88. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/bookingEntry.d.ts +154 -0
  89. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/bookingFocus.d.ts +12 -0
  90. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/event-registration.headless.contract.d.ts +2 -2
  91. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/event-registration.headless.planning.d.ts +2 -2
  92. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/eventSourceRecovery.d.ts +9 -0
  93. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/finishedBooking.d.ts +77 -0
  94. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/index.d.ts +0 -1
  95. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/offeringNoun.d.ts +12 -0
  96. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/paymentResponse.d.ts +2 -1
  97. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/registrationCompletion.d.ts +33 -1
  98. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/registrationDerivations.d.ts +11 -0
  99. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/registrationDisplay.d.ts +31 -0
  100. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/registrationDraft.state.d.ts +60 -0
  101. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/registrationErrorRecovery.d.ts +1 -1
  102. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/registrationEventAdapter.d.ts +3 -0
  103. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/registrationStateMachine.d.ts +58 -10
  104. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/registrationSurface.d.ts +17 -0
  105. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/types.d.ts +23 -7
  106. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/useBookingBarFacts.d.ts +12 -0
  107. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/useEventRegistrationRecoveryRequest.d.ts +3 -0
  108. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/useEventRegistrationStripeReturn.d.ts +4 -0
  109. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/useEventRegistrationSubmit.d.ts +4 -11
  110. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/useEventRegistrationWizard.d.ts +1563 -31
  111. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/useEventSourceRecovery.d.ts +2 -12
  112. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/usePaymentStatusRecovery.d.ts +147 -4
  113. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/utils.d.ts +50 -2
  114. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/waitlistClaimRestoration.d.ts +6 -5
  115. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/wizardSteps.d.ts +85 -15
  116. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration.interactive.d.ts +53 -8
  117. package/dist/_dts/blocks/src/system/runtime/nodes/events/shared/CardListItem.d.ts +1 -1
  118. package/dist/_dts/blocks/src/system/runtime/nodes/events/shared/MonthLookView.d.ts +10 -3
  119. package/dist/_dts/blocks/src/system/runtime/nodes/events/shared/eventCollectionContract.d.ts +290 -882
  120. package/dist/_dts/blocks/src/system/runtime/nodes/file-download.d.ts +3 -2
  121. package/dist/_dts/blocks/src/system/runtime/nodes/media.d.ts +22 -90
  122. package/dist/_dts/blocks/src/system/runtime/nodes/mediaContext.d.ts +69 -0
  123. package/dist/_dts/blocks/src/system/runtime/shared/bookingPaymentOptionCopy.d.ts +5 -1
  124. package/dist/_dts/blocks/src/system/runtime/shared/bookingPaymentOptions.d.ts +37 -1
  125. package/dist/_dts/blocks/src/system/runtime/shared/components/ModalShell.d.ts +9 -2
  126. package/dist/_dts/blocks/src/system/runtime/shared/components/PaymentChoiceRows.d.ts +28 -0
  127. package/dist/_dts/blocks/src/system/runtime/shared/components/StepIndicator.d.ts +6 -8
  128. package/dist/_dts/blocks/src/system/runtime/shared/components/index.d.ts +2 -2
  129. package/dist/_dts/blocks/src/system/runtime/shared/index.d.ts +2 -2
  130. package/dist/_dts/blocks/src/system/runtime/shared/useWaitlistClaim.d.ts +9 -12
  131. package/dist/_dts/blocks/src/system/runtime/shared/useWaitlistClaim.state.d.ts +15 -0
  132. package/dist/_dts/blocks/src/system/runtime/shared/utils/themePortalRoot.d.ts +10 -15
  133. package/dist/_dts/blocks/src/system/transforms/registry/index.d.ts +2 -0
  134. package/dist/_dts/blocks/src/system/transforms/registry/links.d.ts +2 -0
  135. package/dist/_dts/commerce/src/purchase/bookingInstalmentSchedule.d.ts +2 -0
  136. package/dist/_dts/commerce/src/purchase/bookingPaymentSelection.d.ts +6 -0
  137. package/dist/_dts/commerce/src/purchase/confirmationEmailPayment.d.ts +14 -14
  138. package/dist/_dts/commerce/src/purchase/confirmationPayment.d.ts +65 -0
  139. package/dist/_dts/commerce/src/purchase/confirmationPaymentSchema.d.ts +66 -0
  140. package/dist/_dts/commerce/src/purchase/index.d.ts +2 -0
  141. package/dist/_dts/commerce/src/purchase/orderPaymentState.d.ts +8 -0
  142. package/dist/_dts/content-model/src/content-types/schema.d.ts +3 -3
  143. package/dist/_dts/content-model/src/templates/defaults.d.ts +2 -2
  144. package/dist/_dts/core/src/appointment-booking-terms.d.ts +20 -0
  145. package/dist/_dts/core/src/booking-payment-schemas.d.ts +70 -0
  146. package/dist/_dts/core/src/branded.d.ts +0 -2
  147. package/dist/_dts/core/src/demo-claim-access.d.ts +1 -1
  148. package/dist/_dts/core/src/demo-claim-readiness.d.ts +18 -0
  149. package/dist/_dts/core/src/demo-lifecycle-status.d.ts +1 -1
  150. package/dist/_dts/core/src/demo-warm-pool-target.d.ts +1 -1
  151. package/dist/_dts/core/src/index.d.ts +4 -0
  152. package/dist/_dts/core/src/mailing-list-runtime.d.ts +2 -2
  153. package/dist/_dts/core/src/mailing-list-setup-parser.d.ts +9 -1
  154. package/dist/_dts/core/src/mailing-list-setup.d.ts +9 -1
  155. package/dist/_dts/core/src/mailing-list-submission.d.ts +1 -1
  156. package/dist/_dts/core/src/mailing-list-targeting-availability.d.ts +68 -0
  157. package/dist/_dts/core/src/mailing-list-targeting.d.ts +11 -1
  158. package/dist/_dts/core/src/ticket-quantity.d.ts +13 -0
  159. package/dist/_dts/db/src/generated/supabase/database.types.d.ts +106 -36
  160. package/dist/_dts/editor/src/chrome/chromeLayers.d.ts +7 -0
  161. package/dist/_dts/editor/src/index.d.ts +1 -0
  162. package/dist/_dts/editor-blocks/src/index.d.ts +1 -0
  163. package/dist/_dts/editor-blocks/src/media/MediaField.d.ts +2 -2
  164. package/dist/_dts/editor-blocks/src/media/admitEditorMediaField.d.ts +4 -0
  165. package/dist/_dts/editor-blocks/src/widgets/link/LinkAddressInput.d.ts +8 -0
  166. package/dist/_dts/editor-blocks/src/widgets/link/LinkContentList.d.ts +12 -0
  167. package/dist/_dts/editor-blocks/src/widgets/link/LinkPicker.d.ts +5 -1
  168. package/dist/_dts/editor-blocks/src/widgets/link/LinkPickerDialog.d.ts +3 -3
  169. package/dist/_dts/editor-blocks/src/widgets/link/LinkSummary.d.ts +8 -2
  170. package/dist/_dts/editor-blocks/src/widgets/link/LinkWidget.d.ts +4 -3
  171. package/dist/_dts/editor-blocks/src/widgets/link/LinkWidgetContext.d.ts +2 -1
  172. package/dist/_dts/editor-blocks/src/widgets/link/draft.d.ts +44 -0
  173. package/dist/_dts/editor-blocks/src/widgets/link/presentation.d.ts +13 -0
  174. package/dist/_dts/editor-blocks/src/widgets/link/types.d.ts +14 -0
  175. package/dist/_dts/editor-blocks/src/widgets/link/useRoutableContent.d.ts +2 -0
  176. package/dist/_dts/editor-blocks/src/widgets/link/utils.d.ts +5 -4
  177. package/dist/_dts/editor-blocks/src/widgets/mailing-list-targeting/MailingListTargetingField.d.ts +1 -1
  178. package/dist/_dts/editor-blocks/src/widgets/mailing-list-targeting/targetingState.d.ts +13 -8
  179. package/dist/_dts/media-core/src/assetsInUseSummary.d.ts +6 -0
  180. package/dist/_dts/media-core/src/contentMediaAudit.d.ts +3 -2
  181. package/dist/_dts/media-core/src/fileStorage.d.ts +4 -3
  182. package/dist/_dts/media-core/src/index.d.ts +5 -0
  183. package/dist/_dts/media-core/src/media.d.ts +406 -0
  184. package/dist/_dts/media-core/src/mediaIdentifierSchema.d.ts +3 -0
  185. package/dist/_dts/media-core/src/mediaKinds.d.ts +2 -0
  186. package/dist/_dts/media-core/src/mediaUsageProjection.d.ts +13 -1
  187. package/dist/_dts/media-core/src/mediaWriteNormalizer.d.ts +2 -6
  188. package/dist/_dts/media-core/src/portableMediaReferences.d.ts +8 -21
  189. package/dist/_dts/media-core/src/renderableMediaAsset.d.ts +28 -14
  190. package/dist/_dts/media-core/src/renderableMediaAssetAdmission.d.ts +15 -0
  191. package/dist/_dts/media-core/src/storageObjectKey.d.ts +12 -0
  192. package/dist/_dts/media-editor/src/CropDialog.d.ts +6 -3
  193. package/dist/_dts/media-editor/src/MediaEditor.d.ts +2 -1
  194. package/dist/_dts/media-editor/src/MediaPreview.d.ts +3 -3
  195. package/dist/_dts/media-editor/src/cropEditorSource.d.ts +5 -7
  196. package/dist/_dts/media-editor/src/editorMediaState.d.ts +29 -0
  197. package/dist/_dts/media-editor/src/mediaSelection.d.ts +1 -2
  198. package/dist/_dts/media-editor/src/runtimeMediaAssetState.d.ts +4 -5
  199. package/dist/_dts/media-editor/src/types.d.ts +1 -1
  200. package/dist/_dts/media-editor/src/utils.d.ts +0 -1
  201. package/dist/_dts/media-react/src/components/cropChangePolicy.d.ts +4 -1
  202. package/dist/_dts/media-react/src/components/cropGeometry.d.ts +1 -0
  203. package/dist/_dts/media-react/src/components/useImageCropperController.d.ts +1 -0
  204. package/dist/_dts/media-react/src/hooks/useSupabaseMediaUploader.d.ts +1 -1
  205. package/dist/_dts/media-react/src/internal/supabaseResumableUpload.d.ts +13 -0
  206. package/dist/_dts/preview-next/src/client/blocks/BlockBackgroundChooser.d.ts +2 -1
  207. package/dist/_dts/preview-next/src/client/blocks/previewCustomizeSelectedMedia.d.ts +11 -12
  208. package/dist/_dts/preview-next/src/client/preview/previewModeRootAttribute.d.ts +11 -0
  209. package/dist/_dts/sdk/src/contracts/content.d.ts +2 -45
  210. package/dist/_dts/sdk/src/contracts/index.d.ts +1 -1
  211. package/dist/_dts/sdk/src/contracts/rendering.d.ts +3 -3
  212. package/dist/_dts/sdk/src/index.d.ts +1 -0
  213. package/dist/_dts/sdk/src/rendering/blocks-page-renderer.d.ts +0 -1
  214. package/dist/_dts/sdk/src/rendering/image.d.ts +14 -4
  215. package/dist/_dts/sdk/src/rendering/index.d.ts +5 -2
  216. package/dist/_dts/sdk/src/rendering/link.d.ts +5 -0
  217. package/dist/_dts/sdk/src/rendering/mediaAssets.d.ts +2 -4
  218. package/dist/_dts/sdk/src/rendering/overrides.d.ts +1 -0
  219. package/dist/_dts/sdk/src/version.d.ts +1 -1
  220. package/dist/_dts/theme-core/src/accordions/types.d.ts +1 -1
  221. package/dist/_dts/theme-core/src/blocks/opaquePanels.d.ts +3 -3
  222. package/dist/_dts/theme-core/src/boxBackgroundSchema.d.ts +397 -26
  223. package/dist/_dts/theme-core/src/buttons/types.d.ts +21 -21
  224. package/dist/_dts/theme-core/src/cards/types.d.ts +34 -34
  225. package/dist/_dts/theme-core/src/generated/containerResponsiveThemeCss.d.ts +1 -1
  226. package/dist/_dts/theme-core/src/inputs/types.d.ts +3 -3
  227. package/dist/_dts/theme-core/src/lib/media.d.ts +2 -616
  228. package/dist/_dts/theme-core/src/mock-themes/all.d.ts +261 -2619
  229. package/dist/_dts/theme-core/src/palette/curated/types.d.ts +1 -1
  230. package/dist/_dts/theme-core/src/schema.d.ts +548 -3187
  231. package/dist/_dts/theme-core/src/site-styles/backgroundAppearance.d.ts +2 -2
  232. package/dist/_dts/theme-core/src/site-styles/runtimeBackgroundResolution.d.ts +9 -9
  233. package/dist/_dts/theme-core/src/site-styles/styleTags.d.ts +1 -1
  234. package/dist/_dts/theme-core/src/site-styles/tokenRecipes.d.ts +1 -1
  235. package/dist/_dts/theme-core/src/styles/skinTokens.d.ts +2 -2
  236. package/dist/_dts/theme-core/src/typography/schema.d.ts +2 -2
  237. package/dist/client/BookingMailingListNote-DrchYW1m.mjs +386 -0
  238. package/dist/client/{CardListItem-ZDLYh6dL.mjs → CardListItem-CC_GVmF6.mjs} +5 -5
  239. package/dist/client/CheckIcon-CpyXr-Kg.mjs +35 -0
  240. package/dist/client/{CheckboxChoice-M-3HycLw.mjs → CheckboxChoice-Dq30m3DO.mjs} +1 -1
  241. package/dist/client/{DemoBookingPortalAccess-BMtvPTWE.mjs → DemoBookingPortalAccess-ojwbVUmx.mjs} +85 -85
  242. package/dist/client/{EventCombined.client-DUm_DOxW.mjs → EventCombined.client-DyN0v0Xk.mjs} +7 -9
  243. package/dist/client/{EventEmbeddedCheckoutStep-DsNNzgRm.mjs → EventEmbeddedCheckoutStep-CgfpKc3n.mjs} +11 -10
  244. package/dist/client/{EventListing.client-DjNYGUVm.mjs → EventListing.client-CuOuvYMz.mjs} +2 -2
  245. package/dist/client/{LeafletStaticMap-DuFiOHq_.mjs → LeafletStaticMap-B0WhRr3j.mjs} +1 -1
  246. package/dist/client/{ModalShell-CqkBvBIC.mjs → ModalShell-BXzucfR6.mjs} +83 -12
  247. package/dist/client/{RichText-LjPCUGGi.mjs → RichText-C1fIXBzA.mjs} +50 -34
  248. package/dist/client/{ThemedButton-DZKjtaDD.mjs → ThemedButton-B7HFqFBg.mjs} +1 -2
  249. package/dist/client/{accordion-Qe--SILD.mjs → accordion-DCDVWSye.mjs} +1 -1
  250. package/dist/client/{bookingForm-3BCl4g_f.mjs → bookingForm-BJ3C4GlM.mjs} +321 -154
  251. package/dist/client/bookings.mjs +118607 -22429
  252. package/dist/client/{carousel-h2YjKOS2.mjs → carousel-BSCBhfHY.mjs} +2 -2
  253. package/dist/client/{carousel.client-BLrd4hmY.mjs → carousel.client-GEtOGsxo.mjs} +46 -41
  254. package/dist/client/{cart-5NeiigVd.mjs → cart-Yv-aAtck.mjs} +3 -3
  255. package/dist/client/{checkout-G4UWjOEw.mjs → checkout-B9xDkl30.mjs} +3 -3
  256. package/dist/client/client.mjs +37511 -33793
  257. package/dist/client/{context-GK4-AVAh.mjs → context-BOGe3rIA.mjs} +275 -10
  258. package/dist/client/{eventCalendar-Prw3Yo_L.mjs → eventCalendar-D6M0T9hs.mjs} +18 -17
  259. package/dist/client/{eventCombined-BUugi6__.mjs → eventCombined-NM4UFBq8.mjs} +4 -4
  260. package/dist/client/{eventDetails-CBHUda-R.mjs → eventDetails-f_FhV4O8.mjs} +3 -4
  261. package/dist/client/{eventListing-CQeG45Dg.mjs → eventListing-WLkfAROh.mjs} +4 -4
  262. package/dist/client/{eventRegistration-Dif3p3oJ.mjs → eventRegistration-D3ejfLvW.mjs} +2209 -2144
  263. package/dist/client/{form-Cf0WMtfG.mjs → form-D5Or2Jyu.mjs} +5 -6
  264. package/dist/client/{headerSection-Bqdr3JWp.mjs → headerSection-hWgnv27V.mjs} +9 -9
  265. package/dist/client/hooks.mjs +17278 -16106
  266. package/dist/client/{locationMap-BVvPou4O.mjs → locationMap-Di1K0O2S.mjs} +3 -4
  267. package/dist/client/{media-47fcnhzD.mjs → media-D29-F1u_.mjs} +183 -362
  268. package/dist/client/mediaContext-CwbbimYB.mjs +135 -0
  269. package/dist/client/{newsletterForm-Cx-1dnic.mjs → newsletterForm-CwxcwTNO.mjs} +5 -5
  270. package/dist/client/{productDetail-DaUMaJYk.mjs → productDetail-BLSnhPdA.mjs} +3 -3
  271. package/dist/client/{productList-BKQlWEKE.mjs → productList-oGytONOX.mjs} +3 -3
  272. package/dist/client/{propsDecoder-DO5iXphQ.mjs → propsDecoder-lf_dTeZS.mjs} +18659 -14473
  273. package/dist/client/renderableMediaAssetAdmission-CHkXYDC_.mjs +143 -0
  274. package/dist/client/rendering/client.mjs +58971 -55722
  275. package/dist/client/rendering/islands.mjs +20 -23
  276. package/dist/client/rendering.mjs +41610 -37917
  277. package/dist/client/{routeIslandApiConfig-BNm0Z3-g.mjs → routeIslandApiConfig-Bc_WVPS8.mjs} +1 -1
  278. package/dist/client/{shop-D7ytcFBC.mjs → shop-BQ3bR7fA.mjs} +16 -14
  279. package/dist/client/{shop-commerce-D68-hHy2.mjs → shop-commerce-DDPll6ZC.mjs} +9 -9
  280. package/dist/client/{shop-commerce-store-Cu7X2lyu.mjs → shop-commerce-store-DO-e1pTe.mjs} +1 -1
  281. package/dist/client/{shopShared-D12rxE69.mjs → shopShared-BylM9jh1.mjs} +5 -5
  282. package/dist/client/themePortalRoot-DS92SRVZ.mjs +47 -0
  283. package/dist/client/{TerminalPanel-DOTSUCO0.mjs → useDiscountCodeUi-B1Puub2v.mjs} +60 -77
  284. package/dist/client/{useEventPagination-jgKa67KV.mjs → useEventPagination-BBhkmQaT.mjs} +6 -8
  285. package/dist/preview-next/before-render.mjs +96 -4
  286. package/dist/preview-next/client/runtime.mjs +3 -3
  287. package/dist/preview-next/keepAlive.mjs +7 -0
  288. package/dist/preview-next/middleware.mjs +7 -0
  289. package/dist/{sdk-runtime-4cCWO_3i.mjs → sdk-runtime-DWCvhJGu.mjs} +1 -1
  290. package/dist/server/components.mjs +21820 -19902
  291. package/dist/server/config-validation.mjs +1786 -642
  292. package/dist/server/config.mjs +1786 -642
  293. package/dist/server/data.mjs +17220 -16076
  294. package/dist/server/index.mjs +234 -2
  295. package/dist/server/next.mjs +22505 -20528
  296. package/dist/server/page-converter.mjs +7587 -6572
  297. package/dist/server/prebuild.mjs +1 -1
  298. package/dist/server/rendering/server.mjs +20487 -19044
  299. package/dist/server/rendering.mjs +21844 -19901
  300. package/dist/server/routing.mjs +19186 -17961
  301. package/dist/server/server.mjs +1874 -695
  302. package/dist/server/theme-bridge.mjs +1371 -914
  303. package/dist/server/theme.mjs +154 -37
  304. package/dist/{spinner-tSdG9YvG.mjs → spinner-BYM-Y81w.mjs} +1 -1
  305. package/dist/styles/index.css +1088 -721
  306. package/package.json +1 -1
  307. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/OrderSummary.d.ts +0 -15
  308. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/useWaitlistClaimRestoration.d.ts +0 -22
  309. package/dist/_dts/blocks/src/system/runtime/shared/components/SelectableOptionCard.d.ts +0 -30
  310. package/dist/client/BookingMailingListNote-DlDSs5Vm.mjs +0 -567
  311. package/dist/client/colorStyles-DTunOThj.mjs +0 -1316
  312. package/dist/client/pricing-CMD0kADd.mjs +0 -314
  313. package/dist/client/result-DRW7JWXz.mjs +0 -30
  314. package/dist/client/scrollToBlock-C86BYkyV.mjs +0 -17
  315. package/dist/client/themePortalRoot-AGs4cE7t.mjs +0 -30
  316. package/dist/spinner-BL3kW9Uh.mjs +0 -803
@@ -1765,12 +1765,14 @@ const unsafeAsSiteId = (id) => id;
1765
1765
  const unsafeAsApiKeyId = (id) => id;
1766
1766
  const unsafeAsEventOfferingId = (id) => id;
1767
1767
  const unsafeAsEventSeriesId = (id) => id;
1768
+ const unsafeAsMediaAssetId = (id) => id;
1768
1769
  const unsafeAsUserId = (id) => id;
1769
1770
  const unsafeAsContentVersionId = (id) => id;
1770
1771
  const parseSiteId = (value) => parseUuidBrand(value, unsafeAsSiteId);
1771
1772
  const parseApiKeyId = (value) => parseUuidBrand(value, unsafeAsApiKeyId);
1772
1773
  const parseEventOfferingId = (value) => parseUuidBrand(value, unsafeAsEventOfferingId);
1773
1774
  const parseUserId = (value) => parseUuidBrand(value, unsafeAsUserId);
1775
+ const parseMediaAssetId = (value) => parseUuidBrand(value, unsafeAsMediaAssetId);
1774
1776
  const parseContentVersionId = (value) => parseUuidBrand(value, unsafeAsContentVersionId);
1775
1777
  const parseEventSeriesId = (value) => parseUuidBrand(value, unsafeAsEventSeriesId);
1776
1778
  //#endregion
@@ -1975,6 +1977,29 @@ function canonicalEventPricingDisplay(pricing) {
1975
1977
  default: return assertNever$6(pricing);
1976
1978
  }
1977
1979
  }
1980
+ /**
1981
+ * The single renderer for a precomputed booking price `display` ADT. This is
1982
+ * the one place booking price variants map to user-facing strings — every call
1983
+ * site (booking entry, events calendar, event/course details, listings, portal)
1984
+ * should route through here so labels never drift. Adding a variant to the ADT
1985
+ * breaks compilation here (via `assertNever`) until it is handled.
1986
+ */
1987
+ function formatBookingPriceDisplay(display, options = {}) {
1988
+ switch (display.kind) {
1989
+ case "free": return "Free";
1990
+ case "exact": return formatCurrencyAmount(display.amount, display.currency, options);
1991
+ case "from": return `From ${formatCurrencyAmount(display.amount, display.currency, options)}`;
1992
+ case "range": return `${formatCurrencyAmount(display.minAmount, display.currency, options)} - ${formatCurrencyAmount(display.maxAmount, display.currency, options)}`;
1993
+ 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)}`;
1994
+ case "unavailable": return "Pricing unavailable";
1995
+ case "external": return display.label;
1996
+ default: return assertNever$6(display);
1997
+ }
1998
+ }
1999
+ /** Render an event's price directly from its pricing value. */
2000
+ function formatEventPricing(pricing, options = {}) {
2001
+ return formatBookingPriceDisplay(publicEventPricingDisplay(pricing), options);
2002
+ }
1978
2003
  //#endregion
1979
2004
  //#region ../core/src/event-page-surface-subject.ts
1980
2005
  function createEventSeriesPageSurfaceSubject(eventSeriesId, bookingScope, eventDestinationMode) {
@@ -2118,6 +2143,7 @@ function parseBookingTimeZone(timeZone) {
2118
2143
  }
2119
2144
  //#endregion
2120
2145
  //#region ../core/src/booking-date-time.ts
2146
+ const DEFAULT_LOCALE = "en-US";
2121
2147
  const FALLBACK_TIMEZONE = "UTC";
2122
2148
  const START_OF_DAY = {
2123
2149
  hour: 0,
@@ -2240,6 +2266,34 @@ function toScheduledBookingDateTime(input) {
2240
2266
  if (result.ok) return result.value;
2241
2267
  throw new Error(formatParseError(result));
2242
2268
  }
2269
+ function formatBookingInstant(isoString, options) {
2270
+ const date = new Date(isoString);
2271
+ if (Number.isNaN(date.getTime())) return isoString;
2272
+ return getCachedDateTimeFormatter(options.locale ?? DEFAULT_LOCALE, getSafeTimeZone(options.timeZone), options.format).format(date);
2273
+ }
2274
+ function formatBookingDate(input, format = {
2275
+ weekday: "long",
2276
+ year: "numeric",
2277
+ month: "long",
2278
+ day: "numeric"
2279
+ }) {
2280
+ return formatBookingInstant(input.startsAt, {
2281
+ locale: input.locale,
2282
+ timeZone: input.timeZone,
2283
+ format
2284
+ });
2285
+ }
2286
+ function formatBookingTime(input, format = {
2287
+ hour: "numeric",
2288
+ minute: "2-digit",
2289
+ hour12: true
2290
+ }) {
2291
+ return formatBookingInstant(input.startsAt, {
2292
+ locale: input.locale,
2293
+ timeZone: input.timeZone,
2294
+ format
2295
+ });
2296
+ }
2243
2297
  function getBookingLocalDateKey(input) {
2244
2298
  return formatDateKey$1(getLocalScheduleDate(new Date(input.startsAt), input.timeZone));
2245
2299
  }
@@ -2389,7 +2443,7 @@ function parsePositiveInteger(value) {
2389
2443
  return typeof value === "number" && Number.isSafeInteger(value) && value > 0 ? value : null;
2390
2444
  }
2391
2445
  function parseNewsletterListId(value) {
2392
- return parseTrimmedMatchingString(value, UUID_PATTERN);
2446
+ return parseTrimmedMatchingString(typeof value === "string" ? value.toLowerCase() : value, UUID_PATTERN);
2393
2447
  }
2394
2448
  function parseMailchimpAccountId(value) {
2395
2449
  return parseTrimmedMatchingString(value, OPAQUE_ID_PATTERN);
@@ -2431,8 +2485,15 @@ function parseMailingListProviderScope(value) {
2431
2485
  }
2432
2486
  return err$1("invalid_mailing_list_provider_scope");
2433
2487
  }
2434
- //#endregion
2435
- //#region ../core/src/mailing-list-setup-parser.ts
2488
+ z.unknown().transform((value, ctx) => {
2489
+ const parsed = parseNewsletterAudience(value);
2490
+ if (parsed.ok) return parsed.value;
2491
+ ctx.addIssue({
2492
+ code: "custom",
2493
+ message: "Choose a valid newsletter audience."
2494
+ });
2495
+ return z.NEVER;
2496
+ });
2436
2497
  function parseMailingListGlobalForm(value) {
2437
2498
  if (!isRecord$16(value)) return formError("shape");
2438
2499
  const fields = parseGlobalFields(value.fields);
@@ -2452,6 +2513,16 @@ function parseMailingListGlobalForm(value) {
2452
2513
  privacyNote
2453
2514
  });
2454
2515
  }
2516
+ function parseNewsletterAudience(value) {
2517
+ if (!isRecord$16(value)) return err$1("invalid_newsletter_audience");
2518
+ if (value.kind === "general" && Object.keys(value).length === 1) return ok$1({ kind: "general" });
2519
+ if (value.kind !== "topic" || Object.keys(value).some((key) => key !== "kind" && key !== "topicId")) return err$1("invalid_newsletter_audience");
2520
+ const topicId = parseNewsletterListId(value.topicId);
2521
+ return topicId === null ? err$1("invalid_newsletter_audience") : ok$1({
2522
+ kind: "topic",
2523
+ topicId
2524
+ });
2525
+ }
2455
2526
  function parseGlobalFields(value) {
2456
2527
  if (!isRecord$16(value) || typeof value.preset !== "string") return null;
2457
2528
  switch (value.preset) {
@@ -2501,44 +2572,144 @@ function formError(field) {
2501
2572
  }
2502
2573
  //#endregion
2503
2574
  //#region ../core/src/mailing-list-targeting.ts
2575
+ const DEFAULT_MAILING_LIST_TARGETING = { mode: "default" };
2576
+ function mailingListTargetingChoiceIds(targeting) {
2577
+ switch (targeting.provider) {
2578
+ case "native": return targeting.selection.tag === "general" ? [] : targeting.selection.tag === "fixed" ? targeting.selection.topicIds : targeting.selection.offeredTopicIds;
2579
+ case "brevo": return targeting.selection.tag === "fixed" ? targeting.selection.listIds : targeting.selection.offeredListIds;
2580
+ case "mailchimp": return targeting.selection.tag === "fixed" ? targeting.selection.interestIds : targeting.selection.offeredInterestIds;
2581
+ }
2582
+ }
2504
2583
  function planMailingListChoicePresentation(targeting) {
2505
- if (targeting.mode === "default" || targeting.selection.tag === "fixed") return { tag: "hidden" };
2584
+ if (targeting.mode === "default" || targeting.selection.tag === "fixed" || targeting.selection.tag === "general") return { tag: "hidden" };
2506
2585
  switch (targeting.provider) {
2507
- case "native": return targeting.selection.offeredTopicIds.length === 1 ? {
2508
- tag: "automatic",
2509
- selection: {
2510
- provider: "native",
2511
- topicIds: targeting.selection.offeredTopicIds
2512
- }
2513
- } : {
2514
- tag: "visitor_choice",
2586
+ case "native": return presentVisitorSelection({
2515
2587
  provider: "native",
2516
- optionCount: targeting.selection.offeredTopicIds.length
2588
+ topicIds: targeting.selection.offeredTopicIds
2589
+ }, targeting.selection.offeredTopicIds.length);
2590
+ case "brevo": return presentVisitorSelection({
2591
+ provider: "brevo",
2592
+ listIds: targeting.selection.offeredListIds
2593
+ }, targeting.selection.offeredListIds.length);
2594
+ case "mailchimp": return presentVisitorSelection({
2595
+ provider: "mailchimp",
2596
+ interestIds: targeting.selection.offeredInterestIds
2597
+ }, targeting.selection.offeredInterestIds.length);
2598
+ }
2599
+ }
2600
+ function presentVisitorSelection(selection, optionCount) {
2601
+ return optionCount === 1 ? {
2602
+ tag: "automatic",
2603
+ selection
2604
+ } : {
2605
+ tag: "visitor_choice",
2606
+ provider: selection.provider,
2607
+ optionCount
2608
+ };
2609
+ }
2610
+ //#endregion
2611
+ //#region ../core/src/mailing-list-targeting-availability.ts
2612
+ function resolveMailingListDefaultDestination(provider) {
2613
+ switch (provider.provider) {
2614
+ case "native": {
2615
+ const audience = provider.defaultAudience;
2616
+ return audience.kind === "general" ? {
2617
+ id: "general",
2618
+ name: "General mailing list"
2619
+ } : provider.topics.find((item) => item.id === audience.topicId) ?? null;
2620
+ }
2621
+ case "brevo": return provider.lists.find((item) => item.id === provider.defaultListId) ?? null;
2622
+ case "mailchimp": return provider.audiences.find((item) => item.id === provider.defaultAudienceId) ?? null;
2623
+ }
2624
+ }
2625
+ function assessMailingListTargeting(context, targeting) {
2626
+ if (context?.tag !== "configured") return {
2627
+ tag: "unavailable",
2628
+ reason: "setup_missing",
2629
+ targeting
2630
+ };
2631
+ const catalog = context.provider;
2632
+ if (targeting.mode === "custom") return assessCustomTargeting(catalog, targeting);
2633
+ const destination = resolveMailingListDefaultDestination(catalog);
2634
+ return destination === null ? {
2635
+ tag: "unavailable",
2636
+ reason: "default_missing",
2637
+ targeting
2638
+ } : {
2639
+ tag: "available",
2640
+ value: {
2641
+ mode: "default",
2642
+ targeting,
2643
+ provider: catalog.provider,
2644
+ destination
2645
+ }
2646
+ };
2647
+ }
2648
+ function assessCustomTargeting(catalog, targeting) {
2649
+ switch (targeting.provider) {
2650
+ case "native": return catalog.provider === "native" ? assessMatchedChoices({
2651
+ provider: "native",
2652
+ catalog,
2653
+ targeting
2654
+ }, catalog.topics) : {
2655
+ tag: "unavailable",
2656
+ reason: "provider_mismatch",
2657
+ targeting
2517
2658
  };
2518
- case "brevo": return targeting.selection.offeredListIds.length === 1 ? {
2519
- tag: "automatic",
2520
- selection: {
2521
- provider: "brevo",
2522
- listIds: targeting.selection.offeredListIds
2523
- }
2524
- } : {
2525
- tag: "visitor_choice",
2659
+ case "brevo": return catalog.provider === "brevo" ? assessMatchedChoices({
2526
2660
  provider: "brevo",
2527
- optionCount: targeting.selection.offeredListIds.length
2661
+ catalog,
2662
+ targeting
2663
+ }, catalog.lists) : {
2664
+ tag: "unavailable",
2665
+ reason: "provider_mismatch",
2666
+ targeting
2528
2667
  };
2529
- case "mailchimp": return targeting.selection.offeredInterestIds.length === 1 ? {
2530
- tag: "automatic",
2531
- selection: {
2668
+ case "mailchimp": {
2669
+ if (catalog.provider !== "mailchimp") return {
2670
+ tag: "unavailable",
2671
+ reason: "provider_mismatch",
2672
+ targeting
2673
+ };
2674
+ const audience = catalog.audiences.find((item) => item.id === targeting.audienceId);
2675
+ return audience === void 0 ? {
2676
+ tag: "unavailable",
2677
+ reason: "audience_missing",
2678
+ value: {
2679
+ mode: "custom",
2680
+ provider: "mailchimp",
2681
+ catalog,
2682
+ targeting
2683
+ }
2684
+ } : assessMatchedChoices({
2532
2685
  provider: "mailchimp",
2533
- interestIds: targeting.selection.offeredInterestIds
2534
- }
2535
- } : {
2536
- tag: "visitor_choice",
2537
- provider: "mailchimp",
2538
- optionCount: targeting.selection.offeredInterestIds.length
2539
- };
2686
+ catalog,
2687
+ targeting,
2688
+ audience
2689
+ }, audience.interests);
2690
+ }
2540
2691
  }
2541
2692
  }
2693
+ function assessMatchedChoices(matched, choices) {
2694
+ const selected = new Set(mailingListTargetingChoiceIds(matched.targeting));
2695
+ const available = new Set(choices.map((item) => item.id));
2696
+ const missingIds = [...selected].filter((id) => !available.has(id));
2697
+ const value = {
2698
+ mode: "custom",
2699
+ ...matched,
2700
+ choices,
2701
+ selectedChoices: choices.filter((item) => selected.has(item.id))
2702
+ };
2703
+ return missingIds.length > 0 ? {
2704
+ tag: "unavailable",
2705
+ reason: "choices_missing",
2706
+ missingIds,
2707
+ value
2708
+ } : {
2709
+ tag: "available",
2710
+ value
2711
+ };
2712
+ }
2542
2713
  //#endregion
2543
2714
  //#region ../core/src/mailing-list-runtime.ts
2544
2715
  function parseMailingListRuntimeContext(value) {
@@ -2584,11 +2755,12 @@ function parseRuntimeProvider(value) {
2584
2755
  }
2585
2756
  }
2586
2757
  function parseNativeRuntimeProvider(value) {
2587
- const defaultTopicId = parseNewsletterListId(value.defaultTopicId);
2758
+ if ("defaultTopicId" in value) return null;
2759
+ const defaultAudience = parseNewsletterAudience(value.defaultAudience);
2588
2760
  const topics = parseNamedItems(value.topics, parseNewsletterListId);
2589
- return defaultTopicId !== null && topics !== null ? {
2761
+ return defaultAudience.ok && topics !== null ? {
2590
2762
  provider: "native",
2591
- defaultTopicId,
2763
+ defaultAudience: defaultAudience.value,
2592
2764
  topics
2593
2765
  } : null;
2594
2766
  }
@@ -2604,57 +2776,47 @@ function parseBrevoRuntimeProvider(value) {
2604
2776
  function parseMailchimpRuntimeProvider(value) {
2605
2777
  if (!Array.isArray(value.audiences)) return null;
2606
2778
  const defaultAudienceId = parseMailchimpAudienceId(value.defaultAudienceId);
2607
- if (defaultAudienceId === null) return null;
2608
- const audiences = [];
2609
- for (const audience of value.audiences) {
2610
- if (!isRecord$15(audience)) return null;
2611
- const id = parseMailchimpAudienceId(audience.id);
2612
- const name = parseName(audience.name);
2613
- if (id === null || name === null || !Array.isArray(audience.interests)) return null;
2614
- const interests = parseMailchimpInterests(audience.interests);
2615
- if (interests === null) return null;
2616
- audiences.push({
2617
- id,
2618
- name,
2619
- interests
2620
- });
2621
- }
2622
- return {
2779
+ const audiences = Array.from(value.audiences, parseMailchimpAudience);
2780
+ return defaultAudienceId !== null && audiences.every((audience) => audience !== null) ? {
2623
2781
  provider: "mailchimp",
2624
2782
  defaultAudienceId,
2625
2783
  audiences
2626
- };
2784
+ } : null;
2627
2785
  }
2628
- function parseMailchimpInterests(value) {
2629
- const interests = [];
2630
- for (const interest of value) {
2631
- if (!isRecord$15(interest)) return null;
2632
- const id = parseMailchimpInterestId(interest.id);
2633
- const name = parseName(interest.name);
2634
- const categoryName = parseName(interest.categoryName);
2635
- if (id === null || name === null || categoryName === null) return null;
2636
- interests.push({
2637
- id,
2638
- name,
2639
- categoryName
2640
- });
2641
- }
2642
- return interests;
2786
+ function parseMailchimpAudience(value) {
2787
+ if (!isRecord$15(value) || !Array.isArray(value.interests)) return null;
2788
+ const id = parseMailchimpAudienceId(value.id);
2789
+ const name = parseName(value.name);
2790
+ const interests = Array.from(value.interests, parseMailchimpInterest);
2791
+ return id !== null && name !== null && interests.every((interest) => interest !== null) ? {
2792
+ id,
2793
+ name,
2794
+ interests
2795
+ } : null;
2796
+ }
2797
+ function parseMailchimpInterest(value) {
2798
+ if (!isRecord$15(value)) return null;
2799
+ const id = parseMailchimpInterestId(value.id);
2800
+ const name = parseName(value.name);
2801
+ const categoryName = parseName(value.categoryName);
2802
+ return id !== null && name !== null && categoryName !== null ? {
2803
+ id,
2804
+ name,
2805
+ categoryName
2806
+ } : null;
2643
2807
  }
2644
2808
  function parseNamedItems(value, parseId) {
2645
2809
  if (!Array.isArray(value)) return null;
2646
- const items = [];
2647
- for (const item of value) {
2810
+ const items = Array.from(value, (item) => {
2648
2811
  if (!isRecord$15(item)) return null;
2649
2812
  const id = parseId(item.id);
2650
2813
  const name = parseName(item.name);
2651
- if (id === null || name === null) return null;
2652
- items.push({
2814
+ return id !== null && name !== null ? {
2653
2815
  id,
2654
2816
  name
2655
- });
2656
- }
2657
- return items;
2817
+ } : null;
2818
+ });
2819
+ return items.every((item) => item !== null) ? items : null;
2658
2820
  }
2659
2821
  function parseName(value) {
2660
2822
  if (typeof value !== "string") return null;
@@ -2670,6 +2832,7 @@ function invalidRuntimeContext() {
2670
2832
  //#endregion
2671
2833
  //#region ../core/src/mailing-list-targeting-parser.ts
2672
2834
  function parseMailingListPlacementTargeting(value) {
2835
+ if (value == null) return ok$1(DEFAULT_MAILING_LIST_TARGETING);
2673
2836
  if (!isRecord$14(value)) return targetingError("shape");
2674
2837
  if (value.mode === "default") return ok$1({ mode: "default" });
2675
2838
  if (value.mode !== "custom") return targetingError("mode");
@@ -2682,6 +2845,11 @@ function parseMailingListPlacementTargeting(value) {
2682
2845
  }
2683
2846
  }
2684
2847
  function parseNativeTargeting(selection) {
2848
+ if (selection.tag === "general") return Object.keys(selection).length === 1 ? ok$1({
2849
+ mode: "custom",
2850
+ provider: "native",
2851
+ selection: { tag: "general" }
2852
+ }) : targetingError("selection");
2685
2853
  if (selection.tag === "fixed") {
2686
2854
  const topicIds = parseNonEmptyUniqueIds(selection.topicIds, parseNewsletterListId);
2687
2855
  return topicIds.ok ? ok$1({
@@ -2768,14 +2936,11 @@ function parseNonEmptyUniqueIds(value, parseId) {
2768
2936
  }
2769
2937
  function parseUniqueIds(value, parseId) {
2770
2938
  if (!Array.isArray(value)) return targetingError("targets");
2771
- const parsed = [];
2772
- for (const item of value) {
2939
+ return Array.from(value).reduce((parsed, item) => {
2940
+ if (!parsed.ok) return parsed;
2773
2941
  const id = parseId(item);
2774
- if (id === null) return targetingError("targets");
2775
- if (parsed.includes(id)) return targetingError("duplicate_target");
2776
- parsed.push(id);
2777
- }
2778
- return ok$1(parsed);
2942
+ return id === null ? targetingError("targets") : parsed.value.includes(id) ? targetingError("duplicate_target") : ok$1([...parsed.value, id]);
2943
+ }, ok$1([]));
2779
2944
  }
2780
2945
  function isRecord$14(value) {
2781
2946
  return typeof value === "object" && value !== null && !Array.isArray(value);
@@ -3207,6 +3372,45 @@ function resolveSiteVerticalBookingConfig(rawConfig) {
3207
3372
  };
3208
3373
  }
3209
3374
  //#endregion
3375
+ //#region ../core/src/event-preset-vocabulary.ts
3376
+ const EVENT_PRESET_VOCABULARY = {
3377
+ event: {
3378
+ label: "Event",
3379
+ pluralLabel: "Events",
3380
+ description: "Any event, repeating or manually dated, that does not fit the other categories."
3381
+ },
3382
+ class: {
3383
+ label: "Class",
3384
+ pluralLabel: "Classes",
3385
+ description: "Regular scheduled classes and group sessions."
3386
+ },
3387
+ course: {
3388
+ label: "Course",
3389
+ pluralLabel: "Courses",
3390
+ description: "A fixed set of dates that people book together."
3391
+ },
3392
+ term: {
3393
+ label: "Term",
3394
+ pluralLabel: "Terms",
3395
+ description: "A fixed term of sessions that people book together."
3396
+ },
3397
+ workshop: {
3398
+ label: "Workshop",
3399
+ pluralLabel: "Workshops",
3400
+ description: "One-off workshops, day sessions, or special events."
3401
+ },
3402
+ retreat: {
3403
+ label: "Retreat",
3404
+ pluralLabel: "Retreats",
3405
+ description: "Multi-day or residential immersive experiences."
3406
+ },
3407
+ performance: {
3408
+ label: "Performance",
3409
+ pluralLabel: "Performances",
3410
+ description: "Audience-facing shows, concerts, recitals, or showcases."
3411
+ }
3412
+ };
3413
+ //#endregion
3210
3414
  //#region ../api/src/publicEventEndpointDefinitions.ts
3211
3415
  const PUBLIC_EVENT_ENDPOINT_DEFINITIONS = defineEndpointDefinitions({
3212
3416
  checkPublicEventRegistrationNextStep: {
@@ -9457,6 +9661,13 @@ const API_ENDPOINTS$1 = {
9457
9661
  auth: "public",
9458
9662
  responseKind: "json"
9459
9663
  },
9664
+ demoClaimReadiness: {
9665
+ path: "/demo/claims/{claimId}/readiness",
9666
+ method: "GET",
9667
+ auth: "user",
9668
+ responseKind: "json",
9669
+ errors: []
9670
+ },
9460
9671
  demoClaimMagicLink: {
9461
9672
  path: "/demo/auth/send-magic-link",
9462
9673
  method: "POST",
@@ -10362,7 +10573,7 @@ var SimpleCache = class {
10362
10573
  * 1. This constant
10363
10574
  * 2. The "version" field in package.json
10364
10575
  */
10365
- const SDK_VERSION = "0.151.0";
10576
+ const SDK_VERSION = "0.153.0";
10366
10577
  //#endregion
10367
10578
  //#region src/client/error.ts
10368
10579
  let _Symbol$for;
@@ -15667,7 +15878,96 @@ Object.keys(legacyItemSurfaceCardRoles);
15667
15878
  */
15668
15879
  const cardRoleWithLegacyAliasSchema = z.preprocess((value) => normalizeCardRole(value) ?? (typeof value === "string" && Object.prototype.hasOwnProperty.call(legacyItemSurfaceCardRoles, value) ? legacyItemSurfaceCardRoles[value] : value), cardRoleSchema);
15669
15880
  //#endregion
15670
- //#region ../theme-core/src/lib/media.ts
15881
+ //#region ../media-core/src/identifiers.ts
15882
+ function slugifyMediaIdentifier(value) {
15883
+ return value.toLowerCase().trim().replace(/[^a-z0-9\s-]/g, "").replace(/\s+/g, "-").replace(/-+/g, "-");
15884
+ }
15885
+ function parseMediaIdentifier(value) {
15886
+ if (typeof value !== "string") return err$1({ kind: "not_string" });
15887
+ const trimmed = value.trim();
15888
+ if (trimmed.length === 0) return err$1({ kind: "empty_identifier" });
15889
+ const normalized = slugifyMediaIdentifier(trimmed);
15890
+ if (normalized.length === 0) return err$1({
15891
+ kind: "empty_after_normalization",
15892
+ value
15893
+ });
15894
+ return ok$1(normalized);
15895
+ }
15896
+ /**
15897
+ * Admits identifiers read from trusted persistence without repairing dirty data in memory.
15898
+ * User input should use parseMediaIdentifier, which intentionally canonicalizes it.
15899
+ */
15900
+ function parseCanonicalMediaIdentifier(value) {
15901
+ if (typeof value !== "string") return err$1({ kind: "not_string" });
15902
+ const parsed = parseMediaIdentifier(value);
15903
+ if (!parsed.ok) return parsed;
15904
+ if (value !== parsed.value) return err$1({
15905
+ kind: "non_canonical_identifier",
15906
+ value,
15907
+ canonical: parsed.value
15908
+ });
15909
+ return parsed;
15910
+ }
15911
+ //#endregion
15912
+ //#region ../media-core/src/mediaIdentifierSchema.ts
15913
+ /** Strict stored-reference admission delegates the canonical identifier decision. */
15914
+ const mediaIdentifierSchema = z.string().transform((value, context) => {
15915
+ const parsed = parseCanonicalMediaIdentifier(value);
15916
+ if (parsed.ok) return parsed.value;
15917
+ context.addIssue({
15918
+ code: "custom",
15919
+ message: "Expected a canonical media identifier"
15920
+ });
15921
+ return z.NEVER;
15922
+ });
15923
+ //#endregion
15924
+ //#region ../media-core/src/mediaKinds.ts
15925
+ const mediaTypeDiscriminants = [
15926
+ "image",
15927
+ "video",
15928
+ "audio",
15929
+ "document",
15930
+ "spreadsheet",
15931
+ "archive"
15932
+ ];
15933
+ //#endregion
15934
+ //#region ../media-core/src/renderableMediaAsset.ts
15935
+ /** A portable reference never uses an incidental UUID, URL or storage snapshot for identity. */
15936
+ function resolveManagedMediaReference(reference, index) {
15937
+ const asset = index?.byIdentifier.get(reference.identifier);
15938
+ return asset ? {
15939
+ kind: "resolved",
15940
+ reference,
15941
+ asset
15942
+ } : {
15943
+ kind: "missing",
15944
+ reference
15945
+ };
15946
+ }
15947
+ //#endregion
15948
+ //#region ../media-core/src/renderableMediaAssetAdmission.ts
15949
+ const mediaAssetIdSchema = z.string().transform((value, context) => {
15950
+ const parsed = parseMediaAssetId(value);
15951
+ if (parsed.ok) return parsed.value;
15952
+ context.addIssue({
15953
+ code: "custom",
15954
+ message: "Expected a media asset ID"
15955
+ });
15956
+ return z.NEVER;
15957
+ });
15958
+ const nonemptyStorageValue = z.string().refine((value) => value.trim().length > 0);
15959
+ const renderableMediaAssetSchema = z.object({
15960
+ id: mediaAssetIdSchema,
15961
+ identifier: mediaIdentifierSchema,
15962
+ storageBucket: nonemptyStorageValue,
15963
+ storagePath: nonemptyStorageValue,
15964
+ mimeType: nonemptyStorageValue,
15965
+ filename: z.string().optional(),
15966
+ width: z.number().positive().nullable(),
15967
+ height: z.number().positive().nullable()
15968
+ });
15969
+ //#endregion
15970
+ //#region ../media-core/src/media.ts
15671
15971
  const hotspotSchema = z.object({
15672
15972
  x: z.number().min(0).max(1),
15673
15973
  y: z.number().min(0).max(1),
@@ -15701,56 +16001,185 @@ const transformSchema = z.object({
15701
16001
  rotation: rotationSchema.optional()
15702
16002
  });
15703
16003
  const mediaPosterSchema = z.discriminatedUnion("tag", [z.object({
15704
- tag: z.literal("storage"),
15705
- assetId: z.string().optional(),
15706
- storageBucket: z.string().min(1),
15707
- storagePath: z.string().min(1)
15708
- }), z.object({
16004
+ tag: z.literal("media"),
16005
+ identifier: mediaIdentifierSchema
16006
+ }).strict(), z.object({
15709
16007
  tag: z.literal("external-src"),
15710
- assetId: z.string().optional(),
15711
16008
  src: z.string().url()
15712
- })]);
15713
- const mediaBaseSchema = z.object({
16009
+ }).strict()]);
16010
+ const mediaUsageShape = {
15714
16011
  purpose: z.string().optional(),
15715
16012
  placeholder: z.boolean().default(true).optional(),
15716
- assetId: z.string().optional(),
15717
- identifier: z.string().optional(),
15718
- src: z.string().optional(),
15719
16013
  alt: z.string().optional(),
15720
16014
  filename: z.string().optional(),
15721
- mimeType: z.string().optional(),
15722
16015
  width: z.number().optional(),
15723
16016
  height: z.number().optional(),
15724
- storageBucket: z.string().optional(),
15725
- storagePath: z.string().optional(),
15726
16017
  poster: mediaPosterSchema.optional(),
15727
16018
  transform: transformSchema.optional()
15728
- });
15729
- const mediaIdentifierSchema = z.string().trim().min(1).brand();
16019
+ };
16020
+ const environmentExclusions = {
16021
+ assetId: z.never().optional(),
16022
+ id: z.never().optional(),
16023
+ src: z.never().optional(),
16024
+ storageBucket: z.never().optional(),
16025
+ storagePath: z.never().optional(),
16026
+ mimeType: z.never().optional()
16027
+ };
16028
+ const mediaBaseSchema = z.object({
16029
+ ...mediaUsageShape,
16030
+ ...environmentExclusions,
16031
+ identifier: mediaIdentifierSchema
16032
+ }).strict();
15730
16033
  const imageMediaSchema = mediaBaseSchema.extend({ type: z.literal("image") });
15731
16034
  const videoMediaSchema = mediaBaseSchema.extend({ type: z.literal("video") });
15732
- const audioMediaSchema = mediaBaseSchema.extend({ type: z.literal("audio") });
15733
- const documentMediaSchema = mediaBaseSchema.extend({ type: z.literal("document") });
15734
- const spreadsheetMediaSchema = mediaBaseSchema.extend({ type: z.literal("spreadsheet") });
15735
- const archiveMediaSchema = mediaBaseSchema.extend({ type: z.literal("archive") });
15736
- const mediaSchema = z.discriminatedUnion("type", [
15737
- imageMediaSchema,
15738
- videoMediaSchema,
15739
- audioMediaSchema,
15740
- documentMediaSchema,
15741
- spreadsheetMediaSchema,
15742
- archiveMediaSchema
15743
- ]);
15744
- const persistableMediaBaseSchema = mediaBaseSchema.extend({ identifier: mediaIdentifierSchema });
15745
- z.discriminatedUnion("type", [
15746
- persistableMediaBaseSchema.extend({ type: z.literal("image") }),
15747
- persistableMediaBaseSchema.extend({ type: z.literal("video") }),
15748
- persistableMediaBaseSchema.extend({ type: z.literal("audio") }),
15749
- persistableMediaBaseSchema.extend({ type: z.literal("document") }),
15750
- persistableMediaBaseSchema.extend({ type: z.literal("spreadsheet") }),
15751
- persistableMediaBaseSchema.extend({ type: z.literal("archive") })
16035
+ const mediaSchema = mediaBaseSchema.extend({ type: z.enum(mediaTypeDiscriminants) });
16036
+ const externalMediaSchema = z.object({
16037
+ ...mediaUsageShape,
16038
+ ...environmentExclusions,
16039
+ type: z.enum(mediaTypeDiscriminants),
16040
+ src: z.string().min(1),
16041
+ identifier: z.never().optional()
16042
+ }).strict();
16043
+ const assetMediaSchema = z.object({
16044
+ ...mediaUsageShape,
16045
+ ...environmentExclusions,
16046
+ type: z.enum(mediaTypeDiscriminants),
16047
+ asset: renderableMediaAssetSchema,
16048
+ identifier: z.never().optional()
16049
+ }).strict();
16050
+ const runtimeMediaSchema = z.union([
16051
+ mediaSchema,
16052
+ externalMediaSchema,
16053
+ assetMediaSchema
15752
16054
  ]);
15753
16055
  //#endregion
16056
+ //#region ../media-core/src/typeGuards.ts
16057
+ function isRecord$12(value) {
16058
+ return Boolean(value) && typeof value === "object" && !Array.isArray(value);
16059
+ }
16060
+ //#endregion
16061
+ //#region ../media-core/src/transformRect.ts
16062
+ function isValidTransformRectStructure(rect) {
16063
+ if (!Array.isArray(rect) || rect.length !== 4) return false;
16064
+ const [left, top, width, height] = rect;
16065
+ return Number.isFinite(left) && Number.isFinite(top) && Number.isFinite(width) && Number.isFinite(height) && left >= 0 && top >= 0 && width > 0 && height > 0;
16066
+ }
16067
+ //#endregion
16068
+ //#region ../media-core/src/aspectTransforms.ts
16069
+ const DEFAULT_RECT_EPSILON = .5;
16070
+ function parseAspectRatioValue(value) {
16071
+ if (typeof value === "number") return Number.isFinite(value) && value > 0 ? value : void 0;
16072
+ if (typeof value !== "string") return void 0;
16073
+ const normalized = value.trim();
16074
+ if (!normalized) return void 0;
16075
+ if (normalized === "auto") return void 0;
16076
+ if (normalized.includes(":") || normalized.includes("/")) {
16077
+ const delimiter = normalized.includes(":") ? ":" : "/";
16078
+ const [wRaw, hRaw] = normalized.split(delimiter);
16079
+ const width = Number(wRaw?.trim());
16080
+ const height = Number(hRaw?.trim());
16081
+ if (!Number.isFinite(width) || !Number.isFinite(height) || width <= 0 || height <= 0) return;
16082
+ return width / height;
16083
+ }
16084
+ const numeric = Number(normalized);
16085
+ if (!Number.isFinite(numeric) || numeric <= 0) return void 0;
16086
+ return numeric;
16087
+ }
16088
+ const MAX_ASPECT_DISTANCE = .25;
16089
+ function resolveMediaTransformForAspect(transform, targetAspect) {
16090
+ if (!transform) return {};
16091
+ const base = {
16092
+ rect: transform.rect,
16093
+ hotspot: transform.hotspot,
16094
+ rotation: transform.rotation
16095
+ };
16096
+ const target = parseAspectRatioValue(targetAspect);
16097
+ const crops = transform.aspectCrops;
16098
+ if (!target || !crops || Object.keys(crops).length === 0) return base;
16099
+ let best = null;
16100
+ let bestDistance = Number.POSITIVE_INFINITY;
16101
+ for (const [key, candidate] of Object.entries(crops)) {
16102
+ const candidateAspect = parseAspectRatioValue(candidate.aspect ?? key);
16103
+ if (!candidateAspect) continue;
16104
+ const distance = Math.abs(Math.log(candidateAspect / target));
16105
+ if (distance < bestDistance) {
16106
+ best = candidate;
16107
+ bestDistance = distance;
16108
+ }
16109
+ }
16110
+ if (!best) return base;
16111
+ if (bestDistance > MAX_ASPECT_DISTANCE) return base;
16112
+ return {
16113
+ rect: best.rect ?? base.rect,
16114
+ hotspot: best.hotspot ?? base.hotspot,
16115
+ rotation: best.rotation ?? base.rotation
16116
+ };
16117
+ }
16118
+ function isValidTransformRect(rect) {
16119
+ return isValidTransformRectStructure(rect);
16120
+ }
16121
+ function isExplicitRectCrop(rect, sourceWidth, sourceHeight, epsilon = DEFAULT_RECT_EPSILON) {
16122
+ if (!isValidTransformRect(rect)) return false;
16123
+ if (!(typeof sourceWidth === "number" && Number.isFinite(sourceWidth) && sourceWidth > 0 && typeof sourceHeight === "number" && Number.isFinite(sourceHeight) && sourceHeight > 0)) return true;
16124
+ const [x, y, width, height] = rect;
16125
+ return !(Math.abs(x) <= epsilon && Math.abs(y) <= epsilon && Math.abs(width - sourceWidth) <= epsilon && Math.abs(height - sourceHeight) <= epsilon);
16126
+ }
16127
+ //#endregion
16128
+ //#region ../media-core/src/rotation.ts
16129
+ function rotateNormalizedPoint(point, rotation) {
16130
+ const x = point.x;
16131
+ const y = point.y;
16132
+ switch (rotation) {
16133
+ case 0: return {
16134
+ x,
16135
+ y
16136
+ };
16137
+ case 90: return {
16138
+ x: 1 - y,
16139
+ y: x
16140
+ };
16141
+ case 180: return {
16142
+ x: 1 - x,
16143
+ y: 1 - y
16144
+ };
16145
+ case 270: return {
16146
+ x: y,
16147
+ y: 1 - x
16148
+ };
16149
+ }
16150
+ }
16151
+ //#endregion
16152
+ //#region ../media-core/src/mediaRefNormalizer.ts
16153
+ function looksLikeUuid(value) {
16154
+ return typeof value === "string" && /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(value);
16155
+ }
16156
+ /**
16157
+ * Strip environment-specific keys from a CMS media object.
16158
+ * @mutates Mutates the passed object in-place.
16159
+ */
16160
+ function deleteEnvSpecificKeys(obj) {
16161
+ delete obj.assetId;
16162
+ delete obj.storageBucket;
16163
+ delete obj.storagePath;
16164
+ delete obj.src;
16165
+ delete obj.mimeType;
16166
+ delete obj.id;
16167
+ }
16168
+ //#endregion
16169
+ //#region ../media-core/src/richTextImageCollector.ts
16170
+ function walkAndCollectImageAttrs(value, out, location = "") {
16171
+ if (Array.isArray(value)) {
16172
+ value.forEach((item, index) => walkAndCollectImageAttrs(item, out, `${location}[${index}]`));
16173
+ return;
16174
+ }
16175
+ if (!isRecord$12(value)) return;
16176
+ if (value.type === "image" && isRecord$12(value.attrs)) out.push({
16177
+ attrs: value.attrs,
16178
+ location: location ? `${location}.attrs` : "attrs"
16179
+ });
16180
+ for (const [key, child] of Object.entries(value)) walkAndCollectImageAttrs(child, out, location ? `${location}.${key}` : key);
16181
+ }
16182
+ //#endregion
15754
16183
  //#region ../theme-core/src/site-styles/rawStyleFields.ts
15755
16184
  const themeV2RuntimeStyleFieldNames = [
15756
16185
  "_frameStyles",
@@ -15838,8 +16267,23 @@ const boxBackgroundSchema = z.discriminatedUnion("type", [
15838
16267
  showVideoOnMobile: z.boolean().nullable().optional()
15839
16268
  })
15840
16269
  ]);
16270
+ /** Application rendering sources are admitted separately from saved references. */
16271
+ const runtimeBoxBackgroundSchema = z.discriminatedUnion("type", [
16272
+ boxBackgroundSchema.options[0],
16273
+ boxBackgroundSchema.options[1],
16274
+ boxBackgroundSchema.options[2].extend({ image: z.union([
16275
+ imageMediaSchema,
16276
+ externalMediaSchema.extend({ type: z.literal("image") }),
16277
+ assetMediaSchema.extend({ type: z.literal("image") })
16278
+ ]).nullable().optional() }),
16279
+ boxBackgroundSchema.options[3].extend({ image: z.union([
16280
+ videoMediaSchema,
16281
+ externalMediaSchema.extend({ type: z.literal("video") }),
16282
+ assetMediaSchema.extend({ type: z.literal("video") })
16283
+ ]).nullable().optional() })
16284
+ ]);
15841
16285
  function normalizeAuthoringOpacity(value) {
15842
- if (!isRecord$12(value)) return value;
16286
+ if (!isRecord$11(value)) return value;
15843
16287
  return {
15844
16288
  ...value,
15845
16289
  opacity: normalizeOpacity(value.opacity)
@@ -15848,7 +16292,7 @@ function normalizeAuthoringOpacity(value) {
15848
16292
  function normalizeOpacity(value) {
15849
16293
  return typeof value === "number" && value > 1 && value <= 100 ? value / 100 : value;
15850
16294
  }
15851
- function isRecord$12(value) {
16295
+ function isRecord$11(value) {
15852
16296
  return typeof value === "object" && value !== null && !Array.isArray(value);
15853
16297
  }
15854
16298
  //#endregion
@@ -30428,133 +30872,6 @@ const linkSchema = z.union([
30428
30872
  entryLinkSchema$1
30429
30873
  ]);
30430
30874
  //#endregion
30431
- //#region ../media-core/src/typeGuards.ts
30432
- function isRecord$11(value) {
30433
- return Boolean(value) && typeof value === "object" && !Array.isArray(value);
30434
- }
30435
- //#endregion
30436
- //#region ../media-core/src/transformRect.ts
30437
- function isValidTransformRectStructure(rect) {
30438
- if (!Array.isArray(rect) || rect.length !== 4) return false;
30439
- const [left, top, width, height] = rect;
30440
- return Number.isFinite(left) && Number.isFinite(top) && Number.isFinite(width) && Number.isFinite(height) && left >= 0 && top >= 0 && width > 0 && height > 0;
30441
- }
30442
- //#endregion
30443
- //#region ../media-core/src/aspectTransforms.ts
30444
- const DEFAULT_RECT_EPSILON = .5;
30445
- function parseAspectRatioValue(value) {
30446
- if (typeof value === "number") return Number.isFinite(value) && value > 0 ? value : void 0;
30447
- if (typeof value !== "string") return void 0;
30448
- const normalized = value.trim();
30449
- if (!normalized) return void 0;
30450
- if (normalized === "auto") return void 0;
30451
- if (normalized.includes(":") || normalized.includes("/")) {
30452
- const delimiter = normalized.includes(":") ? ":" : "/";
30453
- const [wRaw, hRaw] = normalized.split(delimiter);
30454
- const width = Number(wRaw?.trim());
30455
- const height = Number(hRaw?.trim());
30456
- if (!Number.isFinite(width) || !Number.isFinite(height) || width <= 0 || height <= 0) return;
30457
- return width / height;
30458
- }
30459
- const numeric = Number(normalized);
30460
- if (!Number.isFinite(numeric) || numeric <= 0) return void 0;
30461
- return numeric;
30462
- }
30463
- const MAX_ASPECT_DISTANCE = .25;
30464
- function resolveMediaTransformForAspect(transform, targetAspect) {
30465
- if (!transform) return {};
30466
- const base = {
30467
- rect: transform.rect,
30468
- hotspot: transform.hotspot,
30469
- rotation: transform.rotation
30470
- };
30471
- const target = parseAspectRatioValue(targetAspect);
30472
- const crops = transform.aspectCrops;
30473
- if (!target || !crops || Object.keys(crops).length === 0) return base;
30474
- let best = null;
30475
- let bestDistance = Number.POSITIVE_INFINITY;
30476
- for (const [key, candidate] of Object.entries(crops)) {
30477
- const candidateAspect = parseAspectRatioValue(candidate.aspect ?? key);
30478
- if (!candidateAspect) continue;
30479
- const distance = Math.abs(Math.log(candidateAspect / target));
30480
- if (distance < bestDistance) {
30481
- best = candidate;
30482
- bestDistance = distance;
30483
- }
30484
- }
30485
- if (!best) return base;
30486
- if (bestDistance > MAX_ASPECT_DISTANCE) return base;
30487
- return {
30488
- rect: best.rect ?? base.rect,
30489
- hotspot: best.hotspot ?? base.hotspot,
30490
- rotation: best.rotation ?? base.rotation
30491
- };
30492
- }
30493
- function isValidTransformRect(rect) {
30494
- return isValidTransformRectStructure(rect);
30495
- }
30496
- function isExplicitRectCrop(rect, sourceWidth, sourceHeight, epsilon = DEFAULT_RECT_EPSILON) {
30497
- if (!isValidTransformRect(rect)) return false;
30498
- if (!(typeof sourceWidth === "number" && Number.isFinite(sourceWidth) && sourceWidth > 0 && typeof sourceHeight === "number" && Number.isFinite(sourceHeight) && sourceHeight > 0)) return true;
30499
- const [x, y, width, height] = rect;
30500
- return !(Math.abs(x) <= epsilon && Math.abs(y) <= epsilon && Math.abs(width - sourceWidth) <= epsilon && Math.abs(height - sourceHeight) <= epsilon);
30501
- }
30502
- //#endregion
30503
- //#region ../media-core/src/rotation.ts
30504
- function rotateNormalizedPoint(point, rotation) {
30505
- const x = point.x;
30506
- const y = point.y;
30507
- switch (rotation) {
30508
- case 0: return {
30509
- x,
30510
- y
30511
- };
30512
- case 90: return {
30513
- x: 1 - y,
30514
- y: x
30515
- };
30516
- case 180: return {
30517
- x: 1 - x,
30518
- y: 1 - y
30519
- };
30520
- case 270: return {
30521
- x: y,
30522
- y: 1 - x
30523
- };
30524
- }
30525
- }
30526
- //#endregion
30527
- //#region ../media-core/src/mediaRefNormalizer.ts
30528
- function looksLikeUuid(value) {
30529
- return typeof value === "string" && /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(value);
30530
- }
30531
- /**
30532
- * Strip environment-specific keys from a CMS media object.
30533
- * @mutates Mutates the passed object in-place.
30534
- */
30535
- function deleteEnvSpecificKeys(obj) {
30536
- delete obj.assetId;
30537
- delete obj.storageBucket;
30538
- delete obj.storagePath;
30539
- delete obj.src;
30540
- delete obj.mimeType;
30541
- if (looksLikeUuid(obj.id)) delete obj.id;
30542
- }
30543
- //#endregion
30544
- //#region ../media-core/src/richTextImageCollector.ts
30545
- function walkAndCollectImageAttrs(value, out, location = "") {
30546
- if (Array.isArray(value)) {
30547
- value.forEach((item, index) => walkAndCollectImageAttrs(item, out, `${location}[${index}]`));
30548
- return;
30549
- }
30550
- if (!isRecord$11(value)) return;
30551
- if (value.type === "image" && isRecord$11(value.attrs)) out.push({
30552
- attrs: value.attrs,
30553
- location: location ? `${location}.attrs` : "attrs"
30554
- });
30555
- for (const [key, child] of Object.entries(value)) walkAndCollectImageAttrs(child, out, location ? `${location}.${key}` : key);
30556
- }
30557
- //#endregion
30558
30875
  //#region ../blocks/src/system/node/schema.ts
30559
30876
  const transformStepSchema = z.object({
30560
30877
  id: z.string().min(1, "Transform requires an identifier"),
@@ -32252,7 +32569,8 @@ function validationContext(options = {}) {
32252
32569
  const isDraft = options.mode === "draft";
32253
32570
  return {
32254
32571
  allowNull: options.allowNull ?? true,
32255
- allowIncomplete: isDraft || (options.allowIncomplete ?? false)
32572
+ allowIncomplete: isDraft || (options.allowIncomplete ?? false),
32573
+ mode: options.mode ?? "strict"
32256
32574
  };
32257
32575
  }
32258
32576
  function strictValidationContext(options = {}) {
@@ -32626,7 +32944,7 @@ function validateNormalizedFieldValue(plan, value, ctx) {
32626
32944
  case "select": return validateSelectPlan(plan, value);
32627
32945
  case "richText": return isValidRichTextDoc(value) ? [] : [issue(plan, "invalidRichText")];
32628
32946
  case "inlineText": return validateInlineTextPlan(plan, value);
32629
- case "media": return isPlainObject$2(value) && mediaIdentifierSchema.safeParse(value.identifier).success && (value.type === void 0 || typeof value.type === "string" && plan.allowedMediaTypes.includes(value.type)) ? [] : [issue(plan, "invalidMedia")];
32947
+ case "media": return validateMediaPlan(plan, value, ctx);
32630
32948
  case "link": return linkSchema.safeParse(value).success ? [] : [issue(plan, "invalidLink")];
32631
32949
  case "repeater": return validateRepeaterPlan(plan, value, ctx);
32632
32950
  case "group": return validateGroupPlan(plan, value, ctx);
@@ -32634,6 +32952,15 @@ function validateNormalizedFieldValue(plan, value, ctx) {
32634
32952
  default: return assertNever$4(plan);
32635
32953
  }
32636
32954
  }
32955
+ function validateMediaPlan(plan, value, ctx) {
32956
+ if (!isPlainObject$2(value)) return [issue(plan, "invalidMedia")];
32957
+ const allowedKind = value.type === void 0 || typeof value.type === "string" && plan.allowedMediaTypes.includes(value.type);
32958
+ const admitted = ctx.mode === "render" ? runtimeMediaSchema.safeParse({
32959
+ ...value,
32960
+ type: value.type ?? plan.allowedMediaTypes[0]
32961
+ }).success : mediaIdentifierSchema.safeParse(value.identifier).success;
32962
+ return allowedKind && admitted ? [] : [issue(plan, "invalidMedia")];
32963
+ }
32637
32964
  function shouldRequireValue(plan, value, ctx) {
32638
32965
  if (!plan.required || ctx.allowIncomplete) return false;
32639
32966
  if (value === null && ctx.allowNull) return false;
@@ -32758,7 +33085,7 @@ function validateGroupPlan(plan, value, ctx) {
32758
33085
  const childCtx = childValidationContext(plan, ctx);
32759
33086
  const childIssues = selectedGroupChildPlans(plan, value).flatMap((childPlan) => validateNormalizedFieldValue(childPlan, value[childPlan.fieldId], childCtx));
32760
33087
  if (childIssues.length > 0) return childIssues;
32761
- if (plan.valueContract === "boxBackground" && !boxBackgroundSchema.safeParse(value).success) return [issue(plan, "invalidBoxBackground")];
33088
+ if (plan.valueContract === "boxBackground" && !(ctx.mode === "render" ? runtimeBoxBackgroundSchema : boxBackgroundSchema).safeParse(value).success) return [issue(plan, "invalidBoxBackground")];
32762
33089
  return [];
32763
33090
  }
32764
33091
  function childValidationContext(plan, ctx) {
@@ -32858,11 +33185,16 @@ function fieldPlanToObjectPropertyZod(plan, options = {}) {
32858
33185
  default: return assertNever$4(presence);
32859
33186
  }
32860
33187
  }
32861
- /** Validate stored field values without applying editor/write normalization. */
32862
- function fieldPlanToStoredObjectPropertyZod(plan) {
32863
- const ctx = strictValidationContext({ allowNull: false });
33188
+ /** Validate stored or runtime fields without applying editor/write normalization. */
33189
+ function fieldPlanToStrictObjectPropertyZod(plan, mode = "strict") {
33190
+ const ctx = strictValidationContext({
33191
+ allowNull: false,
33192
+ mode
33193
+ });
32864
33194
  const schema = z.any().superRefine((value, zodCtx) => {
32865
- for (const validationIssue of validateNormalizedFieldValue(plan, value, ctx)) addFieldIssueToZodContext(zodCtx, validationIssue, plan.path);
33195
+ validateNormalizedFieldValue(plan, value, ctx).forEach((validationIssue) => {
33196
+ addFieldIssueToZodContext(zodCtx, validationIssue, plan.path);
33197
+ });
32866
33198
  });
32867
33199
  return plan.required ? schema : schema.optional();
32868
33200
  }
@@ -33065,7 +33397,7 @@ function preprocessEmptyValues(content) {
33065
33397
  function compileManifestContentContract(manifestOrFields) {
33066
33398
  const plans = derivePlansFromManifestOrFields(manifestOrFields);
33067
33399
  const strictOptions = { allowNull: false };
33068
- const strictStoredSchema = deriveStrictStoredZodFromPlans(plans);
33400
+ const strictStoredSchema = deriveStrictContentZodFromPlans(plans);
33069
33401
  return Object.freeze({ [compiledManifestContentContract]: Object.freeze({
33070
33402
  plans,
33071
33403
  strictStoredSchema,
@@ -33075,7 +33407,7 @@ function compileManifestContentContract(manifestOrFields) {
33075
33407
  allowIncomplete: true,
33076
33408
  mode: "draft"
33077
33409
  }, normalizeStoredContentValues),
33078
- renderSchema: strictStoredSchema,
33410
+ renderSchema: deriveStrictContentZodFromPlans(plans, "render"),
33079
33411
  findNonPortableMediaPaths: (content) => findNonPortableMediaPaths(plans, content)
33080
33412
  }) });
33081
33413
  }
@@ -33105,9 +33437,8 @@ function deriveContentZodFromPlans(plans, options) {
33105
33437
  for (const plan of plans) shape[plan.fieldId] = fieldPlanToObjectPropertyZod(plan, options);
33106
33438
  return z.object(shape).catchall(z.unknown()).passthrough();
33107
33439
  }
33108
- function deriveStrictStoredZodFromPlans(plans) {
33109
- const shape = {};
33110
- for (const plan of plans) shape[plan.fieldId] = fieldPlanToStoredObjectPropertyZod(plan);
33440
+ function deriveStrictContentZodFromPlans(plans, mode = "strict") {
33441
+ const shape = Object.fromEntries(plans.map((plan) => [plan.fieldId, fieldPlanToStrictObjectPropertyZod(plan, mode)]));
33111
33442
  return z.object(shape).catchall(z.unknown()).passthrough();
33112
33443
  }
33113
33444
  function derivePlansFromManifestOrFields(manifestOrFields) {
@@ -33131,41 +33462,51 @@ function findNonPortableMediaPaths(plans, content) {
33131
33462
  collectNonPortablePlannedMedia(plans, content, [], paths);
33132
33463
  return [...paths].sort();
33133
33464
  }
33134
- function collectNonPortablePlannedMedia(plans, content, parentPath, paths) {
33465
+ /** One descriptor walker covers references and stored portability, including inactive retained fields. */
33466
+ function visitPlannedMedia(plans, content, parentPath, visit) {
33135
33467
  if (!isPlainRecord(content)) return;
33136
- for (const plan of plans) {
33137
- if (!(plan.fieldId in content)) continue;
33468
+ plans.forEach((plan) => {
33469
+ if (!(plan.fieldId in content)) return;
33138
33470
  const value = content[plan.fieldId];
33139
33471
  const path = [...parentPath, plan.fieldId];
33140
33472
  switch (plan.kind) {
33141
33473
  case "media":
33142
- if (isPlainRecord(value) && hasEnvironmentSpecificMediaField(value)) paths.add(formatFieldPath(path));
33143
- break;
33474
+ visit(isPlainRecord(value) ? {
33475
+ ...value,
33476
+ type: value.type ?? plan.allowedMediaTypes[0]
33477
+ } : value, path, "media");
33478
+ return;
33144
33479
  case "richText":
33145
- collectNonPortableRichTextImages(value, path, paths);
33146
- break;
33480
+ visit(value, path, "richText");
33481
+ return;
33147
33482
  case "group":
33148
- collectNonPortablePlannedMedia(selectedGroupChildPlans(plan, value), value, path, paths);
33149
- break;
33483
+ visitPlannedMedia(allGroupChildPlans(plan), value, path, visit);
33484
+ return;
33150
33485
  case "repeater":
33151
- collectNonPortableRepeaterMedia(plan, value, path, paths);
33152
- break;
33153
- case "string":
33154
- case "number":
33486
+ visitRepeatedMedia(plan, value, path, visit);
33487
+ return;
33155
33488
  case "boolean":
33156
33489
  case "inlineText":
33157
33490
  case "link":
33491
+ case "number":
33492
+ case "passthrough":
33158
33493
  case "select":
33159
- case "passthrough": break;
33494
+ case "string": return;
33160
33495
  }
33161
- }
33496
+ });
33162
33497
  }
33163
- function collectNonPortableRepeaterMedia(plan, value, path, paths) {
33498
+ function visitRepeatedMedia(plan, value, path, visit) {
33164
33499
  if (!Array.isArray(value)) return;
33165
33500
  value.forEach((item, index) => {
33166
33501
  if (!isPlainRecord(item)) return;
33167
- const childPlans = plan.repeatedItemVariants ? plan.repeatedItemVariants.find((variant) => variant.typeId === item._type)?.fields : plan.repeatedItemPlan;
33168
- if (childPlans) collectNonPortablePlannedMedia(childPlans, item, [...path, index], paths);
33502
+ const fields = plan.repeatedItemVariants ? plan.repeatedItemVariants.find((variant) => variant.typeId === item._type)?.fields : plan.repeatedItemPlan;
33503
+ if (fields) visitPlannedMedia(fields, item, [...path, index], visit);
33504
+ });
33505
+ }
33506
+ function collectNonPortablePlannedMedia(plans, content, parentPath, paths) {
33507
+ visitPlannedMedia(plans, content, parentPath, (value, path, kind) => {
33508
+ if (kind === "richText") collectNonPortableRichTextImages(value, path, paths);
33509
+ else if (isPlainRecord(value) && (hasEnvironmentSpecificMediaField(value) || hasNonPortablePoster(value))) paths.add(formatFieldPath(path));
33169
33510
  });
33170
33511
  }
33171
33512
  function collectNonPortableRichTextImages(value, path, paths) {
@@ -33180,6 +33521,10 @@ function isPlainRecord(value) {
33180
33521
  function hasEnvironmentSpecificMediaField(value) {
33181
33522
  return "assetId" in value || "storageBucket" in value || "storagePath" in value || "src" in value || "mimeType" in value || looksLikeUuid(value.id);
33182
33523
  }
33524
+ function hasNonPortablePoster(value) {
33525
+ if (value.poster === void 0) return false;
33526
+ return !mediaPosterSchema.safeParse(value.poster).success;
33527
+ }
33183
33528
  //#endregion
33184
33529
  //#region ../blocks/src/system/designCapabilities.ts
33185
33530
  function declareBlockDesignCapability(capability) {
@@ -35165,6 +35510,12 @@ function parseBackgroundOverlay(value) {
35165
35510
  const result = boxBackgroundOverlaySchema.safeParse(value);
35166
35511
  return result.success ? result.data : void 0;
35167
35512
  }
35513
+ /** Rendering accepts explicit application sources separately from stored references. */
35514
+ const runtimeBackgroundContentSchema = runtimeBoxBackgroundSchema;
35515
+ function parseRuntimeBackgroundContent(value) {
35516
+ const result = runtimeBackgroundContentSchema.safeParse(value);
35517
+ return result.success ? result.data : null;
35518
+ }
35168
35519
  //#endregion
35169
35520
  //#region ../blocks/src/system/transforms/registry/_utils/themeColorResolution.ts
35170
35521
  function resolveBackgroundColorValue(value, context) {
@@ -40015,6 +40366,15 @@ const formBlockDefinition = {
40015
40366
  runtime: formBlockRuntime
40016
40367
  };
40017
40368
  //#endregion
40369
+ //#region ../blocks/src/system/helpers/normalizeMailingListBlockContent.ts
40370
+ /** Fill the declared default without admitting or rewriting invalid custom settings. */
40371
+ function normalizeMailingListBlockContent(record) {
40372
+ return record.targeting == null ? {
40373
+ ...record,
40374
+ targeting: DEFAULT_MAILING_LIST_TARGETING
40375
+ } : record;
40376
+ }
40377
+ //#endregion
40018
40378
  //#region ../blocks/src/system/blocks/contact-newsletter.ts
40019
40379
  const contactNewsletterLayout = styledSection({
40020
40380
  children: sectionContainer([el("div", { className: "contact-newsletter-grid" }, [el("div", { className: "contact-newsletter-column contact-newsletter-contact" }, [el("div", { className: "contact-newsletter-copy" }, [text({
@@ -40178,6 +40538,10 @@ const contactNewsletterBlockDefinition = {
40178
40538
  ],
40179
40539
  icon: "MailPlus"
40180
40540
  }),
40541
+ contentNormalizer: {
40542
+ fieldIds: ["targeting"],
40543
+ normalize: normalizeMailingListBlockContent
40544
+ },
40181
40545
  dataSchemas: { form: formDataSchema.optional() },
40182
40546
  dataLoaders: { form: {
40183
40547
  endpoint: "getPublicFormById",
@@ -43866,6 +44230,14 @@ bookingPaymentOptionSchemas.flexibleBalanceConfig;
43866
44230
  bookingPaymentOptionSchemas.paymentCollectionOption;
43867
44231
  const bookingPaymentTermsSchema = bookingPaymentOptionSchemas.paymentTerms;
43868
44232
  bookingPaymentOptionSchemas.paymentOptionsChoice;
44233
+ z.strictObject({
44234
+ currency: z.string().trim().length(3),
44235
+ totalCents: nonNegativeIntegerSchema,
44236
+ dueNowCents: nonNegativeIntegerSchema,
44237
+ laterPaymentAmountsCents: z.array(nonNegativeIntegerSchema),
44238
+ paymentOption: z.union([createBookingPaymentOptionSchemas("strict").paymentCollectionOption, z.strictObject({ kind: z.literal("not_required") })]),
44239
+ promisedDiscountId: z.string().min(1).nullable().optional()
44240
+ });
43869
44241
  //#endregion
43870
44242
  //#region ../blocks/src/system/blocks/shared/bookingPaymentTerms.ts
43871
44243
  const blockBookingPaymentTermsSchema = bookingPaymentTermsSchema;
@@ -44106,6 +44478,10 @@ const publicEventBaseSchema = z.object({
44106
44478
  eventDestinationMode: eventDestinationModeSchema.optional(),
44107
44479
  scheduleScope: eventScheduleScopeSchema$2.optional(),
44108
44480
  recurrence: publicEventRecurrenceSchema.optional(),
44481
+ scheduleSummary: z.object({
44482
+ recurrenceDescription: z.string(),
44483
+ startTime: z.string().regex(/^(?:[01]\d|2[0-3]):[0-5]\d$/).nullable()
44484
+ }).nullable().optional(),
44109
44485
  id: z.string(),
44110
44486
  occurrenceId: z.string(),
44111
44487
  seriesId: z.string(),
@@ -44114,7 +44490,7 @@ const publicEventBaseSchema = z.object({
44114
44490
  content: z.object({ body: RichTextDocSchema.nullable().optional() }).optional(),
44115
44491
  presentation: z.object({
44116
44492
  summary: z.string().nullable().optional(),
44117
- image: mediaSchema.nullable().optional()
44493
+ image: runtimeMediaSchema.nullable().optional()
44118
44494
  }).optional(),
44119
44495
  slug: z.string(),
44120
44496
  path: z.string().nullable(),
@@ -44516,67 +44892,266 @@ const occurrenceContextSchema = z.object({
44516
44892
  overrides: z.record(z.string(), z.unknown()).nullable().optional()
44517
44893
  }).nullable();
44518
44894
  //#endregion
44519
- //#region ../blocks/src/system/runtime/api/readAuthority.ts
44520
- const browserReadAuthoritySchema = z.discriminatedUnion("kind", [z.object({ kind: z.literal("published") }).strict(), z.object({
44521
- kind: z.literal("preview"),
44522
- token: z.string().min(1)
44523
- }).strict()]);
44524
- const unsupportedEventRegistrationPageSurfaceSubjectSchema = z.object({
44525
- kind: z.literal("unsupported"),
44526
- reason: z.enum([
44527
- "unsupportedSubject",
44528
- "invalidEventSeriesId",
44529
- "invalidEventSeriesBookingScope",
44530
- "invalidEventOfferingId",
44531
- "invalidEventOfferingBookingScope",
44532
- "invalidEventOfferingCatalogue"
44533
- ])
44534
- });
44535
- const eventRegistrationPageSurfaceSubjectSchema = z.unknown().transform((value, ctx) => {
44536
- const unsupported = unsupportedEventRegistrationPageSurfaceSubjectSchema.safeParse(value);
44537
- if (unsupported.success) return unsupported.data;
44538
- const parsed = parseEventPageSurfaceSubject(value);
44539
- if (parsed.ok) return parsed.value;
44540
- ctx.addIssue({
44541
- code: z.ZodIssueCode.custom,
44542
- message: `Invalid event page-surface subject (${parsed.error.kind})`
44543
- });
44544
- return z.NEVER;
44545
- });
44546
- function resolveEventRegistrationScope(context) {
44547
- const occurrenceSeriesId = context.occurrenceContext?.seriesId;
44548
- if (occurrenceSeriesId) return {
44549
- kind: "occurrenceSeries",
44550
- seriesId: occurrenceSeriesId
44895
+ //#region ../blocks/src/system/runtime/nodes/events/domain/capacity.ts
44896
+ /**
44897
+ * Compute remaining spots for an event from `availableSpots` with a fallback
44898
+ * to `capacity - registeredCount`. Returns `{ available: null, isFull: false }`
44899
+ * for uncapped events so call sites can render no capacity indicator at all.
44900
+ *
44901
+ * The single source of truth for capacity math across event cards, agenda
44902
+ * rows, and timetable cells — keep wording variations in the call sites.
44903
+ */
44904
+ function getEventAvailability(event) {
44905
+ if (event.capacity == null) return {
44906
+ available: null,
44907
+ isFull: false
44551
44908
  };
44552
- if (context.subject?.kind === "eventSeries") return {
44553
- kind: "pageSurfaceSeries",
44554
- seriesId: context.subject.eventSeriesId
44909
+ const available = event.availableSpots ?? event.capacity - event.registeredCount;
44910
+ if (available <= 0) return {
44911
+ available: 0,
44912
+ isFull: true
44555
44913
  };
44556
- if (context.subject?.kind === "eventOffering") return {
44557
- kind: "pageSurfaceOffering",
44558
- offeringId: context.subject.eventOfferingId,
44559
- seriesIds: context.subject.catalogueEventSeriesIds
44914
+ return {
44915
+ available,
44916
+ isFull: false
44560
44917
  };
44561
- if (context.subject?.kind === "unsupported") return {
44562
- kind: "unsupportedPageSurfaceSubject",
44563
- reason: context.subject.reason
44918
+ }
44919
+ /**
44920
+ * Pick the capacity row a booking decision should read: the selected
44921
+ * attendance mode's availability when the visitor has chosen one and the
44922
+ * event publishes it, otherwise the event-level aggregate.
44923
+ *
44924
+ * The mode row is used wholesale. A mode with `capacity: null` on a capped
44925
+ * event is uncapped *by design*: the registration write path applies the
44926
+ * same rule via `getEffectiveOccurrenceCapacityForAttendanceMode`, so falling
44927
+ * back to the event's `availableSpots` here would block bookings the server
44928
+ * accepts. (Waitlist claims are still occurrence-scoped; see issue 1377.)
44929
+ */
44930
+ function selectEventCapacitySource(event, attendanceMode) {
44931
+ if (!attendanceMode) return event;
44932
+ return (event.attendanceModes ?? []).find((mode) => mode.mode === attendanceMode) ?? event;
44933
+ }
44934
+ /**
44935
+ * Whether the visitor's next step is the waitlist: the source is full *and*
44936
+ * a waitlist is open. The one place this is decided — cards, rows, the
44937
+ * wizard's steps and its action bar all route through here so they agree,
44938
+ * including for producers that omit `availableSpots` and rely on the
44939
+ * `capacity - registeredCount` fallback.
44940
+ */
44941
+ function canJoinEventWaitlist(event, options) {
44942
+ const source = selectEventCapacitySource(event, options?.attendanceMode);
44943
+ return getEventAvailability(source).isFull && source.waitlistEnabled === true;
44944
+ }
44945
+ /**
44946
+ * Remaining places at or below which an event reads as scarce and the exact
44947
+ * count is worth showing. Above it, no number is shown at all: "147 places
44948
+ * left" is not something a visitor acts on, and it is the figure most likely
44949
+ * to be stale on a cached page.
44950
+ *
44951
+ * max( min(3, ceil(capacity * 0.25)), ceil(capacity * 0.05) )
44952
+ *
44953
+ * The 25% arm stops small groups shouting early (3 of 4 is not urgency); the
44954
+ * 5% floor stops large events staying silent until three remain.
44955
+ * capacity 4 -> 1, 12 -> 3, 40 -> 3, 100 -> 5, 200 -> 10.
44956
+ */
44957
+ function lowCapacityThreshold(capacity) {
44958
+ return Math.max(Math.min(3, Math.ceil(capacity * .25)), Math.ceil(capacity * .05));
44959
+ }
44960
+ function getRemainingPlaces(source) {
44961
+ const { available, isFull } = getEventAvailability(source);
44962
+ if (available === null || source.capacity === null) return { kind: "unlimited" };
44963
+ if (isFull) return {
44964
+ kind: "full",
44965
+ waitlist: canJoinEventWaitlist(source)
44564
44966
  };
44565
- return { kind: "unscoped" };
44967
+ if (available <= lowCapacityThreshold(source.capacity)) return {
44968
+ kind: "low",
44969
+ remaining: available
44970
+ };
44971
+ return { kind: "open" };
44566
44972
  }
44567
- function resolveEventRegistrationScopedEvents(events, context) {
44568
- const scope = resolveEventRegistrationScope(context);
44569
- switch (scope.kind) {
44570
- case "occurrenceSeries":
44571
- case "pageSurfaceSeries": return events.filter((event) => event.seriesId === scope.seriesId);
44572
- case "pageSurfaceOffering": {
44573
- const seriesIds = new Set(scope.seriesIds);
44574
- return events.filter((event) => seriesIds.has(event.seriesId));
44575
- }
44576
- case "unsupportedPageSurfaceSubject": return [];
44577
- case "unscoped": return [...events];
44973
+ /**
44974
+ * The one place a remaining-places count becomes text. Returns `null` when
44975
+ * nothing should be shown: uncapped events, and capped events above the
44976
+ * scarcity threshold. Surfaces that need their own full-state wording pass
44977
+ * it in rather than re-deriving the count.
44978
+ */
44979
+ function formatRemainingPlaces(source, wording = {}) {
44980
+ const remaining = getRemainingPlaces(source);
44981
+ switch (remaining.kind) {
44982
+ case "unlimited":
44983
+ case "open": return null;
44984
+ case "low": return formatPlacesLeft(remaining.remaining, wording.compact === true);
44985
+ case "full": return remaining.waitlist ? wording.waitlist ?? "Waitlist" : wording.full ?? "Full";
44578
44986
  }
44579
44987
  }
44988
+ function formatPlacesLeft(count, compact) {
44989
+ if (compact) return `${count} left`;
44990
+ return count === 1 ? "1 place left" : `${count} places left`;
44991
+ }
44992
+ function formatTicketTypeRemaining(remainingCapacity) {
44993
+ if (remainingCapacity === null || remainingCapacity > 3) return null;
44994
+ if (remainingCapacity <= 0) return "None left for this ticket type";
44995
+ return `${formatPlacesLeft(remainingCapacity, false)} for this ticket type`;
44996
+ }
44997
+ /**
44998
+ * Which capacity figure wins when they disagree.
44999
+ *
45000
+ * For a hybrid event that publishes both attendance-mode rows, **the mode
45001
+ * rows are authoritative and the event-level aggregate is only a summary**.
45002
+ * The registration write path admits or refuses per mode
45003
+ * (`getEffectiveOccurrenceCapacityForAttendanceMode` on the server), so the
45004
+ * aggregate can read full while a mode still has room, and can read open
45005
+ * while the mode the visitor wants is full. Believing the aggregate on a
45006
+ * card is how a "places available" event turns into a dead end at step two.
45007
+ *
45008
+ * Everything else — single-mode events, events that publish no rows, and the
45009
+ * aggregate-only helpers {@link getEventAvailability} /
45010
+ * {@link getRemainingPlaces} that existing callers rely on — keeps reading
45011
+ * the aggregate, unchanged.
45012
+ */
45013
+ function findHybridModeRows(event) {
45014
+ if (event.deliveryMode !== "hybrid") return null;
45015
+ const rows = event.attendanceModes ?? [];
45016
+ const inPerson = rows.find((row) => row.mode === "in_person");
45017
+ const online = rows.find((row) => row.mode === "online");
45018
+ return inPerson && online ? {
45019
+ inPerson,
45020
+ online
45021
+ } : null;
45022
+ }
45023
+ /** Openness rank; higher means more room. A full mode with a waitlist beats one without. */
45024
+ function remainingPlacesRank(remaining) {
45025
+ switch (remaining.kind) {
45026
+ case "unlimited": return 4;
45027
+ case "open": return 3;
45028
+ case "low": return 2;
45029
+ case "full": return remaining.waitlist ? 1 : 0;
45030
+ }
45031
+ }
45032
+ function isMoreOpen(candidate, than) {
45033
+ const rank = remainingPlacesRank(candidate);
45034
+ const otherRank = remainingPlacesRank(than);
45035
+ if (rank !== otherRank) return rank > otherRank;
45036
+ return candidate.kind === "low" && than.kind === "low" ? candidate.remaining > than.remaining : false;
45037
+ }
45038
+ /** True when `candidate` has less room left than `than`. */
45039
+ function isScarcer(candidate, than) {
45040
+ return isMoreOpen(than, candidate);
45041
+ }
45042
+ const ATTENDANCE_MODE_LABEL = {
45043
+ in_person: "In person",
45044
+ online: "Online"
45045
+ };
45046
+ /** Wording for a mode with room to spare, where the count is deliberately withheld. */
45047
+ const PLACES_AVAILABLE = "Places available";
45048
+ /**
45049
+ * The browse-surface label for an event's remaining places. Single-mode
45050
+ * events get exactly the aggregate label {@link formatRemainingPlaces}
45051
+ * produces, and so does a hybrid event whose two modes tell the same story.
45052
+ * When the modes differ, both sides are named: "available" on a card is a
45053
+ * promise the registration wizard would otherwise have to break.
45054
+ *
45055
+ * Hybrid examples, in person full with 3 online places left:
45056
+ * default -> "Online: 3 places left · In person: Full"
45057
+ * { full: 'Sold out' } -> "Online: 3 places left · In person: Sold out"
45058
+ * { compact: true } -> "Online: 3 left"
45059
+ */
45060
+ function formatEventRemainingPlaces(event, wording = {}) {
45061
+ const rows = findHybridModeRows(event);
45062
+ if (!rows) return formatRemainingPlaces(event, wording);
45063
+ const inPerson = formatRemainingPlaces(rows.inPerson, wording);
45064
+ const online = formatRemainingPlaces(rows.online, wording);
45065
+ if (inPerson === online) return inPerson;
45066
+ if (wording.compact === true) return formatCompactHybridPlaces(rows, wording);
45067
+ return formatBothHybridSides(rows, {
45068
+ inPerson,
45069
+ online
45070
+ });
45071
+ }
45072
+ /**
45073
+ * Both sides, the one with room first: that is the half a visitor can act on,
45074
+ * and it should not arrive after a full stop's worth of bad news.
45075
+ */
45076
+ function formatBothHybridSides(rows, labels) {
45077
+ 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(" · ");
45078
+ }
45079
+ function namedModePlaces(mode, label) {
45080
+ return `${ATTENDANCE_MODE_LABEL[mode]}: ${label ?? PLACES_AVAILABLE}`;
45081
+ }
45082
+ /**
45083
+ * Pills and timetable cells have room for one short phrase, so they name the
45084
+ * single mode that carries news rather than listing both: the scarce side
45085
+ * when there is a count to show, and otherwise the side that has closed —
45086
+ * "In person: Full", never "Online only", which reads as a statement about
45087
+ * how the event is delivered rather than about its remaining places.
45088
+ */
45089
+ function formatCompactHybridPlaces(rows, wording) {
45090
+ const inPerson = getRemainingPlaces(rows.inPerson);
45091
+ const online = getRemainingPlaces(rows.online);
45092
+ if (inPerson.kind === "full" && online.kind === "full") return formatRemainingPlaces(inPerson.waitlist ? rows.inPerson : rows.online, wording);
45093
+ if (inPerson.kind === "full" || online.kind === "full") {
45094
+ const fullSide = inPerson.kind === "full" ? "in_person" : "online";
45095
+ const openSide = inPerson.kind === "full" ? "online" : "in_person";
45096
+ const openLabel = formatRemainingPlaces(rowFor(rows, openSide), wording);
45097
+ return openLabel !== null ? namedModePlaces(openSide, openLabel) : namedModePlaces(fullSide, formatRemainingPlaces(rowFor(rows, fullSide), wording));
45098
+ }
45099
+ const scarcerSide = isScarcer(online, inPerson) ? "online" : "in_person";
45100
+ return namedModePlaces(scarcerSide, formatRemainingPlaces(rowFor(rows, scarcerSide), wording));
45101
+ }
45102
+ function rowFor(rows, mode) {
45103
+ return mode === "online" ? rows.online : rows.inPerson;
45104
+ }
45105
+ //#endregion
45106
+ //#region ../blocks/src/system/runtime/nodes/event-registration/registrationDisplay.ts
45107
+ const registrationDisplaySchema = z.object({
45108
+ venue: z.enum(["shown", "hidden"]),
45109
+ placesLeft: z.enum(["shown", "hidden"])
45110
+ });
45111
+ function resolveRegistrationDisplay(source) {
45112
+ return {
45113
+ venue: source.showVenue === false ? "hidden" : "shown",
45114
+ placesLeft: source.showCapacity === false ? "hidden" : "shown"
45115
+ };
45116
+ }
45117
+ /** Venue stays in domain data; views receive only the permitted presentation. */
45118
+ function shownVenue(event, display) {
45119
+ return display.venue === "shown" ? event.venue?.name ?? null : null;
45120
+ }
45121
+ /** Full/waitlist explanations remain visible even when optional counts are hidden. */
45122
+ function placesLeftLabel(source, display, wording = {}) {
45123
+ if ("kind" in source) return ticketPlacesLabel(source.remaining, display);
45124
+ if (source.deliveryMode === "hybrid" && source.attendanceModes?.length) return hybridPlacesLabel(source, display, wording);
45125
+ return capacityPlacesLabel(source, display, wording);
45126
+ }
45127
+ function capacityPlacesLabel(source, display, wording) {
45128
+ const remaining = getRemainingPlaces(source);
45129
+ if (display.placesLeft === "hidden" && remaining.kind !== "full") return null;
45130
+ switch (remaining.kind) {
45131
+ case "full": return formatRemainingPlaces(source, wording);
45132
+ case "low": return formatRemainingPlaces(source, wording);
45133
+ case "unlimited": return wording.unlimited ?? null;
45134
+ case "open": return wording.open ?? null;
45135
+ default: return assertNever$6(remaining);
45136
+ }
45137
+ }
45138
+ function hybridPlacesLabel(event, display, wording) {
45139
+ if (display.placesLeft === "shown") return formatEventRemainingPlaces(event, wording);
45140
+ return (event.attendanceModes ?? []).flatMap((mode) => {
45141
+ const label = placesLeftLabel(mode, display, wording);
45142
+ return label ? [`${mode.mode === "online" ? "Online" : "In person"}: ${label}`] : [];
45143
+ }).join(" · ") || null;
45144
+ }
45145
+ function ticketPlacesLabel(remaining, display) {
45146
+ if (remaining !== null && remaining > 0 && display.placesLeft === "hidden") return null;
45147
+ return formatTicketTypeRemaining(remaining);
45148
+ }
45149
+ //#endregion
45150
+ //#region ../blocks/src/system/runtime/api/readAuthority.ts
45151
+ const browserReadAuthoritySchema = z.discriminatedUnion("kind", [z.object({ kind: z.literal("published") }).strict(), z.object({
45152
+ kind: z.literal("preview"),
45153
+ token: z.string().min(1)
45154
+ }).strict()]);
44580
45155
  //#endregion
44581
45156
  //#region ../calendar/src/grid.ts
44582
45157
  const MONTH_NAMES = [
@@ -44664,6 +45239,11 @@ function buildCalendarViewport(input) {
44664
45239
  //#endregion
44665
45240
  //#region ../blocks/src/system/runtime/nodes/events/domain/dates.ts
44666
45241
  /**
45242
+ * Date and time presentation for events: the single place an event's
45243
+ * timestamps become strings or date buckets. Calendar-grid primitives are
45244
+ * re-exported from @riverbankcms/calendar so block code has one import.
45245
+ */
45246
+ /**
44667
45247
  * The English locale a site's public dates are written in, read from the
44668
45248
  * site's own time zone (Will, 2026-09-26: "date style from site time zone").
44669
45249
  *
@@ -44683,6 +45263,28 @@ function eventDateLocale(siteTimeZone) {
44683
45263
  if (siteTimeZone === "Pacific/Auckland" || siteTimeZone === "Pacific/Chatham") return "en-NZ";
44684
45264
  return "en-US";
44685
45265
  }
45266
+ function formatEventCompactDateRail(isoTimestamp, options, letterCase = "capitals") {
45267
+ const locale = options?.locale ?? "en-US";
45268
+ const dateTime = toScheduledBookingDateTime({
45269
+ startsAt: isoTimestamp,
45270
+ timeZone: toBookingTimeZone(options?.timeZone ?? "UTC"),
45271
+ locale
45272
+ });
45273
+ const cased = (value) => letterCase === "capitals" ? value.toLocaleUpperCase(locale) : value;
45274
+ const weekday = cased(formatBookingDate(dateTime, { weekday: "short" }));
45275
+ const month = cased(formatBookingDate(dateTime, { month: "short" }).slice(0, 3));
45276
+ return {
45277
+ weekday,
45278
+ day: formatBookingDate(dateTime, { day: "numeric" }),
45279
+ month,
45280
+ label: formatBookingDate(dateTime, {
45281
+ weekday: "long",
45282
+ year: "numeric",
45283
+ month: "long",
45284
+ day: "numeric"
45285
+ })
45286
+ };
45287
+ }
44686
45288
  //#endregion
44687
45289
  //#region ../blocks/src/system/runtime/nodes/events/domain/destination.ts
44688
45290
  function isRecord$8(value) {
@@ -44702,6 +45304,15 @@ function resolveRuntimeEventDestination(event) {
44702
45304
  };
44703
45305
  }
44704
45306
  //#endregion
45307
+ //#region ../blocks/src/system/runtime/nodes/events/domain/pricing.ts
45308
+ /**
45309
+ * Format an event's price directly from its pricing value (without the full
45310
+ * event). Same output as {@link formatEventPrice}.
45311
+ */
45312
+ function formatEventPriceDisplay(pricing) {
45313
+ return formatEventPricing(pricing, { decimals: "auto" });
45314
+ }
45315
+ //#endregion
44705
45316
  //#region ../blocks/src/system/runtime/nodes/events/domain/listWeek.ts
44706
45317
  /** Civil weeks use the site zone, including weeks containing a clock change. */
44707
45318
  function buildEventListWeek(anchor, startOfWeek, timeZone) {
@@ -44736,6 +45347,642 @@ function eventListWindowEvents(events, window) {
44736
45347
  });
44737
45348
  }
44738
45349
  //#endregion
45350
+ //#region ../blocks/src/system/runtime/nodes/event-registration/registrationDerivations.ts
45351
+ function eventMatchesOccurrenceId(event, occurrenceId) {
45352
+ return event.registrationSubject.kind === "occurrence" && (event.registrationSubject.occurrenceId === occurrenceId || event.selectionId === occurrenceId);
45353
+ }
45354
+ function findPreSelectedEvent(input) {
45355
+ const occurrenceContext = input.occurrenceContext;
45356
+ if (!occurrenceContext) return input.scopedEvents.length === 1 ? input.scopedEvents[0] ?? null : null;
45357
+ const contextDate = getBookingLocalDateKey(toScheduledBookingDateTime({
45358
+ startsAt: occurrenceContext.startsAt,
45359
+ timeZone: occurrenceContext.timeZone
45360
+ }));
45361
+ return input.scopedEvents.find((event) => eventMatchesOccurrenceId(event, occurrenceContext.id) || getBookingLocalDateKey(toScheduledBookingDateTime({
45362
+ startsAt: event.startsAt,
45363
+ timeZone: event.timeZone
45364
+ })) === contextDate) ?? null;
45365
+ }
45366
+ function resolveEventRequiresPayment(input) {
45367
+ if (input.eligibility) return input.eligibility.requiresPayment;
45368
+ if (input.selectedTicketType) return input.selectedTicketType.priceAmount > 0;
45369
+ if (!input.event) return false;
45370
+ return publicEventPricingType(input.event.pricing) === "paid";
45371
+ }
45372
+ //#endregion
45373
+ //#region ../blocks/src/system/runtime/nodes/event-registration/utils.ts
45374
+ /**
45375
+ * The locale a site's booking dates are written in: the one the site's time
45376
+ * zone places it in (`eventDateLocale`, shared with event cards), or British
45377
+ * English when the site names no zone.
45378
+ */
45379
+ function bookingDateLocale(siteTimeZone) {
45380
+ return siteTimeZone ? eventDateLocale(siteTimeZone) : "en-GB";
45381
+ }
45382
+ function bookingInstant(startsAt, input) {
45383
+ return toScheduledBookingDateTime({
45384
+ startsAt,
45385
+ timeZone: toBookingTimeZone(input.timeZone),
45386
+ locale: bookingDateLocale(input.siteTimeZone)
45387
+ });
45388
+ }
45389
+ /** "7:00pm": a twelve-hour clock with a lowercase, unspaced day period. */
45390
+ function bookingClock(startsAt, input) {
45391
+ return formatBookingTime(bookingInstant(startsAt, input), {
45392
+ hour: "numeric",
45393
+ minute: "2-digit",
45394
+ hour12: true
45395
+ }).replace(/\s*([ap])\.?\s?m\.?$/i, (_match, period) => `${period.toLowerCase()}m`);
45396
+ }
45397
+ /** "7:00pm – 8:15pm", or just the start when there's no distinct end. */
45398
+ function clockRange(input) {
45399
+ const start = bookingClock(input.startsAt, input);
45400
+ const end = input.endsAt ? bookingClock(input.endsAt, input) : null;
45401
+ return end && end !== start ? `${start} – ${end}` : start;
45402
+ }
45403
+ /** A stored local clock has no date or DST offset; UTC preserves its digits. */
45404
+ function formatBookingScheduleSummary(event) {
45405
+ const summary = event.scheduleSummary;
45406
+ if (!summary) return null;
45407
+ const time = summary.startTime ? formatBookingWhen({
45408
+ startsAt: `1970-01-01T${summary.startTime}:00Z`,
45409
+ timeZone: "UTC",
45410
+ siteTimeZone: event.siteTimeZone
45411
+ }, "time") : null;
45412
+ return [summary.recurrenceDescription, time].filter(Boolean).join(" · ");
45413
+ }
45414
+ /** The one booking date formatter; every variant derives from it. */
45415
+ function formatBookingWhen(input, variant) {
45416
+ const instant = bookingInstant(input.startsAt, input);
45417
+ const day = formatBookingDate(instant, {
45418
+ weekday: "short",
45419
+ day: "numeric",
45420
+ month: "short"
45421
+ });
45422
+ switch (variant) {
45423
+ case "day": return day;
45424
+ case "dayLong": return formatBookingDate(instant, {
45425
+ weekday: "long",
45426
+ day: "numeric",
45427
+ month: "long"
45428
+ });
45429
+ case "month": return formatBookingDate(instant, { month: "long" });
45430
+ case "monthYear": return formatBookingDate(instant, {
45431
+ month: "long",
45432
+ year: "numeric"
45433
+ });
45434
+ case "time": return bookingClock(input.startsAt, input);
45435
+ case "timeRange": return clockRange(input);
45436
+ case "short": return `${day}, ${bookingClock(input.startsAt, input)}`;
45437
+ case "long": return `${day} · ${clockRange(input)}`;
45438
+ }
45439
+ }
45440
+ /** A single instant in the short form, e.g. "Mon 5 Oct, 7:00pm". */
45441
+ function formatEventDateTime(isoTimestamp, options) {
45442
+ return formatBookingWhen({
45443
+ startsAt: isoTimestamp,
45444
+ timeZone: options?.timeZone,
45445
+ siteTimeZone: options?.siteTimeZone
45446
+ }, "short");
45447
+ }
45448
+ /** The title an occurrence was given on its own, when it overrides the event's. */
45449
+ function occurrenceTitleOverride(occurrence) {
45450
+ const title = occurrence?.overrides?.title;
45451
+ return typeof title === "string" && title.trim() ? title.trim() : null;
45452
+ }
45453
+ /** The event as this page names it: an occurrence's own title wins. */
45454
+ function withOccurrenceTitle(event, occurrence) {
45455
+ const title = occurrenceTitleOverride(occurrence);
45456
+ return event && title ? {
45457
+ ...event,
45458
+ title
45459
+ } : event;
45460
+ }
45461
+ function courseSchedule(sessions, siteTimeZone) {
45462
+ const first = sessions[0];
45463
+ if (!first) return null;
45464
+ const last = sessions[sessions.length - 1] ?? first;
45465
+ const firstLabel = formatEventDateTime(first.startsAt, {
45466
+ timeZone: first.timeZone,
45467
+ siteTimeZone
45468
+ });
45469
+ if (last.startsAt === first.startsAt) return `${sessions.length} session${sessions.length === 1 ? "" : "s"} · ${firstLabel}`;
45470
+ const lastLabel = formatEventDateTime(last.startsAt, {
45471
+ timeZone: last.timeZone,
45472
+ siteTimeZone
45473
+ });
45474
+ return `${sessions.length} sessions · ${firstLabel} – ${lastLabel}`;
45475
+ }
45476
+ /**
45477
+ * When a booking happens, in one line: the occurrence's date and time, or a
45478
+ * course's session count and first and last dates. `short` by default (bar,
45479
+ * confirmation); `long` adds the end time (card, panel header).
45480
+ */
45481
+ function formatRegistrationSchedule(event, occurrence, variant = "short") {
45482
+ if (event.registrationSubject.kind === "wholeSeries") return courseSchedule(event.registrationSubject.sessions, event.siteTimeZone);
45483
+ const input = scheduleInput(event, occurrence ?? null);
45484
+ return input ? formatBookingWhen(input, variant) : null;
45485
+ }
45486
+ /** The occurrence's own times when the page names one, otherwise the event's. */
45487
+ function scheduleInput(event, occurrence) {
45488
+ const times = occurrence?.startsAt ? {
45489
+ startsAt: occurrence.startsAt,
45490
+ endsAt: occurrence.endsAt ?? null
45491
+ } : {
45492
+ startsAt: event.startsAt,
45493
+ endsAt: event.endsAt
45494
+ };
45495
+ if (!times.startsAt) return null;
45496
+ return {
45497
+ ...times,
45498
+ timeZone: occurrence?.timeZone ?? event.timeZone ?? null,
45499
+ siteTimeZone: event.siteTimeZone
45500
+ };
45501
+ }
45502
+ //#endregion
45503
+ //#region ../blocks/src/system/runtime/nodes/event-registration/wizardSteps.ts
45504
+ function registrationRoute(input) {
45505
+ const base = input.chooseEvent ? [
45506
+ "select-event",
45507
+ "select-tickets",
45508
+ "enter-details"
45509
+ ] : ["select-tickets", "enter-details"];
45510
+ switch (input.payment) {
45511
+ case "none": return base;
45512
+ case "options": return [...base, "select-payment"];
45513
+ case "optionsAndPayment": return [
45514
+ ...base,
45515
+ "select-payment",
45516
+ "payment"
45517
+ ];
45518
+ }
45519
+ }
45520
+ const REGISTRATION_STEP_LABELS = [
45521
+ "Event",
45522
+ "Date",
45523
+ "Tickets",
45524
+ "Places",
45525
+ "Your details",
45526
+ "How you’ll pay",
45527
+ "Payment"
45528
+ ];
45529
+ /** Several dates of one series are a date choice; anything else is an event choice. */
45530
+ function getEventSelectionMode(events) {
45531
+ const firstSeriesId = events[0]?.seriesId;
45532
+ return events.length > 1 && firstSeriesId && events.every((event) => event.seriesId === firstSeriesId) ? "date" : "event";
45533
+ }
45534
+ /** The label facts for a surface: what the visitor chooses from, and what they book. */
45535
+ function registrationStepLabelContext(scopedEvents, bookedEvent) {
45536
+ return {
45537
+ eventSelection: getEventSelectionMode(scopedEvents),
45538
+ booksWholeSeries: bookedEvent?.registrationSubject.kind === "wholeSeries"
45539
+ };
45540
+ }
45541
+ /** One label owner for the live progress and the server placeholder. */
45542
+ function registrationStepLabel(step, context) {
45543
+ switch (step) {
45544
+ case "select-event": return context.eventSelection === "date" ? "Date" : "Event";
45545
+ case "select-tickets": return context.booksWholeSeries ? "Places" : "Tickets";
45546
+ case "enter-details": return "Your details";
45547
+ case "select-payment": return "How you’ll pay";
45548
+ case "payment": return "Payment";
45549
+ }
45550
+ }
45551
+ function bookingButtonText(buttonText) {
45552
+ return buttonText?.trim() || "Book your place";
45553
+ }
45554
+ function placesLabel(event, input, compact) {
45555
+ return placesLeftLabel(selectEventCapacitySource(event, input.attendanceMode), input.display, { compact });
45556
+ }
45557
+ function isFull(event, mode) {
45558
+ return formatRemainingPlaces(selectEventCapacitySource(event, mode), {
45559
+ full: "full",
45560
+ waitlist: "full"
45561
+ }) === "full";
45562
+ }
45563
+ function priceLabel(event) {
45564
+ return formatEventPriceDisplay(event.pricing).trim() || null;
45565
+ }
45566
+ /** The lowest price a display can mean; null when it names no paid amount. */
45567
+ function lowestPaidAmount(display) {
45568
+ switch (display.kind) {
45569
+ case "exact":
45570
+ case "from": return display.amount;
45571
+ case "range": return display.minAmount;
45572
+ case "mixed": return display.paidFromAmount;
45573
+ case "free":
45574
+ case "unavailable":
45575
+ case "external": return null;
45576
+ }
45577
+ }
45578
+ /**
45579
+ * One price for a set of dates: the shared label when every date costs the
45580
+ * same, otherwise "From" the lowest paid amount, through the one formatter.
45581
+ */
45582
+ function seriesPriceLabel(events) {
45583
+ const firstEvent = events[0];
45584
+ if (!firstEvent) return null;
45585
+ const displays = events.map((event) => publicEventPricingDisplay(event.pricing));
45586
+ const labels = new Set(displays.map((display) => formatBookingPriceDisplay(display, { decimals: "auto" })));
45587
+ if (labels.size === 1) return [...labels][0] || null;
45588
+ const lowest = displays.flatMap((display) => {
45589
+ const amount = lowestPaidAmount(display);
45590
+ return amount === null || !("currency" in display) ? [] : [{
45591
+ amount,
45592
+ currency: display.currency
45593
+ }];
45594
+ }).reduce((best, candidate) => best === null || candidate.amount < best.amount ? candidate : best, null);
45595
+ return lowest ? formatBookingPriceDisplay({
45596
+ kind: "from",
45597
+ amount: lowest.amount,
45598
+ currency: lowest.currency
45599
+ }, { decimals: "auto" }) : priceLabel(firstEvent);
45600
+ }
45601
+ function dateTile(event) {
45602
+ if (event.registrationSubject.kind === "wholeSeries" || !event.startsAt) return null;
45603
+ const rail = formatEventCompactDateRail(event.startsAt, { timeZone: event.timeZone });
45604
+ return {
45605
+ month: rail.month,
45606
+ day: rail.day
45607
+ };
45608
+ }
45609
+ /** "Mon 5 Oct": the compact date the chips and the bar share. */
45610
+ function formatBookingShortDate(event) {
45611
+ return formatBookingWhen(event, "day");
45612
+ }
45613
+ function byStart(left, right) {
45614
+ return Date.parse(left.startsAt) - Date.parse(right.startsAt);
45615
+ }
45616
+ function singleEntry(event, input) {
45617
+ const isCourse = event.registrationSubject.kind === "wholeSeries";
45618
+ return {
45619
+ kind: "single",
45620
+ title: event.title,
45621
+ when: formatRegistrationSchedule(event, null, "long"),
45622
+ shortWhen: formatRegistrationSchedule(event),
45623
+ venue: shownVenue(event, input.display),
45624
+ price: priceLabel(event),
45625
+ places: placesLabel(event, input, false),
45626
+ tile: dateTile(event),
45627
+ action: isCourse ? `Book the ${input.noun.label}` : bookingButtonText(input.buttonText)
45628
+ };
45629
+ }
45630
+ function chooseDateEntry(input) {
45631
+ const ordered = [...input.events].sort(byStart);
45632
+ const first = ordered[0];
45633
+ const shown = ordered.slice(0, 4);
45634
+ return {
45635
+ kind: "chooseDate",
45636
+ title: first?.title ?? "",
45637
+ schedule: (first ? formatBookingScheduleSummary(first) : null) ?? (ordered.length === 1 ? "1 date" : `${ordered.length} dates`),
45638
+ venue: first ? shownVenue(first, input.display) : null,
45639
+ price: seriesPriceLabel(ordered),
45640
+ nextDate: first ? formatBookingShortDate(first) : null,
45641
+ dateCount: ordered.length,
45642
+ dates: shown.map((event) => ({
45643
+ selectionId: event.selectionId,
45644
+ label: formatBookingShortDate(event),
45645
+ places: placesLabel(event, input, true),
45646
+ full: isFull(event, input.attendanceMode)
45647
+ })),
45648
+ moreCount: Math.max(0, ordered.length - shown.length),
45649
+ action: "Choose a date"
45650
+ };
45651
+ }
45652
+ /** The card before a booking starts; the server renders this too. */
45653
+ function idleBookingEntry(input) {
45654
+ if (input.event) return singleEntry(input.event, input);
45655
+ const only = input.events.length === 1 ? input.events[0] : void 0;
45656
+ if (only) return singleEntry(only, input);
45657
+ if (getEventSelectionMode(input.events) === "date") return chooseDateEntry(input);
45658
+ return {
45659
+ kind: "chooseEvent",
45660
+ title: input.events[0]?.title ?? "",
45661
+ count: input.events.length,
45662
+ noun: input.noun.pluralLabel,
45663
+ action: "Choose an event"
45664
+ };
45665
+ }
45666
+ //#endregion
45667
+ //#region ../blocks/src/system/blocks/offerings-preview/model.ts
45668
+ const OFFERINGS_PREVIEW_DEFAULT_BUTTON_TEXT = "Book";
45669
+ const OFFERINGS_PREVIEW_DEFAULT_EMPTY_MESSAGE = "Nothing scheduled just yet.";
45670
+ const OFFERINGS_PREVIEW_EVENT_PRESET_DEFINITIONS = [
45671
+ {
45672
+ preset: "class",
45673
+ sourceKey: "eventPreset:class",
45674
+ dataKey: "classEvents",
45675
+ label: "Classes"
45676
+ },
45677
+ {
45678
+ preset: "workshop",
45679
+ sourceKey: "eventPreset:workshop",
45680
+ dataKey: "workshopEvents",
45681
+ label: "Workshops"
45682
+ },
45683
+ {
45684
+ preset: "retreat",
45685
+ sourceKey: "eventPreset:retreat",
45686
+ dataKey: "retreatEvents",
45687
+ label: "Retreats"
45688
+ },
45689
+ {
45690
+ preset: "performance",
45691
+ sourceKey: "eventPreset:performance",
45692
+ dataKey: "performanceEvents",
45693
+ label: "Performances"
45694
+ }
45695
+ ];
45696
+ const OFFERINGS_PREVIEW_EVENT_PRESETS = OFFERINGS_PREVIEW_EVENT_PRESET_DEFINITIONS.map((definition) => definition.preset);
45697
+ const OFFERINGS_PREVIEW_AUTHORABLE_SOURCE_KEYS = [...OFFERINGS_PREVIEW_EVENT_PRESET_DEFINITIONS.map((definition) => definition.sourceKey), "course"];
45698
+ const OFFERINGS_PREVIEW_SOURCE_KEYS = OFFERINGS_PREVIEW_AUTHORABLE_SOURCE_KEYS;
45699
+ Object.fromEntries(OFFERINGS_PREVIEW_EVENT_PRESET_DEFINITIONS.map((definition) => [definition.sourceKey, {
45700
+ kind: "eventPreset",
45701
+ preset: definition.preset
45702
+ }]));
45703
+ function isOfferingsPreviewSourceKey(value) {
45704
+ return typeof value === "string" && OFFERINGS_PREVIEW_SOURCE_KEYS.includes(value);
45705
+ }
45706
+ function normalizeOfferingsPreviewGroupOrder(value) {
45707
+ if (value == null) return { kind: "legacy" };
45708
+ if (value.length === 0) return { kind: "default" };
45709
+ const keys = uniqueOfferingsPreviewSourceKeys(value);
45710
+ return keys.length > 0 ? {
45711
+ kind: "custom",
45712
+ keys
45713
+ } : { kind: "default" };
45714
+ }
45715
+ function inferLegacyOfferingsPreviewGroupOrder(overrides) {
45716
+ const keys = uniqueOfferingsPreviewSourceKeys((overrides ?? []).map((override) => override.sourceKey));
45717
+ return keys.length >= 2 ? keys : [];
45718
+ }
45719
+ function normalizeOfferingsPreviewContentForWrite(value) {
45720
+ if (!isRecord$7(value)) return null;
45721
+ const groupOrderValue = value.groupOrder;
45722
+ if (Array.isArray(groupOrderValue)) {
45723
+ const normalized = normalizeOfferingsPreviewGroupOrder(groupOrderValue);
45724
+ return {
45725
+ ...value,
45726
+ groupOrder: normalized.kind === "custom" ? normalized.keys : []
45727
+ };
45728
+ }
45729
+ if (groupOrderValue != null) return {
45730
+ ...value,
45731
+ groupOrder: []
45732
+ };
45733
+ const legacyOrder = inferLegacyOfferingsPreviewGroupOrder(Array.isArray(value.groupOverrides) ? value.groupOverrides.filter(isRecord$7) : []);
45734
+ return legacyOrder.length > 0 ? {
45735
+ ...value,
45736
+ groupOrder: legacyOrder
45737
+ } : value;
45738
+ }
45739
+ /** Which offerings a site preset row names; one owner for "the course preset means whole-series runs". */
45740
+ function sourceFromPresetSummary(preset) {
45741
+ if (preset.engine === "event_series" && preset.canonicalBehaviour === "course") return { kind: "wholeSeries" };
45742
+ if (preset.engine !== "event_series") return null;
45743
+ if (isOfferingsPreviewEventPreset(preset.canonicalBehaviour)) return {
45744
+ kind: "eventPreset",
45745
+ preset: preset.canonicalBehaviour
45746
+ };
45747
+ return null;
45748
+ }
45749
+ function uniqueOfferingsPreviewSourceKeys(values) {
45750
+ const keys = [];
45751
+ for (const value of values) {
45752
+ if (!isOfferingsPreviewSourceKey(value)) continue;
45753
+ if (!keys.includes(value)) keys.push(value);
45754
+ }
45755
+ return keys;
45756
+ }
45757
+ function isRecord$7(value) {
45758
+ return typeof value === "object" && value !== null && !Array.isArray(value);
45759
+ }
45760
+ function isOfferingsPreviewEventPreset(value) {
45761
+ return typeof value === "string" && OFFERINGS_PREVIEW_EVENT_PRESETS.includes(value);
45762
+ }
45763
+ //#endregion
45764
+ //#region ../blocks/src/system/runtime/nodes/event-registration/offeringNoun.ts
45765
+ function offeringKindForEvent(event) {
45766
+ return event.registrationSubject.kind === "wholeSeries" ? "course" : event.eventPreset ?? "event";
45767
+ }
45768
+ function presetKind(preset) {
45769
+ const source = sourceFromPresetSummary(preset);
45770
+ if (!source) return null;
45771
+ return source.kind === "wholeSeries" ? "course" : source.preset;
45772
+ }
45773
+ /**
45774
+ * What the site calls this kind of offering, in lower case for use mid-sentence
45775
+ * ("Book the course", "3 workshops"). Site preset labels win; the canonical
45776
+ * name stands in when the site has none.
45777
+ */
45778
+ function offeringNounForEvent(event, presets) {
45779
+ if (!event) return lowerCased(EVENT_PRESET_VOCABULARY.event);
45780
+ const kind = offeringKindForEvent(event);
45781
+ const preset = presets?.find((candidate) => presetKind(candidate) === kind);
45782
+ return lowerCased(preset?.label.trim() && preset.pluralLabel.trim() ? preset : EVENT_PRESET_VOCABULARY[kind]);
45783
+ }
45784
+ function lowerCased(noun) {
45785
+ return {
45786
+ label: noun.label.trim().toLocaleLowerCase(),
45787
+ pluralLabel: noun.pluralLabel.trim().toLocaleLowerCase()
45788
+ };
45789
+ }
45790
+ //#endregion
45791
+ //#region ../blocks/src/system/runtime/nodes/event-registration/registrationEventAdapter.ts
45792
+ function toRegistrationEvent(event) {
45793
+ return {
45794
+ ...event,
45795
+ selectionId: event.id,
45796
+ registrationSubject: {
45797
+ kind: "occurrence",
45798
+ occurrenceId: event.occurrenceId
45799
+ }
45800
+ };
45801
+ }
45802
+ function toRegistrationEvents(events) {
45803
+ return events.filter((event) => event.bookingScope === "single_occurrence").map(toRegistrationEvent);
45804
+ }
45805
+ /**
45806
+ * Project one whole-series run onto the registration surface.
45807
+ *
45808
+ * Capacity, pricing, payment terms and consent come straight off the canonical
45809
+ * Event record; only the schedule is widened from the single occurrence to the
45810
+ * live run. A run without live sessions has no registration event.
45811
+ */
45812
+ function toWholeSeriesRegistrationEvent(group) {
45813
+ const { representative, run } = group;
45814
+ const sessions = orderedLiveRunSessions(run.sessions);
45815
+ const firstSession = sessions[0];
45816
+ if (!firstSession) return null;
45817
+ const lastSession = sessions[sessions.length - 1] ?? firstSession;
45818
+ return {
45819
+ ...representative,
45820
+ selectionId: run.eventSeriesId,
45821
+ registrationSubject: {
45822
+ kind: "wholeSeries",
45823
+ eventSeriesId: run.eventSeriesId,
45824
+ sessions
45825
+ },
45826
+ startsAt: firstSession.startsAt,
45827
+ endsAt: lastSession.endsAt,
45828
+ timeZone: firstSession.timeZone
45829
+ };
45830
+ }
45831
+ function toWholeSeriesRegistrationEvents(events) {
45832
+ return groupPublicEventsByRun(events).map(toWholeSeriesRegistrationEvent).filter((event) => event !== null);
45833
+ }
45834
+ //#endregion
45835
+ //#region ../blocks/src/system/runtime/nodes/event-registration/bookingCardDisplay.ts
45836
+ /**
45837
+ * The booking card the server renders on CMS pages (issue 1581), and the
45838
+ * offering noun it resolves for the wizard. Kept beside the island contract
45839
+ * so the server and the client agree on one card.
45840
+ */
45841
+ const bookingHostSchema = z.enum(["overlay", "inPlace"]);
45842
+ const tileSchema = z.object({
45843
+ month: z.string(),
45844
+ day: z.string()
45845
+ }).nullable();
45846
+ const idleBookingEntrySchema = z.discriminatedUnion("kind", [
45847
+ z.object({
45848
+ kind: z.literal("single"),
45849
+ title: z.string(),
45850
+ when: z.string().nullable(),
45851
+ shortWhen: z.string().nullable(),
45852
+ venue: z.string().nullable(),
45853
+ price: z.string().nullable(),
45854
+ places: z.string().nullable(),
45855
+ tile: tileSchema,
45856
+ action: z.string()
45857
+ }),
45858
+ z.object({
45859
+ kind: z.literal("chooseDate"),
45860
+ title: z.string(),
45861
+ schedule: z.string(),
45862
+ venue: z.string().nullable(),
45863
+ price: z.string().nullable(),
45864
+ nextDate: z.string().nullable(),
45865
+ dateCount: z.number(),
45866
+ dates: z.array(z.object({
45867
+ selectionId: z.string(),
45868
+ label: z.string(),
45869
+ places: z.string().nullable(),
45870
+ full: z.boolean()
45871
+ })),
45872
+ moreCount: z.number(),
45873
+ action: z.string()
45874
+ }),
45875
+ z.object({
45876
+ kind: z.literal("chooseEvent"),
45877
+ title: z.string(),
45878
+ count: z.number(),
45879
+ noun: z.string(),
45880
+ action: z.string()
45881
+ })
45882
+ ]);
45883
+ function toScopedEventRegistrationEvents(scopedSourceEvents) {
45884
+ return [...toRegistrationEvents(scopedSourceEvents), ...toWholeSeriesRegistrationEvents(scopedSourceEvents)];
45885
+ }
45886
+ function scopedRegistrationEvents(source, scopedSourceEvents) {
45887
+ const scopedEvents = toScopedEventRegistrationEvents(scopedSourceEvents);
45888
+ return {
45889
+ scopedEvents,
45890
+ preSelected: findPreSelectedEvent({
45891
+ scopedEvents,
45892
+ occurrenceContext: source.occurrenceContext
45893
+ })
45894
+ };
45895
+ }
45896
+ function nounFor(scoped, presets) {
45897
+ return offeringNounForEvent(scoped.preSelected ?? scoped.scopedEvents[0] ?? null, presets);
45898
+ }
45899
+ /** What the site calls the offering this block books, from its preset rows. */
45900
+ function resolveOfferingNoun(source, scopedSourceEvents) {
45901
+ return nounFor(scopedRegistrationEvents(source, scopedSourceEvents), source.bookingOfferingPresets);
45902
+ }
45903
+ function idleEntry(source, scopedSourceEvents, display) {
45904
+ const scoped = scopedRegistrationEvents(source, scopedSourceEvents);
45905
+ if (scoped.scopedEvents.length === 0) return null;
45906
+ return idleBookingEntry({
45907
+ events: scoped.scopedEvents,
45908
+ event: withOccurrenceTitle(scoped.preSelected, source.occurrenceContext),
45909
+ display,
45910
+ buttonText: source.buttonText,
45911
+ noun: nounFor(scoped, source.bookingOfferingPresets),
45912
+ attendanceMode: null
45913
+ });
45914
+ }
45915
+ /** The server's card on CMS pages, or null where the shell still applies. */
45916
+ function bookingCardDisplay(source, scopedSourceEvents, display) {
45917
+ if ((source.host ?? "overlay") !== "overlay") return null;
45918
+ const entry = idleEntry(source, scopedSourceEvents, display);
45919
+ if (!entry) return null;
45920
+ return {
45921
+ kind: "event-registration",
45922
+ state: "booking-card",
45923
+ className: source.className ?? null,
45924
+ entry,
45925
+ buttonVariant: source.buttonVariant ?? null,
45926
+ buttonText: source.buttonText?.trim() ?? ""
45927
+ };
45928
+ }
45929
+ const unsupportedEventRegistrationPageSurfaceSubjectSchema = z.object({
45930
+ kind: z.literal("unsupported"),
45931
+ reason: z.enum([
45932
+ "unsupportedSubject",
45933
+ "invalidEventSeriesId",
45934
+ "invalidEventSeriesBookingScope",
45935
+ "invalidEventOfferingId",
45936
+ "invalidEventOfferingBookingScope",
45937
+ "invalidEventOfferingCatalogue"
45938
+ ])
45939
+ });
45940
+ const eventRegistrationPageSurfaceSubjectSchema = z.unknown().transform((value, ctx) => {
45941
+ const unsupported = unsupportedEventRegistrationPageSurfaceSubjectSchema.safeParse(value);
45942
+ if (unsupported.success) return unsupported.data;
45943
+ const parsed = parseEventPageSurfaceSubject(value);
45944
+ if (parsed.ok) return parsed.value;
45945
+ ctx.addIssue({
45946
+ code: z.ZodIssueCode.custom,
45947
+ message: `Invalid event page-surface subject (${parsed.error.kind})`
45948
+ });
45949
+ return z.NEVER;
45950
+ });
45951
+ function resolveEventRegistrationScope(context) {
45952
+ const occurrenceSeriesId = context.occurrenceContext?.seriesId;
45953
+ if (occurrenceSeriesId) return {
45954
+ kind: "occurrenceSeries",
45955
+ seriesId: occurrenceSeriesId
45956
+ };
45957
+ if (context.subject?.kind === "eventSeries") return {
45958
+ kind: "pageSurfaceSeries",
45959
+ seriesId: context.subject.eventSeriesId
45960
+ };
45961
+ if (context.subject?.kind === "eventOffering") return {
45962
+ kind: "pageSurfaceOffering",
45963
+ offeringId: context.subject.eventOfferingId,
45964
+ seriesIds: context.subject.catalogueEventSeriesIds
45965
+ };
45966
+ if (context.subject?.kind === "unsupported") return {
45967
+ kind: "unsupportedPageSurfaceSubject",
45968
+ reason: context.subject.reason
45969
+ };
45970
+ return { kind: "unscoped" };
45971
+ }
45972
+ function resolveEventRegistrationScopedEvents(events, context) {
45973
+ const scope = resolveEventRegistrationScope(context);
45974
+ switch (scope.kind) {
45975
+ case "occurrenceSeries":
45976
+ case "pageSurfaceSeries": return events.filter((event) => event.seriesId === scope.seriesId);
45977
+ case "pageSurfaceOffering": {
45978
+ const seriesIds = new Set(scope.seriesIds);
45979
+ return events.filter((event) => seriesIds.has(event.seriesId));
45980
+ }
45981
+ case "unsupportedPageSurfaceSubject": return [];
45982
+ case "unscoped": return [...events];
45983
+ }
45984
+ }
45985
+ //#endregion
44739
45986
  //#region ../blocks/src/system/runtime/nodes/shared/contract-schemas.ts
44740
45987
  const previewStageSchema = z.enum(["preview", "published"]);
44741
45988
  const registrationButtonVariantSchema = blockButtonRoleSchema;
@@ -44760,95 +46007,6 @@ const calendarTabViewsSchema = z.enum([
44760
46007
  "week"
44761
46008
  ]);
44762
46009
  //#endregion
44763
- //#region ../blocks/src/system/runtime/nodes/event-registration/wizardSteps.ts
44764
- /**
44765
- * Determine which wizard steps to show based on context and payment requirements.
44766
- *
44767
- * @param hasPreSelectedContext - Whether event is pre-selected (skips event selection)
44768
- * @param needsPaymentStep - Whether user must complete payment method selection
44769
- * @returns Array of wizard steps to display
44770
- */
44771
- function getWizardStepsForContext(hasPreSelectedContext, needsPaymentStep, paymentActive = false) {
44772
- const baseSteps = hasPreSelectedContext ? ["select-tickets", "enter-details"] : [
44773
- "select-event",
44774
- "select-tickets",
44775
- "enter-details"
44776
- ];
44777
- if (!needsPaymentStep) return baseSteps;
44778
- return paymentActive ? [
44779
- ...baseSteps,
44780
- "select-payment",
44781
- "payment"
44782
- ] : [...baseSteps, "select-payment"];
44783
- }
44784
- //#endregion
44785
- //#region ../blocks/src/system/runtime/nodes/event-registration/registrationDerivations.ts
44786
- function eventMatchesOccurrenceId(event, occurrenceId) {
44787
- return event.registrationSubject.kind === "occurrence" && (event.registrationSubject.occurrenceId === occurrenceId || event.selectionId === occurrenceId);
44788
- }
44789
- function findPreSelectedEvent(input) {
44790
- const occurrenceContext = input.occurrenceContext;
44791
- if (!occurrenceContext) return input.scopedEvents.length === 1 ? input.scopedEvents[0] ?? null : null;
44792
- const contextDate = getBookingLocalDateKey(toScheduledBookingDateTime({
44793
- startsAt: occurrenceContext.startsAt,
44794
- timeZone: occurrenceContext.timeZone
44795
- }));
44796
- return input.scopedEvents.find((event) => eventMatchesOccurrenceId(event, occurrenceContext.id) || getBookingLocalDateKey(toScheduledBookingDateTime({
44797
- startsAt: event.startsAt,
44798
- timeZone: event.timeZone
44799
- })) === contextDate) ?? null;
44800
- }
44801
- function resolveEventRequiresPayment(input) {
44802
- if (input.eligibility) return input.eligibility.requiresPayment;
44803
- if (input.selectedTicketType) return input.selectedTicketType.priceAmount > 0;
44804
- if (!input.event) return false;
44805
- return publicEventPricingType(input.event.pricing) === "paid";
44806
- }
44807
- //#endregion
44808
- //#region ../blocks/src/system/runtime/nodes/event-registration/registrationEventAdapter.ts
44809
- function toRegistrationEvent(event) {
44810
- return {
44811
- ...event,
44812
- selectionId: event.id,
44813
- registrationSubject: {
44814
- kind: "occurrence",
44815
- occurrenceId: event.occurrenceId
44816
- }
44817
- };
44818
- }
44819
- function toRegistrationEvents(events) {
44820
- return events.filter((event) => event.bookingScope === "single_occurrence").map(toRegistrationEvent);
44821
- }
44822
- /**
44823
- * Project one whole-series run onto the registration surface.
44824
- *
44825
- * Capacity, pricing, payment terms and consent come straight off the canonical
44826
- * Event record; only the schedule is widened from the single occurrence to the
44827
- * live run. A run without live sessions has no registration event.
44828
- */
44829
- function toWholeSeriesRegistrationEvent(group) {
44830
- const { representative, run } = group;
44831
- const sessions = orderedLiveRunSessions(run.sessions);
44832
- const firstSession = sessions[0];
44833
- if (!firstSession) return null;
44834
- const lastSession = sessions[sessions.length - 1] ?? firstSession;
44835
- return {
44836
- ...representative,
44837
- selectionId: run.eventSeriesId,
44838
- registrationSubject: {
44839
- kind: "wholeSeries",
44840
- eventSeriesId: run.eventSeriesId,
44841
- sessions
44842
- },
44843
- startsAt: firstSession.startsAt,
44844
- endsAt: lastSession.endsAt,
44845
- timeZone: firstSession.timeZone
44846
- };
44847
- }
44848
- function toWholeSeriesRegistrationEvents(events) {
44849
- return groupPublicEventsByRun(events).map(toWholeSeriesRegistrationEvent).filter((event) => event !== null);
44850
- }
44851
- //#endregion
44852
46010
  //#region ../blocks/src/system/runtime/nodes/event-registration/eventSourceRecovery.ts
44853
46011
  const { limit: _rawLimit, eventSurface: _rawEventSurface, scheduleScope: _rawScheduleScope, detailLevel: _rawDetailLevel, ...stringEventQueryShape } = publicEventLoaderQuerySchemaShape;
44854
46012
  const eventRecoveryRequestSchema = z.object({
@@ -44891,27 +46049,17 @@ function parseEventLoaderRecoveryRequest(value) {
44891
46049
  const EVENT_REGISTRATION_GENERIC_SHELL_STEP_LABELS = [
44892
46050
  "Date",
44893
46051
  "Tickets",
44894
- "Details"
44895
- ];
44896
- const EVENT_REGISTRATION_SHELL_STEP_LABELS = [
44897
- "Event",
44898
- "Date",
44899
- "Tickets",
44900
- "Details",
44901
- "Payment options",
44902
- "Payment"
46052
+ "Your details"
44903
46053
  ];
44904
46054
  const eventRegistrationPublicEventSchema = z.preprocess(normalizeEventRegistrationEventInput, publicEventSchema);
44905
- const eventRegistrationHydrationSchemaShape = {
46055
+ const eventRegistrationCommonSchemaShape = {
44906
46056
  demoEmailSuppressed: z.boolean().optional(),
44907
46057
  occurrenceContext: occurrenceContextSchema.optional(),
44908
46058
  subject: eventRegistrationPageSurfaceSubjectSchema.nullable().optional(),
44909
46059
  maxTickets: z.string().optional(),
44910
- showVenue: z.boolean().optional(),
44911
- showCapacity: z.boolean().optional(),
44912
46060
  successMessage: z.string().optional(),
44913
46061
  buttonVariant: registrationButtonVariantSchema.optional(),
44914
- overlayEnabled: z.boolean().optional(),
46062
+ host: bookingHostSchema.optional(),
44915
46063
  spamProtectionEnabled: z.boolean().optional(),
44916
46064
  supportEmail: z.string().optional(),
44917
46065
  sitePasses: publicPassProductCollectionSchema.nullable().optional(),
@@ -44936,32 +46084,51 @@ const eventRegistrationSourceSchema = z.object({
44936
46084
  portalToken: z.string().nullable().optional(),
44937
46085
  previewToken: z.string().nullable().optional(),
44938
46086
  readAuthority: browserReadAuthoritySchema.optional(),
44939
- ...eventRegistrationHydrationSchemaShape,
46087
+ ...eventRegistrationCommonSchemaShape,
46088
+ showVenue: z.boolean().optional(),
46089
+ showCapacity: z.boolean().optional(),
44940
46090
  events: z.array(eventRegistrationPublicEventSchema).nullable().optional(),
44941
46091
  wholeSeriesRunEvents: z.array(eventRegistrationPublicEventSchema).nullable().optional(),
44942
46092
  eventLoaderFailures: z.record(z.string(), z.unknown()).nullable().optional(),
44943
46093
  className: z.string().nullable().optional(),
44944
46094
  buttonText: z.string().nullable().optional(),
44945
46095
  maxTickets: z.string().nullable().optional(),
44946
- stage: previewStageSchema.optional()
46096
+ stage: previewStageSchema.optional(),
46097
+ bookingOfferingPresets: z.array(z.custom((value) => typeof value === "object" && value !== null && typeof value.label === "string")).nullable().optional()
44947
46098
  });
44948
- const eventRegistrationDisplaySchema = z.discriminatedUnion("state", [z.object({
44949
- kind: z.literal("event-registration"),
44950
- state: z.literal("generic-shell"),
44951
- className: z.string().nullable(),
44952
- titleText: z.string(),
44953
- bodyText: z.string(),
44954
- stepLabels: z.array(z.enum(EVENT_REGISTRATION_SHELL_STEP_LABELS)).min(1),
44955
- summaryButtonText: z.string()
44956
- }), z.object({
44957
- kind: z.literal("event-registration"),
44958
- state: z.literal("registration-closed"),
44959
- className: z.string().nullable(),
44960
- titleText: z.string(),
44961
- bodyText: z.string()
44962
- })]);
46099
+ const eventRegistrationDisplaySchema = z.discriminatedUnion("state", [
46100
+ z.object({
46101
+ kind: z.literal("event-registration"),
46102
+ state: z.literal("generic-shell"),
46103
+ className: z.string().nullable(),
46104
+ titleText: z.string(),
46105
+ bodyText: z.string(),
46106
+ stepLabels: z.array(z.enum(REGISTRATION_STEP_LABELS)).min(1),
46107
+ summaryButtonText: z.string()
46108
+ }),
46109
+ z.object({
46110
+ kind: z.literal("event-registration"),
46111
+ state: z.literal("registration-closed"),
46112
+ className: z.string().nullable(),
46113
+ titleText: z.string(),
46114
+ bodyText: z.string()
46115
+ }),
46116
+ z.object({
46117
+ kind: z.literal("event-registration"),
46118
+ state: z.literal("booking-card"),
46119
+ className: z.string().nullable(),
46120
+ entry: idleBookingEntrySchema,
46121
+ buttonVariant: registrationButtonVariantSchema.nullable(),
46122
+ buttonText: z.string()
46123
+ })
46124
+ ]);
44963
46125
  const eventRegistrationHydrationSchema = z.object({
44964
- ...eventRegistrationHydrationSchemaShape,
46126
+ ...eventRegistrationCommonSchemaShape,
46127
+ display: registrationDisplaySchema,
46128
+ offeringNoun: z.object({
46129
+ label: z.string(),
46130
+ pluralLabel: z.string()
46131
+ }).optional(),
44965
46132
  eventSource: eventRegistrationEventSourceSchema
44966
46133
  });
44967
46134
  const eventRegistrationRenderSchema = z.object({
@@ -44976,7 +46143,7 @@ z.object({
44976
46143
  readAuthority: browserReadAuthoritySchema.optional(),
44977
46144
  stage: previewStageSchema.optional()
44978
46145
  });
44979
- function buildEventRegistrationDisplayFromScopedEvents(source, scopedSourceEvents) {
46146
+ function buildEventRegistrationDisplayFromScopedEvents(source, scopedSourceEvents, display) {
44980
46147
  const selectedRun = resolveSelectedWholeSeriesRun(scopedSourceEvents);
44981
46148
  if (selectedRun && (!selectedRun.run.enrollmentOpen || toWholeSeriesRegistrationEvent(selectedRun) === null)) return {
44982
46149
  kind: "event-registration",
@@ -44985,6 +46152,8 @@ function buildEventRegistrationDisplayFromScopedEvents(source, scopedSourceEvent
44985
46152
  titleText: "Registration closed",
44986
46153
  bodyText: "This run is no longer open for bookings."
44987
46154
  };
46155
+ const card = bookingCardDisplay(source, scopedSourceEvents, display);
46156
+ if (card) return card;
44988
46157
  const stepLabels = buildEventRegistrationShellStepLabels(source, scopedSourceEvents);
44989
46158
  return {
44990
46159
  kind: "event-registration",
@@ -45001,11 +46170,14 @@ function buildEventRegistrationShellStepLabels(source, scopedSourceEvents) {
45001
46170
  if (selectedRun) {
45002
46171
  const selectedEvent = toWholeSeriesRegistrationEvent(selectedRun);
45003
46172
  if (!selectedEvent) return EVENT_REGISTRATION_GENERIC_SHELL_STEP_LABELS;
45004
- return getWizardStepsForContext(true, resolveEventRequiresPayment({
45005
- eligibility: null,
45006
- selectedTicketType: null,
45007
- event: selectedEvent
45008
- })).map((step) => shellLabelForRegistrationStep(step, [selectedEvent]));
46173
+ return shellStepLabels(registrationRoute({
46174
+ chooseEvent: false,
46175
+ payment: resolveEventRequiresPayment({
46176
+ eligibility: null,
46177
+ selectedTicketType: null,
46178
+ event: selectedEvent
46179
+ }) ? "optionsAndPayment" : "none"
46180
+ }), registrationStepLabelContext([selectedEvent], selectedEvent));
45009
46181
  }
45010
46182
  const scopedEvents = toScopedEventRegistrationEvents(scopedSourceEvents);
45011
46183
  if (scopedEvents.length === 0) return EVENT_REGISTRATION_GENERIC_SHELL_STEP_LABELS;
@@ -45014,14 +46186,15 @@ function buildEventRegistrationShellStepLabels(source, scopedSourceEvents) {
45014
46186
  occurrenceContext: source.occurrenceContext
45015
46187
  });
45016
46188
  const hasPreSelectedContext = Boolean(source.occurrenceContext || preSelectedEvent);
45017
- return getWizardStepsForContext(hasPreSelectedContext, hasPreSelectedContext && preSelectedEvent ? resolveEventRequiresPayment({
46189
+ const needsPaymentStep = hasPreSelectedContext && preSelectedEvent ? resolveEventRequiresPayment({
45018
46190
  eligibility: null,
45019
46191
  selectedTicketType: null,
45020
46192
  event: preSelectedEvent
45021
- }) : false).map((step) => shellLabelForRegistrationStep(step, scopedEvents));
45022
- }
45023
- function toScopedEventRegistrationEvents(scopedSourceEvents) {
45024
- return [...toRegistrationEvents(scopedSourceEvents), ...toWholeSeriesRegistrationEvents(scopedSourceEvents)];
46193
+ }) : false;
46194
+ return shellStepLabels(registrationRoute({
46195
+ chooseEvent: !hasPreSelectedContext,
46196
+ payment: needsPaymentStep ? "optionsAndPayment" : "none"
46197
+ }), registrationStepLabelContext(scopedEvents, preSelectedEvent));
45025
46198
  }
45026
46199
  function scopeEventRegistrationSourceEvents(source, eventSource) {
45027
46200
  return resolveEventRegistrationScopedEvents(eventSource.kind === "loaded" ? eventSource.events : [], {
@@ -45037,29 +46210,12 @@ function resolveSelectedWholeSeriesRun(scopedSourceEvents) {
45037
46210
  const runs = groupPublicEventsByRun(scopedSourceEvents);
45038
46211
  return runs.length === 1 ? runs[0] : null;
45039
46212
  }
45040
- function shellLabelForRegistrationStep(step, events) {
45041
- switch (step) {
45042
- case "select-event": return isDateSelection(events) ? "Date" : "Event";
45043
- case "select-tickets": return "Tickets";
45044
- case "enter-details": return "Details";
45045
- case "select-payment": return "Payment options";
45046
- case "payment": return "Payment";
45047
- case "error":
45048
- case "success":
45049
- case "submitting":
45050
- case "verifying-payment":
45051
- case "verifying-timeout":
45052
- case "payment-status":
45053
- case "payment-failed": return "Details";
45054
- }
45055
- }
45056
- function isDateSelection(events) {
45057
- const firstSeriesId = events[0]?.seriesId;
45058
- return Boolean(events.length > 1 && firstSeriesId && events.every((event) => event.seriesId === firstSeriesId));
46213
+ function shellStepLabels(route, context) {
46214
+ return route.map((step) => registrationStepLabel(step, context));
45059
46215
  }
45060
46216
  function buildEventRegistrationShellBodyText(stepLabels) {
45061
- if (stepLabels.includes("Payment options")) return "Preparing tickets, details, and payment options.";
45062
- if (stepLabels[0] === "Tickets") return "Preparing tickets and booking details.";
46217
+ if (stepLabels.includes("How you’ll pay")) return "Preparing tickets, details, and payment options.";
46218
+ if (stepLabels[0] === "Tickets" || stepLabels[0] === "Places") return "Preparing tickets and booking details.";
45063
46219
  return "Preparing dates, tickets, and booking details.";
45064
46220
  }
45065
46221
  const eventRegistrationBlockRuntime = defineClientOwnedBlockRuntime({
@@ -45069,6 +46225,7 @@ const eventRegistrationBlockRuntime = defineClientOwnedBlockRuntime({
45069
46225
  render: createZodCodec(eventRegistrationRenderSchema)
45070
46226
  },
45071
46227
  buildRender: (source) => {
46228
+ const display = resolveRegistrationDisplay(source);
45072
46229
  const eventSource = resolveEventRegistrationEventSource({
45073
46230
  events: source.events,
45074
46231
  wholeSeriesRunEvents: source.wholeSeriesRunEvents,
@@ -45076,7 +46233,7 @@ const eventRegistrationBlockRuntime = defineClientOwnedBlockRuntime({
45076
46233
  });
45077
46234
  const scopedSourceEvents = scopeEventRegistrationSourceEvents(source, eventSource);
45078
46235
  return {
45079
- display: buildEventRegistrationDisplayFromScopedEvents(source, scopedSourceEvents),
46236
+ display: buildEventRegistrationDisplayFromScopedEvents(source, scopedSourceEvents, display),
45080
46237
  hydration: {
45081
46238
  occurrenceContext: source.occurrenceContext ?? null,
45082
46239
  subject: source.subject ?? null,
@@ -45086,29 +46243,29 @@ const eventRegistrationBlockRuntime = defineClientOwnedBlockRuntime({
45086
46243
  } : eventSource,
45087
46244
  demoEmailSuppressed: source.demoEmailSuppressed,
45088
46245
  maxTickets: source.maxTickets ?? void 0,
45089
- showVenue: source.showVenue,
45090
- showCapacity: source.showCapacity,
46246
+ display,
45091
46247
  successMessage: source.successMessage,
45092
46248
  buttonVariant: source.buttonVariant,
45093
- overlayEnabled: source.overlayEnabled,
46249
+ host: source.host ?? "overlay",
45094
46250
  spamProtectionEnabled: source.spamProtectionEnabled,
45095
46251
  supportEmail: source.supportEmail,
45096
46252
  sitePasses: source.sitePasses ?? null,
45097
46253
  siteMemberships: source.siteMemberships ?? null,
45098
46254
  mailingList: source.mailingList,
45099
- siteName: source.siteName
46255
+ siteName: source.siteName,
46256
+ offeringNoun: resolveOfferingNoun(source, scopedSourceEvents)
45100
46257
  }
45101
46258
  };
45102
46259
  }
45103
46260
  });
45104
46261
  function normalizeEventRegistrationEventInput(value) {
45105
- if (!isRecord$7(value)) return value;
46262
+ if (!isRecord$6(value)) return value;
45106
46263
  return {
45107
46264
  ...value,
45108
46265
  destination: resolveRuntimeEventDestination(value)
45109
46266
  };
45110
46267
  }
45111
- function isRecord$7(value) {
46268
+ function isRecord$6(value) {
45112
46269
  return typeof value === "object" && value !== null && !Array.isArray(value);
45113
46270
  }
45114
46271
  const eventRegistrationBlockDefinition = {
@@ -45148,7 +46305,7 @@ const eventRegistrationBlockDefinition = {
45148
46305
  description: "Main heading for the registration form",
45149
46306
  required: false,
45150
46307
  multiline: false,
45151
- defaultValue: "Register for this event"
46308
+ defaultValue: ""
45152
46309
  },
45153
46310
  {
45154
46311
  id: "description",
@@ -45220,11 +46377,11 @@ const eventRegistrationBlockDefinition = {
45220
46377
  {
45221
46378
  id: "buttonText",
45222
46379
  type: "text",
45223
- label: "Submit button text",
45224
- description: "Text for the registration submit button",
46380
+ label: "Button text",
46381
+ description: "Text on the booking button",
45225
46382
  required: false,
45226
46383
  multiline: false,
45227
- defaultValue: "Complete Registration",
46384
+ defaultValue: "Book your place",
45228
46385
  maxLength: 40
45229
46386
  },
45230
46387
  createButtonVariantField()
@@ -45256,21 +46413,21 @@ const eventRegistrationBlockDefinition = {
45256
46413
  successMessage: { $bind: { from: "content.successMessage" } },
45257
46414
  buttonText: { $bind: {
45258
46415
  from: "content.buttonText",
45259
- fallback: "Complete Registration"
46416
+ fallback: "Book your place"
45260
46417
  } },
45261
46418
  buttonVariant: { $bind: {
45262
46419
  from: "content.buttonVariant",
45263
46420
  fallback: "primary"
45264
46421
  } },
45265
- overlayEnabled: { $bind: {
45266
- from: "$root.eventRegistrationOverlayEnabled",
45267
- fallback: false
45268
- } },
45269
46422
  events: { $bind: { from: "data.events" } },
45270
46423
  wholeSeriesRunEvents: { $bind: { from: "data.wholeSeriesRunEvents" } },
45271
46424
  loaderFailures: { $bind: { from: "$root.loaderFailures" } },
45272
46425
  mailingList: { $bind: { from: "$root.mailingList" } },
45273
46426
  siteName: { $bind: { from: "$root.siteName" } },
46427
+ bookingOfferingPresets: { $bind: {
46428
+ from: "$root.bookingOfferingPresets",
46429
+ fallback: []
46430
+ } },
45274
46431
  sitePasses: { $bind: { from: "data.sitePasses" } },
45275
46432
  siteMemberships: { $bind: { from: "data.siteMemberships" } },
45276
46433
  stage: { $bind: {
@@ -47931,7 +49088,21 @@ const eventCalendarBlockDefinition = {
47931
49088
  list: { label: "List" }
47932
49089
  }
47933
49090
  }, eventCalendarContentShape),
47934
- normalizeContent: normalizeEventCalendarContent,
49091
+ contentNormalizer: {
49092
+ fieldIds: [
49093
+ "variant",
49094
+ "displayMode",
49095
+ "listLayout",
49096
+ "defaultCalendarView",
49097
+ "eventSurface",
49098
+ "filterEventPresets",
49099
+ "scheduleScope",
49100
+ "filterCategoryIds",
49101
+ "filterStaffMemberId",
49102
+ "filterVenueId"
49103
+ ],
49104
+ normalize: normalizeEventCalendarContent
49105
+ },
47935
49106
  dataSchemas: {
47936
49107
  events: publicEventsArraySchema.optional(),
47937
49108
  calendarWindow: calendarWindowPageSchema.optional()
@@ -48404,90 +49575,6 @@ const recurringScheduleBlockDefinition = {
48404
49575
  } }
48405
49576
  };
48406
49577
  //#endregion
48407
- //#region ../blocks/src/system/blocks/offerings-preview/model.ts
48408
- const OFFERINGS_PREVIEW_DEFAULT_BUTTON_TEXT = "Book";
48409
- const OFFERINGS_PREVIEW_DEFAULT_EMPTY_MESSAGE = "Nothing scheduled just yet.";
48410
- const OFFERINGS_PREVIEW_EVENT_PRESET_DEFINITIONS = [
48411
- {
48412
- preset: "class",
48413
- sourceKey: "eventPreset:class",
48414
- dataKey: "classEvents",
48415
- label: "Classes"
48416
- },
48417
- {
48418
- preset: "workshop",
48419
- sourceKey: "eventPreset:workshop",
48420
- dataKey: "workshopEvents",
48421
- label: "Workshops"
48422
- },
48423
- {
48424
- preset: "retreat",
48425
- sourceKey: "eventPreset:retreat",
48426
- dataKey: "retreatEvents",
48427
- label: "Retreats"
48428
- },
48429
- {
48430
- preset: "performance",
48431
- sourceKey: "eventPreset:performance",
48432
- dataKey: "performanceEvents",
48433
- label: "Performances"
48434
- }
48435
- ];
48436
- OFFERINGS_PREVIEW_EVENT_PRESET_DEFINITIONS.map((definition) => definition.preset);
48437
- const OFFERINGS_PREVIEW_AUTHORABLE_SOURCE_KEYS = [...OFFERINGS_PREVIEW_EVENT_PRESET_DEFINITIONS.map((definition) => definition.sourceKey), "course"];
48438
- const OFFERINGS_PREVIEW_SOURCE_KEYS = OFFERINGS_PREVIEW_AUTHORABLE_SOURCE_KEYS;
48439
- Object.fromEntries(OFFERINGS_PREVIEW_EVENT_PRESET_DEFINITIONS.map((definition) => [definition.sourceKey, {
48440
- kind: "eventPreset",
48441
- preset: definition.preset
48442
- }]));
48443
- function isOfferingsPreviewSourceKey(value) {
48444
- return typeof value === "string" && OFFERINGS_PREVIEW_SOURCE_KEYS.includes(value);
48445
- }
48446
- function normalizeOfferingsPreviewGroupOrder(value) {
48447
- if (value == null) return { kind: "legacy" };
48448
- if (value.length === 0) return { kind: "default" };
48449
- const keys = uniqueOfferingsPreviewSourceKeys(value);
48450
- return keys.length > 0 ? {
48451
- kind: "custom",
48452
- keys
48453
- } : { kind: "default" };
48454
- }
48455
- function inferLegacyOfferingsPreviewGroupOrder(overrides) {
48456
- const keys = uniqueOfferingsPreviewSourceKeys((overrides ?? []).map((override) => override.sourceKey));
48457
- return keys.length >= 2 ? keys : [];
48458
- }
48459
- function normalizeOfferingsPreviewContentForWrite(value) {
48460
- if (!isRecord$6(value)) return null;
48461
- const groupOrderValue = value.groupOrder;
48462
- if (Array.isArray(groupOrderValue)) {
48463
- const normalized = normalizeOfferingsPreviewGroupOrder(groupOrderValue);
48464
- return {
48465
- ...value,
48466
- groupOrder: normalized.kind === "custom" ? normalized.keys : []
48467
- };
48468
- }
48469
- if (groupOrderValue != null) return {
48470
- ...value,
48471
- groupOrder: []
48472
- };
48473
- const legacyOrder = inferLegacyOfferingsPreviewGroupOrder(Array.isArray(value.groupOverrides) ? value.groupOverrides.filter(isRecord$6) : []);
48474
- return legacyOrder.length > 0 ? {
48475
- ...value,
48476
- groupOrder: legacyOrder
48477
- } : value;
48478
- }
48479
- function uniqueOfferingsPreviewSourceKeys(values) {
48480
- const keys = [];
48481
- for (const value of values) {
48482
- if (!isOfferingsPreviewSourceKey(value)) continue;
48483
- if (!keys.includes(value)) keys.push(value);
48484
- }
48485
- return keys;
48486
- }
48487
- function isRecord$6(value) {
48488
- return typeof value === "object" && value !== null && !Array.isArray(value);
48489
- }
48490
- //#endregion
48491
49578
  //#region ../blocks/src/system/blocks/offerings-preview/fields.ts
48492
49579
  const groupOverrideFields = [
48493
49580
  {
@@ -48851,7 +49938,22 @@ const offeringsPreviewEventDataSchemas = Object.fromEntries(OFFERINGS_PREVIEW_EV
48851
49938
  const offeringsPreviewEventDataLoaders = Object.fromEntries(OFFERINGS_PREVIEW_EVENT_PRESET_DEFINITIONS.map((definition) => [definition.dataKey, eventPresetLoader(definition.preset)]));
48852
49939
  const offeringsPreviewBlockDefinition = {
48853
49940
  manifest: offeringsPreviewManifest,
48854
- normalizeContent: normalizeOfferingsPreviewBlockContent,
49941
+ contentNormalizer: {
49942
+ fieldIds: [
49943
+ "displayMode",
49944
+ "heading",
49945
+ "description",
49946
+ "itemLimit",
49947
+ "groupOrder",
49948
+ "groupOverrides",
49949
+ "_cardStyles",
49950
+ "buttonVariant",
49951
+ "buttonText",
49952
+ "emptyMessage",
49953
+ "_sectionStyles"
49954
+ ],
49955
+ normalize: normalizeOfferingsPreviewBlockContent
49956
+ },
48855
49957
  dataSchemas: {
48856
49958
  ...offeringsPreviewEventDataSchemas,
48857
49959
  routeCandidates: publicRouteCandidatesSchema.optional(),
@@ -52449,11 +53551,11 @@ function buildNewsletterDisplay(source) {
52449
53551
  successMessage: DEFAULT_MAILING_LIST_SUCCESS_MESSAGE,
52450
53552
  privacyNote: null
52451
53553
  };
52452
- const targeting = source.targeting ?? { mode: "default" };
52453
- const choices = resolveRuntimeChoices(context, targeting);
53554
+ const assessment = assessMailingListTargeting(context, source.targeting ?? { mode: "default" });
53555
+ const choices = resolveRuntimeChoices(assessment);
52454
53556
  return {
52455
53557
  kind: "newsletter-form",
52456
- availability: runtimeTargetingIsAvailable(context, targeting, choices) ? "ready" : "unavailable",
53558
+ availability: assessment.tag === "available" ? "ready" : "unavailable",
52457
53559
  className: source.className ?? null,
52458
53560
  buttonLabel: form.buttonLabel,
52459
53561
  successMessage: form.successMessage,
@@ -52510,60 +53612,12 @@ function projectNewsletterFormHydrationTarget(source) {
52510
53612
  default: return assertNever$6(source);
52511
53613
  }
52512
53614
  }
52513
- function resolveRuntimeChoices(context, targeting) {
52514
- if (context?.tag !== "configured" || targeting.mode !== "custom" || targeting.selection.tag !== "visitor_choice" || context.provider.provider !== targeting.provider) return [];
52515
- switch (targeting.provider) {
52516
- case "native": {
52517
- const offered = new Set(targeting.selection.offeredTopicIds);
52518
- return context.provider.provider === "native" ? context.provider.topics.filter((item) => offered.has(item.id)).map((item) => ({
52519
- id: item.id,
52520
- label: item.name
52521
- })) : [];
52522
- }
52523
- case "brevo": {
52524
- const offered = new Set(targeting.selection.offeredListIds);
52525
- return context.provider.provider === "brevo" ? context.provider.lists.filter((item) => offered.has(item.id)).map((item) => ({
52526
- id: String(item.id),
52527
- label: item.name
52528
- })) : [];
52529
- }
52530
- case "mailchimp": {
52531
- const offered = new Set(targeting.selection.offeredInterestIds);
52532
- return (context.provider.provider === "mailchimp" ? context.provider.audiences.find((item) => item.id === targeting.audienceId) : void 0)?.interests.filter((item) => offered.has(item.id)).map((item) => ({
52533
- id: item.id,
52534
- label: item.name
52535
- })) ?? [];
52536
- }
52537
- }
52538
- }
52539
- function runtimeTargetingIsAvailable(context, targeting, choices) {
52540
- if (context?.tag !== "configured") return false;
52541
- const provider = context.provider;
52542
- if (targeting.mode === "default") switch (provider.provider) {
52543
- case "native": return provider.topics.some((item) => item.id === provider.defaultTopicId);
52544
- case "brevo": return provider.lists.some((item) => item.id === provider.defaultListId);
52545
- case "mailchimp": return provider.audiences.some((item) => item.id === provider.defaultAudienceId);
52546
- }
52547
- if (targeting.provider !== provider.provider) return false;
52548
- switch (targeting.provider) {
52549
- case "native": return provider.provider === "native" && nativeTargetIsAvailable(provider, targeting, choices);
52550
- case "brevo": return provider.provider === "brevo" && brevoTargetIsAvailable(provider, targeting, choices);
52551
- case "mailchimp": return provider.provider === "mailchimp" && mailchimpTargetIsAvailable(provider, targeting, choices);
52552
- }
52553
- }
52554
- function nativeTargetIsAvailable(provider, targeting, choices) {
52555
- const ids = targeting.selection.tag === "fixed" ? targeting.selection.topicIds : targeting.selection.offeredTopicIds;
52556
- return ids.every((id) => provider.topics.some((item) => item.id === id)) && (targeting.selection.tag !== "visitor_choice" || choices.length === ids.length);
52557
- }
52558
- function brevoTargetIsAvailable(provider, targeting, choices) {
52559
- const ids = targeting.selection.tag === "fixed" ? targeting.selection.listIds : targeting.selection.offeredListIds;
52560
- return ids.every((id) => provider.lists.some((item) => item.id === id)) && (targeting.selection.tag !== "visitor_choice" || choices.length === ids.length);
52561
- }
52562
- function mailchimpTargetIsAvailable(provider, targeting, choices) {
52563
- const audience = provider.audiences.find((item) => item.id === targeting.audienceId);
52564
- if (!audience) return false;
52565
- const ids = targeting.selection.tag === "fixed" ? targeting.selection.interestIds : targeting.selection.offeredInterestIds;
52566
- return ids.every((id) => audience.interests.some((item) => item.id === id)) && (targeting.selection.tag !== "visitor_choice" || choices.length === ids.length);
53615
+ function resolveRuntimeChoices(assessment) {
53616
+ if (!("value" in assessment) || assessment.value.mode !== "custom" || !("selectedChoices" in assessment.value) || assessment.value.targeting.selection.tag !== "visitor_choice") return [];
53617
+ return assessment.value.selectedChoices.map((item) => ({
53618
+ id: String(item.id),
53619
+ label: item.name
53620
+ }));
52567
53621
  }
52568
53622
  function parseRequiredMailingListSignupBlockId(value) {
52569
53623
  const parsed = parseMailingListBlockId(value);
@@ -52672,6 +53726,10 @@ function sanitizeNewsletterSignupPublicContent(content) {
52672
53726
  }
52673
53727
  const newsletterSignupBlockDefinition = {
52674
53728
  manifest: newsletterSignupManifest,
53729
+ contentNormalizer: {
53730
+ fieldIds: ["targeting"],
53731
+ normalize: normalizeMailingListBlockContent
53732
+ },
52675
53733
  runtime: newsletterFormBlockRuntime
52676
53734
  };
52677
53735
  //#endregion
@@ -52699,7 +53757,7 @@ const memberPhotoClassName = (baseClass) => cardRoleChildClass({
52699
53757
  baseClass,
52700
53758
  stylesFrom: "content._cardStyles"
52701
53759
  });
52702
- const teamMemberPhotoSchema = mediaSchema.refine((media) => media.type === "image", { message: "Team member photos must be image media" }).nullable();
53760
+ const teamMemberPhotoSchema = runtimeMediaSchema.refine((media) => media.type === "image", { message: "Team member photos must be image media" }).nullable();
52703
53761
  const teamMemberDataSchema = z.object({
52704
53762
  _type: z.literal("teamMember"),
52705
53763
  id: z.string(),
@@ -53404,7 +54462,20 @@ const locationMapBlockDefinition = {
53404
54462
  })], { gap: "md" })
53405
54463
  })
53406
54464
  }),
53407
- normalizeContent: normalizeLocationMapContent
54465
+ contentNormalizer: {
54466
+ fieldIds: [
54467
+ "heading",
54468
+ "locationSource",
54469
+ "venueId",
54470
+ "location",
54471
+ "showAddressText",
54472
+ "showDirections",
54473
+ "directionsLabel",
54474
+ "zoom",
54475
+ "heightPx"
54476
+ ],
54477
+ normalize: normalizeLocationMapContent
54478
+ }
53408
54479
  };
53409
54480
  //#endregion
53410
54481
  //#region ../blocks/src/system/blocks/index.ts
@@ -54228,11 +55299,14 @@ const offeringsPreviewDatesReadEnabled = {
54228
55299
  //#endregion
54229
55300
  //#region ../blocks/src/system/helpers/backgroundMedia.ts
54230
55301
  function resolveBackgroundMedia(input) {
54231
- const parsedMedia = mediaSchema.safeParse(input.image);
55302
+ const parsedMedia = runtimeMediaSchema.safeParse(input.image);
54232
55303
  if (!parsedMedia.success) return null;
54233
55304
  switch (parsedMedia.data.type) {
54234
55305
  case "image":
54235
- case "video": return parsedMedia.data;
55306
+ case "video": return {
55307
+ ...parsedMedia.data,
55308
+ type: parsedMedia.data.type
55309
+ };
54236
55310
  case "audio":
54237
55311
  case "document":
54238
55312
  case "spreadsheet":
@@ -54244,6 +55318,75 @@ function backgroundVideoMobileClassNames(input) {
54244
55318
  return input.showVideoOnMobile === true ? "" : "rb-hidden rb-md-block";
54245
55319
  }
54246
55320
  //#endregion
55321
+ //#region ../blocks/src/system/runtime/nodes/mediaContext.ts
55322
+ /**
55323
+ * Server-side: AsyncLocalStorage for request isolation.
55324
+ * Prevents race conditions when multiple requests are processed concurrently.
55325
+ *
55326
+ * Lazy initialization to avoid issues in environments that don't support async_hooks.
55327
+ */
55328
+ const serverStorageState = {
55329
+ storage: null,
55330
+ checked: false
55331
+ };
55332
+ function getServerStorage() {
55333
+ if (typeof window !== "undefined") return null;
55334
+ if (serverStorageState.checked) return serverStorageState.storage;
55335
+ serverStorageState.checked = true;
55336
+ try {
55337
+ const globalWithNonWebpackRequire = globalThis;
55338
+ const AsyncLocalStorage = (typeof globalWithNonWebpackRequire.__non_webpack_require__ === "function" ? globalWithNonWebpackRequire.__non_webpack_require__ : new Function("id", "return require(id)"))("async_hooks").AsyncLocalStorage;
55339
+ if (AsyncLocalStorage) {
55340
+ serverStorageState.storage = new AsyncLocalStorage();
55341
+ return serverStorageState.storage;
55342
+ }
55343
+ return null;
55344
+ } catch {
55345
+ if (process.env.NODE_ENV !== "production") console.warn("[blocks] AsyncLocalStorage not available - using module-level state");
55346
+ return null;
55347
+ }
55348
+ }
55349
+ /**
55350
+ * Client-side context storage.
55351
+ * Safe because browsers are single-threaded per tab.
55352
+ *
55353
+ * IMPORTANT: This must be shared across bundles.
55354
+ * SDK preview mode can load multiple independently-bundled entrypoints
55355
+ * (e.g. `@riverbankcms/sdk/rendering` + `@riverbankcms/sdk/preview-next/client`),
55356
+ * which can otherwise each get their own module-scoped singleton.
55357
+ */
55358
+ const RB_MEDIA_CONTEXT_KEY = Symbol.for("riverbankcms.blocks.mediaContext");
55359
+ function getClientContextStore() {
55360
+ const g = globalThis;
55361
+ const existing = g[RB_MEDIA_CONTEXT_KEY];
55362
+ if (existing && typeof existing === "object") return existing;
55363
+ const next = {};
55364
+ g[RB_MEDIA_CONTEXT_KEY] = next;
55365
+ return next;
55366
+ }
55367
+ /**
55368
+ * Get the current media context.
55369
+ * Server-side: Uses AsyncLocalStorage for request isolation.
55370
+ * Client-side: Uses module-level state (safe in single-threaded browser).
55371
+ */
55372
+ function getMediaContext() {
55373
+ const storage = getServerStorage();
55374
+ if (storage) return storage.getStore() ?? {};
55375
+ return getClientContextStore();
55376
+ }
55377
+ function getCurrentResolveImageUrlContext() {
55378
+ const context = getMediaContext();
55379
+ if (!context.supabaseUrl && !context.siteId && !context.mediaAssetIndex) return;
55380
+ return {
55381
+ supabaseUrl: context.supabaseUrl,
55382
+ siteId: context.siteId,
55383
+ mediaAssetIndex: context.mediaAssetIndex
55384
+ };
55385
+ }
55386
+ function getMediaAssetIndex(context) {
55387
+ return context?.mediaAssetIndex ?? getMediaContext().mediaAssetIndex;
55388
+ }
55389
+ //#endregion
54247
55390
  //#region ../blocks/src/system/runtime/nodes/media.tsx
54248
55391
  function getValidSourceDimension(value) {
54249
55392
  return typeof value === "number" && Number.isFinite(value) && value > 0 ? value : void 0;
@@ -54279,41 +55422,72 @@ function applyRectTransform(existingStyle, patch) {
54279
55422
  if (patch.transform) next.transform = appendTransform(existingStyle?.transform, patch.transform);
54280
55423
  return next;
54281
55424
  }
54282
- function computeMediaTransformStyle(value, style, targetAspectRatio, options) {
55425
+ /** Resolve portable content without exposing runtime metadata as saved values. */
55426
+ function resolveMediaWithContext(reference, context) {
55427
+ return resolveManagedMediaReference(reference, getMediaAssetIndex(context ?? void 0));
55428
+ }
55429
+ function sourceFromAsset(value, asset) {
55430
+ return {
55431
+ type: value.type,
55432
+ alt: value.alt,
55433
+ filename: value.filename,
55434
+ purpose: value.purpose,
55435
+ placeholder: value.placeholder,
55436
+ transform: value.transform,
55437
+ poster: value.poster,
55438
+ identifier: asset.identifier,
55439
+ assetId: asset.id,
55440
+ storageBucket: asset.storageBucket,
55441
+ storagePath: asset.storagePath,
55442
+ mimeType: asset.mimeType,
55443
+ width: asset.width,
55444
+ height: asset.height
55445
+ };
55446
+ }
55447
+ function resolveRuntimeMedia(value, context) {
55448
+ if ("asset" in value && value.asset) return sourceFromAsset(value, value.asset);
55449
+ if ("src" in value && value.src) return value;
55450
+ if (!value.identifier) return void 0;
55451
+ const resolved = resolveMediaWithContext(value, context);
55452
+ return resolved.kind === "resolved" ? sourceFromAsset(resolved.reference, resolved.asset) : void 0;
55453
+ }
55454
+ function usesProxyFraming(source, transform) {
55455
+ if (!source?.assetId || !isExplicitCropProxyEnabled()) return false;
55456
+ return Boolean(transform.rotation) || isExplicitRectCrop(transform.rect, getValidSourceDimension(source.width), getValidSourceDimension(source.height));
55457
+ }
55458
+ function rectFramingPatch(source, rect, proxy, style) {
55459
+ if (!source || !rect || proxy) return {};
55460
+ const patch = buildCssRectStylePatch(source, rect);
55461
+ return patch ? applyRectTransform(style, patch) : {};
55462
+ }
55463
+ function hotspotFramingPatch(transform, proxy, rectPatch, style) {
55464
+ if (!transform.hotspot || rectPatch.objectPosition !== void 0) return {};
55465
+ const fit = style?.objectFit;
55466
+ if (fit && fit !== "cover" && fit !== "contain") return {};
55467
+ const point = proxy && transform.rotation ? rotateNormalizedPoint(transform.hotspot, transform.rotation) : transform.hotspot;
55468
+ return { objectPosition: `${point.x * 100}% ${point.y * 100}%` };
55469
+ }
55470
+ function rotationFramingPatch(rotation, proxy, rectPatch, style) {
55471
+ if (!rotation || proxy) return {};
55472
+ return {
55473
+ transform: appendTransform(rectPatch.transform ?? style?.transform, `rotate(${rotation}deg)`),
55474
+ transformOrigin: rectPatch.transformOrigin ?? "center"
55475
+ };
55476
+ }
55477
+ /** CSS framing is a pure projection of the resolved source and per-use transform. */
55478
+ function deriveMediaFramingStyle(source, transform, proxy, style) {
55479
+ if (!transform.rect && !transform.hotspot && !transform.rotation) return style;
55480
+ const rectPatch = rectFramingPatch(source, transform.rect, proxy, style);
55481
+ return mergeStyles(style, {
55482
+ ...rectPatch,
55483
+ ...hotspotFramingPatch(transform, proxy, rectPatch, style),
55484
+ ...rotationFramingPatch(transform.rotation, proxy, rectPatch, style)
55485
+ });
55486
+ }
55487
+ function computeMediaTransformStyle(value, style, targetAspectRatio, options, context) {
55488
+ const source = resolveRuntimeMedia(value, context ?? getCurrentResolveImageUrlContext());
54283
55489
  const transform = options?.resolvedTransform ?? resolveMediaTransformForAspect(value.transform, targetAspectRatio);
54284
- const rect = transform.rect;
54285
- const hotspot = transform.hotspot;
54286
- const rotation = transform.rotation;
54287
- if (!rect && !hotspot && !rotation) return style;
54288
- const stylePatch = {};
54289
- const heuristicSuppressCss = Boolean(isExplicitCropProxyEnabled() && value.assetId && (typeof rotation === "number" && rotation !== 0 || isExplicitRectCrop(rect, getValidSourceDimension(value.width), getValidSourceDimension(value.height))));
54290
- const shouldSuppressCss = options?.isProxy ?? heuristicSuppressCss;
54291
- if (rect && !shouldSuppressCss) {
54292
- const rectPatch = buildCssRectStylePatch(value, rect);
54293
- if (rectPatch) {
54294
- const mergedRect = applyRectTransform(style, rectPatch);
54295
- Object.assign(stylePatch, mergedRect);
54296
- }
54297
- }
54298
- if (hotspot) {
54299
- const effectiveObjectFit = stylePatch.objectFit ?? style?.objectFit;
54300
- if ((!effectiveObjectFit || effectiveObjectFit === "cover" || effectiveObjectFit === "contain") && stylePatch.objectPosition === void 0) {
54301
- const resolved = shouldSuppressCss && rotation ? rotateNormalizedPoint({
54302
- x: hotspot.x,
54303
- y: hotspot.y
54304
- }, rotation) : {
54305
- x: hotspot.x,
54306
- y: hotspot.y
54307
- };
54308
- stylePatch.objectPosition = `${resolved.x * 100}% ${resolved.y * 100}%`;
54309
- }
54310
- }
54311
- if (rotation && !shouldSuppressCss) {
54312
- const existingTransform = stylePatch.transform ?? style?.transform;
54313
- stylePatch.transform = existingTransform ? `${existingTransform} rotate(${rotation}deg)` : `rotate(${rotation}deg)`;
54314
- if (!stylePatch.transformOrigin) stylePatch.transformOrigin = "center";
54315
- }
54316
- return mergeStyles(style, stylePatch);
55490
+ return deriveMediaFramingStyle(source, transform, options?.isProxy ?? usesProxyFraming(source, transform), style);
54317
55491
  }
54318
55492
  //#endregion
54319
55493
  //#region ../blocks/src/system/transforms/registry/background.ts
@@ -54462,11 +55636,11 @@ const backgroundTransforms = [
54462
55636
  }
54463
55637
  ];
54464
55638
  function parseMediaBackground(value) {
54465
- const background = parseBackgroundContent(value);
55639
+ const background = parseRuntimeBackgroundContent(value);
54466
55640
  return background?.type === "image" || background?.type === "video" ? background : null;
54467
55641
  }
54468
55642
  function resolveBackgroundLayerStyle(value, fallbackToken, context) {
54469
- const background = parseBackgroundContent(value);
55643
+ const background = parseRuntimeBackgroundContent(value);
54470
55644
  const style = {};
54471
55645
  const gradient = background?.type === "gradient" ? isString(background.gradient) ? background.gradient.trim() : gradientPresetCss(background.gradientPresetId) : null;
54472
55646
  const color = background?.type === "color" && isString(background.color) ? background.color.trim() : null;
@@ -54609,17 +55783,7 @@ const linksTransforms = [{
54609
55783
  id: "links.resolve",
54610
55784
  kind: "formatter",
54611
55785
  summary: "Resolve LinkValue objects to href strings using the route map",
54612
- run: (value, _options, context) => {
54613
- if (!value || typeof value !== "object") return null;
54614
- const link = value;
54615
- const kind = typeof link.kind === "string" ? link.kind : null;
54616
- if (kind === "internal") return resolveInternalLink(link, context);
54617
- if (kind === "page") return resolvePageLink(link, context);
54618
- if (kind === "entry") return resolveEntryLink(link, context);
54619
- if (kind === "external" || kind === "url") return resolveExternalLink(link);
54620
- if (!kind && typeof link.href === "string") return resolveExternalLink(link);
54621
- return null;
54622
- }
55786
+ run: (value, _options, context) => resolveAuthoredLinkHref(value, context)
54623
55787
  }, {
54624
55788
  id: "links.label",
54625
55789
  kind: "formatter",
@@ -54646,6 +55810,21 @@ const linksTransforms = [{
54646
55810
  return null;
54647
55811
  }
54648
55812
  }];
55813
+ function resolveAuthoredLinkHref(value, context = {}) {
55814
+ if (!value || typeof value !== "object") return null;
55815
+ const link = value;
55816
+ switch (link.kind) {
55817
+ case "internal": return resolveInternalLink(link, context);
55818
+ case "page": return resolvePageLink(link, context);
55819
+ case "entry": return resolveEntryLink(link, context);
55820
+ case "external":
55821
+ case "url": return resolveExternalLink(link);
55822
+ default: return resolvePreResolvedLink(link);
55823
+ }
55824
+ }
55825
+ function resolvePreResolvedLink(link) {
55826
+ return !(typeof link.kind === "string" ? link.kind : null) && typeof link.href === "string" ? resolveExternalLink(link) : null;
55827
+ }
54649
55828
  const eventCalendarSelectionParam = {
54650
55829
  id: "events.calendarSelectionParam",
54651
55830
  kind: "formatter",