@riverbankcms/sdk 0.136.0 → 0.138.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 (343) hide show
  1. package/CHANGELOG.md +109 -0
  2. package/README.md +7 -4
  3. package/dist/{PreviewEditorSidebar-BU5ZlhQR.mjs → PreviewEditorSidebar-BVPur_CU.mjs} +12 -18
  4. package/dist/{PreviewEditorUI-9XkHL-bC.mjs → PreviewEditorUI-DLhWX8oh.mjs} +3 -3
  5. package/dist/{PreviewQueryProvider-wbEpXryy.mjs → PreviewQueryProvider-BguJY1SW.mjs} +1 -1
  6. package/dist/{SdkPreviewModeRuntime-CnWmj-2B.mjs → SdkPreviewModeRuntime-Tda6qw4z.mjs} +1375 -495
  7. package/dist/{SiteChromeCustomizeContext-B6d5zkog.mjs → SiteChromeCustomizeContext-BHFgMjde.mjs} +35665 -33602
  8. package/dist/_dts/ai/src/contracts/proposals.d.ts +7226 -3057
  9. package/dist/_dts/ai/src/contracts/siteStyleProposalSchemas.d.ts +1223 -811
  10. package/dist/_dts/ai/src/designer/themePatch.d.ts +5 -10
  11. package/dist/_dts/api/src/accessAdmin.d.ts +16 -18
  12. package/dist/_dts/api/src/accountApi.d.ts +49 -2
  13. package/dist/_dts/api/src/appointmentServiceContracts.d.ts +83 -0
  14. package/dist/_dts/api/src/appointmentServiceLocation.d.ts +12 -0
  15. package/dist/_dts/api/src/appointmentServiceVisibility.d.ts +14 -0
  16. package/dist/_dts/api/src/appointmentSetup.d.ts +8 -0
  17. package/dist/_dts/api/src/bookingConfig.d.ts +35 -87
  18. package/dist/_dts/api/src/bookingManagementEndpoints.d.ts +1 -1
  19. package/dist/_dts/api/src/bookingOperations.d.ts +2 -69
  20. package/dist/_dts/api/src/bookingRecords.d.ts +2 -2
  21. package/dist/_dts/api/src/bookingRefundOutcomes.d.ts +2 -2
  22. package/dist/_dts/api/src/bookingServicePricing.d.ts +47 -0
  23. package/dist/_dts/api/src/bookings.d.ts +2 -2
  24. package/dist/_dts/api/src/cache-tags.d.ts +7 -2
  25. package/dist/_dts/api/src/common/index.d.ts +1 -0
  26. package/dist/_dts/api/src/common/zodFieldErrors.d.ts +34 -0
  27. package/dist/_dts/api/src/communications.d.ts +14 -8
  28. package/dist/_dts/api/src/contentRuntime.d.ts +3 -6
  29. package/dist/_dts/api/src/domainConnection.d.ts +41 -0
  30. package/dist/_dts/api/src/endpoints.d.ts +1636 -1637
  31. package/dist/_dts/api/src/eventAttendanceLabels.d.ts +2 -2
  32. package/dist/_dts/api/src/eventManagementContracts.d.ts +10 -14
  33. package/dist/_dts/api/src/index.d.ts +11 -9
  34. package/dist/_dts/api/src/indexCoreTypes.d.ts +1 -1
  35. package/dist/_dts/api/src/indexExtendedTypes.d.ts +1 -1
  36. package/dist/_dts/api/src/media.d.ts +19 -2
  37. package/dist/_dts/api/src/occurrenceScheduling.d.ts +1 -1
  38. package/dist/_dts/api/src/participantMessaging.d.ts +3 -3
  39. package/dist/_dts/api/src/public/endpoints.d.ts +1 -1
  40. package/dist/_dts/api/src/publicBookingAvailabilityEndpointDefinitions.d.ts +62 -0
  41. package/dist/_dts/api/src/publicBookingService.d.ts +109 -2
  42. package/dist/_dts/api/src/publicEventCheckoutEndpointDefinitions.d.ts +27 -0
  43. package/dist/_dts/api/src/publicEventEndpointDefinitions.d.ts +7 -0
  44. package/dist/_dts/api/src/publicExperience/commerceEndpoints.d.ts +2 -1
  45. package/dist/_dts/api/src/publicExperience/eventEndpoints.d.ts +30 -148
  46. package/dist/_dts/api/src/publicExperience/sharedTypes.d.ts +8 -26
  47. package/dist/_dts/api/src/publicExperience/siteEndpoints.d.ts +5 -13
  48. package/dist/_dts/api/src/publicExperienceEndpoints.d.ts +2 -3
  49. package/dist/_dts/api/src/schemas/admin-allowed-domains.d.ts +76 -0
  50. package/dist/_dts/api/src/schemas/admin-observability-logs.d.ts +61 -0
  51. package/dist/_dts/api/src/schemas/admin-users.d.ts +80 -0
  52. package/dist/_dts/api/src/schemas/newsletter-memberships.d.ts +98 -0
  53. package/dist/_dts/api/src/schemas/sdk-entries.d.ts +128 -0
  54. package/dist/_dts/api/src/schemas/site-api-keys.d.ts +112 -0
  55. package/dist/_dts/api/src/schemas/support.d.ts +35 -0
  56. package/dist/_dts/api/src/sdk-event-booking-offer.d.ts +14 -0
  57. package/dist/_dts/api/src/sdk-event-payment-terms.d.ts +4 -16
  58. package/dist/_dts/api/src/sdk-event-upsert.d.ts +11 -21
  59. package/dist/_dts/api/src/sdkContracts.d.ts +8 -23
  60. package/dist/_dts/api/src/sdkManagedEventContracts.d.ts +37 -0
  61. package/dist/_dts/api/src/sdkMediaContracts.d.ts +40 -0
  62. package/dist/_dts/api/src/sdkMediaEndpointDefinitions.d.ts +66 -0
  63. package/dist/_dts/api/src/sdkThemeWire.d.ts +157 -146
  64. package/dist/_dts/api/src/siteInfrastructure.d.ts +37 -37
  65. package/dist/_dts/api/src/siteManagementEndpoints.d.ts +35 -54
  66. package/dist/_dts/api/src/siteOnboarding.d.ts +3 -3
  67. package/dist/_dts/api/src/sitePlatformEndpoints.d.ts +38 -16
  68. package/dist/_dts/api/src/siteRuntimeEndpoints.d.ts +37 -17
  69. package/dist/_dts/api/src/types.d.ts +2 -2
  70. package/dist/_dts/api/src/{wholeSeriesOfferings.d.ts → wholeSeriesRuns.d.ts} +11 -67
  71. package/dist/_dts/block-form/src/FormRenderer.d.ts +3 -1
  72. package/dist/_dts/block-form/src/backend/store-backend.d.ts +2 -1
  73. package/dist/_dts/block-form/src/backend/types.d.ts +2 -0
  74. package/dist/_dts/block-form/src/context/FormFocusContext.d.ts +7 -0
  75. package/dist/_dts/block-form/src/index.d.ts +2 -0
  76. package/dist/_dts/block-form/src/manifest/ManifestForm.d.ts +2 -1
  77. package/dist/_dts/block-form/src/utils/promotedFieldBackend.d.ts +8 -0
  78. package/dist/_dts/block-form/src/utils/promotedFieldPlan.d.ts +18 -0
  79. package/dist/_dts/block-form/src/utils/resolvePromotedFields.d.ts +8 -1
  80. package/dist/_dts/blocks/src/BlockErrorBoundary.d.ts +46 -0
  81. package/dist/_dts/blocks/src/PageRenderer.d.ts +2 -141
  82. package/dist/_dts/blocks/src/contracts/index.d.ts +2 -0
  83. package/dist/_dts/blocks/src/contracts/proof-block-islands.d.ts +12 -6
  84. package/dist/_dts/blocks/src/index.d.ts +1 -0
  85. package/dist/_dts/blocks/src/pageRenderAudience.d.ts +54 -0
  86. package/dist/_dts/blocks/src/pageRenderDiagnostics.d.ts +26 -0
  87. package/dist/_dts/blocks/src/pageRenderPlan.d.ts +116 -0
  88. package/dist/_dts/blocks/src/pageRenderPreparation.d.ts +109 -0
  89. package/dist/_dts/blocks/src/pageRendererBoundaries.d.ts +1 -0
  90. package/dist/_dts/blocks/src/pageRendererContracts.d.ts +154 -0
  91. package/dist/_dts/blocks/src/portal/types.d.ts +1 -1
  92. package/dist/_dts/blocks/src/server.d.ts +1 -0
  93. package/dist/_dts/blocks/src/system/blocks/event-registration.d.ts +12 -20
  94. package/dist/_dts/blocks/src/system/blocks/events/event-calendar.d.ts +1 -1
  95. package/dist/_dts/blocks/src/system/blocks/events/eventCalendarDataLoaders.d.ts +3 -0
  96. package/dist/_dts/blocks/src/system/blocks/events/shared/calendarWindow.d.ts +95 -0
  97. package/dist/_dts/blocks/src/system/blocks/events/shared/index.d.ts +1 -0
  98. package/dist/_dts/blocks/src/system/blocks/events/shared/runGrouping.d.ts +37 -0
  99. package/dist/_dts/blocks/src/system/blocks/events/shared/schemas.d.ts +1046 -773
  100. package/dist/_dts/blocks/src/system/blocks/events/shared/types.d.ts +59 -46
  101. package/dist/_dts/blocks/src/system/blocks/location-map.d.ts +3 -3
  102. package/dist/_dts/blocks/src/system/blocks/media-text.d.ts +161 -161
  103. package/dist/_dts/blocks/src/system/blocks/offerings-preview/model.d.ts +13 -6
  104. package/dist/_dts/blocks/src/system/blocks/offerings-preview/scheduleSummary.d.ts +10 -4
  105. package/dist/_dts/blocks/src/system/constants/buttonRoles.d.ts +2 -2
  106. package/dist/_dts/blocks/src/system/constants/events.d.ts +4 -0
  107. package/dist/_dts/blocks/src/system/customize/facets.d.ts +4 -4
  108. package/dist/_dts/blocks/src/system/data/loaderEndpoints.d.ts +1 -1
  109. package/dist/_dts/blocks/src/system/data/publicLoaderCachePolicy.d.ts +5 -9
  110. package/dist/_dts/blocks/src/system/manifest/schema.d.ts +83 -83
  111. package/dist/_dts/blocks/src/system/runtime/api/client.d.ts +12 -3
  112. package/dist/_dts/blocks/src/system/runtime/api/endpoints.d.ts +8 -8
  113. package/dist/_dts/blocks/src/system/runtime/api/index.d.ts +1 -1
  114. package/dist/_dts/blocks/src/system/runtime/api/types/core.d.ts +13 -0
  115. package/dist/_dts/blocks/src/system/runtime/api/types/events.d.ts +12 -20
  116. package/dist/_dts/blocks/src/system/runtime/components/registry.client.d.ts +32 -37
  117. package/dist/_dts/blocks/src/system/runtime/components/runtime-backed-blocks.d.ts +15 -18
  118. package/dist/_dts/blocks/src/system/runtime/hooks/useCalendarEvents.d.ts +11 -8
  119. package/dist/_dts/blocks/src/system/runtime/hooks/useCalendarEvents.state.d.ts +38 -22
  120. package/dist/_dts/blocks/src/system/runtime/hooks/useHeadlessEventRegistrationFlow.d.ts +7 -0
  121. package/dist/_dts/blocks/src/system/runtime/nodes/event-details/WholeSeriesPricingDisplay.d.ts +2 -2
  122. package/dist/_dts/blocks/src/system/runtime/nodes/event-details/logic.d.ts +16 -4
  123. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/ErrorStep.d.ts +3 -5
  124. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/EventRegistrationWizardActions.d.ts +4 -2
  125. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/PaymentSelectionStep.d.ts +1 -1
  126. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/event-registration.headless.contract.d.ts +5 -3
  127. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/event-registration.headless.planning.d.ts +1 -1
  128. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/eventScope.d.ts +1 -1
  129. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/hooks.d.ts +2 -0
  130. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/registrationCompletion.d.ts +15 -0
  131. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/registrationDerivations.d.ts +2 -2
  132. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/registrationErrorPresentation.d.ts +5 -0
  133. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/registrationErrorRecovery.d.ts +25 -0
  134. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/registrationEventAdapter.d.ts +14 -3
  135. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/registrationSurface.d.ts +3 -21
  136. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/registrationWizardContext.d.ts +4 -0
  137. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/siteCreditPreview.d.ts +0 -3
  138. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/ticketSelectionLines.d.ts +14 -16
  139. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/types.d.ts +13 -15
  140. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/useEventRegistrationStripeReturn.d.ts +7 -3
  141. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/useEventRegistrationSubmit.d.ts +9 -3
  142. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/useEventRegistrationWizard.d.ts +25 -23
  143. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/useWaitlistClaimRestoration.d.ts +2 -0
  144. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/utils.d.ts +11 -4
  145. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration.interactive.d.ts +8 -13
  146. package/dist/_dts/blocks/src/system/runtime/nodes/events/EventCalendar.interactive.d.ts +5 -0
  147. package/dist/_dts/blocks/src/system/runtime/nodes/events/EventCombined.client.d.ts +1 -0
  148. package/dist/_dts/blocks/src/system/runtime/nodes/events/EventCombined.interactive.d.ts +11 -3
  149. package/dist/_dts/blocks/src/system/runtime/nodes/events/domain/capacity.d.ts +164 -0
  150. package/dist/_dts/blocks/src/system/runtime/nodes/events/domain/cta.d.ts +29 -0
  151. package/dist/_dts/blocks/src/system/runtime/nodes/events/domain/dates.d.ts +47 -0
  152. package/dist/_dts/blocks/src/system/runtime/nodes/events/domain/index.d.ts +14 -0
  153. package/dist/_dts/blocks/src/system/runtime/nodes/events/domain/people.d.ts +6 -0
  154. package/dist/_dts/blocks/src/system/runtime/nodes/events/domain/pricing.d.ts +24 -0
  155. package/dist/_dts/blocks/src/system/runtime/nodes/events/shared/OfferingTitle.d.ts +11 -0
  156. package/dist/_dts/blocks/src/system/runtime/nodes/events/shared/WeekAgendaView.d.ts +1 -1
  157. package/dist/_dts/blocks/src/system/runtime/nodes/events/shared/WeekBoardView.d.ts +1 -1
  158. package/dist/_dts/blocks/src/system/runtime/nodes/events/shared/eventCollectionContract.d.ts +1080 -560
  159. package/dist/_dts/blocks/src/system/runtime/nodes/events/shared/eventProductEligibility.d.ts +2 -2
  160. package/dist/_dts/blocks/src/system/runtime/nodes/events/shared/index.d.ts +3 -1
  161. package/dist/_dts/blocks/src/system/runtime/nodes/events/shared/listingLayout.d.ts +23 -0
  162. package/dist/_dts/blocks/src/system/runtime/nodes/offerings-preview.d.ts +2 -2
  163. package/dist/_dts/blocks/src/system/runtime/nodes/shared/contract-schemas.d.ts +7 -7
  164. package/dist/_dts/blocks/src/system/transforms/registry/eventCalendar.d.ts +11 -0
  165. package/dist/_dts/commerce/src/moneyReport.d.ts +1 -1
  166. package/dist/_dts/commerce/src/purchase/checkoutErrorCodes.d.ts +1 -1
  167. package/dist/_dts/commerce/src/purchase/event-registration-errors.d.ts +57 -0
  168. package/dist/_dts/commerce/src/purchase/eventOrderOptionSnapshot.d.ts +3 -0
  169. package/dist/_dts/commerce/src/purchase/index.d.ts +1 -0
  170. package/dist/_dts/commerce/src/purchase/orderItemMetadata.d.ts +6 -3
  171. package/dist/_dts/commerce/src/purchase/types.d.ts +3 -2
  172. package/dist/_dts/commerce/src/waitlists.d.ts +1 -1
  173. package/dist/_dts/content-editor/src/controller/EntryControllerProvider.d.ts +4 -0
  174. package/dist/_dts/content-editor/src/controller/useBlockedPublishPlan.d.ts +11 -0
  175. package/dist/_dts/content-editor/src/hooks/applyBlockedPublishPlan.d.ts +1 -1
  176. package/dist/_dts/content-editor/src/hooks/blockedPublishPlan.d.ts +13 -5
  177. package/dist/_dts/content-editor/src/hooks/useEntryActions.d.ts +5 -1
  178. package/dist/_dts/content-editor/src/hooks/validateBlocksForPublish.d.ts +34 -2
  179. package/dist/_dts/content-editor/src/index.d.ts +2 -1
  180. package/dist/_dts/content-editor/src/ui/blockValidationManifestRegistry.d.ts +2 -0
  181. package/dist/_dts/content-editor/src/ui/editorUiStore.d.ts +4 -2
  182. package/dist/_dts/content-editor/src/ui/stores/types.d.ts +27 -5
  183. package/dist/_dts/content-model/src/content-types/schema.d.ts +3 -3
  184. package/dist/_dts/content-model/src/templates/defaults.d.ts +11 -0
  185. package/dist/_dts/core/src/branded.d.ts +28 -9
  186. package/dist/_dts/core/src/event-booking-offer-parser.d.ts +82 -0
  187. package/dist/_dts/core/src/event-booking-offer.d.ts +89 -0
  188. package/dist/_dts/core/src/event-page-surface-subject.d.ts +81 -4
  189. package/dist/_dts/core/src/event-pricing.d.ts +81 -89
  190. package/dist/_dts/core/src/index.d.ts +3 -0
  191. package/dist/_dts/core/src/non-empty-array.d.ts +3 -0
  192. package/dist/_dts/core/src/whole-series-run-schedule.d.ts +92 -0
  193. package/dist/_dts/db/src/generated/supabase/database.types.d.ts +495 -279
  194. package/dist/_dts/db/src/index.d.ts +1 -0
  195. package/dist/_dts/db/src/utils/postgrestFilter.d.ts +2 -0
  196. package/dist/_dts/editor-blocks/src/BlockForm.d.ts +2 -6
  197. package/dist/_dts/editor-blocks/src/block-editor/BlockCard.d.ts +1 -1
  198. package/dist/_dts/editor-blocks/src/block-editor/BlockCardHeader.d.ts +2 -2
  199. package/dist/_dts/editor-blocks/src/block-editor/BlockSettingsModal.d.ts +4 -2
  200. package/dist/_dts/editor-blocks/src/block-editor/hooks/useBlockValidationManifests.d.ts +1 -0
  201. package/dist/_dts/editor-blocks/src/block-editor/types.d.ts +22 -7
  202. package/dist/_dts/editor-blocks/src/block-editor/useSettingsFieldFocus.d.ts +18 -0
  203. package/dist/_dts/media-core/src/bulkDeletePlanning.d.ts +4 -0
  204. package/dist/_dts/media-core/src/fileDomain.d.ts +3 -0
  205. package/dist/_dts/preview-next/src/client/blocks/previewBatchLoaderParams.d.ts +2 -0
  206. package/dist/_dts/preview-next/src/client/preview/PreviewShell.d.ts +1 -0
  207. package/dist/_dts/preview-next/src/client/preview/PreviewShellLayout.d.ts +1 -0
  208. package/dist/_dts/preview-next/src/client/preview/SiteChromeCustomizeContext.d.ts +3 -2
  209. package/dist/_dts/preview-next/src/client/preview/siteChromeBrandPreferences.d.ts +1 -1
  210. package/dist/_dts/preview-next/src/client/preview/siteChromeQuickControlHistoryState.d.ts +2 -0
  211. package/dist/_dts/preview-next/src/client/preview/siteChromeQuickControlSaveRequest.d.ts +51 -0
  212. package/dist/_dts/preview-next/src/client/preview/siteChromeSavePlan.d.ts +24 -2
  213. package/dist/_dts/preview-next/src/client/preview/siteStylePreviewPlanner.d.ts +23 -1
  214. package/dist/_dts/preview-next/src/client/preview/usePersistedHeaderPreferences.d.ts +8 -0
  215. package/dist/_dts/sdk/src/client/{eventSubrouteReadOperations.d.ts → publicEventReadOperations.d.ts} +4 -3
  216. package/dist/_dts/sdk/src/client/types.d.ts +6 -10
  217. package/dist/_dts/sdk/src/config/types.d.ts +1 -1
  218. package/dist/_dts/sdk/src/config/validation.d.ts +3 -3
  219. package/dist/_dts/sdk/src/contracts/system-block-content.d.ts +5 -0
  220. package/dist/_dts/sdk/src/data/prefetchBlockData.d.ts +2 -3
  221. package/dist/_dts/sdk/src/manifest/passes/pass1-fields.d.ts +8 -8
  222. package/dist/_dts/sdk/src/next/tags.d.ts +5 -8
  223. package/dist/_dts/sdk/src/public-api/contracts.d.ts +2 -26
  224. package/dist/_dts/sdk/src/rendering/helpers/footerPageOutline.d.ts +9 -1
  225. package/dist/_dts/sdk/src/rendering/index.d.ts +1 -0
  226. package/dist/_dts/sdk/src/rendering/islands/types.d.ts +6 -53
  227. package/dist/_dts/sdk/src/rendering/server.d.ts +1 -0
  228. package/dist/_dts/sdk/src/version.d.ts +1 -1
  229. package/dist/_dts/theme-core/src/accordions/types.d.ts +5 -5
  230. package/dist/_dts/theme-core/src/buttons/styles/recipes/flatInk.d.ts +25 -25
  231. package/dist/_dts/theme-core/src/buttons/styles/registry.d.ts +25 -25
  232. package/dist/_dts/theme-core/src/buttons/styles/selection.d.ts +7 -7
  233. package/dist/_dts/theme-core/src/buttons/types.d.ts +22 -22
  234. package/dist/_dts/theme-core/src/cards/types.d.ts +49 -49
  235. package/dist/_dts/theme-core/src/generated/containerResponsiveThemeCss.d.ts +1 -1
  236. package/dist/_dts/theme-core/src/gradients/types.d.ts +5 -0
  237. package/dist/_dts/theme-core/src/header/interactionPalette.d.ts +26 -3
  238. package/dist/_dts/theme-core/src/inputs/types.d.ts +2 -2
  239. package/dist/_dts/theme-core/src/interactive/baseSchema.d.ts +1 -1
  240. package/dist/_dts/theme-core/src/mock-themes/all.d.ts +195 -195
  241. package/dist/_dts/theme-core/src/navigation/types.d.ts +3 -3
  242. package/dist/_dts/theme-core/src/palette/colors.d.ts +1 -1
  243. package/dist/_dts/theme-core/src/palette/curated/types.d.ts +2 -2
  244. package/dist/_dts/theme-core/src/schema.d.ts +258 -257
  245. package/dist/_dts/theme-core/src/shared/componentStyles.d.ts +1 -1
  246. package/dist/_dts/theme-core/src/site-styles/backgroundAppearance.d.ts +2 -2
  247. package/dist/_dts/theme-core/src/site-styles/cardRoleVocabulary.d.ts +1 -1
  248. package/dist/_dts/theme-core/src/site-styles/compilationPlan.d.ts +43 -3
  249. package/dist/_dts/theme-core/src/site-styles/curatedSiteStyles.d.ts +10 -0
  250. package/dist/_dts/theme-core/src/site-styles/designState.d.ts +4 -4
  251. package/dist/_dts/theme-core/src/site-styles/generatedSchemas.d.ts +2 -2
  252. package/dist/_dts/theme-core/src/site-styles/headerBrandPresentation.d.ts +63 -0
  253. package/dist/_dts/theme-core/src/site-styles/headerBytesComparison.d.ts +30 -0
  254. package/dist/_dts/theme-core/src/site-styles/headerInteractionPalette.d.ts +50 -1
  255. package/dist/_dts/theme-core/src/site-styles/headerLayoutCatalog.d.ts +48 -9
  256. package/dist/_dts/theme-core/src/site-styles/headerPreferences.d.ts +162 -0
  257. package/dist/_dts/theme-core/src/site-styles/headerPreferencesPersistence.d.ts +73 -0
  258. package/dist/_dts/theme-core/src/site-styles/headerRecompile.d.ts +118 -0
  259. package/dist/_dts/theme-core/src/site-styles/headerStyleCompiler.d.ts +10 -1
  260. package/dist/_dts/theme-core/src/site-styles/headerStyleDefaults.d.ts +1 -1
  261. package/dist/_dts/theme-core/src/site-styles/headerStyleRecipe.d.ts +7 -0
  262. package/dist/_dts/theme-core/src/site-styles/index.d.ts +5 -0
  263. package/dist/_dts/theme-core/src/site-styles/previewCompiler.d.ts +1 -1
  264. package/dist/_dts/theme-core/src/site-styles/siteChromeBackgrounds.d.ts +20 -1
  265. package/dist/_dts/theme-core/src/site-styles/siteChromeNavPreferences.d.ts +43 -2
  266. package/dist/_dts/theme-core/src/site-styles/tokenRecipes.d.ts +1 -1
  267. package/dist/_dts/theme-core/src/tokens/themeColorTokens.d.ts +3 -3
  268. package/dist/_dts/ui/src/badge.d.ts +1 -1
  269. package/dist/_dts/ui/src/button.d.ts +2 -2
  270. package/dist/client/{BookingConsentAgreement-DCch21a6.mjs → BookingConsentAgreement-DQUik1Mx.mjs} +2 -2
  271. package/dist/client/{DiscountCodeDisclosure-DIj1T59K.mjs → DiscountCodeDisclosure-CU67Q4vH.mjs} +1 -1
  272. package/dist/client/{EventCombined.client-BWVNlXTx.mjs → EventCombined.client-a02TXUgg.mjs} +233 -348
  273. package/dist/client/{EventListing.client-DVFCDe3i.mjs → EventListing.client-99qx7jFl.mjs} +2 -2
  274. package/dist/client/{LeafletStaticMap-BY5mIHw9.mjs → LeafletStaticMap-CWzcq73L.mjs} +1 -1
  275. package/dist/client/{accordion-CK29E8rG.mjs → accordion-D1nb3-Oj.mjs} +1 -1
  276. package/dist/client/{bookingForm-DFvnet-N.mjs → bookingForm-BGrmLhaO.mjs} +5 -5
  277. package/dist/client/bookings.mjs +10555 -10475
  278. package/dist/client/{carousel-BVU5NQlb.mjs → carousel-DSmOx3ad.mjs} +1 -1
  279. package/dist/client/{cart-BNItcZVa.mjs → cart-COWeOIUk.mjs} +3 -3
  280. package/dist/client/{checkout-Bu6iCdkP.mjs → checkout-CeysaIJK.mjs} +3 -3
  281. package/dist/client/client.mjs +58968 -57441
  282. package/dist/client/{context-BImarOAq.mjs → context-DBzX85Xc.mjs} +46 -18
  283. package/dist/client/dates-DCYhZBlW.mjs +264 -0
  284. package/dist/client/{eventCalendar-DS2h-UXD.mjs → eventCalendar-BgsN73Ul.mjs} +21 -44
  285. package/dist/client/{eventCombined-Drt4Oq9p.mjs → eventCombined-DcZQAMQP.mjs} +3 -3
  286. package/dist/client/{eventDetails-Bl2vWgTX.mjs → eventDetails-BWwGJ54l.mjs} +131 -51
  287. package/dist/client/{eventListing-CS7PHY7v.mjs → eventListing-DqYqV8vp.mjs} +3 -3
  288. package/dist/client/{eventRegistration-D-j8eNy2.mjs → eventRegistration-DQGDgu_e.mjs} +1122 -803
  289. package/dist/client/{form-RMpoLTyr.mjs → form-ClfpasVT.mjs} +2 -2
  290. package/dist/client/{headerSection-BYE3Lb3k.mjs → headerSection-BuOMe98N.mjs} +2 -2
  291. package/dist/client/hooks.mjs +9630 -9526
  292. package/dist/client/{locationMap-BxSr7Kkh.mjs → locationMap-CEqu_sfa.mjs} +2 -2
  293. package/dist/client/{newsletterForm-DerbXwFC.mjs → newsletterForm-IbrHjMxy.mjs} +2 -2
  294. package/dist/client/pricing-xj7MPZlK.mjs +314 -0
  295. package/dist/client/{productDetail-4SZCLhAf.mjs → productDetail-vxqTt5Xj.mjs} +3 -3
  296. package/dist/client/{productList-D7AP4GNq.mjs → productList-OTdZu8sb.mjs} +3 -3
  297. package/dist/client/{propsDecoder-pzBeCh14.mjs → propsDecoder-CY9gGEF7.mjs} +786 -1194
  298. package/dist/client/rendering/client.mjs +11481 -10469
  299. package/dist/client/rendering/islands.mjs +17 -17
  300. package/dist/client/rendering.mjs +26871 -25232
  301. package/dist/client/{scrollToBlock-CduAKf8T.mjs → scrollToBlock--BFV6kl-.mjs} +1 -1
  302. package/dist/client/{shop-Cp5p_kGe.mjs → shop-CP4YbDL9.mjs} +6 -6
  303. package/dist/client/{shop-commerce-DicXYaYX.mjs → shop-commerce-APs4XBXE.mjs} +5 -5
  304. package/dist/client/{shop-commerce-store-CGWnpY4K.mjs → shop-commerce-store-CAVLxLFr.mjs} +1 -1
  305. package/dist/client/{shopShared-BDf9ljkm.mjs → shopShared-BnSRrwXI.mjs} +3 -3
  306. package/dist/client/{useEventFilters-DeeyGYuk.mjs → useEventFilters-1a1by4c8.mjs} +33 -18
  307. package/dist/preview-next/before-render.mjs +186 -148
  308. package/dist/preview-next/client/runtime.mjs +4 -4
  309. package/dist/preview-next/keepAlive.mjs +186 -148
  310. package/dist/preview-next/middleware.mjs +186 -148
  311. package/dist/{previewAuthStore-DbqBol0V.mjs → previewAuthStore-BkEgnYPP.mjs} +186 -148
  312. package/dist/{sdk-runtime-19MO27F0.mjs → sdk-runtime-DwGfsT0S.mjs} +2 -2
  313. package/dist/{separator-DaPLtJ1f.mjs → separator-BeqDLneQ.mjs} +1 -1
  314. package/dist/server/components.mjs +53243 -51879
  315. package/dist/server/config-validation.mjs +1015 -906
  316. package/dist/server/config.mjs +1019 -910
  317. package/dist/server/data.mjs +10304 -10200
  318. package/dist/server/index.mjs +217 -198
  319. package/dist/server/next/tags.mjs +18 -4
  320. package/dist/server/next.mjs +62787 -61617
  321. package/dist/server/page-converter.mjs +797 -917
  322. package/dist/server/prebuild.mjs +1 -1
  323. package/dist/server/rendering/server.mjs +52557 -51365
  324. package/dist/server/rendering.mjs +54373 -53054
  325. package/dist/server/routing.mjs +13274 -13142
  326. package/dist/server/server.mjs +1076 -1154
  327. package/dist/server/theme-bridge.mjs +69 -9
  328. package/dist/server/theme.mjs +27 -8
  329. package/dist/styles/index.css +32 -1
  330. package/package.json +1 -1
  331. package/dist/_dts/api/src/publicExperience/wholeSeriesEndpoints.d.ts +0 -65
  332. package/dist/_dts/api/src/publicWholeSeriesEndpointDefinitions.d.ts +0 -20
  333. package/dist/_dts/api/src/sdk-event-pricing.d.ts +0 -61
  334. package/dist/_dts/blocks/src/system/blocks/events/whole-series/index.d.ts +0 -3
  335. package/dist/_dts/blocks/src/system/blocks/events/whole-series/pricing.d.ts +0 -50
  336. package/dist/_dts/blocks/src/system/blocks/events/whole-series/schemas.d.ts +0 -135
  337. package/dist/_dts/blocks/src/system/blocks/events/whole-series/types.d.ts +0 -87
  338. package/dist/_dts/blocks/src/system/runtime/nodes/events/shared/eventCapacity.d.ts +0 -48
  339. package/dist/_dts/blocks/src/system/runtime/nodes/events/shared/pricing.d.ts +0 -17
  340. package/dist/_dts/blocks/src/system/runtime/nodes/events/shared/utils.d.ts +0 -100
  341. package/dist/_dts/sdk/src/client/wholeSeriesReadOperations.d.ts +0 -11
  342. package/dist/_dts/sdk/src/public-api/wholeSeriesContracts.d.ts +0 -2
  343. /package/dist/_dts/blocks/src/system/runtime/nodes/events/{shared → domain}/destination.d.ts +0 -0
@@ -53,53 +53,80 @@ function formatCurrencyAmount(amountCents, currency, options) {
53
53
  }
54
54
  }
55
55
  //#endregion
56
+ //#region ../core/src/planner.ts
57
+ /** Default clock for production callers that do not inject time explicitly. */
58
+ const systemPlanningClock = { now: () => Date.now() };
59
+ /** Resolve current time as a Date from an explicit Date, injected clock, or system clock. */
60
+ function resolvePlanningNowDate(input = {}) {
61
+ if (input.now !== void 0) return input.now;
62
+ return new Date((input.clock ?? systemPlanningClock).now());
63
+ }
64
+ //#endregion
56
65
  //#region ../core/src/event-pricing.ts
66
+ /** Capacity closes booking, but does not make an otherwise eligible price unknown. */
67
+ function isPublicEventPricingOptionDisplayable(option) {
68
+ if (!option.isActive) return false;
69
+ if (option.availability.kind === "available") return true;
70
+ const reason = option.availability.reason;
71
+ switch (reason) {
72
+ case "sold_out":
73
+ case "overallCapacityUnavailable": return true;
74
+ case "inactive":
75
+ case "not_on_sale_yet":
76
+ case "sale_ended":
77
+ case "attendance_mode_unavailable": return false;
78
+ default: return assertNever$2(reason);
79
+ }
80
+ }
57
81
  function publicEventPricingDisplay(pricing) {
58
- return pricing.display;
82
+ switch (pricing.kind) {
83
+ case "free":
84
+ case "single":
85
+ case "sliding_scale": return canonicalEventPricingDisplay(pricing);
86
+ case "tiered": return resolvePricingOptionsPriceDisplay({
87
+ options: pricing.options.filter(isPublicEventPricingOptionDisplayable),
88
+ currency: pricing.currency,
89
+ purpose: "configured"
90
+ });
91
+ case "external": return {
92
+ kind: "external",
93
+ label: pricing.label
94
+ };
95
+ }
59
96
  }
60
97
  function publicEventPricingType(pricing) {
61
98
  switch (pricing.kind) {
62
- case "drop_in":
63
- case "free_registration": return "free";
64
- case "single_paid":
65
- case "ticket_types":
66
- case "sliding_scale":
67
- case "whole_series": return isFreePublicEventPriceDisplay(pricing.display) ? "free" : "paid";
99
+ case "free": return "free";
100
+ case "single":
101
+ case "tiered":
102
+ case "sliding_scale": return isFreePublicEventPriceDisplay(publicEventPricingDisplay(pricing)) ? "free" : "paid";
68
103
  case "external": return "external";
69
104
  }
70
105
  }
71
106
  function publicEventPricingCurrency(pricing) {
72
107
  switch (pricing.kind) {
73
- case "drop_in":
74
- case "free_registration":
75
- case "single_paid":
76
- case "ticket_types":
108
+ case "free":
109
+ case "single":
110
+ case "tiered":
77
111
  case "sliding_scale": return pricing.currency;
78
- case "whole_series": return pricing.display.display.currency;
79
112
  case "external": return null;
80
113
  }
81
114
  }
82
- function publicEventScalarPrice(pricing) {
83
- return pricing.kind === "single_paid" ? pricing.amount : null;
84
- }
85
- function publicEventPricingMode(pricing) {
115
+ function publicEventPricingStrategy(pricing) {
86
116
  switch (pricing.kind) {
87
- case "single_paid":
88
- case "free_registration": return "single";
89
- case "ticket_types": return "ticket_types";
117
+ case "single": return "single";
118
+ case "free": return "free";
119
+ case "tiered": return "tiered";
90
120
  case "sliding_scale": return "sliding_scale";
91
- case "drop_in":
92
- case "whole_series":
93
121
  case "external": return;
94
122
  }
95
123
  }
96
- function publicEventTicketTypes(pricing) {
97
- return pricing.kind === "ticket_types" ? pricing.ticketTypes : [];
124
+ function publicEventPricingOptions(pricing) {
125
+ return pricing.kind === "tiered" ? pricing.options : [];
98
126
  }
99
127
  function isFreePublicEventPriceDisplay(display) {
100
128
  switch (display.kind) {
101
129
  case "free": return true;
102
- case "whole_series": return display.display.kind === "free";
103
130
  case "exact":
104
131
  case "from":
105
132
  case "range":
@@ -108,15 +135,77 @@ function isFreePublicEventPriceDisplay(display) {
108
135
  case "external": return false;
109
136
  }
110
137
  }
111
- function formatCoursePriceDisplayLabel(display, options) {
112
- switch (display.kind) {
113
- case "free":
114
- case "unavailable": return "Course";
115
- case "exact": return `Course (${formatCurrencyAmount(display.amount, display.currency, options)})`;
116
- case "from": return `Course (From ${formatCurrencyAmount(display.amount, display.currency, options)})`;
117
- case "range": return `Course (${formatCurrencyAmount(display.minAmount, display.currency, options)} - ${formatCurrencyAmount(display.maxAmount, display.currency, options)})`;
118
- case "mixed": return display.paidToAmount > display.paidFromAmount ? `Course (Free / ${formatCurrencyAmount(display.paidFromAmount, display.currency, options)} - ${formatCurrencyAmount(display.paidToAmount, display.currency, options)})` : `Course (Free / ${formatCurrencyAmount(display.paidFromAmount, display.currency, options)})`;
119
- default: return assertNever$2(display);
138
+ function resolvePricingOptionSaleWindowState(option, now) {
139
+ if (option.saleStartsAt !== null && option.saleStartsAt !== void 0 && new Date(option.saleStartsAt) > now) return "not_started";
140
+ if (option.saleEndsAt !== null && option.saleEndsAt !== void 0 && new Date(option.saleEndsAt) < now) return "ended";
141
+ return "open";
142
+ }
143
+ function resolvePricingOptionsPriceDisplay(input) {
144
+ const now = input.purpose === "available_now" ? resolvePlanningNowDate({
145
+ now: input.now,
146
+ clock: input.clock
147
+ }) : null;
148
+ const amounts = input.options.filter((option) => option.isActive && (now === null || resolvePricingOptionSaleWindowState(option, now) === "open")).map((option) => option.priceAmount);
149
+ return amounts.length === 0 ? {
150
+ kind: "unavailable",
151
+ currency: input.currency
152
+ } : amountRangeDisplay(amounts, input.currency);
153
+ }
154
+ function amountRangeDisplay(amounts, currency) {
155
+ const minAmount = Math.min(...amounts);
156
+ const maxAmount = Math.max(...amounts);
157
+ if (minAmount === maxAmount) return minAmount === 0 ? {
158
+ kind: "free",
159
+ currency
160
+ } : {
161
+ kind: "exact",
162
+ amount: minAmount,
163
+ currency
164
+ };
165
+ if (minAmount === 0) {
166
+ const paidAmounts = amounts.filter((amount) => amount > 0);
167
+ return {
168
+ kind: "mixed",
169
+ paidFromAmount: Math.min(...paidAmounts),
170
+ paidToAmount: maxAmount,
171
+ currency
172
+ };
173
+ }
174
+ return {
175
+ kind: "range",
176
+ minAmount,
177
+ maxAmount,
178
+ currency
179
+ };
180
+ }
181
+ /** Derives configured price presentation directly from canonical pricing. */
182
+ function canonicalEventPricingDisplay(pricing) {
183
+ switch (pricing.kind) {
184
+ case "free": return {
185
+ kind: "free",
186
+ currency: pricing.currency
187
+ };
188
+ case "single": return {
189
+ kind: "exact",
190
+ amount: pricing.amount,
191
+ currency: pricing.currency
192
+ };
193
+ case "sliding_scale": return pricing.minimum === pricing.maximum ? {
194
+ kind: "exact",
195
+ amount: pricing.maximum,
196
+ currency: pricing.currency
197
+ } : {
198
+ kind: "range",
199
+ minAmount: pricing.minimum,
200
+ maxAmount: pricing.maximum,
201
+ currency: pricing.currency
202
+ };
203
+ case "tiered": return resolvePricingOptionsPriceDisplay({
204
+ options: pricing.options,
205
+ currency: pricing.currency,
206
+ purpose: "configured"
207
+ });
208
+ default: return assertNever$2(pricing);
120
209
  }
121
210
  }
122
211
  /**
@@ -135,7 +224,6 @@ function formatBookingPriceDisplay(display, options = {}) {
135
224
  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)}`;
136
225
  case "unavailable": return "Pricing unavailable";
137
226
  case "external": return display.label;
138
- case "whole_series": return formatCoursePriceDisplayLabel(display.display, options);
139
227
  default: return assertNever$2(display);
140
228
  }
141
229
  }
@@ -145,20 +233,51 @@ function formatEventPricing(pricing, options = {}) {
145
233
  }
146
234
  //#endregion
147
235
  //#region ../core/src/event-page-surface-subject.ts
148
- function createEventSeriesPageSurfaceSubject(eventSeriesId, bookingScope) {
236
+ function createEventSeriesPageSurfaceSubject(eventSeriesId, bookingScope, eventDestinationMode) {
237
+ const mode = eventDestinationMode ? { eventDestinationMode } : {};
149
238
  return bookingScope === "whole_series" ? {
150
239
  kind: "eventSeries",
151
240
  eventSeriesId,
152
241
  bookingScope,
153
- isWholeSeriesBooking: true
242
+ isWholeSeriesBooking: true,
243
+ ...mode
154
244
  } : {
155
245
  kind: "eventSeries",
156
246
  eventSeriesId,
157
- bookingScope
247
+ bookingScope,
248
+ ...mode
249
+ };
250
+ }
251
+ function createEventOfferingPageSurfaceSubject(eventOfferingId, input) {
252
+ const mode = input.eventDestinationMode ? { eventDestinationMode: input.eventDestinationMode } : {};
253
+ const identity = {
254
+ kind: "eventOffering",
255
+ eventOfferingId,
256
+ catalogueEventSeriesIds: input.catalogueEventSeriesIds
257
+ };
258
+ return input.bookingScope === "whole_series" ? {
259
+ ...identity,
260
+ bookingScope: input.bookingScope,
261
+ isWholeSeriesBooking: true,
262
+ ...mode
263
+ } : {
264
+ ...identity,
265
+ bookingScope: input.bookingScope,
266
+ ...mode
158
267
  };
159
268
  }
269
+ /**
270
+ * Read the destination mode off a wire value.
271
+ *
272
+ * Absent or unrecognised reads as absent, which leaves the routed Event record
273
+ * as the only authority — the behaviour every caller had before the field
274
+ * existed.
275
+ */
276
+ function parseEventPageSurfaceDestinationMode(value) {
277
+ return value === "cms-managed" || value === "system-managed" ? value : void 0;
278
+ }
160
279
  function parseEventPageSurfaceSubject(value) {
161
- if (!isRecord$6(value)) return err$1({ kind: "notObject" });
280
+ if (!isRecord$5(value)) return err$1({ kind: "notObject" });
162
281
  switch (value.kind) {
163
282
  case "eventSeries": {
164
283
  const eventSeriesId = parseEventSeriesId(value.eventSeriesId);
@@ -170,7 +289,7 @@ function parseEventPageSurfaceSubject(value) {
170
289
  kind: "invalidEventSeriesBookingScope",
171
290
  bookingScope: value.bookingScope
172
291
  });
173
- return ok$1(createEventSeriesPageSurfaceSubject(eventSeriesId.value, value.bookingScope));
292
+ return ok$1(createEventSeriesPageSurfaceSubject(eventSeriesId.value, value.bookingScope, parseEventPageSurfaceDestinationMode(value.eventDestinationMode)));
174
293
  }
175
294
  case "eventOffering": {
176
295
  const eventOfferingId = parseEventOfferingId(value.eventOfferingId);
@@ -178,24 +297,22 @@ function parseEventPageSurfaceSubject(value) {
178
297
  kind: "invalidEventOfferingId",
179
298
  error: eventOfferingId.error
180
299
  });
181
- if (!Array.isArray(value.availableEventSeriesIds)) return err$1({
182
- kind: "unsupportedSubject",
183
- subjectKind: value.kind
300
+ if (!Array.isArray(value.catalogueEventSeriesIds)) return err$1({
301
+ kind: "invalidEventOfferingCatalogue",
302
+ catalogue: value.catalogueEventSeriesIds
184
303
  });
185
- const parsedSeriesIds = [];
186
- for (const valueId of value.availableEventSeriesIds) {
187
- const eventSeriesId = parseEventSeriesId(valueId);
188
- if (!eventSeriesId.ok) return err$1({
189
- kind: "invalidEventSeriesId",
190
- error: eventSeriesId.error
191
- });
192
- parsedSeriesIds.push(eventSeriesId.value);
193
- }
194
- return ok$1({
195
- kind: "eventOffering",
196
- eventOfferingId: eventOfferingId.value,
197
- availableEventSeriesIds: parsedSeriesIds
304
+ if (value.bookingScope !== "single_occurrence" && value.bookingScope !== "whole_series") return err$1({
305
+ kind: "invalidEventOfferingBookingScope",
306
+ bookingScope: value.bookingScope
198
307
  });
308
+ const parsedCatalogueIds = parseEventSeriesIdList(value.catalogueEventSeriesIds);
309
+ if (!parsedCatalogueIds.ok) return parsedCatalogueIds;
310
+ const eventDestinationMode = parseEventPageSurfaceDestinationMode(value.eventDestinationMode);
311
+ return ok$1(createEventOfferingPageSurfaceSubject(eventOfferingId.value, {
312
+ catalogueEventSeriesIds: parsedCatalogueIds.value,
313
+ bookingScope: value.bookingScope,
314
+ ...eventDestinationMode ? { eventDestinationMode } : {}
315
+ }));
199
316
  }
200
317
  default: return err$1({
201
318
  kind: "unsupportedSubject",
@@ -203,7 +320,19 @@ function parseEventPageSurfaceSubject(value) {
203
320
  });
204
321
  }
205
322
  }
206
- function isRecord$6(value) {
323
+ function parseEventSeriesIdList(values) {
324
+ const parsed = [];
325
+ for (const value of values) {
326
+ const eventSeriesId = parseEventSeriesId(value);
327
+ if (!eventSeriesId.ok) return err$1({
328
+ kind: "invalidEventSeriesId",
329
+ error: eventSeriesId.error
330
+ });
331
+ parsed.push(eventSeriesId.value);
332
+ }
333
+ return ok$1(parsed);
334
+ }
335
+ function isRecord$5(value) {
207
336
  return typeof value === "object" && value !== null && !Array.isArray(value);
208
337
  }
209
338
  //#endregion
@@ -302,7 +431,7 @@ function getZonedDateTimeParts(date, timeZone) {
302
431
  second: read("second")
303
432
  };
304
433
  }
305
- function formatDateKey(date) {
434
+ function formatDateKey$1(date) {
306
435
  return `${String(date.year).padStart(4, "0")}-${String(date.month).padStart(2, "0")}-${String(date.day).padStart(2, "0")}`;
307
436
  }
308
437
  function toBookingTimeZone(timeZone) {
@@ -450,7 +579,7 @@ function formatBookingDateParts(input, options) {
450
579
  };
451
580
  }
452
581
  function getBookingLocalDateKey(input) {
453
- return formatDateKey(getLocalScheduleDate(new Date(input.startsAt), input.timeZone));
582
+ return formatDateKey$1(getLocalScheduleDate(new Date(input.startsAt), input.timeZone));
454
583
  }
455
584
  function compareLocalScheduleDates(left, right) {
456
585
  return Date.UTC(left.year, left.month - 1, left.day) - Date.UTC(right.year, right.month - 1, right.day);
@@ -525,7 +654,7 @@ function getLocalScheduleDate(date, timeZone) {
525
654
  };
526
655
  }
527
656
  function formatLocalScheduleDate(date) {
528
- return formatDateKey(date);
657
+ return formatDateKey$1(date);
529
658
  }
530
659
  function addLocalScheduleDays(date, days) {
531
660
  const utcDate = new Date(Date.UTC(date.year, date.month - 1, date.day + days, 12, 0, 0));
@@ -557,23 +686,78 @@ function parseNewsletterSignupDestinationProvider(value) {
557
686
  return value === "riverbank" || value === "mailchimp" ? value : null;
558
687
  }
559
688
  //#endregion
560
- //#region ../core/src/planner.ts
561
- /** Default clock for production callers that do not inject time explicitly. */
562
- const systemPlanningClock = { now: () => Date.now() };
563
- /** Resolve current time as a Date from an explicit Date, injected clock, or system clock. */
564
- function resolvePlanningNowDate(input = {}) {
565
- if (input.now !== void 0) return input.now;
566
- return new Date((input.clock ?? systemPlanningClock).now());
689
+ //#region ../core/src/whole-series-run-schedule.ts
690
+ /**
691
+ * A session that is not happening.
692
+ *
693
+ * Exported because every surface that renders a run's schedule has to mark the
694
+ * same weeks, and a second copy of the string is a second chance to disagree.
695
+ */
696
+ const CANCELLED_SESSION_STATUS = "cancelled";
697
+ function sessionStartMs(session) {
698
+ return Date.parse(session.startsAt);
699
+ }
700
+ /**
701
+ * The run's sessions that still count, in schedule order.
702
+ *
703
+ * Sessions whose start cannot be read are dropped rather than sorted to an
704
+ * arbitrary end: an unparseable date is not a date a visitor can attend.
705
+ */
706
+ function orderedLiveRunSessions(sessions) {
707
+ return sessions.filter((session) => session.status !== "cancelled" && Number.isFinite(sessionStartMs(session))).sort((left, right) => sessionStartMs(left) - sessionStartMs(right));
708
+ }
709
+ /** When the first session a visitor could attend begins. */
710
+ function firstLiveRunSessionStartMs(sessions) {
711
+ const first = orderedLiveRunSessions(sessions)[0];
712
+ return first === void 0 ? null : sessionStartMs(first);
713
+ }
714
+ /**
715
+ * Booking stays open until the run's first surviving session has started.
716
+ *
717
+ * There has to be something left to sell: a run with no schedule at all, or one
718
+ * whose every session has been cancelled, is closed rather than open. Adding a
719
+ * future session opens it again, subject to the usual status, visibility and
720
+ * admission rules.
721
+ */
722
+ function isWholeSeriesRegistrationOpen(sessions, now) {
723
+ const firstStart = firstLiveRunSessionStartMs(sessions);
724
+ return firstStart !== null && firstStart > now.getTime();
567
725
  }
568
726
  /**
569
- * Resolve a Date-compatible planner input.
727
+ * Whether a prepared run is still inviting bookings at render time.
570
728
  *
571
- * Precedence: bare Date, `{ now }`, `{ clock }`, then `systemPlanningClock`.
572
- * Explicit Date inputs are returned by reference, not cloned.
729
+ * `lifecycle` and `enrollmentOpen` come from the projection; the first session
730
+ * start is the one fact re-read against `now`, because that is the boundary a
731
+ * cached page can cross while it sits in front of a visitor.
573
732
  */
574
- function resolvePlanningDateInput(input = {}) {
575
- if (input instanceof Date) return input;
576
- return resolvePlanningNowDate(input);
733
+ function resolveWholeSeriesBookingPhase(run, now) {
734
+ switch (run.lifecycle) {
735
+ case "cancelled": return {
736
+ kind: "closed",
737
+ reason: "cancelled"
738
+ };
739
+ case "in_progress": return {
740
+ kind: "closed",
741
+ reason: "in-progress"
742
+ };
743
+ case "completed": return {
744
+ kind: "closed",
745
+ reason: "completed"
746
+ };
747
+ case "upcoming":
748
+ if (firstLiveRunSessionStartMs(run.sessions) === null) return {
749
+ kind: "closed",
750
+ reason: "no-sessions"
751
+ };
752
+ if (!isWholeSeriesRegistrationOpen(run.sessions, now)) return {
753
+ kind: "closed",
754
+ reason: "in-progress"
755
+ };
756
+ return run.enrollmentOpen ? { kind: "open" } : {
757
+ kind: "closed",
758
+ reason: "enrolment-closed"
759
+ };
760
+ }
577
761
  }
578
762
  //#endregion
579
763
  //#region ../core/src/event-series-behaviour.ts
@@ -760,7 +944,7 @@ function assertNever$1(value, message) {
760
944
  /**
761
945
  * Strict record guard: excludes null, primitives, and arrays.
762
946
  */
763
- function isRecord$5(value) {
947
+ function isRecord$4(value) {
764
948
  return typeof value === "object" && value !== null && !Array.isArray(value);
765
949
  }
766
950
  //#endregion
@@ -1015,12 +1199,12 @@ function resolveBookingSiteId(siteId, form) {
1015
1199
  return siteId || form?.siteId || "";
1016
1200
  }
1017
1201
  function normalizeBookingFormConfig(form) {
1018
- if (!isRecord$4(form)) return null;
1202
+ if (!isRecord$3(form)) return null;
1019
1203
  const id = readNonEmptyString(form.id);
1020
1204
  const name = readNonEmptyString(form.name);
1021
1205
  if (!id || !name) return null;
1022
- const schemaSource = isRecord$4(form.schema) ? form.schema : isRecord$4(form.schemaJson) ? form.schemaJson : null;
1023
- const settingsSource = isRecord$4(form.settings) ? form.settings : isRecord$4(form.settingsJson) ? form.settingsJson : null;
1206
+ const schemaSource = isRecord$3(form.schema) ? form.schema : isRecord$3(form.schemaJson) ? form.schemaJson : null;
1207
+ const settingsSource = isRecord$3(form.settings) ? form.settings : isRecord$3(form.settingsJson) ? form.settingsJson : null;
1024
1208
  return {
1025
1209
  id,
1026
1210
  name,
@@ -1040,8 +1224,8 @@ function normalizeBookingServices(services) {
1040
1224
  };
1041
1225
  if (!services) return [];
1042
1226
  if (Array.isArray(services)) return services.filter(isBookingFormService).map(normalizeService);
1043
- if (isRecord$4(services) && "services" in services) return Array.isArray(services.services) ? services.services.filter(isBookingFormService).map(normalizeService) : [];
1044
- return isRecord$4(services) ? Object.values(services).filter(isBookingFormService).map(normalizeService) : [];
1227
+ if (isRecord$3(services) && "services" in services) return Array.isArray(services.services) ? services.services.filter(isBookingFormService).map(normalizeService) : [];
1228
+ return isRecord$3(services) ? Object.values(services).filter(isBookingFormService).map(normalizeService) : [];
1045
1229
  }
1046
1230
  function normalizeBookingSchema(value) {
1047
1231
  if (!value) return void 0;
@@ -1052,7 +1236,7 @@ function normalizeBookingSchema(value) {
1052
1236
  };
1053
1237
  }
1054
1238
  function readBookingFormField(value) {
1055
- if (!isRecord$4(value)) return null;
1239
+ if (!isRecord$3(value)) return null;
1056
1240
  const id = readNonEmptyString(value.id);
1057
1241
  const label = readNonEmptyString(value.label);
1058
1242
  const type = readBookingFieldType(value.type);
@@ -1095,7 +1279,7 @@ function readBookingFieldOptions(value) {
1095
1279
  return options.length > 0 ? options : void 0;
1096
1280
  }
1097
1281
  function readBookingFieldOption(value) {
1098
- if (!isRecord$4(value)) return null;
1282
+ if (!isRecord$3(value)) return null;
1099
1283
  const optionValue = readNonEmptyString(value.value);
1100
1284
  const label = readNonEmptyString(value.label);
1101
1285
  return optionValue && label ? {
@@ -1115,7 +1299,7 @@ function normalizeBookingSettings(value) {
1115
1299
  };
1116
1300
  }
1117
1301
  function isBookingFormService(value) {
1118
- return isRecord$4(value) && typeof value.id === "string" && typeof value.siteId === "string" && typeof value.title === "string" && (value.consent === void 0 || blockBookingConsentSchema.safeParse(value.consent).success);
1302
+ return isRecord$3(value) && typeof value.id === "string" && typeof value.siteId === "string" && typeof value.title === "string" && (value.consent === void 0 || blockBookingConsentSchema.safeParse(value.consent).success);
1119
1303
  }
1120
1304
  function collectBookingCustomFields(data) {
1121
1305
  const customFields = {};
@@ -1165,7 +1349,7 @@ function readStringArray(value) {
1165
1349
  const strings = value.filter((item) => typeof item === "string");
1166
1350
  return strings.length > 0 ? strings : void 0;
1167
1351
  }
1168
- function isRecord$4(value) {
1352
+ function isRecord$3(value) {
1169
1353
  return typeof value === "object" && value !== null;
1170
1354
  }
1171
1355
  function getBookingServicePaymentSelectionMode(service) {
@@ -4551,6 +4735,11 @@ const gradientConfigSchema = z.object({
4551
4735
  position: z.string().optional(),
4552
4736
  stops: z.array(gradientStopSchema).min(2)
4553
4737
  });
4738
+ const gradientsSchema = z.object({
4739
+ button: z.string().min(1).nullable().optional(),
4740
+ hero: z.string().min(1).nullable().optional(),
4741
+ background: z.string().min(1).nullable().optional()
4742
+ });
4554
4743
  //#endregion
4555
4744
  //#region ../theme-core/src/interactive/baseSchema.ts
4556
4745
  /**
@@ -5208,11 +5397,30 @@ const headerContainerSchema = z.object({
5208
5397
  tint: z.string().nullable().optional(),
5209
5398
  opacity: z.number().min(0).max(1).default(.12)
5210
5399
  }).optional();
5211
- const headerInteractionPaletteSchema = z.enum([
5400
+ //#endregion
5401
+ //#region ../theme-core/src/header/interactionPalette.ts
5402
+ /**
5403
+ * The palettes that fill the nav link with a colour on hover and focus. Each
5404
+ * one names a background/foreground token pair.
5405
+ */
5406
+ const headerInteractionPillPaletteValues = [
5212
5407
  "primary",
5213
5408
  "secondary",
5214
5409
  "accent"
5215
- ]);
5410
+ ];
5411
+ /**
5412
+ * "No pill": the nav link keeps whatever hover treatment its recipe compiled,
5413
+ * and the palette step writes nothing at all (#1318 Phase 4c decision 2).
5414
+ *
5415
+ * It exists because "a colour change on hover, no filled background" was not
5416
+ * expressible in the vocabulary, and it is what
5417
+ * `anna-raven-therapy-and-yoga` and `kirtan-lucy` have rendered since before
5418
+ * the palette existed. Without it those two rows can only be classified as a
5419
+ * conscious visual change.
5420
+ */
5421
+ const headerInteractionQuietPalette = "quiet";
5422
+ const headerInteractionPaletteValues = [...headerInteractionPillPaletteValues, headerInteractionQuietPalette];
5423
+ const headerInteractionPaletteSchema = z.enum(headerInteractionPaletteValues);
5216
5424
  //#endregion
5217
5425
  //#region ../theme-core/src/boxBackgroundSchema.ts
5218
5426
  const boxBackgroundOverlaySchema = z.preprocess(normalizeAuthoringOpacity, z.discriminatedUnion("type", [
@@ -5270,7 +5478,7 @@ const boxBackgroundSchema = z.discriminatedUnion("type", [
5270
5478
  })
5271
5479
  ]);
5272
5480
  function normalizeAuthoringOpacity(value) {
5273
- if (!isRecord$3(value)) return value;
5481
+ if (!isRecord$2(value)) return value;
5274
5482
  return {
5275
5483
  ...value,
5276
5484
  opacity: normalizeOpacity(value.opacity)
@@ -5279,7 +5487,7 @@ function normalizeAuthoringOpacity(value) {
5279
5487
  function normalizeOpacity(value) {
5280
5488
  return typeof value === "number" && value > 1 && value <= 100 ? value / 100 : value;
5281
5489
  }
5282
- function isRecord$3(value) {
5490
+ function isRecord$2(value) {
5283
5491
  return typeof value === "object" && value !== null && !Array.isArray(value);
5284
5492
  }
5285
5493
  //#endregion
@@ -5733,7 +5941,7 @@ const headerSchema = z.object({
5733
5941
  variant: headerVariantWithLegacyAliasSchema,
5734
5942
  positioning: headerPositioning,
5735
5943
  shrinkOnScroll: z.boolean(),
5736
- maxWidth: headerMaxWidth,
5944
+ maxWidth: headerMaxWidth.default("container"),
5737
5945
  scrollTreatment: headerScrollTreatmentSchema,
5738
5946
  background: headerBackgroundSchema.default({
5739
5947
  type: "color",
@@ -5932,11 +6140,6 @@ const bodyTextSchema = z.object({ intro: z.object({
5932
6140
  paragraphSize: bodyTextIntroTypographySizeSchema.optional(),
5933
6141
  headingSize: bodyTextIntroTypographySizeSchema.optional()
5934
6142
  }).optional() }).optional();
5935
- const gradientsSchema = z.object({
5936
- button: z.string().min(1).nullable().optional(),
5937
- hero: z.string().min(1).nullable().optional(),
5938
- background: z.string().min(1).nullable().optional()
5939
- });
5940
6143
  /**
5941
6144
  * Testimonial quote-mark treatment, owned by the site style (analogous to the
5942
6145
  * accordion icon). `style` is the coarse selector; the optional fields fine-tune
@@ -6022,267 +6225,6 @@ z.object({
6022
6225
  themes: z.array(themeSchema).length(3)
6023
6226
  });
6024
6227
  //#endregion
6025
- //#region ../blocks/src/system/blocks/events/whole-series/pricing.ts
6026
- function isSaleWindowOpen(option, now) {
6027
- if (!option.isActive) return false;
6028
- if (option.saleStartsAt && new Date(option.saleStartsAt) > now) return false;
6029
- if (option.saleEndsAt && new Date(option.saleEndsAt) < now) return false;
6030
- return true;
6031
- }
6032
- function toExactOrFreeDisplay(amount, currency) {
6033
- if (amount <= 0) return {
6034
- kind: "free",
6035
- currency
6036
- };
6037
- return {
6038
- kind: "exact",
6039
- amount,
6040
- currency
6041
- };
6042
- }
6043
- function getSelectableWholeSeriesPricingOptionsForNow(offering, now) {
6044
- if (offering.pricing.pricingStrategy !== "tiered") return [];
6045
- return [...offering.pricing.options].filter((option) => isSaleWindowOpen(option, now)).sort((left, right) => {
6046
- if (left.sortOrder !== right.sortOrder) return left.sortOrder - right.sortOrder;
6047
- if (left.priceAmount !== right.priceAmount) return left.priceAmount - right.priceAmount;
6048
- return left.name.localeCompare(right.name);
6049
- });
6050
- }
6051
- function resolveWholeSeriesPricingState(offering, selectedPricingOptionId, selectedSlidingAmountOrNowInput, nowInput = {}) {
6052
- const selection = normalizePricingSelectionInput(selectedSlidingAmountOrNowInput, nowInput);
6053
- const currency = offering.pricing.currency;
6054
- switch (offering.pricing.pricingStrategy) {
6055
- case "free": return resolveFreePricing(currency);
6056
- case "single": return resolveSinglePricing(offering.pricing.singlePriceAmount ?? 0, currency);
6057
- case "tiered": return resolveTieredPricing(offering, selectedPricingOptionId, selection.nowInput);
6058
- case "sliding_scale": return resolveSlidingScalePricing(offering, selection.selectedSlidingAmount);
6059
- }
6060
- }
6061
- function normalizePricingSelectionInput(selectedSlidingAmountOrNowInput, nowInput) {
6062
- return typeof selectedSlidingAmountOrNowInput === "object" && selectedSlidingAmountOrNowInput !== null ? {
6063
- selectedSlidingAmount: null,
6064
- nowInput: selectedSlidingAmountOrNowInput
6065
- } : {
6066
- selectedSlidingAmount: typeof selectedSlidingAmountOrNowInput === "number" || selectedSlidingAmountOrNowInput === null ? selectedSlidingAmountOrNowInput : null,
6067
- nowInput
6068
- };
6069
- }
6070
- function resolveFreePricing(currency) {
6071
- return {
6072
- currency,
6073
- display: {
6074
- kind: "free",
6075
- currency
6076
- },
6077
- selectableOptions: [],
6078
- requiresSelection: false,
6079
- selectedOption: null,
6080
- slidingScale: null,
6081
- selectedSlidingAmount: null,
6082
- selectedPriceAmount: 0,
6083
- selectedRequiresPayment: false,
6084
- hasPaidSelection: false,
6085
- hasFreeSelection: true
6086
- };
6087
- }
6088
- function resolveSinglePricing(amount, currency) {
6089
- return {
6090
- currency,
6091
- display: toExactOrFreeDisplay(amount, currency),
6092
- selectableOptions: [],
6093
- requiresSelection: false,
6094
- selectedOption: null,
6095
- slidingScale: null,
6096
- selectedSlidingAmount: null,
6097
- selectedPriceAmount: amount,
6098
- selectedRequiresPayment: amount > 0,
6099
- hasPaidSelection: amount > 0,
6100
- hasFreeSelection: amount <= 0
6101
- };
6102
- }
6103
- function resolveTieredPricing(offering, selectedPricingOptionId, nowInput) {
6104
- const currency = offering.pricing.currency;
6105
- const selectableOptions = getSelectableWholeSeriesPricingOptionsForNow(offering, resolvePlanningDateInput(nowInput));
6106
- const selectedOption = resolveSelectedTier(selectableOptions, selectedPricingOptionId);
6107
- return {
6108
- currency,
6109
- display: resolveTieredDisplay(selectableOptions.reduce((lowest, option) => lowest === null || option.priceAmount < lowest ? option.priceAmount : lowest, null), selectableOptions.length, currency),
6110
- selectableOptions,
6111
- requiresSelection: selectableOptions.length > 1,
6112
- selectedOption,
6113
- slidingScale: null,
6114
- selectedSlidingAmount: null,
6115
- selectedPriceAmount: selectedOption?.priceAmount ?? null,
6116
- selectedRequiresPayment: (selectedOption?.priceAmount ?? 0) > 0,
6117
- hasPaidSelection: selectableOptions.some((option) => option.priceAmount > 0),
6118
- hasFreeSelection: selectableOptions.some((option) => option.priceAmount === 0)
6119
- };
6120
- }
6121
- function resolveSelectedTier(options, selectedId) {
6122
- return (selectedId ? options.find((option) => option.id === selectedId) : null) ?? (options.length === 1 ? options[0] ?? null : null);
6123
- }
6124
- function resolveTieredDisplay(lowestAmount, optionCount, currency) {
6125
- if (lowestAmount === null) return {
6126
- kind: "unavailable",
6127
- currency
6128
- };
6129
- return optionCount > 1 && lowestAmount > 0 ? {
6130
- kind: "from",
6131
- amount: lowestAmount,
6132
- currency
6133
- } : toExactOrFreeDisplay(lowestAmount, currency);
6134
- }
6135
- function resolveSlidingScalePricing(offering, selectedSlidingAmount) {
6136
- const currency = offering.pricing.currency;
6137
- const minAmount = offering.pricing.slidingMinAmount ?? null;
6138
- const maxAmount = offering.pricing.slidingMaxAmount ?? null;
6139
- if (minAmount === null || maxAmount === null || minAmount < 0 || maxAmount < minAmount) return unavailableSlidingScale(currency);
6140
- const selectedAmount = Number.isFinite(selectedSlidingAmount) ? selectedSlidingAmount : null;
6141
- return {
6142
- currency,
6143
- display: {
6144
- kind: "range",
6145
- minAmount,
6146
- maxAmount,
6147
- currency
6148
- },
6149
- selectableOptions: [],
6150
- requiresSelection: selectedAmount === null,
6151
- selectedOption: null,
6152
- slidingScale: {
6153
- minAmount,
6154
- maxAmount,
6155
- suggestedAmount: offering.pricing.slidingSuggestedAmount ?? null
6156
- },
6157
- selectedSlidingAmount: selectedAmount,
6158
- selectedPriceAmount: selectedAmount,
6159
- selectedRequiresPayment: (selectedAmount ?? 0) > 0,
6160
- hasPaidSelection: maxAmount > 0,
6161
- hasFreeSelection: minAmount === 0
6162
- };
6163
- }
6164
- function unavailableSlidingScale(currency) {
6165
- return {
6166
- currency,
6167
- display: {
6168
- kind: "unavailable",
6169
- currency
6170
- },
6171
- selectableOptions: [],
6172
- requiresSelection: true,
6173
- selectedOption: null,
6174
- slidingScale: null,
6175
- selectedSlidingAmount: null,
6176
- selectedPriceAmount: null,
6177
- selectedRequiresPayment: false,
6178
- hasPaidSelection: false,
6179
- hasFreeSelection: false
6180
- };
6181
- }
6182
- function formatWholeSeriesPriceDisplay(display, locale) {
6183
- return formatBookingPriceDisplay(display, {
6184
- decimals: "exact",
6185
- locale
6186
- });
6187
- }
6188
- function formatWholeSeriesPriceAmount(amount, currency, locale) {
6189
- return formatWholeSeriesPriceDisplay(amount > 0 ? {
6190
- kind: "exact",
6191
- amount,
6192
- currency
6193
- } : {
6194
- kind: "free",
6195
- currency
6196
- }, locale);
6197
- }
6198
- //#endregion
6199
- //#region ../blocks/src/system/blocks/events/whole-series/schemas.ts
6200
- const wholeSeriesVenueSchema = z.object({
6201
- id: z.string(),
6202
- name: z.string(),
6203
- address: z.string().nullable()
6204
- });
6205
- const wholeSeriesSessionSchema = z.object({
6206
- id: z.string(),
6207
- startsAt: z.string(),
6208
- endsAt: z.string(),
6209
- timeZone: z.string(),
6210
- capacityOverride: z.number().nullable().optional(),
6211
- overrides: z.record(z.string(), z.unknown()).nullable().optional()
6212
- });
6213
- const wholeSeriesTeamMemberSummarySchema = z.object({
6214
- id: z.string(),
6215
- name: z.string(),
6216
- roleLabel: z.string().nullable(),
6217
- summary: z.string().nullable(),
6218
- profilePath: z.string().nullable()
6219
- });
6220
- const wholeSeriesPricingOptionSchema = z.object({
6221
- id: z.string(),
6222
- name: z.string(),
6223
- description: z.string().nullable(),
6224
- priceAmount: z.number(),
6225
- isActive: z.boolean(),
6226
- sortOrder: z.number(),
6227
- capacityLimit: z.number().nullable(),
6228
- saleStartsAt: z.string().nullable(),
6229
- saleEndsAt: z.string().nullable(),
6230
- attendanceScope: z.enum([
6231
- "in_person",
6232
- "online",
6233
- "both"
6234
- ]).nullable().optional()
6235
- });
6236
- const wholeSeriesPricingSchema = z.object({
6237
- bookingRequirement: z.enum(["none", "required"]),
6238
- pricingStrategy: z.enum([
6239
- "free",
6240
- "single",
6241
- "tiered",
6242
- "sliding_scale"
6243
- ]),
6244
- currency: z.string(),
6245
- singlePriceAmount: z.number().nullable(),
6246
- slidingMinAmount: z.number().nullable().optional(),
6247
- slidingMaxAmount: z.number().nullable().optional(),
6248
- slidingSuggestedAmount: z.number().nullable().optional(),
6249
- paymentTerms: blockBookingPaymentTermsSchema,
6250
- options: z.array(wholeSeriesPricingOptionSchema)
6251
- });
6252
- const publicWholeSeriesOfferingSchema = z.object({
6253
- eventSeriesId: z.string(),
6254
- eventOfferingId: z.string(),
6255
- title: z.string(),
6256
- slug: z.string(),
6257
- description: z.unknown().nullable(),
6258
- deliveryMode: z.enum([
6259
- "in_person",
6260
- "online",
6261
- "hybrid"
6262
- ]),
6263
- waitlistEnabled: z.boolean(),
6264
- requireGuestDetails: z.boolean(),
6265
- teamMembers: z.array(wholeSeriesTeamMemberSummarySchema).optional(),
6266
- enrollmentOpen: z.boolean(),
6267
- pricing: wholeSeriesPricingSchema,
6268
- currency: z.string(),
6269
- paymentTerms: blockBookingPaymentTermsSchema,
6270
- consent: blockBookingConsentSchema.nullable(),
6271
- enrollmentCapacity: z.number().nullable(),
6272
- acceptsMemberships: z.boolean(),
6273
- status: z.enum([
6274
- "active",
6275
- "draft",
6276
- "cancelled"
6277
- ]),
6278
- sessionsCount: z.number(),
6279
- enrolledCount: z.number(),
6280
- availableSpots: z.number().nullable(),
6281
- sessions: z.array(wholeSeriesSessionSchema),
6282
- venue: wholeSeriesVenueSchema.nullable()
6283
- });
6284
- const publicWholeSeriesOfferingsArraySchema = z.array(publicWholeSeriesOfferingSchema);
6285
- //#endregion
6286
6228
  //#region ../blocks/src/system/blocks/events/shared/schemas.ts
6287
6229
  const eventPresetSchema = z.enum([
6288
6230
  "event",
@@ -6337,77 +6279,14 @@ const eventCategorySchema = z.object({
6337
6279
  name: z.string(),
6338
6280
  color: z.string().nullable()
6339
6281
  });
6340
- z.enum([
6341
- "free",
6342
- "paid",
6343
- "external"
6344
- ]);
6345
6282
  /**
6346
6283
  * Access restriction type for public events
6347
6284
  */
6348
6285
  const accessRestrictionSchema = z.enum(["none", "category"]);
6349
- z.enum(["single", "ticket_types"]);
6350
- const freePriceDisplaySchema = z.object({
6351
- kind: z.literal("free"),
6352
- currency: z.string()
6353
- });
6354
- const exactPriceDisplaySchema = z.object({
6355
- kind: z.literal("exact"),
6356
- amount: z.number(),
6357
- currency: z.string()
6358
- });
6359
- const fromPriceDisplaySchema = z.object({
6360
- kind: z.literal("from"),
6361
- amount: z.number(),
6362
- currency: z.string()
6363
- });
6364
- const unavailablePriceDisplaySchema = z.object({
6365
- kind: z.literal("unavailable"),
6366
- currency: z.string()
6367
- });
6368
- const rangePriceDisplaySchema = z.object({
6369
- kind: z.literal("range"),
6370
- minAmount: z.number(),
6371
- maxAmount: z.number(),
6372
- currency: z.string()
6373
- });
6374
- const mixedPriceDisplaySchema = z.object({
6375
- kind: z.literal("mixed"),
6376
- paidFromAmount: z.number(),
6377
- paidToAmount: z.number(),
6378
- currency: z.string()
6379
- });
6380
- const publicWholeSeriesPriceDisplaySchema = z.discriminatedUnion("kind", [
6381
- freePriceDisplaySchema,
6382
- exactPriceDisplaySchema,
6383
- fromPriceDisplaySchema,
6384
- rangePriceDisplaySchema,
6385
- unavailablePriceDisplaySchema
6386
- ]);
6387
- z.discriminatedUnion("kind", [
6388
- freePriceDisplaySchema,
6389
- exactPriceDisplaySchema,
6390
- fromPriceDisplaySchema,
6391
- rangePriceDisplaySchema,
6392
- mixedPriceDisplaySchema,
6393
- unavailablePriceDisplaySchema,
6394
- z.object({
6395
- kind: z.literal("external"),
6396
- label: z.string()
6397
- }),
6398
- z.object({
6399
- kind: z.literal("whole_series"),
6400
- display: publicWholeSeriesPriceDisplaySchema
6401
- })
6402
- ]);
6403
- const publicEventTicketTypePriceDisplaySchema = z.union([
6404
- freePriceDisplaySchema,
6405
- exactPriceDisplaySchema,
6406
- fromPriceDisplaySchema,
6407
- rangePriceDisplaySchema,
6408
- mixedPriceDisplaySchema,
6409
- unavailablePriceDisplaySchema
6410
- ]);
6286
+ const publicCurrencySchema = z.string().regex(/^[A-Z]{3}$/u);
6287
+ const publicCentsSchema = z.number().int().nonnegative();
6288
+ const publicPositiveCentsSchema = z.number().int().positive();
6289
+ const publicIsoDateTimeSchema = z.string().datetime({ offset: true });
6411
6290
  const publicEventAttendanceModeSchema = z.enum(["in_person", "online"]);
6412
6291
  const publicEventAttendanceModeAvailabilitySchema = z.object({
6413
6292
  mode: publicEventAttendanceModeSchema,
@@ -6416,9 +6295,9 @@ const publicEventAttendanceModeAvailabilitySchema = z.object({
6416
6295
  availableSpots: z.number().nullable(),
6417
6296
  waitlistEnabled: z.boolean()
6418
6297
  });
6419
- const publicEventTicketTypeAvailabilitySchema = z.union([z.object({
6298
+ const publicEventPricingOptionAvailabilitySchema = z.union([z.object({
6420
6299
  kind: z.literal("available"),
6421
- remainingCapacity: z.number().nullable()
6300
+ remainingPlaces: z.number().int().nonnegative().nullable()
6422
6301
  }), z.object({
6423
6302
  kind: z.literal("unavailable"),
6424
6303
  reason: z.enum([
@@ -6426,91 +6305,69 @@ const publicEventTicketTypeAvailabilitySchema = z.union([z.object({
6426
6305
  "not_on_sale_yet",
6427
6306
  "sale_ended",
6428
6307
  "sold_out",
6429
- "event_sold_out"
6308
+ "attendance_mode_unavailable",
6309
+ "overallCapacityUnavailable"
6430
6310
  ]),
6431
- remainingCapacity: z.number().nullable()
6311
+ remainingPlaces: z.number().int().nonnegative().nullable()
6432
6312
  })]);
6433
- const publicEventTicketTypeSchema = z.object({
6434
- id: z.string(),
6435
- siteId: z.string(),
6436
- eventSeriesId: z.string(),
6437
- name: z.string(),
6313
+ const publicEventPricingOptionSchema = z.object({
6314
+ id: z.string().min(1),
6315
+ name: z.string().min(1),
6438
6316
  description: z.string().nullable(),
6439
- priceAmount: z.number(),
6317
+ priceAmount: publicCentsSchema,
6440
6318
  isActive: z.boolean(),
6441
- sortOrder: z.number(),
6442
- capacityMode: z.enum(["limited", "unlimited"]),
6443
- capacityLimit: z.number().nullable(),
6319
+ sortOrder: z.number().int().nonnegative(),
6444
6320
  attendanceScope: z.enum([
6445
6321
  "in_person",
6446
6322
  "online",
6447
6323
  "both"
6448
- ]).optional(),
6449
- saleStartsAt: z.string().nullable(),
6450
- saleEndsAt: z.string().nullable(),
6451
- availability: publicEventTicketTypeAvailabilitySchema,
6452
- createdAt: z.string(),
6453
- updatedAt: z.string()
6324
+ ]),
6325
+ saleStartsAt: publicIsoDateTimeSchema.nullable(),
6326
+ saleEndsAt: publicIsoDateTimeSchema.nullable(),
6327
+ availability: publicEventPricingOptionAvailabilitySchema
6328
+ }).superRefine((option, context) => {
6329
+ if (option.saleStartsAt && option.saleEndsAt && Date.parse(option.saleEndsAt) <= Date.parse(option.saleStartsAt)) context.addIssue({
6330
+ code: "custom",
6331
+ path: ["saleEndsAt"],
6332
+ message: "Sale end must be after sale start"
6333
+ });
6454
6334
  });
6455
6335
  const publicEventPricingSchema = z.discriminatedUnion("kind", [
6456
6336
  z.object({
6457
- kind: z.literal("drop_in"),
6458
- currency: z.string(),
6459
- display: z.object({
6460
- kind: z.literal("free"),
6461
- currency: z.string()
6462
- })
6463
- }),
6464
- z.object({
6465
- kind: z.literal("free_registration"),
6466
- currency: z.string(),
6467
- display: z.object({
6468
- kind: z.literal("free"),
6469
- currency: z.string()
6470
- })
6337
+ kind: z.literal("free"),
6338
+ currency: publicCurrencySchema
6471
6339
  }),
6472
6340
  z.object({
6473
- kind: z.literal("single_paid"),
6474
- amount: z.number(),
6475
- currency: z.string(),
6476
- display: z.object({
6477
- kind: z.literal("exact"),
6478
- amount: z.number(),
6479
- currency: z.string()
6480
- })
6341
+ kind: z.literal("single"),
6342
+ amount: publicPositiveCentsSchema,
6343
+ currency: publicCurrencySchema
6481
6344
  }),
6482
6345
  z.object({
6483
- kind: z.literal("ticket_types"),
6484
- currency: z.string(),
6485
- ticketTypes: z.array(publicEventTicketTypeSchema),
6486
- display: publicEventTicketTypePriceDisplaySchema
6346
+ kind: z.literal("tiered"),
6347
+ currency: publicCurrencySchema,
6348
+ options: z.tuple([publicEventPricingOptionSchema], publicEventPricingOptionSchema)
6487
6349
  }),
6488
6350
  z.object({
6489
6351
  kind: z.literal("sliding_scale"),
6490
- minAmount: z.number(),
6491
- maxAmount: z.number(),
6492
- suggestedAmount: z.number().nullable(),
6493
- currency: z.string(),
6494
- display: z.object({
6495
- kind: z.literal("range"),
6496
- minAmount: z.number(),
6497
- maxAmount: z.number(),
6498
- currency: z.string()
6499
- })
6500
- }),
6501
- z.object({
6502
- kind: z.literal("whole_series"),
6503
- display: z.object({
6504
- kind: z.literal("whole_series"),
6505
- display: publicWholeSeriesPriceDisplaySchema
6506
- })
6352
+ minimum: publicCentsSchema,
6353
+ maximum: publicPositiveCentsSchema,
6354
+ suggested: publicCentsSchema.nullable(),
6355
+ currency: publicCurrencySchema
6356
+ }).superRefine((pricing, context) => {
6357
+ if (pricing.minimum > pricing.maximum) context.addIssue({
6358
+ code: "custom",
6359
+ path: ["minimum"],
6360
+ message: "Minimum must not exceed maximum"
6361
+ });
6362
+ if (pricing.suggested !== null && (pricing.suggested < pricing.minimum || pricing.suggested > pricing.maximum)) context.addIssue({
6363
+ code: "custom",
6364
+ path: ["suggested"],
6365
+ message: "Suggested amount must be within the sliding-scale range"
6366
+ });
6507
6367
  }),
6508
6368
  z.object({
6509
6369
  kind: z.literal("external"),
6510
- display: z.object({
6511
- kind: z.literal("external"),
6512
- label: z.string()
6513
- })
6370
+ label: z.string().min(1)
6514
6371
  })
6515
6372
  ]);
6516
6373
  const publicTeamMemberSummarySchema = z.object({
@@ -6536,11 +6393,30 @@ const publicEventBookingAvailabilitySchema = z.discriminatedUnion("kind", [z.obj
6536
6393
  "enrollment_closed"
6537
6394
  ])
6538
6395
  })]);
6396
+ const publicRunSessionSchema = z.object({
6397
+ occurrenceId: z.string(),
6398
+ startsAt: z.string(),
6399
+ endsAt: z.string(),
6400
+ timeZone: z.string(),
6401
+ status: z.string()
6402
+ });
6403
+ const publicWholeSeriesRunSchema = z.object({
6404
+ eventSeriesId: z.string(),
6405
+ eventOfferingId: z.string(),
6406
+ enrollmentOpen: z.boolean(),
6407
+ visibility: z.enum(["public", "unlisted"]),
6408
+ lifecycle: z.enum([
6409
+ "upcoming",
6410
+ "in_progress",
6411
+ "completed",
6412
+ "cancelled"
6413
+ ]),
6414
+ sessions: z.array(publicRunSessionSchema)
6415
+ });
6539
6416
  /**
6540
- * Zod schema for public event data
6417
+ * Zod schema for the fields shared by both booking scopes.
6541
6418
  */
6542
- const publicEventSchema = z.object({
6543
- bookingScope: z.enum(["single_occurrence", "whole_series"]),
6419
+ const publicEventBaseSchema = z.object({
6544
6420
  eventPreset: eventPresetSchema.optional(),
6545
6421
  eventDestinationMode: eventDestinationModeSchema.optional(),
6546
6422
  scheduleScope: eventScheduleScopeSchema$2.optional(),
@@ -6590,20 +6466,68 @@ const publicEventSchema = z.object({
6590
6466
  accessRestriction: accessRestrictionSchema,
6591
6467
  allowedCategoryIds: z.array(z.string()),
6592
6468
  requireGuestDetails: z.boolean().optional(),
6593
- acceptsPasses: z.boolean().optional(),
6594
- acceptsMemberships: z.boolean().optional(),
6595
- wholeSeries: z.object({
6596
- eventSeriesId: z.string(),
6597
- pricing: wholeSeriesPricingSchema,
6598
- currency: z.string(),
6599
- enrollmentOpen: z.boolean()
6600
- }).nullable().optional()
6469
+ acceptsPasses: z.boolean(),
6470
+ acceptsMemberships: z.boolean()
6601
6471
  });
6602
6472
  /**
6473
+ * Zod schema for public event data.
6474
+ *
6475
+ * The booking scope discriminates: a `whole_series` record without complete
6476
+ * `run` facts is rejected, and a `single_occurrence` record never carries them.
6477
+ */
6478
+ const publicEventSchema = z.union([publicEventBaseSchema.extend({
6479
+ bookingScope: z.literal("single_occurrence"),
6480
+ run: z.undefined().optional()
6481
+ }), publicEventBaseSchema.extend({
6482
+ bookingScope: z.literal("whole_series"),
6483
+ run: publicWholeSeriesRunSchema
6484
+ })]);
6485
+ /**
6603
6486
  * Schema for an array of public events
6604
6487
  */
6605
6488
  const publicEventsArraySchema = z.array(publicEventSchema);
6606
6489
  //#endregion
6490
+ //#region ../blocks/src/system/blocks/events/shared/runGrouping.ts
6491
+ function isWholeSeriesPublicEvent(event) {
6492
+ return event.bookingScope === "whole_series";
6493
+ }
6494
+ /**
6495
+ * Group events into one entry per whole-series run.
6496
+ *
6497
+ * Single-occurrence events are ignored. Run order follows first appearance, so
6498
+ * a caller's sort survives the grouping, and an occurrence that arrives twice
6499
+ * is kept once.
6500
+ */
6501
+ function groupPublicEventsByRun(events) {
6502
+ const occurrencesBySeriesId = /* @__PURE__ */ new Map();
6503
+ const order = [];
6504
+ const seenOccurrenceIds = /* @__PURE__ */ new Set();
6505
+ for (const event of events) {
6506
+ if (!isWholeSeriesPublicEvent(event)) continue;
6507
+ if (seenOccurrenceIds.has(event.occurrenceId)) continue;
6508
+ seenOccurrenceIds.add(event.occurrenceId);
6509
+ const seriesId = event.run.eventSeriesId;
6510
+ const existing = occurrencesBySeriesId.get(seriesId);
6511
+ if (existing) {
6512
+ existing.push(event);
6513
+ continue;
6514
+ }
6515
+ occurrencesBySeriesId.set(seriesId, [event]);
6516
+ order.push(seriesId);
6517
+ }
6518
+ return order.map((eventSeriesId) => {
6519
+ const occurrences = occurrencesBySeriesId.get(eventSeriesId) ?? [];
6520
+ const representative = occurrences[0];
6521
+ if (!representative) throw new Error(`Run ${eventSeriesId} grouped without any occurrence`);
6522
+ return {
6523
+ eventSeriesId,
6524
+ run: representative.run,
6525
+ occurrences,
6526
+ representative
6527
+ };
6528
+ });
6529
+ }
6530
+ //#endregion
6607
6531
  //#region ../blocks/src/system/helpers/normalizeIds.ts
6608
6532
  function normalizeIdList(parts) {
6609
6533
  const seen = /* @__PURE__ */ new Set();
@@ -6669,94 +6593,142 @@ function normalizeEventStaffMemberId(value) {
6669
6593
  return trimmed ? trimmed : null;
6670
6594
  }
6671
6595
  //#endregion
6672
- //#region ../calendar/src/timezone.ts
6673
- /**
6674
- * Timezone boundary helpers — internal.
6675
- *
6676
- * Both functions read an ISO instant in a given IANA timezone and project it
6677
- * onto the wall clock of that zone. They use Intl.DateTimeFormat with
6678
- * hourCycle: 'h23' to avoid 12/24-hour ambiguity. Used internally by the
6679
- * timetable model and `groupItemsByDate`; not exported from the package's
6680
- * public surface.
6681
- */
6682
- const MINUTES_PER_HOUR = 60;
6683
- /**
6684
- * Local minute-of-day [0, 24*60) for an ISO instant in the given zone.
6685
- * Returns NaN if the inputs cannot be parsed (caller should drop the item).
6686
- */
6687
- function getLocalMinuteOfDay(isoTimestamp, timeZone) {
6688
- const date = new Date(isoTimestamp);
6689
- if (Number.isNaN(date.getTime())) return NaN;
6690
- if (!isValidTimeZone(timeZone)) return NaN;
6691
- const parts = new Intl.DateTimeFormat("en-GB", {
6692
- timeZone,
6693
- hour: "2-digit",
6694
- minute: "2-digit",
6695
- hourCycle: "h23"
6696
- }).formatToParts(date);
6697
- const hourPart = Number(parts.find((part) => part.type === "hour")?.value);
6698
- const minute = Number(parts.find((part) => part.type === "minute")?.value);
6699
- const hour = hourPart === 24 ? 0 : hourPart;
6700
- if (Number.isNaN(hour) || Number.isNaN(minute)) return NaN;
6701
- return hour * MINUTES_PER_HOUR + minute;
6702
- }
6703
- /**
6704
- * Local YYYY-MM-DD for an ISO instant in the given zone.
6705
- * Returns null if the inputs cannot be parsed.
6706
- */
6707
- function getLocalDateKey(isoTimestamp, timeZone) {
6708
- const date = new Date(isoTimestamp);
6709
- if (Number.isNaN(date.getTime())) return null;
6710
- if (!isValidTimeZone(timeZone)) return null;
6711
- const parts = new Intl.DateTimeFormat("en-CA", {
6712
- timeZone,
6713
- year: "numeric",
6714
- month: "2-digit",
6715
- day: "2-digit"
6716
- }).formatToParts(date);
6717
- const year = parts.find((part) => part.type === "year")?.value;
6718
- const month = parts.find((part) => part.type === "month")?.value;
6719
- const day = parts.find((part) => part.type === "day")?.value;
6720
- if (year === void 0 || year.length === 0) return null;
6721
- if (month === void 0 || month.length === 0) return null;
6722
- if (day === void 0 || day.length === 0) return null;
6723
- return `${year}-${month}-${day}`;
6724
- }
6725
- const validTimeZoneCache = /* @__PURE__ */ new Map();
6726
- function isValidTimeZone(timeZone) {
6727
- if (typeof timeZone !== "string" || timeZone.length === 0) return false;
6728
- const cached = validTimeZoneCache.get(timeZone);
6729
- if (cached !== void 0) return cached;
6730
- try {
6731
- new Intl.DateTimeFormat("en-US", { timeZone });
6732
- validTimeZoneCache.set(timeZone, true);
6733
- return true;
6734
- } catch {
6735
- validTimeZoneCache.set(timeZone, false);
6736
- return false;
6737
- }
6596
+ //#region ../blocks/src/system/runtime/nodes/events/domain/destination.ts
6597
+ function isRecord$1(value) {
6598
+ return typeof value === "object" && value !== null && !Array.isArray(value);
6599
+ }
6600
+ function isRuntimeEventDestination(value) {
6601
+ if (!isRecord$1(value) || typeof value.kind !== "string") return false;
6602
+ if (value.kind === "native") return (value.mode === "cms-managed" || value.mode === "system-managed") && typeof value.path === "string";
6603
+ return value.kind === "external" && typeof value.url === "string" && typeof value.label === "string";
6604
+ }
6605
+ function resolveRuntimeEventDestination(event) {
6606
+ if (isRuntimeEventDestination(event.destination)) return event.destination;
6607
+ return {
6608
+ kind: "native",
6609
+ mode: event.eventDestinationMode === "system-managed" ? "system-managed" : "cms-managed",
6610
+ path: typeof event.path === "string" ? event.path : ""
6611
+ };
6738
6612
  }
6739
6613
  //#endregion
6740
- //#region ../calendar/src/group.ts
6741
- /**
6742
- * Group items by their local date (YYYY-MM-DD in the item's own timezone).
6743
- *
6744
- * Generic over `T`. Items with un-parseable startsAt or invalid timezone are
6745
- * silently dropped — call sites that need diagnostics should validate upstream.
6746
- *
6747
- * The output map preserves insertion order of dates (ES2015 spec) and the
6748
- * relative order of items within each date matches input order.
6749
- */
6750
- function groupItemsByDate(items, accessors) {
6751
- const grouped = /* @__PURE__ */ new Map();
6752
- for (const item of items) {
6753
- const dateKey = getLocalDateKey(accessors.getStartsAt(item), accessors.getTimeZone(item));
6754
- if (dateKey == null) continue;
6755
- const existing = grouped.get(dateKey);
6756
- if (existing !== void 0) existing.push(item);
6757
- else grouped.set(dateKey, [item]);
6614
+ //#region ../calendar/src/grid.ts
6615
+ const DAY_NAMES = [
6616
+ "Sun",
6617
+ "Mon",
6618
+ "Tue",
6619
+ "Wed",
6620
+ "Thu",
6621
+ "Fri",
6622
+ "Sat"
6623
+ ];
6624
+ const MONTH_NAMES = [
6625
+ "January",
6626
+ "February",
6627
+ "March",
6628
+ "April",
6629
+ "May",
6630
+ "June",
6631
+ "July",
6632
+ "August",
6633
+ "September",
6634
+ "October",
6635
+ "November",
6636
+ "December"
6637
+ ];
6638
+ function formatDateKey(date) {
6639
+ return `${String(date.year).padStart(4, "0")}-${String(date.month).padStart(2, "0")}-${String(date.day).padStart(2, "0")}`;
6640
+ }
6641
+ function isToday(date, today) {
6642
+ return isSameCalendarDay(date, today);
6643
+ }
6644
+ function isSameCalendarDay(left, right) {
6645
+ return left.year === right.year && left.month === right.month && left.day === right.day;
6646
+ }
6647
+ function addMonths(date, months) {
6648
+ const target = new Date(Date.UTC(date.year, date.month - 1 + months, 1, 12));
6649
+ const year = target.getUTCFullYear();
6650
+ const month = target.getUTCMonth() + 1;
6651
+ const lastDay = new Date(Date.UTC(year, month, 0, 12)).getUTCDate();
6652
+ return {
6653
+ year,
6654
+ month,
6655
+ day: Math.min(date.day, lastDay)
6656
+ };
6657
+ }
6658
+ function addDays(date, days) {
6659
+ const result = new Date(Date.UTC(date.year, date.month - 1, date.day + days, 12));
6660
+ return {
6661
+ year: result.getUTCFullYear(),
6662
+ month: result.getUTCMonth() + 1,
6663
+ day: result.getUTCDate()
6664
+ };
6665
+ }
6666
+ function shiftCalendarAnchorDate(date, view, steps) {
6667
+ return view === "week" ? addDays(date, steps * 7) : addMonths(date, steps);
6668
+ }
6669
+ function getMonthCalendarDays(year, month, startOfWeek) {
6670
+ const firstDay = {
6671
+ year,
6672
+ month,
6673
+ day: 1
6674
+ };
6675
+ const dayOfWeek = getDayOfWeek(firstDay);
6676
+ const gridStart = addDays(firstDay, -(startOfWeek === "monday" ? dayOfWeek === 0 ? 6 : dayOfWeek - 1 : dayOfWeek));
6677
+ return Array.from({ length: 42 }, (_, index) => addDays(gridStart, index));
6678
+ }
6679
+ function getWeekCalendarDays(baseDate, startOfWeek) {
6680
+ const dayOfWeek = getDayOfWeek(baseDate);
6681
+ const weekStart = addDays(baseDate, -(startOfWeek === "monday" ? dayOfWeek === 0 ? 6 : dayOfWeek - 1 : dayOfWeek));
6682
+ return Array.from({ length: 7 }, (_, index) => addDays(weekStart, index));
6683
+ }
6684
+ function getDayOfWeek(date) {
6685
+ return new Date(Date.UTC(date.year, date.month - 1, date.day, 12)).getUTCDay();
6686
+ }
6687
+ function getWeekCalendarDayTuple(baseDate, startOfWeek) {
6688
+ const days = getWeekCalendarDays(baseDate, startOfWeek);
6689
+ assertWeekCalendarDays(days);
6690
+ return days;
6691
+ }
6692
+ function assertWeekCalendarDays(days) {
6693
+ if (days.length !== 7) throw new Error("Expected exactly 7 calendar days");
6694
+ }
6695
+ function getOrderedDayNames(startOfWeek, dayNames = DAY_NAMES) {
6696
+ if (startOfWeek === "monday") {
6697
+ const sunday = dayNames[0] ?? "Sun";
6698
+ return [...dayNames.slice(1), sunday];
6699
+ }
6700
+ return [...dayNames];
6701
+ }
6702
+ function formatCalendarTitle(viewDate, view, days, monthNames = MONTH_NAMES) {
6703
+ if (view === "week") {
6704
+ const weekStart = days[0];
6705
+ const weekEnd = days[6];
6706
+ if (weekStart === void 0 || weekEnd === void 0) return `${monthNames[viewDate.month - 1] ?? ""} ${viewDate.year}`;
6707
+ const startMonth = monthNames[weekStart.month - 1] ?? "";
6708
+ const endMonth = monthNames[weekEnd.month - 1] ?? "";
6709
+ if (weekStart.month === weekEnd.month) return `${startMonth} ${weekStart.day} - ${weekEnd.day}, ${weekEnd.year}`;
6710
+ return `${startMonth} ${weekStart.day} - ${endMonth} ${weekEnd.day}, ${weekEnd.year}`;
6758
6711
  }
6759
- return grouped;
6712
+ return `${monthNames[viewDate.month - 1] ?? ""} ${viewDate.year}`;
6713
+ }
6714
+ function buildCalendarViewport(input) {
6715
+ const monthNames = input.monthNames ?? MONTH_NAMES;
6716
+ if (input.view === "week") {
6717
+ const days = getWeekCalendarDayTuple(input.anchorDate, input.startOfWeek);
6718
+ return {
6719
+ kind: "week",
6720
+ anchorDate: input.anchorDate,
6721
+ days,
6722
+ title: formatCalendarTitle(input.anchorDate, "week", days, monthNames)
6723
+ };
6724
+ }
6725
+ const days = getMonthCalendarDays(input.anchorDate.year, input.anchorDate.month, input.startOfWeek);
6726
+ return {
6727
+ kind: "month",
6728
+ anchorDate: input.anchorDate,
6729
+ days,
6730
+ title: formatCalendarTitle(input.anchorDate, "month", days, monthNames)
6731
+ };
6760
6732
  }
6761
6733
  /**
6762
6734
  * Canonical responsive ladder for a user-configured column count: 1 column at
@@ -6796,254 +6768,10 @@ const GRID_BREAKPOINTS = [
6796
6768
  "xl"
6797
6769
  ];
6798
6770
  //#endregion
6799
- //#region ../blocks/src/system/runtime/nodes/events/shared/eventCapacity.ts
6771
+ //#region ../blocks/src/system/runtime/nodes/events/shared/listingLayout.ts
6800
6772
  /**
6801
- * Compute remaining spots for an event from `availableSpots` with a fallback
6802
- * to `capacity - registeredCount`. Returns `{ available: null, isFull: false }`
6803
- * for uncapped events so call sites can render no capacity indicator at all.
6804
- *
6805
- * The single source of truth for capacity math across event cards, agenda
6806
- * rows, and timetable cells — keep wording variations in the call sites.
6807
- */
6808
- function getEventAvailability(event) {
6809
- if (event.capacity == null) return {
6810
- available: null,
6811
- isFull: false
6812
- };
6813
- const available = event.availableSpots ?? event.capacity - event.registeredCount;
6814
- if (available <= 0) return {
6815
- available: 0,
6816
- isFull: true
6817
- };
6818
- return {
6819
- available,
6820
- isFull: false
6821
- };
6822
- }
6823
- /**
6824
- * Derive the three-state availability status (available / filling / waitlist)
6825
- * plus a short pill label. Builds on {@link getEventAvailability} so the
6826
- * capacity math (availableSpots with a `capacity - registeredCount` fallback)
6827
- * stays consistent with the binary helper used elsewhere.
6828
- */
6829
- function getEventAvailabilityStatus(event, options) {
6830
- const threshold = options?.fillingThreshold ?? 2;
6831
- const { available, isFull } = getEventAvailability(event);
6832
- if (available == null) return {
6833
- status: "available",
6834
- available: null,
6835
- isFull: false,
6836
- label: null
6837
- };
6838
- if (isFull) return {
6839
- status: "waitlist",
6840
- available: 0,
6841
- isFull: true,
6842
- label: event.waitlistEnabled ? "Waitlist" : "Full"
6843
- };
6844
- if (available <= threshold) return {
6845
- status: "filling",
6846
- available,
6847
- isFull: false,
6848
- label: `${available} left`
6849
- };
6850
- return {
6851
- status: "available",
6852
- available,
6853
- isFull: false,
6854
- label: `${available} places`
6855
- };
6856
- }
6857
- //#endregion
6858
- //#region ../blocks/src/system/runtime/nodes/events/shared/destination.ts
6859
- function isRecord$2(value) {
6860
- return typeof value === "object" && value !== null && !Array.isArray(value);
6861
- }
6862
- function isRuntimeEventDestination(value) {
6863
- if (!isRecord$2(value) || typeof value.kind !== "string") return false;
6864
- if (value.kind === "native") return (value.mode === "cms-managed" || value.mode === "system-managed") && typeof value.path === "string";
6865
- return value.kind === "external" && typeof value.url === "string" && typeof value.label === "string";
6866
- }
6867
- function resolveRuntimeEventDestination(event) {
6868
- if (isRuntimeEventDestination(event.destination)) return event.destination;
6869
- return {
6870
- kind: "native",
6871
- mode: event.eventDestinationMode === "system-managed" ? "system-managed" : "cms-managed",
6872
- path: typeof event.path === "string" ? event.path : ""
6873
- };
6874
- }
6875
- //#endregion
6876
- //#region ../blocks/src/system/runtime/nodes/events/shared/pricing.ts
6877
- const DEFAULT_EVENT_CURRENCY = "USD";
6878
- function resolveRuntimeEventPricing(event) {
6879
- if (isPublicEventPricing(event.pricing)) return event.pricing;
6880
- if (resolveRuntimeEventDestination(event).kind === "external") return {
6881
- kind: "external",
6882
- display: {
6883
- kind: "external",
6884
- label: "External registration"
6885
- }
6886
- };
6887
- const legacy = event;
6888
- const currency = readCurrency(legacy.currency);
6889
- if (event.requiresRegistration === false) return {
6890
- kind: "drop_in",
6891
- currency,
6892
- display: {
6893
- kind: "free",
6894
- currency
6895
- }
6896
- };
6897
- if (legacy.pricingMode === "ticket_types") return {
6898
- kind: "ticket_types",
6899
- currency,
6900
- ticketTypes: Array.isArray(legacy.ticketTypes) ? legacy.ticketTypes.filter(isPublicEventTicketType) : [],
6901
- display: readTicketTypePriceDisplay(legacy.priceDisplay, currency)
6902
- };
6903
- const amount = typeof legacy.price === "number" ? legacy.price : 0;
6904
- if (legacy.pricingType === "paid" && amount > 0) return {
6905
- kind: "single_paid",
6906
- amount,
6907
- currency,
6908
- display: {
6909
- kind: "exact",
6910
- amount,
6911
- currency
6912
- }
6913
- };
6914
- return {
6915
- kind: "free_registration",
6916
- currency,
6917
- display: {
6918
- kind: "free",
6919
- currency
6920
- }
6921
- };
6922
- }
6923
- function readCurrency(value) {
6924
- return typeof value === "string" && value.trim().length > 0 ? value : DEFAULT_EVENT_CURRENCY;
6925
- }
6926
- function isRecord$1(value) {
6927
- return typeof value === "object" && value !== null && !Array.isArray(value);
6928
- }
6929
- function isPublicEventPricing(value) {
6930
- if (!isRecord$1(value) || typeof value.kind !== "string") return false;
6931
- return value.kind === "drop_in" || value.kind === "free_registration" || value.kind === "single_paid" || value.kind === "sliding_scale" || value.kind === "ticket_types" || value.kind === "whole_series" || value.kind === "external";
6932
- }
6933
- function isPublicEventTicketType(value) {
6934
- return isRecord$1(value) && typeof value.id === "string";
6935
- }
6936
- function readTicketTypePriceDisplay(value, currency) {
6937
- const display = readLegacyPriceDisplay(value, currency);
6938
- return display.kind === "whole_series" || display.kind === "external" ? {
6939
- kind: "unavailable",
6940
- currency
6941
- } : display;
6942
- }
6943
- function readLegacyPriceDisplay(value, currency) {
6944
- if (!isRecord$1(value) || typeof value.kind !== "string") return {
6945
- kind: "unavailable",
6946
- currency
6947
- };
6948
- const displayCurrency = readCurrency(value.currency);
6949
- switch (value.kind) {
6950
- case "free": return {
6951
- kind: "free",
6952
- currency: displayCurrency
6953
- };
6954
- case "exact": return readAmount(value.amount) !== null ? {
6955
- kind: "exact",
6956
- amount: readAmount(value.amount) ?? 0,
6957
- currency: displayCurrency
6958
- } : {
6959
- kind: "unavailable",
6960
- currency
6961
- };
6962
- case "from": return readAmount(value.amount) !== null ? {
6963
- kind: "from",
6964
- amount: readAmount(value.amount) ?? 0,
6965
- currency: displayCurrency
6966
- } : {
6967
- kind: "unavailable",
6968
- currency
6969
- };
6970
- case "range": {
6971
- const minAmount = readAmount(value.minAmount);
6972
- const maxAmount = readAmount(value.maxAmount);
6973
- return minAmount !== null && maxAmount !== null ? {
6974
- kind: "range",
6975
- minAmount,
6976
- maxAmount,
6977
- currency: displayCurrency
6978
- } : {
6979
- kind: "unavailable",
6980
- currency
6981
- };
6982
- }
6983
- case "mixed": return readAmount(value.paidFromAmount) !== null ? {
6984
- kind: "mixed",
6985
- paidFromAmount: readAmount(value.paidFromAmount) ?? 0,
6986
- paidToAmount: readAmount(value.paidToAmount) ?? readAmount(value.paidFromAmount) ?? 0,
6987
- currency: displayCurrency
6988
- } : {
6989
- kind: "unavailable",
6990
- currency
6991
- };
6992
- case "unavailable": return {
6993
- kind: "unavailable",
6994
- currency: displayCurrency
6995
- };
6996
- case "whole_series": return isRecord$1(value.display) ? {
6997
- kind: "whole_series",
6998
- display: readCoursePriceDisplay(value.display, displayCurrency)
6999
- } : {
7000
- kind: "whole_series",
7001
- display: {
7002
- kind: "unavailable",
7003
- currency: displayCurrency
7004
- }
7005
- };
7006
- default: return {
7007
- kind: "unavailable",
7008
- currency
7009
- };
7010
- }
7011
- }
7012
- function readCoursePriceDisplay(value, currency) {
7013
- switch (value.kind) {
7014
- case "free": return {
7015
- kind: "free",
7016
- currency: readCurrency(value.currency)
7017
- };
7018
- case "exact": return readAmount(value.amount) !== null ? {
7019
- kind: "exact",
7020
- amount: readAmount(value.amount) ?? 0,
7021
- currency: readCurrency(value.currency)
7022
- } : {
7023
- kind: "unavailable",
7024
- currency
7025
- };
7026
- case "from": return readAmount(value.amount) !== null ? {
7027
- kind: "from",
7028
- amount: readAmount(value.amount) ?? 0,
7029
- currency: readCurrency(value.currency)
7030
- } : {
7031
- kind: "unavailable",
7032
- currency
7033
- };
7034
- default: return {
7035
- kind: "unavailable",
7036
- currency
7037
- };
7038
- }
7039
- }
7040
- function readAmount(value) {
7041
- return typeof value === "number" && Number.isFinite(value) ? value : null;
7042
- }
7043
- //#endregion
7044
- //#region ../blocks/src/system/runtime/nodes/events/shared/utils.ts
7045
- /**
7046
- * Shared utility functions for event blocks
6773
+ * Layout classes for event listing containers and cards. Presentation
6774
+ * plumbing rather than event domain logic, so it lives beside the components.
7047
6775
  */
7048
6776
  /**
7049
6777
  * Column class mappings for grid layouts, derived from the canonical
@@ -7078,187 +6806,6 @@ function getCardOrientation(layout) {
7078
6806
  case "compact": return "vertical";
7079
6807
  }
7080
6808
  }
7081
- /**
7082
- * Format date for display.
7083
- * Returns: "Monday, January 15, 2025"
7084
- */
7085
- function formatDate(isoTimestamp, options) {
7086
- return formatBookingDate(toScheduledBookingDateTime({
7087
- startsAt: isoTimestamp,
7088
- timeZone: toBookingTimeZone(options?.timeZone ?? "UTC"),
7089
- locale: options?.locale ?? "en-US"
7090
- }), {
7091
- weekday: "long",
7092
- year: "numeric",
7093
- month: "long",
7094
- day: "numeric"
7095
- });
7096
- }
7097
- /**
7098
- * Format time for display.
7099
- * Returns: "10:00 AM"
7100
- */
7101
- function formatTime(isoTimestamp, options) {
7102
- return formatBookingTime(toScheduledBookingDateTime({
7103
- startsAt: isoTimestamp,
7104
- timeZone: toBookingTimeZone(options?.timeZone ?? "UTC"),
7105
- locale: options?.locale ?? "en-US"
7106
- }), {
7107
- hour: "numeric",
7108
- minute: "2-digit",
7109
- hour12: true
7110
- });
7111
- }
7112
- function formatEventDateParts(args) {
7113
- const scheduledRange = toScheduledBookingDateRange({
7114
- startsAt: args.startsAt,
7115
- endsAt: args.endsAt,
7116
- timeZone: toBookingTimeZone(args.timeZone ?? "UTC"),
7117
- locale: args.locale ?? "en-US"
7118
- });
7119
- const { date, time: start } = formatBookingDateParts(scheduledRange, { dateFormat: {
7120
- weekday: "long",
7121
- year: "numeric",
7122
- month: "long",
7123
- day: "numeric"
7124
- } });
7125
- return {
7126
- date,
7127
- timeRange: `${start} – ${formatBookingTime(toScheduledBookingDateTime({
7128
- startsAt: args.endsAt,
7129
- timeZone: scheduledRange.timeZone,
7130
- locale: args.locale ?? "en-US"
7131
- }))}`
7132
- };
7133
- }
7134
- function formatEventCompactDateRail(isoTimestamp, options) {
7135
- const dateTime = toScheduledBookingDateTime({
7136
- startsAt: isoTimestamp,
7137
- timeZone: toBookingTimeZone(options?.timeZone ?? "UTC"),
7138
- locale: options?.locale ?? "en-US"
7139
- });
7140
- const weekday = formatBookingDate(dateTime, { weekday: "short" }).toLocaleUpperCase(options?.locale ?? "en-US");
7141
- const month = formatBookingDate(dateTime, { month: "short" }).toLocaleUpperCase(options?.locale ?? "en-US");
7142
- return {
7143
- weekday,
7144
- day: formatBookingDate(dateTime, { day: "numeric" }),
7145
- month,
7146
- label: formatBookingDate(dateTime, {
7147
- weekday: "long",
7148
- year: "numeric",
7149
- month: "long",
7150
- day: "numeric"
7151
- })
7152
- };
7153
- }
7154
- function formatTeamMemberNames(names) {
7155
- const normalized = names.map((name) => name.trim()).filter(Boolean);
7156
- if (normalized.length === 0) return null;
7157
- const [firstName, secondName] = normalized;
7158
- if (!firstName) return null;
7159
- if (normalized.length === 1) return firstName;
7160
- if (normalized.length === 2 && secondName) return `${firstName} and ${secondName}`;
7161
- const finalName = normalized[normalized.length - 1] ?? "";
7162
- return `${normalized.slice(0, -1).join(", ")}, and ${finalName}`;
7163
- }
7164
- function formatEventTeacherLine(event) {
7165
- const names = formatTeamMemberNames((event.teamMembers ?? []).map((teamMember) => teamMember.name));
7166
- return names ? `with ${names}` : null;
7167
- }
7168
- /**
7169
- * Group events by their local date (YYYY-MM-DD key in each event's own
7170
- * timezone). Thin domain wrapper over `groupItemsByDate` from the calendar
7171
- * package; preserves the historical `Map<string, PublicEvent[]>` shape.
7172
- */
7173
- function groupEventsByDate(events, calendarTimeZone) {
7174
- const grouped = groupItemsByDate(events, {
7175
- getStartsAt: (event) => event.startsAt,
7176
- getTimeZone: () => calendarTimeZone
7177
- });
7178
- const map = /* @__PURE__ */ new Map();
7179
- for (const [dateKey, bucket] of grouped) map.set(dateKey, [...bucket]);
7180
- return map;
7181
- }
7182
- /**
7183
- * Format event price for display.
7184
- * @returns "Free", "£25", "£12 - £25", "Free / £12 - £25", "Course (From £40)", etc.
7185
- *
7186
- * Events render compact amounts (no trailing ".00"); the actual ADT→string
7187
- * mapping lives in `formatBookingPriceDisplay` in @riverbankcms/core so all
7188
- * surfaces stay consistent.
7189
- */
7190
- function formatEventPrice(event) {
7191
- return formatEventPricing(resolveRuntimeEventPricing(event), { decimals: "auto" });
7192
- }
7193
- /**
7194
- * Format an event's price directly from its pricing value (without the full
7195
- * event). Same output as {@link formatEventPrice}.
7196
- */
7197
- function formatEventPriceDisplay(pricing) {
7198
- return formatEventPricing(pricing, { decimals: "auto" });
7199
- }
7200
- function closedEventBookingLabel(reason) {
7201
- switch (reason) {
7202
- case "occurrence_cancelled":
7203
- case "series_cancelled": return "Cancelled";
7204
- case "occurrence_completed":
7205
- case "series_completed": return "Completed";
7206
- case "series_draft":
7207
- case "series_archived":
7208
- case "parent_archived":
7209
- case "parent_inconsistent":
7210
- case "not_public":
7211
- case "already_started":
7212
- case "enrollment_closed": return "Registration closed";
7213
- default: return assertNever$2(reason);
7214
- }
7215
- }
7216
- function resolveEventHref(event) {
7217
- const destination = resolveRuntimeEventDestination(event);
7218
- return destination.kind === "external" ? destination.url : destination.path;
7219
- }
7220
- function resolveEventLinkProps(event) {
7221
- const href = resolveEventHref(event);
7222
- return resolveRuntimeEventDestination(event).kind === "external" ? {
7223
- href,
7224
- target: "_blank",
7225
- rel: "noopener noreferrer"
7226
- } : { href };
7227
- }
7228
- /**
7229
- * Resolve the CTA state for an event.
7230
- * Shared by EventCard and EventCompactRow to keep sold-out, registration-closed,
7231
- * and course-specific defaults consistent.
7232
- */
7233
- function resolveEventCta(event, buttonText) {
7234
- const { isFull: isSoldOut } = getEventAvailability(event);
7235
- const destination = resolveRuntimeEventDestination(event);
7236
- const isExternal = destination.kind === "external";
7237
- const defaultButtonText = isExternal ? destination.label : event.bookingScope === "whole_series" ? "Book course" : "Book";
7238
- const effectiveButtonText = buttonText ?? defaultButtonText;
7239
- const href = resolveEventHref(event);
7240
- if (event.bookingAvailability.kind === "closed") {
7241
- const linksToClosedWholeSeriesDetails = event.bookingAvailability.reason === "enrollment_closed" && !isExternal;
7242
- return {
7243
- label: closedEventBookingLabel(event.bookingAvailability.reason),
7244
- href,
7245
- external: isExternal,
7246
- hidden: !linksToClosedWholeSeriesDetails
7247
- };
7248
- }
7249
- if (!isExternal && isSoldOut) return {
7250
- label: "Sold out",
7251
- href,
7252
- external: false,
7253
- hidden: true
7254
- };
7255
- return {
7256
- label: effectiveButtonText,
7257
- href,
7258
- external: isExternal,
7259
- hidden: false
7260
- };
7261
- }
7262
6809
  //#endregion
7263
6810
  //#region ../blocks/src/system/constants/events.ts
7264
6811
  const eventCalendarDisplayModeValues = [
@@ -7274,6 +6821,13 @@ const eventCalendarListLayoutValues = [
7274
6821
  "grid",
7275
6822
  "compact"
7276
6823
  ];
6824
+ function isEventCalendarDisplayMode(value) {
6825
+ return typeof value === "string" && eventCalendarDisplayModeValues.includes(value);
6826
+ }
6827
+ function normalizeEventCalendarDisplayMode(input) {
6828
+ if (isEventCalendarDisplayMode(input.variant)) return input.variant;
6829
+ return isEventCalendarDisplayMode(input.displayMode) ? input.displayMode : void 0;
6830
+ }
7277
6831
  //#endregion
7278
6832
  //#region ../blocks/src/system/constants/buttonRoles.ts
7279
6833
  /**
@@ -7411,6 +6965,158 @@ function getEventRuntimeFiltersKey(filters) {
7411
6965
  });
7412
6966
  }
7413
6967
  //#endregion
6968
+ //#region ../blocks/src/system/blocks/events/shared/calendarWindow.ts
6969
+ function resolveCalendarReadProfile(content) {
6970
+ const mode = normalizeEventCalendarDisplayMode(content);
6971
+ return mode === "timetable" || mode === "week-board" ? "timetable" : "listing-card";
6972
+ }
6973
+ /** Runtime picker sentinels and bound loader IDs must describe the same query. */
6974
+ function normalizeCalendarFilterId(value) {
6975
+ const id = value?.trim();
6976
+ return !id || id === "all" ? void 0 : id;
6977
+ }
6978
+ const calendarMonthSchema = z.string().regex(/^\d{4}-(0[1-9]|1[0-2])$/).refine((month) => Number(month.slice(0, 4)) > 0, "Invalid calendar year");
6979
+ function calendarMonthDate(month) {
6980
+ return {
6981
+ year: Number(month.slice(0, 4)),
6982
+ month: Number(month.slice(5, 7)),
6983
+ day: 1
6984
+ };
6985
+ }
6986
+ function calendarMonthKey(date) {
6987
+ return `${String(date.year).padStart(4, "0")}-${String(date.month).padStart(2, "0")}`;
6988
+ }
6989
+ const monthNumber = (month) => {
6990
+ const date = calendarMonthDate(month);
6991
+ return date.year * 12 + date.month - 1;
6992
+ };
6993
+ const calendarMonthWindowSchema = z.object({
6994
+ startMonth: calendarMonthSchema,
6995
+ endMonthExclusive: calendarMonthSchema
6996
+ }).refine((window) => {
6997
+ const width = monthNumber(window.endMonthExclusive) - monthNumber(window.startMonth);
6998
+ return width > 0 && width <= 3;
6999
+ }, "Calendar windows must cover one to three months");
7000
+ z.object({
7001
+ startMonth: calendarMonthSchema.optional(),
7002
+ endMonthExclusive: calendarMonthSchema.optional(),
7003
+ afterStartsAt: z.string().datetime({ offset: true }).optional(),
7004
+ afterId: z.string().uuid().optional(),
7005
+ limit: z.coerce.number().int().min(1).max(200).default(100),
7006
+ stage: z.enum(["published", "preview"]).default("published"),
7007
+ readProfile: z.enum(["listing-card", "timetable"]).optional(),
7008
+ eventSurface: z.enum(eventSurfaceScopeValues).optional(),
7009
+ eventPresets: z.string().optional(),
7010
+ scheduleScope: z.enum(eventScheduleScopeValues).optional(),
7011
+ categoryIds: z.string().optional(),
7012
+ venueId: z.string().transform(normalizeCalendarFilterId).optional(),
7013
+ staffMemberId: z.string().transform(normalizeCalendarFilterId).optional()
7014
+ }).superRefine((query, ctx) => {
7015
+ if (Boolean(query.startMonth) !== Boolean(query.endMonthExclusive)) ctx.addIssue({
7016
+ code: "custom",
7017
+ message: "Both month bounds are required"
7018
+ });
7019
+ if (query.startMonth && query.endMonthExclusive && !calendarMonthWindowSchema.safeParse(query).success) ctx.addIssue({
7020
+ code: "custom",
7021
+ message: "Invalid calendar window"
7022
+ });
7023
+ if (Boolean(query.afterStartsAt) !== Boolean(query.afterId)) ctx.addIssue({
7024
+ code: "custom",
7025
+ message: "Both cursor fields are required"
7026
+ });
7027
+ if (query.afterStartsAt && !query.startMonth) ctx.addIssue({
7028
+ code: "custom",
7029
+ message: "A continuation requires explicit month bounds"
7030
+ });
7031
+ });
7032
+ function normalizedCsv(value) {
7033
+ return [...new Set((value ?? "").split(",").map((part) => part.trim()).filter(Boolean))].sort().join(",");
7034
+ }
7035
+ /** Pagination and window bounds deliberately do not change the query's identity. */
7036
+ function calendarQueryKey(siteId, stage, filters) {
7037
+ return JSON.stringify([
7038
+ siteId,
7039
+ stage ?? "published",
7040
+ filters.eventSurface ?? "all",
7041
+ normalizedCsv(filters.eventPresets),
7042
+ filters.scheduleScope ?? "any",
7043
+ normalizedCsv(filters.categoryIds),
7044
+ normalizeCalendarFilterId(filters.venueId) ?? "",
7045
+ normalizeCalendarFilterId(filters.staffMemberId) ?? "",
7046
+ filters.readProfile ?? "listing-card"
7047
+ ]);
7048
+ }
7049
+ function calendarWindowMonths(window) {
7050
+ const first = calendarMonthDate(window.startMonth);
7051
+ return Array.from({ length: monthNumber(window.endMonthExclusive) - monthNumber(window.startMonth) }, (_, index) => calendarMonthKey(addMonths(first, index)));
7052
+ }
7053
+ function calendarWindowBounds(window, timeZone) {
7054
+ const start = buildBookingUtcDateWindow({
7055
+ startDate: formatLocalScheduleDate(calendarMonthDate(window.startMonth)),
7056
+ endDate: formatLocalScheduleDate(calendarMonthDate(window.startMonth)),
7057
+ timeZone
7058
+ });
7059
+ const end = buildBookingUtcDateWindow({
7060
+ startDate: formatLocalScheduleDate(calendarMonthDate(window.endMonthExclusive)),
7061
+ endDate: formatLocalScheduleDate(calendarMonthDate(window.endMonthExclusive)),
7062
+ timeZone
7063
+ });
7064
+ return {
7065
+ startInclusive: start.startAt.toISOString(),
7066
+ endExclusive: end.startAt.toISOString()
7067
+ };
7068
+ }
7069
+ const calendarSourceCursorSchema = z.object({
7070
+ startsAt: z.string().datetime({ offset: true }),
7071
+ occurrenceId: z.string().uuid()
7072
+ });
7073
+ z.discriminatedUnion("kind", [z.object({ kind: z.literal("complete") }), z.object({
7074
+ kind: z.literal("partial"),
7075
+ nextCursor: calendarSourceCursorSchema
7076
+ })]);
7077
+ const pageFields = {
7078
+ scope: z.object({
7079
+ key: z.string(),
7080
+ timeZone: z.string().transform(toBookingTimeZone)
7081
+ }),
7082
+ window: calendarMonthWindowSchema,
7083
+ initialToday: z.object({
7084
+ year: z.number().int(),
7085
+ month: z.number().int().min(1).max(12),
7086
+ day: z.number().int().min(1).max(31)
7087
+ }),
7088
+ events: z.array(publicEventSchema)
7089
+ };
7090
+ function calendarPageResponseError(page, expected) {
7091
+ if (page.scope.key !== expected.key || page.scope.timeZone !== expected.timeZone || page.window.startMonth !== expected.window.startMonth || page.window.endMonthExclusive !== expected.window.endMonthExclusive) return "scopeMismatch";
7092
+ if (page.kind === "complete" || !expected.cursor) return null;
7093
+ const difference = Date.parse(page.nextCursor.startsAt) - Date.parse(expected.cursor.startsAt);
7094
+ return difference < 0 || difference === 0 && page.nextCursor.occurrenceId <= expected.cursor.occurrenceId ? "nonAdvancingCursor" : null;
7095
+ }
7096
+ const calendarWindowPageSchema = z.discriminatedUnion("kind", [z.object({
7097
+ ...pageFields,
7098
+ kind: z.literal("complete")
7099
+ }), z.object({
7100
+ ...pageFields,
7101
+ kind: z.literal("partial"),
7102
+ nextCursor: calendarSourceCursorSchema
7103
+ })]);
7104
+ /** Split missing months without bridging covered months or exceeding the wire cap. */
7105
+ function planCalendarMonthRanges(months) {
7106
+ const sorted = [...new Set(months)].sort();
7107
+ const ranges = [];
7108
+ for (const month of sorted) {
7109
+ const previous = ranges.at(-1);
7110
+ const end = calendarMonthKey(addMonths(calendarMonthDate(month), 1));
7111
+ if (previous?.endMonthExclusive === month && monthNumber(end) - monthNumber(previous.startMonth) <= 3) previous.endMonthExclusive = end;
7112
+ else ranges.push({
7113
+ startMonth: month,
7114
+ endMonthExclusive: end
7115
+ });
7116
+ }
7117
+ return ranges;
7118
+ }
7119
+ //#endregion
7414
7120
  //#region ../blocks/src/system/runtime/nodes/events/shared/eventCollectionContract.ts
7415
7121
  const eventSurfaceScopeSchema$1 = z.enum(eventSurfaceScopeValues);
7416
7122
  const eventScheduleScopeSchema$1 = z.enum(eventScheduleScopeValues);
@@ -7427,6 +7133,7 @@ const eventCollectionFilterSourceSchemaShape = {
7427
7133
  filterVenueId: z.string().nullable().optional()
7428
7134
  };
7429
7135
  const eventCollectionSourceSchemaShape = {
7136
+ calendarWindow: calendarWindowPageSchema.nullable().optional(),
7430
7137
  events: z.array(publicEventSchema).nullable().optional(),
7431
7138
  siteId: z.string(),
7432
7139
  apiBaseUrl: z.string().optional(),
@@ -7480,6 +7187,7 @@ const eventCollectionDisplaySchemaShape = {
7480
7187
  };
7481
7188
  const eventCollectionHydrationSchema = z.object({
7482
7189
  siteId: z.string(),
7190
+ calendarWindow: calendarWindowPageSchema.optional(),
7483
7191
  stage: previewStageSchema.optional(),
7484
7192
  prefetchedEvents: z.boolean(),
7485
7193
  eventSurface: eventSurfaceScopeSchema$1,
@@ -7538,8 +7246,9 @@ function buildEventCollectionPresentationDisplay(source) {
7538
7246
  };
7539
7247
  }
7540
7248
  function buildEventCollectionHydration(source, now = /* @__PURE__ */ new Date()) {
7541
- const timeZone = toBookingTimeZone(source.bookingTimeZone);
7249
+ const timeZone = source.calendarWindow?.scope.timeZone ?? toBookingTimeZone(source.bookingTimeZone);
7542
7250
  return {
7251
+ calendarWindow: source.calendarWindow ?? void 0,
7543
7252
  siteId: source.siteId,
7544
7253
  stage: source.stage,
7545
7254
  prefetchedEvents: source.events != null,
@@ -7548,7 +7257,7 @@ function buildEventCollectionHydration(source, now = /* @__PURE__ */ new Date())
7548
7257
  scheduleScope: normalizeEventScheduleScope(source.scheduleScope),
7549
7258
  calendarContext: {
7550
7259
  timeZone,
7551
- initialToday: getLocalScheduleDate(now, timeZone)
7260
+ initialToday: source.calendarWindow?.initialToday ?? getLocalScheduleDate(now, timeZone)
7552
7261
  }
7553
7262
  };
7554
7263
  }
@@ -10264,7 +9973,9 @@ const unsupportedEventRegistrationPageSurfaceSubjectSchema = z.object({
10264
9973
  "unsupportedSubject",
10265
9974
  "invalidEventSeriesId",
10266
9975
  "invalidEventSeriesBookingScope",
10267
- "invalidEventOfferingId"
9976
+ "invalidEventOfferingId",
9977
+ "invalidEventOfferingBookingScope",
9978
+ "invalidEventOfferingCatalogue"
10268
9979
  ])
10269
9980
  });
10270
9981
  const eventRegistrationPageSurfaceSubjectSchema = z.unknown().transform((value, ctx) => {
@@ -10291,7 +10002,7 @@ function resolveEventRegistrationScope(context) {
10291
10002
  if (context.subject?.kind === "eventOffering") return {
10292
10003
  kind: "pageSurfaceOffering",
10293
10004
  offeringId: context.subject.eventOfferingId,
10294
- seriesIds: context.subject.availableEventSeriesIds
10005
+ seriesIds: context.subject.catalogueEventSeriesIds
10295
10006
  };
10296
10007
  if (context.subject?.kind === "unsupported") return {
10297
10008
  kind: "unsupportedPageSurfaceSubject",
@@ -10452,7 +10163,7 @@ function buildEventCalendarDisplay(source) {
10452
10163
  listColumns: source.listColumns ?? source.columns
10453
10164
  }),
10454
10165
  kind: "event-calendar",
10455
- displayMode: source.variant ?? source.displayMode ?? "combined",
10166
+ displayMode: normalizeEventCalendarDisplayMode(source) ?? "combined",
10456
10167
  className: source.className ?? null,
10457
10168
  events: (source.events ?? []).slice().sort((left, right) => new Date(left.startsAt).getTime() - new Date(right.startsAt).getTime()),
10458
10169
  boardTitle: normalizeOptionalText(source.boardTitle),
@@ -10498,7 +10209,10 @@ const eventCalendarInteractiveIslandPropsCodec = createInteractiveIslandCodec({
10498
10209
  render: createZodCodec(eventCalendarRenderSchema)
10499
10210
  },
10500
10211
  buildRender: (source) => ({
10501
- display: buildEventCalendarDisplay(source),
10212
+ display: buildEventCalendarDisplay(source.calendarWindow && (source.variant ?? source.displayMode ?? "combined") !== "combined" && (source.variant ?? source.displayMode) !== "list" ? {
10213
+ ...source,
10214
+ events: source.calendarWindow.events
10215
+ } : source),
10502
10216
  hydration: buildEventCollectionHydration(source)
10503
10217
  })
10504
10218
  }),
@@ -10559,54 +10273,6 @@ const eventCombinedInteractiveIslandPropsCodec = createInteractiveIslandCodec({
10559
10273
  })
10560
10274
  });
10561
10275
  //#endregion
10562
- //#region ../blocks/src/system/runtime/nodes/event-registration/registrationSurface.ts
10563
- function planEventRegistrationScope(input) {
10564
- const subject = input.subject;
10565
- const selectedRunOffering = input.wholeSeriesOffering;
10566
- if (subject?.kind !== "eventOffering" || !selectedRunOffering) return {
10567
- kind: "context",
10568
- events: resolveEventRegistrationScopedEvents(input.events, {
10569
- occurrenceContext: input.occurrenceContext,
10570
- subject
10571
- })
10572
- };
10573
- if (!(selectedRunOffering.eventOfferingId === subject.eventOfferingId && subject.availableEventSeriesIds.some((seriesId) => seriesId === selectedRunOffering.eventSeriesId))) return { kind: "invalidSelectedRun" };
10574
- return {
10575
- kind: "selectedRun",
10576
- events: input.events.filter((event) => event.seriesId === selectedRunOffering.eventSeriesId),
10577
- offering: selectedRunOffering
10578
- };
10579
- }
10580
- function planEventRegistrationSurface(input) {
10581
- const scope = planEventRegistrationScope(input);
10582
- if (scope.kind === "invalidSelectedRun") return { kind: "unavailableWholeSeries" };
10583
- const scopedEvents = scope.events;
10584
- const bookingScopes = new Set(scopedEvents.map((event) => event.bookingScope));
10585
- if (scope.kind === "selectedRun") {
10586
- if (bookingScopes.size !== 1 || !bookingScopes.has("whole_series")) return { kind: "unavailableWholeSeries" };
10587
- return {
10588
- kind: "wholeSeries",
10589
- offerings: [scope.offering]
10590
- };
10591
- }
10592
- if (!input.occurrenceContext && !input.subject && bookingScopes.has("single_occurrence")) return {
10593
- kind: "occurrence",
10594
- events: scopedEvents.filter((event) => event.bookingScope === "single_occurrence")
10595
- };
10596
- if (bookingScopes.size === 0 || bookingScopes.has("single_occurrence")) return bookingScopes.has("whole_series") ? { kind: "mixedBookingScopes" } : {
10597
- kind: "occurrence",
10598
- events: scopedEvents
10599
- };
10600
- const requiredSeriesIds = new Set(scopedEvents.map((event) => event.seriesId));
10601
- const wholeSeriesOffering = input.wholeSeriesOffering;
10602
- const offerings = (wholeSeriesOffering ? [wholeSeriesOffering, ...input.wholeSeriesOfferings.filter((offering) => offering.eventSeriesId !== wholeSeriesOffering.eventSeriesId)] : input.wholeSeriesOfferings).filter((offering) => requiredSeriesIds.has(offering.eventSeriesId));
10603
- const loadedSeriesIds = new Set(offerings.map((offering) => offering.eventSeriesId));
10604
- return [...requiredSeriesIds].every((seriesId) => loadedSeriesIds.has(seriesId)) && offerings.length > 0 ? {
10605
- kind: "wholeSeries",
10606
- offerings
10607
- } : { kind: "unavailableWholeSeries" };
10608
- }
10609
- //#endregion
10610
10276
  //#region ../blocks/src/system/runtime/nodes/event-registration/wizardSteps.ts
10611
10277
  /**
10612
10278
  * Steps that are part of the main wizard and should show the step indicator
@@ -10675,7 +10341,7 @@ function resolveEventRequiresPayment(input) {
10675
10341
  if (input.eligibility) return input.eligibility.requiresPayment;
10676
10342
  if (input.selectedTicketType) return input.selectedTicketType.priceAmount > 0;
10677
10343
  if (!input.event) return false;
10678
- return publicEventPricingType(resolveRuntimeEventPricing(input.event)) === "paid";
10344
+ return publicEventPricingType(input.event.pricing) === "paid";
10679
10345
  }
10680
10346
  function resolveDiscountContext(input) {
10681
10347
  const selection = input.paymentSelection.method ? input.paymentSelection : null;
@@ -10703,7 +10369,6 @@ function resolveDiscountContext(input) {
10703
10369
  function toRegistrationEvent(event) {
10704
10370
  return {
10705
10371
  ...event,
10706
- pricing: resolveRuntimeEventPricing(event),
10707
10372
  selectionId: event.id,
10708
10373
  registrationSubject: {
10709
10374
  kind: "occurrence",
@@ -10714,120 +10379,38 @@ function toRegistrationEvent(event) {
10714
10379
  function toRegistrationEvents(events) {
10715
10380
  return events.filter((event) => event.bookingScope === "single_occurrence").map(toRegistrationEvent);
10716
10381
  }
10717
- function toWholeSeriesTicketType(option) {
10718
- return {
10719
- ...option,
10720
- capacityMode: option.capacityLimit === null ? "unlimited" : "limited",
10721
- attendanceScope: option.attendanceScope ?? void 0,
10722
- availability: {
10723
- kind: "available",
10724
- remainingCapacity: option.capacityLimit
10725
- }
10726
- };
10727
- }
10728
- function toWholeSeriesPricing(offering) {
10729
- const resolved = resolveWholeSeriesPricingState(offering);
10730
- switch (offering.pricing.pricingStrategy) {
10731
- case "free": return {
10732
- kind: "free_registration",
10733
- currency: offering.currency,
10734
- display: {
10735
- kind: "free",
10736
- currency: offering.currency
10737
- }
10738
- };
10739
- case "single": {
10740
- const amount = offering.pricing.singlePriceAmount ?? 0;
10741
- return amount === 0 ? {
10742
- kind: "free_registration",
10743
- currency: offering.currency,
10744
- display: {
10745
- kind: "free",
10746
- currency: offering.currency
10747
- }
10748
- } : {
10749
- kind: "single_paid",
10750
- amount,
10751
- currency: offering.currency,
10752
- display: {
10753
- kind: "exact",
10754
- amount,
10755
- currency: offering.currency
10756
- }
10757
- };
10758
- }
10759
- case "tiered": return {
10760
- kind: "ticket_types",
10761
- currency: offering.currency,
10762
- ticketTypes: resolved.selectableOptions.map((option) => toWholeSeriesTicketType(option)),
10763
- display: resolved.display
10764
- };
10765
- case "sliding_scale": return {
10766
- kind: "sliding_scale",
10767
- minAmount: offering.pricing.slidingMinAmount ?? 0,
10768
- maxAmount: offering.pricing.slidingMaxAmount ?? 0,
10769
- suggestedAmount: offering.pricing.slidingSuggestedAmount ?? null,
10770
- currency: offering.currency,
10771
- display: {
10772
- kind: "range",
10773
- minAmount: offering.pricing.slidingMinAmount ?? 0,
10774
- maxAmount: offering.pricing.slidingMaxAmount ?? 0,
10775
- currency: offering.currency
10776
- }
10777
- };
10778
- }
10779
- }
10780
- function toAttendanceModes(offering) {
10781
- return (offering.deliveryMode === "hybrid" ? ["in_person", "online"] : [offering.deliveryMode]).map((mode) => ({
10782
- mode,
10783
- capacity: offering.enrollmentCapacity,
10784
- registeredCount: offering.enrolledCount,
10785
- availableSpots: offering.availableSpots,
10786
- waitlistEnabled: offering.waitlistEnabled
10787
- }));
10788
- }
10789
- function toWholeSeriesRegistrationEvent(offering) {
10790
- const firstSession = offering.sessions[0];
10791
- const lastSession = offering.sessions[offering.sessions.length - 1];
10382
+ /**
10383
+ * Project one whole-series run onto the registration surface.
10384
+ *
10385
+ * Capacity, pricing, payment terms and consent come straight off the canonical
10386
+ * Event record; only the schedule is widened from the single occurrence to the
10387
+ * live run. A run without live sessions has no registration event.
10388
+ */
10389
+ function toWholeSeriesRegistrationEvent(group) {
10390
+ const { representative, run } = group;
10391
+ const sessions = orderedLiveRunSessions(run.sessions);
10392
+ const firstSession = sessions[0];
10393
+ if (!firstSession) return null;
10394
+ const lastSession = sessions[sessions.length - 1] ?? firstSession;
10792
10395
  return {
10793
- selectionId: offering.eventSeriesId,
10396
+ ...representative,
10397
+ selectionId: run.eventSeriesId,
10794
10398
  registrationSubject: {
10795
10399
  kind: "wholeSeries",
10796
- eventSeriesId: offering.eventSeriesId,
10797
- sessions: offering.sessions
10798
- },
10799
- seriesId: offering.eventSeriesId,
10800
- title: offering.title,
10801
- startsAt: firstSession?.startsAt ?? "",
10802
- endsAt: lastSession?.endsAt ?? firstSession?.endsAt ?? "",
10803
- bookingAvailability: offering.enrollmentOpen ? { kind: "open" } : {
10804
- kind: "closed",
10805
- reason: "enrollment_closed"
10400
+ eventSeriesId: run.eventSeriesId,
10401
+ sessions
10806
10402
  },
10807
- deliveryMode: offering.deliveryMode,
10808
- attendanceModes: toAttendanceModes(offering),
10809
- capacity: offering.enrollmentCapacity,
10810
- registeredCount: offering.enrolledCount,
10811
- availableSpots: offering.availableSpots,
10812
- waitlistEnabled: offering.waitlistEnabled,
10813
- venue: offering.venue ? {
10814
- ...offering.venue,
10815
- location: null
10816
- } : null,
10817
- eventCategory: null,
10818
- teamMembers: offering.teamMembers,
10819
- status: offering.status,
10820
- timeZone: firstSession?.timeZone ?? "UTC",
10821
- pricing: toWholeSeriesPricing(offering),
10822
- paymentTerms: offering.paymentTerms,
10823
- consent: offering.consent,
10824
- requireGuestDetails: offering.requireGuestDetails,
10825
- acceptsPasses: false,
10826
- acceptsMemberships: offering.acceptsMemberships
10403
+ startsAt: firstSession.startsAt,
10404
+ endsAt: lastSession.endsAt,
10405
+ timeZone: firstSession.timeZone
10827
10406
  };
10828
10407
  }
10829
- function toWholeSeriesRegistrationEvents(offerings) {
10830
- return offerings.map(toWholeSeriesRegistrationEvent);
10408
+ function toWholeSeriesRegistrationEvents(events) {
10409
+ return groupPublicEventsByRun(events).map(toWholeSeriesRegistrationEvent).filter((event) => event !== null);
10410
+ }
10411
+ /** An occurrence-page context must not replace a whole run's booking start. */
10412
+ function getRegistrationStartAt(event, occurrenceContext) {
10413
+ return event?.registrationSubject.kind === "wholeSeries" ? event.startsAt : occurrenceContext?.startsAt ?? event?.startsAt;
10831
10414
  }
10832
10415
  //#endregion
10833
10416
  //#region ../blocks/src/system/runtime/nodes/event-registration.interactive.ts
@@ -10848,8 +10431,7 @@ const eventRegistrationHydrationSchemaShape = {
10848
10431
  occurrenceContext: occurrenceContextSchema.optional(),
10849
10432
  subject: eventRegistrationPageSurfaceSubjectSchema.nullable().optional(),
10850
10433
  events: z.array(eventRegistrationPublicEventSchema).nullable().optional(),
10851
- wholeSeriesOfferings: publicWholeSeriesOfferingsArraySchema.nullable().optional(),
10852
- wholeSeriesOffering: publicWholeSeriesOfferingSchema.nullable().optional(),
10434
+ wholeSeriesRunEvents: z.array(eventRegistrationPublicEventSchema).nullable().optional(),
10853
10435
  maxTickets: z.string().optional(),
10854
10436
  showVenue: z.boolean().optional(),
10855
10437
  showCapacity: z.boolean().optional(),
@@ -10867,7 +10449,6 @@ const eventRegistrationSourceSchema = z.object({
10867
10449
  portalToken: z.string().nullable().optional(),
10868
10450
  previewToken: z.string().nullable().optional(),
10869
10451
  ...eventRegistrationHydrationSchemaShape,
10870
- subjectWholeSeriesOffering: publicWholeSeriesOfferingSchema.nullable().optional(),
10871
10452
  className: z.string().nullable().optional(),
10872
10453
  buttonText: z.string().nullable().optional(),
10873
10454
  maxTickets: z.string().nullable().optional(),
@@ -10902,7 +10483,8 @@ const eventRegistrationIslandMetaCodec = createZodCodec(z.object({
10902
10483
  }));
10903
10484
  const eventRegistrationIslandMetaKeys = defineInteractiveIslandMetaKeys("siteId", "apiBaseUrl", "portalToken", "previewToken", "stage");
10904
10485
  function buildEventRegistrationDisplay(source) {
10905
- if (resolveWholeSeriesOffering(source)?.enrollmentOpen === false) return {
10486
+ const selectedRun = resolveSelectedWholeSeriesRun(source);
10487
+ if (selectedRun && (!selectedRun.run.enrollmentOpen || toWholeSeriesRegistrationEvent(selectedRun) === null)) return {
10906
10488
  kind: "event-registration",
10907
10489
  state: "registration-closed",
10908
10490
  className: source.className ?? null,
@@ -10920,13 +10502,11 @@ function buildEventRegistrationDisplay(source) {
10920
10502
  summaryButtonText: source.buttonText?.trim() || "Register"
10921
10503
  };
10922
10504
  }
10923
- function resolveWholeSeriesOffering(source) {
10924
- return source.wholeSeriesOffering ?? source.subjectWholeSeriesOffering ?? null;
10925
- }
10926
10505
  function buildEventRegistrationShellStepLabels(source) {
10927
- const selectedOffering = resolveWholeSeriesOffering(source);
10928
- if (selectedOffering) {
10929
- const selectedEvent = toWholeSeriesRegistrationEvent(selectedOffering);
10506
+ const selectedRun = resolveSelectedWholeSeriesRun(source);
10507
+ if (selectedRun) {
10508
+ const selectedEvent = toWholeSeriesRegistrationEvent(selectedRun);
10509
+ if (!selectedEvent) return EVENT_REGISTRATION_GENERIC_SHELL_STEP_LABELS;
10930
10510
  return getWizardStepsForContext(true, resolveEventRequiresPayment({
10931
10511
  eligibility: null,
10932
10512
  selectedTicketType: null,
@@ -10947,16 +10527,31 @@ function buildEventRegistrationShellStepLabels(source) {
10947
10527
  }) : false).map((step) => shellLabelForRegistrationStep(step, scopedEvents));
10948
10528
  }
10949
10529
  function scopeEventRegistrationEvents(source) {
10950
- return toRegistrationEvents(scopeEventRegistrationSourceEvents(source));
10530
+ const scoped = scopeEventRegistrationSourceEvents(source);
10531
+ return [...toRegistrationEvents(scoped), ...toWholeSeriesRegistrationEvents(scoped)];
10951
10532
  }
10533
+ /**
10534
+ * Every event this surface may register for, from both of its loads.
10535
+ *
10536
+ * The date page and the run read answer different questions and can name the
10537
+ * same occurrence, so the two lists are joined and any occurrence that arrives
10538
+ * twice is kept once, in the order the date page gave it.
10539
+ */
10952
10540
  function scopeEventRegistrationSourceEvents(source) {
10953
- const scope = planEventRegistrationScope({
10954
- events: source.events ?? [],
10955
- wholeSeriesOffering: source.wholeSeriesOffering,
10541
+ const byOccurrenceId = /* @__PURE__ */ new Map();
10542
+ for (const event of [...source.events ?? [], ...source.wholeSeriesRunEvents ?? []]) if (!byOccurrenceId.has(event.occurrenceId)) byOccurrenceId.set(event.occurrenceId, event);
10543
+ return resolveEventRegistrationScopedEvents([...byOccurrenceId.values()], {
10956
10544
  occurrenceContext: source.occurrenceContext,
10957
10545
  subject: source.subject
10958
10546
  });
10959
- return scope.kind === "invalidSelectedRun" ? [] : [...scope.events];
10547
+ }
10548
+ /**
10549
+ * The one run this surface is registering for, when the scoped events describe
10550
+ * exactly one. More than one run means the visitor still has to choose.
10551
+ */
10552
+ function resolveSelectedWholeSeriesRun(source) {
10553
+ const runs = groupPublicEventsByRun(scopeEventRegistrationSourceEvents(source));
10554
+ return runs.length === 1 ? runs[0] : null;
10960
10555
  }
10961
10556
  function shellLabelForRegistrationStep(step, events) {
10962
10557
  switch (step) {
@@ -10996,8 +10591,6 @@ const eventRegistrationBlockRuntime = defineClientOwnedBlockRuntime({
10996
10591
  occurrenceContext: source.occurrenceContext ?? null,
10997
10592
  subject: source.subject ?? null,
10998
10593
  events,
10999
- wholeSeriesOfferings: source.wholeSeriesOfferings ?? null,
11000
- wholeSeriesOffering: resolveWholeSeriesOffering(source),
11001
10594
  maxTickets: source.maxTickets ?? void 0,
11002
10595
  showVenue: source.showVenue,
11003
10596
  showCapacity: source.showCapacity,
@@ -11016,8 +10609,7 @@ function normalizeEventRegistrationEventInput(value) {
11016
10609
  if (!isRecord(value)) return value;
11017
10610
  return {
11018
10611
  ...value,
11019
- destination: resolveRuntimeEventDestination(value),
11020
- pricing: resolveRuntimeEventPricing(value)
10612
+ destination: resolveRuntimeEventDestination(value)
11021
10613
  };
11022
10614
  }
11023
10615
  function isRecord(value) {
@@ -12483,4 +12075,4 @@ function defineIslandPropsDecoder(kind) {
12483
12075
  };
12484
12076
  }
12485
12077
  //#endregion
12486
- export { getLocalMinuteOfDay as $, publicEventScalarPrice as $t, isSemanticSpacing as A, assertNever$1 as At, formatEventPrice as B, formatBookingDateTime as Bt, getStepNumber as C, toSelectedCollectionOptionKind as Ct, staticHeaderChromeRuntime as D, isBookingRequirementConsentFieldId as Dt, buildEventCalendarListingRender as E, BOOKING_FORM_DATA_RESERVED_KEYS as Et, normalizeCsvString as F, reportWorkflowError as Ft, getContainerClass as G, getLocalScheduleDate as Gt, formatEventTeacherLine as H, formatBookingTime as Ht, normalizeOptionalId as I, reportWorkflowSuccess as It, resolveEventLinkProps as J, toBookingTimeZone as Jt, groupEventsByDate as K, parseBookingLocalDate as Kt, formatDate as L, addLocalScheduleDays as Lt, buildEventRuntimeFilters as M, err as Mt, getEventRuntimeFiltersKey as N, ok as Nt, resolveEventRegistrationScopedEvents as O, RichTextDocSchema as Ot, normalizeCsvIds as P, okWith as Pt, getLocalDateKey as Q, publicEventPricingType as Qt, formatEventCompactDateRail as R, buildBookingUtcDateWindow as Rt, WIZARD_STEPS as S, resolveDefaultBookingServiceId as St, planEventRegistrationSurface as T, resolveSelectedAppointmentConsent as Tt, formatTime as U, formatLocalScheduleDate as Ut, formatEventPriceDisplay as V, formatBookingInstant as Vt, getCardOrientation as W, getBookingLocalDateKey as Wt, getEventAvailability as X, publicEventPricingCurrency as Xt, resolveRuntimeEventPricing as Y, toScheduledBookingDateTime as Yt, getEventAvailabilityStatus as Z, publicEventPricingMode as Zt, findEventByOccurrenceId as _, parseBookingCurrencyInputToCents as _t, buildProductDetailDisplay as a, resolveOnColorForThemeToken as at, resolveEventRequiresPayment as b, resolveBookingPaymentStep as bt, EMPTY_COMMERCE_CART_STATE as c, normalizeCardRole as ct, deriveCommerceCheckoutState as d, formatBookingCentsAsCurrencyInput as dt, publicEventTicketTypes as en, formatWholeSeriesPriceAmount as et, parseStoredCommerceCartState as f, formatBookingSubmissionPreparationError as ft, toWholeSeriesRegistrationEvents as g, isBookingServiceSlidingAmountInRange as gt, toRegistrationEvents as h, getBookingSuccessMessage as ht, buildProductCardDisplay as i, isCssColorLiteral as it, resolveSpacing as j, clearWorkflowFeedback as jt, SEMANTIC_SPACING_VALUES as k, isRecord$5 as kt, buildPassCartItem as l, logger as lt, reduceCommerceCart as m, getBookingServiceSlidingScale as mt, getShopDisplayForItems as n, assertNever$2 as nn, resolveWholeSeriesPricingState as nt, formatCommercePrice as o, resolveTokenOrCssColorValue as ot, productItemKey as p, getBookingRedirectStateFromLocation as pt, resolveEventCta as q, resolveBookingTimeZoneId as qt, buildCartDisplay as r, boxBackgroundSchema as rt, formatInterval as s, themeButtonClassName as st, defineIslandPropsDecoder as t, formatCurrencyAmount as tn, formatWholeSeriesPriceDisplay as tt, buildProductCartItem as u, deriveBookingFormViewState as ut, findPreSelectedEvent as v, prepareBookingSubmission as vt, getWizardStepsForContext as w, projectSelectedAppointmentConsentIntoForm as wt, resolveRegistrationTarget as x, resolveBookingSiteId as xt, resolveDiscountContext as y, resolveAppointmentCheckoutVerificationState as yt, formatEventDateParts as z, formatBookingDate as zt };
12078
+ export { getOrderedDayNames as $, toBookingTimeZone as $t, isSemanticSpacing as A, isBookingRequirementConsentFieldId as At, buildEventRuntimeFilters as B, CANCELLED_SESSION_STATUS as Bt, WIZARD_STEPS as C, resolveBookingPaymentStep as Ct, staticHeaderChromeRuntime as D, projectSelectedAppointmentConsentIntoForm as Dt, buildEventCalendarListingRender as E, toSelectedCollectionOptionKind as Et, calendarWindowBounds as F, err as Ft, getCardOrientation as G, formatBookingDateTime as Gt, normalizeCsvIds as H, addLocalScheduleDays as Ht, calendarWindowMonths as I, ok as It, addDays as J, formatLocalScheduleDate as Jt, getContainerClass as K, formatBookingInstant as Kt, calendarWindowPageSchema as L, okWith as Lt, calendarMonthDate as M, isRecord$4 as Mt, calendarPageResponseError as N, assertNever$1 as Nt, resolveEventRegistrationScopedEvents as O, resolveSelectedAppointmentConsent as Ot, calendarQueryKey as P, clearWorkflowFeedback as Pt, getDayOfWeek as Q, resolveBookingTimeZoneId as Qt, planCalendarMonthRanges as R, reportWorkflowError as Rt, resolveRegistrationTarget as S, resolveAppointmentCheckoutVerificationState as St, getWizardStepsForContext as T, resolveDefaultBookingServiceId as Tt, normalizeCsvString as U, formatBookingDate as Ut, getEventRuntimeFiltersKey as V, resolveWholeSeriesBookingPhase as Vt, normalizeOptionalId as W, formatBookingDateParts as Wt, buildCalendarViewport as X, getLocalScheduleDate as Xt, addMonths as Y, getBookingLocalDateKey as Yt, formatDateKey as Z, parseBookingLocalDate as Zt, toWholeSeriesRegistrationEvents as _, getBookingServiceSlidingScale as _t, buildProductDetailDisplay as a, publicEventPricingCurrency as an, groupPublicEventsByRun as at, resolveDiscountContext as b, parseBookingCurrencyInputToCents as bt, EMPTY_COMMERCE_CART_STATE as c, publicEventPricingStrategy as cn, resolveOnColorForThemeToken as ct, deriveCommerceCheckoutState as d, assertNever$2 as dn, normalizeCardRole as dt, toScheduledBookingDateRange as en, getWeekCalendarDays as et, parseStoredCommerceCartState as f, logger as ft, toRegistrationEvents as g, getBookingRedirectStateFromLocation as gt, getRegistrationStartAt as h, formatBookingSubmissionPreparationError as ht, buildProductCardDisplay as i, isPublicEventPricingOptionDisplayable as in, resolveRuntimeEventDestination as it, resolveSpacing as j, RichTextDocSchema as jt, SEMANTIC_SPACING_VALUES as k, BOOKING_FORM_DATA_RESERVED_KEYS as kt, buildPassCartItem as l, publicEventPricingType as ln, resolveTokenOrCssColorValue as lt, reduceCommerceCart as m, formatBookingCentsAsCurrencyInput as mt, getShopDisplayForItems as n, formatBookingPriceDisplay as nn, isToday as nt, formatCommercePrice as o, publicEventPricingDisplay as on, boxBackgroundSchema as ot, productItemKey as p, deriveBookingFormViewState as pt, MONTH_NAMES as q, formatBookingTime as qt, buildCartDisplay as r, formatEventPricing as rn, shiftCalendarAnchorDate as rt, formatInterval as s, publicEventPricingOptions as sn, isCssColorLiteral as st, defineIslandPropsDecoder as t, toScheduledBookingDateTime as tn, isSameCalendarDay as tt, buildProductCartItem as u, formatCurrencyAmount as un, themeButtonClassName as ut, findEventByOccurrenceId as v, getBookingSuccessMessage as vt, getStepNumber as w, resolveBookingSiteId as wt, resolveEventRequiresPayment as x, prepareBookingSubmission as xt, findPreSelectedEvent as y, isBookingServiceSlidingAmountInRange as yt, resolveCalendarReadProfile as z, reportWorkflowSuccess as zt };