@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
@@ -1776,12 +1776,14 @@ const unsafeAsSiteId = (id) => id;
1776
1776
  const unsafeAsApiKeyId = (id) => id;
1777
1777
  const unsafeAsEventOfferingId = (id) => id;
1778
1778
  const unsafeAsEventSeriesId = (id) => id;
1779
+ const unsafeAsMediaAssetId = (id) => id;
1779
1780
  const unsafeAsUserId = (id) => id;
1780
1781
  const unsafeAsContentVersionId = (id) => id;
1781
1782
  const parseSiteId = (value) => parseUuidBrand(value, unsafeAsSiteId);
1782
1783
  const parseApiKeyId = (value) => parseUuidBrand(value, unsafeAsApiKeyId);
1783
1784
  const parseEventOfferingId = (value) => parseUuidBrand(value, unsafeAsEventOfferingId);
1784
1785
  const parseUserId = (value) => parseUuidBrand(value, unsafeAsUserId);
1786
+ const parseMediaAssetId = (value) => parseUuidBrand(value, unsafeAsMediaAssetId);
1785
1787
  const parseContentVersionId = (value) => parseUuidBrand(value, unsafeAsContentVersionId);
1786
1788
  const parseEventSeriesId = (value) => parseUuidBrand(value, unsafeAsEventSeriesId);
1787
1789
  //#endregion
@@ -1986,6 +1988,29 @@ function canonicalEventPricingDisplay(pricing) {
1986
1988
  default: return assertNever$2(pricing);
1987
1989
  }
1988
1990
  }
1991
+ /**
1992
+ * The single renderer for a precomputed booking price `display` ADT. This is
1993
+ * the one place booking price variants map to user-facing strings — every call
1994
+ * site (booking entry, events calendar, event/course details, listings, portal)
1995
+ * should route through here so labels never drift. Adding a variant to the ADT
1996
+ * breaks compilation here (via `assertNever`) until it is handled.
1997
+ */
1998
+ function formatBookingPriceDisplay(display, options = {}) {
1999
+ switch (display.kind) {
2000
+ case "free": return "Free";
2001
+ case "exact": return formatCurrencyAmount(display.amount, display.currency, options);
2002
+ case "from": return `From ${formatCurrencyAmount(display.amount, display.currency, options)}`;
2003
+ case "range": return `${formatCurrencyAmount(display.minAmount, display.currency, options)} - ${formatCurrencyAmount(display.maxAmount, display.currency, options)}`;
2004
+ 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)}`;
2005
+ case "unavailable": return "Pricing unavailable";
2006
+ case "external": return display.label;
2007
+ default: return assertNever$2(display);
2008
+ }
2009
+ }
2010
+ /** Render an event's price directly from its pricing value. */
2011
+ function formatEventPricing(pricing, options = {}) {
2012
+ return formatBookingPriceDisplay(publicEventPricingDisplay(pricing), options);
2013
+ }
1989
2014
  //#endregion
1990
2015
  //#region ../core/src/event-page-surface-subject.ts
1991
2016
  function createEventSeriesPageSurfaceSubject(eventSeriesId, bookingScope, eventDestinationMode) {
@@ -2129,6 +2154,7 @@ function parseBookingTimeZone(timeZone) {
2129
2154
  }
2130
2155
  //#endregion
2131
2156
  //#region ../core/src/booking-date-time.ts
2157
+ const DEFAULT_LOCALE = "en-US";
2132
2158
  const FALLBACK_TIMEZONE = "UTC";
2133
2159
  const START_OF_DAY = {
2134
2160
  hour: 0,
@@ -2251,6 +2277,34 @@ function toScheduledBookingDateTime(input) {
2251
2277
  if (result.ok) return result.value;
2252
2278
  throw new Error(formatParseError(result));
2253
2279
  }
2280
+ function formatBookingInstant(isoString, options) {
2281
+ const date = new Date(isoString);
2282
+ if (Number.isNaN(date.getTime())) return isoString;
2283
+ return getCachedDateTimeFormatter(options.locale ?? DEFAULT_LOCALE, getSafeTimeZone(options.timeZone), options.format).format(date);
2284
+ }
2285
+ function formatBookingDate(input, format = {
2286
+ weekday: "long",
2287
+ year: "numeric",
2288
+ month: "long",
2289
+ day: "numeric"
2290
+ }) {
2291
+ return formatBookingInstant(input.startsAt, {
2292
+ locale: input.locale,
2293
+ timeZone: input.timeZone,
2294
+ format
2295
+ });
2296
+ }
2297
+ function formatBookingTime(input, format = {
2298
+ hour: "numeric",
2299
+ minute: "2-digit",
2300
+ hour12: true
2301
+ }) {
2302
+ return formatBookingInstant(input.startsAt, {
2303
+ locale: input.locale,
2304
+ timeZone: input.timeZone,
2305
+ format
2306
+ });
2307
+ }
2254
2308
  function getBookingLocalDateKey(input) {
2255
2309
  return formatDateKey$1(getLocalScheduleDate(new Date(input.startsAt), input.timeZone));
2256
2310
  }
@@ -2412,7 +2466,7 @@ function parsePositiveInteger(value) {
2412
2466
  return typeof value === "number" && Number.isSafeInteger(value) && value > 0 ? value : null;
2413
2467
  }
2414
2468
  function parseNewsletterListId(value) {
2415
- return parseTrimmedMatchingString(value, UUID_PATTERN);
2469
+ return parseTrimmedMatchingString(typeof value === "string" ? value.toLowerCase() : value, UUID_PATTERN);
2416
2470
  }
2417
2471
  function parseMailchimpAccountId(value) {
2418
2472
  return parseTrimmedMatchingString(value, OPAQUE_ID_PATTERN);
@@ -2454,8 +2508,15 @@ function parseMailingListProviderScope(value) {
2454
2508
  }
2455
2509
  return err("invalid_mailing_list_provider_scope");
2456
2510
  }
2457
- //#endregion
2458
- //#region ../core/src/mailing-list-setup-parser.ts
2511
+ z.unknown().transform((value, ctx) => {
2512
+ const parsed = parseNewsletterAudience(value);
2513
+ if (parsed.ok) return parsed.value;
2514
+ ctx.addIssue({
2515
+ code: "custom",
2516
+ message: "Choose a valid newsletter audience."
2517
+ });
2518
+ return z.NEVER;
2519
+ });
2459
2520
  function parseMailingListGlobalForm(value) {
2460
2521
  if (!isRecord$9(value)) return formError("shape");
2461
2522
  const fields = parseGlobalFields(value.fields);
@@ -2475,6 +2536,16 @@ function parseMailingListGlobalForm(value) {
2475
2536
  privacyNote
2476
2537
  });
2477
2538
  }
2539
+ function parseNewsletterAudience(value) {
2540
+ if (!isRecord$9(value)) return err("invalid_newsletter_audience");
2541
+ if (value.kind === "general" && Object.keys(value).length === 1) return ok({ kind: "general" });
2542
+ if (value.kind !== "topic" || Object.keys(value).some((key) => key !== "kind" && key !== "topicId")) return err("invalid_newsletter_audience");
2543
+ const topicId = parseNewsletterListId(value.topicId);
2544
+ return topicId === null ? err("invalid_newsletter_audience") : ok({
2545
+ kind: "topic",
2546
+ topicId
2547
+ });
2548
+ }
2478
2549
  function parseGlobalFields(value) {
2479
2550
  if (!isRecord$9(value) || typeof value.preset !== "string") return null;
2480
2551
  switch (value.preset) {
@@ -2524,44 +2595,144 @@ function formError(field) {
2524
2595
  }
2525
2596
  //#endregion
2526
2597
  //#region ../core/src/mailing-list-targeting.ts
2598
+ const DEFAULT_MAILING_LIST_TARGETING = { mode: "default" };
2599
+ function mailingListTargetingChoiceIds(targeting) {
2600
+ switch (targeting.provider) {
2601
+ case "native": return targeting.selection.tag === "general" ? [] : targeting.selection.tag === "fixed" ? targeting.selection.topicIds : targeting.selection.offeredTopicIds;
2602
+ case "brevo": return targeting.selection.tag === "fixed" ? targeting.selection.listIds : targeting.selection.offeredListIds;
2603
+ case "mailchimp": return targeting.selection.tag === "fixed" ? targeting.selection.interestIds : targeting.selection.offeredInterestIds;
2604
+ }
2605
+ }
2527
2606
  function planMailingListChoicePresentation(targeting) {
2528
- if (targeting.mode === "default" || targeting.selection.tag === "fixed") return { tag: "hidden" };
2607
+ if (targeting.mode === "default" || targeting.selection.tag === "fixed" || targeting.selection.tag === "general") return { tag: "hidden" };
2529
2608
  switch (targeting.provider) {
2530
- case "native": return targeting.selection.offeredTopicIds.length === 1 ? {
2531
- tag: "automatic",
2532
- selection: {
2533
- provider: "native",
2534
- topicIds: targeting.selection.offeredTopicIds
2535
- }
2536
- } : {
2537
- tag: "visitor_choice",
2609
+ case "native": return presentVisitorSelection({
2610
+ provider: "native",
2611
+ topicIds: targeting.selection.offeredTopicIds
2612
+ }, targeting.selection.offeredTopicIds.length);
2613
+ case "brevo": return presentVisitorSelection({
2614
+ provider: "brevo",
2615
+ listIds: targeting.selection.offeredListIds
2616
+ }, targeting.selection.offeredListIds.length);
2617
+ case "mailchimp": return presentVisitorSelection({
2618
+ provider: "mailchimp",
2619
+ interestIds: targeting.selection.offeredInterestIds
2620
+ }, targeting.selection.offeredInterestIds.length);
2621
+ }
2622
+ }
2623
+ function presentVisitorSelection(selection, optionCount) {
2624
+ return optionCount === 1 ? {
2625
+ tag: "automatic",
2626
+ selection
2627
+ } : {
2628
+ tag: "visitor_choice",
2629
+ provider: selection.provider,
2630
+ optionCount
2631
+ };
2632
+ }
2633
+ //#endregion
2634
+ //#region ../core/src/mailing-list-targeting-availability.ts
2635
+ function resolveMailingListDefaultDestination(provider) {
2636
+ switch (provider.provider) {
2637
+ case "native": {
2638
+ const audience = provider.defaultAudience;
2639
+ return audience.kind === "general" ? {
2640
+ id: "general",
2641
+ name: "General mailing list"
2642
+ } : provider.topics.find((item) => item.id === audience.topicId) ?? null;
2643
+ }
2644
+ case "brevo": return provider.lists.find((item) => item.id === provider.defaultListId) ?? null;
2645
+ case "mailchimp": return provider.audiences.find((item) => item.id === provider.defaultAudienceId) ?? null;
2646
+ }
2647
+ }
2648
+ function assessMailingListTargeting(context, targeting) {
2649
+ if (context?.tag !== "configured") return {
2650
+ tag: "unavailable",
2651
+ reason: "setup_missing",
2652
+ targeting
2653
+ };
2654
+ const catalog = context.provider;
2655
+ if (targeting.mode === "custom") return assessCustomTargeting(catalog, targeting);
2656
+ const destination = resolveMailingListDefaultDestination(catalog);
2657
+ return destination === null ? {
2658
+ tag: "unavailable",
2659
+ reason: "default_missing",
2660
+ targeting
2661
+ } : {
2662
+ tag: "available",
2663
+ value: {
2664
+ mode: "default",
2665
+ targeting,
2666
+ provider: catalog.provider,
2667
+ destination
2668
+ }
2669
+ };
2670
+ }
2671
+ function assessCustomTargeting(catalog, targeting) {
2672
+ switch (targeting.provider) {
2673
+ case "native": return catalog.provider === "native" ? assessMatchedChoices({
2538
2674
  provider: "native",
2539
- optionCount: targeting.selection.offeredTopicIds.length
2675
+ catalog,
2676
+ targeting
2677
+ }, catalog.topics) : {
2678
+ tag: "unavailable",
2679
+ reason: "provider_mismatch",
2680
+ targeting
2540
2681
  };
2541
- case "brevo": return targeting.selection.offeredListIds.length === 1 ? {
2542
- tag: "automatic",
2543
- selection: {
2544
- provider: "brevo",
2545
- listIds: targeting.selection.offeredListIds
2546
- }
2547
- } : {
2548
- tag: "visitor_choice",
2682
+ case "brevo": return catalog.provider === "brevo" ? assessMatchedChoices({
2549
2683
  provider: "brevo",
2550
- optionCount: targeting.selection.offeredListIds.length
2684
+ catalog,
2685
+ targeting
2686
+ }, catalog.lists) : {
2687
+ tag: "unavailable",
2688
+ reason: "provider_mismatch",
2689
+ targeting
2551
2690
  };
2552
- case "mailchimp": return targeting.selection.offeredInterestIds.length === 1 ? {
2553
- tag: "automatic",
2554
- selection: {
2691
+ case "mailchimp": {
2692
+ if (catalog.provider !== "mailchimp") return {
2693
+ tag: "unavailable",
2694
+ reason: "provider_mismatch",
2695
+ targeting
2696
+ };
2697
+ const audience = catalog.audiences.find((item) => item.id === targeting.audienceId);
2698
+ return audience === void 0 ? {
2699
+ tag: "unavailable",
2700
+ reason: "audience_missing",
2701
+ value: {
2702
+ mode: "custom",
2703
+ provider: "mailchimp",
2704
+ catalog,
2705
+ targeting
2706
+ }
2707
+ } : assessMatchedChoices({
2555
2708
  provider: "mailchimp",
2556
- interestIds: targeting.selection.offeredInterestIds
2557
- }
2558
- } : {
2559
- tag: "visitor_choice",
2560
- provider: "mailchimp",
2561
- optionCount: targeting.selection.offeredInterestIds.length
2562
- };
2709
+ catalog,
2710
+ targeting,
2711
+ audience
2712
+ }, audience.interests);
2713
+ }
2563
2714
  }
2564
2715
  }
2716
+ function assessMatchedChoices(matched, choices) {
2717
+ const selected = new Set(mailingListTargetingChoiceIds(matched.targeting));
2718
+ const available = new Set(choices.map((item) => item.id));
2719
+ const missingIds = [...selected].filter((id) => !available.has(id));
2720
+ const value = {
2721
+ mode: "custom",
2722
+ ...matched,
2723
+ choices,
2724
+ selectedChoices: choices.filter((item) => selected.has(item.id))
2725
+ };
2726
+ return missingIds.length > 0 ? {
2727
+ tag: "unavailable",
2728
+ reason: "choices_missing",
2729
+ missingIds,
2730
+ value
2731
+ } : {
2732
+ tag: "available",
2733
+ value
2734
+ };
2735
+ }
2565
2736
  //#endregion
2566
2737
  //#region ../core/src/mailing-list-runtime.ts
2567
2738
  function parseMailingListRuntimeContext(value) {
@@ -2607,11 +2778,12 @@ function parseRuntimeProvider(value) {
2607
2778
  }
2608
2779
  }
2609
2780
  function parseNativeRuntimeProvider(value) {
2610
- const defaultTopicId = parseNewsletterListId(value.defaultTopicId);
2781
+ if ("defaultTopicId" in value) return null;
2782
+ const defaultAudience = parseNewsletterAudience(value.defaultAudience);
2611
2783
  const topics = parseNamedItems(value.topics, parseNewsletterListId);
2612
- return defaultTopicId !== null && topics !== null ? {
2784
+ return defaultAudience.ok && topics !== null ? {
2613
2785
  provider: "native",
2614
- defaultTopicId,
2786
+ defaultAudience: defaultAudience.value,
2615
2787
  topics
2616
2788
  } : null;
2617
2789
  }
@@ -2627,57 +2799,47 @@ function parseBrevoRuntimeProvider(value) {
2627
2799
  function parseMailchimpRuntimeProvider(value) {
2628
2800
  if (!Array.isArray(value.audiences)) return null;
2629
2801
  const defaultAudienceId = parseMailchimpAudienceId(value.defaultAudienceId);
2630
- if (defaultAudienceId === null) return null;
2631
- const audiences = [];
2632
- for (const audience of value.audiences) {
2633
- if (!isRecord$8(audience)) return null;
2634
- const id = parseMailchimpAudienceId(audience.id);
2635
- const name = parseName(audience.name);
2636
- if (id === null || name === null || !Array.isArray(audience.interests)) return null;
2637
- const interests = parseMailchimpInterests(audience.interests);
2638
- if (interests === null) return null;
2639
- audiences.push({
2640
- id,
2641
- name,
2642
- interests
2643
- });
2644
- }
2645
- return {
2802
+ const audiences = Array.from(value.audiences, parseMailchimpAudience);
2803
+ return defaultAudienceId !== null && audiences.every((audience) => audience !== null) ? {
2646
2804
  provider: "mailchimp",
2647
2805
  defaultAudienceId,
2648
2806
  audiences
2649
- };
2807
+ } : null;
2650
2808
  }
2651
- function parseMailchimpInterests(value) {
2652
- const interests = [];
2653
- for (const interest of value) {
2654
- if (!isRecord$8(interest)) return null;
2655
- const id = parseMailchimpInterestId(interest.id);
2656
- const name = parseName(interest.name);
2657
- const categoryName = parseName(interest.categoryName);
2658
- if (id === null || name === null || categoryName === null) return null;
2659
- interests.push({
2660
- id,
2661
- name,
2662
- categoryName
2663
- });
2664
- }
2665
- return interests;
2809
+ function parseMailchimpAudience(value) {
2810
+ if (!isRecord$8(value) || !Array.isArray(value.interests)) return null;
2811
+ const id = parseMailchimpAudienceId(value.id);
2812
+ const name = parseName(value.name);
2813
+ const interests = Array.from(value.interests, parseMailchimpInterest);
2814
+ return id !== null && name !== null && interests.every((interest) => interest !== null) ? {
2815
+ id,
2816
+ name,
2817
+ interests
2818
+ } : null;
2819
+ }
2820
+ function parseMailchimpInterest(value) {
2821
+ if (!isRecord$8(value)) return null;
2822
+ const id = parseMailchimpInterestId(value.id);
2823
+ const name = parseName(value.name);
2824
+ const categoryName = parseName(value.categoryName);
2825
+ return id !== null && name !== null && categoryName !== null ? {
2826
+ id,
2827
+ name,
2828
+ categoryName
2829
+ } : null;
2666
2830
  }
2667
2831
  function parseNamedItems(value, parseId) {
2668
2832
  if (!Array.isArray(value)) return null;
2669
- const items = [];
2670
- for (const item of value) {
2833
+ const items = Array.from(value, (item) => {
2671
2834
  if (!isRecord$8(item)) return null;
2672
2835
  const id = parseId(item.id);
2673
2836
  const name = parseName(item.name);
2674
- if (id === null || name === null) return null;
2675
- items.push({
2837
+ return id !== null && name !== null ? {
2676
2838
  id,
2677
2839
  name
2678
- });
2679
- }
2680
- return items;
2840
+ } : null;
2841
+ });
2842
+ return items.every((item) => item !== null) ? items : null;
2681
2843
  }
2682
2844
  function parseName(value) {
2683
2845
  if (typeof value !== "string") return null;
@@ -2693,6 +2855,7 @@ function invalidRuntimeContext() {
2693
2855
  //#endregion
2694
2856
  //#region ../core/src/mailing-list-targeting-parser.ts
2695
2857
  function parseMailingListPlacementTargeting(value) {
2858
+ if (value == null) return ok(DEFAULT_MAILING_LIST_TARGETING);
2696
2859
  if (!isRecord$7(value)) return targetingError("shape");
2697
2860
  if (value.mode === "default") return ok({ mode: "default" });
2698
2861
  if (value.mode !== "custom") return targetingError("mode");
@@ -2705,6 +2868,11 @@ function parseMailingListPlacementTargeting(value) {
2705
2868
  }
2706
2869
  }
2707
2870
  function parseNativeTargeting(selection) {
2871
+ if (selection.tag === "general") return Object.keys(selection).length === 1 ? ok({
2872
+ mode: "custom",
2873
+ provider: "native",
2874
+ selection: { tag: "general" }
2875
+ }) : targetingError("selection");
2708
2876
  if (selection.tag === "fixed") {
2709
2877
  const topicIds = parseNonEmptyUniqueIds(selection.topicIds, parseNewsletterListId);
2710
2878
  return topicIds.ok ? ok({
@@ -2791,14 +2959,11 @@ function parseNonEmptyUniqueIds(value, parseId) {
2791
2959
  }
2792
2960
  function parseUniqueIds(value, parseId) {
2793
2961
  if (!Array.isArray(value)) return targetingError("targets");
2794
- const parsed = [];
2795
- for (const item of value) {
2962
+ return Array.from(value).reduce((parsed, item) => {
2963
+ if (!parsed.ok) return parsed;
2796
2964
  const id = parseId(item);
2797
- if (id === null) return targetingError("targets");
2798
- if (parsed.includes(id)) return targetingError("duplicate_target");
2799
- parsed.push(id);
2800
- }
2801
- return ok(parsed);
2965
+ return id === null ? targetingError("targets") : parsed.value.includes(id) ? targetingError("duplicate_target") : ok([...parsed.value, id]);
2966
+ }, ok([]));
2802
2967
  }
2803
2968
  function isRecord$7(value) {
2804
2969
  return typeof value === "object" && value !== null && !Array.isArray(value);
@@ -3227,6 +3392,45 @@ function resolveSiteVerticalBookingConfig(rawConfig) {
3227
3392
  };
3228
3393
  }
3229
3394
  //#endregion
3395
+ //#region ../core/src/event-preset-vocabulary.ts
3396
+ const EVENT_PRESET_VOCABULARY = {
3397
+ event: {
3398
+ label: "Event",
3399
+ pluralLabel: "Events",
3400
+ description: "Any event, repeating or manually dated, that does not fit the other categories."
3401
+ },
3402
+ class: {
3403
+ label: "Class",
3404
+ pluralLabel: "Classes",
3405
+ description: "Regular scheduled classes and group sessions."
3406
+ },
3407
+ course: {
3408
+ label: "Course",
3409
+ pluralLabel: "Courses",
3410
+ description: "A fixed set of dates that people book together."
3411
+ },
3412
+ term: {
3413
+ label: "Term",
3414
+ pluralLabel: "Terms",
3415
+ description: "A fixed term of sessions that people book together."
3416
+ },
3417
+ workshop: {
3418
+ label: "Workshop",
3419
+ pluralLabel: "Workshops",
3420
+ description: "One-off workshops, day sessions, or special events."
3421
+ },
3422
+ retreat: {
3423
+ label: "Retreat",
3424
+ pluralLabel: "Retreats",
3425
+ description: "Multi-day or residential immersive experiences."
3426
+ },
3427
+ performance: {
3428
+ label: "Performance",
3429
+ pluralLabel: "Performances",
3430
+ description: "Audience-facing shows, concerts, recitals, or showcases."
3431
+ }
3432
+ };
3433
+ //#endregion
3230
3434
  //#region ../api/src/publicEventEndpointDefinitions.ts
3231
3435
  const PUBLIC_EVENT_ENDPOINT_DEFINITIONS = defineEndpointDefinitions({
3232
3436
  checkPublicEventRegistrationNextStep: {
@@ -7753,7 +7957,96 @@ const themeLookMetadataSchema = z.object({
7753
7957
  skinMarks: skinMarksSchema.optional()
7754
7958
  });
7755
7959
  //#endregion
7756
- //#region ../theme-core/src/lib/media.ts
7960
+ //#region ../media-core/src/identifiers.ts
7961
+ function slugifyMediaIdentifier(value) {
7962
+ return value.toLowerCase().trim().replace(/[^a-z0-9\s-]/g, "").replace(/\s+/g, "-").replace(/-+/g, "-");
7963
+ }
7964
+ function parseMediaIdentifier(value) {
7965
+ if (typeof value !== "string") return err({ kind: "not_string" });
7966
+ const trimmed = value.trim();
7967
+ if (trimmed.length === 0) return err({ kind: "empty_identifier" });
7968
+ const normalized = slugifyMediaIdentifier(trimmed);
7969
+ if (normalized.length === 0) return err({
7970
+ kind: "empty_after_normalization",
7971
+ value
7972
+ });
7973
+ return ok(normalized);
7974
+ }
7975
+ /**
7976
+ * Admits identifiers read from trusted persistence without repairing dirty data in memory.
7977
+ * User input should use parseMediaIdentifier, which intentionally canonicalizes it.
7978
+ */
7979
+ function parseCanonicalMediaIdentifier(value) {
7980
+ if (typeof value !== "string") return err({ kind: "not_string" });
7981
+ const parsed = parseMediaIdentifier(value);
7982
+ if (!parsed.ok) return parsed;
7983
+ if (value !== parsed.value) return err({
7984
+ kind: "non_canonical_identifier",
7985
+ value,
7986
+ canonical: parsed.value
7987
+ });
7988
+ return parsed;
7989
+ }
7990
+ //#endregion
7991
+ //#region ../media-core/src/mediaIdentifierSchema.ts
7992
+ /** Strict stored-reference admission delegates the canonical identifier decision. */
7993
+ const mediaIdentifierSchema = z.string().transform((value, context) => {
7994
+ const parsed = parseCanonicalMediaIdentifier(value);
7995
+ if (parsed.ok) return parsed.value;
7996
+ context.addIssue({
7997
+ code: "custom",
7998
+ message: "Expected a canonical media identifier"
7999
+ });
8000
+ return z.NEVER;
8001
+ });
8002
+ //#endregion
8003
+ //#region ../media-core/src/mediaKinds.ts
8004
+ const mediaTypeDiscriminants = [
8005
+ "image",
8006
+ "video",
8007
+ "audio",
8008
+ "document",
8009
+ "spreadsheet",
8010
+ "archive"
8011
+ ];
8012
+ //#endregion
8013
+ //#region ../media-core/src/renderableMediaAsset.ts
8014
+ /** A portable reference never uses an incidental UUID, URL or storage snapshot for identity. */
8015
+ function resolveManagedMediaReference(reference, index) {
8016
+ const asset = index?.byIdentifier.get(reference.identifier);
8017
+ return asset ? {
8018
+ kind: "resolved",
8019
+ reference,
8020
+ asset
8021
+ } : {
8022
+ kind: "missing",
8023
+ reference
8024
+ };
8025
+ }
8026
+ //#endregion
8027
+ //#region ../media-core/src/renderableMediaAssetAdmission.ts
8028
+ const mediaAssetIdSchema = z.string().transform((value, context) => {
8029
+ const parsed = parseMediaAssetId(value);
8030
+ if (parsed.ok) return parsed.value;
8031
+ context.addIssue({
8032
+ code: "custom",
8033
+ message: "Expected a media asset ID"
8034
+ });
8035
+ return z.NEVER;
8036
+ });
8037
+ const nonemptyStorageValue = z.string().refine((value) => value.trim().length > 0);
8038
+ const renderableMediaAssetSchema = z.object({
8039
+ id: mediaAssetIdSchema,
8040
+ identifier: mediaIdentifierSchema,
8041
+ storageBucket: nonemptyStorageValue,
8042
+ storagePath: nonemptyStorageValue,
8043
+ mimeType: nonemptyStorageValue,
8044
+ filename: z.string().optional(),
8045
+ width: z.number().positive().nullable(),
8046
+ height: z.number().positive().nullable()
8047
+ });
8048
+ //#endregion
8049
+ //#region ../media-core/src/media.ts
7757
8050
  const hotspotSchema = z.object({
7758
8051
  x: z.number().min(0).max(1),
7759
8052
  y: z.number().min(0).max(1),
@@ -7787,56 +8080,149 @@ const transformSchema = z.object({
7787
8080
  rotation: rotationSchema.optional()
7788
8081
  });
7789
8082
  const mediaPosterSchema = z.discriminatedUnion("tag", [z.object({
7790
- tag: z.literal("storage"),
7791
- assetId: z.string().optional(),
7792
- storageBucket: z.string().min(1),
7793
- storagePath: z.string().min(1)
7794
- }), z.object({
8083
+ tag: z.literal("media"),
8084
+ identifier: mediaIdentifierSchema
8085
+ }).strict(), z.object({
7795
8086
  tag: z.literal("external-src"),
7796
- assetId: z.string().optional(),
7797
8087
  src: z.string().url()
7798
- })]);
7799
- const mediaBaseSchema = z.object({
8088
+ }).strict()]);
8089
+ const mediaUsageShape = {
7800
8090
  purpose: z.string().optional(),
7801
8091
  placeholder: z.boolean().default(true).optional(),
7802
- assetId: z.string().optional(),
7803
- identifier: z.string().optional(),
7804
- src: z.string().optional(),
7805
8092
  alt: z.string().optional(),
7806
8093
  filename: z.string().optional(),
7807
- mimeType: z.string().optional(),
7808
8094
  width: z.number().optional(),
7809
8095
  height: z.number().optional(),
7810
- storageBucket: z.string().optional(),
7811
- storagePath: z.string().optional(),
7812
8096
  poster: mediaPosterSchema.optional(),
7813
8097
  transform: transformSchema.optional()
7814
- });
7815
- const mediaIdentifierSchema = z.string().trim().min(1).brand();
8098
+ };
8099
+ const environmentExclusions = {
8100
+ assetId: z.never().optional(),
8101
+ id: z.never().optional(),
8102
+ src: z.never().optional(),
8103
+ storageBucket: z.never().optional(),
8104
+ storagePath: z.never().optional(),
8105
+ mimeType: z.never().optional()
8106
+ };
8107
+ const mediaBaseSchema = z.object({
8108
+ ...mediaUsageShape,
8109
+ ...environmentExclusions,
8110
+ identifier: mediaIdentifierSchema
8111
+ }).strict();
7816
8112
  const imageMediaSchema = mediaBaseSchema.extend({ type: z.literal("image") });
7817
8113
  const videoMediaSchema = mediaBaseSchema.extend({ type: z.literal("video") });
7818
- const audioMediaSchema = mediaBaseSchema.extend({ type: z.literal("audio") });
7819
- const documentMediaSchema = mediaBaseSchema.extend({ type: z.literal("document") });
7820
- const spreadsheetMediaSchema = mediaBaseSchema.extend({ type: z.literal("spreadsheet") });
7821
- const archiveMediaSchema = mediaBaseSchema.extend({ type: z.literal("archive") });
7822
- const mediaSchema = z.discriminatedUnion("type", [
7823
- imageMediaSchema,
7824
- videoMediaSchema,
7825
- audioMediaSchema,
7826
- documentMediaSchema,
7827
- spreadsheetMediaSchema,
7828
- archiveMediaSchema
7829
- ]);
7830
- const persistableMediaBaseSchema = mediaBaseSchema.extend({ identifier: mediaIdentifierSchema });
7831
- z.discriminatedUnion("type", [
7832
- persistableMediaBaseSchema.extend({ type: z.literal("image") }),
7833
- persistableMediaBaseSchema.extend({ type: z.literal("video") }),
7834
- persistableMediaBaseSchema.extend({ type: z.literal("audio") }),
7835
- persistableMediaBaseSchema.extend({ type: z.literal("document") }),
7836
- persistableMediaBaseSchema.extend({ type: z.literal("spreadsheet") }),
7837
- persistableMediaBaseSchema.extend({ type: z.literal("archive") })
8114
+ const mediaSchema = mediaBaseSchema.extend({ type: z.enum(mediaTypeDiscriminants) });
8115
+ const externalMediaSchema = z.object({
8116
+ ...mediaUsageShape,
8117
+ ...environmentExclusions,
8118
+ type: z.enum(mediaTypeDiscriminants),
8119
+ src: z.string().min(1),
8120
+ identifier: z.never().optional()
8121
+ }).strict();
8122
+ const assetMediaSchema = z.object({
8123
+ ...mediaUsageShape,
8124
+ ...environmentExclusions,
8125
+ type: z.enum(mediaTypeDiscriminants),
8126
+ asset: renderableMediaAssetSchema,
8127
+ identifier: z.never().optional()
8128
+ }).strict();
8129
+ const runtimeMediaSchema = z.union([
8130
+ mediaSchema,
8131
+ externalMediaSchema,
8132
+ assetMediaSchema
7838
8133
  ]);
7839
8134
  //#endregion
8135
+ //#region ../media-core/src/transformRect.ts
8136
+ function isValidTransformRectStructure(rect) {
8137
+ if (!Array.isArray(rect) || rect.length !== 4) return false;
8138
+ const [left, top, width, height] = rect;
8139
+ return Number.isFinite(left) && Number.isFinite(top) && Number.isFinite(width) && Number.isFinite(height) && left >= 0 && top >= 0 && width > 0 && height > 0;
8140
+ }
8141
+ //#endregion
8142
+ //#region ../media-core/src/aspectTransforms.ts
8143
+ const DEFAULT_RECT_EPSILON = .5;
8144
+ function parseAspectRatioValue(value) {
8145
+ if (typeof value === "number") return Number.isFinite(value) && value > 0 ? value : void 0;
8146
+ if (typeof value !== "string") return void 0;
8147
+ const normalized = value.trim();
8148
+ if (!normalized) return void 0;
8149
+ if (normalized === "auto") return void 0;
8150
+ if (normalized.includes(":") || normalized.includes("/")) {
8151
+ const delimiter = normalized.includes(":") ? ":" : "/";
8152
+ const [wRaw, hRaw] = normalized.split(delimiter);
8153
+ const width = Number(wRaw?.trim());
8154
+ const height = Number(hRaw?.trim());
8155
+ if (!Number.isFinite(width) || !Number.isFinite(height) || width <= 0 || height <= 0) return;
8156
+ return width / height;
8157
+ }
8158
+ const numeric = Number(normalized);
8159
+ if (!Number.isFinite(numeric) || numeric <= 0) return void 0;
8160
+ return numeric;
8161
+ }
8162
+ const MAX_ASPECT_DISTANCE = .25;
8163
+ function resolveMediaTransformForAspect(transform, targetAspect) {
8164
+ if (!transform) return {};
8165
+ const base = {
8166
+ rect: transform.rect,
8167
+ hotspot: transform.hotspot,
8168
+ rotation: transform.rotation
8169
+ };
8170
+ const target = parseAspectRatioValue(targetAspect);
8171
+ const crops = transform.aspectCrops;
8172
+ if (!target || !crops || Object.keys(crops).length === 0) return base;
8173
+ let best = null;
8174
+ let bestDistance = Number.POSITIVE_INFINITY;
8175
+ for (const [key, candidate] of Object.entries(crops)) {
8176
+ const candidateAspect = parseAspectRatioValue(candidate.aspect ?? key);
8177
+ if (!candidateAspect) continue;
8178
+ const distance = Math.abs(Math.log(candidateAspect / target));
8179
+ if (distance < bestDistance) {
8180
+ best = candidate;
8181
+ bestDistance = distance;
8182
+ }
8183
+ }
8184
+ if (!best) return base;
8185
+ if (bestDistance > MAX_ASPECT_DISTANCE) return base;
8186
+ return {
8187
+ rect: best.rect ?? base.rect,
8188
+ hotspot: best.hotspot ?? base.hotspot,
8189
+ rotation: best.rotation ?? base.rotation
8190
+ };
8191
+ }
8192
+ function isValidTransformRect(rect) {
8193
+ return isValidTransformRectStructure(rect);
8194
+ }
8195
+ function isExplicitRectCrop(rect, sourceWidth, sourceHeight, epsilon = DEFAULT_RECT_EPSILON) {
8196
+ if (!isValidTransformRect(rect)) return false;
8197
+ if (!(typeof sourceWidth === "number" && Number.isFinite(sourceWidth) && sourceWidth > 0 && typeof sourceHeight === "number" && Number.isFinite(sourceHeight) && sourceHeight > 0)) return true;
8198
+ const [x, y, width, height] = rect;
8199
+ return !(Math.abs(x) <= epsilon && Math.abs(y) <= epsilon && Math.abs(width - sourceWidth) <= epsilon && Math.abs(height - sourceHeight) <= epsilon);
8200
+ }
8201
+ //#endregion
8202
+ //#region ../media-core/src/rotation.ts
8203
+ function rotateNormalizedPoint(point, rotation) {
8204
+ const x = point.x;
8205
+ const y = point.y;
8206
+ switch (rotation) {
8207
+ case 0: return {
8208
+ x,
8209
+ y
8210
+ };
8211
+ case 90: return {
8212
+ x: 1 - y,
8213
+ y: x
8214
+ };
8215
+ case 180: return {
8216
+ x: 1 - x,
8217
+ y: 1 - y
8218
+ };
8219
+ case 270: return {
8220
+ x: y,
8221
+ y: 1 - x
8222
+ };
8223
+ }
8224
+ }
8225
+ //#endregion
7840
8226
  //#region ../theme-core/src/palette/schema.ts
7841
8227
  const colourHexString = z.string().regex(/^#([0-9A-Fa-f]{3}){1,2}$/);
7842
8228
  const paletteColorSchema = z.object({
@@ -16385,6 +16771,21 @@ const boxBackgroundSchema = z.discriminatedUnion("type", [
16385
16771
  showVideoOnMobile: z.boolean().nullable().optional()
16386
16772
  })
16387
16773
  ]);
16774
+ /** Application rendering sources are admitted separately from saved references. */
16775
+ const runtimeBoxBackgroundSchema = z.discriminatedUnion("type", [
16776
+ boxBackgroundSchema.options[0],
16777
+ boxBackgroundSchema.options[1],
16778
+ boxBackgroundSchema.options[2].extend({ image: z.union([
16779
+ imageMediaSchema,
16780
+ externalMediaSchema.extend({ type: z.literal("image") }),
16781
+ assetMediaSchema.extend({ type: z.literal("image") })
16782
+ ]).nullable().optional() }),
16783
+ boxBackgroundSchema.options[3].extend({ image: z.union([
16784
+ videoMediaSchema,
16785
+ externalMediaSchema.extend({ type: z.literal("video") }),
16786
+ assetMediaSchema.extend({ type: z.literal("video") })
16787
+ ]).nullable().optional() })
16788
+ ]);
16388
16789
  function normalizeAuthoringOpacity(value) {
16389
16790
  if (!isRecord$6(value)) return value;
16390
16791
  return {
@@ -23234,97 +23635,6 @@ function registerManifest(manifest) {
23234
23635
  return manifest;
23235
23636
  }
23236
23637
  //#endregion
23237
- //#region ../media-core/src/transformRect.ts
23238
- function isValidTransformRectStructure(rect) {
23239
- if (!Array.isArray(rect) || rect.length !== 4) return false;
23240
- const [left, top, width, height] = rect;
23241
- return Number.isFinite(left) && Number.isFinite(top) && Number.isFinite(width) && Number.isFinite(height) && left >= 0 && top >= 0 && width > 0 && height > 0;
23242
- }
23243
- //#endregion
23244
- //#region ../media-core/src/aspectTransforms.ts
23245
- const DEFAULT_RECT_EPSILON = .5;
23246
- function parseAspectRatioValue(value) {
23247
- if (typeof value === "number") return Number.isFinite(value) && value > 0 ? value : void 0;
23248
- if (typeof value !== "string") return void 0;
23249
- const normalized = value.trim();
23250
- if (!normalized) return void 0;
23251
- if (normalized === "auto") return void 0;
23252
- if (normalized.includes(":") || normalized.includes("/")) {
23253
- const delimiter = normalized.includes(":") ? ":" : "/";
23254
- const [wRaw, hRaw] = normalized.split(delimiter);
23255
- const width = Number(wRaw?.trim());
23256
- const height = Number(hRaw?.trim());
23257
- if (!Number.isFinite(width) || !Number.isFinite(height) || width <= 0 || height <= 0) return;
23258
- return width / height;
23259
- }
23260
- const numeric = Number(normalized);
23261
- if (!Number.isFinite(numeric) || numeric <= 0) return void 0;
23262
- return numeric;
23263
- }
23264
- const MAX_ASPECT_DISTANCE = .25;
23265
- function resolveMediaTransformForAspect(transform, targetAspect) {
23266
- if (!transform) return {};
23267
- const base = {
23268
- rect: transform.rect,
23269
- hotspot: transform.hotspot,
23270
- rotation: transform.rotation
23271
- };
23272
- const target = parseAspectRatioValue(targetAspect);
23273
- const crops = transform.aspectCrops;
23274
- if (!target || !crops || Object.keys(crops).length === 0) return base;
23275
- let best = null;
23276
- let bestDistance = Number.POSITIVE_INFINITY;
23277
- for (const [key, candidate] of Object.entries(crops)) {
23278
- const candidateAspect = parseAspectRatioValue(candidate.aspect ?? key);
23279
- if (!candidateAspect) continue;
23280
- const distance = Math.abs(Math.log(candidateAspect / target));
23281
- if (distance < bestDistance) {
23282
- best = candidate;
23283
- bestDistance = distance;
23284
- }
23285
- }
23286
- if (!best) return base;
23287
- if (bestDistance > MAX_ASPECT_DISTANCE) return base;
23288
- return {
23289
- rect: best.rect ?? base.rect,
23290
- hotspot: best.hotspot ?? base.hotspot,
23291
- rotation: best.rotation ?? base.rotation
23292
- };
23293
- }
23294
- function isValidTransformRect(rect) {
23295
- return isValidTransformRectStructure(rect);
23296
- }
23297
- function isExplicitRectCrop(rect, sourceWidth, sourceHeight, epsilon = DEFAULT_RECT_EPSILON) {
23298
- if (!isValidTransformRect(rect)) return false;
23299
- if (!(typeof sourceWidth === "number" && Number.isFinite(sourceWidth) && sourceWidth > 0 && typeof sourceHeight === "number" && Number.isFinite(sourceHeight) && sourceHeight > 0)) return true;
23300
- const [x, y, width, height] = rect;
23301
- return !(Math.abs(x) <= epsilon && Math.abs(y) <= epsilon && Math.abs(width - sourceWidth) <= epsilon && Math.abs(height - sourceHeight) <= epsilon);
23302
- }
23303
- //#endregion
23304
- //#region ../media-core/src/rotation.ts
23305
- function rotateNormalizedPoint(point, rotation) {
23306
- const x = point.x;
23307
- const y = point.y;
23308
- switch (rotation) {
23309
- case 0: return {
23310
- x,
23311
- y
23312
- };
23313
- case 90: return {
23314
- x: 1 - y,
23315
- y: x
23316
- };
23317
- case 180: return {
23318
- x: 1 - x,
23319
- y: 1 - y
23320
- };
23321
- case 270: return {
23322
- x: y,
23323
- y: 1 - x
23324
- };
23325
- }
23326
- }
23327
- //#endregion
23328
23638
  //#region ../blocks/src/system/node/schema.ts
23329
23639
  const transformStepSchema = z.object({
23330
23640
  id: z.string().min(1, "Transform requires an identifier"),
@@ -24262,6 +24572,14 @@ z.object({
24262
24572
  icon: z.string().optional()
24263
24573
  });
24264
24574
  //#endregion
24575
+ //#region ../blocks/src/lib/typeGuards.ts
24576
+ /**
24577
+ * Strict record guard: excludes null, primitives, and arrays.
24578
+ */
24579
+ function isRecord$5(value) {
24580
+ return typeof value === "object" && value !== null && !Array.isArray(value);
24581
+ }
24582
+ //#endregion
24265
24583
  //#region ../blocks/src/system/designCapabilities.ts
24266
24584
  function declareBlockDesignCapability(capability) {
24267
24585
  return {
@@ -26242,6 +26560,12 @@ function parseBackgroundOverlay(value) {
26242
26560
  const result = boxBackgroundOverlaySchema.safeParse(value);
26243
26561
  return result.success ? result.data : void 0;
26244
26562
  }
26563
+ /** Rendering accepts explicit application sources separately from stored references. */
26564
+ const runtimeBackgroundContentSchema = runtimeBoxBackgroundSchema;
26565
+ function parseRuntimeBackgroundContent(value) {
26566
+ const result = runtimeBackgroundContentSchema.safeParse(value);
26567
+ return result.success ? result.data : null;
26568
+ }
26245
26569
  //#endregion
26246
26570
  //#region ../blocks/src/system/transforms/registry/_utils/themeColorResolution.ts
26247
26571
  function resolveBackgroundColorValue(value, context) {
@@ -31115,6 +31439,15 @@ const formBlockDefinition = {
31115
31439
  runtime: formBlockRuntime
31116
31440
  };
31117
31441
  //#endregion
31442
+ //#region ../blocks/src/system/helpers/normalizeMailingListBlockContent.ts
31443
+ /** Fill the declared default without admitting or rewriting invalid custom settings. */
31444
+ function normalizeMailingListBlockContent(record) {
31445
+ return record.targeting == null ? {
31446
+ ...record,
31447
+ targeting: DEFAULT_MAILING_LIST_TARGETING
31448
+ } : record;
31449
+ }
31450
+ //#endregion
31118
31451
  //#region ../blocks/src/system/blocks/contact-newsletter.ts
31119
31452
  const contactNewsletterLayout = styledSection({
31120
31453
  children: sectionContainer([el("div", { className: "contact-newsletter-grid" }, [el("div", { className: "contact-newsletter-column contact-newsletter-contact" }, [el("div", { className: "contact-newsletter-copy" }, [text({
@@ -31278,6 +31611,10 @@ const contactNewsletterBlockDefinition = {
31278
31611
  ],
31279
31612
  icon: "MailPlus"
31280
31613
  }),
31614
+ contentNormalizer: {
31615
+ fieldIds: ["targeting"],
31616
+ normalize: normalizeMailingListBlockContent
31617
+ },
31281
31618
  dataSchemas: { form: formDataSchema.optional() },
31282
31619
  dataLoaders: { form: {
31283
31620
  endpoint: "getPublicFormById",
@@ -32695,14 +33032,6 @@ const mediaTransforms = [{
32695
33032
  }
32696
33033
  }];
32697
33034
  //#endregion
32698
- //#region ../blocks/src/lib/typeGuards.ts
32699
- /**
32700
- * Strict record guard: excludes null, primitives, and arrays.
32701
- */
32702
- function isRecord$5(value) {
32703
- return typeof value === "object" && value !== null && !Array.isArray(value);
32704
- }
32705
- //#endregion
32706
33035
  //#region ../blocks/src/system/transforms/registry/content.ts
32707
33036
  const supportedRichTextTokenSchema = z.enum(["year", "site_name"]);
32708
33037
  const interpolateRichTextTokensOptionsSchema = z.object({ tokens: z.array(supportedRichTextTokenSchema).default(["year", "site_name"]) });
@@ -35495,14 +35824,6 @@ const weeklySlotAvailabilityBlockDefinition = {
35495
35824
  } }
35496
35825
  };
35497
35826
  //#endregion
35498
- //#region ../blocks/src/lib/media.ts
35499
- const PUBLIC_DOWNLOAD_MEDIA_TYPES = [
35500
- "document",
35501
- "spreadsheet",
35502
- "archive",
35503
- "audio"
35504
- ];
35505
- //#endregion
35506
35827
  //#region ../blocks/src/system/manifest/richTextSchema.ts
35507
35828
  /**
35508
35829
  * Shared Rich Text Schema
@@ -35678,6 +35999,14 @@ bookingPaymentOptionSchemas.flexibleBalanceConfig;
35678
35999
  bookingPaymentOptionSchemas.paymentCollectionOption;
35679
36000
  const bookingPaymentTermsSchema = bookingPaymentOptionSchemas.paymentTerms;
35680
36001
  bookingPaymentOptionSchemas.paymentOptionsChoice;
36002
+ z.strictObject({
36003
+ currency: z.string().trim().length(3),
36004
+ totalCents: nonNegativeIntegerSchema,
36005
+ dueNowCents: nonNegativeIntegerSchema,
36006
+ laterPaymentAmountsCents: z.array(nonNegativeIntegerSchema),
36007
+ paymentOption: z.union([createBookingPaymentOptionSchemas("strict").paymentCollectionOption, z.strictObject({ kind: z.literal("not_required") })]),
36008
+ promisedDiscountId: z.string().min(1).nullable().optional()
36009
+ });
35681
36010
  //#endregion
35682
36011
  //#region ../blocks/src/system/blocks/shared/bookingPaymentTerms.ts
35683
36012
  const blockBookingPaymentTermsSchema = bookingPaymentTermsSchema;
@@ -35918,6 +36247,10 @@ const publicEventBaseSchema = z.object({
35918
36247
  eventDestinationMode: eventDestinationModeSchema.optional(),
35919
36248
  scheduleScope: eventScheduleScopeSchema$2.optional(),
35920
36249
  recurrence: publicEventRecurrenceSchema.optional(),
36250
+ scheduleSummary: z.object({
36251
+ recurrenceDescription: z.string(),
36252
+ startTime: z.string().regex(/^(?:[01]\d|2[0-3]):[0-5]\d$/).nullable()
36253
+ }).nullable().optional(),
35921
36254
  id: z.string(),
35922
36255
  occurrenceId: z.string(),
35923
36256
  seriesId: z.string(),
@@ -35926,7 +36259,7 @@ const publicEventBaseSchema = z.object({
35926
36259
  content: z.object({ body: RichTextDocSchema.nullable().optional() }).optional(),
35927
36260
  presentation: z.object({
35928
36261
  summary: z.string().nullable().optional(),
35929
- image: mediaSchema.nullable().optional()
36262
+ image: runtimeMediaSchema.nullable().optional()
35930
36263
  }).optional(),
35931
36264
  slug: z.string(),
35932
36265
  path: z.string().nullable(),
@@ -36328,67 +36661,266 @@ const occurrenceContextSchema = z.object({
36328
36661
  overrides: z.record(z.string(), z.unknown()).nullable().optional()
36329
36662
  }).nullable();
36330
36663
  //#endregion
36331
- //#region ../blocks/src/system/runtime/api/readAuthority.ts
36332
- const browserReadAuthoritySchema = z.discriminatedUnion("kind", [z.object({ kind: z.literal("published") }).strict(), z.object({
36333
- kind: z.literal("preview"),
36334
- token: z.string().min(1)
36335
- }).strict()]);
36336
- const unsupportedEventRegistrationPageSurfaceSubjectSchema = z.object({
36337
- kind: z.literal("unsupported"),
36338
- reason: z.enum([
36339
- "unsupportedSubject",
36340
- "invalidEventSeriesId",
36341
- "invalidEventSeriesBookingScope",
36342
- "invalidEventOfferingId",
36343
- "invalidEventOfferingBookingScope",
36344
- "invalidEventOfferingCatalogue"
36345
- ])
36346
- });
36347
- const eventRegistrationPageSurfaceSubjectSchema = z.unknown().transform((value, ctx) => {
36348
- const unsupported = unsupportedEventRegistrationPageSurfaceSubjectSchema.safeParse(value);
36349
- if (unsupported.success) return unsupported.data;
36350
- const parsed = parseEventPageSurfaceSubject(value);
36351
- if (parsed.ok) return parsed.value;
36352
- ctx.addIssue({
36353
- code: z.ZodIssueCode.custom,
36354
- message: `Invalid event page-surface subject (${parsed.error.kind})`
36355
- });
36356
- return z.NEVER;
36357
- });
36358
- function resolveEventRegistrationScope(context) {
36359
- const occurrenceSeriesId = context.occurrenceContext?.seriesId;
36360
- if (occurrenceSeriesId) return {
36361
- kind: "occurrenceSeries",
36362
- seriesId: occurrenceSeriesId
36664
+ //#region ../blocks/src/system/runtime/nodes/events/domain/capacity.ts
36665
+ /**
36666
+ * Compute remaining spots for an event from `availableSpots` with a fallback
36667
+ * to `capacity - registeredCount`. Returns `{ available: null, isFull: false }`
36668
+ * for uncapped events so call sites can render no capacity indicator at all.
36669
+ *
36670
+ * The single source of truth for capacity math across event cards, agenda
36671
+ * rows, and timetable cells — keep wording variations in the call sites.
36672
+ */
36673
+ function getEventAvailability(event) {
36674
+ if (event.capacity == null) return {
36675
+ available: null,
36676
+ isFull: false
36363
36677
  };
36364
- if (context.subject?.kind === "eventSeries") return {
36365
- kind: "pageSurfaceSeries",
36366
- seriesId: context.subject.eventSeriesId
36678
+ const available = event.availableSpots ?? event.capacity - event.registeredCount;
36679
+ if (available <= 0) return {
36680
+ available: 0,
36681
+ isFull: true
36367
36682
  };
36368
- if (context.subject?.kind === "eventOffering") return {
36369
- kind: "pageSurfaceOffering",
36370
- offeringId: context.subject.eventOfferingId,
36371
- seriesIds: context.subject.catalogueEventSeriesIds
36683
+ return {
36684
+ available,
36685
+ isFull: false
36372
36686
  };
36373
- if (context.subject?.kind === "unsupported") return {
36374
- kind: "unsupportedPageSurfaceSubject",
36375
- reason: context.subject.reason
36687
+ }
36688
+ /**
36689
+ * Pick the capacity row a booking decision should read: the selected
36690
+ * attendance mode's availability when the visitor has chosen one and the
36691
+ * event publishes it, otherwise the event-level aggregate.
36692
+ *
36693
+ * The mode row is used wholesale. A mode with `capacity: null` on a capped
36694
+ * event is uncapped *by design*: the registration write path applies the
36695
+ * same rule via `getEffectiveOccurrenceCapacityForAttendanceMode`, so falling
36696
+ * back to the event's `availableSpots` here would block bookings the server
36697
+ * accepts. (Waitlist claims are still occurrence-scoped; see issue 1377.)
36698
+ */
36699
+ function selectEventCapacitySource(event, attendanceMode) {
36700
+ if (!attendanceMode) return event;
36701
+ return (event.attendanceModes ?? []).find((mode) => mode.mode === attendanceMode) ?? event;
36702
+ }
36703
+ /**
36704
+ * Whether the visitor's next step is the waitlist: the source is full *and*
36705
+ * a waitlist is open. The one place this is decided — cards, rows, the
36706
+ * wizard's steps and its action bar all route through here so they agree,
36707
+ * including for producers that omit `availableSpots` and rely on the
36708
+ * `capacity - registeredCount` fallback.
36709
+ */
36710
+ function canJoinEventWaitlist(event, options) {
36711
+ const source = selectEventCapacitySource(event, options?.attendanceMode);
36712
+ return getEventAvailability(source).isFull && source.waitlistEnabled === true;
36713
+ }
36714
+ /**
36715
+ * Remaining places at or below which an event reads as scarce and the exact
36716
+ * count is worth showing. Above it, no number is shown at all: "147 places
36717
+ * left" is not something a visitor acts on, and it is the figure most likely
36718
+ * to be stale on a cached page.
36719
+ *
36720
+ * max( min(3, ceil(capacity * 0.25)), ceil(capacity * 0.05) )
36721
+ *
36722
+ * The 25% arm stops small groups shouting early (3 of 4 is not urgency); the
36723
+ * 5% floor stops large events staying silent until three remain.
36724
+ * capacity 4 -> 1, 12 -> 3, 40 -> 3, 100 -> 5, 200 -> 10.
36725
+ */
36726
+ function lowCapacityThreshold(capacity) {
36727
+ return Math.max(Math.min(3, Math.ceil(capacity * .25)), Math.ceil(capacity * .05));
36728
+ }
36729
+ function getRemainingPlaces(source) {
36730
+ const { available, isFull } = getEventAvailability(source);
36731
+ if (available === null || source.capacity === null) return { kind: "unlimited" };
36732
+ if (isFull) return {
36733
+ kind: "full",
36734
+ waitlist: canJoinEventWaitlist(source)
36376
36735
  };
36377
- return { kind: "unscoped" };
36736
+ if (available <= lowCapacityThreshold(source.capacity)) return {
36737
+ kind: "low",
36738
+ remaining: available
36739
+ };
36740
+ return { kind: "open" };
36378
36741
  }
36379
- function resolveEventRegistrationScopedEvents(events, context) {
36380
- const scope = resolveEventRegistrationScope(context);
36381
- switch (scope.kind) {
36382
- case "occurrenceSeries":
36383
- case "pageSurfaceSeries": return events.filter((event) => event.seriesId === scope.seriesId);
36384
- case "pageSurfaceOffering": {
36385
- const seriesIds = new Set(scope.seriesIds);
36386
- return events.filter((event) => seriesIds.has(event.seriesId));
36387
- }
36388
- case "unsupportedPageSurfaceSubject": return [];
36389
- case "unscoped": return [...events];
36742
+ /**
36743
+ * The one place a remaining-places count becomes text. Returns `null` when
36744
+ * nothing should be shown: uncapped events, and capped events above the
36745
+ * scarcity threshold. Surfaces that need their own full-state wording pass
36746
+ * it in rather than re-deriving the count.
36747
+ */
36748
+ function formatRemainingPlaces(source, wording = {}) {
36749
+ const remaining = getRemainingPlaces(source);
36750
+ switch (remaining.kind) {
36751
+ case "unlimited":
36752
+ case "open": return null;
36753
+ case "low": return formatPlacesLeft(remaining.remaining, wording.compact === true);
36754
+ case "full": return remaining.waitlist ? wording.waitlist ?? "Waitlist" : wording.full ?? "Full";
36755
+ }
36756
+ }
36757
+ function formatPlacesLeft(count, compact) {
36758
+ if (compact) return `${count} left`;
36759
+ return count === 1 ? "1 place left" : `${count} places left`;
36760
+ }
36761
+ function formatTicketTypeRemaining(remainingCapacity) {
36762
+ if (remainingCapacity === null || remainingCapacity > 3) return null;
36763
+ if (remainingCapacity <= 0) return "None left for this ticket type";
36764
+ return `${formatPlacesLeft(remainingCapacity, false)} for this ticket type`;
36765
+ }
36766
+ /**
36767
+ * Which capacity figure wins when they disagree.
36768
+ *
36769
+ * For a hybrid event that publishes both attendance-mode rows, **the mode
36770
+ * rows are authoritative and the event-level aggregate is only a summary**.
36771
+ * The registration write path admits or refuses per mode
36772
+ * (`getEffectiveOccurrenceCapacityForAttendanceMode` on the server), so the
36773
+ * aggregate can read full while a mode still has room, and can read open
36774
+ * while the mode the visitor wants is full. Believing the aggregate on a
36775
+ * card is how a "places available" event turns into a dead end at step two.
36776
+ *
36777
+ * Everything else — single-mode events, events that publish no rows, and the
36778
+ * aggregate-only helpers {@link getEventAvailability} /
36779
+ * {@link getRemainingPlaces} that existing callers rely on — keeps reading
36780
+ * the aggregate, unchanged.
36781
+ */
36782
+ function findHybridModeRows(event) {
36783
+ if (event.deliveryMode !== "hybrid") return null;
36784
+ const rows = event.attendanceModes ?? [];
36785
+ const inPerson = rows.find((row) => row.mode === "in_person");
36786
+ const online = rows.find((row) => row.mode === "online");
36787
+ return inPerson && online ? {
36788
+ inPerson,
36789
+ online
36790
+ } : null;
36791
+ }
36792
+ /** Openness rank; higher means more room. A full mode with a waitlist beats one without. */
36793
+ function remainingPlacesRank(remaining) {
36794
+ switch (remaining.kind) {
36795
+ case "unlimited": return 4;
36796
+ case "open": return 3;
36797
+ case "low": return 2;
36798
+ case "full": return remaining.waitlist ? 1 : 0;
36799
+ }
36800
+ }
36801
+ function isMoreOpen(candidate, than) {
36802
+ const rank = remainingPlacesRank(candidate);
36803
+ const otherRank = remainingPlacesRank(than);
36804
+ if (rank !== otherRank) return rank > otherRank;
36805
+ return candidate.kind === "low" && than.kind === "low" ? candidate.remaining > than.remaining : false;
36806
+ }
36807
+ /** True when `candidate` has less room left than `than`. */
36808
+ function isScarcer(candidate, than) {
36809
+ return isMoreOpen(than, candidate);
36810
+ }
36811
+ const ATTENDANCE_MODE_LABEL = {
36812
+ in_person: "In person",
36813
+ online: "Online"
36814
+ };
36815
+ /** Wording for a mode with room to spare, where the count is deliberately withheld. */
36816
+ const PLACES_AVAILABLE = "Places available";
36817
+ /**
36818
+ * The browse-surface label for an event's remaining places. Single-mode
36819
+ * events get exactly the aggregate label {@link formatRemainingPlaces}
36820
+ * produces, and so does a hybrid event whose two modes tell the same story.
36821
+ * When the modes differ, both sides are named: "available" on a card is a
36822
+ * promise the registration wizard would otherwise have to break.
36823
+ *
36824
+ * Hybrid examples, in person full with 3 online places left:
36825
+ * default -> "Online: 3 places left · In person: Full"
36826
+ * { full: 'Sold out' } -> "Online: 3 places left · In person: Sold out"
36827
+ * { compact: true } -> "Online: 3 left"
36828
+ */
36829
+ function formatEventRemainingPlaces(event, wording = {}) {
36830
+ const rows = findHybridModeRows(event);
36831
+ if (!rows) return formatRemainingPlaces(event, wording);
36832
+ const inPerson = formatRemainingPlaces(rows.inPerson, wording);
36833
+ const online = formatRemainingPlaces(rows.online, wording);
36834
+ if (inPerson === online) return inPerson;
36835
+ if (wording.compact === true) return formatCompactHybridPlaces(rows, wording);
36836
+ return formatBothHybridSides(rows, {
36837
+ inPerson,
36838
+ online
36839
+ });
36840
+ }
36841
+ /**
36842
+ * Both sides, the one with room first: that is the half a visitor can act on,
36843
+ * and it should not arrive after a full stop's worth of bad news.
36844
+ */
36845
+ function formatBothHybridSides(rows, labels) {
36846
+ 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(" · ");
36847
+ }
36848
+ function namedModePlaces(mode, label) {
36849
+ return `${ATTENDANCE_MODE_LABEL[mode]}: ${label ?? PLACES_AVAILABLE}`;
36850
+ }
36851
+ /**
36852
+ * Pills and timetable cells have room for one short phrase, so they name the
36853
+ * single mode that carries news rather than listing both: the scarce side
36854
+ * when there is a count to show, and otherwise the side that has closed —
36855
+ * "In person: Full", never "Online only", which reads as a statement about
36856
+ * how the event is delivered rather than about its remaining places.
36857
+ */
36858
+ function formatCompactHybridPlaces(rows, wording) {
36859
+ const inPerson = getRemainingPlaces(rows.inPerson);
36860
+ const online = getRemainingPlaces(rows.online);
36861
+ if (inPerson.kind === "full" && online.kind === "full") return formatRemainingPlaces(inPerson.waitlist ? rows.inPerson : rows.online, wording);
36862
+ if (inPerson.kind === "full" || online.kind === "full") {
36863
+ const fullSide = inPerson.kind === "full" ? "in_person" : "online";
36864
+ const openSide = inPerson.kind === "full" ? "online" : "in_person";
36865
+ const openLabel = formatRemainingPlaces(rowFor(rows, openSide), wording);
36866
+ return openLabel !== null ? namedModePlaces(openSide, openLabel) : namedModePlaces(fullSide, formatRemainingPlaces(rowFor(rows, fullSide), wording));
36867
+ }
36868
+ const scarcerSide = isScarcer(online, inPerson) ? "online" : "in_person";
36869
+ return namedModePlaces(scarcerSide, formatRemainingPlaces(rowFor(rows, scarcerSide), wording));
36870
+ }
36871
+ function rowFor(rows, mode) {
36872
+ return mode === "online" ? rows.online : rows.inPerson;
36873
+ }
36874
+ //#endregion
36875
+ //#region ../blocks/src/system/runtime/nodes/event-registration/registrationDisplay.ts
36876
+ const registrationDisplaySchema = z.object({
36877
+ venue: z.enum(["shown", "hidden"]),
36878
+ placesLeft: z.enum(["shown", "hidden"])
36879
+ });
36880
+ function resolveRegistrationDisplay(source) {
36881
+ return {
36882
+ venue: source.showVenue === false ? "hidden" : "shown",
36883
+ placesLeft: source.showCapacity === false ? "hidden" : "shown"
36884
+ };
36885
+ }
36886
+ /** Venue stays in domain data; views receive only the permitted presentation. */
36887
+ function shownVenue(event, display) {
36888
+ return display.venue === "shown" ? event.venue?.name ?? null : null;
36889
+ }
36890
+ /** Full/waitlist explanations remain visible even when optional counts are hidden. */
36891
+ function placesLeftLabel(source, display, wording = {}) {
36892
+ if ("kind" in source) return ticketPlacesLabel(source.remaining, display);
36893
+ if (source.deliveryMode === "hybrid" && source.attendanceModes?.length) return hybridPlacesLabel(source, display, wording);
36894
+ return capacityPlacesLabel(source, display, wording);
36895
+ }
36896
+ function capacityPlacesLabel(source, display, wording) {
36897
+ const remaining = getRemainingPlaces(source);
36898
+ if (display.placesLeft === "hidden" && remaining.kind !== "full") return null;
36899
+ switch (remaining.kind) {
36900
+ case "full": return formatRemainingPlaces(source, wording);
36901
+ case "low": return formatRemainingPlaces(source, wording);
36902
+ case "unlimited": return wording.unlimited ?? null;
36903
+ case "open": return wording.open ?? null;
36904
+ default: return assertNever$2(remaining);
36390
36905
  }
36391
36906
  }
36907
+ function hybridPlacesLabel(event, display, wording) {
36908
+ if (display.placesLeft === "shown") return formatEventRemainingPlaces(event, wording);
36909
+ return (event.attendanceModes ?? []).flatMap((mode) => {
36910
+ const label = placesLeftLabel(mode, display, wording);
36911
+ return label ? [`${mode.mode === "online" ? "Online" : "In person"}: ${label}`] : [];
36912
+ }).join(" · ") || null;
36913
+ }
36914
+ function ticketPlacesLabel(remaining, display) {
36915
+ if (remaining !== null && remaining > 0 && display.placesLeft === "hidden") return null;
36916
+ return formatTicketTypeRemaining(remaining);
36917
+ }
36918
+ //#endregion
36919
+ //#region ../blocks/src/system/runtime/api/readAuthority.ts
36920
+ const browserReadAuthoritySchema = z.discriminatedUnion("kind", [z.object({ kind: z.literal("published") }).strict(), z.object({
36921
+ kind: z.literal("preview"),
36922
+ token: z.string().min(1)
36923
+ }).strict()]);
36392
36924
  //#endregion
36393
36925
  //#region ../calendar/src/grid.ts
36394
36926
  const MONTH_NAMES = [
@@ -36476,6 +37008,11 @@ function buildCalendarViewport(input) {
36476
37008
  //#endregion
36477
37009
  //#region ../blocks/src/system/runtime/nodes/events/domain/dates.ts
36478
37010
  /**
37011
+ * Date and time presentation for events: the single place an event's
37012
+ * timestamps become strings or date buckets. Calendar-grid primitives are
37013
+ * re-exported from @riverbankcms/calendar so block code has one import.
37014
+ */
37015
+ /**
36479
37016
  * The English locale a site's public dates are written in, read from the
36480
37017
  * site's own time zone (Will, 2026-09-26: "date style from site time zone").
36481
37018
  *
@@ -36495,6 +37032,28 @@ function eventDateLocale(siteTimeZone) {
36495
37032
  if (siteTimeZone === "Pacific/Auckland" || siteTimeZone === "Pacific/Chatham") return "en-NZ";
36496
37033
  return "en-US";
36497
37034
  }
37035
+ function formatEventCompactDateRail(isoTimestamp, options, letterCase = "capitals") {
37036
+ const locale = options?.locale ?? "en-US";
37037
+ const dateTime = toScheduledBookingDateTime({
37038
+ startsAt: isoTimestamp,
37039
+ timeZone: toBookingTimeZone(options?.timeZone ?? "UTC"),
37040
+ locale
37041
+ });
37042
+ const cased = (value) => letterCase === "capitals" ? value.toLocaleUpperCase(locale) : value;
37043
+ const weekday = cased(formatBookingDate(dateTime, { weekday: "short" }));
37044
+ const month = cased(formatBookingDate(dateTime, { month: "short" }).slice(0, 3));
37045
+ return {
37046
+ weekday,
37047
+ day: formatBookingDate(dateTime, { day: "numeric" }),
37048
+ month,
37049
+ label: formatBookingDate(dateTime, {
37050
+ weekday: "long",
37051
+ year: "numeric",
37052
+ month: "long",
37053
+ day: "numeric"
37054
+ })
37055
+ };
37056
+ }
36498
37057
  //#endregion
36499
37058
  //#region ../blocks/src/system/runtime/nodes/events/domain/destination.ts
36500
37059
  function isRecord$4(value) {
@@ -36514,6 +37073,15 @@ function resolveRuntimeEventDestination(event) {
36514
37073
  };
36515
37074
  }
36516
37075
  //#endregion
37076
+ //#region ../blocks/src/system/runtime/nodes/events/domain/pricing.ts
37077
+ /**
37078
+ * Format an event's price directly from its pricing value (without the full
37079
+ * event). Same output as {@link formatEventPrice}.
37080
+ */
37081
+ function formatEventPriceDisplay(pricing) {
37082
+ return formatEventPricing(pricing, { decimals: "auto" });
37083
+ }
37084
+ //#endregion
36517
37085
  //#region ../blocks/src/system/runtime/nodes/events/domain/listWeek.ts
36518
37086
  /** Civil weeks use the site zone, including weeks containing a clock change. */
36519
37087
  function buildEventListWeek(anchor, startOfWeek, timeZone) {
@@ -36548,6 +37116,642 @@ function eventListWindowEvents(events, window) {
36548
37116
  });
36549
37117
  }
36550
37118
  //#endregion
37119
+ //#region ../blocks/src/system/runtime/nodes/event-registration/registrationDerivations.ts
37120
+ function eventMatchesOccurrenceId(event, occurrenceId) {
37121
+ return event.registrationSubject.kind === "occurrence" && (event.registrationSubject.occurrenceId === occurrenceId || event.selectionId === occurrenceId);
37122
+ }
37123
+ function findPreSelectedEvent(input) {
37124
+ const occurrenceContext = input.occurrenceContext;
37125
+ if (!occurrenceContext) return input.scopedEvents.length === 1 ? input.scopedEvents[0] ?? null : null;
37126
+ const contextDate = getBookingLocalDateKey(toScheduledBookingDateTime({
37127
+ startsAt: occurrenceContext.startsAt,
37128
+ timeZone: occurrenceContext.timeZone
37129
+ }));
37130
+ return input.scopedEvents.find((event) => eventMatchesOccurrenceId(event, occurrenceContext.id) || getBookingLocalDateKey(toScheduledBookingDateTime({
37131
+ startsAt: event.startsAt,
37132
+ timeZone: event.timeZone
37133
+ })) === contextDate) ?? null;
37134
+ }
37135
+ function resolveEventRequiresPayment(input) {
37136
+ if (input.eligibility) return input.eligibility.requiresPayment;
37137
+ if (input.selectedTicketType) return input.selectedTicketType.priceAmount > 0;
37138
+ if (!input.event) return false;
37139
+ return publicEventPricingType(input.event.pricing) === "paid";
37140
+ }
37141
+ //#endregion
37142
+ //#region ../blocks/src/system/runtime/nodes/event-registration/utils.ts
37143
+ /**
37144
+ * The locale a site's booking dates are written in: the one the site's time
37145
+ * zone places it in (`eventDateLocale`, shared with event cards), or British
37146
+ * English when the site names no zone.
37147
+ */
37148
+ function bookingDateLocale(siteTimeZone) {
37149
+ return siteTimeZone ? eventDateLocale(siteTimeZone) : "en-GB";
37150
+ }
37151
+ function bookingInstant(startsAt, input) {
37152
+ return toScheduledBookingDateTime({
37153
+ startsAt,
37154
+ timeZone: toBookingTimeZone(input.timeZone),
37155
+ locale: bookingDateLocale(input.siteTimeZone)
37156
+ });
37157
+ }
37158
+ /** "7:00pm": a twelve-hour clock with a lowercase, unspaced day period. */
37159
+ function bookingClock(startsAt, input) {
37160
+ return formatBookingTime(bookingInstant(startsAt, input), {
37161
+ hour: "numeric",
37162
+ minute: "2-digit",
37163
+ hour12: true
37164
+ }).replace(/\s*([ap])\.?\s?m\.?$/i, (_match, period) => `${period.toLowerCase()}m`);
37165
+ }
37166
+ /** "7:00pm – 8:15pm", or just the start when there's no distinct end. */
37167
+ function clockRange(input) {
37168
+ const start = bookingClock(input.startsAt, input);
37169
+ const end = input.endsAt ? bookingClock(input.endsAt, input) : null;
37170
+ return end && end !== start ? `${start} – ${end}` : start;
37171
+ }
37172
+ /** A stored local clock has no date or DST offset; UTC preserves its digits. */
37173
+ function formatBookingScheduleSummary(event) {
37174
+ const summary = event.scheduleSummary;
37175
+ if (!summary) return null;
37176
+ const time = summary.startTime ? formatBookingWhen({
37177
+ startsAt: `1970-01-01T${summary.startTime}:00Z`,
37178
+ timeZone: "UTC",
37179
+ siteTimeZone: event.siteTimeZone
37180
+ }, "time") : null;
37181
+ return [summary.recurrenceDescription, time].filter(Boolean).join(" · ");
37182
+ }
37183
+ /** The one booking date formatter; every variant derives from it. */
37184
+ function formatBookingWhen(input, variant) {
37185
+ const instant = bookingInstant(input.startsAt, input);
37186
+ const day = formatBookingDate(instant, {
37187
+ weekday: "short",
37188
+ day: "numeric",
37189
+ month: "short"
37190
+ });
37191
+ switch (variant) {
37192
+ case "day": return day;
37193
+ case "dayLong": return formatBookingDate(instant, {
37194
+ weekday: "long",
37195
+ day: "numeric",
37196
+ month: "long"
37197
+ });
37198
+ case "month": return formatBookingDate(instant, { month: "long" });
37199
+ case "monthYear": return formatBookingDate(instant, {
37200
+ month: "long",
37201
+ year: "numeric"
37202
+ });
37203
+ case "time": return bookingClock(input.startsAt, input);
37204
+ case "timeRange": return clockRange(input);
37205
+ case "short": return `${day}, ${bookingClock(input.startsAt, input)}`;
37206
+ case "long": return `${day} · ${clockRange(input)}`;
37207
+ }
37208
+ }
37209
+ /** A single instant in the short form, e.g. "Mon 5 Oct, 7:00pm". */
37210
+ function formatEventDateTime(isoTimestamp, options) {
37211
+ return formatBookingWhen({
37212
+ startsAt: isoTimestamp,
37213
+ timeZone: options?.timeZone,
37214
+ siteTimeZone: options?.siteTimeZone
37215
+ }, "short");
37216
+ }
37217
+ /** The title an occurrence was given on its own, when it overrides the event's. */
37218
+ function occurrenceTitleOverride(occurrence) {
37219
+ const title = occurrence?.overrides?.title;
37220
+ return typeof title === "string" && title.trim() ? title.trim() : null;
37221
+ }
37222
+ /** The event as this page names it: an occurrence's own title wins. */
37223
+ function withOccurrenceTitle(event, occurrence) {
37224
+ const title = occurrenceTitleOverride(occurrence);
37225
+ return event && title ? {
37226
+ ...event,
37227
+ title
37228
+ } : event;
37229
+ }
37230
+ function courseSchedule(sessions, siteTimeZone) {
37231
+ const first = sessions[0];
37232
+ if (!first) return null;
37233
+ const last = sessions[sessions.length - 1] ?? first;
37234
+ const firstLabel = formatEventDateTime(first.startsAt, {
37235
+ timeZone: first.timeZone,
37236
+ siteTimeZone
37237
+ });
37238
+ if (last.startsAt === first.startsAt) return `${sessions.length} session${sessions.length === 1 ? "" : "s"} · ${firstLabel}`;
37239
+ const lastLabel = formatEventDateTime(last.startsAt, {
37240
+ timeZone: last.timeZone,
37241
+ siteTimeZone
37242
+ });
37243
+ return `${sessions.length} sessions · ${firstLabel} – ${lastLabel}`;
37244
+ }
37245
+ /**
37246
+ * When a booking happens, in one line: the occurrence's date and time, or a
37247
+ * course's session count and first and last dates. `short` by default (bar,
37248
+ * confirmation); `long` adds the end time (card, panel header).
37249
+ */
37250
+ function formatRegistrationSchedule(event, occurrence, variant = "short") {
37251
+ if (event.registrationSubject.kind === "wholeSeries") return courseSchedule(event.registrationSubject.sessions, event.siteTimeZone);
37252
+ const input = scheduleInput(event, occurrence ?? null);
37253
+ return input ? formatBookingWhen(input, variant) : null;
37254
+ }
37255
+ /** The occurrence's own times when the page names one, otherwise the event's. */
37256
+ function scheduleInput(event, occurrence) {
37257
+ const times = occurrence?.startsAt ? {
37258
+ startsAt: occurrence.startsAt,
37259
+ endsAt: occurrence.endsAt ?? null
37260
+ } : {
37261
+ startsAt: event.startsAt,
37262
+ endsAt: event.endsAt
37263
+ };
37264
+ if (!times.startsAt) return null;
37265
+ return {
37266
+ ...times,
37267
+ timeZone: occurrence?.timeZone ?? event.timeZone ?? null,
37268
+ siteTimeZone: event.siteTimeZone
37269
+ };
37270
+ }
37271
+ //#endregion
37272
+ //#region ../blocks/src/system/runtime/nodes/event-registration/wizardSteps.ts
37273
+ function registrationRoute(input) {
37274
+ const base = input.chooseEvent ? [
37275
+ "select-event",
37276
+ "select-tickets",
37277
+ "enter-details"
37278
+ ] : ["select-tickets", "enter-details"];
37279
+ switch (input.payment) {
37280
+ case "none": return base;
37281
+ case "options": return [...base, "select-payment"];
37282
+ case "optionsAndPayment": return [
37283
+ ...base,
37284
+ "select-payment",
37285
+ "payment"
37286
+ ];
37287
+ }
37288
+ }
37289
+ const REGISTRATION_STEP_LABELS = [
37290
+ "Event",
37291
+ "Date",
37292
+ "Tickets",
37293
+ "Places",
37294
+ "Your details",
37295
+ "How you’ll pay",
37296
+ "Payment"
37297
+ ];
37298
+ /** Several dates of one series are a date choice; anything else is an event choice. */
37299
+ function getEventSelectionMode(events) {
37300
+ const firstSeriesId = events[0]?.seriesId;
37301
+ return events.length > 1 && firstSeriesId && events.every((event) => event.seriesId === firstSeriesId) ? "date" : "event";
37302
+ }
37303
+ /** The label facts for a surface: what the visitor chooses from, and what they book. */
37304
+ function registrationStepLabelContext(scopedEvents, bookedEvent) {
37305
+ return {
37306
+ eventSelection: getEventSelectionMode(scopedEvents),
37307
+ booksWholeSeries: bookedEvent?.registrationSubject.kind === "wholeSeries"
37308
+ };
37309
+ }
37310
+ /** One label owner for the live progress and the server placeholder. */
37311
+ function registrationStepLabel(step, context) {
37312
+ switch (step) {
37313
+ case "select-event": return context.eventSelection === "date" ? "Date" : "Event";
37314
+ case "select-tickets": return context.booksWholeSeries ? "Places" : "Tickets";
37315
+ case "enter-details": return "Your details";
37316
+ case "select-payment": return "How you’ll pay";
37317
+ case "payment": return "Payment";
37318
+ }
37319
+ }
37320
+ function bookingButtonText(buttonText) {
37321
+ return buttonText?.trim() || "Book your place";
37322
+ }
37323
+ function placesLabel(event, input, compact) {
37324
+ return placesLeftLabel(selectEventCapacitySource(event, input.attendanceMode), input.display, { compact });
37325
+ }
37326
+ function isFull(event, mode) {
37327
+ return formatRemainingPlaces(selectEventCapacitySource(event, mode), {
37328
+ full: "full",
37329
+ waitlist: "full"
37330
+ }) === "full";
37331
+ }
37332
+ function priceLabel(event) {
37333
+ return formatEventPriceDisplay(event.pricing).trim() || null;
37334
+ }
37335
+ /** The lowest price a display can mean; null when it names no paid amount. */
37336
+ function lowestPaidAmount(display) {
37337
+ switch (display.kind) {
37338
+ case "exact":
37339
+ case "from": return display.amount;
37340
+ case "range": return display.minAmount;
37341
+ case "mixed": return display.paidFromAmount;
37342
+ case "free":
37343
+ case "unavailable":
37344
+ case "external": return null;
37345
+ }
37346
+ }
37347
+ /**
37348
+ * One price for a set of dates: the shared label when every date costs the
37349
+ * same, otherwise "From" the lowest paid amount, through the one formatter.
37350
+ */
37351
+ function seriesPriceLabel(events) {
37352
+ const firstEvent = events[0];
37353
+ if (!firstEvent) return null;
37354
+ const displays = events.map((event) => publicEventPricingDisplay(event.pricing));
37355
+ const labels = new Set(displays.map((display) => formatBookingPriceDisplay(display, { decimals: "auto" })));
37356
+ if (labels.size === 1) return [...labels][0] || null;
37357
+ const lowest = displays.flatMap((display) => {
37358
+ const amount = lowestPaidAmount(display);
37359
+ return amount === null || !("currency" in display) ? [] : [{
37360
+ amount,
37361
+ currency: display.currency
37362
+ }];
37363
+ }).reduce((best, candidate) => best === null || candidate.amount < best.amount ? candidate : best, null);
37364
+ return lowest ? formatBookingPriceDisplay({
37365
+ kind: "from",
37366
+ amount: lowest.amount,
37367
+ currency: lowest.currency
37368
+ }, { decimals: "auto" }) : priceLabel(firstEvent);
37369
+ }
37370
+ function dateTile(event) {
37371
+ if (event.registrationSubject.kind === "wholeSeries" || !event.startsAt) return null;
37372
+ const rail = formatEventCompactDateRail(event.startsAt, { timeZone: event.timeZone });
37373
+ return {
37374
+ month: rail.month,
37375
+ day: rail.day
37376
+ };
37377
+ }
37378
+ /** "Mon 5 Oct": the compact date the chips and the bar share. */
37379
+ function formatBookingShortDate(event) {
37380
+ return formatBookingWhen(event, "day");
37381
+ }
37382
+ function byStart(left, right) {
37383
+ return Date.parse(left.startsAt) - Date.parse(right.startsAt);
37384
+ }
37385
+ function singleEntry(event, input) {
37386
+ const isCourse = event.registrationSubject.kind === "wholeSeries";
37387
+ return {
37388
+ kind: "single",
37389
+ title: event.title,
37390
+ when: formatRegistrationSchedule(event, null, "long"),
37391
+ shortWhen: formatRegistrationSchedule(event),
37392
+ venue: shownVenue(event, input.display),
37393
+ price: priceLabel(event),
37394
+ places: placesLabel(event, input, false),
37395
+ tile: dateTile(event),
37396
+ action: isCourse ? `Book the ${input.noun.label}` : bookingButtonText(input.buttonText)
37397
+ };
37398
+ }
37399
+ function chooseDateEntry(input) {
37400
+ const ordered = [...input.events].sort(byStart);
37401
+ const first = ordered[0];
37402
+ const shown = ordered.slice(0, 4);
37403
+ return {
37404
+ kind: "chooseDate",
37405
+ title: first?.title ?? "",
37406
+ schedule: (first ? formatBookingScheduleSummary(first) : null) ?? (ordered.length === 1 ? "1 date" : `${ordered.length} dates`),
37407
+ venue: first ? shownVenue(first, input.display) : null,
37408
+ price: seriesPriceLabel(ordered),
37409
+ nextDate: first ? formatBookingShortDate(first) : null,
37410
+ dateCount: ordered.length,
37411
+ dates: shown.map((event) => ({
37412
+ selectionId: event.selectionId,
37413
+ label: formatBookingShortDate(event),
37414
+ places: placesLabel(event, input, true),
37415
+ full: isFull(event, input.attendanceMode)
37416
+ })),
37417
+ moreCount: Math.max(0, ordered.length - shown.length),
37418
+ action: "Choose a date"
37419
+ };
37420
+ }
37421
+ /** The card before a booking starts; the server renders this too. */
37422
+ function idleBookingEntry(input) {
37423
+ if (input.event) return singleEntry(input.event, input);
37424
+ const only = input.events.length === 1 ? input.events[0] : void 0;
37425
+ if (only) return singleEntry(only, input);
37426
+ if (getEventSelectionMode(input.events) === "date") return chooseDateEntry(input);
37427
+ return {
37428
+ kind: "chooseEvent",
37429
+ title: input.events[0]?.title ?? "",
37430
+ count: input.events.length,
37431
+ noun: input.noun.pluralLabel,
37432
+ action: "Choose an event"
37433
+ };
37434
+ }
37435
+ //#endregion
37436
+ //#region ../blocks/src/system/blocks/offerings-preview/model.ts
37437
+ const OFFERINGS_PREVIEW_DEFAULT_BUTTON_TEXT = "Book";
37438
+ const OFFERINGS_PREVIEW_DEFAULT_EMPTY_MESSAGE = "Nothing scheduled just yet.";
37439
+ const OFFERINGS_PREVIEW_EVENT_PRESET_DEFINITIONS = [
37440
+ {
37441
+ preset: "class",
37442
+ sourceKey: "eventPreset:class",
37443
+ dataKey: "classEvents",
37444
+ label: "Classes"
37445
+ },
37446
+ {
37447
+ preset: "workshop",
37448
+ sourceKey: "eventPreset:workshop",
37449
+ dataKey: "workshopEvents",
37450
+ label: "Workshops"
37451
+ },
37452
+ {
37453
+ preset: "retreat",
37454
+ sourceKey: "eventPreset:retreat",
37455
+ dataKey: "retreatEvents",
37456
+ label: "Retreats"
37457
+ },
37458
+ {
37459
+ preset: "performance",
37460
+ sourceKey: "eventPreset:performance",
37461
+ dataKey: "performanceEvents",
37462
+ label: "Performances"
37463
+ }
37464
+ ];
37465
+ const OFFERINGS_PREVIEW_EVENT_PRESETS = OFFERINGS_PREVIEW_EVENT_PRESET_DEFINITIONS.map((definition) => definition.preset);
37466
+ const OFFERINGS_PREVIEW_AUTHORABLE_SOURCE_KEYS = [...OFFERINGS_PREVIEW_EVENT_PRESET_DEFINITIONS.map((definition) => definition.sourceKey), "course"];
37467
+ const OFFERINGS_PREVIEW_SOURCE_KEYS = OFFERINGS_PREVIEW_AUTHORABLE_SOURCE_KEYS;
37468
+ Object.fromEntries(OFFERINGS_PREVIEW_EVENT_PRESET_DEFINITIONS.map((definition) => [definition.sourceKey, {
37469
+ kind: "eventPreset",
37470
+ preset: definition.preset
37471
+ }]));
37472
+ function isOfferingsPreviewSourceKey(value) {
37473
+ return typeof value === "string" && OFFERINGS_PREVIEW_SOURCE_KEYS.includes(value);
37474
+ }
37475
+ function normalizeOfferingsPreviewGroupOrder(value) {
37476
+ if (value == null) return { kind: "legacy" };
37477
+ if (value.length === 0) return { kind: "default" };
37478
+ const keys = uniqueOfferingsPreviewSourceKeys(value);
37479
+ return keys.length > 0 ? {
37480
+ kind: "custom",
37481
+ keys
37482
+ } : { kind: "default" };
37483
+ }
37484
+ function inferLegacyOfferingsPreviewGroupOrder(overrides) {
37485
+ const keys = uniqueOfferingsPreviewSourceKeys((overrides ?? []).map((override) => override.sourceKey));
37486
+ return keys.length >= 2 ? keys : [];
37487
+ }
37488
+ function normalizeOfferingsPreviewContentForWrite(value) {
37489
+ if (!isRecord$3(value)) return null;
37490
+ const groupOrderValue = value.groupOrder;
37491
+ if (Array.isArray(groupOrderValue)) {
37492
+ const normalized = normalizeOfferingsPreviewGroupOrder(groupOrderValue);
37493
+ return {
37494
+ ...value,
37495
+ groupOrder: normalized.kind === "custom" ? normalized.keys : []
37496
+ };
37497
+ }
37498
+ if (groupOrderValue != null) return {
37499
+ ...value,
37500
+ groupOrder: []
37501
+ };
37502
+ const legacyOrder = inferLegacyOfferingsPreviewGroupOrder(Array.isArray(value.groupOverrides) ? value.groupOverrides.filter(isRecord$3) : []);
37503
+ return legacyOrder.length > 0 ? {
37504
+ ...value,
37505
+ groupOrder: legacyOrder
37506
+ } : value;
37507
+ }
37508
+ /** Which offerings a site preset row names; one owner for "the course preset means whole-series runs". */
37509
+ function sourceFromPresetSummary(preset) {
37510
+ if (preset.engine === "event_series" && preset.canonicalBehaviour === "course") return { kind: "wholeSeries" };
37511
+ if (preset.engine !== "event_series") return null;
37512
+ if (isOfferingsPreviewEventPreset(preset.canonicalBehaviour)) return {
37513
+ kind: "eventPreset",
37514
+ preset: preset.canonicalBehaviour
37515
+ };
37516
+ return null;
37517
+ }
37518
+ function uniqueOfferingsPreviewSourceKeys(values) {
37519
+ const keys = [];
37520
+ for (const value of values) {
37521
+ if (!isOfferingsPreviewSourceKey(value)) continue;
37522
+ if (!keys.includes(value)) keys.push(value);
37523
+ }
37524
+ return keys;
37525
+ }
37526
+ function isRecord$3(value) {
37527
+ return typeof value === "object" && value !== null && !Array.isArray(value);
37528
+ }
37529
+ function isOfferingsPreviewEventPreset(value) {
37530
+ return typeof value === "string" && OFFERINGS_PREVIEW_EVENT_PRESETS.includes(value);
37531
+ }
37532
+ //#endregion
37533
+ //#region ../blocks/src/system/runtime/nodes/event-registration/offeringNoun.ts
37534
+ function offeringKindForEvent(event) {
37535
+ return event.registrationSubject.kind === "wholeSeries" ? "course" : event.eventPreset ?? "event";
37536
+ }
37537
+ function presetKind(preset) {
37538
+ const source = sourceFromPresetSummary(preset);
37539
+ if (!source) return null;
37540
+ return source.kind === "wholeSeries" ? "course" : source.preset;
37541
+ }
37542
+ /**
37543
+ * What the site calls this kind of offering, in lower case for use mid-sentence
37544
+ * ("Book the course", "3 workshops"). Site preset labels win; the canonical
37545
+ * name stands in when the site has none.
37546
+ */
37547
+ function offeringNounForEvent(event, presets) {
37548
+ if (!event) return lowerCased(EVENT_PRESET_VOCABULARY.event);
37549
+ const kind = offeringKindForEvent(event);
37550
+ const preset = presets?.find((candidate) => presetKind(candidate) === kind);
37551
+ return lowerCased(preset?.label.trim() && preset.pluralLabel.trim() ? preset : EVENT_PRESET_VOCABULARY[kind]);
37552
+ }
37553
+ function lowerCased(noun) {
37554
+ return {
37555
+ label: noun.label.trim().toLocaleLowerCase(),
37556
+ pluralLabel: noun.pluralLabel.trim().toLocaleLowerCase()
37557
+ };
37558
+ }
37559
+ //#endregion
37560
+ //#region ../blocks/src/system/runtime/nodes/event-registration/registrationEventAdapter.ts
37561
+ function toRegistrationEvent(event) {
37562
+ return {
37563
+ ...event,
37564
+ selectionId: event.id,
37565
+ registrationSubject: {
37566
+ kind: "occurrence",
37567
+ occurrenceId: event.occurrenceId
37568
+ }
37569
+ };
37570
+ }
37571
+ function toRegistrationEvents(events) {
37572
+ return events.filter((event) => event.bookingScope === "single_occurrence").map(toRegistrationEvent);
37573
+ }
37574
+ /**
37575
+ * Project one whole-series run onto the registration surface.
37576
+ *
37577
+ * Capacity, pricing, payment terms and consent come straight off the canonical
37578
+ * Event record; only the schedule is widened from the single occurrence to the
37579
+ * live run. A run without live sessions has no registration event.
37580
+ */
37581
+ function toWholeSeriesRegistrationEvent(group) {
37582
+ const { representative, run } = group;
37583
+ const sessions = orderedLiveRunSessions(run.sessions);
37584
+ const firstSession = sessions[0];
37585
+ if (!firstSession) return null;
37586
+ const lastSession = sessions[sessions.length - 1] ?? firstSession;
37587
+ return {
37588
+ ...representative,
37589
+ selectionId: run.eventSeriesId,
37590
+ registrationSubject: {
37591
+ kind: "wholeSeries",
37592
+ eventSeriesId: run.eventSeriesId,
37593
+ sessions
37594
+ },
37595
+ startsAt: firstSession.startsAt,
37596
+ endsAt: lastSession.endsAt,
37597
+ timeZone: firstSession.timeZone
37598
+ };
37599
+ }
37600
+ function toWholeSeriesRegistrationEvents(events) {
37601
+ return groupPublicEventsByRun(events).map(toWholeSeriesRegistrationEvent).filter((event) => event !== null);
37602
+ }
37603
+ //#endregion
37604
+ //#region ../blocks/src/system/runtime/nodes/event-registration/bookingCardDisplay.ts
37605
+ /**
37606
+ * The booking card the server renders on CMS pages (issue 1581), and the
37607
+ * offering noun it resolves for the wizard. Kept beside the island contract
37608
+ * so the server and the client agree on one card.
37609
+ */
37610
+ const bookingHostSchema = z.enum(["overlay", "inPlace"]);
37611
+ const tileSchema = z.object({
37612
+ month: z.string(),
37613
+ day: z.string()
37614
+ }).nullable();
37615
+ const idleBookingEntrySchema = z.discriminatedUnion("kind", [
37616
+ z.object({
37617
+ kind: z.literal("single"),
37618
+ title: z.string(),
37619
+ when: z.string().nullable(),
37620
+ shortWhen: z.string().nullable(),
37621
+ venue: z.string().nullable(),
37622
+ price: z.string().nullable(),
37623
+ places: z.string().nullable(),
37624
+ tile: tileSchema,
37625
+ action: z.string()
37626
+ }),
37627
+ z.object({
37628
+ kind: z.literal("chooseDate"),
37629
+ title: z.string(),
37630
+ schedule: z.string(),
37631
+ venue: z.string().nullable(),
37632
+ price: z.string().nullable(),
37633
+ nextDate: z.string().nullable(),
37634
+ dateCount: z.number(),
37635
+ dates: z.array(z.object({
37636
+ selectionId: z.string(),
37637
+ label: z.string(),
37638
+ places: z.string().nullable(),
37639
+ full: z.boolean()
37640
+ })),
37641
+ moreCount: z.number(),
37642
+ action: z.string()
37643
+ }),
37644
+ z.object({
37645
+ kind: z.literal("chooseEvent"),
37646
+ title: z.string(),
37647
+ count: z.number(),
37648
+ noun: z.string(),
37649
+ action: z.string()
37650
+ })
37651
+ ]);
37652
+ function toScopedEventRegistrationEvents(scopedSourceEvents) {
37653
+ return [...toRegistrationEvents(scopedSourceEvents), ...toWholeSeriesRegistrationEvents(scopedSourceEvents)];
37654
+ }
37655
+ function scopedRegistrationEvents(source, scopedSourceEvents) {
37656
+ const scopedEvents = toScopedEventRegistrationEvents(scopedSourceEvents);
37657
+ return {
37658
+ scopedEvents,
37659
+ preSelected: findPreSelectedEvent({
37660
+ scopedEvents,
37661
+ occurrenceContext: source.occurrenceContext
37662
+ })
37663
+ };
37664
+ }
37665
+ function nounFor(scoped, presets) {
37666
+ return offeringNounForEvent(scoped.preSelected ?? scoped.scopedEvents[0] ?? null, presets);
37667
+ }
37668
+ /** What the site calls the offering this block books, from its preset rows. */
37669
+ function resolveOfferingNoun(source, scopedSourceEvents) {
37670
+ return nounFor(scopedRegistrationEvents(source, scopedSourceEvents), source.bookingOfferingPresets);
37671
+ }
37672
+ function idleEntry(source, scopedSourceEvents, display) {
37673
+ const scoped = scopedRegistrationEvents(source, scopedSourceEvents);
37674
+ if (scoped.scopedEvents.length === 0) return null;
37675
+ return idleBookingEntry({
37676
+ events: scoped.scopedEvents,
37677
+ event: withOccurrenceTitle(scoped.preSelected, source.occurrenceContext),
37678
+ display,
37679
+ buttonText: source.buttonText,
37680
+ noun: nounFor(scoped, source.bookingOfferingPresets),
37681
+ attendanceMode: null
37682
+ });
37683
+ }
37684
+ /** The server's card on CMS pages, or null where the shell still applies. */
37685
+ function bookingCardDisplay(source, scopedSourceEvents, display) {
37686
+ if ((source.host ?? "overlay") !== "overlay") return null;
37687
+ const entry = idleEntry(source, scopedSourceEvents, display);
37688
+ if (!entry) return null;
37689
+ return {
37690
+ kind: "event-registration",
37691
+ state: "booking-card",
37692
+ className: source.className ?? null,
37693
+ entry,
37694
+ buttonVariant: source.buttonVariant ?? null,
37695
+ buttonText: source.buttonText?.trim() ?? ""
37696
+ };
37697
+ }
37698
+ const unsupportedEventRegistrationPageSurfaceSubjectSchema = z.object({
37699
+ kind: z.literal("unsupported"),
37700
+ reason: z.enum([
37701
+ "unsupportedSubject",
37702
+ "invalidEventSeriesId",
37703
+ "invalidEventSeriesBookingScope",
37704
+ "invalidEventOfferingId",
37705
+ "invalidEventOfferingBookingScope",
37706
+ "invalidEventOfferingCatalogue"
37707
+ ])
37708
+ });
37709
+ const eventRegistrationPageSurfaceSubjectSchema = z.unknown().transform((value, ctx) => {
37710
+ const unsupported = unsupportedEventRegistrationPageSurfaceSubjectSchema.safeParse(value);
37711
+ if (unsupported.success) return unsupported.data;
37712
+ const parsed = parseEventPageSurfaceSubject(value);
37713
+ if (parsed.ok) return parsed.value;
37714
+ ctx.addIssue({
37715
+ code: z.ZodIssueCode.custom,
37716
+ message: `Invalid event page-surface subject (${parsed.error.kind})`
37717
+ });
37718
+ return z.NEVER;
37719
+ });
37720
+ function resolveEventRegistrationScope(context) {
37721
+ const occurrenceSeriesId = context.occurrenceContext?.seriesId;
37722
+ if (occurrenceSeriesId) return {
37723
+ kind: "occurrenceSeries",
37724
+ seriesId: occurrenceSeriesId
37725
+ };
37726
+ if (context.subject?.kind === "eventSeries") return {
37727
+ kind: "pageSurfaceSeries",
37728
+ seriesId: context.subject.eventSeriesId
37729
+ };
37730
+ if (context.subject?.kind === "eventOffering") return {
37731
+ kind: "pageSurfaceOffering",
37732
+ offeringId: context.subject.eventOfferingId,
37733
+ seriesIds: context.subject.catalogueEventSeriesIds
37734
+ };
37735
+ if (context.subject?.kind === "unsupported") return {
37736
+ kind: "unsupportedPageSurfaceSubject",
37737
+ reason: context.subject.reason
37738
+ };
37739
+ return { kind: "unscoped" };
37740
+ }
37741
+ function resolveEventRegistrationScopedEvents(events, context) {
37742
+ const scope = resolveEventRegistrationScope(context);
37743
+ switch (scope.kind) {
37744
+ case "occurrenceSeries":
37745
+ case "pageSurfaceSeries": return events.filter((event) => event.seriesId === scope.seriesId);
37746
+ case "pageSurfaceOffering": {
37747
+ const seriesIds = new Set(scope.seriesIds);
37748
+ return events.filter((event) => seriesIds.has(event.seriesId));
37749
+ }
37750
+ case "unsupportedPageSurfaceSubject": return [];
37751
+ case "unscoped": return [...events];
37752
+ }
37753
+ }
37754
+ //#endregion
36551
37755
  //#region ../blocks/src/system/constants/events.ts
36552
37756
  const eventCalendarDisplayModeValues = [
36553
37757
  "combined",
@@ -36670,95 +37874,6 @@ const calendarTabViewsSchema = z.enum([
36670
37874
  "week"
36671
37875
  ]);
36672
37876
  //#endregion
36673
- //#region ../blocks/src/system/runtime/nodes/event-registration/wizardSteps.ts
36674
- /**
36675
- * Determine which wizard steps to show based on context and payment requirements.
36676
- *
36677
- * @param hasPreSelectedContext - Whether event is pre-selected (skips event selection)
36678
- * @param needsPaymentStep - Whether user must complete payment method selection
36679
- * @returns Array of wizard steps to display
36680
- */
36681
- function getWizardStepsForContext(hasPreSelectedContext, needsPaymentStep, paymentActive = false) {
36682
- const baseSteps = hasPreSelectedContext ? ["select-tickets", "enter-details"] : [
36683
- "select-event",
36684
- "select-tickets",
36685
- "enter-details"
36686
- ];
36687
- if (!needsPaymentStep) return baseSteps;
36688
- return paymentActive ? [
36689
- ...baseSteps,
36690
- "select-payment",
36691
- "payment"
36692
- ] : [...baseSteps, "select-payment"];
36693
- }
36694
- //#endregion
36695
- //#region ../blocks/src/system/runtime/nodes/event-registration/registrationDerivations.ts
36696
- function eventMatchesOccurrenceId(event, occurrenceId) {
36697
- return event.registrationSubject.kind === "occurrence" && (event.registrationSubject.occurrenceId === occurrenceId || event.selectionId === occurrenceId);
36698
- }
36699
- function findPreSelectedEvent(input) {
36700
- const occurrenceContext = input.occurrenceContext;
36701
- if (!occurrenceContext) return input.scopedEvents.length === 1 ? input.scopedEvents[0] ?? null : null;
36702
- const contextDate = getBookingLocalDateKey(toScheduledBookingDateTime({
36703
- startsAt: occurrenceContext.startsAt,
36704
- timeZone: occurrenceContext.timeZone
36705
- }));
36706
- return input.scopedEvents.find((event) => eventMatchesOccurrenceId(event, occurrenceContext.id) || getBookingLocalDateKey(toScheduledBookingDateTime({
36707
- startsAt: event.startsAt,
36708
- timeZone: event.timeZone
36709
- })) === contextDate) ?? null;
36710
- }
36711
- function resolveEventRequiresPayment(input) {
36712
- if (input.eligibility) return input.eligibility.requiresPayment;
36713
- if (input.selectedTicketType) return input.selectedTicketType.priceAmount > 0;
36714
- if (!input.event) return false;
36715
- return publicEventPricingType(input.event.pricing) === "paid";
36716
- }
36717
- //#endregion
36718
- //#region ../blocks/src/system/runtime/nodes/event-registration/registrationEventAdapter.ts
36719
- function toRegistrationEvent(event) {
36720
- return {
36721
- ...event,
36722
- selectionId: event.id,
36723
- registrationSubject: {
36724
- kind: "occurrence",
36725
- occurrenceId: event.occurrenceId
36726
- }
36727
- };
36728
- }
36729
- function toRegistrationEvents(events) {
36730
- return events.filter((event) => event.bookingScope === "single_occurrence").map(toRegistrationEvent);
36731
- }
36732
- /**
36733
- * Project one whole-series run onto the registration surface.
36734
- *
36735
- * Capacity, pricing, payment terms and consent come straight off the canonical
36736
- * Event record; only the schedule is widened from the single occurrence to the
36737
- * live run. A run without live sessions has no registration event.
36738
- */
36739
- function toWholeSeriesRegistrationEvent(group) {
36740
- const { representative, run } = group;
36741
- const sessions = orderedLiveRunSessions(run.sessions);
36742
- const firstSession = sessions[0];
36743
- if (!firstSession) return null;
36744
- const lastSession = sessions[sessions.length - 1] ?? firstSession;
36745
- return {
36746
- ...representative,
36747
- selectionId: run.eventSeriesId,
36748
- registrationSubject: {
36749
- kind: "wholeSeries",
36750
- eventSeriesId: run.eventSeriesId,
36751
- sessions
36752
- },
36753
- startsAt: firstSession.startsAt,
36754
- endsAt: lastSession.endsAt,
36755
- timeZone: firstSession.timeZone
36756
- };
36757
- }
36758
- function toWholeSeriesRegistrationEvents(events) {
36759
- return groupPublicEventsByRun(events).map(toWholeSeriesRegistrationEvent).filter((event) => event !== null);
36760
- }
36761
- //#endregion
36762
37877
  //#region ../blocks/src/system/runtime/nodes/event-registration/eventSourceRecovery.ts
36763
37878
  const { limit: _rawLimit, eventSurface: _rawEventSurface, scheduleScope: _rawScheduleScope, detailLevel: _rawDetailLevel, ...stringEventQueryShape } = publicEventLoaderQuerySchemaShape;
36764
37879
  const eventRecoveryRequestSchema = z.object({
@@ -36801,27 +37916,17 @@ function parseEventLoaderRecoveryRequest(value) {
36801
37916
  const EVENT_REGISTRATION_GENERIC_SHELL_STEP_LABELS = [
36802
37917
  "Date",
36803
37918
  "Tickets",
36804
- "Details"
36805
- ];
36806
- const EVENT_REGISTRATION_SHELL_STEP_LABELS = [
36807
- "Event",
36808
- "Date",
36809
- "Tickets",
36810
- "Details",
36811
- "Payment options",
36812
- "Payment"
37919
+ "Your details"
36813
37920
  ];
36814
37921
  const eventRegistrationPublicEventSchema = z.preprocess(normalizeEventRegistrationEventInput, publicEventSchema);
36815
- const eventRegistrationHydrationSchemaShape = {
37922
+ const eventRegistrationCommonSchemaShape = {
36816
37923
  demoEmailSuppressed: z.boolean().optional(),
36817
37924
  occurrenceContext: occurrenceContextSchema.optional(),
36818
37925
  subject: eventRegistrationPageSurfaceSubjectSchema.nullable().optional(),
36819
37926
  maxTickets: z.string().optional(),
36820
- showVenue: z.boolean().optional(),
36821
- showCapacity: z.boolean().optional(),
36822
37927
  successMessage: z.string().optional(),
36823
37928
  buttonVariant: registrationButtonVariantSchema.optional(),
36824
- overlayEnabled: z.boolean().optional(),
37929
+ host: bookingHostSchema.optional(),
36825
37930
  spamProtectionEnabled: z.boolean().optional(),
36826
37931
  supportEmail: z.string().optional(),
36827
37932
  sitePasses: publicPassProductCollectionSchema.nullable().optional(),
@@ -36846,32 +37951,51 @@ const eventRegistrationSourceSchema = z.object({
36846
37951
  portalToken: z.string().nullable().optional(),
36847
37952
  previewToken: z.string().nullable().optional(),
36848
37953
  readAuthority: browserReadAuthoritySchema.optional(),
36849
- ...eventRegistrationHydrationSchemaShape,
37954
+ ...eventRegistrationCommonSchemaShape,
37955
+ showVenue: z.boolean().optional(),
37956
+ showCapacity: z.boolean().optional(),
36850
37957
  events: z.array(eventRegistrationPublicEventSchema).nullable().optional(),
36851
37958
  wholeSeriesRunEvents: z.array(eventRegistrationPublicEventSchema).nullable().optional(),
36852
37959
  eventLoaderFailures: z.record(z.string(), z.unknown()).nullable().optional(),
36853
37960
  className: z.string().nullable().optional(),
36854
37961
  buttonText: z.string().nullable().optional(),
36855
37962
  maxTickets: z.string().nullable().optional(),
36856
- stage: previewStageSchema.optional()
37963
+ stage: previewStageSchema.optional(),
37964
+ bookingOfferingPresets: z.array(z.custom((value) => typeof value === "object" && value !== null && typeof value.label === "string")).nullable().optional()
36857
37965
  });
36858
- const eventRegistrationDisplaySchema = z.discriminatedUnion("state", [z.object({
36859
- kind: z.literal("event-registration"),
36860
- state: z.literal("generic-shell"),
36861
- className: z.string().nullable(),
36862
- titleText: z.string(),
36863
- bodyText: z.string(),
36864
- stepLabels: z.array(z.enum(EVENT_REGISTRATION_SHELL_STEP_LABELS)).min(1),
36865
- summaryButtonText: z.string()
36866
- }), z.object({
36867
- kind: z.literal("event-registration"),
36868
- state: z.literal("registration-closed"),
36869
- className: z.string().nullable(),
36870
- titleText: z.string(),
36871
- bodyText: z.string()
36872
- })]);
37966
+ const eventRegistrationDisplaySchema = z.discriminatedUnion("state", [
37967
+ z.object({
37968
+ kind: z.literal("event-registration"),
37969
+ state: z.literal("generic-shell"),
37970
+ className: z.string().nullable(),
37971
+ titleText: z.string(),
37972
+ bodyText: z.string(),
37973
+ stepLabels: z.array(z.enum(REGISTRATION_STEP_LABELS)).min(1),
37974
+ summaryButtonText: z.string()
37975
+ }),
37976
+ z.object({
37977
+ kind: z.literal("event-registration"),
37978
+ state: z.literal("registration-closed"),
37979
+ className: z.string().nullable(),
37980
+ titleText: z.string(),
37981
+ bodyText: z.string()
37982
+ }),
37983
+ z.object({
37984
+ kind: z.literal("event-registration"),
37985
+ state: z.literal("booking-card"),
37986
+ className: z.string().nullable(),
37987
+ entry: idleBookingEntrySchema,
37988
+ buttonVariant: registrationButtonVariantSchema.nullable(),
37989
+ buttonText: z.string()
37990
+ })
37991
+ ]);
36873
37992
  const eventRegistrationHydrationSchema = z.object({
36874
- ...eventRegistrationHydrationSchemaShape,
37993
+ ...eventRegistrationCommonSchemaShape,
37994
+ display: registrationDisplaySchema,
37995
+ offeringNoun: z.object({
37996
+ label: z.string(),
37997
+ pluralLabel: z.string()
37998
+ }).optional(),
36875
37999
  eventSource: eventRegistrationEventSourceSchema
36876
38000
  });
36877
38001
  const eventRegistrationRenderSchema = z.object({
@@ -36886,7 +38010,7 @@ z.object({
36886
38010
  readAuthority: browserReadAuthoritySchema.optional(),
36887
38011
  stage: previewStageSchema.optional()
36888
38012
  });
36889
- function buildEventRegistrationDisplayFromScopedEvents(source, scopedSourceEvents) {
38013
+ function buildEventRegistrationDisplayFromScopedEvents(source, scopedSourceEvents, display) {
36890
38014
  const selectedRun = resolveSelectedWholeSeriesRun(scopedSourceEvents);
36891
38015
  if (selectedRun && (!selectedRun.run.enrollmentOpen || toWholeSeriesRegistrationEvent(selectedRun) === null)) return {
36892
38016
  kind: "event-registration",
@@ -36895,6 +38019,8 @@ function buildEventRegistrationDisplayFromScopedEvents(source, scopedSourceEvent
36895
38019
  titleText: "Registration closed",
36896
38020
  bodyText: "This run is no longer open for bookings."
36897
38021
  };
38022
+ const card = bookingCardDisplay(source, scopedSourceEvents, display);
38023
+ if (card) return card;
36898
38024
  const stepLabels = buildEventRegistrationShellStepLabels(source, scopedSourceEvents);
36899
38025
  return {
36900
38026
  kind: "event-registration",
@@ -36911,11 +38037,14 @@ function buildEventRegistrationShellStepLabels(source, scopedSourceEvents) {
36911
38037
  if (selectedRun) {
36912
38038
  const selectedEvent = toWholeSeriesRegistrationEvent(selectedRun);
36913
38039
  if (!selectedEvent) return EVENT_REGISTRATION_GENERIC_SHELL_STEP_LABELS;
36914
- return getWizardStepsForContext(true, resolveEventRequiresPayment({
36915
- eligibility: null,
36916
- selectedTicketType: null,
36917
- event: selectedEvent
36918
- })).map((step) => shellLabelForRegistrationStep(step, [selectedEvent]));
38040
+ return shellStepLabels(registrationRoute({
38041
+ chooseEvent: false,
38042
+ payment: resolveEventRequiresPayment({
38043
+ eligibility: null,
38044
+ selectedTicketType: null,
38045
+ event: selectedEvent
38046
+ }) ? "optionsAndPayment" : "none"
38047
+ }), registrationStepLabelContext([selectedEvent], selectedEvent));
36919
38048
  }
36920
38049
  const scopedEvents = toScopedEventRegistrationEvents(scopedSourceEvents);
36921
38050
  if (scopedEvents.length === 0) return EVENT_REGISTRATION_GENERIC_SHELL_STEP_LABELS;
@@ -36924,14 +38053,15 @@ function buildEventRegistrationShellStepLabels(source, scopedSourceEvents) {
36924
38053
  occurrenceContext: source.occurrenceContext
36925
38054
  });
36926
38055
  const hasPreSelectedContext = Boolean(source.occurrenceContext || preSelectedEvent);
36927
- return getWizardStepsForContext(hasPreSelectedContext, hasPreSelectedContext && preSelectedEvent ? resolveEventRequiresPayment({
38056
+ const needsPaymentStep = hasPreSelectedContext && preSelectedEvent ? resolveEventRequiresPayment({
36928
38057
  eligibility: null,
36929
38058
  selectedTicketType: null,
36930
38059
  event: preSelectedEvent
36931
- }) : false).map((step) => shellLabelForRegistrationStep(step, scopedEvents));
36932
- }
36933
- function toScopedEventRegistrationEvents(scopedSourceEvents) {
36934
- return [...toRegistrationEvents(scopedSourceEvents), ...toWholeSeriesRegistrationEvents(scopedSourceEvents)];
38060
+ }) : false;
38061
+ return shellStepLabels(registrationRoute({
38062
+ chooseEvent: !hasPreSelectedContext,
38063
+ payment: needsPaymentStep ? "optionsAndPayment" : "none"
38064
+ }), registrationStepLabelContext(scopedEvents, preSelectedEvent));
36935
38065
  }
36936
38066
  function scopeEventRegistrationSourceEvents(source, eventSource) {
36937
38067
  return resolveEventRegistrationScopedEvents(eventSource.kind === "loaded" ? eventSource.events : [], {
@@ -36947,29 +38077,12 @@ function resolveSelectedWholeSeriesRun(scopedSourceEvents) {
36947
38077
  const runs = groupPublicEventsByRun(scopedSourceEvents);
36948
38078
  return runs.length === 1 ? runs[0] : null;
36949
38079
  }
36950
- function shellLabelForRegistrationStep(step, events) {
36951
- switch (step) {
36952
- case "select-event": return isDateSelection(events) ? "Date" : "Event";
36953
- case "select-tickets": return "Tickets";
36954
- case "enter-details": return "Details";
36955
- case "select-payment": return "Payment options";
36956
- case "payment": return "Payment";
36957
- case "error":
36958
- case "success":
36959
- case "submitting":
36960
- case "verifying-payment":
36961
- case "verifying-timeout":
36962
- case "payment-status":
36963
- case "payment-failed": return "Details";
36964
- }
36965
- }
36966
- function isDateSelection(events) {
36967
- const firstSeriesId = events[0]?.seriesId;
36968
- return Boolean(events.length > 1 && firstSeriesId && events.every((event) => event.seriesId === firstSeriesId));
38080
+ function shellStepLabels(route, context) {
38081
+ return route.map((step) => registrationStepLabel(step, context));
36969
38082
  }
36970
38083
  function buildEventRegistrationShellBodyText(stepLabels) {
36971
- if (stepLabels.includes("Payment options")) return "Preparing tickets, details, and payment options.";
36972
- if (stepLabels[0] === "Tickets") return "Preparing tickets and booking details.";
38084
+ if (stepLabels.includes("How you’ll pay")) return "Preparing tickets, details, and payment options.";
38085
+ if (stepLabels[0] === "Tickets" || stepLabels[0] === "Places") return "Preparing tickets and booking details.";
36973
38086
  return "Preparing dates, tickets, and booking details.";
36974
38087
  }
36975
38088
  const eventRegistrationBlockRuntime = defineClientOwnedBlockRuntime({
@@ -36979,6 +38092,7 @@ const eventRegistrationBlockRuntime = defineClientOwnedBlockRuntime({
36979
38092
  render: createZodCodec(eventRegistrationRenderSchema)
36980
38093
  },
36981
38094
  buildRender: (source) => {
38095
+ const display = resolveRegistrationDisplay(source);
36982
38096
  const eventSource = resolveEventRegistrationEventSource({
36983
38097
  events: source.events,
36984
38098
  wholeSeriesRunEvents: source.wholeSeriesRunEvents,
@@ -36986,7 +38100,7 @@ const eventRegistrationBlockRuntime = defineClientOwnedBlockRuntime({
36986
38100
  });
36987
38101
  const scopedSourceEvents = scopeEventRegistrationSourceEvents(source, eventSource);
36988
38102
  return {
36989
- display: buildEventRegistrationDisplayFromScopedEvents(source, scopedSourceEvents),
38103
+ display: buildEventRegistrationDisplayFromScopedEvents(source, scopedSourceEvents, display),
36990
38104
  hydration: {
36991
38105
  occurrenceContext: source.occurrenceContext ?? null,
36992
38106
  subject: source.subject ?? null,
@@ -36996,29 +38110,29 @@ const eventRegistrationBlockRuntime = defineClientOwnedBlockRuntime({
36996
38110
  } : eventSource,
36997
38111
  demoEmailSuppressed: source.demoEmailSuppressed,
36998
38112
  maxTickets: source.maxTickets ?? void 0,
36999
- showVenue: source.showVenue,
37000
- showCapacity: source.showCapacity,
38113
+ display,
37001
38114
  successMessage: source.successMessage,
37002
38115
  buttonVariant: source.buttonVariant,
37003
- overlayEnabled: source.overlayEnabled,
38116
+ host: source.host ?? "overlay",
37004
38117
  spamProtectionEnabled: source.spamProtectionEnabled,
37005
38118
  supportEmail: source.supportEmail,
37006
38119
  sitePasses: source.sitePasses ?? null,
37007
38120
  siteMemberships: source.siteMemberships ?? null,
37008
38121
  mailingList: source.mailingList,
37009
- siteName: source.siteName
38122
+ siteName: source.siteName,
38123
+ offeringNoun: resolveOfferingNoun(source, scopedSourceEvents)
37010
38124
  }
37011
38125
  };
37012
38126
  }
37013
38127
  });
37014
38128
  function normalizeEventRegistrationEventInput(value) {
37015
- if (!isRecord$3(value)) return value;
38129
+ if (!isRecord$2(value)) return value;
37016
38130
  return {
37017
38131
  ...value,
37018
38132
  destination: resolveRuntimeEventDestination(value)
37019
38133
  };
37020
38134
  }
37021
- function isRecord$3(value) {
38135
+ function isRecord$2(value) {
37022
38136
  return typeof value === "object" && value !== null && !Array.isArray(value);
37023
38137
  }
37024
38138
  const eventRegistrationBlockDefinition = {
@@ -37058,7 +38172,7 @@ const eventRegistrationBlockDefinition = {
37058
38172
  description: "Main heading for the registration form",
37059
38173
  required: false,
37060
38174
  multiline: false,
37061
- defaultValue: "Register for this event"
38175
+ defaultValue: ""
37062
38176
  },
37063
38177
  {
37064
38178
  id: "description",
@@ -37130,11 +38244,11 @@ const eventRegistrationBlockDefinition = {
37130
38244
  {
37131
38245
  id: "buttonText",
37132
38246
  type: "text",
37133
- label: "Submit button text",
37134
- description: "Text for the registration submit button",
38247
+ label: "Button text",
38248
+ description: "Text on the booking button",
37135
38249
  required: false,
37136
38250
  multiline: false,
37137
- defaultValue: "Complete Registration",
38251
+ defaultValue: "Book your place",
37138
38252
  maxLength: 40
37139
38253
  },
37140
38254
  createButtonVariantField()
@@ -37166,21 +38280,21 @@ const eventRegistrationBlockDefinition = {
37166
38280
  successMessage: { $bind: { from: "content.successMessage" } },
37167
38281
  buttonText: { $bind: {
37168
38282
  from: "content.buttonText",
37169
- fallback: "Complete Registration"
38283
+ fallback: "Book your place"
37170
38284
  } },
37171
38285
  buttonVariant: { $bind: {
37172
38286
  from: "content.buttonVariant",
37173
38287
  fallback: "primary"
37174
38288
  } },
37175
- overlayEnabled: { $bind: {
37176
- from: "$root.eventRegistrationOverlayEnabled",
37177
- fallback: false
37178
- } },
37179
38289
  events: { $bind: { from: "data.events" } },
37180
38290
  wholeSeriesRunEvents: { $bind: { from: "data.wholeSeriesRunEvents" } },
37181
38291
  loaderFailures: { $bind: { from: "$root.loaderFailures" } },
37182
38292
  mailingList: { $bind: { from: "$root.mailingList" } },
37183
38293
  siteName: { $bind: { from: "$root.siteName" } },
38294
+ bookingOfferingPresets: { $bind: {
38295
+ from: "$root.bookingOfferingPresets",
38296
+ fallback: []
38297
+ } },
37184
38298
  sitePasses: { $bind: { from: "data.sitePasses" } },
37185
38299
  siteMemberships: { $bind: { from: "data.siteMemberships" } },
37186
38300
  stage: { $bind: {
@@ -39841,7 +40955,21 @@ const eventCalendarBlockDefinition = {
39841
40955
  list: { label: "List" }
39842
40956
  }
39843
40957
  }, eventCalendarContentShape),
39844
- normalizeContent: normalizeEventCalendarContent,
40958
+ contentNormalizer: {
40959
+ fieldIds: [
40960
+ "variant",
40961
+ "displayMode",
40962
+ "listLayout",
40963
+ "defaultCalendarView",
40964
+ "eventSurface",
40965
+ "filterEventPresets",
40966
+ "scheduleScope",
40967
+ "filterCategoryIds",
40968
+ "filterStaffMemberId",
40969
+ "filterVenueId"
40970
+ ],
40971
+ normalize: normalizeEventCalendarContent
40972
+ },
39845
40973
  dataSchemas: {
39846
40974
  events: publicEventsArraySchema.optional(),
39847
40975
  calendarWindow: calendarWindowPageSchema.optional()
@@ -40090,90 +41218,6 @@ const recurringScheduleBlockDefinition = {
40090
41218
  } }
40091
41219
  };
40092
41220
  //#endregion
40093
- //#region ../blocks/src/system/blocks/offerings-preview/model.ts
40094
- const OFFERINGS_PREVIEW_DEFAULT_BUTTON_TEXT = "Book";
40095
- const OFFERINGS_PREVIEW_DEFAULT_EMPTY_MESSAGE = "Nothing scheduled just yet.";
40096
- const OFFERINGS_PREVIEW_EVENT_PRESET_DEFINITIONS = [
40097
- {
40098
- preset: "class",
40099
- sourceKey: "eventPreset:class",
40100
- dataKey: "classEvents",
40101
- label: "Classes"
40102
- },
40103
- {
40104
- preset: "workshop",
40105
- sourceKey: "eventPreset:workshop",
40106
- dataKey: "workshopEvents",
40107
- label: "Workshops"
40108
- },
40109
- {
40110
- preset: "retreat",
40111
- sourceKey: "eventPreset:retreat",
40112
- dataKey: "retreatEvents",
40113
- label: "Retreats"
40114
- },
40115
- {
40116
- preset: "performance",
40117
- sourceKey: "eventPreset:performance",
40118
- dataKey: "performanceEvents",
40119
- label: "Performances"
40120
- }
40121
- ];
40122
- OFFERINGS_PREVIEW_EVENT_PRESET_DEFINITIONS.map((definition) => definition.preset);
40123
- const OFFERINGS_PREVIEW_AUTHORABLE_SOURCE_KEYS = [...OFFERINGS_PREVIEW_EVENT_PRESET_DEFINITIONS.map((definition) => definition.sourceKey), "course"];
40124
- const OFFERINGS_PREVIEW_SOURCE_KEYS = OFFERINGS_PREVIEW_AUTHORABLE_SOURCE_KEYS;
40125
- Object.fromEntries(OFFERINGS_PREVIEW_EVENT_PRESET_DEFINITIONS.map((definition) => [definition.sourceKey, {
40126
- kind: "eventPreset",
40127
- preset: definition.preset
40128
- }]));
40129
- function isOfferingsPreviewSourceKey(value) {
40130
- return typeof value === "string" && OFFERINGS_PREVIEW_SOURCE_KEYS.includes(value);
40131
- }
40132
- function normalizeOfferingsPreviewGroupOrder(value) {
40133
- if (value == null) return { kind: "legacy" };
40134
- if (value.length === 0) return { kind: "default" };
40135
- const keys = uniqueOfferingsPreviewSourceKeys(value);
40136
- return keys.length > 0 ? {
40137
- kind: "custom",
40138
- keys
40139
- } : { kind: "default" };
40140
- }
40141
- function inferLegacyOfferingsPreviewGroupOrder(overrides) {
40142
- const keys = uniqueOfferingsPreviewSourceKeys((overrides ?? []).map((override) => override.sourceKey));
40143
- return keys.length >= 2 ? keys : [];
40144
- }
40145
- function normalizeOfferingsPreviewContentForWrite(value) {
40146
- if (!isRecord$2(value)) return null;
40147
- const groupOrderValue = value.groupOrder;
40148
- if (Array.isArray(groupOrderValue)) {
40149
- const normalized = normalizeOfferingsPreviewGroupOrder(groupOrderValue);
40150
- return {
40151
- ...value,
40152
- groupOrder: normalized.kind === "custom" ? normalized.keys : []
40153
- };
40154
- }
40155
- if (groupOrderValue != null) return {
40156
- ...value,
40157
- groupOrder: []
40158
- };
40159
- const legacyOrder = inferLegacyOfferingsPreviewGroupOrder(Array.isArray(value.groupOverrides) ? value.groupOverrides.filter(isRecord$2) : []);
40160
- return legacyOrder.length > 0 ? {
40161
- ...value,
40162
- groupOrder: legacyOrder
40163
- } : value;
40164
- }
40165
- function uniqueOfferingsPreviewSourceKeys(values) {
40166
- const keys = [];
40167
- for (const value of values) {
40168
- if (!isOfferingsPreviewSourceKey(value)) continue;
40169
- if (!keys.includes(value)) keys.push(value);
40170
- }
40171
- return keys;
40172
- }
40173
- function isRecord$2(value) {
40174
- return typeof value === "object" && value !== null && !Array.isArray(value);
40175
- }
40176
- //#endregion
40177
41221
  //#region ../blocks/src/system/blocks/offerings-preview/fields.ts
40178
41222
  const groupOverrideFields = [
40179
41223
  {
@@ -40537,7 +41581,22 @@ const offeringsPreviewEventDataSchemas = Object.fromEntries(OFFERINGS_PREVIEW_EV
40537
41581
  const offeringsPreviewEventDataLoaders = Object.fromEntries(OFFERINGS_PREVIEW_EVENT_PRESET_DEFINITIONS.map((definition) => [definition.dataKey, eventPresetLoader(definition.preset)]));
40538
41582
  const offeringsPreviewBlockDefinition = {
40539
41583
  manifest: offeringsPreviewManifest,
40540
- normalizeContent: normalizeOfferingsPreviewBlockContent,
41584
+ contentNormalizer: {
41585
+ fieldIds: [
41586
+ "displayMode",
41587
+ "heading",
41588
+ "description",
41589
+ "itemLimit",
41590
+ "groupOrder",
41591
+ "groupOverrides",
41592
+ "_cardStyles",
41593
+ "buttonVariant",
41594
+ "buttonText",
41595
+ "emptyMessage",
41596
+ "_sectionStyles"
41597
+ ],
41598
+ normalize: normalizeOfferingsPreviewBlockContent
41599
+ },
40541
41600
  dataSchemas: {
40542
41601
  ...offeringsPreviewEventDataSchemas,
40543
41602
  routeCandidates: publicRouteCandidatesSchema.optional(),
@@ -42091,6 +43150,14 @@ const audioPlayerBlockDefinition = { manifest: createBlockManifest({
42091
43150
  }
42092
43151
  }) };
42093
43152
  //#endregion
43153
+ //#region ../blocks/src/lib/media.ts
43154
+ const PUBLIC_DOWNLOAD_MEDIA_TYPES = [
43155
+ "document",
43156
+ "spreadsheet",
43157
+ "archive",
43158
+ "audio"
43159
+ ];
43160
+ //#endregion
42094
43161
  //#region ../blocks/src/system/blocks/file-download.ts
42095
43162
  const bound = (from) => ({ $bind: { from } });
42096
43163
  const fileDownloadLayout = styledSection({
@@ -44135,11 +45202,11 @@ function buildNewsletterDisplay(source) {
44135
45202
  successMessage: DEFAULT_MAILING_LIST_SUCCESS_MESSAGE,
44136
45203
  privacyNote: null
44137
45204
  };
44138
- const targeting = source.targeting ?? { mode: "default" };
44139
- const choices = resolveRuntimeChoices(context, targeting);
45205
+ const assessment = assessMailingListTargeting(context, source.targeting ?? { mode: "default" });
45206
+ const choices = resolveRuntimeChoices(assessment);
44140
45207
  return {
44141
45208
  kind: "newsletter-form",
44142
- availability: runtimeTargetingIsAvailable(context, targeting, choices) ? "ready" : "unavailable",
45209
+ availability: assessment.tag === "available" ? "ready" : "unavailable",
44143
45210
  className: source.className ?? null,
44144
45211
  buttonLabel: form.buttonLabel,
44145
45212
  successMessage: form.successMessage,
@@ -44196,60 +45263,12 @@ function projectNewsletterFormHydrationTarget(source) {
44196
45263
  default: return assertNever$2(source);
44197
45264
  }
44198
45265
  }
44199
- function resolveRuntimeChoices(context, targeting) {
44200
- if (context?.tag !== "configured" || targeting.mode !== "custom" || targeting.selection.tag !== "visitor_choice" || context.provider.provider !== targeting.provider) return [];
44201
- switch (targeting.provider) {
44202
- case "native": {
44203
- const offered = new Set(targeting.selection.offeredTopicIds);
44204
- return context.provider.provider === "native" ? context.provider.topics.filter((item) => offered.has(item.id)).map((item) => ({
44205
- id: item.id,
44206
- label: item.name
44207
- })) : [];
44208
- }
44209
- case "brevo": {
44210
- const offered = new Set(targeting.selection.offeredListIds);
44211
- return context.provider.provider === "brevo" ? context.provider.lists.filter((item) => offered.has(item.id)).map((item) => ({
44212
- id: String(item.id),
44213
- label: item.name
44214
- })) : [];
44215
- }
44216
- case "mailchimp": {
44217
- const offered = new Set(targeting.selection.offeredInterestIds);
44218
- 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) => ({
44219
- id: item.id,
44220
- label: item.name
44221
- })) ?? [];
44222
- }
44223
- }
44224
- }
44225
- function runtimeTargetingIsAvailable(context, targeting, choices) {
44226
- if (context?.tag !== "configured") return false;
44227
- const provider = context.provider;
44228
- if (targeting.mode === "default") switch (provider.provider) {
44229
- case "native": return provider.topics.some((item) => item.id === provider.defaultTopicId);
44230
- case "brevo": return provider.lists.some((item) => item.id === provider.defaultListId);
44231
- case "mailchimp": return provider.audiences.some((item) => item.id === provider.defaultAudienceId);
44232
- }
44233
- if (targeting.provider !== provider.provider) return false;
44234
- switch (targeting.provider) {
44235
- case "native": return provider.provider === "native" && nativeTargetIsAvailable(provider, targeting, choices);
44236
- case "brevo": return provider.provider === "brevo" && brevoTargetIsAvailable(provider, targeting, choices);
44237
- case "mailchimp": return provider.provider === "mailchimp" && mailchimpTargetIsAvailable(provider, targeting, choices);
44238
- }
44239
- }
44240
- function nativeTargetIsAvailable(provider, targeting, choices) {
44241
- const ids = targeting.selection.tag === "fixed" ? targeting.selection.topicIds : targeting.selection.offeredTopicIds;
44242
- return ids.every((id) => provider.topics.some((item) => item.id === id)) && (targeting.selection.tag !== "visitor_choice" || choices.length === ids.length);
44243
- }
44244
- function brevoTargetIsAvailable(provider, targeting, choices) {
44245
- const ids = targeting.selection.tag === "fixed" ? targeting.selection.listIds : targeting.selection.offeredListIds;
44246
- return ids.every((id) => provider.lists.some((item) => item.id === id)) && (targeting.selection.tag !== "visitor_choice" || choices.length === ids.length);
44247
- }
44248
- function mailchimpTargetIsAvailable(provider, targeting, choices) {
44249
- const audience = provider.audiences.find((item) => item.id === targeting.audienceId);
44250
- if (!audience) return false;
44251
- const ids = targeting.selection.tag === "fixed" ? targeting.selection.interestIds : targeting.selection.offeredInterestIds;
44252
- return ids.every((id) => audience.interests.some((item) => item.id === id)) && (targeting.selection.tag !== "visitor_choice" || choices.length === ids.length);
45266
+ function resolveRuntimeChoices(assessment) {
45267
+ if (!("value" in assessment) || assessment.value.mode !== "custom" || !("selectedChoices" in assessment.value) || assessment.value.targeting.selection.tag !== "visitor_choice") return [];
45268
+ return assessment.value.selectedChoices.map((item) => ({
45269
+ id: String(item.id),
45270
+ label: item.name
45271
+ }));
44253
45272
  }
44254
45273
  function parseRequiredMailingListSignupBlockId(value) {
44255
45274
  const parsed = parseMailingListBlockId(value);
@@ -44353,6 +45372,10 @@ const newsletterSignupManifest = createBlockManifest({
44353
45372
  [...(newsletterSignupManifest.fields ?? []).map((field) => field.id)];
44354
45373
  const newsletterSignupBlockDefinition = {
44355
45374
  manifest: newsletterSignupManifest,
45375
+ contentNormalizer: {
45376
+ fieldIds: ["targeting"],
45377
+ normalize: normalizeMailingListBlockContent
45378
+ },
44356
45379
  runtime: newsletterFormBlockRuntime
44357
45380
  };
44358
45381
  //#endregion
@@ -44380,7 +45403,7 @@ const memberPhotoClassName = (baseClass) => cardRoleChildClass({
44380
45403
  baseClass,
44381
45404
  stylesFrom: "content._cardStyles"
44382
45405
  });
44383
- const teamMemberPhotoSchema = mediaSchema.refine((media) => media.type === "image", { message: "Team member photos must be image media" }).nullable();
45406
+ const teamMemberPhotoSchema = runtimeMediaSchema.refine((media) => media.type === "image", { message: "Team member photos must be image media" }).nullable();
44384
45407
  const teamMemberDataSchema = z.object({
44385
45408
  _type: z.literal("teamMember"),
44386
45409
  id: z.string(),
@@ -45085,7 +46108,20 @@ const locationMapBlockDefinition = {
45085
46108
  })], { gap: "md" })
45086
46109
  })
45087
46110
  }),
45088
- normalizeContent: normalizeLocationMapContent
46111
+ contentNormalizer: {
46112
+ fieldIds: [
46113
+ "heading",
46114
+ "locationSource",
46115
+ "venueId",
46116
+ "location",
46117
+ "showAddressText",
46118
+ "showDirections",
46119
+ "directionsLabel",
46120
+ "zoom",
46121
+ "heightPx"
46122
+ ],
46123
+ normalize: normalizeLocationMapContent
46124
+ }
45089
46125
  };
45090
46126
  //#endregion
45091
46127
  //#region ../blocks/src/system/blocks/index.ts
@@ -45909,11 +46945,14 @@ const offeringsPreviewDatesReadEnabled = {
45909
46945
  //#endregion
45910
46946
  //#region ../blocks/src/system/helpers/backgroundMedia.ts
45911
46947
  function resolveBackgroundMedia(input) {
45912
- const parsedMedia = mediaSchema.safeParse(input.image);
46948
+ const parsedMedia = runtimeMediaSchema.safeParse(input.image);
45913
46949
  if (!parsedMedia.success) return null;
45914
46950
  switch (parsedMedia.data.type) {
45915
46951
  case "image":
45916
- case "video": return parsedMedia.data;
46952
+ case "video": return {
46953
+ ...parsedMedia.data,
46954
+ type: parsedMedia.data.type
46955
+ };
45917
46956
  case "audio":
45918
46957
  case "document":
45919
46958
  case "spreadsheet":
@@ -45925,6 +46964,75 @@ function backgroundVideoMobileClassNames(input) {
45925
46964
  return input.showVideoOnMobile === true ? "" : "rb-hidden rb-md-block";
45926
46965
  }
45927
46966
  //#endregion
46967
+ //#region ../blocks/src/system/runtime/nodes/mediaContext.ts
46968
+ /**
46969
+ * Server-side: AsyncLocalStorage for request isolation.
46970
+ * Prevents race conditions when multiple requests are processed concurrently.
46971
+ *
46972
+ * Lazy initialization to avoid issues in environments that don't support async_hooks.
46973
+ */
46974
+ const serverStorageState = {
46975
+ storage: null,
46976
+ checked: false
46977
+ };
46978
+ function getServerStorage() {
46979
+ if (typeof window !== "undefined") return null;
46980
+ if (serverStorageState.checked) return serverStorageState.storage;
46981
+ serverStorageState.checked = true;
46982
+ try {
46983
+ const globalWithNonWebpackRequire = globalThis;
46984
+ const AsyncLocalStorage = (typeof globalWithNonWebpackRequire.__non_webpack_require__ === "function" ? globalWithNonWebpackRequire.__non_webpack_require__ : new Function("id", "return require(id)"))("async_hooks").AsyncLocalStorage;
46985
+ if (AsyncLocalStorage) {
46986
+ serverStorageState.storage = new AsyncLocalStorage();
46987
+ return serverStorageState.storage;
46988
+ }
46989
+ return null;
46990
+ } catch {
46991
+ if (process.env.NODE_ENV !== "production") console.warn("[blocks] AsyncLocalStorage not available - using module-level state");
46992
+ return null;
46993
+ }
46994
+ }
46995
+ /**
46996
+ * Client-side context storage.
46997
+ * Safe because browsers are single-threaded per tab.
46998
+ *
46999
+ * IMPORTANT: This must be shared across bundles.
47000
+ * SDK preview mode can load multiple independently-bundled entrypoints
47001
+ * (e.g. `@riverbankcms/sdk/rendering` + `@riverbankcms/sdk/preview-next/client`),
47002
+ * which can otherwise each get their own module-scoped singleton.
47003
+ */
47004
+ const RB_MEDIA_CONTEXT_KEY = Symbol.for("riverbankcms.blocks.mediaContext");
47005
+ function getClientContextStore() {
47006
+ const g = globalThis;
47007
+ const existing = g[RB_MEDIA_CONTEXT_KEY];
47008
+ if (existing && typeof existing === "object") return existing;
47009
+ const next = {};
47010
+ g[RB_MEDIA_CONTEXT_KEY] = next;
47011
+ return next;
47012
+ }
47013
+ /**
47014
+ * Get the current media context.
47015
+ * Server-side: Uses AsyncLocalStorage for request isolation.
47016
+ * Client-side: Uses module-level state (safe in single-threaded browser).
47017
+ */
47018
+ function getMediaContext() {
47019
+ const storage = getServerStorage();
47020
+ if (storage) return storage.getStore() ?? {};
47021
+ return getClientContextStore();
47022
+ }
47023
+ function getCurrentResolveImageUrlContext() {
47024
+ const context = getMediaContext();
47025
+ if (!context.supabaseUrl && !context.siteId && !context.mediaAssetIndex) return;
47026
+ return {
47027
+ supabaseUrl: context.supabaseUrl,
47028
+ siteId: context.siteId,
47029
+ mediaAssetIndex: context.mediaAssetIndex
47030
+ };
47031
+ }
47032
+ function getMediaAssetIndex(context) {
47033
+ return context?.mediaAssetIndex ?? getMediaContext().mediaAssetIndex;
47034
+ }
47035
+ //#endregion
45928
47036
  //#region ../blocks/src/system/runtime/nodes/media.tsx
45929
47037
  function getValidSourceDimension(value) {
45930
47038
  return typeof value === "number" && Number.isFinite(value) && value > 0 ? value : void 0;
@@ -45960,41 +47068,72 @@ function applyRectTransform(existingStyle, patch) {
45960
47068
  if (patch.transform) next.transform = appendTransform(existingStyle?.transform, patch.transform);
45961
47069
  return next;
45962
47070
  }
45963
- function computeMediaTransformStyle(value, style, targetAspectRatio, options) {
47071
+ /** Resolve portable content without exposing runtime metadata as saved values. */
47072
+ function resolveMediaWithContext(reference, context) {
47073
+ return resolveManagedMediaReference(reference, getMediaAssetIndex(context ?? void 0));
47074
+ }
47075
+ function sourceFromAsset(value, asset) {
47076
+ return {
47077
+ type: value.type,
47078
+ alt: value.alt,
47079
+ filename: value.filename,
47080
+ purpose: value.purpose,
47081
+ placeholder: value.placeholder,
47082
+ transform: value.transform,
47083
+ poster: value.poster,
47084
+ identifier: asset.identifier,
47085
+ assetId: asset.id,
47086
+ storageBucket: asset.storageBucket,
47087
+ storagePath: asset.storagePath,
47088
+ mimeType: asset.mimeType,
47089
+ width: asset.width,
47090
+ height: asset.height
47091
+ };
47092
+ }
47093
+ function resolveRuntimeMedia(value, context) {
47094
+ if ("asset" in value && value.asset) return sourceFromAsset(value, value.asset);
47095
+ if ("src" in value && value.src) return value;
47096
+ if (!value.identifier) return void 0;
47097
+ const resolved = resolveMediaWithContext(value, context);
47098
+ return resolved.kind === "resolved" ? sourceFromAsset(resolved.reference, resolved.asset) : void 0;
47099
+ }
47100
+ function usesProxyFraming(source, transform) {
47101
+ if (!source?.assetId || !isExplicitCropProxyEnabled()) return false;
47102
+ return Boolean(transform.rotation) || isExplicitRectCrop(transform.rect, getValidSourceDimension(source.width), getValidSourceDimension(source.height));
47103
+ }
47104
+ function rectFramingPatch(source, rect, proxy, style) {
47105
+ if (!source || !rect || proxy) return {};
47106
+ const patch = buildCssRectStylePatch(source, rect);
47107
+ return patch ? applyRectTransform(style, patch) : {};
47108
+ }
47109
+ function hotspotFramingPatch(transform, proxy, rectPatch, style) {
47110
+ if (!transform.hotspot || rectPatch.objectPosition !== void 0) return {};
47111
+ const fit = style?.objectFit;
47112
+ if (fit && fit !== "cover" && fit !== "contain") return {};
47113
+ const point = proxy && transform.rotation ? rotateNormalizedPoint(transform.hotspot, transform.rotation) : transform.hotspot;
47114
+ return { objectPosition: `${point.x * 100}% ${point.y * 100}%` };
47115
+ }
47116
+ function rotationFramingPatch(rotation, proxy, rectPatch, style) {
47117
+ if (!rotation || proxy) return {};
47118
+ return {
47119
+ transform: appendTransform(rectPatch.transform ?? style?.transform, `rotate(${rotation}deg)`),
47120
+ transformOrigin: rectPatch.transformOrigin ?? "center"
47121
+ };
47122
+ }
47123
+ /** CSS framing is a pure projection of the resolved source and per-use transform. */
47124
+ function deriveMediaFramingStyle(source, transform, proxy, style) {
47125
+ if (!transform.rect && !transform.hotspot && !transform.rotation) return style;
47126
+ const rectPatch = rectFramingPatch(source, transform.rect, proxy, style);
47127
+ return mergeStyles(style, {
47128
+ ...rectPatch,
47129
+ ...hotspotFramingPatch(transform, proxy, rectPatch, style),
47130
+ ...rotationFramingPatch(transform.rotation, proxy, rectPatch, style)
47131
+ });
47132
+ }
47133
+ function computeMediaTransformStyle(value, style, targetAspectRatio, options, context) {
47134
+ const source = resolveRuntimeMedia(value, context ?? getCurrentResolveImageUrlContext());
45964
47135
  const transform = options?.resolvedTransform ?? resolveMediaTransformForAspect(value.transform, targetAspectRatio);
45965
- const rect = transform.rect;
45966
- const hotspot = transform.hotspot;
45967
- const rotation = transform.rotation;
45968
- if (!rect && !hotspot && !rotation) return style;
45969
- const stylePatch = {};
45970
- const heuristicSuppressCss = Boolean(isExplicitCropProxyEnabled() && value.assetId && (typeof rotation === "number" && rotation !== 0 || isExplicitRectCrop(rect, getValidSourceDimension(value.width), getValidSourceDimension(value.height))));
45971
- const shouldSuppressCss = options?.isProxy ?? heuristicSuppressCss;
45972
- if (rect && !shouldSuppressCss) {
45973
- const rectPatch = buildCssRectStylePatch(value, rect);
45974
- if (rectPatch) {
45975
- const mergedRect = applyRectTransform(style, rectPatch);
45976
- Object.assign(stylePatch, mergedRect);
45977
- }
45978
- }
45979
- if (hotspot) {
45980
- const effectiveObjectFit = stylePatch.objectFit ?? style?.objectFit;
45981
- if ((!effectiveObjectFit || effectiveObjectFit === "cover" || effectiveObjectFit === "contain") && stylePatch.objectPosition === void 0) {
45982
- const resolved = shouldSuppressCss && rotation ? rotateNormalizedPoint({
45983
- x: hotspot.x,
45984
- y: hotspot.y
45985
- }, rotation) : {
45986
- x: hotspot.x,
45987
- y: hotspot.y
45988
- };
45989
- stylePatch.objectPosition = `${resolved.x * 100}% ${resolved.y * 100}%`;
45990
- }
45991
- }
45992
- if (rotation && !shouldSuppressCss) {
45993
- const existingTransform = stylePatch.transform ?? style?.transform;
45994
- stylePatch.transform = existingTransform ? `${existingTransform} rotate(${rotation}deg)` : `rotate(${rotation}deg)`;
45995
- if (!stylePatch.transformOrigin) stylePatch.transformOrigin = "center";
45996
- }
45997
- return mergeStyles(style, stylePatch);
47136
+ return deriveMediaFramingStyle(source, transform, options?.isProxy ?? usesProxyFraming(source, transform), style);
45998
47137
  }
45999
47138
  //#endregion
46000
47139
  //#region ../blocks/src/system/transforms/registry/background.ts
@@ -46143,11 +47282,11 @@ const backgroundTransforms = [
46143
47282
  }
46144
47283
  ];
46145
47284
  function parseMediaBackground(value) {
46146
- const background = parseBackgroundContent(value);
47285
+ const background = parseRuntimeBackgroundContent(value);
46147
47286
  return background?.type === "image" || background?.type === "video" ? background : null;
46148
47287
  }
46149
47288
  function resolveBackgroundLayerStyle(value, fallbackToken, context) {
46150
- const background = parseBackgroundContent(value);
47289
+ const background = parseRuntimeBackgroundContent(value);
46151
47290
  const style = {};
46152
47291
  const gradient = background?.type === "gradient" ? isString(background.gradient) ? background.gradient.trim() : gradientPresetCss(background.gradientPresetId) : null;
46153
47292
  const color = background?.type === "color" && isString(background.color) ? background.color.trim() : null;
@@ -46290,17 +47429,7 @@ const linksTransforms = [{
46290
47429
  id: "links.resolve",
46291
47430
  kind: "formatter",
46292
47431
  summary: "Resolve LinkValue objects to href strings using the route map",
46293
- run: (value, _options, context) => {
46294
- if (!value || typeof value !== "object") return null;
46295
- const link = value;
46296
- const kind = typeof link.kind === "string" ? link.kind : null;
46297
- if (kind === "internal") return resolveInternalLink(link, context);
46298
- if (kind === "page") return resolvePageLink(link, context);
46299
- if (kind === "entry") return resolveEntryLink(link, context);
46300
- if (kind === "external" || kind === "url") return resolveExternalLink(link);
46301
- if (!kind && typeof link.href === "string") return resolveExternalLink(link);
46302
- return null;
46303
- }
47432
+ run: (value, _options, context) => resolveAuthoredLinkHref(value, context)
46304
47433
  }, {
46305
47434
  id: "links.label",
46306
47435
  kind: "formatter",
@@ -46327,6 +47456,21 @@ const linksTransforms = [{
46327
47456
  return null;
46328
47457
  }
46329
47458
  }];
47459
+ function resolveAuthoredLinkHref(value, context = {}) {
47460
+ if (!value || typeof value !== "object") return null;
47461
+ const link = value;
47462
+ switch (link.kind) {
47463
+ case "internal": return resolveInternalLink(link, context);
47464
+ case "page": return resolvePageLink(link, context);
47465
+ case "entry": return resolveEntryLink(link, context);
47466
+ case "external":
47467
+ case "url": return resolveExternalLink(link);
47468
+ default: return resolvePreResolvedLink(link);
47469
+ }
47470
+ }
47471
+ function resolvePreResolvedLink(link) {
47472
+ return !(typeof link.kind === "string" ? link.kind : null) && typeof link.href === "string" ? resolveExternalLink(link) : null;
47473
+ }
46330
47474
  const eventCalendarSelectionParam = {
46331
47475
  id: "events.calendarSelectionParam",
46332
47476
  kind: "formatter",