@riverbankcms/sdk 0.138.0 → 0.139.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 (208) hide show
  1. package/README.md +11 -15
  2. package/dist/{PreviewEditorSidebar-BVPur_CU.mjs → PreviewEditorSidebar-BDeenhi3.mjs} +4 -4
  3. package/dist/{PreviewEditorUI-DLhWX8oh.mjs → PreviewEditorUI-DSRhgjF5.mjs} +3 -3
  4. package/dist/{PreviewQueryProvider-BguJY1SW.mjs → PreviewQueryProvider-D2K8GLUG.mjs} +1 -1
  5. package/dist/{SdkPreviewModeRuntime-Tda6qw4z.mjs → SdkPreviewModeRuntime-CL7wSnYu.mjs} +47 -27
  6. package/dist/{SiteChromeCustomizeContext-BHFgMjde.mjs → SiteChromeCustomizeContext-BhDn-5ir.mjs} +7771 -5863
  7. package/dist/_dts/ai/src/contracts/proposals.d.ts +99 -330
  8. package/dist/_dts/ai/src/contracts/siteStyleProposalSchemas.d.ts +9 -30
  9. package/dist/_dts/api/src/billing.d.ts +10 -3
  10. package/dist/_dts/api/src/contentRuntime.d.ts +8 -1
  11. package/dist/_dts/api/src/endpoints.d.ts +182 -103
  12. package/dist/_dts/api/src/footerSettings.d.ts +2 -5
  13. package/dist/_dts/api/src/index.d.ts +11 -9
  14. package/dist/_dts/api/src/mailchimpIntegration.d.ts +2 -59
  15. package/dist/_dts/api/src/mailingListIntegration.d.ts +117 -0
  16. package/dist/_dts/api/src/navigation/locations.d.ts +1 -0
  17. package/dist/_dts/api/src/navigation.d.ts +1 -26
  18. package/dist/_dts/api/src/newsletterEndpointDefinitions.d.ts +109 -2
  19. package/dist/_dts/api/src/onboarding/surfaceData.d.ts +185 -0
  20. package/dist/_dts/api/src/privacyPolicy.d.ts +74 -2
  21. package/dist/_dts/api/src/publicBookingAvailabilityEndpointDefinitions.d.ts +6 -6
  22. package/dist/_dts/api/src/publicEventEndpointDefinitions.d.ts +1 -1
  23. package/dist/_dts/api/src/publicExperience/newsletterEndpoints.d.ts +2 -4
  24. package/dist/_dts/api/src/publicExperience/sharedTypes.d.ts +10 -24
  25. package/dist/_dts/api/src/publicReadEndpointDefinitions.d.ts +73 -0
  26. package/dist/_dts/api/src/schemas/site-api-keys.d.ts +1 -1
  27. package/dist/_dts/api/src/sdkContracts.d.ts +2 -10
  28. package/dist/_dts/api/src/sdkThemeWire.d.ts +2 -2
  29. package/dist/_dts/api/src/siteManagementEndpoints.d.ts +0 -29
  30. package/dist/_dts/api/src/siteOnboarding.d.ts +91 -228
  31. package/dist/_dts/api/src/sitePlatformEndpoints.d.ts +1 -9
  32. package/dist/_dts/api/src/siteSettings.d.ts +20 -1
  33. package/dist/_dts/api/src/types.d.ts +5 -4
  34. package/dist/_dts/billing/src/membership/index.d.ts +14 -7
  35. package/dist/_dts/billing/src/plans/display.d.ts +15 -0
  36. package/dist/_dts/billing/src/plans/registry.d.ts +2 -0
  37. package/dist/_dts/billing/src/plans/standing.d.ts +19 -0
  38. package/dist/_dts/billing/src/plans/types.d.ts +1 -7
  39. package/dist/_dts/billing/src/subscriptions/evidence.d.ts +12 -0
  40. package/dist/_dts/billing/src/subscriptions/standingEvidence.d.ts +31 -0
  41. package/dist/_dts/billing/src/subscriptions/store.d.ts +23 -5
  42. package/dist/_dts/blocks/src/PageRenderer.d.ts +6 -15
  43. package/dist/_dts/blocks/src/client.d.ts +1 -1
  44. package/dist/_dts/blocks/src/contracts/proof-block-islands.d.ts +32 -16
  45. package/dist/_dts/blocks/src/pageRendererContracts.d.ts +4 -0
  46. package/dist/_dts/blocks/src/pageRenderingMode.d.ts +12 -6
  47. package/dist/_dts/blocks/src/system/blocks/appointment-services.d.ts +11 -0
  48. package/dist/_dts/blocks/src/system/blocks/contact-newsletter.d.ts +39 -66
  49. package/dist/_dts/blocks/src/system/blocks/newsletter-signup.d.ts +39 -114
  50. package/dist/_dts/blocks/src/system/blocks/offerings-preview/catalogue.d.ts +18 -0
  51. package/dist/_dts/blocks/src/system/blocks/offerings-preview/fields.d.ts +2 -0
  52. package/dist/_dts/blocks/src/system/blocks/offerings-preview/index.d.ts +4 -0
  53. package/dist/_dts/blocks/src/system/blocks/site-footer-runtime.d.ts +7 -13
  54. package/dist/_dts/blocks/src/system/constants/blockKinds.d.ts +1 -1
  55. package/dist/_dts/blocks/src/system/data/index.d.ts +2 -2
  56. package/dist/_dts/blocks/src/system/data/loaderEndpoints.d.ts +1 -1
  57. package/dist/_dts/blocks/src/system/data/publicLoaderCachePolicy.d.ts +2 -74
  58. package/dist/_dts/blocks/src/system/fragments/library/newsletterForm.d.ts +33 -100
  59. package/dist/_dts/blocks/src/system/runtime/api/types/newsletter.d.ts +5 -15
  60. package/dist/_dts/blocks/src/system/runtime/components/registry.client.d.ts +48 -24
  61. package/dist/_dts/blocks/src/system/runtime/components/runtime-backed-blocks.d.ts +24 -12
  62. package/dist/_dts/blocks/src/system/runtime/hooks/useCalendarEvents.d.ts +3 -0
  63. package/dist/_dts/blocks/src/system/runtime/hooks/useCalendarEvents.state.d.ts +1 -1
  64. package/dist/_dts/blocks/src/system/runtime/hooks/useCalendarToday.d.ts +22 -0
  65. package/dist/_dts/blocks/src/system/runtime/nodes/appointment-services.d.ts +51 -0
  66. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/useEventRegistrationWizard.d.ts +2 -0
  67. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration.interactive.d.ts +0 -1
  68. package/dist/_dts/blocks/src/system/runtime/nodes/events/EventCombined.client.d.ts +28 -0
  69. package/dist/_dts/blocks/src/system/runtime/nodes/events/domain/bookability.d.ts +9 -0
  70. package/dist/_dts/blocks/src/system/runtime/nodes/events/domain/dates.d.ts +14 -0
  71. package/dist/_dts/blocks/src/system/runtime/nodes/events/domain/duration.d.ts +2 -0
  72. package/dist/_dts/blocks/src/system/runtime/nodes/events/domain/index.d.ts +4 -0
  73. package/dist/_dts/blocks/src/system/runtime/nodes/events/domain/location.d.ts +2 -0
  74. package/dist/_dts/blocks/src/system/runtime/nodes/events/domain/quickDates.d.ts +16 -0
  75. package/dist/_dts/blocks/src/system/runtime/nodes/events/shared/AgendaEventCard.d.ts +9 -1
  76. package/dist/_dts/blocks/src/system/runtime/nodes/events/shared/CalendarDayCell.d.ts +2 -0
  77. package/dist/_dts/blocks/src/system/runtime/nodes/events/shared/CalendarDayHeaders.d.ts +4 -0
  78. package/dist/_dts/blocks/src/system/runtime/nodes/events/shared/DayEventsModal.d.ts +2 -1
  79. package/dist/_dts/blocks/src/system/runtime/nodes/events/shared/EventCard.d.ts +5 -0
  80. package/dist/_dts/blocks/src/system/runtime/nodes/events/shared/EventModals.d.ts +2 -1
  81. package/dist/_dts/blocks/src/system/runtime/nodes/events/shared/EventQuickDates.d.ts +15 -0
  82. package/dist/_dts/blocks/src/system/runtime/nodes/events/shared/WeekAgendaView.d.ts +7 -3
  83. package/dist/_dts/blocks/src/system/runtime/nodes/events/shared/WeekTimetableView.d.ts +1 -0
  84. package/dist/_dts/blocks/src/system/runtime/nodes/events/shared/index.d.ts +2 -0
  85. package/dist/_dts/blocks/src/system/runtime/nodes/events/shared/timetableModel.d.ts +1 -0
  86. package/dist/_dts/blocks/src/system/runtime/nodes/events/shared/useHydrationReady.d.ts +6 -0
  87. package/dist/_dts/blocks/src/system/runtime/nodes/newsletter-form.client.state.d.ts +3 -0
  88. package/dist/_dts/blocks/src/system/runtime/nodes/newsletter-form.interactive.d.ts +56 -33
  89. package/dist/_dts/blocks/src/system/runtime/nodes/newsletter-form.submission.d.ts +3 -3
  90. package/dist/_dts/blocks/src/system/runtime/nodes/newsletter-form.types.d.ts +13 -31
  91. package/dist/_dts/blocks/src/system/runtime/nodes/offerings-preview-catalogue.d.ts +14 -0
  92. package/dist/_dts/blocks/src/system/runtime/nodes/offerings-preview-content.d.ts +23 -0
  93. package/dist/_dts/blocks/src/system/runtime/nodes/offerings-preview.d.ts +4 -2
  94. package/dist/_dts/blocks/src/system/transforms/registry/formatting.d.ts +5 -0
  95. package/dist/_dts/commerce/src/purchase/index.d.ts +1 -0
  96. package/dist/_dts/commerce/src/purchase/orderItemMetadata.d.ts +5 -12
  97. package/dist/_dts/commerce/src/purchase/stripeCommissionMetadata.d.ts +34 -0
  98. package/dist/_dts/content-editor/src/footerEngagementDraft.d.ts +6 -24
  99. package/dist/_dts/content-editor/src/useFooterComposerState.d.ts +1 -2
  100. package/dist/_dts/core/src/footer-engagement.d.ts +2 -6
  101. package/dist/_dts/core/src/index.d.ts +9 -1
  102. package/dist/_dts/core/src/mailing-list-identifiers.d.ts +21 -0
  103. package/dist/_dts/core/src/mailing-list-runtime.d.ts +42 -0
  104. package/dist/_dts/core/src/mailing-list-setup-parser.d.ts +14 -0
  105. package/dist/_dts/core/src/mailing-list-setup.d.ts +129 -0
  106. package/dist/_dts/core/src/mailing-list-submission.d.ts +244 -0
  107. package/dist/_dts/core/src/mailing-list-targeting-parser.d.ts +12 -0
  108. package/dist/_dts/core/src/mailing-list-targeting.d.ts +93 -0
  109. package/dist/_dts/core/src/navigation-locations.d.ts +28 -0
  110. package/dist/_dts/core/src/public-profile.d.ts +9 -1
  111. package/dist/_dts/core/src/public-read-cache-contract.d.ts +45 -0
  112. package/dist/_dts/db/src/generated/supabase/database.types.d.ts +529 -373
  113. package/dist/_dts/editor-blocks/src/widgets/index.d.ts +1 -0
  114. package/dist/_dts/editor-blocks/src/widgets/mailing-list-targeting/MailingListTargetingField.d.ts +11 -0
  115. package/dist/_dts/editor-blocks/src/widgets/mailing-list-targeting/context.d.ts +13 -0
  116. package/dist/_dts/editor-blocks/src/widgets/mailing-list-targeting/index.d.ts +3 -0
  117. package/dist/_dts/editor-blocks/src/widgets/mailing-list-targeting/loaders.d.ts +6 -0
  118. package/dist/_dts/preview-next/src/client/widgets/widgetOverrides.d.ts +1 -0
  119. package/dist/_dts/sdk/src/client/publicAppointmentReadOperations.d.ts +12 -0
  120. package/dist/_dts/sdk/src/client/publicEventReadOperations.d.ts +2 -1
  121. package/dist/_dts/sdk/src/client/publicLoaderCacheAccess.d.ts +11 -4
  122. package/dist/_dts/sdk/src/config/content-types.d.ts +3 -13
  123. package/dist/_dts/sdk/src/config/content-validation.d.ts +5 -8
  124. package/dist/_dts/sdk/src/constants.d.ts +3 -3
  125. package/dist/_dts/sdk/src/contracts/index.d.ts +1 -1
  126. package/dist/_dts/sdk/src/contracts/system-block-content.d.ts +12 -31
  127. package/dist/_dts/sdk/src/navigation/index.d.ts +11 -29
  128. package/dist/_dts/sdk/src/next/index.d.ts +4 -4
  129. package/dist/_dts/sdk/src/prebuild/types.d.ts +1 -5
  130. package/dist/_dts/sdk/src/public-api/contracts.d.ts +33 -16
  131. package/dist/_dts/sdk/src/rendering/blocks-page-renderer.d.ts +3 -0
  132. package/dist/_dts/sdk/src/rendering/client.d.ts +3 -20
  133. package/dist/_dts/sdk/src/rendering/components/pageShared.d.ts +3 -0
  134. package/dist/_dts/sdk/src/rendering/helpers/loadContent.d.ts +4 -0
  135. package/dist/_dts/sdk/src/rendering/helpers/pageRuntimeSupport.d.ts +1 -0
  136. package/dist/_dts/sdk/src/rendering/index.d.ts +1 -1
  137. package/dist/_dts/sdk/src/types/block-kinds.d.ts +1 -1
  138. package/dist/_dts/sdk/src/version.d.ts +1 -1
  139. package/dist/_dts/theme-core/src/generated/containerResponsiveThemeCss.d.ts +1 -1
  140. package/dist/_dts/theme-core/src/hosted-booking-appearance.d.ts +41 -0
  141. package/dist/_dts/theme-core/src/index.d.ts +1 -0
  142. package/dist/_dts/theme-core/src/runtime/buildThemeRuntime.d.ts +0 -1
  143. package/dist/_dts/theme-core/src/tokens/tokens.d.ts +7 -0
  144. package/dist/_dts/ui/src/badge.d.ts +1 -1
  145. package/dist/_dts/ui/src/callout.d.ts +1 -1
  146. package/dist/client/{BookingConsentAgreement-DQUik1Mx.mjs → BookingConsentAgreement-CZvTDtsK.mjs} +2 -2
  147. package/dist/client/{DiscountCodeDisclosure-CU67Q4vH.mjs → DiscountCodeDisclosure-D-DsLCMU.mjs} +1 -1
  148. package/dist/client/{EventCombined.client-a02TXUgg.mjs → EventCombined.client-dWqgd5ZO.mjs} +443 -199
  149. package/dist/client/{EventListing.client-99qx7jFl.mjs → EventListing.client-BMRa9IYp.mjs} +2 -2
  150. package/dist/client/{LeafletStaticMap-CWzcq73L.mjs → LeafletStaticMap-D21W9kBZ.mjs} +1 -1
  151. package/dist/client/{accordion-D1nb3-Oj.mjs → accordion-FxF_ta47.mjs} +1 -1
  152. package/dist/client/{bookingForm-BGrmLhaO.mjs → bookingForm-YnwW4TGw.mjs} +5 -5
  153. package/dist/client/bookings.mjs +43 -2
  154. package/dist/client/{carousel-DSmOx3ad.mjs → carousel-vng7fFT5.mjs} +1 -1
  155. package/dist/client/{cart-COWeOIUk.mjs → cart-DEPDsjNk.mjs} +3 -3
  156. package/dist/client/{checkout-CeysaIJK.mjs → checkout-CZRWu9rW.mjs} +3 -3
  157. package/dist/client/client.mjs +3331 -1407
  158. package/dist/client/{context-DBzX85Xc.mjs → context-DvU_WUmd.mjs} +4 -3
  159. package/dist/client/{dates-DCYhZBlW.mjs → dates-wnPMiRl5.mjs} +21 -3
  160. package/dist/client/{eventCalendar-BgsN73Ul.mjs → eventCalendar-BA3Q5L15.mjs} +80 -78
  161. package/dist/client/{eventCombined-DcZQAMQP.mjs → eventCombined-51wgR-1R.mjs} +3 -3
  162. package/dist/client/{eventDetails-BWwGJ54l.mjs → eventDetails-modaetpm.mjs} +4 -4
  163. package/dist/client/{eventListing-DqYqV8vp.mjs → eventListing-D8t2J3Kv.mjs} +3 -3
  164. package/dist/client/{eventRegistration-DQGDgu_e.mjs → eventRegistration-CYqtkCE_.mjs} +49 -9
  165. package/dist/client/{form-ClfpasVT.mjs → form-CBKXN5In.mjs} +2 -2
  166. package/dist/client/{headerSection-BuOMe98N.mjs → headerSection-DylDqNrs.mjs} +2 -2
  167. package/dist/client/hooks.mjs +1259 -555
  168. package/dist/client/{locationMap-CEqu_sfa.mjs → locationMap-CvWrG0vZ.mjs} +2 -2
  169. package/dist/client/{newsletterForm-IbrHjMxy.mjs → newsletterForm-Btwk70zw.mjs} +117 -38
  170. package/dist/client/{pricing-xj7MPZlK.mjs → pricing-Uzw-GQ6Y.mjs} +1 -1
  171. package/dist/client/{productDetail-vxqTt5Xj.mjs → productDetail-JLcUYKnd.mjs} +3 -3
  172. package/dist/client/{productList-OTdZu8sb.mjs → productList-FM0cbUzj.mjs} +3 -3
  173. package/dist/client/{propsDecoder-CY9gGEF7.mjs → propsDecoder-DYzafZb2.mjs} +592 -152
  174. package/dist/client/rendering/client.mjs +2259 -835
  175. package/dist/client/rendering/islands.mjs +17 -17
  176. package/dist/client/rendering.mjs +3308 -1236
  177. package/dist/client/{scrollToBlock--BFV6kl-.mjs → scrollToBlock-CvXszexS.mjs} +1 -1
  178. package/dist/client/{shop-CP4YbDL9.mjs → shop-BIcjvy5a.mjs} +6 -6
  179. package/dist/client/{shop-commerce-APs4XBXE.mjs → shop-commerce-BZQsy7xw.mjs} +5 -5
  180. package/dist/client/{shop-commerce-store-CAVLxLFr.mjs → shop-commerce-store-D5V7XAb5.mjs} +1 -1
  181. package/dist/client/{shopShared-BnSRrwXI.mjs → shopShared-DowkXevB.mjs} +3 -3
  182. package/dist/client/{useEventFilters-1a1by4c8.mjs → useEventFilters-oMoTw_JE.mjs} +56 -9
  183. package/dist/preview-next/before-render.mjs +238 -111
  184. package/dist/preview-next/client/runtime.mjs +4 -4
  185. package/dist/preview-next/keepAlive.mjs +238 -111
  186. package/dist/preview-next/middleware.mjs +238 -111
  187. package/dist/{previewAuthStore-BkEgnYPP.mjs → previewAuthStore-COUyAKaX.mjs} +238 -111
  188. package/dist/{sdk-runtime-DwGfsT0S.mjs → sdk-runtime-D4k2LSGF.mjs} +2 -2
  189. package/dist/{separator-BeqDLneQ.mjs → separator-UGtpag4Z.mjs} +1 -1
  190. package/dist/server/components.mjs +2697 -1023
  191. package/dist/server/config-validation.mjs +2424 -1720
  192. package/dist/server/config.mjs +2429 -1724
  193. package/dist/server/data.mjs +1164 -454
  194. package/dist/server/index.mjs +364 -280
  195. package/dist/server/navigation.mjs +16 -47
  196. package/dist/server/next.mjs +2915 -1380
  197. package/dist/server/page-converter.mjs +969 -522
  198. package/dist/server/prebuild.mjs +2 -3
  199. package/dist/server/rendering/server.mjs +2704 -1030
  200. package/dist/server/rendering.mjs +2742 -1063
  201. package/dist/server/routing.mjs +1386 -595
  202. package/dist/server/server.mjs +2878 -2324
  203. package/dist/server/theme-bridge.mjs +292 -24
  204. package/dist/styles/index.css +289 -25
  205. package/package.json +1 -1
  206. package/dist/_dts/api/src/navigation/identity.d.ts +0 -59
  207. package/dist/_dts/blocks/src/system/blocks/newsletter-signup-destination.d.ts +0 -291
  208. package/dist/_dts/core/src/newsletter-signup-destination.d.ts +0 -38
@@ -16,12 +16,12 @@ function err(error) {
16
16
  }
17
17
  //#endregion
18
18
  //#region ../core/src/branded.ts
19
- const UUID_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
19
+ const UUID_PATTERN$1 = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
20
20
  function parseUuidBrand(value, unsafeConstructor) {
21
21
  if (typeof value !== "string") return err({ kind: "not_string" });
22
22
  const normalized = value.trim().toLowerCase();
23
23
  if (normalized.length === 0) return err({ kind: "empty_string" });
24
- if (!UUID_PATTERN.test(normalized)) return err({ kind: "invalid_uuid" });
24
+ if (!UUID_PATTERN$1.test(normalized)) return err({ kind: "invalid_uuid" });
25
25
  return ok(unsafeConstructor(normalized));
26
26
  }
27
27
  const unsafeAsSiteId = (id) => id;
@@ -266,7 +266,7 @@ function parseEventPageSurfaceDestinationMode(value) {
266
266
  return value === "cms-managed" || value === "system-managed" ? value : void 0;
267
267
  }
268
268
  function parseEventPageSurfaceSubject(value) {
269
- if (!isRecord$9(value)) return err({ kind: "notObject" });
269
+ if (!isRecord$12(value)) return err({ kind: "notObject" });
270
270
  switch (value.kind) {
271
271
  case "eventSeries": {
272
272
  const eventSeriesId = parseEventSeriesId(value.eventSeriesId);
@@ -321,7 +321,7 @@ function parseEventSeriesIdList(values) {
321
321
  }
322
322
  return ok(parsed);
323
323
  }
324
- function isRecord$9(value) {
324
+ function isRecord$12(value) {
325
325
  return typeof value === "object" && value !== null && !Array.isArray(value);
326
326
  }
327
327
  //#endregion
@@ -491,9 +491,379 @@ function toLocationCoordinates(value) {
491
491
  };
492
492
  }
493
493
  //#endregion
494
- //#region ../core/src/newsletter-signup-destination.ts
495
- function parseNewsletterSignupDestinationProvider(value) {
496
- return value === "riverbank" || value === "mailchimp" ? value : null;
494
+ //#region ../core/src/mailing-list-identifiers.ts
495
+ const UUID_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/iu;
496
+ const OPAQUE_ID_PATTERN = /^[A-Za-z0-9_-]{1,100}$/u;
497
+ function parseTrimmedMatchingString(value, pattern) {
498
+ if (typeof value !== "string") return null;
499
+ const trimmed = value.trim();
500
+ return pattern.test(trimmed) ? trimmed : null;
501
+ }
502
+ function parsePositiveInteger(value) {
503
+ return typeof value === "number" && Number.isSafeInteger(value) && value > 0 ? value : null;
504
+ }
505
+ function parseNewsletterListId(value) {
506
+ return parseTrimmedMatchingString(value, UUID_PATTERN);
507
+ }
508
+ function parseMailchimpAudienceId(value) {
509
+ return parseTrimmedMatchingString(value, OPAQUE_ID_PATTERN);
510
+ }
511
+ function parseMailchimpInterestId(value) {
512
+ return parseTrimmedMatchingString(value, OPAQUE_ID_PATTERN);
513
+ }
514
+ function parseBrevoListId(value) {
515
+ return parsePositiveInteger(value);
516
+ }
517
+ //#endregion
518
+ //#region ../core/src/mailing-list-setup-parser.ts
519
+ function parseMailingListGlobalForm(value) {
520
+ if (!isRecord$11(value)) return formError("shape");
521
+ const fields = parseGlobalFields(value.fields);
522
+ if (fields === null) return formError("fields");
523
+ if (typeof value.includeOptionalPhone !== "boolean") return formError("includeOptionalPhone");
524
+ const buttonLabel = parseRequiredString(value.buttonLabel);
525
+ if (buttonLabel === null) return formError("buttonLabel");
526
+ const privacyNote = parseOptionalString(value.privacyNote);
527
+ if (privacyNote === void 0) return formError("privacyNote");
528
+ return ok({
529
+ fields,
530
+ includeOptionalPhone: value.includeOptionalPhone,
531
+ buttonLabel,
532
+ privacyNote
533
+ });
534
+ }
535
+ function parseGlobalFields(value) {
536
+ if (!isRecord$11(value) || typeof value.preset !== "string") return null;
537
+ switch (value.preset) {
538
+ case "emailOnly": return { preset: "emailOnly" };
539
+ case "emailName": return isRequirement(value.name) ? {
540
+ preset: "emailName",
541
+ name: value.name
542
+ } : null;
543
+ case "emailFirstLast": return isRequirement(value.firstName) && isRequirement(value.lastName) ? {
544
+ preset: "emailFirstLast",
545
+ firstName: value.firstName,
546
+ lastName: value.lastName
547
+ } : null;
548
+ case "emailNameCompany": return isRequirement(value.name) && isRequirement(value.company) ? {
549
+ preset: "emailNameCompany",
550
+ name: value.name,
551
+ company: value.company
552
+ } : null;
553
+ default: return null;
554
+ }
555
+ }
556
+ function parseMailingListSetupRevision(value) {
557
+ return typeof value === "number" && Number.isSafeInteger(value) && value > 0 ? value : null;
558
+ }
559
+ function parseRequiredString(value) {
560
+ if (typeof value !== "string") return null;
561
+ const trimmed = value.trim();
562
+ return trimmed.length > 0 ? trimmed : null;
563
+ }
564
+ function parseOptionalString(value) {
565
+ if (value === null || value === void 0) return null;
566
+ if (typeof value !== "string") return void 0;
567
+ const trimmed = value.trim();
568
+ return trimmed.length > 0 ? trimmed : null;
569
+ }
570
+ function isRequirement(value) {
571
+ return value === "required" || value === "optional";
572
+ }
573
+ function isRecord$11(value) {
574
+ return typeof value === "object" && value !== null && !Array.isArray(value);
575
+ }
576
+ function formError(field) {
577
+ return err({
578
+ tag: "invalid_mailing_list_global_form",
579
+ field
580
+ });
581
+ }
582
+ //#endregion
583
+ //#region ../core/src/mailing-list-targeting.ts
584
+ function planMailingListChoicePresentation(targeting) {
585
+ if (targeting.mode === "default" || targeting.selection.tag === "fixed") return { tag: "hidden" };
586
+ switch (targeting.provider) {
587
+ case "native": return targeting.selection.offeredTopicIds.length === 1 ? {
588
+ tag: "automatic",
589
+ selection: {
590
+ provider: "native",
591
+ topicIds: targeting.selection.offeredTopicIds
592
+ }
593
+ } : {
594
+ tag: "visitor_choice",
595
+ provider: "native",
596
+ optionCount: targeting.selection.offeredTopicIds.length
597
+ };
598
+ case "brevo": return targeting.selection.offeredListIds.length === 1 ? {
599
+ tag: "automatic",
600
+ selection: {
601
+ provider: "brevo",
602
+ listIds: targeting.selection.offeredListIds
603
+ }
604
+ } : {
605
+ tag: "visitor_choice",
606
+ provider: "brevo",
607
+ optionCount: targeting.selection.offeredListIds.length
608
+ };
609
+ case "mailchimp": return targeting.selection.offeredInterestIds.length === 1 ? {
610
+ tag: "automatic",
611
+ selection: {
612
+ provider: "mailchimp",
613
+ interestIds: targeting.selection.offeredInterestIds
614
+ }
615
+ } : {
616
+ tag: "visitor_choice",
617
+ provider: "mailchimp",
618
+ optionCount: targeting.selection.offeredInterestIds.length
619
+ };
620
+ }
621
+ }
622
+ //#endregion
623
+ //#region ../core/src/mailing-list-runtime.ts
624
+ function parseMailingListRuntimeContext(value) {
625
+ if (!isRecord$10(value)) return invalidRuntimeContext();
626
+ if (value.tag === "not_configured") return ok({ tag: "not_configured" });
627
+ if (value.tag !== "configured") return invalidRuntimeContext();
628
+ const revision = parseMailingListSetupRevision(value.revision);
629
+ const form = parseMailingListGlobalForm(value.form);
630
+ const provider = parseRuntimeProvider(value.provider);
631
+ if (revision === null || !form.ok || provider === null) return invalidRuntimeContext();
632
+ return ok({
633
+ tag: "configured",
634
+ revision,
635
+ form: form.value,
636
+ provider
637
+ });
638
+ }
639
+ function parseRuntimeProvider(value) {
640
+ if (!isRecord$10(value)) return null;
641
+ switch (value.provider) {
642
+ case "native": return parseNativeRuntimeProvider(value);
643
+ case "brevo": return parseBrevoRuntimeProvider(value);
644
+ case "mailchimp": return parseMailchimpRuntimeProvider(value);
645
+ default: return null;
646
+ }
647
+ }
648
+ function parseNativeRuntimeProvider(value) {
649
+ const defaultTopicId = parseNewsletterListId(value.defaultTopicId);
650
+ const topics = parseNamedItems(value.topics, parseNewsletterListId);
651
+ return defaultTopicId !== null && topics !== null ? {
652
+ provider: "native",
653
+ defaultTopicId,
654
+ topics
655
+ } : null;
656
+ }
657
+ function parseBrevoRuntimeProvider(value) {
658
+ const defaultListId = parseBrevoListId(value.defaultListId);
659
+ const lists = parseNamedItems(value.lists, parseBrevoListId);
660
+ return defaultListId !== null && lists !== null ? {
661
+ provider: "brevo",
662
+ defaultListId,
663
+ lists
664
+ } : null;
665
+ }
666
+ function parseMailchimpRuntimeProvider(value) {
667
+ if (!Array.isArray(value.audiences)) return null;
668
+ const defaultAudienceId = parseMailchimpAudienceId(value.defaultAudienceId);
669
+ if (defaultAudienceId === null) return null;
670
+ const audiences = [];
671
+ for (const audience of value.audiences) {
672
+ if (!isRecord$10(audience)) return null;
673
+ const id = parseMailchimpAudienceId(audience.id);
674
+ const name = parseName(audience.name);
675
+ if (id === null || name === null || !Array.isArray(audience.interests)) return null;
676
+ const interests = parseMailchimpInterests(audience.interests);
677
+ if (interests === null) return null;
678
+ audiences.push({
679
+ id,
680
+ name,
681
+ interests
682
+ });
683
+ }
684
+ return {
685
+ provider: "mailchimp",
686
+ defaultAudienceId,
687
+ audiences
688
+ };
689
+ }
690
+ function parseMailchimpInterests(value) {
691
+ const interests = [];
692
+ for (const interest of value) {
693
+ if (!isRecord$10(interest)) return null;
694
+ const id = parseMailchimpInterestId(interest.id);
695
+ const name = parseName(interest.name);
696
+ const categoryName = parseName(interest.categoryName);
697
+ if (id === null || name === null || categoryName === null) return null;
698
+ interests.push({
699
+ id,
700
+ name,
701
+ categoryName
702
+ });
703
+ }
704
+ return interests;
705
+ }
706
+ function parseNamedItems(value, parseId) {
707
+ if (!Array.isArray(value)) return null;
708
+ const items = [];
709
+ for (const item of value) {
710
+ if (!isRecord$10(item)) return null;
711
+ const id = parseId(item.id);
712
+ const name = parseName(item.name);
713
+ if (id === null || name === null) return null;
714
+ items.push({
715
+ id,
716
+ name
717
+ });
718
+ }
719
+ return items;
720
+ }
721
+ function parseName(value) {
722
+ if (typeof value !== "string") return null;
723
+ const name = value.trim();
724
+ return name.length > 0 ? name : null;
725
+ }
726
+ function isRecord$10(value) {
727
+ return typeof value === "object" && value !== null && !Array.isArray(value);
728
+ }
729
+ function invalidRuntimeContext() {
730
+ return err({ tag: "invalid_mailing_list_runtime_context" });
731
+ }
732
+ //#endregion
733
+ //#region ../core/src/mailing-list-targeting-parser.ts
734
+ function parseMailingListPlacementTargeting(value) {
735
+ if (!isRecord$9(value)) return targetingError("shape");
736
+ if (value.mode === "default") return ok({ mode: "default" });
737
+ if (value.mode !== "custom") return targetingError("mode");
738
+ if (!isRecord$9(value.selection)) return targetingError("selection");
739
+ switch (value.provider) {
740
+ case "native": return parseNativeTargeting(value.selection);
741
+ case "brevo": return parseBrevoTargeting(value.selection);
742
+ case "mailchimp": return parseMailchimpTargeting(value, value.selection);
743
+ default: return targetingError("provider");
744
+ }
745
+ }
746
+ function parseNativeTargeting(selection) {
747
+ if (selection.tag === "fixed") {
748
+ const topicIds = parseNonEmptyUniqueIds(selection.topicIds, parseNewsletterListId);
749
+ return topicIds.ok ? ok({
750
+ mode: "custom",
751
+ provider: "native",
752
+ selection: {
753
+ tag: "fixed",
754
+ topicIds: topicIds.value
755
+ }
756
+ }) : topicIds;
757
+ }
758
+ if (selection.tag === "visitor_choice") {
759
+ const offeredTopicIds = parseNonEmptyUniqueIds(selection.offeredTopicIds, parseNewsletterListId);
760
+ return offeredTopicIds.ok ? ok({
761
+ mode: "custom",
762
+ provider: "native",
763
+ selection: {
764
+ tag: "visitor_choice",
765
+ offeredTopicIds: offeredTopicIds.value
766
+ }
767
+ }) : offeredTopicIds;
768
+ }
769
+ return targetingError("selection");
770
+ }
771
+ function parseBrevoTargeting(selection) {
772
+ if (selection.tag === "fixed") {
773
+ const listIds = parseNonEmptyUniqueIds(selection.listIds, parseBrevoListId);
774
+ return listIds.ok ? ok({
775
+ mode: "custom",
776
+ provider: "brevo",
777
+ selection: {
778
+ tag: "fixed",
779
+ listIds: listIds.value
780
+ }
781
+ }) : listIds;
782
+ }
783
+ if (selection.tag === "visitor_choice") {
784
+ const offeredListIds = parseNonEmptyUniqueIds(selection.offeredListIds, parseBrevoListId);
785
+ return offeredListIds.ok ? ok({
786
+ mode: "custom",
787
+ provider: "brevo",
788
+ selection: {
789
+ tag: "visitor_choice",
790
+ offeredListIds: offeredListIds.value
791
+ }
792
+ }) : offeredListIds;
793
+ }
794
+ return targetingError("selection");
795
+ }
796
+ function parseMailchimpTargeting(value, selection) {
797
+ const audienceId = parseMailchimpAudienceId(value.audienceId);
798
+ if (audienceId === null) return targetingError("audienceId");
799
+ if (selection.tag === "fixed") {
800
+ const interestIds = parseUniqueIds(selection.interestIds, parseMailchimpInterestId);
801
+ return interestIds.ok ? ok({
802
+ mode: "custom",
803
+ provider: "mailchimp",
804
+ audienceId,
805
+ selection: {
806
+ tag: "fixed",
807
+ interestIds: interestIds.value
808
+ }
809
+ }) : interestIds;
810
+ }
811
+ if (selection.tag === "visitor_choice") {
812
+ const offeredInterestIds = parseNonEmptyUniqueIds(selection.offeredInterestIds, parseMailchimpInterestId);
813
+ return offeredInterestIds.ok ? ok({
814
+ mode: "custom",
815
+ provider: "mailchimp",
816
+ audienceId,
817
+ selection: {
818
+ tag: "visitor_choice",
819
+ offeredInterestIds: offeredInterestIds.value
820
+ }
821
+ }) : offeredInterestIds;
822
+ }
823
+ return targetingError("selection");
824
+ }
825
+ function parseNonEmptyUniqueIds(value, parseId) {
826
+ const parsed = parseUniqueIds(value, parseId);
827
+ if (!parsed.ok) return parsed;
828
+ const [first, ...rest] = parsed.value;
829
+ return first === void 0 ? targetingError("targets") : ok([first, ...rest]);
830
+ }
831
+ function parseUniqueIds(value, parseId) {
832
+ if (!Array.isArray(value)) return targetingError("targets");
833
+ const parsed = [];
834
+ for (const item of value) {
835
+ const id = parseId(item);
836
+ if (id === null) return targetingError("targets");
837
+ if (parsed.includes(id)) return targetingError("duplicate_target");
838
+ parsed.push(id);
839
+ }
840
+ return ok(parsed);
841
+ }
842
+ function isRecord$9(value) {
843
+ return typeof value === "object" && value !== null && !Array.isArray(value);
844
+ }
845
+ function targetingError(field) {
846
+ return err({
847
+ tag: "invalid_mailing_list_targeting",
848
+ field
849
+ });
850
+ }
851
+ //#endregion
852
+ //#region ../core/src/mailing-list-submission.ts
853
+ function parseMailingListBlockId(value) {
854
+ const parsed = typeof value === "string" ? value.trim() : "";
855
+ return /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/iu.test(parsed) ? parsed : null;
856
+ }
857
+ function parseMailingListSignedSourceToken(value) {
858
+ const parsed = typeof value === "string" ? value.trim() : "";
859
+ return /^[A-Za-z0-9._~-]{16,1000}$/u.test(parsed) ? parsed : null;
860
+ }
861
+ function buildMailingListSignupBlockSourceRef(blockId) {
862
+ const parsed = parseMailingListBlockId(blockId);
863
+ return parsed !== null ? {
864
+ tag: "block_ref",
865
+ blockId: parsed
866
+ } : null;
497
867
  }
498
868
  //#endregion
499
869
  //#region ../core/src/secret.ts
@@ -555,6 +925,56 @@ function sessionStartMs(session) {
555
925
  function orderedLiveRunSessions(sessions) {
556
926
  return sessions.filter((session) => session.status !== "cancelled" && Number.isFinite(sessionStartMs(session))).sort((left, right) => sessionStartMs(left) - sessionStartMs(right));
557
927
  }
928
+ const PUBLIC_CONTENT_FRESHNESS_SECONDS = 3600;
929
+ function availability(transport) {
930
+ return {
931
+ kind: "live",
932
+ reason: "booking_correctness",
933
+ freshness: "availability",
934
+ transport
935
+ };
936
+ }
937
+ function content(tags, transport = "direct") {
938
+ return {
939
+ kind: "tagged",
940
+ tags,
941
+ freshness: "content",
942
+ transport
943
+ };
944
+ }
945
+ /** The single classification authority for cacheable public reads. */
946
+ const PUBLIC_READ_CACHE_POLICIES = {
947
+ listPublishedEntries: content(["published_content"]),
948
+ getPublishedEntryPreview: content(["published_content"]),
949
+ listPublicEvents: availability("direct"),
950
+ listPublicEventCalendarWindow: availability("direct"),
951
+ listPublicRecurringSchedule: availability("batch_only"),
952
+ getPublicEventDetails: availability("batch_only"),
953
+ resolveEventSubroute: availability("direct"),
954
+ listPublicRouteCandidates: content(["published_content"], "batch_only"),
955
+ listPublicTeamMembers: content(["public_team_members"]),
956
+ listPublicProducts: availability("direct"),
957
+ listPublicPasses: availability("direct"),
958
+ listPublicMemberships: availability("direct"),
959
+ getPublicFormById: content(["site_forms"]),
960
+ getPublicBookingServices: availability("direct"),
961
+ getPublicBookingService: availability("direct"),
962
+ getPublicBookingResourcesReference: availability("direct"),
963
+ getPublicBookingAvailableDates: availability("direct"),
964
+ getPublicBookingAvailableSlots: availability("direct"),
965
+ getPublicWeeklySlotAvailability: availability("direct"),
966
+ listPublicForms: content(["site_forms"]),
967
+ checkRedirect: content([]),
968
+ getPublicRoutableContent: content(["published_content"]),
969
+ getPublicSitemapRoutes: content(["published_content"])
970
+ };
971
+ new Set(Object.keys(PUBLIC_READ_CACHE_POLICIES));
972
+ function getPublicReadCachePolicy(endpoint) {
973
+ return PUBLIC_READ_CACHE_POLICIES[endpoint];
974
+ }
975
+ function publicReadFreshnessSeconds(endpoint) {
976
+ return getPublicReadCachePolicy(endpoint).freshness === "availability" ? 60 : PUBLIC_CONTENT_FRESHNESS_SECONDS;
977
+ }
558
978
  //#endregion
559
979
  //#region ../core/src/event-series-behaviour.ts
560
980
  const WHOLE_SERIES_EVENT_PRESET_VALUES = ["course", "term"];
@@ -7761,6 +8181,14 @@ const calendarWindowPageSchema = z.discriminatedUnion("kind", [z.object({
7761
8181
  kind: z.literal("partial"),
7762
8182
  nextCursor: calendarSourceCursorSchema
7763
8183
  })]);
8184
+ //#endregion
8185
+ //#region ../api/src/apiEndpointTypes.ts
8186
+ function allowedDemoMutationPolicy() {
8187
+ return {
8188
+ classification: "allowed",
8189
+ publicMessage: ""
8190
+ };
8191
+ }
7764
8192
  function blockedDemoMutationPolicy(classification, publicMessage) {
7765
8193
  if (publicMessage.trim() === "") throw new Error(`Demo mutation policy ${classification} requires publicMessage.`);
7766
8194
  return {
@@ -7773,6 +8201,7 @@ function blockedDemoMutationPolicy(classification, publicMessage) {
7773
8201
  const DEMO_MUTATION_BLOCKED_HIDDEN = blockedDemoMutationPolicy("blocked-hidden", "This action is not available in demo sites.");
7774
8202
  const DEMO_MUTATION_BLOCKED_DISABLED = blockedDemoMutationPolicy("blocked-disabled", "This setting is disabled in demo sites.");
7775
8203
  blockedDemoMutationPolicy("internal-only", "This internal operation is not available in demo sites.");
8204
+ const DEMO_MUTATION_ALLOWED = allowedDemoMutationPolicy();
7776
8205
  //#endregion
7777
8206
  //#region ../api/src/cache-tags.ts
7778
8207
  /**
@@ -8704,14 +9133,117 @@ const NEWSLETTER_ENDPOINT_DEFINITIONS = defineEndpointDefinitions({
8704
9133
  responseKind: "json",
8705
9134
  errors: ["auth:forbidden", "server:internal_error"]
8706
9135
  },
8707
- getMailchimpSignupSyncHealth: {
8708
- path: "/sites/{siteId}/integrations/mailchimp/signup-sync-health",
9136
+ getMailingListSetup: {
9137
+ path: "/sites/{siteId}/mailing-list/setup",
9138
+ method: "GET",
9139
+ tags: [TAG_TEMPLATES.site, TAG_TEMPLATES.siteNewsletter],
9140
+ auth: "user",
9141
+ responseKind: "json",
9142
+ errors: ["auth:forbidden", "server:internal_error"]
9143
+ },
9144
+ getMailingListAuthoringContext: {
9145
+ path: "/sites/{siteId}/mailing-list/authoring-context",
9146
+ method: "GET",
9147
+ tags: [TAG_TEMPLATES.site, TAG_TEMPLATES.siteNewsletter],
9148
+ auth: "user",
9149
+ responseKind: "json",
9150
+ errors: ["auth:forbidden", "server:internal_error"]
9151
+ },
9152
+ previewMailingListSetupChange: {
9153
+ path: "/sites/{siteId}/mailing-list/setup/preview",
9154
+ method: "POST",
9155
+ tags: [TAG_TEMPLATES.site, TAG_TEMPLATES.siteNewsletter],
9156
+ auth: "user",
9157
+ responseKind: "json",
9158
+ demoMutation: DEMO_MUTATION_ALLOWED,
9159
+ errors: [
9160
+ "auth:forbidden",
9161
+ "validation:invalid_input",
9162
+ "server:internal_error"
9163
+ ]
9164
+ },
9165
+ applyMailingListSetupChange: {
9166
+ path: "/sites/{siteId}/mailing-list/setup",
9167
+ method: "PUT",
9168
+ tags: [TAG_TEMPLATES.site, TAG_TEMPLATES.siteNewsletter],
9169
+ auth: "user",
9170
+ responseKind: "json",
9171
+ demoMutation: DEMO_MUTATION_BLOCKED_DISABLED,
9172
+ errors: [
9173
+ "auth:forbidden",
9174
+ "validation:invalid_input",
9175
+ "resource:conflict",
9176
+ "server:internal_error"
9177
+ ]
9178
+ },
9179
+ getMailingListProviderCatalog: {
9180
+ path: "/sites/{siteId}/mailing-list/providers/{provider}/catalog",
9181
+ method: "GET",
9182
+ tags: [TAG_TEMPLATES.site, TAG_TEMPLATES.siteNewsletter],
9183
+ auth: "user",
9184
+ responseKind: "json",
9185
+ errors: [
9186
+ "auth:forbidden",
9187
+ "validation:invalid_input",
9188
+ "server:internal_error"
9189
+ ]
9190
+ },
9191
+ refreshMailingListProviderCatalog: {
9192
+ path: "/sites/{siteId}/mailing-list/providers/{provider}/catalog/refresh",
9193
+ method: "POST",
9194
+ tags: [TAG_TEMPLATES.site, TAG_TEMPLATES.siteNewsletter],
9195
+ auth: "user",
9196
+ responseKind: "json",
9197
+ demoMutation: DEMO_MUTATION_BLOCKED_DISABLED,
9198
+ errors: [
9199
+ "auth:forbidden",
9200
+ "validation:invalid_input",
9201
+ "server:internal_error"
9202
+ ]
9203
+ },
9204
+ getMailingListProviderHealth: {
9205
+ path: "/sites/{siteId}/mailing-list/health",
9206
+ method: "GET",
9207
+ tags: [TAG_TEMPLATES.site, TAG_TEMPLATES.siteNewsletter],
9208
+ auth: "user",
9209
+ responseKind: "json",
9210
+ errors: ["auth:forbidden", "server:internal_error"]
9211
+ },
9212
+ getBrevoConnection: {
9213
+ path: "/sites/{siteId}/integrations/brevo/connection",
8709
9214
  method: "GET",
8710
9215
  tags: [TAG_TEMPLATES.site],
8711
9216
  auth: "user",
8712
9217
  responseKind: "json",
8713
9218
  errors: ["auth:forbidden", "server:internal_error"]
8714
9219
  },
9220
+ connectBrevo: {
9221
+ path: "/sites/{siteId}/integrations/brevo/connection",
9222
+ method: "PUT",
9223
+ tags: [TAG_TEMPLATES.site],
9224
+ auth: "user",
9225
+ responseKind: "json",
9226
+ demoMutation: DEMO_MUTATION_BLOCKED_DISABLED,
9227
+ errors: [
9228
+ "auth:forbidden",
9229
+ "validation:invalid_input",
9230
+ "resource:conflict",
9231
+ "server:internal_error"
9232
+ ]
9233
+ },
9234
+ disconnectBrevo: {
9235
+ path: "/sites/{siteId}/integrations/brevo/disconnect",
9236
+ method: "POST",
9237
+ tags: [TAG_TEMPLATES.site],
9238
+ auth: "user",
9239
+ responseKind: "json",
9240
+ demoMutation: DEMO_MUTATION_BLOCKED_DISABLED,
9241
+ errors: [
9242
+ "auth:forbidden",
9243
+ "resource:conflict",
9244
+ "server:internal_error"
9245
+ ]
9246
+ },
8715
9247
  getMailchimpConnection: {
8716
9248
  path: "/sites/{siteId}/integrations/mailchimp/connection",
8717
9249
  method: "GET",
@@ -8967,7 +9499,7 @@ const PUBLIC_EVENT_ENDPOINT_DEFINITIONS = defineEndpointDefinitions({
8967
9499
  resolveEventSubroute: {
8968
9500
  path: "/public/sites/{siteId}/events/subroutes/resolve",
8969
9501
  method: "GET",
8970
- revalidate: 60,
9502
+ revalidate: publicReadFreshnessSeconds("resolveEventSubroute"),
8971
9503
  tags: [TAG_TEMPLATES.publicEvents, "event-subroute"],
8972
9504
  auth: "public",
8973
9505
  responseKind: "json",
@@ -9020,7 +9552,7 @@ const PUBLIC_BOOKING_AVAILABILITY_ENDPOINT_DEFINITIONS = defineEndpointDefinitio
9020
9552
  getPublicBookingServices: {
9021
9553
  path: "/public/bookings/services",
9022
9554
  method: "GET",
9023
- revalidate: 60,
9555
+ revalidate: publicReadFreshnessSeconds("getPublicBookingServices"),
9024
9556
  tags: [TAG_TEMPLATES.publicBookingServices],
9025
9557
  auth: "public",
9026
9558
  responseKind: "json"
@@ -9028,7 +9560,7 @@ const PUBLIC_BOOKING_AVAILABILITY_ENDPOINT_DEFINITIONS = defineEndpointDefinitio
9028
9560
  getPublicBookingService: {
9029
9561
  path: "/public/bookings/services/{serviceId}",
9030
9562
  method: "GET",
9031
- revalidate: 60,
9563
+ revalidate: publicReadFreshnessSeconds("getPublicBookingService"),
9032
9564
  tags: [TAG_TEMPLATES.publicBookingServices],
9033
9565
  auth: "public",
9034
9566
  responseKind: "json"
@@ -9036,7 +9568,7 @@ const PUBLIC_BOOKING_AVAILABILITY_ENDPOINT_DEFINITIONS = defineEndpointDefinitio
9036
9568
  getPublicBookingResourcesReference: {
9037
9569
  path: "/public/bookings/resources/reference",
9038
9570
  method: "GET",
9039
- revalidate: 60,
9571
+ revalidate: publicReadFreshnessSeconds("getPublicBookingResourcesReference"),
9040
9572
  tags: [TAG_TEMPLATES.publicBookingServices],
9041
9573
  auth: "public",
9042
9574
  responseKind: "json"
@@ -9044,7 +9576,7 @@ const PUBLIC_BOOKING_AVAILABILITY_ENDPOINT_DEFINITIONS = defineEndpointDefinitio
9044
9576
  getPublicBookingAvailableDates: {
9045
9577
  path: "/public/bookings/availability/dates",
9046
9578
  method: "GET",
9047
- revalidate: 60,
9579
+ revalidate: publicReadFreshnessSeconds("getPublicBookingAvailableDates"),
9048
9580
  tags: [TAG_TEMPLATES.publicBookingAvailability, TAG_TEMPLATES.publicBookingServices],
9049
9581
  auth: "public",
9050
9582
  responseKind: "json"
@@ -9052,7 +9584,7 @@ const PUBLIC_BOOKING_AVAILABILITY_ENDPOINT_DEFINITIONS = defineEndpointDefinitio
9052
9584
  getPublicBookingAvailableSlots: {
9053
9585
  path: "/public/bookings/availability/slots",
9054
9586
  method: "GET",
9055
- revalidate: 60,
9587
+ revalidate: publicReadFreshnessSeconds("getPublicBookingAvailableSlots"),
9056
9588
  tags: [TAG_TEMPLATES.publicBookingAvailability, TAG_TEMPLATES.publicBookingServices],
9057
9589
  auth: "public",
9058
9590
  responseKind: "json"
@@ -9060,7 +9592,7 @@ const PUBLIC_BOOKING_AVAILABILITY_ENDPOINT_DEFINITIONS = defineEndpointDefinitio
9060
9592
  getPublicWeeklySlotAvailability: {
9061
9593
  path: "/public/bookings/weekly-slot-availability",
9062
9594
  method: "GET",
9063
- revalidate: 60,
9595
+ revalidate: publicReadFreshnessSeconds("getPublicWeeklySlotAvailability"),
9064
9596
  tags: [TAG_TEMPLATES.publicBookingAvailability, TAG_TEMPLATES.publicBookingServices],
9065
9597
  auth: "public",
9066
9598
  responseKind: "json",
@@ -9510,15 +10042,91 @@ const SDK_MEDIA_ENDPOINT_DEFINITIONS = defineEndpointDefinitions({
9510
10042
  responseKind: "json"
9511
10043
  }
9512
10044
  });
10045
+ //#endregion
10046
+ //#region ../api/src/publicReadEndpointDefinitions.ts
10047
+ /**
10048
+ * Direct public reads governed by the canonical public-read freshness policy.
10049
+ * Keeping these definitions together makes the endpoint identity used to
10050
+ * derive freshness reviewable beside its route and tags.
10051
+ */
10052
+ const PUBLIC_READ_ENDPOINT_DEFINITIONS = defineEndpointDefinitions({
10053
+ getPublishedEntryPreview: {
10054
+ path: "/public/content/{siteId}/{type}/{slug}/preview",
10055
+ method: "GET",
10056
+ revalidate: publicReadFreshnessSeconds("getPublishedEntryPreview"),
10057
+ tags: ["content-{siteId}-{type}-{slug}"],
10058
+ auth: "public",
10059
+ responseKind: "json"
10060
+ },
10061
+ listPublishedEntries: {
10062
+ path: "/public/content/{siteId}/{type}/entries",
10063
+ method: "GET",
10064
+ revalidate: publicReadFreshnessSeconds("listPublishedEntries"),
10065
+ tags: ["content-{siteId}-{type}"],
10066
+ auth: "public",
10067
+ responseKind: "json"
10068
+ },
10069
+ getPublicFormById: {
10070
+ path: "/public/forms/{formId}",
10071
+ method: "GET",
10072
+ revalidate: publicReadFreshnessSeconds("getPublicFormById"),
10073
+ tags: [TAG_TEMPLATES.formById],
10074
+ auth: "public",
10075
+ responseKind: "json"
10076
+ },
10077
+ listPublicEvents: {
10078
+ path: "/public/sites/{siteId}/events",
10079
+ method: "GET",
10080
+ revalidate: publicReadFreshnessSeconds("listPublicEvents"),
10081
+ tags: [TAG_TEMPLATES.publicEvents],
10082
+ auth: "public",
10083
+ responseKind: "json"
10084
+ },
10085
+ listPublicTeamMembers: {
10086
+ path: "/public/sites/{siteId}/team-members",
10087
+ method: "GET",
10088
+ revalidate: publicReadFreshnessSeconds("listPublicTeamMembers"),
10089
+ tags: [TAG_TEMPLATES.publicTeamMembers],
10090
+ errors: [],
10091
+ auth: "public",
10092
+ responseKind: "json"
10093
+ },
10094
+ listPublicPasses: {
10095
+ path: "/public/sites/{siteId}/bookings/products/passes",
10096
+ method: "GET",
10097
+ revalidate: publicReadFreshnessSeconds("listPublicPasses"),
10098
+ tags: [TAG_TEMPLATES.publicPasses],
10099
+ auth: "public",
10100
+ responseKind: "json"
10101
+ },
10102
+ listPublicMemberships: {
10103
+ path: "/public/sites/{siteId}/bookings/products/memberships",
10104
+ method: "GET",
10105
+ revalidate: publicReadFreshnessSeconds("listPublicMemberships"),
10106
+ tags: [TAG_TEMPLATES.publicMemberships],
10107
+ auth: "public",
10108
+ responseKind: "json"
10109
+ },
10110
+ listPublicProducts: {
10111
+ path: "/public/sites/{siteId}/products",
10112
+ method: "GET",
10113
+ revalidate: publicReadFreshnessSeconds("listPublicProducts"),
10114
+ tags: [TAG_TEMPLATES.publicProducts],
10115
+ errors: [],
10116
+ auth: "public",
10117
+ responseKind: "json"
10118
+ }
10119
+ });
9513
10120
  ({
9514
10121
  ...ACCOUNT_ENDPOINT_DEFINITIONS,
9515
10122
  ...PUBLIC_EVENT_ENDPOINT_DEFINITIONS,
9516
- ...GIFTING_ENDPOINT_DEFINITIONS
10123
+ ...GIFTING_ENDPOINT_DEFINITIONS,
10124
+ ...PUBLIC_READ_ENDPOINT_DEFINITIONS
9517
10125
  }), TAG_TEMPLATES.siteCollection, TAG_TEMPLATES.siteTheme, TAG_TEMPLATES.siteCollection, { ...SITE_API_KEY_ENDPOINT_DEFINITIONS }, TAG_TEMPLATES.site, TAG_TEMPLATES.siteBookingSettings, TAG_TEMPLATES.site, TAG_TEMPLATES.siteBookingSettings, TAG_TEMPLATES.site, TAG_TEMPLATES.siteEmailSettings, TAG_TEMPLATES.site, TAG_TEMPLATES.siteEmailSettings, TAG_TEMPLATES.site, TAG_TEMPLATES.siteEmailSettings, TAG_TEMPLATES.site, TAG_TEMPLATES.siteEmailSettings, {
9518
10126
  ...NEWSLETTER_ENDPOINT_DEFINITIONS,
9519
10127
  ...APPOINTMENT_ENDPOINT_DEFINITIONS,
9520
10128
  ...EVENT_SERIES_REGISTRATION_ENDPOINT_DEFINITIONS
9521
- }, TAG_TEMPLATES.siteCollection, TAG_TEMPLATES.siteById, TAG_TEMPLATES.siteBySlug, TAG_TEMPLATES.siteByDomain, TAG_TEMPLATES.siteCollection, TAG_TEMPLATES.siteById, TAG_TEMPLATES.siteBySlug, TAG_TEMPLATES.siteByDomain, TAG_TEMPLATES.siteCollection, TAG_TEMPLATES.pageSurfaceById, TAG_TEMPLATES.siteCollection, TAG_TEMPLATES.pageSurfaceById, TAG_TEMPLATES.siteCollection, TAG_TEMPLATES.siteRoutableContent, TAG_TEMPLATES.pageSurfaceById, TAG_TEMPLATES.siteCollection, TAG_TEMPLATES.siteRoutableContent, TAG_TEMPLATES.pageSurfaceById, TAG_TEMPLATES.workflowRunById, TAG_TEMPLATES.siteCollection, TAG_TEMPLATES.siteRoutableContent, TAG_TEMPLATES.routeById, TAG_TEMPLATES.site, TAG_TEMPLATES.siteSettings, TAG_TEMPLATES.siteCollection, TAG_TEMPLATES.site, TAG_TEMPLATES.siteSettings, TAG_TEMPLATES.siteCollection, TAG_TEMPLATES.site, TAG_TEMPLATES.siteSettings, TAG_TEMPLATES.siteCollection, TAG_TEMPLATES.site, TAG_TEMPLATES.site, TAG_TEMPLATES.siteSettings, TAG_TEMPLATES.site, TAG_TEMPLATES.siteSettings, TAG_TEMPLATES.site, TAG_TEMPLATES.siteSettings, TAG_TEMPLATES.site, TAG_TEMPLATES.siteSettings, TAG_TEMPLATES.site, TAG_TEMPLATES.siteSettings, TAG_TEMPLATES.site, TAG_TEMPLATES.siteSettings, TAG_TEMPLATES.site, TAG_TEMPLATES.siteSettings, TAG_TEMPLATES.site, TAG_TEMPLATES.siteSettings, TAG_TEMPLATES.site, TAG_TEMPLATES.siteSettings, TAG_TEMPLATES.site, TAG_TEMPLATES.siteSettings, TAG_TEMPLATES.site, TAG_TEMPLATES.siteSettings, TAG_TEMPLATES.site, TAG_TEMPLATES.siteSettings, TAG_TEMPLATES.site, TAG_TEMPLATES.siteSettings, TAG_TEMPLATES.site, TAG_TEMPLATES.siteSettings, TAG_TEMPLATES.site, TAG_TEMPLATES.siteSettings, TAG_TEMPLATES.site, TAG_TEMPLATES.siteSettings, TAG_TEMPLATES.site, TAG_TEMPLATES.siteSettings, TAG_TEMPLATES.site, TAG_TEMPLATES.siteSettings, TAG_TEMPLATES.site, TAG_TEMPLATES.siteSettings, TAG_TEMPLATES.site, TAG_TEMPLATES.siteSettings, TAG_TEMPLATES.site, TAG_TEMPLATES.siteSettings, TAG_TEMPLATES.site, TAG_TEMPLATES.siteSettings, TAG_TEMPLATES.site, TAG_TEMPLATES.siteSettings, TAG_TEMPLATES.site, TAG_TEMPLATES.siteSettings, TAG_TEMPLATES.site, TAG_TEMPLATES.siteSettings, TAG_TEMPLATES.site, TAG_TEMPLATES.siteSettings, TAG_TEMPLATES.siteCollection, TAG_TEMPLATES.siteNavigation, TAG_TEMPLATES.siteCollection, TAG_TEMPLATES.siteNavigation, TAG_TEMPLATES.siteCollection, TAG_TEMPLATES.siteNavigation, TAG_TEMPLATES.navigationMenuById, TAG_TEMPLATES.siteCollection, TAG_TEMPLATES.siteNavigation, TAG_TEMPLATES.navigationMenuById, TAG_TEMPLATES.siteCollection, TAG_TEMPLATES.siteNavigation, TAG_TEMPLATES.navigationMenuById, TAG_TEMPLATES.siteCollection, TAG_TEMPLATES.siteNavigation, TAG_TEMPLATES.navigationMenuById, TAG_TEMPLATES.siteCollection, TAG_TEMPLATES.siteNavigation, TAG_TEMPLATES.navigationMenuById, TAG_TEMPLATES.navigationItemById, TAG_TEMPLATES.siteCollection, TAG_TEMPLATES.siteNavigation, TAG_TEMPLATES.navigationMenuById, TAG_TEMPLATES.navigationItemById, TAG_TEMPLATES.siteCollection, TAG_TEMPLATES.siteNavigation, TAG_TEMPLATES.navigationMenuById, TAG_TEMPLATES.siteRoutableContent, TAG_TEMPLATES.siteRoutableContent, TAG_TEMPLATES.siteRoutableContent, TAG_TEMPLATES.siteSettings, TAG_TEMPLATES.siteCollection, TAG_TEMPLATES.siteTheme, TAG_TEMPLATES.siteCollection, TAG_TEMPLATES.siteTheme, TAG_TEMPLATES.siteCollection, TAG_TEMPLATES.siteTheme, TAG_TEMPLATES.siteCollection, TAG_TEMPLATES.pageSurfaceById, TAG_TEMPLATES.siteCollection, TAG_TEMPLATES.pageSurfaceById, TAG_TEMPLATES.siteCollection, TAG_TEMPLATES.pageSurfaceById, TAG_TEMPLATES.siteCollection, TAG_TEMPLATES.pageSurfaceById, TAG_TEMPLATES.siteRoutableContent, TAG_TEMPLATES.pageById, TAG_TEMPLATES.siteRoutableContent, TAG_TEMPLATES.pageById, TAG_TEMPLATES.siteRoutableContent, TAG_TEMPLATES.pageById, TAG_TEMPLATES.siteRoutableContent, TAG_TEMPLATES.pageById, TAG_TEMPLATES.siteRoutableContent, TAG_TEMPLATES.pageById, TAG_TEMPLATES.siteRoutableContent, TAG_TEMPLATES.pageById, TAG_TEMPLATES.siteRoutableContent, TAG_TEMPLATES.pageById, TAG_TEMPLATES.siteRoutableContent, TAG_TEMPLATES.pageById, TAG_TEMPLATES.siteRoutableContent, TAG_TEMPLATES.pageById, TAG_TEMPLATES.siteRoutableContent, TAG_TEMPLATES.siteRoutableContent, TAG_TEMPLATES.siteRedirects, TAG_TEMPLATES.siteRouteOutcomes, TAG_TEMPLATES.siteRoutableContent, TAG_TEMPLATES.siteRedirects, TAG_TEMPLATES.siteRouteOutcomes, TAG_TEMPLATES.siteRoutableContent, TAG_TEMPLATES.siteForms, TAG_TEMPLATES.siteForms, TAG_TEMPLATES.siteForms, TAG_TEMPLATES.siteForms, TAG_TEMPLATES.siteForms, TAG_TEMPLATES.siteForms, TAG_TEMPLATES.formSubmissionsBySlug, TAG_TEMPLATES.siteForms, TAG_TEMPLATES.formSubmissionsBySlug, TAG_TEMPLATES.siteForms, TAG_TEMPLATES.formSubmissionsBySlug, TAG_TEMPLATES.formById, TAG_TEMPLATES.siteForms, { ...PUBLIC_BOOKING_AVAILABILITY_ENDPOINT_DEFINITIONS }, TAG_TEMPLATES.publicEvents, TAG_TEMPLATES.publicEventFilters, TAG_TEMPLATES.publicTeamMembers, TAG_TEMPLATES.publicEvents, TAG_TEMPLATES.publicPasses, TAG_TEMPLATES.publicMemberships, TAG_TEMPLATES.publicProducts, TAG_TEMPLATES.publicShop, TAG_TEMPLATES.publicCheckout, TAG_TEMPLATES.siteRoutableContent, { ...PUBLIC_EVENT_CHECKOUT_ENDPOINT_DEFINITIONS }, TAG_TEMPLATES.publicEvents, TAG_TEMPLATES.publicEvents, { ...EVENT_MANAGEMENT_ENDPOINT_DEFINITIONS }, { ...SDK_MEDIA_ENDPOINT_DEFINITIONS }, { ...SDK_ENDPOINT_DEFINITIONS }, { ...SDK_ENTRY_READ_ENDPOINTS };
10129
+ }, TAG_TEMPLATES.siteCollection, TAG_TEMPLATES.siteById, TAG_TEMPLATES.siteBySlug, TAG_TEMPLATES.siteByDomain, TAG_TEMPLATES.siteCollection, TAG_TEMPLATES.siteById, TAG_TEMPLATES.siteBySlug, TAG_TEMPLATES.siteByDomain, TAG_TEMPLATES.siteCollection, TAG_TEMPLATES.pageSurfaceById, TAG_TEMPLATES.siteCollection, TAG_TEMPLATES.pageSurfaceById, TAG_TEMPLATES.siteCollection, TAG_TEMPLATES.siteRoutableContent, TAG_TEMPLATES.pageSurfaceById, TAG_TEMPLATES.siteCollection, TAG_TEMPLATES.siteRoutableContent, TAG_TEMPLATES.pageSurfaceById, TAG_TEMPLATES.workflowRunById, TAG_TEMPLATES.siteCollection, TAG_TEMPLATES.siteRoutableContent, TAG_TEMPLATES.routeById, TAG_TEMPLATES.site, TAG_TEMPLATES.siteSettings, TAG_TEMPLATES.siteCollection, TAG_TEMPLATES.site, TAG_TEMPLATES.siteSettings, TAG_TEMPLATES.siteCollection, TAG_TEMPLATES.site, TAG_TEMPLATES.siteSettings, TAG_TEMPLATES.siteCollection, TAG_TEMPLATES.site, TAG_TEMPLATES.site, TAG_TEMPLATES.siteSettings, TAG_TEMPLATES.site, TAG_TEMPLATES.siteSettings, TAG_TEMPLATES.site, TAG_TEMPLATES.siteSettings, TAG_TEMPLATES.site, TAG_TEMPLATES.siteSettings, TAG_TEMPLATES.site, TAG_TEMPLATES.siteSettings, TAG_TEMPLATES.site, TAG_TEMPLATES.siteSettings, TAG_TEMPLATES.site, TAG_TEMPLATES.siteSettings, TAG_TEMPLATES.site, TAG_TEMPLATES.siteSettings, TAG_TEMPLATES.site, TAG_TEMPLATES.siteSettings, TAG_TEMPLATES.site, TAG_TEMPLATES.siteSettings, TAG_TEMPLATES.site, TAG_TEMPLATES.siteSettings, TAG_TEMPLATES.site, TAG_TEMPLATES.siteSettings, TAG_TEMPLATES.site, TAG_TEMPLATES.siteSettings, TAG_TEMPLATES.site, TAG_TEMPLATES.siteSettings, TAG_TEMPLATES.site, TAG_TEMPLATES.siteSettings, TAG_TEMPLATES.site, TAG_TEMPLATES.siteSettings, TAG_TEMPLATES.site, TAG_TEMPLATES.siteSettings, TAG_TEMPLATES.site, TAG_TEMPLATES.siteSettings, TAG_TEMPLATES.site, TAG_TEMPLATES.siteSettings, TAG_TEMPLATES.site, TAG_TEMPLATES.siteSettings, TAG_TEMPLATES.site, TAG_TEMPLATES.siteSettings, TAG_TEMPLATES.site, TAG_TEMPLATES.siteSettings, TAG_TEMPLATES.site, TAG_TEMPLATES.siteSettings, TAG_TEMPLATES.site, TAG_TEMPLATES.siteSettings, TAG_TEMPLATES.site, TAG_TEMPLATES.siteSettings, TAG_TEMPLATES.site, TAG_TEMPLATES.siteSettings, TAG_TEMPLATES.siteCollection, TAG_TEMPLATES.siteNavigation, TAG_TEMPLATES.siteCollection, TAG_TEMPLATES.siteNavigation, TAG_TEMPLATES.navigationMenuById, TAG_TEMPLATES.siteCollection, TAG_TEMPLATES.siteNavigation, TAG_TEMPLATES.navigationMenuById, TAG_TEMPLATES.siteCollection, TAG_TEMPLATES.siteNavigation, TAG_TEMPLATES.navigationMenuById, TAG_TEMPLATES.navigationItemById, TAG_TEMPLATES.siteCollection, TAG_TEMPLATES.siteNavigation, TAG_TEMPLATES.navigationMenuById, TAG_TEMPLATES.navigationItemById, TAG_TEMPLATES.siteCollection, TAG_TEMPLATES.siteNavigation, TAG_TEMPLATES.navigationMenuById, TAG_TEMPLATES.siteRoutableContent, TAG_TEMPLATES.siteRoutableContent, TAG_TEMPLATES.siteRoutableContent, TAG_TEMPLATES.siteSettings, TAG_TEMPLATES.siteCollection, TAG_TEMPLATES.siteTheme, TAG_TEMPLATES.siteCollection, TAG_TEMPLATES.siteTheme, TAG_TEMPLATES.siteCollection, TAG_TEMPLATES.siteTheme, TAG_TEMPLATES.siteCollection, TAG_TEMPLATES.pageSurfaceById, TAG_TEMPLATES.siteCollection, TAG_TEMPLATES.pageSurfaceById, TAG_TEMPLATES.siteCollection, TAG_TEMPLATES.pageSurfaceById, TAG_TEMPLATES.siteCollection, TAG_TEMPLATES.pageSurfaceById, TAG_TEMPLATES.siteRoutableContent, TAG_TEMPLATES.pageById, TAG_TEMPLATES.siteRoutableContent, TAG_TEMPLATES.pageById, TAG_TEMPLATES.siteRoutableContent, TAG_TEMPLATES.pageById, TAG_TEMPLATES.siteRoutableContent, TAG_TEMPLATES.pageById, TAG_TEMPLATES.siteRoutableContent, TAG_TEMPLATES.pageById, TAG_TEMPLATES.siteRoutableContent, TAG_TEMPLATES.pageById, TAG_TEMPLATES.siteRoutableContent, TAG_TEMPLATES.pageById, TAG_TEMPLATES.siteRoutableContent, TAG_TEMPLATES.pageById, TAG_TEMPLATES.siteRoutableContent, TAG_TEMPLATES.pageById, TAG_TEMPLATES.siteRoutableContent, TAG_TEMPLATES.siteRoutableContent, TAG_TEMPLATES.siteRedirects, TAG_TEMPLATES.siteRouteOutcomes, TAG_TEMPLATES.siteRoutableContent, TAG_TEMPLATES.siteRedirects, TAG_TEMPLATES.siteRouteOutcomes, TAG_TEMPLATES.siteRoutableContent, TAG_TEMPLATES.siteForms, TAG_TEMPLATES.siteForms, TAG_TEMPLATES.siteForms, TAG_TEMPLATES.siteForms, TAG_TEMPLATES.siteForms, TAG_TEMPLATES.siteForms, TAG_TEMPLATES.formSubmissionsBySlug, TAG_TEMPLATES.siteForms, TAG_TEMPLATES.formSubmissionsBySlug, TAG_TEMPLATES.siteForms, TAG_TEMPLATES.formSubmissionsBySlug, TAG_TEMPLATES.siteForms, { ...PUBLIC_BOOKING_AVAILABILITY_ENDPOINT_DEFINITIONS }, TAG_TEMPLATES.publicEventFilters, TAG_TEMPLATES.publicEvents, TAG_TEMPLATES.publicShop, TAG_TEMPLATES.publicCheckout, TAG_TEMPLATES.siteRoutableContent, { ...PUBLIC_EVENT_CHECKOUT_ENDPOINT_DEFINITIONS }, TAG_TEMPLATES.publicEvents, TAG_TEMPLATES.publicEvents, { ...EVENT_MANAGEMENT_ENDPOINT_DEFINITIONS }, { ...SDK_MEDIA_ENDPOINT_DEFINITIONS }, { ...SDK_ENDPOINT_DEFINITIONS }, { ...SDK_ENTRY_READ_ENDPOINTS };
9522
10130
  z.object({ refreshToken: z.string().min(1).max(4096) }).strict();
9523
10131
  z.object({
9524
10132
  accessToken: z.string().min(1).max(4096),
@@ -21273,102 +21881,31 @@ const newsletterFormFragment = defineFragment({
21273
21881
  description: "Newsletter subscription form with preset field configurations.",
21274
21882
  fields: [
21275
21883
  {
21276
- id: "destinationProvider",
21277
- type: "select",
21278
- label: "Send signups to",
21279
- description: "Choose where new subscribers from this form should go.",
21280
- defaultValue: "riverbank",
21281
- required: false,
21282
- ui: { intent: "behavior" },
21283
- options: [{
21284
- value: "riverbank",
21285
- label: "Riverbank newsletter"
21286
- }]
21287
- },
21288
- {
21289
- id: "listId",
21290
- type: "reference",
21291
- label: "Newsletter list",
21292
- description: "Select the list new subscribers should be added to.",
21293
- required: false,
21294
- referenceKind: "newsletterList",
21295
- allowManualEntry: false,
21296
- ui: {
21297
- intent: "behavior",
21298
- visibleWhen: {
21299
- field: "destinationProvider",
21300
- equals: "riverbank"
21301
- }
21302
- }
21303
- },
21304
- {
21305
- id: "mailchimpAudienceId",
21306
- type: "reference",
21307
- label: "Mailchimp audience",
21308
- description: "Choose the Mailchimp audience for new subscribers.",
21309
- required: false,
21310
- referenceKind: "mailchimpAudience",
21311
- allowManualEntry: false,
21312
- ui: {
21313
- intent: "behavior",
21314
- visibleWhen: {
21315
- field: "destinationProvider",
21316
- equals: "mailchimp"
21317
- }
21318
- }
21319
- },
21320
- {
21321
- id: "mailchimpTags",
21322
- type: "text",
21323
- label: "Mailchimp tags",
21324
- description: "Optional tags to apply in Mailchimp, separated by commas or semicolons.",
21884
+ id: "targeting",
21885
+ type: "group",
21886
+ label: "Mailing-list targeting",
21887
+ description: "Choose what kind of emails people signing up here will receive.",
21888
+ defaultValue: { mode: "default" },
21889
+ required: true,
21325
21890
  ui: {
21326
21891
  intent: "behavior",
21327
- visibleWhen: {
21328
- field: "destinationProvider",
21329
- equals: "mailchimp"
21330
- }
21331
- }
21332
- },
21333
- {
21334
- id: "preset",
21335
- type: "select",
21336
- label: "Form fields",
21337
- description: "Choose which fields to collect.",
21338
- defaultValue: "emailOnly",
21339
- ui: { intent: "display" },
21340
- options: [
21341
- {
21342
- value: "emailOnly",
21343
- label: "Email only"
21344
- },
21345
- {
21346
- value: "emailName",
21347
- label: "Email + Name"
21348
- },
21349
- {
21350
- value: "emailFirstLast",
21351
- label: "Email + First/Last Name"
21352
- },
21353
- {
21354
- value: "emailNameCompany",
21355
- label: "Email + Name + Company (B2B)"
21356
- }
21357
- ]
21358
- },
21359
- {
21360
- id: "includePhone",
21361
- type: "boolean",
21362
- label: "Include phone field (optional)",
21363
- description: "Adds an optional phone field to the selected preset (never required).",
21364
- defaultValue: false,
21365
- ui: { intent: "display" }
21366
- },
21367
- {
21368
- id: "buttonLabel",
21369
- type: "text",
21370
- label: "Button label",
21371
- defaultValue: "Subscribe"
21892
+ widget: "mailingListTargeting"
21893
+ },
21894
+ schema: { fields: [{
21895
+ id: "mode",
21896
+ type: "select",
21897
+ label: "Mode",
21898
+ required: true,
21899
+ defaultValue: "default",
21900
+ options: [{
21901
+ value: "default",
21902
+ label: "Default"
21903
+ }, {
21904
+ value: "custom",
21905
+ label: "Custom"
21906
+ }],
21907
+ ui: { hidden: true }
21908
+ }] }
21372
21909
  },
21373
21910
  createButtonVariantField({
21374
21911
  id: "buttonVariant",
@@ -21376,46 +21913,42 @@ const newsletterFormFragment = defineFragment({
21376
21913
  defaultValue: "primary"
21377
21914
  }),
21378
21915
  {
21379
- id: "successMessage",
21380
- type: "text",
21381
- label: "Success message",
21382
- description: "Shown after successful subscription.",
21383
- defaultValue: "Thanks for subscribing!"
21384
- },
21385
- {
21386
- id: "privacyNote",
21916
+ id: "mailingListSource",
21387
21917
  type: "text",
21388
- label: "Privacy note",
21389
- description: "Optional small text below the form (e.g., privacy policy link)."
21918
+ label: "Mailing-list source",
21919
+ ui: {
21920
+ hidden: true,
21921
+ intent: "behavior"
21922
+ }
21390
21923
  },
21391
21924
  {
21392
- id: "tags",
21925
+ id: "mailingListSourceToken",
21393
21926
  type: "text",
21394
- label: "Tags (optional)",
21395
- description: "Optional tags to apply on subscribe (comma or semicolon separated).",
21927
+ label: "Mailing-list source token",
21396
21928
  ui: {
21397
- intent: "behavior",
21398
- visibleWhen: {
21399
- field: "destinationProvider",
21400
- equals: "riverbank"
21401
- }
21929
+ hidden: true,
21930
+ intent: "behavior"
21402
21931
  }
21403
21932
  }
21404
21933
  ],
21405
21934
  layout: [el("newsletter-form", withApiBindings({
21406
- source: "block",
21407
- destinationProvider: { $bind: { from: "destinationProvider" } },
21408
- listId: { $bind: { from: "listId" } },
21409
- preset: { $bind: { from: "preset" } },
21410
- includePhone: { $bind: { from: "includePhone" } },
21411
- buttonLabel: { $bind: { from: "buttonLabel" } },
21935
+ source: { $bind: {
21936
+ from: "mailingListSource",
21937
+ fallback: "block"
21938
+ } },
21939
+ sourceRef: {
21940
+ tag: "signed_source_ref",
21941
+ token: { $bind: { from: "mailingListSourceToken" } }
21942
+ },
21943
+ targeting: { $bind: {
21944
+ from: "targeting",
21945
+ fallback: { mode: "default" }
21946
+ } },
21947
+ mailingList: { $bind: { from: "$root.mailingList" } },
21412
21948
  buttonVariant: { $bind: {
21413
21949
  from: "buttonVariant",
21414
21950
  fallback: "primary"
21415
21951
  } },
21416
- successMessage: { $bind: { from: "successMessage" } },
21417
- privacyNote: { $bind: { from: "privacyNote" } },
21418
- tags: { $bind: { from: "tags" } },
21419
21952
  spamProtectionEnabled: { $bind: { from: "$root.spamProtectionEnabled" } }
21420
21953
  }), [])]
21421
21954
  });
@@ -23590,14 +24123,19 @@ const contactNewsletterLayout = styledSection({
23590
24123
  as: "h2",
23591
24124
  className: "newsletter-copy-title"
23592
24125
  }, when("newsletterTitle"), bind("newsletterTitle")), richText({ className: "newsletter-copy-subtitle rb-prose rb-max-w-none" }, when("newsletterSubtitle"), bind("newsletterSubtitle"))]), el("newsletter-form", withApiBindings({
23593
- source: "block",
23594
- listId: { $bind: { from: "listId" } },
23595
- preset: { $bind: { from: "preset" } },
23596
- includePhone: { $bind: { from: "includePhone" } },
23597
- buttonLabel: { $bind: { from: "newsletterButtonLabel" } },
23598
- successMessage: { $bind: { from: "newsletterSuccessMessage" } },
23599
- privacyNote: { $bind: { from: "newsletterPrivacyNote" } },
23600
- tags: { $bind: { from: "newsletterTags" } },
24126
+ source: { $bind: {
24127
+ from: "mailingListSource",
24128
+ fallback: "block"
24129
+ } },
24130
+ sourceRef: {
24131
+ tag: "signed_source_ref",
24132
+ token: { $bind: { from: "mailingListSourceToken" } }
24133
+ },
24134
+ targeting: { $bind: {
24135
+ from: "targeting",
24136
+ fallback: { mode: "default" }
24137
+ } },
24138
+ mailingList: { $bind: { from: "$root.mailingList" } },
23601
24139
  spamProtectionEnabled: { $bind: { from: "$root.spamProtectionEnabled" } }
23602
24140
  }), [])])])], { className: "contact-newsletter-container" }),
23603
24141
  baseClass: "",
@@ -23633,79 +24171,55 @@ const contactNewsletterBlockDefinition = {
23633
24171
  label: "Newsletter intro"
23634
24172
  },
23635
24173
  {
23636
- id: "listId",
23637
- type: "reference",
23638
- label: "Newsletter list",
23639
- description: "Select the list new subscribers should be added to.",
24174
+ id: "targeting",
24175
+ type: "group",
24176
+ label: "Mailing-list targeting",
24177
+ description: "Choose what kind of emails people signing up here will receive.",
23640
24178
  required: true,
23641
- referenceKind: "newsletterList",
23642
- allowManualEntry: false,
23643
- ui: { intent: "behavior" }
23644
- },
23645
- {
23646
- id: "preset",
23647
- type: "select",
23648
- label: "Newsletter fields",
23649
- defaultValue: "emailOnly",
23650
- ui: { intent: "display" },
23651
- options: [
23652
- {
23653
- value: "emailOnly",
23654
- label: "Email only"
23655
- },
23656
- {
23657
- value: "emailName",
23658
- label: "Email + Name"
23659
- },
23660
- {
23661
- value: "emailFirstLast",
23662
- label: "Email + First/Last Name"
23663
- },
23664
- {
23665
- value: "emailNameCompany",
23666
- label: "Email + Name + Company (B2B)"
23667
- }
23668
- ]
23669
- },
23670
- {
23671
- id: "includePhone",
23672
- type: "boolean",
23673
- label: "Include phone field",
23674
- defaultValue: false,
23675
- ui: { intent: "display" }
23676
- },
23677
- {
23678
- id: "newsletterButtonLabel",
23679
- type: "text",
23680
- label: "Newsletter button label",
23681
- defaultValue: "Subscribe",
23682
- ui: { intent: "display" }
23683
- },
23684
- {
23685
- id: "newsletterSuccessMessage",
23686
- type: "text",
23687
- label: "Newsletter success message",
23688
- defaultValue: "Thanks for subscribing!",
23689
- ui: { intent: "behavior" }
24179
+ defaultValue: { mode: "default" },
24180
+ ui: {
24181
+ intent: "behavior",
24182
+ widget: "mailingListTargeting"
24183
+ },
24184
+ schema: { fields: [{
24185
+ id: "mode",
24186
+ type: "select",
24187
+ label: "Mode",
24188
+ required: true,
24189
+ defaultValue: "default",
24190
+ options: [{
24191
+ value: "default",
24192
+ label: "Default"
24193
+ }, {
24194
+ value: "custom",
24195
+ label: "Custom"
24196
+ }],
24197
+ ui: { hidden: true }
24198
+ }] }
23690
24199
  },
23691
24200
  {
23692
- id: "newsletterPrivacyNote",
24201
+ id: "contactTitle",
23693
24202
  type: "text",
23694
- label: "Privacy note",
23695
- ui: { intent: "display" }
24203
+ label: "Contact title",
24204
+ defaultValue: "Get in touch"
23696
24205
  },
23697
24206
  {
23698
- id: "newsletterTags",
24207
+ id: "mailingListSource",
23699
24208
  type: "text",
23700
- label: "Newsletter tags",
23701
- description: "Optional tags to apply on subscribe, separated by comma or semicolon.",
23702
- ui: { intent: "behavior" }
24209
+ label: "Mailing-list source",
24210
+ ui: {
24211
+ hidden: true,
24212
+ intent: "behavior"
24213
+ }
23703
24214
  },
23704
24215
  {
23705
- id: "contactTitle",
24216
+ id: "mailingListSourceToken",
23706
24217
  type: "text",
23707
- label: "Contact title",
23708
- defaultValue: "Get in touch"
24218
+ label: "Mailing-list source token",
24219
+ ui: {
24220
+ hidden: true,
24221
+ intent: "behavior"
24222
+ }
23709
24223
  },
23710
24224
  {
23711
24225
  id: "contactIntro",
@@ -23906,6 +24420,12 @@ const arrayLengthTransform = {
23906
24420
  summary: "Get the length of an array",
23907
24421
  run: (value) => Array.isArray(value) ? value.length : 0
23908
24422
  };
24423
+ const booleanNotTransform = {
24424
+ id: "boolean.not",
24425
+ kind: "formatter",
24426
+ summary: "Invert a boolean value; fail closed to false for other inputs",
24427
+ run: (value) => typeof value === "boolean" ? !value : false
24428
+ };
23909
24429
  const jsonArrayTransform = {
23910
24430
  id: "value.jsonArray",
23911
24431
  kind: "formatter",
@@ -23956,33 +24476,51 @@ const marqueeLoopTransform = {
23956
24476
  function toUnknownArray(value) {
23957
24477
  return Array.isArray(value) ? value : [];
23958
24478
  }
24479
+ /**
24480
+ * Map a content state/enum to a theme token colour — the first-class way to turn
24481
+ * a value like a class's availability (`available`/`filling`/`waitlist`), a
24482
+ * card's featured flag, or a pricing tier level into a themed colour. Authors
24483
+ * write role names and get `rgb(var(--tb-<role>))`, so the result live-recolours
24484
+ * with the theme and drops directly into a `style` binding (e.g.
24485
+ * `borderLeftColor`). Returns `undefined` when the value matches no case and no
24486
+ * fallback is given, so the style property is simply omitted.
24487
+ */
24488
+ const mapTokenTransform = {
24489
+ id: "value.mapToken",
24490
+ kind: "formatter",
24491
+ summary: "Map a state/enum value to a theme token colour (rgb(var(--tb-<role>)))",
24492
+ schema: z.object({
24493
+ /**
24494
+ * Maps an input state/enum value (stringified) to a theme token ROLE name
24495
+ * (e.g. `secondary`, `accent`, `muted`, `border`). The role is wrapped to
24496
+ * `rgb(var(--tb-<role>))` so the result drops straight into a `style` binding.
24497
+ */
24498
+ cases: z.record(z.string(), z.string()),
24499
+ /** Role used when the value matches no case. Omit to yield `undefined`. */
24500
+ fallback: z.string().optional()
24501
+ }),
24502
+ run: (value, options) => {
24503
+ const role = options.cases[String(value)] ?? options.fallback;
24504
+ return role ? `rgb(var(--tb-${role}))` : void 0;
24505
+ }
24506
+ };
23959
24507
  const formattingTransforms = [
24508
+ {
24509
+ id: "value.equals",
24510
+ kind: "formatter",
24511
+ summary: "Compare a value to a fixed string",
24512
+ schema: z.object({ value: z.string() }),
24513
+ run: (value, options) => value === options.value
24514
+ },
23960
24515
  uppercaseTransform,
23961
24516
  fallbackTransform,
23962
24517
  dateFormatShort,
23963
24518
  arrayLengthTransform,
24519
+ booleanNotTransform,
23964
24520
  jsonArrayTransform,
23965
24521
  csvTransform,
23966
24522
  marqueeLoopTransform,
23967
- {
23968
- id: "value.mapToken",
23969
- kind: "formatter",
23970
- summary: "Map a state/enum value to a theme token colour (rgb(var(--tb-<role>)))",
23971
- schema: z.object({
23972
- /**
23973
- * Maps an input state/enum value (stringified) to a theme token ROLE name
23974
- * (e.g. `secondary`, `accent`, `muted`, `border`). The role is wrapped to
23975
- * `rgb(var(--tb-<role>))` so the result drops straight into a `style` binding.
23976
- */
23977
- cases: z.record(z.string(), z.string()),
23978
- /** Role used when the value matches no case. Omit to yield `undefined`. */
23979
- fallback: z.string().optional()
23980
- }),
23981
- run: (value, options) => {
23982
- const role = options.cases[String(value)] ?? options.fallback;
23983
- return role ? `rgb(var(--tb-${role}))` : void 0;
23984
- }
23985
- }
24523
+ mapTokenTransform
23986
24524
  ];
23987
24525
  //#endregion
23988
24526
  //#region ../blocks/src/system/transforms/registry/ui.ts
@@ -25391,13 +25929,9 @@ const footerStandardContactBindings = {
25391
25929
  };
25392
25930
  const footerMailingListBindings = {
25393
25931
  source: { $bind: { from: "module.engagement.runtime.source" } },
25394
- destinationRef: { $bind: { from: "module.engagement.runtime.destinationRef" } },
25395
- listId: { $bind: { from: "module.engagement.runtime.listId" } },
25396
- preset: { $bind: { from: "module.engagement.runtime.preset" } },
25397
- includePhone: { $bind: { from: "module.engagement.runtime.includePhone" } },
25398
- buttonLabel: { $bind: { from: "module.engagement.runtime.buttonLabel" } },
25399
- successMessage: { $bind: { from: "module.engagement.runtime.successMessage" } },
25400
- privacyNote: { $bind: { from: "module.engagement.runtime.privacyNote" } }
25932
+ sourceRef: { $bind: { from: "module.engagement.runtime.sourceRef" } },
25933
+ mailingList: { $bind: { from: "module.engagement.runtime.mailingList" } },
25934
+ targeting: { $bind: { from: "module.engagement.runtime.targeting" } }
25401
25935
  };
25402
25936
  const footerSpamProtectionBinding = { spamProtectionEnabled: { $bind: { from: "$root.spamProtectionEnabled" } } };
25403
25937
  const brandLogo = link({
@@ -27085,6 +27619,78 @@ const appointmentBookingBlockDefinition = {
27085
27619
  }
27086
27620
  }
27087
27621
  };
27622
+ const appointmentServicesBlockDefinition = {
27623
+ manifest: createBlockManifest({
27624
+ id: "block.appointment-services",
27625
+ version: "1.0.0",
27626
+ title: "Appointment Services",
27627
+ titleSource: "heading",
27628
+ description: "List public appointment services with booking links",
27629
+ skipSectionStyles: true,
27630
+ additionalFields: [
27631
+ {
27632
+ id: "heading",
27633
+ type: "text",
27634
+ label: "Heading",
27635
+ required: false,
27636
+ multiline: false,
27637
+ defaultValue: "Appointments",
27638
+ ui: { intent: "content" }
27639
+ },
27640
+ {
27641
+ id: "description",
27642
+ type: "richText",
27643
+ label: "Description",
27644
+ required: false,
27645
+ ui: { intent: "content" }
27646
+ },
27647
+ sectionStylesField({
27648
+ id: "_sectionStyles",
27649
+ label: "Section styles",
27650
+ renderInHeader: false
27651
+ })
27652
+ ],
27653
+ styleTokens: {
27654
+ background: "surface",
27655
+ typography: "body",
27656
+ spacing: "md"
27657
+ },
27658
+ behaviours: {
27659
+ supportsThemeSwitching: true,
27660
+ inlineEditing: true,
27661
+ animation: false,
27662
+ paletteHidden: false
27663
+ },
27664
+ category: "interactive",
27665
+ contentSource: { kind: "inline" },
27666
+ tags: [
27667
+ "booking",
27668
+ "appointment",
27669
+ "services",
27670
+ "scheduling"
27671
+ ],
27672
+ icon: "CalendarDays",
27673
+ layout: [styledSection({
27674
+ baseClass: "",
27675
+ spacing: "spacious",
27676
+ children: sectionContainer([
27677
+ text({ as: "h2" }, bind("content.heading"), when("content.heading")),
27678
+ richText({ className: "rb-prose rb-max-w-none" }, bind("content.description"), when("content.description")),
27679
+ el("appointment-services", {
27680
+ sectionHeading: { $bind: { from: "content.heading" } },
27681
+ services: { $bind: { from: "data.services" } },
27682
+ pathname: { $bind: { from: "$root.pathname" } }
27683
+ })
27684
+ ], { gap: "lg" })
27685
+ })]
27686
+ }),
27687
+ dataSchemas: { services: z.unknown().optional() },
27688
+ dataLoaders: { services: {
27689
+ endpoint: "getPublicBookingServices",
27690
+ params: { siteId: { $bind: { from: "$root.siteId" } } },
27691
+ mode: "server"
27692
+ } }
27693
+ };
27088
27694
  //#endregion
27089
27695
  //#region ../blocks/src/system/blocks/weekly-slot-availability.ts
27090
27696
  const weeklySlotAvailabilityManifest = createBlockManifest({
@@ -27797,8 +28403,8 @@ z.object({
27797
28403
  previewToken: z.string().nullable().optional(),
27798
28404
  stage: previewStageSchema.optional()
27799
28405
  });
27800
- function buildEventRegistrationDisplay(source) {
27801
- const selectedRun = resolveSelectedWholeSeriesRun(source);
28406
+ function buildEventRegistrationDisplayFromScopedEvents(source, scopedSourceEvents) {
28407
+ const selectedRun = resolveSelectedWholeSeriesRun(scopedSourceEvents);
27802
28408
  if (selectedRun && (!selectedRun.run.enrollmentOpen || toWholeSeriesRegistrationEvent(selectedRun) === null)) return {
27803
28409
  kind: "event-registration",
27804
28410
  state: "registration-closed",
@@ -27806,7 +28412,7 @@ function buildEventRegistrationDisplay(source) {
27806
28412
  titleText: "Registration closed",
27807
28413
  bodyText: "This run is no longer open for bookings."
27808
28414
  };
27809
- const stepLabels = buildEventRegistrationShellStepLabels(source);
28415
+ const stepLabels = buildEventRegistrationShellStepLabels(source, scopedSourceEvents);
27810
28416
  return {
27811
28417
  kind: "event-registration",
27812
28418
  state: "generic-shell",
@@ -27817,8 +28423,8 @@ function buildEventRegistrationDisplay(source) {
27817
28423
  summaryButtonText: source.buttonText?.trim() || "Register"
27818
28424
  };
27819
28425
  }
27820
- function buildEventRegistrationShellStepLabels(source) {
27821
- const selectedRun = resolveSelectedWholeSeriesRun(source);
28426
+ function buildEventRegistrationShellStepLabels(source, scopedSourceEvents) {
28427
+ const selectedRun = resolveSelectedWholeSeriesRun(scopedSourceEvents);
27822
28428
  if (selectedRun) {
27823
28429
  const selectedEvent = toWholeSeriesRegistrationEvent(selectedRun);
27824
28430
  if (!selectedEvent) return EVENT_REGISTRATION_GENERIC_SHELL_STEP_LABELS;
@@ -27828,7 +28434,7 @@ function buildEventRegistrationShellStepLabels(source) {
27828
28434
  event: selectedEvent
27829
28435
  })).map((step) => shellLabelForRegistrationStep(step, [selectedEvent]));
27830
28436
  }
27831
- const scopedEvents = scopeEventRegistrationEvents(source);
28437
+ const scopedEvents = toScopedEventRegistrationEvents(scopedSourceEvents);
27832
28438
  if (scopedEvents.length === 0) return EVENT_REGISTRATION_GENERIC_SHELL_STEP_LABELS;
27833
28439
  const preSelectedEvent = findPreSelectedEvent({
27834
28440
  scopedEvents,
@@ -27841,31 +28447,25 @@ function buildEventRegistrationShellStepLabels(source) {
27841
28447
  event: preSelectedEvent
27842
28448
  }) : false).map((step) => shellLabelForRegistrationStep(step, scopedEvents));
27843
28449
  }
27844
- function scopeEventRegistrationEvents(source) {
27845
- const scoped = scopeEventRegistrationSourceEvents(source);
27846
- return [...toRegistrationEvents(scoped), ...toWholeSeriesRegistrationEvents(scoped)];
28450
+ function toScopedEventRegistrationEvents(scopedSourceEvents) {
28451
+ return [...toRegistrationEvents(scopedSourceEvents), ...toWholeSeriesRegistrationEvents(scopedSourceEvents)];
27847
28452
  }
27848
- /**
27849
- * Every event this surface may register for, from both of its loads.
27850
- *
27851
- * The date page and the run read answer different questions and can name the
27852
- * same occurrence, so the two lists are joined and any occurrence that arrives
27853
- * twice is kept once, in the order the date page gave it.
27854
- */
27855
28453
  function scopeEventRegistrationSourceEvents(source) {
27856
- const byOccurrenceId = /* @__PURE__ */ new Map();
27857
- for (const event of [...source.events ?? [], ...source.wholeSeriesRunEvents ?? []]) if (!byOccurrenceId.has(event.occurrenceId)) byOccurrenceId.set(event.occurrenceId, event);
27858
- return resolveEventRegistrationScopedEvents([...byOccurrenceId.values()], {
28454
+ return resolveEventRegistrationScopedEvents(source.wholeSeriesRunEvents ?? source.events ?? [], {
27859
28455
  occurrenceContext: source.occurrenceContext,
27860
28456
  subject: source.subject
27861
28457
  });
27862
28458
  }
28459
+ function resolveLoadedEventRegistrationSourceEvents(source, scopedSourceEvents) {
28460
+ if (source.events == null && source.wholeSeriesRunEvents == null) return null;
28461
+ return [...scopedSourceEvents];
28462
+ }
27863
28463
  /**
27864
28464
  * The one run this surface is registering for, when the scoped events describe
27865
28465
  * exactly one. More than one run means the visitor still has to choose.
27866
28466
  */
27867
- function resolveSelectedWholeSeriesRun(source) {
27868
- const runs = groupPublicEventsByRun(scopeEventRegistrationSourceEvents(source));
28467
+ function resolveSelectedWholeSeriesRun(scopedSourceEvents) {
28468
+ const runs = groupPublicEventsByRun(scopedSourceEvents);
27869
28469
  return runs.length === 1 ? runs[0] : null;
27870
28470
  }
27871
28471
  function shellLabelForRegistrationStep(step, events) {
@@ -27899,9 +28499,10 @@ const eventRegistrationBlockRuntime = defineClientOwnedBlockRuntime({
27899
28499
  render: createZodCodec(eventRegistrationRenderSchema)
27900
28500
  },
27901
28501
  buildRender: (source) => {
27902
- const events = source.events == null ? null : scopeEventRegistrationSourceEvents(source);
28502
+ const scopedSourceEvents = scopeEventRegistrationSourceEvents(source);
28503
+ const events = resolveLoadedEventRegistrationSourceEvents(source, scopedSourceEvents);
27903
28504
  return {
27904
- display: buildEventRegistrationDisplay(source),
28505
+ display: buildEventRegistrationDisplayFromScopedEvents(source, scopedSourceEvents),
27905
28506
  hydration: {
27906
28507
  occurrenceContext: source.occurrenceContext ?? null,
27907
28508
  subject: source.subject ?? null,
@@ -28102,6 +28703,11 @@ const eventRegistrationBlockDefinition = {
28102
28703
  dataLoaders: {
28103
28704
  events: {
28104
28705
  endpoint: "listPublicEvents",
28706
+ when: { $bind: {
28707
+ from: "$root.subject.isWholeSeriesBooking",
28708
+ fallback: true,
28709
+ transforms: [{ id: "boolean.not" }]
28710
+ } },
28105
28711
  params: {
28106
28712
  siteId: { $bind: { from: "$root.siteId" } },
28107
28713
  limit: "50",
@@ -28123,13 +28729,9 @@ const eventRegistrationBlockDefinition = {
28123
28729
  /**
28124
28730
  * The runs this page can register for, finished ones included.
28125
28731
  *
28126
- * The `events` page above is date-bounded, so a run whose first session has
28127
- * passed contributes no rows to it and the surface would have nothing to
28128
- * render as closed. This asks for runs instead, and only on a page whose
28129
- * subject books whole runs. It is scoped exactly like the date page — same
28130
- * series, same run subroute — because scope and grain are separate
28131
- * questions. The two lists are joined by occurrence, so a run that appears
28132
- * in both is still one run.
28732
+ * A run read is the sole Event source on a whole-series surface. It is
28733
+ * status-bounded rather than date-bounded, so completed runs can still
28734
+ * render their closed state and every run carries its complete schedule.
28133
28735
  */
28134
28736
  wholeSeriesRunEvents: {
28135
28737
  endpoint: "listPublicEvents",
@@ -30855,20 +31457,7 @@ function isRecord$4(value) {
30855
31457
  return typeof value === "object" && value !== null && !Array.isArray(value);
30856
31458
  }
30857
31459
  //#endregion
30858
- //#region ../blocks/src/system/blocks/offerings-preview/index.ts
30859
- /**
30860
- * The whole-series presets, as the CSV `listPublicEvents` expects. Both
30861
- * whole-series presets share one loader: the block renders them as a single
30862
- * run group.
30863
- */
30864
- const WHOLE_SERIES_EVENT_PRESETS_PARAM = WHOLE_SERIES_EVENT_PRESET_VALUES.join(",");
30865
- const publicRouteCandidatesSchema = z.object({ routes: z.array(z.object({
30866
- entityId: z.string(),
30867
- path: z.string(),
30868
- title: z.string(),
30869
- kind: z.enum(RESOLVED_INTERNAL_LINK_ENTITY_TYPES),
30870
- contentTypeKey: z.string().nullable().optional()
30871
- })) });
31460
+ //#region ../blocks/src/system/blocks/offerings-preview/fields.ts
30872
31461
  const groupOverrideFields = [
30873
31462
  {
30874
31463
  id: "sourceKey",
@@ -31074,7 +31663,65 @@ const offeringsPreviewFields = [
31074
31663
  renderInHeader: false
31075
31664
  })
31076
31665
  ];
31666
+ function labelForOfferingPreviewSourceKey(sourceKey) {
31667
+ const eventDefinition = OFFERINGS_PREVIEW_EVENT_PRESET_DEFINITIONS.find((definition) => definition.sourceKey === sourceKey);
31668
+ if (eventDefinition) return eventDefinition.label;
31669
+ return "Courses";
31670
+ }
31671
+ //#endregion
31672
+ //#region ../blocks/src/system/blocks/offerings-preview/index.ts
31673
+ /**
31674
+ * The whole-series presets, as the CSV `listPublicEvents` expects. Both
31675
+ * whole-series presets share one loader: the block renders them as a single
31676
+ * run group.
31677
+ */
31678
+ const WHOLE_SERIES_EVENT_PRESETS_PARAM = WHOLE_SERIES_EVENT_PRESET_VALUES.join(",");
31679
+ const publicRouteCandidatesSchema = z.object({ routes: z.array(z.object({
31680
+ entityId: z.string(),
31681
+ path: z.string(),
31682
+ title: z.string(),
31683
+ kind: z.enum(RESOLVED_INTERNAL_LINK_ENTITY_TYPES),
31684
+ contentTypeKey: z.string().nullable().optional()
31685
+ })) });
31077
31686
  const offeringsPreviewEventDataBindings = Object.fromEntries(OFFERINGS_PREVIEW_EVENT_PRESET_DEFINITIONS.map((definition) => [definition.dataKey, { $bind: { from: `data.${definition.dataKey}` } }]));
31687
+ const DEFAULT_OFFERINGS_PREVIEW_DISPLAY_MODE = "preview";
31688
+ const displayModeBinding = {
31689
+ from: "content.displayMode",
31690
+ fallback: DEFAULT_OFFERINGS_PREVIEW_DISPLAY_MODE
31691
+ };
31692
+ const catalogueMode = { $bind: {
31693
+ ...displayModeBinding,
31694
+ transforms: [{
31695
+ id: "value.equals",
31696
+ options: { value: "catalogue" }
31697
+ }]
31698
+ } };
31699
+ const previewMode = { $bind: {
31700
+ ...displayModeBinding,
31701
+ transforms: [{
31702
+ id: "value.equals",
31703
+ options: { value: "preview" }
31704
+ }]
31705
+ } };
31706
+ function offeringsPreviewHeaderNodes() {
31707
+ return [text({
31708
+ as: "h2",
31709
+ className: { $bind: {
31710
+ from: "content.displayMode",
31711
+ fallback: "rb-text-center",
31712
+ transforms: [{
31713
+ id: "value.equals",
31714
+ options: { value: "catalogue" }
31715
+ }, {
31716
+ id: "ui.conditionalClass",
31717
+ options: {
31718
+ whenTrue: "rb-text-left",
31719
+ whenFalse: "rb-text-center"
31720
+ }
31721
+ }]
31722
+ } }
31723
+ }, bind("content.heading"), when("content.heading")), richText({ className: "rb-prose rb-max-w-none" }, bind("content.description"), when("content.description"))];
31724
+ }
31078
31725
  const offeringsPreviewManifest = createBlockManifest({
31079
31726
  id: "block.offerings-preview",
31080
31727
  version: "1.0.0",
@@ -31105,48 +31752,45 @@ const offeringsPreviewManifest = createBlockManifest({
31105
31752
  ],
31106
31753
  icon: "PanelsTopLeft",
31107
31754
  layout: [styledSection({
31108
- children: sectionContainer([
31109
- text({
31110
- as: "h2",
31111
- className: "rb-text-center"
31112
- }, bind("content.heading"), when("content.heading")),
31113
- richText({ className: "rb-prose rb-max-w-none" }, bind("content.description"), when("content.description")),
31114
- el("offerings-preview", {
31115
- bookingOfferingPresets: { $bind: {
31116
- from: "$root.bookingOfferingPresets",
31117
- fallback: []
31118
- } },
31119
- groupOverrides: { $bind: {
31120
- from: "content.groupOverrides",
31121
- fallback: []
31122
- } },
31123
- groupOrder: { $bind: { from: "content.groupOrder" } },
31124
- cardStyles: { $bind: { from: "content._cardStyles" } },
31125
- itemLimit: { $bind: {
31126
- from: "content.itemLimit",
31127
- fallback: "3"
31128
- } },
31129
- buttonVariant: { $bind: {
31130
- from: "content.buttonVariant",
31131
- fallback: "primary"
31132
- } },
31133
- buttonText: { $bind: {
31134
- from: "content.buttonText",
31135
- fallback: OFFERINGS_PREVIEW_DEFAULT_BUTTON_TEXT
31136
- } },
31137
- emptyMessage: { $bind: {
31138
- from: "content.emptyMessage",
31139
- fallback: OFFERINGS_PREVIEW_DEFAULT_EMPTY_MESSAGE
31140
- } },
31141
- previewStage: { $bind: {
31142
- from: "$root.previewStage",
31143
- fallback: "published"
31144
- } },
31145
- routeCandidates: { $bind: { from: "data.routeCandidates" } },
31146
- wholeSeriesRunEvents: { $bind: { from: "data.wholeSeriesRunEvents" } },
31147
- ...offeringsPreviewEventDataBindings
31148
- })
31149
- ], { gap: "lg" }),
31755
+ children: sectionContainer([el("offerings-preview", {
31756
+ displayMode: { $bind: displayModeBinding },
31757
+ pathname: { $bind: { from: "$root.pathname" } },
31758
+ catalogueEvents: { $bind: { from: "data.catalogueEvents" } },
31759
+ quickDateEvents: { $bind: { from: "data.quickDateEvents" } },
31760
+ bookingOfferingPresets: { $bind: {
31761
+ from: "$root.bookingOfferingPresets",
31762
+ fallback: []
31763
+ } },
31764
+ groupOverrides: { $bind: {
31765
+ from: "content.groupOverrides",
31766
+ fallback: []
31767
+ } },
31768
+ groupOrder: { $bind: { from: "content.groupOrder" } },
31769
+ cardStyles: { $bind: { from: "content._cardStyles" } },
31770
+ itemLimit: { $bind: {
31771
+ from: "content.itemLimit",
31772
+ fallback: "3"
31773
+ } },
31774
+ buttonVariant: { $bind: {
31775
+ from: "content.buttonVariant",
31776
+ fallback: "primary"
31777
+ } },
31778
+ buttonText: { $bind: {
31779
+ from: "content.buttonText",
31780
+ fallback: OFFERINGS_PREVIEW_DEFAULT_BUTTON_TEXT
31781
+ } },
31782
+ emptyMessage: { $bind: {
31783
+ from: "content.emptyMessage",
31784
+ fallback: OFFERINGS_PREVIEW_DEFAULT_EMPTY_MESSAGE
31785
+ } },
31786
+ previewStage: { $bind: {
31787
+ from: "$root.previewStage",
31788
+ fallback: "published"
31789
+ } },
31790
+ routeCandidates: { $bind: { from: "data.routeCandidates" } },
31791
+ wholeSeriesRunEvents: { $bind: { from: "data.wholeSeriesRunEvents" } },
31792
+ ...offeringsPreviewEventDataBindings
31793
+ }, offeringsPreviewHeaderNodes())], { gap: "lg" }),
31150
31794
  baseClass: "",
31151
31795
  spacing: "comfortable"
31152
31796
  })]
@@ -31160,16 +31804,48 @@ const offeringsPreviewBlockDefinition = {
31160
31804
  dataSchemas: {
31161
31805
  ...offeringsPreviewEventDataSchemas,
31162
31806
  routeCandidates: publicRouteCandidatesSchema.optional(),
31163
- wholeSeriesRunEvents: optionalPublicEventsArraySchema
31807
+ wholeSeriesRunEvents: optionalPublicEventsArraySchema,
31808
+ catalogueEvents: optionalPublicEventsArraySchema,
31809
+ quickDateEvents: optionalPublicEventsArraySchema
31164
31810
  },
31165
31811
  dataLoaders: {
31166
31812
  ...offeringsPreviewEventDataLoaders,
31167
31813
  routeCandidates: {
31168
31814
  endpoint: "listPublicRouteCandidates",
31815
+ when: previewMode,
31169
31816
  params: {},
31170
31817
  mode: "server"
31171
31818
  },
31172
- wholeSeriesRunEvents: wholeSeriesRunLoader()
31819
+ wholeSeriesRunEvents: wholeSeriesRunLoader(),
31820
+ catalogueEvents: {
31821
+ endpoint: "listPublicEvents",
31822
+ when: catalogueMode,
31823
+ params: {
31824
+ stage: { $bind: {
31825
+ from: "$root.previewStage",
31826
+ fallback: "published"
31827
+ } },
31828
+ detailLevel: "listing",
31829
+ readProfile: "listing-card",
31830
+ readIntent: "catalogue"
31831
+ },
31832
+ mode: "server"
31833
+ },
31834
+ quickDateEvents: {
31835
+ endpoint: "listPublicEvents",
31836
+ when: catalogueMode,
31837
+ params: {
31838
+ stage: { $bind: {
31839
+ from: "$root.previewStage",
31840
+ fallback: "published"
31841
+ } },
31842
+ detailLevel: "listing",
31843
+ readProfile: "listing-card",
31844
+ readIntent: "occurrences",
31845
+ limit: 40
31846
+ },
31847
+ mode: "server"
31848
+ }
31173
31849
  }
31174
31850
  };
31175
31851
  /**
@@ -31186,6 +31862,7 @@ function wholeSeriesRunLoader() {
31186
31862
  function eventPresetLoader(eventPresets, extraParams = {}) {
31187
31863
  return {
31188
31864
  endpoint: "listPublicEvents",
31865
+ when: previewMode,
31189
31866
  params: {
31190
31867
  siteId: { $bind: { from: "$root.siteId" } },
31191
31868
  limit: { $bind: {
@@ -31204,15 +31881,10 @@ function eventPresetLoader(eventPresets, extraParams = {}) {
31204
31881
  mode: "server"
31205
31882
  };
31206
31883
  }
31207
- function labelForOfferingPreviewSourceKey(sourceKey) {
31208
- const eventDefinition = OFFERINGS_PREVIEW_EVENT_PRESET_DEFINITIONS.find((definition) => definition.sourceKey === sourceKey);
31209
- if (eventDefinition) return eventDefinition.label;
31210
- return "Courses";
31211
- }
31212
31884
  function normalizeOfferingsPreviewBlockContent(value) {
31213
31885
  const record = normalizeOfferingsPreviewContentForWrite(value);
31214
- if (!record) return {};
31215
- const normalized = {};
31886
+ if (!record) return { displayMode: DEFAULT_OFFERINGS_PREVIEW_DISPLAY_MODE };
31887
+ const normalized = { displayMode: normalizeOfferingsPreviewDisplayMode(record.displayMode) };
31216
31888
  if (typeof record.heading === "string" || record.heading === null) normalized.heading = record.heading;
31217
31889
  if ("description" in record) normalized.description = record.description;
31218
31890
  if (isOfferingsPreviewItemLimit(record.itemLimit)) normalized.itemLimit = record.itemLimit;
@@ -31229,6 +31901,9 @@ function normalizeOfferingsPreviewBlockContent(value) {
31229
31901
  if (sectionStyles !== void 0) normalized._sectionStyles = sectionStyles;
31230
31902
  return normalized;
31231
31903
  }
31904
+ function normalizeOfferingsPreviewDisplayMode(value) {
31905
+ return value === "catalogue" ? "catalogue" : DEFAULT_OFFERINGS_PREVIEW_DISPLAY_MODE;
31906
+ }
31232
31907
  function isOfferingsPreviewItemLimit(value) {
31233
31908
  return value === "1" || value === "2" || value === "3" || value === "4" || value === "5" || value === "6" || value === null;
31234
31909
  }
@@ -34443,63 +35118,13 @@ const giftingBlockDefinition = {
34443
35118
  } }
34444
35119
  };
34445
35120
  //#endregion
34446
- //#region ../blocks/src/system/blocks/newsletter-signup-destination.ts
34447
- function parseNewsletterSignupBlockId(value) {
34448
- const trimmed = parseTrimmedString(value);
34449
- return trimmed && BLOCK_ID_PATTERN.test(trimmed) ? trimmed : null;
34450
- }
34451
- function parseSignedNewsletterDestinationRef(value) {
34452
- const trimmed = parseTrimmedString(value);
34453
- return trimmed && SIGNED_DESTINATION_REF_PATTERN.test(trimmed) ? trimmed : null;
34454
- }
34455
- function buildNewsletterSignupBlockRef(blockId) {
34456
- const parsed = parseNewsletterSignupBlockId(blockId);
34457
- return parsed ? {
34458
- tag: "block_ref",
34459
- blockId: parsed
34460
- } : null;
34461
- }
34462
- function normalizeNewsletterSignupEditorValuesForDestination(values) {
34463
- const destinationProvider = parseNewsletterSignupDestinationProvider(values.destinationProvider) ?? "riverbank";
34464
- if (destinationProvider === "mailchimp") return {
34465
- destinationProvider,
34466
- mailchimpAudienceId: values.mailchimpAudienceId,
34467
- mailchimpTags: values.mailchimpTags
34468
- };
34469
- return {
34470
- destinationProvider,
34471
- listId: values.listId,
34472
- tags: values.tags
34473
- };
34474
- }
34475
- function deriveNewsletterSignupRuntimeSource(values) {
34476
- const normalized = normalizeNewsletterSignupEditorValuesForDestination(values);
34477
- if (normalized.destinationProvider === "mailchimp") return {
34478
- destinationProvider: "mailchimp",
34479
- listId: null,
34480
- tags: null
34481
- };
34482
- return {
34483
- destinationProvider: "riverbank",
34484
- listId: parseTrimmedString(normalized.listId),
34485
- tags: parseTrimmedString(normalized.tags)
34486
- };
34487
- }
34488
- const BLOCK_ID_PATTERN = /^[A-Za-z0-9._:-]{1,200}$/;
34489
- const SIGNED_DESTINATION_REF_PATTERN = /^[A-Za-z0-9._~-]{16,1000}$/;
34490
- function parseTrimmedString(value) {
34491
- if (typeof value !== "string") return null;
34492
- const trimmed = value.trim();
34493
- return trimmed.length > 0 ? trimmed : null;
34494
- }
34495
- //#endregion
34496
35121
  //#region ../blocks/src/system/runtime/nodes/newsletter-form.types.ts
34497
35122
  /**
34498
35123
  * Get form fields based on preset configuration.
34499
35124
  */
34500
- function getPresetFields(preset, options) {
34501
- const includePhone = Boolean(options?.includePhone);
34502
- switch (preset) {
35125
+ function getGlobalFormFields(form) {
35126
+ const includePhone = form.includeOptionalPhone;
35127
+ switch (form.fields.preset) {
34503
35128
  case "emailOnly": return withOptionalPhone([{
34504
35129
  id: "email",
34505
35130
  type: "email",
@@ -34517,7 +35142,7 @@ function getPresetFields(preset, options) {
34517
35142
  id: "name",
34518
35143
  type: "text",
34519
35144
  label: "Name",
34520
- required: false,
35145
+ required: form.fields.name === "required",
34521
35146
  placeholder: "Your name"
34522
35147
  }], includePhone);
34523
35148
  case "emailFirstLast": return withOptionalPhone([
@@ -34532,14 +35157,14 @@ function getPresetFields(preset, options) {
34532
35157
  id: "firstName",
34533
35158
  type: "text",
34534
35159
  label: "First Name",
34535
- required: false,
35160
+ required: form.fields.firstName === "required",
34536
35161
  placeholder: "First name"
34537
35162
  },
34538
35163
  {
34539
35164
  id: "lastName",
34540
35165
  type: "text",
34541
35166
  label: "Last Name",
34542
- required: false,
35167
+ required: form.fields.lastName === "required",
34543
35168
  placeholder: "Last name"
34544
35169
  }
34545
35170
  ], includePhone);
@@ -34555,14 +35180,14 @@ function getPresetFields(preset, options) {
34555
35180
  id: "name",
34556
35181
  type: "text",
34557
35182
  label: "Name",
34558
- required: false,
35183
+ required: form.fields.name === "required",
34559
35184
  placeholder: "Your name"
34560
35185
  },
34561
35186
  {
34562
35187
  id: "company",
34563
35188
  type: "text",
34564
35189
  label: "Company",
34565
- required: false,
35190
+ required: form.fields.company === "required",
34566
35191
  placeholder: "Company name"
34567
35192
  }
34568
35193
  ], includePhone);
@@ -34581,14 +35206,29 @@ function withOptionalPhone(fields, includePhone) {
34581
35206
  }
34582
35207
  //#endregion
34583
35208
  //#region ../blocks/src/system/runtime/nodes/newsletter-form.interactive.ts
34584
- const newsletterPresetSchema = z.enum([
34585
- "emailOnly",
34586
- "emailName",
34587
- "emailFirstLast",
34588
- "emailNameCompany"
34589
- ]);
34590
- const newsletterDestinationProviderSchema = z.enum(["riverbank", "mailchimp"]);
34591
35209
  const newsletterButtonVariantSchema = blockButtonRoleSchema;
35210
+ const mailingListContextSchema = z.unknown().transform((value, ctx) => {
35211
+ const parsed = parseMailingListRuntimeContext(value);
35212
+ if (!parsed.ok) {
35213
+ ctx.addIssue({
35214
+ code: z.ZodIssueCode.custom,
35215
+ message: "Invalid mailing-list runtime context"
35216
+ });
35217
+ return z.NEVER;
35218
+ }
35219
+ return parsed.value;
35220
+ });
35221
+ const mailingListTargetingSchema = z.unknown().transform((value, ctx) => {
35222
+ const parsed = parseMailingListPlacementTargeting(value);
35223
+ if (!parsed.ok) {
35224
+ ctx.addIssue({
35225
+ code: z.ZodIssueCode.custom,
35226
+ message: "Invalid mailing-list targeting"
35227
+ });
35228
+ return z.NEVER;
35229
+ }
35230
+ return parsed.value;
35231
+ });
34592
35232
  const newsletterFieldSchema = z.object({
34593
35233
  id: z.string(),
34594
35234
  type: z.enum(["email", "text"]),
@@ -34596,73 +35236,73 @@ const newsletterFieldSchema = z.object({
34596
35236
  required: z.boolean(),
34597
35237
  placeholder: z.string().optional()
34598
35238
  });
34599
- const newsletterSignupBlockIdSchema = z.string().trim().min(1).transform(parseRequiredNewsletterSignupBlockId);
34600
- const signedNewsletterDestinationRefSchema = z.string().trim().min(1).transform(parseRequiredSignedNewsletterDestinationRef);
35239
+ const mailingListSignupBlockIdSchema = z.string().trim().min(1).transform(parseRequiredMailingListSignupBlockId);
35240
+ const signedMailingListSourceTokenSchema = z.string().trim().min(1).transform(parseRequiredMailingListSignedSourceToken);
34601
35241
  const newsletterSourceCommonSchema = z.object({
34602
35242
  siteId: z.string().optional(),
34603
35243
  apiBaseUrl: z.string().optional(),
34604
- preset: newsletterPresetSchema.optional(),
34605
- includePhone: z.boolean().optional(),
34606
- buttonLabel: z.string().nullable().optional(),
35244
+ mailingList: mailingListContextSchema.optional(),
35245
+ targeting: mailingListTargetingSchema.optional(),
34607
35246
  buttonVariant: newsletterButtonVariantSchema.nullable().optional(),
34608
- successMessage: z.string().nullable().optional(),
34609
- privacyNote: z.string().nullable().optional(),
34610
- tags: z.string().nullable().optional(),
34611
35247
  spamProtectionEnabled: z.boolean().optional(),
34612
35248
  className: z.string().nullable().optional()
34613
35249
  });
34614
35250
  const newsletterSourceSchema = z.discriminatedUnion("source", [
34615
35251
  newsletterSourceCommonSchema.extend({
34616
35252
  source: z.literal("block"),
34617
- blockId: z.string().nullable().optional(),
34618
- listId: z.string().nullable().optional(),
34619
- destinationProvider: newsletterDestinationProviderSchema.nullable().optional()
35253
+ blockId: z.string().nullable().optional()
34620
35254
  }),
34621
35255
  newsletterSourceCommonSchema.extend({
34622
35256
  source: z.literal("signed-footer"),
34623
- destinationRef: z.object({
34624
- tag: z.literal("signed_destination_ref"),
34625
- token: signedNewsletterDestinationRefSchema
35257
+ sourceRef: z.object({
35258
+ tag: z.literal("signed_source_ref"),
35259
+ token: signedMailingListSourceTokenSchema
34626
35260
  })
34627
35261
  }),
34628
- newsletterSourceCommonSchema.extend({
34629
- source: z.literal("legacy-direct-list"),
34630
- listId: z.string().nullable()
34631
- })
35262
+ newsletterSourceCommonSchema.extend({ source: z.literal("preview") })
34632
35263
  ]);
34633
35264
  const newsletterDisplaySchema = z.object({
34634
35265
  kind: z.literal("newsletter-form"),
35266
+ availability: z.enum(["ready", "unavailable"]),
34635
35267
  className: z.string().nullable(),
34636
35268
  buttonLabel: z.string(),
34637
35269
  buttonVariant: newsletterButtonVariantSchema,
34638
- successMessage: z.string().nullable(),
34639
35270
  privacyNote: z.string().nullable(),
34640
- fields: z.array(newsletterFieldSchema)
35271
+ fields: z.array(newsletterFieldSchema),
35272
+ choices: z.array(z.object({
35273
+ id: z.string(),
35274
+ label: z.string()
35275
+ }))
34641
35276
  });
34642
35277
  const newsletterHydrationSchema = z.object({
34643
35278
  target: z.discriminatedUnion("tag", [
34644
35279
  z.object({
34645
35280
  tag: z.literal("block-source"),
34646
- destinationRef: z.object({
35281
+ sourceRef: z.object({
34647
35282
  tag: z.literal("block_ref"),
34648
- blockId: newsletterSignupBlockIdSchema
35283
+ blockId: mailingListSignupBlockIdSchema
34649
35284
  }).nullable()
34650
35285
  }),
34651
35286
  z.object({
34652
35287
  tag: z.literal("signed-footer-source"),
34653
- destinationRef: z.object({
34654
- tag: z.literal("signed_destination_ref"),
34655
- token: signedNewsletterDestinationRefSchema
35288
+ sourceRef: z.object({
35289
+ tag: z.literal("signed_source_ref"),
35290
+ token: signedMailingListSourceTokenSchema
34656
35291
  })
34657
35292
  }),
34658
- z.object({
34659
- tag: z.literal("legacy-direct-list"),
34660
- listId: z.string().nullable()
34661
- })
35293
+ z.object({ tag: z.literal("preview") })
35294
+ ]),
35295
+ setupRevision: z.number().int().positive().nullable(),
35296
+ choiceProvider: z.enum([
35297
+ "native",
35298
+ "brevo",
35299
+ "mailchimp"
35300
+ ]).nullable(),
35301
+ choicePresentation: z.enum([
35302
+ "hidden",
35303
+ "automatic",
35304
+ "visitor_choice"
34662
35305
  ]),
34663
- preset: newsletterPresetSchema,
34664
- includePhone: z.boolean(),
34665
- tags: z.string().nullable().optional(),
34666
35306
  spamProtectionEnabled: z.boolean().optional()
34667
35307
  });
34668
35308
  const newsletterRenderSchema = z.object({
@@ -34674,16 +35314,24 @@ z.object({
34674
35314
  apiBaseUrl: z.string().optional()
34675
35315
  });
34676
35316
  function buildNewsletterDisplay(source) {
34677
- const preset = source.preset ?? "emailOnly";
34678
- const includePhone = source.includePhone ?? false;
35317
+ const context = source.mailingList;
35318
+ const form = context?.tag === "configured" ? context.form : {
35319
+ fields: { preset: "emailOnly" },
35320
+ includeOptionalPhone: false,
35321
+ buttonLabel: "Subscribe",
35322
+ privacyNote: null
35323
+ };
35324
+ const targeting = source.targeting ?? { mode: "default" };
35325
+ const choices = resolveRuntimeChoices(context, targeting);
34679
35326
  return {
34680
35327
  kind: "newsletter-form",
35328
+ availability: runtimeTargetingIsAvailable(context, targeting, choices) ? "ready" : "unavailable",
34681
35329
  className: source.className ?? null,
34682
- buttonLabel: source.buttonLabel?.trim() || "Subscribe",
35330
+ buttonLabel: form.buttonLabel,
34683
35331
  buttonVariant: source.buttonVariant ?? "primary",
34684
- successMessage: source.successMessage ?? "Thanks for subscribing!",
34685
- privacyNote: source.privacyNote ?? null,
34686
- fields: getPresetFields(preset, { includePhone })
35332
+ privacyNote: form.privacyNote,
35333
+ fields: getGlobalFormFields(form),
35334
+ choices
34687
35335
  };
34688
35336
  }
34689
35337
  const newsletterFormBlockRuntime = defineProgressiveBlockRuntime({
@@ -34693,14 +35341,15 @@ const newsletterFormBlockRuntime = defineProgressiveBlockRuntime({
34693
35341
  render: createZodCodec(newsletterRenderSchema)
34694
35342
  },
34695
35343
  buildRender: (source) => {
34696
- const runtimeSource = source.source === "block" ? deriveNewsletterSignupRuntimeSource(source) : null;
35344
+ const targeting = source.targeting ?? { mode: "default" };
35345
+ const presentation = planMailingListChoicePresentation(targeting);
34697
35346
  return {
34698
35347
  display: buildNewsletterDisplay(source),
34699
35348
  hydration: {
34700
35349
  target: projectNewsletterFormHydrationTarget(source),
34701
- preset: source.preset ?? "emailOnly",
34702
- includePhone: source.includePhone ?? false,
34703
- tags: runtimeSource ? runtimeSource.tags : source.tags,
35350
+ setupRevision: source.mailingList?.tag === "configured" ? source.mailingList.revision : null,
35351
+ choiceProvider: targeting.mode === "custom" ? targeting.provider : null,
35352
+ choicePresentation: presentation.tag,
34704
35353
  spamProtectionEnabled: source.spamProtectionEnabled
34705
35354
  }
34706
35355
  };
@@ -34709,35 +35358,87 @@ const newsletterFormBlockRuntime = defineProgressiveBlockRuntime({
34709
35358
  function projectNewsletterFormHydrationTarget(source) {
34710
35359
  switch (source.source) {
34711
35360
  case "block": {
34712
- const destinationRef = buildNewsletterSignupBlockRef(source.blockId);
34713
- if (destinationRef?.tag === "block_ref") return {
35361
+ const sourceRef = buildMailingListSignupBlockSourceRef(source.blockId);
35362
+ if (sourceRef?.tag === "block_ref") return {
34714
35363
  tag: "block-source",
34715
- destinationRef
35364
+ sourceRef
34716
35365
  };
34717
35366
  return {
34718
- tag: "legacy-direct-list",
34719
- listId: source.listId ?? null
35367
+ tag: "block-source",
35368
+ sourceRef: null
34720
35369
  };
34721
35370
  }
34722
35371
  case "signed-footer": return {
34723
35372
  tag: "signed-footer-source",
34724
- destinationRef: source.destinationRef
34725
- };
34726
- case "legacy-direct-list": return {
34727
- tag: "legacy-direct-list",
34728
- listId: source.listId
35373
+ sourceRef: source.sourceRef
34729
35374
  };
35375
+ case "preview": return { tag: "preview" };
34730
35376
  default: return assertNever$3(source);
34731
35377
  }
34732
35378
  }
34733
- function parseRequiredNewsletterSignupBlockId(value) {
34734
- const parsed = parseNewsletterSignupBlockId(value);
34735
- if (!parsed) throw new Error("Newsletter signup block destination ref requires blockId");
35379
+ function resolveRuntimeChoices(context, targeting) {
35380
+ if (context?.tag !== "configured" || targeting.mode !== "custom" || targeting.selection.tag !== "visitor_choice" || context.provider.provider !== targeting.provider) return [];
35381
+ switch (targeting.provider) {
35382
+ case "native": {
35383
+ const offered = new Set(targeting.selection.offeredTopicIds);
35384
+ return context.provider.provider === "native" ? context.provider.topics.filter((item) => offered.has(item.id)).map((item) => ({
35385
+ id: item.id,
35386
+ label: item.name
35387
+ })) : [];
35388
+ }
35389
+ case "brevo": {
35390
+ const offered = new Set(targeting.selection.offeredListIds);
35391
+ return context.provider.provider === "brevo" ? context.provider.lists.filter((item) => offered.has(item.id)).map((item) => ({
35392
+ id: String(item.id),
35393
+ label: item.name
35394
+ })) : [];
35395
+ }
35396
+ case "mailchimp": {
35397
+ const offered = new Set(targeting.selection.offeredInterestIds);
35398
+ return (context.provider.provider === "mailchimp" ? context.provider.audiences.find((item) => item.id === targeting.audienceId) : void 0)?.interests.filter((item) => offered.has(item.id)).map((item) => ({
35399
+ id: item.id,
35400
+ label: item.name
35401
+ })) ?? [];
35402
+ }
35403
+ }
35404
+ }
35405
+ function runtimeTargetingIsAvailable(context, targeting, choices) {
35406
+ if (context?.tag !== "configured") return false;
35407
+ const provider = context.provider;
35408
+ if (targeting.mode === "default") switch (provider.provider) {
35409
+ case "native": return provider.topics.some((item) => item.id === provider.defaultTopicId);
35410
+ case "brevo": return provider.lists.some((item) => item.id === provider.defaultListId);
35411
+ case "mailchimp": return provider.audiences.some((item) => item.id === provider.defaultAudienceId);
35412
+ }
35413
+ if (targeting.provider !== provider.provider) return false;
35414
+ switch (targeting.provider) {
35415
+ case "native": return provider.provider === "native" && nativeTargetIsAvailable(provider, targeting, choices);
35416
+ case "brevo": return provider.provider === "brevo" && brevoTargetIsAvailable(provider, targeting, choices);
35417
+ case "mailchimp": return provider.provider === "mailchimp" && mailchimpTargetIsAvailable(provider, targeting, choices);
35418
+ }
35419
+ }
35420
+ function nativeTargetIsAvailable(provider, targeting, choices) {
35421
+ const ids = targeting.selection.tag === "fixed" ? targeting.selection.topicIds : targeting.selection.offeredTopicIds;
35422
+ return ids.every((id) => provider.topics.some((item) => item.id === id)) && (targeting.selection.tag !== "visitor_choice" || choices.length === ids.length);
35423
+ }
35424
+ function brevoTargetIsAvailable(provider, targeting, choices) {
35425
+ const ids = targeting.selection.tag === "fixed" ? targeting.selection.listIds : targeting.selection.offeredListIds;
35426
+ return ids.every((id) => provider.lists.some((item) => item.id === id)) && (targeting.selection.tag !== "visitor_choice" || choices.length === ids.length);
35427
+ }
35428
+ function mailchimpTargetIsAvailable(provider, targeting, choices) {
35429
+ const audience = provider.audiences.find((item) => item.id === targeting.audienceId);
35430
+ if (!audience) return false;
35431
+ const ids = targeting.selection.tag === "fixed" ? targeting.selection.interestIds : targeting.selection.offeredInterestIds;
35432
+ return ids.every((id) => audience.interests.some((item) => item.id === id)) && (targeting.selection.tag !== "visitor_choice" || choices.length === ids.length);
35433
+ }
35434
+ function parseRequiredMailingListSignupBlockId(value) {
35435
+ const parsed = parseMailingListBlockId(value);
35436
+ if (!parsed) throw new Error("Mailing-list signup block source ref requires blockId");
34736
35437
  return parsed;
34737
35438
  }
34738
- function parseRequiredSignedNewsletterDestinationRef(value) {
34739
- const parsed = parseSignedNewsletterDestinationRef(value);
34740
- if (!parsed) throw new Error("Newsletter signup signed destination ref requires token");
35439
+ function parseRequiredMailingListSignedSourceToken(value) {
35440
+ const parsed = parseMailingListSignedSourceToken(value);
35441
+ if (!parsed) throw new Error("Mailing-list signup signed source ref requires token");
34741
35442
  return parsed;
34742
35443
  }
34743
35444
  //#endregion
@@ -36110,6 +36811,14 @@ const systemBlockDefinitions = [
36110
36811
  weight: "standard"
36111
36812
  })]
36112
36813
  })),
36814
+ withDesignCapability("block.appointment-services", appointmentServicesBlockDefinition, sectionOnly(), defineBlockDesignMetadata({
36815
+ flow: conversionFlow,
36816
+ layoutProfiles: [layoutProfile({
36817
+ variantKey: "default",
36818
+ family: "stack",
36819
+ weight: "standard"
36820
+ })]
36821
+ })),
36113
36822
  withDesignCapability("block.weekly-slot-availability", weeklySlotAvailabilityBlockDefinition, sectionOnly(), defineBlockDesignMetadata({
36114
36823
  flow: offeringsFlow,
36115
36824
  layoutProfiles: [layoutProfile({
@@ -38725,6 +39434,7 @@ const SYSTEM_BLOCK_KINDS = [
38725
39434
  "block.form",
38726
39435
  "block.contact-newsletter",
38727
39436
  "block.appointment-booking",
39437
+ "block.appointment-services",
38728
39438
  "block.weekly-slot-availability",
38729
39439
  "block.event-registration",
38730
39440
  "block.event-details",