@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
@@ -17,12 +17,12 @@ function err(error) {
17
17
  }
18
18
  //#endregion
19
19
  //#region ../core/src/branded.ts
20
- const UUID_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
20
+ 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;
21
21
  function parseUuidBrand(value, unsafeConstructor) {
22
22
  if (typeof value !== "string") return err({ kind: "not_string" });
23
23
  const normalized = value.trim().toLowerCase();
24
24
  if (normalized.length === 0) return err({ kind: "empty_string" });
25
- if (!UUID_PATTERN.test(normalized)) return err({ kind: "invalid_uuid" });
25
+ if (!UUID_PATTERN$1.test(normalized)) return err({ kind: "invalid_uuid" });
26
26
  return ok(unsafeConstructor(normalized));
27
27
  }
28
28
  const unsafeAsSiteId = (id) => id;
@@ -267,7 +267,7 @@ function parseEventPageSurfaceDestinationMode(value) {
267
267
  return value === "cms-managed" || value === "system-managed" ? value : void 0;
268
268
  }
269
269
  function parseEventPageSurfaceSubject(value) {
270
- if (!isRecord$13(value)) return err({ kind: "notObject" });
270
+ if (!isRecord$15(value)) return err({ kind: "notObject" });
271
271
  switch (value.kind) {
272
272
  case "eventSeries": {
273
273
  const eventSeriesId = parseEventSeriesId(value.eventSeriesId);
@@ -322,7 +322,7 @@ function parseEventSeriesIdList(values) {
322
322
  }
323
323
  return ok(parsed);
324
324
  }
325
- function isRecord$13(value) {
325
+ function isRecord$15(value) {
326
326
  return typeof value === "object" && value !== null && !Array.isArray(value);
327
327
  }
328
328
  //#endregion
@@ -492,9 +492,379 @@ function toLocationCoordinates(value) {
492
492
  };
493
493
  }
494
494
  //#endregion
495
- //#region ../core/src/newsletter-signup-destination.ts
496
- function parseNewsletterSignupDestinationProvider(value) {
497
- return value === "riverbank" || value === "mailchimp" ? value : null;
495
+ //#region ../core/src/mailing-list-identifiers.ts
496
+ 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;
497
+ const OPAQUE_ID_PATTERN = /^[A-Za-z0-9_-]{1,100}$/u;
498
+ function parseTrimmedMatchingString(value, pattern) {
499
+ if (typeof value !== "string") return null;
500
+ const trimmed = value.trim();
501
+ return pattern.test(trimmed) ? trimmed : null;
502
+ }
503
+ function parsePositiveInteger(value) {
504
+ return typeof value === "number" && Number.isSafeInteger(value) && value > 0 ? value : null;
505
+ }
506
+ function parseNewsletterListId(value) {
507
+ return parseTrimmedMatchingString(value, UUID_PATTERN);
508
+ }
509
+ function parseMailchimpAudienceId(value) {
510
+ return parseTrimmedMatchingString(value, OPAQUE_ID_PATTERN);
511
+ }
512
+ function parseMailchimpInterestId(value) {
513
+ return parseTrimmedMatchingString(value, OPAQUE_ID_PATTERN);
514
+ }
515
+ function parseBrevoListId(value) {
516
+ return parsePositiveInteger(value);
517
+ }
518
+ //#endregion
519
+ //#region ../core/src/mailing-list-setup-parser.ts
520
+ function parseMailingListGlobalForm(value) {
521
+ if (!isRecord$14(value)) return formError("shape");
522
+ const fields = parseGlobalFields(value.fields);
523
+ if (fields === null) return formError("fields");
524
+ if (typeof value.includeOptionalPhone !== "boolean") return formError("includeOptionalPhone");
525
+ const buttonLabel = parseRequiredString(value.buttonLabel);
526
+ if (buttonLabel === null) return formError("buttonLabel");
527
+ const privacyNote = parseOptionalString(value.privacyNote);
528
+ if (privacyNote === void 0) return formError("privacyNote");
529
+ return ok({
530
+ fields,
531
+ includeOptionalPhone: value.includeOptionalPhone,
532
+ buttonLabel,
533
+ privacyNote
534
+ });
535
+ }
536
+ function parseGlobalFields(value) {
537
+ if (!isRecord$14(value) || typeof value.preset !== "string") return null;
538
+ switch (value.preset) {
539
+ case "emailOnly": return { preset: "emailOnly" };
540
+ case "emailName": return isRequirement(value.name) ? {
541
+ preset: "emailName",
542
+ name: value.name
543
+ } : null;
544
+ case "emailFirstLast": return isRequirement(value.firstName) && isRequirement(value.lastName) ? {
545
+ preset: "emailFirstLast",
546
+ firstName: value.firstName,
547
+ lastName: value.lastName
548
+ } : null;
549
+ case "emailNameCompany": return isRequirement(value.name) && isRequirement(value.company) ? {
550
+ preset: "emailNameCompany",
551
+ name: value.name,
552
+ company: value.company
553
+ } : null;
554
+ default: return null;
555
+ }
556
+ }
557
+ function parseMailingListSetupRevision(value) {
558
+ return typeof value === "number" && Number.isSafeInteger(value) && value > 0 ? value : null;
559
+ }
560
+ function parseRequiredString(value) {
561
+ if (typeof value !== "string") return null;
562
+ const trimmed = value.trim();
563
+ return trimmed.length > 0 ? trimmed : null;
564
+ }
565
+ function parseOptionalString(value) {
566
+ if (value === null || value === void 0) return null;
567
+ if (typeof value !== "string") return void 0;
568
+ const trimmed = value.trim();
569
+ return trimmed.length > 0 ? trimmed : null;
570
+ }
571
+ function isRequirement(value) {
572
+ return value === "required" || value === "optional";
573
+ }
574
+ function isRecord$14(value) {
575
+ return typeof value === "object" && value !== null && !Array.isArray(value);
576
+ }
577
+ function formError(field) {
578
+ return err({
579
+ tag: "invalid_mailing_list_global_form",
580
+ field
581
+ });
582
+ }
583
+ //#endregion
584
+ //#region ../core/src/mailing-list-targeting.ts
585
+ function planMailingListChoicePresentation(targeting) {
586
+ if (targeting.mode === "default" || targeting.selection.tag === "fixed") return { tag: "hidden" };
587
+ switch (targeting.provider) {
588
+ case "native": return targeting.selection.offeredTopicIds.length === 1 ? {
589
+ tag: "automatic",
590
+ selection: {
591
+ provider: "native",
592
+ topicIds: targeting.selection.offeredTopicIds
593
+ }
594
+ } : {
595
+ tag: "visitor_choice",
596
+ provider: "native",
597
+ optionCount: targeting.selection.offeredTopicIds.length
598
+ };
599
+ case "brevo": return targeting.selection.offeredListIds.length === 1 ? {
600
+ tag: "automatic",
601
+ selection: {
602
+ provider: "brevo",
603
+ listIds: targeting.selection.offeredListIds
604
+ }
605
+ } : {
606
+ tag: "visitor_choice",
607
+ provider: "brevo",
608
+ optionCount: targeting.selection.offeredListIds.length
609
+ };
610
+ case "mailchimp": return targeting.selection.offeredInterestIds.length === 1 ? {
611
+ tag: "automatic",
612
+ selection: {
613
+ provider: "mailchimp",
614
+ interestIds: targeting.selection.offeredInterestIds
615
+ }
616
+ } : {
617
+ tag: "visitor_choice",
618
+ provider: "mailchimp",
619
+ optionCount: targeting.selection.offeredInterestIds.length
620
+ };
621
+ }
622
+ }
623
+ //#endregion
624
+ //#region ../core/src/mailing-list-runtime.ts
625
+ function parseMailingListRuntimeContext(value) {
626
+ if (!isRecord$13(value)) return invalidRuntimeContext();
627
+ if (value.tag === "not_configured") return ok({ tag: "not_configured" });
628
+ if (value.tag !== "configured") return invalidRuntimeContext();
629
+ const revision = parseMailingListSetupRevision(value.revision);
630
+ const form = parseMailingListGlobalForm(value.form);
631
+ const provider = parseRuntimeProvider(value.provider);
632
+ if (revision === null || !form.ok || provider === null) return invalidRuntimeContext();
633
+ return ok({
634
+ tag: "configured",
635
+ revision,
636
+ form: form.value,
637
+ provider
638
+ });
639
+ }
640
+ function parseRuntimeProvider(value) {
641
+ if (!isRecord$13(value)) return null;
642
+ switch (value.provider) {
643
+ case "native": return parseNativeRuntimeProvider(value);
644
+ case "brevo": return parseBrevoRuntimeProvider(value);
645
+ case "mailchimp": return parseMailchimpRuntimeProvider(value);
646
+ default: return null;
647
+ }
648
+ }
649
+ function parseNativeRuntimeProvider(value) {
650
+ const defaultTopicId = parseNewsletterListId(value.defaultTopicId);
651
+ const topics = parseNamedItems(value.topics, parseNewsletterListId);
652
+ return defaultTopicId !== null && topics !== null ? {
653
+ provider: "native",
654
+ defaultTopicId,
655
+ topics
656
+ } : null;
657
+ }
658
+ function parseBrevoRuntimeProvider(value) {
659
+ const defaultListId = parseBrevoListId(value.defaultListId);
660
+ const lists = parseNamedItems(value.lists, parseBrevoListId);
661
+ return defaultListId !== null && lists !== null ? {
662
+ provider: "brevo",
663
+ defaultListId,
664
+ lists
665
+ } : null;
666
+ }
667
+ function parseMailchimpRuntimeProvider(value) {
668
+ if (!Array.isArray(value.audiences)) return null;
669
+ const defaultAudienceId = parseMailchimpAudienceId(value.defaultAudienceId);
670
+ if (defaultAudienceId === null) return null;
671
+ const audiences = [];
672
+ for (const audience of value.audiences) {
673
+ if (!isRecord$13(audience)) return null;
674
+ const id = parseMailchimpAudienceId(audience.id);
675
+ const name = parseName(audience.name);
676
+ if (id === null || name === null || !Array.isArray(audience.interests)) return null;
677
+ const interests = parseMailchimpInterests(audience.interests);
678
+ if (interests === null) return null;
679
+ audiences.push({
680
+ id,
681
+ name,
682
+ interests
683
+ });
684
+ }
685
+ return {
686
+ provider: "mailchimp",
687
+ defaultAudienceId,
688
+ audiences
689
+ };
690
+ }
691
+ function parseMailchimpInterests(value) {
692
+ const interests = [];
693
+ for (const interest of value) {
694
+ if (!isRecord$13(interest)) return null;
695
+ const id = parseMailchimpInterestId(interest.id);
696
+ const name = parseName(interest.name);
697
+ const categoryName = parseName(interest.categoryName);
698
+ if (id === null || name === null || categoryName === null) return null;
699
+ interests.push({
700
+ id,
701
+ name,
702
+ categoryName
703
+ });
704
+ }
705
+ return interests;
706
+ }
707
+ function parseNamedItems(value, parseId) {
708
+ if (!Array.isArray(value)) return null;
709
+ const items = [];
710
+ for (const item of value) {
711
+ if (!isRecord$13(item)) return null;
712
+ const id = parseId(item.id);
713
+ const name = parseName(item.name);
714
+ if (id === null || name === null) return null;
715
+ items.push({
716
+ id,
717
+ name
718
+ });
719
+ }
720
+ return items;
721
+ }
722
+ function parseName(value) {
723
+ if (typeof value !== "string") return null;
724
+ const name = value.trim();
725
+ return name.length > 0 ? name : null;
726
+ }
727
+ function isRecord$13(value) {
728
+ return typeof value === "object" && value !== null && !Array.isArray(value);
729
+ }
730
+ function invalidRuntimeContext() {
731
+ return err({ tag: "invalid_mailing_list_runtime_context" });
732
+ }
733
+ //#endregion
734
+ //#region ../core/src/mailing-list-targeting-parser.ts
735
+ function parseMailingListPlacementTargeting(value) {
736
+ if (!isRecord$12(value)) return targetingError("shape");
737
+ if (value.mode === "default") return ok({ mode: "default" });
738
+ if (value.mode !== "custom") return targetingError("mode");
739
+ if (!isRecord$12(value.selection)) return targetingError("selection");
740
+ switch (value.provider) {
741
+ case "native": return parseNativeTargeting(value.selection);
742
+ case "brevo": return parseBrevoTargeting(value.selection);
743
+ case "mailchimp": return parseMailchimpTargeting(value, value.selection);
744
+ default: return targetingError("provider");
745
+ }
746
+ }
747
+ function parseNativeTargeting(selection) {
748
+ if (selection.tag === "fixed") {
749
+ const topicIds = parseNonEmptyUniqueIds(selection.topicIds, parseNewsletterListId);
750
+ return topicIds.ok ? ok({
751
+ mode: "custom",
752
+ provider: "native",
753
+ selection: {
754
+ tag: "fixed",
755
+ topicIds: topicIds.value
756
+ }
757
+ }) : topicIds;
758
+ }
759
+ if (selection.tag === "visitor_choice") {
760
+ const offeredTopicIds = parseNonEmptyUniqueIds(selection.offeredTopicIds, parseNewsletterListId);
761
+ return offeredTopicIds.ok ? ok({
762
+ mode: "custom",
763
+ provider: "native",
764
+ selection: {
765
+ tag: "visitor_choice",
766
+ offeredTopicIds: offeredTopicIds.value
767
+ }
768
+ }) : offeredTopicIds;
769
+ }
770
+ return targetingError("selection");
771
+ }
772
+ function parseBrevoTargeting(selection) {
773
+ if (selection.tag === "fixed") {
774
+ const listIds = parseNonEmptyUniqueIds(selection.listIds, parseBrevoListId);
775
+ return listIds.ok ? ok({
776
+ mode: "custom",
777
+ provider: "brevo",
778
+ selection: {
779
+ tag: "fixed",
780
+ listIds: listIds.value
781
+ }
782
+ }) : listIds;
783
+ }
784
+ if (selection.tag === "visitor_choice") {
785
+ const offeredListIds = parseNonEmptyUniqueIds(selection.offeredListIds, parseBrevoListId);
786
+ return offeredListIds.ok ? ok({
787
+ mode: "custom",
788
+ provider: "brevo",
789
+ selection: {
790
+ tag: "visitor_choice",
791
+ offeredListIds: offeredListIds.value
792
+ }
793
+ }) : offeredListIds;
794
+ }
795
+ return targetingError("selection");
796
+ }
797
+ function parseMailchimpTargeting(value, selection) {
798
+ const audienceId = parseMailchimpAudienceId(value.audienceId);
799
+ if (audienceId === null) return targetingError("audienceId");
800
+ if (selection.tag === "fixed") {
801
+ const interestIds = parseUniqueIds(selection.interestIds, parseMailchimpInterestId);
802
+ return interestIds.ok ? ok({
803
+ mode: "custom",
804
+ provider: "mailchimp",
805
+ audienceId,
806
+ selection: {
807
+ tag: "fixed",
808
+ interestIds: interestIds.value
809
+ }
810
+ }) : interestIds;
811
+ }
812
+ if (selection.tag === "visitor_choice") {
813
+ const offeredInterestIds = parseNonEmptyUniqueIds(selection.offeredInterestIds, parseMailchimpInterestId);
814
+ return offeredInterestIds.ok ? ok({
815
+ mode: "custom",
816
+ provider: "mailchimp",
817
+ audienceId,
818
+ selection: {
819
+ tag: "visitor_choice",
820
+ offeredInterestIds: offeredInterestIds.value
821
+ }
822
+ }) : offeredInterestIds;
823
+ }
824
+ return targetingError("selection");
825
+ }
826
+ function parseNonEmptyUniqueIds(value, parseId) {
827
+ const parsed = parseUniqueIds(value, parseId);
828
+ if (!parsed.ok) return parsed;
829
+ const [first, ...rest] = parsed.value;
830
+ return first === void 0 ? targetingError("targets") : ok([first, ...rest]);
831
+ }
832
+ function parseUniqueIds(value, parseId) {
833
+ if (!Array.isArray(value)) return targetingError("targets");
834
+ const parsed = [];
835
+ for (const item of value) {
836
+ const id = parseId(item);
837
+ if (id === null) return targetingError("targets");
838
+ if (parsed.includes(id)) return targetingError("duplicate_target");
839
+ parsed.push(id);
840
+ }
841
+ return ok(parsed);
842
+ }
843
+ function isRecord$12(value) {
844
+ return typeof value === "object" && value !== null && !Array.isArray(value);
845
+ }
846
+ function targetingError(field) {
847
+ return err({
848
+ tag: "invalid_mailing_list_targeting",
849
+ field
850
+ });
851
+ }
852
+ //#endregion
853
+ //#region ../core/src/mailing-list-submission.ts
854
+ function parseMailingListBlockId(value) {
855
+ const parsed = typeof value === "string" ? value.trim() : "";
856
+ 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;
857
+ }
858
+ function parseMailingListSignedSourceToken(value) {
859
+ const parsed = typeof value === "string" ? value.trim() : "";
860
+ return /^[A-Za-z0-9._~-]{16,1000}$/u.test(parsed) ? parsed : null;
861
+ }
862
+ function buildMailingListSignupBlockSourceRef(blockId) {
863
+ const parsed = parseMailingListBlockId(blockId);
864
+ return parsed !== null ? {
865
+ tag: "block_ref",
866
+ blockId: parsed
867
+ } : null;
498
868
  }
499
869
  //#endregion
500
870
  //#region ../core/src/secret.ts
@@ -556,6 +926,56 @@ function sessionStartMs(session) {
556
926
  function orderedLiveRunSessions(sessions) {
557
927
  return sessions.filter((session) => session.status !== "cancelled" && Number.isFinite(sessionStartMs(session))).sort((left, right) => sessionStartMs(left) - sessionStartMs(right));
558
928
  }
929
+ const PUBLIC_CONTENT_FRESHNESS_SECONDS = 3600;
930
+ function availability(transport) {
931
+ return {
932
+ kind: "live",
933
+ reason: "booking_correctness",
934
+ freshness: "availability",
935
+ transport
936
+ };
937
+ }
938
+ function content(tags, transport = "direct") {
939
+ return {
940
+ kind: "tagged",
941
+ tags,
942
+ freshness: "content",
943
+ transport
944
+ };
945
+ }
946
+ /** The single classification authority for cacheable public reads. */
947
+ const PUBLIC_READ_CACHE_POLICIES = {
948
+ listPublishedEntries: content(["published_content"]),
949
+ getPublishedEntryPreview: content(["published_content"]),
950
+ listPublicEvents: availability("direct"),
951
+ listPublicEventCalendarWindow: availability("direct"),
952
+ listPublicRecurringSchedule: availability("batch_only"),
953
+ getPublicEventDetails: availability("batch_only"),
954
+ resolveEventSubroute: availability("direct"),
955
+ listPublicRouteCandidates: content(["published_content"], "batch_only"),
956
+ listPublicTeamMembers: content(["public_team_members"]),
957
+ listPublicProducts: availability("direct"),
958
+ listPublicPasses: availability("direct"),
959
+ listPublicMemberships: availability("direct"),
960
+ getPublicFormById: content(["site_forms"]),
961
+ getPublicBookingServices: availability("direct"),
962
+ getPublicBookingService: availability("direct"),
963
+ getPublicBookingResourcesReference: availability("direct"),
964
+ getPublicBookingAvailableDates: availability("direct"),
965
+ getPublicBookingAvailableSlots: availability("direct"),
966
+ getPublicWeeklySlotAvailability: availability("direct"),
967
+ listPublicForms: content(["site_forms"]),
968
+ checkRedirect: content([]),
969
+ getPublicRoutableContent: content(["published_content"]),
970
+ getPublicSitemapRoutes: content(["published_content"])
971
+ };
972
+ new Set(Object.keys(PUBLIC_READ_CACHE_POLICIES));
973
+ function getPublicReadCachePolicy(endpoint) {
974
+ return PUBLIC_READ_CACHE_POLICIES[endpoint];
975
+ }
976
+ function publicReadFreshnessSeconds(endpoint) {
977
+ return getPublicReadCachePolicy(endpoint).freshness === "availability" ? 60 : PUBLIC_CONTENT_FRESHNESS_SECONDS;
978
+ }
559
979
  //#endregion
560
980
  //#region ../core/src/event-series-behaviour.ts
561
981
  const WHOLE_SERIES_EVENT_PRESET_VALUES = ["course", "term"];
@@ -851,7 +1271,7 @@ function resolveSiteVerticalBookingConfig(rawConfig) {
851
1271
  }
852
1272
  //#endregion
853
1273
  //#region ../media-core/src/typeGuards.ts
854
- function isRecord$12(value) {
1274
+ function isRecord$11(value) {
855
1275
  return Boolean(value) && typeof value === "object" && !Array.isArray(value);
856
1276
  }
857
1277
  //#endregion
@@ -969,8 +1389,8 @@ function walkAndCollectImageAttrs(value, out, location = "") {
969
1389
  value.forEach((item, index) => walkAndCollectImageAttrs(item, out, `${location}[${index}]`));
970
1390
  return;
971
1391
  }
972
- if (!isRecord$12(value)) return;
973
- if (value.type === "image" && isRecord$12(value.attrs)) out.push({
1392
+ if (!isRecord$11(value)) return;
1393
+ if (value.type === "image" && isRecord$11(value.attrs)) out.push({
974
1394
  attrs: value.attrs,
975
1395
  location: location ? `${location}.attrs` : "attrs"
976
1396
  });
@@ -6415,7 +6835,7 @@ const boxBackgroundSchema = z.discriminatedUnion("type", [
6415
6835
  })
6416
6836
  ]);
6417
6837
  function normalizeAuthoringOpacity(value) {
6418
- if (!isRecord$11(value)) return value;
6838
+ if (!isRecord$10(value)) return value;
6419
6839
  return {
6420
6840
  ...value,
6421
6841
  opacity: normalizeOpacity(value.opacity)
@@ -6424,7 +6844,7 @@ function normalizeAuthoringOpacity(value) {
6424
6844
  function normalizeOpacity(value) {
6425
6845
  return typeof value === "number" && value > 1 && value <= 100 ? value / 100 : value;
6426
6846
  }
6427
- function isRecord$11(value) {
6847
+ function isRecord$10(value) {
6428
6848
  return typeof value === "object" && value !== null && !Array.isArray(value);
6429
6849
  }
6430
6850
  //#endregion
@@ -7175,7 +7595,7 @@ const PUBLIC_DOWNLOAD_MEDIA_TYPES = [
7175
7595
  /**
7176
7596
  * Strict record guard: excludes null, primitives, and arrays.
7177
7597
  */
7178
- function isRecord$10(value) {
7598
+ function isRecord$9(value) {
7179
7599
  return typeof value === "object" && value !== null && !Array.isArray(value);
7180
7600
  }
7181
7601
  /**
@@ -7807,6 +8227,14 @@ const calendarWindowPageSchema = z.discriminatedUnion("kind", [z.object({
7807
8227
  kind: z.literal("partial"),
7808
8228
  nextCursor: calendarSourceCursorSchema
7809
8229
  })]);
8230
+ //#endregion
8231
+ //#region ../api/src/apiEndpointTypes.ts
8232
+ function allowedDemoMutationPolicy() {
8233
+ return {
8234
+ classification: "allowed",
8235
+ publicMessage: ""
8236
+ };
8237
+ }
7810
8238
  function blockedDemoMutationPolicy(classification, publicMessage) {
7811
8239
  if (publicMessage.trim() === "") throw new Error(`Demo mutation policy ${classification} requires publicMessage.`);
7812
8240
  return {
@@ -7819,6 +8247,7 @@ function blockedDemoMutationPolicy(classification, publicMessage) {
7819
8247
  const DEMO_MUTATION_BLOCKED_HIDDEN = blockedDemoMutationPolicy("blocked-hidden", "This action is not available in demo sites.");
7820
8248
  const DEMO_MUTATION_BLOCKED_DISABLED = blockedDemoMutationPolicy("blocked-disabled", "This setting is disabled in demo sites.");
7821
8249
  blockedDemoMutationPolicy("internal-only", "This internal operation is not available in demo sites.");
8250
+ const DEMO_MUTATION_ALLOWED = allowedDemoMutationPolicy();
7822
8251
  //#endregion
7823
8252
  //#region ../api/src/cache-tags.ts
7824
8253
  /**
@@ -8750,14 +9179,117 @@ const NEWSLETTER_ENDPOINT_DEFINITIONS = defineEndpointDefinitions({
8750
9179
  responseKind: "json",
8751
9180
  errors: ["auth:forbidden", "server:internal_error"]
8752
9181
  },
8753
- getMailchimpSignupSyncHealth: {
8754
- path: "/sites/{siteId}/integrations/mailchimp/signup-sync-health",
9182
+ getMailingListSetup: {
9183
+ path: "/sites/{siteId}/mailing-list/setup",
9184
+ method: "GET",
9185
+ tags: [TAG_TEMPLATES.site, TAG_TEMPLATES.siteNewsletter],
9186
+ auth: "user",
9187
+ responseKind: "json",
9188
+ errors: ["auth:forbidden", "server:internal_error"]
9189
+ },
9190
+ getMailingListAuthoringContext: {
9191
+ path: "/sites/{siteId}/mailing-list/authoring-context",
9192
+ method: "GET",
9193
+ tags: [TAG_TEMPLATES.site, TAG_TEMPLATES.siteNewsletter],
9194
+ auth: "user",
9195
+ responseKind: "json",
9196
+ errors: ["auth:forbidden", "server:internal_error"]
9197
+ },
9198
+ previewMailingListSetupChange: {
9199
+ path: "/sites/{siteId}/mailing-list/setup/preview",
9200
+ method: "POST",
9201
+ tags: [TAG_TEMPLATES.site, TAG_TEMPLATES.siteNewsletter],
9202
+ auth: "user",
9203
+ responseKind: "json",
9204
+ demoMutation: DEMO_MUTATION_ALLOWED,
9205
+ errors: [
9206
+ "auth:forbidden",
9207
+ "validation:invalid_input",
9208
+ "server:internal_error"
9209
+ ]
9210
+ },
9211
+ applyMailingListSetupChange: {
9212
+ path: "/sites/{siteId}/mailing-list/setup",
9213
+ method: "PUT",
9214
+ tags: [TAG_TEMPLATES.site, TAG_TEMPLATES.siteNewsletter],
9215
+ auth: "user",
9216
+ responseKind: "json",
9217
+ demoMutation: DEMO_MUTATION_BLOCKED_DISABLED,
9218
+ errors: [
9219
+ "auth:forbidden",
9220
+ "validation:invalid_input",
9221
+ "resource:conflict",
9222
+ "server:internal_error"
9223
+ ]
9224
+ },
9225
+ getMailingListProviderCatalog: {
9226
+ path: "/sites/{siteId}/mailing-list/providers/{provider}/catalog",
9227
+ method: "GET",
9228
+ tags: [TAG_TEMPLATES.site, TAG_TEMPLATES.siteNewsletter],
9229
+ auth: "user",
9230
+ responseKind: "json",
9231
+ errors: [
9232
+ "auth:forbidden",
9233
+ "validation:invalid_input",
9234
+ "server:internal_error"
9235
+ ]
9236
+ },
9237
+ refreshMailingListProviderCatalog: {
9238
+ path: "/sites/{siteId}/mailing-list/providers/{provider}/catalog/refresh",
9239
+ method: "POST",
9240
+ tags: [TAG_TEMPLATES.site, TAG_TEMPLATES.siteNewsletter],
9241
+ auth: "user",
9242
+ responseKind: "json",
9243
+ demoMutation: DEMO_MUTATION_BLOCKED_DISABLED,
9244
+ errors: [
9245
+ "auth:forbidden",
9246
+ "validation:invalid_input",
9247
+ "server:internal_error"
9248
+ ]
9249
+ },
9250
+ getMailingListProviderHealth: {
9251
+ path: "/sites/{siteId}/mailing-list/health",
9252
+ method: "GET",
9253
+ tags: [TAG_TEMPLATES.site, TAG_TEMPLATES.siteNewsletter],
9254
+ auth: "user",
9255
+ responseKind: "json",
9256
+ errors: ["auth:forbidden", "server:internal_error"]
9257
+ },
9258
+ getBrevoConnection: {
9259
+ path: "/sites/{siteId}/integrations/brevo/connection",
8755
9260
  method: "GET",
8756
9261
  tags: [TAG_TEMPLATES.site],
8757
9262
  auth: "user",
8758
9263
  responseKind: "json",
8759
9264
  errors: ["auth:forbidden", "server:internal_error"]
8760
9265
  },
9266
+ connectBrevo: {
9267
+ path: "/sites/{siteId}/integrations/brevo/connection",
9268
+ method: "PUT",
9269
+ tags: [TAG_TEMPLATES.site],
9270
+ auth: "user",
9271
+ responseKind: "json",
9272
+ demoMutation: DEMO_MUTATION_BLOCKED_DISABLED,
9273
+ errors: [
9274
+ "auth:forbidden",
9275
+ "validation:invalid_input",
9276
+ "resource:conflict",
9277
+ "server:internal_error"
9278
+ ]
9279
+ },
9280
+ disconnectBrevo: {
9281
+ path: "/sites/{siteId}/integrations/brevo/disconnect",
9282
+ method: "POST",
9283
+ tags: [TAG_TEMPLATES.site],
9284
+ auth: "user",
9285
+ responseKind: "json",
9286
+ demoMutation: DEMO_MUTATION_BLOCKED_DISABLED,
9287
+ errors: [
9288
+ "auth:forbidden",
9289
+ "resource:conflict",
9290
+ "server:internal_error"
9291
+ ]
9292
+ },
8761
9293
  getMailchimpConnection: {
8762
9294
  path: "/sites/{siteId}/integrations/mailchimp/connection",
8763
9295
  method: "GET",
@@ -9013,7 +9545,7 @@ const PUBLIC_EVENT_ENDPOINT_DEFINITIONS = defineEndpointDefinitions({
9013
9545
  resolveEventSubroute: {
9014
9546
  path: "/public/sites/{siteId}/events/subroutes/resolve",
9015
9547
  method: "GET",
9016
- revalidate: 60,
9548
+ revalidate: publicReadFreshnessSeconds("resolveEventSubroute"),
9017
9549
  tags: [TAG_TEMPLATES.publicEvents, "event-subroute"],
9018
9550
  auth: "public",
9019
9551
  responseKind: "json",
@@ -9066,7 +9598,7 @@ const PUBLIC_BOOKING_AVAILABILITY_ENDPOINT_DEFINITIONS = defineEndpointDefinitio
9066
9598
  getPublicBookingServices: {
9067
9599
  path: "/public/bookings/services",
9068
9600
  method: "GET",
9069
- revalidate: 60,
9601
+ revalidate: publicReadFreshnessSeconds("getPublicBookingServices"),
9070
9602
  tags: [TAG_TEMPLATES.publicBookingServices],
9071
9603
  auth: "public",
9072
9604
  responseKind: "json"
@@ -9074,7 +9606,7 @@ const PUBLIC_BOOKING_AVAILABILITY_ENDPOINT_DEFINITIONS = defineEndpointDefinitio
9074
9606
  getPublicBookingService: {
9075
9607
  path: "/public/bookings/services/{serviceId}",
9076
9608
  method: "GET",
9077
- revalidate: 60,
9609
+ revalidate: publicReadFreshnessSeconds("getPublicBookingService"),
9078
9610
  tags: [TAG_TEMPLATES.publicBookingServices],
9079
9611
  auth: "public",
9080
9612
  responseKind: "json"
@@ -9082,7 +9614,7 @@ const PUBLIC_BOOKING_AVAILABILITY_ENDPOINT_DEFINITIONS = defineEndpointDefinitio
9082
9614
  getPublicBookingResourcesReference: {
9083
9615
  path: "/public/bookings/resources/reference",
9084
9616
  method: "GET",
9085
- revalidate: 60,
9617
+ revalidate: publicReadFreshnessSeconds("getPublicBookingResourcesReference"),
9086
9618
  tags: [TAG_TEMPLATES.publicBookingServices],
9087
9619
  auth: "public",
9088
9620
  responseKind: "json"
@@ -9090,7 +9622,7 @@ const PUBLIC_BOOKING_AVAILABILITY_ENDPOINT_DEFINITIONS = defineEndpointDefinitio
9090
9622
  getPublicBookingAvailableDates: {
9091
9623
  path: "/public/bookings/availability/dates",
9092
9624
  method: "GET",
9093
- revalidate: 60,
9625
+ revalidate: publicReadFreshnessSeconds("getPublicBookingAvailableDates"),
9094
9626
  tags: [TAG_TEMPLATES.publicBookingAvailability, TAG_TEMPLATES.publicBookingServices],
9095
9627
  auth: "public",
9096
9628
  responseKind: "json"
@@ -9098,7 +9630,7 @@ const PUBLIC_BOOKING_AVAILABILITY_ENDPOINT_DEFINITIONS = defineEndpointDefinitio
9098
9630
  getPublicBookingAvailableSlots: {
9099
9631
  path: "/public/bookings/availability/slots",
9100
9632
  method: "GET",
9101
- revalidate: 60,
9633
+ revalidate: publicReadFreshnessSeconds("getPublicBookingAvailableSlots"),
9102
9634
  tags: [TAG_TEMPLATES.publicBookingAvailability, TAG_TEMPLATES.publicBookingServices],
9103
9635
  auth: "public",
9104
9636
  responseKind: "json"
@@ -9106,7 +9638,7 @@ const PUBLIC_BOOKING_AVAILABILITY_ENDPOINT_DEFINITIONS = defineEndpointDefinitio
9106
9638
  getPublicWeeklySlotAvailability: {
9107
9639
  path: "/public/bookings/weekly-slot-availability",
9108
9640
  method: "GET",
9109
- revalidate: 60,
9641
+ revalidate: publicReadFreshnessSeconds("getPublicWeeklySlotAvailability"),
9110
9642
  tags: [TAG_TEMPLATES.publicBookingAvailability, TAG_TEMPLATES.publicBookingServices],
9111
9643
  auth: "public",
9112
9644
  responseKind: "json",
@@ -9556,15 +10088,91 @@ const SDK_MEDIA_ENDPOINT_DEFINITIONS = defineEndpointDefinitions({
9556
10088
  responseKind: "json"
9557
10089
  }
9558
10090
  });
10091
+ //#endregion
10092
+ //#region ../api/src/publicReadEndpointDefinitions.ts
10093
+ /**
10094
+ * Direct public reads governed by the canonical public-read freshness policy.
10095
+ * Keeping these definitions together makes the endpoint identity used to
10096
+ * derive freshness reviewable beside its route and tags.
10097
+ */
10098
+ const PUBLIC_READ_ENDPOINT_DEFINITIONS = defineEndpointDefinitions({
10099
+ getPublishedEntryPreview: {
10100
+ path: "/public/content/{siteId}/{type}/{slug}/preview",
10101
+ method: "GET",
10102
+ revalidate: publicReadFreshnessSeconds("getPublishedEntryPreview"),
10103
+ tags: ["content-{siteId}-{type}-{slug}"],
10104
+ auth: "public",
10105
+ responseKind: "json"
10106
+ },
10107
+ listPublishedEntries: {
10108
+ path: "/public/content/{siteId}/{type}/entries",
10109
+ method: "GET",
10110
+ revalidate: publicReadFreshnessSeconds("listPublishedEntries"),
10111
+ tags: ["content-{siteId}-{type}"],
10112
+ auth: "public",
10113
+ responseKind: "json"
10114
+ },
10115
+ getPublicFormById: {
10116
+ path: "/public/forms/{formId}",
10117
+ method: "GET",
10118
+ revalidate: publicReadFreshnessSeconds("getPublicFormById"),
10119
+ tags: [TAG_TEMPLATES.formById],
10120
+ auth: "public",
10121
+ responseKind: "json"
10122
+ },
10123
+ listPublicEvents: {
10124
+ path: "/public/sites/{siteId}/events",
10125
+ method: "GET",
10126
+ revalidate: publicReadFreshnessSeconds("listPublicEvents"),
10127
+ tags: [TAG_TEMPLATES.publicEvents],
10128
+ auth: "public",
10129
+ responseKind: "json"
10130
+ },
10131
+ listPublicTeamMembers: {
10132
+ path: "/public/sites/{siteId}/team-members",
10133
+ method: "GET",
10134
+ revalidate: publicReadFreshnessSeconds("listPublicTeamMembers"),
10135
+ tags: [TAG_TEMPLATES.publicTeamMembers],
10136
+ errors: [],
10137
+ auth: "public",
10138
+ responseKind: "json"
10139
+ },
10140
+ listPublicPasses: {
10141
+ path: "/public/sites/{siteId}/bookings/products/passes",
10142
+ method: "GET",
10143
+ revalidate: publicReadFreshnessSeconds("listPublicPasses"),
10144
+ tags: [TAG_TEMPLATES.publicPasses],
10145
+ auth: "public",
10146
+ responseKind: "json"
10147
+ },
10148
+ listPublicMemberships: {
10149
+ path: "/public/sites/{siteId}/bookings/products/memberships",
10150
+ method: "GET",
10151
+ revalidate: publicReadFreshnessSeconds("listPublicMemberships"),
10152
+ tags: [TAG_TEMPLATES.publicMemberships],
10153
+ auth: "public",
10154
+ responseKind: "json"
10155
+ },
10156
+ listPublicProducts: {
10157
+ path: "/public/sites/{siteId}/products",
10158
+ method: "GET",
10159
+ revalidate: publicReadFreshnessSeconds("listPublicProducts"),
10160
+ tags: [TAG_TEMPLATES.publicProducts],
10161
+ errors: [],
10162
+ auth: "public",
10163
+ responseKind: "json"
10164
+ }
10165
+ });
9559
10166
  ({
9560
10167
  ...ACCOUNT_ENDPOINT_DEFINITIONS,
9561
10168
  ...PUBLIC_EVENT_ENDPOINT_DEFINITIONS,
9562
- ...GIFTING_ENDPOINT_DEFINITIONS
10169
+ ...GIFTING_ENDPOINT_DEFINITIONS,
10170
+ ...PUBLIC_READ_ENDPOINT_DEFINITIONS
9563
10171
  }), 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, {
9564
10172
  ...NEWSLETTER_ENDPOINT_DEFINITIONS,
9565
10173
  ...APPOINTMENT_ENDPOINT_DEFINITIONS,
9566
10174
  ...EVENT_SERIES_REGISTRATION_ENDPOINT_DEFINITIONS
9567
- }, 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 };
10175
+ }, 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 };
9568
10176
  z.object({ refreshToken: z.string().min(1).max(4096) }).strict();
9569
10177
  z.object({
9570
10178
  accessToken: z.string().min(1).max(4096),
@@ -17664,7 +18272,7 @@ function isSemanticIconId(value) {
17664
18272
  //#endregion
17665
18273
  //#region ../blocks/src/system/icons/value.ts
17666
18274
  const NO_BLOCK_ICON_VALUE = { kind: "none" };
17667
- function isRecord$9(value) {
18275
+ function isRecord$8(value) {
17668
18276
  return typeof value === "object" && value !== null && !Array.isArray(value);
17669
18277
  }
17670
18278
  function parseBlockIconValue(value) {
@@ -17672,7 +18280,7 @@ function parseBlockIconValue(value) {
17672
18280
  kind: "semantic",
17673
18281
  iconId: value
17674
18282
  };
17675
- if (!isRecord$9(value)) return NO_BLOCK_ICON_VALUE;
18283
+ if (!isRecord$8(value)) return NO_BLOCK_ICON_VALUE;
17676
18284
  switch (value.kind) {
17677
18285
  case "none": return NO_BLOCK_ICON_VALUE;
17678
18286
  case "semantic": return isSemanticIconId(value.iconId) ? {
@@ -23134,102 +23742,31 @@ const newsletterFormFragment = defineFragment({
23134
23742
  description: "Newsletter subscription form with preset field configurations.",
23135
23743
  fields: [
23136
23744
  {
23137
- id: "destinationProvider",
23138
- type: "select",
23139
- label: "Send signups to",
23140
- description: "Choose where new subscribers from this form should go.",
23141
- defaultValue: "riverbank",
23142
- required: false,
23143
- ui: { intent: "behavior" },
23144
- options: [{
23145
- value: "riverbank",
23146
- label: "Riverbank newsletter"
23147
- }]
23148
- },
23149
- {
23150
- id: "listId",
23151
- type: "reference",
23152
- label: "Newsletter list",
23153
- description: "Select the list new subscribers should be added to.",
23154
- required: false,
23155
- referenceKind: "newsletterList",
23156
- allowManualEntry: false,
23157
- ui: {
23158
- intent: "behavior",
23159
- visibleWhen: {
23160
- field: "destinationProvider",
23161
- equals: "riverbank"
23162
- }
23163
- }
23164
- },
23165
- {
23166
- id: "mailchimpAudienceId",
23167
- type: "reference",
23168
- label: "Mailchimp audience",
23169
- description: "Choose the Mailchimp audience for new subscribers.",
23170
- required: false,
23171
- referenceKind: "mailchimpAudience",
23172
- allowManualEntry: false,
23173
- ui: {
23174
- intent: "behavior",
23175
- visibleWhen: {
23176
- field: "destinationProvider",
23177
- equals: "mailchimp"
23178
- }
23179
- }
23180
- },
23181
- {
23182
- id: "mailchimpTags",
23183
- type: "text",
23184
- label: "Mailchimp tags",
23185
- description: "Optional tags to apply in Mailchimp, separated by commas or semicolons.",
23745
+ id: "targeting",
23746
+ type: "group",
23747
+ label: "Mailing-list targeting",
23748
+ description: "Choose what kind of emails people signing up here will receive.",
23749
+ defaultValue: { mode: "default" },
23750
+ required: true,
23186
23751
  ui: {
23187
23752
  intent: "behavior",
23188
- visibleWhen: {
23189
- field: "destinationProvider",
23190
- equals: "mailchimp"
23191
- }
23192
- }
23193
- },
23194
- {
23195
- id: "preset",
23196
- type: "select",
23197
- label: "Form fields",
23198
- description: "Choose which fields to collect.",
23199
- defaultValue: "emailOnly",
23200
- ui: { intent: "display" },
23201
- options: [
23202
- {
23203
- value: "emailOnly",
23204
- label: "Email only"
23205
- },
23206
- {
23207
- value: "emailName",
23208
- label: "Email + Name"
23209
- },
23210
- {
23211
- value: "emailFirstLast",
23212
- label: "Email + First/Last Name"
23213
- },
23214
- {
23215
- value: "emailNameCompany",
23216
- label: "Email + Name + Company (B2B)"
23217
- }
23218
- ]
23219
- },
23220
- {
23221
- id: "includePhone",
23222
- type: "boolean",
23223
- label: "Include phone field (optional)",
23224
- description: "Adds an optional phone field to the selected preset (never required).",
23225
- defaultValue: false,
23226
- ui: { intent: "display" }
23227
- },
23228
- {
23229
- id: "buttonLabel",
23230
- type: "text",
23231
- label: "Button label",
23232
- defaultValue: "Subscribe"
23753
+ widget: "mailingListTargeting"
23754
+ },
23755
+ schema: { fields: [{
23756
+ id: "mode",
23757
+ type: "select",
23758
+ label: "Mode",
23759
+ required: true,
23760
+ defaultValue: "default",
23761
+ options: [{
23762
+ value: "default",
23763
+ label: "Default"
23764
+ }, {
23765
+ value: "custom",
23766
+ label: "Custom"
23767
+ }],
23768
+ ui: { hidden: true }
23769
+ }] }
23233
23770
  },
23234
23771
  createButtonVariantField({
23235
23772
  id: "buttonVariant",
@@ -23237,46 +23774,42 @@ const newsletterFormFragment = defineFragment({
23237
23774
  defaultValue: "primary"
23238
23775
  }),
23239
23776
  {
23240
- id: "successMessage",
23777
+ id: "mailingListSource",
23241
23778
  type: "text",
23242
- label: "Success message",
23243
- description: "Shown after successful subscription.",
23244
- defaultValue: "Thanks for subscribing!"
23245
- },
23246
- {
23247
- id: "privacyNote",
23248
- type: "text",
23249
- label: "Privacy note",
23250
- description: "Optional small text below the form (e.g., privacy policy link)."
23779
+ label: "Mailing-list source",
23780
+ ui: {
23781
+ hidden: true,
23782
+ intent: "behavior"
23783
+ }
23251
23784
  },
23252
23785
  {
23253
- id: "tags",
23786
+ id: "mailingListSourceToken",
23254
23787
  type: "text",
23255
- label: "Tags (optional)",
23256
- description: "Optional tags to apply on subscribe (comma or semicolon separated).",
23788
+ label: "Mailing-list source token",
23257
23789
  ui: {
23258
- intent: "behavior",
23259
- visibleWhen: {
23260
- field: "destinationProvider",
23261
- equals: "riverbank"
23262
- }
23790
+ hidden: true,
23791
+ intent: "behavior"
23263
23792
  }
23264
23793
  }
23265
23794
  ],
23266
23795
  layout: [el("newsletter-form", withApiBindings({
23267
- source: "block",
23268
- destinationProvider: { $bind: { from: "destinationProvider" } },
23269
- listId: { $bind: { from: "listId" } },
23270
- preset: { $bind: { from: "preset" } },
23271
- includePhone: { $bind: { from: "includePhone" } },
23272
- buttonLabel: { $bind: { from: "buttonLabel" } },
23796
+ source: { $bind: {
23797
+ from: "mailingListSource",
23798
+ fallback: "block"
23799
+ } },
23800
+ sourceRef: {
23801
+ tag: "signed_source_ref",
23802
+ token: { $bind: { from: "mailingListSourceToken" } }
23803
+ },
23804
+ targeting: { $bind: {
23805
+ from: "targeting",
23806
+ fallback: { mode: "default" }
23807
+ } },
23808
+ mailingList: { $bind: { from: "$root.mailingList" } },
23273
23809
  buttonVariant: { $bind: {
23274
23810
  from: "buttonVariant",
23275
23811
  fallback: "primary"
23276
23812
  } },
23277
- successMessage: { $bind: { from: "successMessage" } },
23278
- privacyNote: { $bind: { from: "privacyNote" } },
23279
- tags: { $bind: { from: "tags" } },
23280
23813
  spamProtectionEnabled: { $bind: { from: "$root.spamProtectionEnabled" } }
23281
23814
  }), [])]
23282
23815
  });
@@ -25451,14 +25984,19 @@ const contactNewsletterLayout = styledSection({
25451
25984
  as: "h2",
25452
25985
  className: "newsletter-copy-title"
25453
25986
  }, when("newsletterTitle"), bind("newsletterTitle")), richText({ className: "newsletter-copy-subtitle rb-prose rb-max-w-none" }, when("newsletterSubtitle"), bind("newsletterSubtitle"))]), el("newsletter-form", withApiBindings({
25454
- source: "block",
25455
- listId: { $bind: { from: "listId" } },
25456
- preset: { $bind: { from: "preset" } },
25457
- includePhone: { $bind: { from: "includePhone" } },
25458
- buttonLabel: { $bind: { from: "newsletterButtonLabel" } },
25459
- successMessage: { $bind: { from: "newsletterSuccessMessage" } },
25460
- privacyNote: { $bind: { from: "newsletterPrivacyNote" } },
25461
- tags: { $bind: { from: "newsletterTags" } },
25987
+ source: { $bind: {
25988
+ from: "mailingListSource",
25989
+ fallback: "block"
25990
+ } },
25991
+ sourceRef: {
25992
+ tag: "signed_source_ref",
25993
+ token: { $bind: { from: "mailingListSourceToken" } }
25994
+ },
25995
+ targeting: { $bind: {
25996
+ from: "targeting",
25997
+ fallback: { mode: "default" }
25998
+ } },
25999
+ mailingList: { $bind: { from: "$root.mailingList" } },
25462
26000
  spamProtectionEnabled: { $bind: { from: "$root.spamProtectionEnabled" } }
25463
26001
  }), [])])])], { className: "contact-newsletter-container" }),
25464
26002
  baseClass: "",
@@ -25494,79 +26032,55 @@ const contactNewsletterBlockDefinition = {
25494
26032
  label: "Newsletter intro"
25495
26033
  },
25496
26034
  {
25497
- id: "listId",
25498
- type: "reference",
25499
- label: "Newsletter list",
25500
- description: "Select the list new subscribers should be added to.",
26035
+ id: "targeting",
26036
+ type: "group",
26037
+ label: "Mailing-list targeting",
26038
+ description: "Choose what kind of emails people signing up here will receive.",
25501
26039
  required: true,
25502
- referenceKind: "newsletterList",
25503
- allowManualEntry: false,
25504
- ui: { intent: "behavior" }
25505
- },
25506
- {
25507
- id: "preset",
25508
- type: "select",
25509
- label: "Newsletter fields",
25510
- defaultValue: "emailOnly",
25511
- ui: { intent: "display" },
25512
- options: [
25513
- {
25514
- value: "emailOnly",
25515
- label: "Email only"
25516
- },
25517
- {
25518
- value: "emailName",
25519
- label: "Email + Name"
25520
- },
25521
- {
25522
- value: "emailFirstLast",
25523
- label: "Email + First/Last Name"
25524
- },
25525
- {
25526
- value: "emailNameCompany",
25527
- label: "Email + Name + Company (B2B)"
25528
- }
25529
- ]
25530
- },
25531
- {
25532
- id: "includePhone",
25533
- type: "boolean",
25534
- label: "Include phone field",
25535
- defaultValue: false,
25536
- ui: { intent: "display" }
25537
- },
25538
- {
25539
- id: "newsletterButtonLabel",
25540
- type: "text",
25541
- label: "Newsletter button label",
25542
- defaultValue: "Subscribe",
25543
- ui: { intent: "display" }
25544
- },
25545
- {
25546
- id: "newsletterSuccessMessage",
25547
- type: "text",
25548
- label: "Newsletter success message",
25549
- defaultValue: "Thanks for subscribing!",
25550
- ui: { intent: "behavior" }
26040
+ defaultValue: { mode: "default" },
26041
+ ui: {
26042
+ intent: "behavior",
26043
+ widget: "mailingListTargeting"
26044
+ },
26045
+ schema: { fields: [{
26046
+ id: "mode",
26047
+ type: "select",
26048
+ label: "Mode",
26049
+ required: true,
26050
+ defaultValue: "default",
26051
+ options: [{
26052
+ value: "default",
26053
+ label: "Default"
26054
+ }, {
26055
+ value: "custom",
26056
+ label: "Custom"
26057
+ }],
26058
+ ui: { hidden: true }
26059
+ }] }
25551
26060
  },
25552
26061
  {
25553
- id: "newsletterPrivacyNote",
26062
+ id: "contactTitle",
25554
26063
  type: "text",
25555
- label: "Privacy note",
25556
- ui: { intent: "display" }
26064
+ label: "Contact title",
26065
+ defaultValue: "Get in touch"
25557
26066
  },
25558
26067
  {
25559
- id: "newsletterTags",
26068
+ id: "mailingListSource",
25560
26069
  type: "text",
25561
- label: "Newsletter tags",
25562
- description: "Optional tags to apply on subscribe, separated by comma or semicolon.",
25563
- ui: { intent: "behavior" }
26070
+ label: "Mailing-list source",
26071
+ ui: {
26072
+ hidden: true,
26073
+ intent: "behavior"
26074
+ }
25564
26075
  },
25565
26076
  {
25566
- id: "contactTitle",
26077
+ id: "mailingListSourceToken",
25567
26078
  type: "text",
25568
- label: "Contact title",
25569
- defaultValue: "Get in touch"
26079
+ label: "Mailing-list source token",
26080
+ ui: {
26081
+ hidden: true,
26082
+ intent: "behavior"
26083
+ }
25570
26084
  },
25571
26085
  {
25572
26086
  id: "contactIntro",
@@ -25767,6 +26281,12 @@ const arrayLengthTransform = {
25767
26281
  summary: "Get the length of an array",
25768
26282
  run: (value) => Array.isArray(value) ? value.length : 0
25769
26283
  };
26284
+ const booleanNotTransform = {
26285
+ id: "boolean.not",
26286
+ kind: "formatter",
26287
+ summary: "Invert a boolean value; fail closed to false for other inputs",
26288
+ run: (value) => typeof value === "boolean" ? !value : false
26289
+ };
25770
26290
  const jsonArrayTransform = {
25771
26291
  id: "value.jsonArray",
25772
26292
  kind: "formatter",
@@ -25817,33 +26337,51 @@ const marqueeLoopTransform = {
25817
26337
  function toUnknownArray(value) {
25818
26338
  return Array.isArray(value) ? value : [];
25819
26339
  }
26340
+ /**
26341
+ * Map a content state/enum to a theme token colour — the first-class way to turn
26342
+ * a value like a class's availability (`available`/`filling`/`waitlist`), a
26343
+ * card's featured flag, or a pricing tier level into a themed colour. Authors
26344
+ * write role names and get `rgb(var(--tb-<role>))`, so the result live-recolours
26345
+ * with the theme and drops directly into a `style` binding (e.g.
26346
+ * `borderLeftColor`). Returns `undefined` when the value matches no case and no
26347
+ * fallback is given, so the style property is simply omitted.
26348
+ */
26349
+ const mapTokenTransform = {
26350
+ id: "value.mapToken",
26351
+ kind: "formatter",
26352
+ summary: "Map a state/enum value to a theme token colour (rgb(var(--tb-<role>)))",
26353
+ schema: z.object({
26354
+ /**
26355
+ * Maps an input state/enum value (stringified) to a theme token ROLE name
26356
+ * (e.g. `secondary`, `accent`, `muted`, `border`). The role is wrapped to
26357
+ * `rgb(var(--tb-<role>))` so the result drops straight into a `style` binding.
26358
+ */
26359
+ cases: z.record(z.string(), z.string()),
26360
+ /** Role used when the value matches no case. Omit to yield `undefined`. */
26361
+ fallback: z.string().optional()
26362
+ }),
26363
+ run: (value, options) => {
26364
+ const role = options.cases[String(value)] ?? options.fallback;
26365
+ return role ? `rgb(var(--tb-${role}))` : void 0;
26366
+ }
26367
+ };
25820
26368
  const formattingTransforms = [
26369
+ {
26370
+ id: "value.equals",
26371
+ kind: "formatter",
26372
+ summary: "Compare a value to a fixed string",
26373
+ schema: z.object({ value: z.string() }),
26374
+ run: (value, options) => value === options.value
26375
+ },
25821
26376
  uppercaseTransform,
25822
26377
  fallbackTransform,
25823
26378
  dateFormatShort,
25824
26379
  arrayLengthTransform,
26380
+ booleanNotTransform,
25825
26381
  jsonArrayTransform,
25826
26382
  csvTransform,
25827
26383
  marqueeLoopTransform,
25828
- {
25829
- id: "value.mapToken",
25830
- kind: "formatter",
25831
- summary: "Map a state/enum value to a theme token colour (rgb(var(--tb-<role>)))",
25832
- schema: z.object({
25833
- /**
25834
- * Maps an input state/enum value (stringified) to a theme token ROLE name
25835
- * (e.g. `secondary`, `accent`, `muted`, `border`). The role is wrapped to
25836
- * `rgb(var(--tb-<role>))` so the result drops straight into a `style` binding.
25837
- */
25838
- cases: z.record(z.string(), z.string()),
25839
- /** Role used when the value matches no case. Omit to yield `undefined`. */
25840
- fallback: z.string().optional()
25841
- }),
25842
- run: (value, options) => {
25843
- const role = options.cases[String(value)] ?? options.fallback;
25844
- return role ? `rgb(var(--tb-${role}))` : void 0;
25845
- }
25846
- }
26384
+ mapTokenTransform
25847
26385
  ];
25848
26386
  //#endregion
25849
26387
  //#region ../blocks/src/system/transforms/registry/ui.ts
@@ -26838,16 +27376,16 @@ const interpolateRichTextTokensOptionsSchema = z.object({ tokens: z.array(suppor
26838
27376
  const richTextTokenPattern = /\{\{\s*([a-zA-Z0-9_]+)\s*\}\}/g;
26839
27377
  function getSiteName(context) {
26840
27378
  const viewModel = context.viewModel;
26841
- if (!isRecord$10(viewModel)) return null;
27379
+ if (!isRecord$9(viewModel)) return null;
26842
27380
  const site = viewModel.site;
26843
- if (isRecord$10(site) && typeof site.title === "string") {
27381
+ if (isRecord$9(site) && typeof site.title === "string") {
26844
27382
  const title = site.title.trim();
26845
27383
  return title.length > 0 ? title : null;
26846
27384
  }
26847
27385
  const root = viewModel.$root;
26848
- if (isRecord$10(root)) {
27386
+ if (isRecord$9(root)) {
26849
27387
  const rootSite = root.site;
26850
- if (isRecord$10(rootSite) && typeof rootSite.title === "string") {
27388
+ if (isRecord$9(rootSite) && typeof rootSite.title === "string") {
26851
27389
  const title = rootSite.title.trim();
26852
27390
  if (title.length > 0) return title;
26853
27391
  }
@@ -26869,7 +27407,7 @@ function replaceTokenText(input, allowedTokens, tokenValues) {
26869
27407
  });
26870
27408
  }
26871
27409
  function visitRichTextNode(node, allowedTokens, tokenValues) {
26872
- if (!isRecord$10(node)) return node;
27410
+ if (!isRecord$9(node)) return node;
26873
27411
  let changed = false;
26874
27412
  const nextNode = { ...node };
26875
27413
  if (nextNode.type === "text" && typeof nextNode.text === "string") {
@@ -26890,7 +27428,7 @@ function visitRichTextNode(node, allowedTokens, tokenValues) {
26890
27428
  return changed ? nextNode : node;
26891
27429
  }
26892
27430
  function interpolateRichTextTokens(value, options, context) {
26893
- if (!isRecord$10(value)) return value;
27431
+ if (!isRecord$9(value)) return value;
26894
27432
  return visitRichTextNode(value, new Set(options.tokens), {
26895
27433
  year: String((/* @__PURE__ */ new Date()).getFullYear()),
26896
27434
  site_name: getSiteName(context)
@@ -27252,13 +27790,9 @@ const footerStandardContactBindings = {
27252
27790
  };
27253
27791
  const footerMailingListBindings = {
27254
27792
  source: { $bind: { from: "module.engagement.runtime.source" } },
27255
- destinationRef: { $bind: { from: "module.engagement.runtime.destinationRef" } },
27256
- listId: { $bind: { from: "module.engagement.runtime.listId" } },
27257
- preset: { $bind: { from: "module.engagement.runtime.preset" } },
27258
- includePhone: { $bind: { from: "module.engagement.runtime.includePhone" } },
27259
- buttonLabel: { $bind: { from: "module.engagement.runtime.buttonLabel" } },
27260
- successMessage: { $bind: { from: "module.engagement.runtime.successMessage" } },
27261
- privacyNote: { $bind: { from: "module.engagement.runtime.privacyNote" } }
27793
+ sourceRef: { $bind: { from: "module.engagement.runtime.sourceRef" } },
27794
+ mailingList: { $bind: { from: "module.engagement.runtime.mailingList" } },
27795
+ targeting: { $bind: { from: "module.engagement.runtime.targeting" } }
27262
27796
  };
27263
27797
  const footerSpamProtectionBinding = { spamProtectionEnabled: { $bind: { from: "$root.spamProtectionEnabled" } } };
27264
27798
  const brandLogo = link({
@@ -28946,6 +29480,78 @@ const appointmentBookingBlockDefinition = {
28946
29480
  }
28947
29481
  }
28948
29482
  };
29483
+ const appointmentServicesBlockDefinition = {
29484
+ manifest: createBlockManifest({
29485
+ id: "block.appointment-services",
29486
+ version: "1.0.0",
29487
+ title: "Appointment Services",
29488
+ titleSource: "heading",
29489
+ description: "List public appointment services with booking links",
29490
+ skipSectionStyles: true,
29491
+ additionalFields: [
29492
+ {
29493
+ id: "heading",
29494
+ type: "text",
29495
+ label: "Heading",
29496
+ required: false,
29497
+ multiline: false,
29498
+ defaultValue: "Appointments",
29499
+ ui: { intent: "content" }
29500
+ },
29501
+ {
29502
+ id: "description",
29503
+ type: "richText",
29504
+ label: "Description",
29505
+ required: false,
29506
+ ui: { intent: "content" }
29507
+ },
29508
+ sectionStylesField({
29509
+ id: "_sectionStyles",
29510
+ label: "Section styles",
29511
+ renderInHeader: false
29512
+ })
29513
+ ],
29514
+ styleTokens: {
29515
+ background: "surface",
29516
+ typography: "body",
29517
+ spacing: "md"
29518
+ },
29519
+ behaviours: {
29520
+ supportsThemeSwitching: true,
29521
+ inlineEditing: true,
29522
+ animation: false,
29523
+ paletteHidden: false
29524
+ },
29525
+ category: "interactive",
29526
+ contentSource: { kind: "inline" },
29527
+ tags: [
29528
+ "booking",
29529
+ "appointment",
29530
+ "services",
29531
+ "scheduling"
29532
+ ],
29533
+ icon: "CalendarDays",
29534
+ layout: [styledSection({
29535
+ baseClass: "",
29536
+ spacing: "spacious",
29537
+ children: sectionContainer([
29538
+ text({ as: "h2" }, bind("content.heading"), when("content.heading")),
29539
+ richText({ className: "rb-prose rb-max-w-none" }, bind("content.description"), when("content.description")),
29540
+ el("appointment-services", {
29541
+ sectionHeading: { $bind: { from: "content.heading" } },
29542
+ services: { $bind: { from: "data.services" } },
29543
+ pathname: { $bind: { from: "$root.pathname" } }
29544
+ })
29545
+ ], { gap: "lg" })
29546
+ })]
29547
+ }),
29548
+ dataSchemas: { services: z.unknown().optional() },
29549
+ dataLoaders: { services: {
29550
+ endpoint: "getPublicBookingServices",
29551
+ params: { siteId: { $bind: { from: "$root.siteId" } } },
29552
+ mode: "server"
29553
+ } }
29554
+ };
28949
29555
  //#endregion
28950
29556
  //#region ../blocks/src/system/blocks/weekly-slot-availability.ts
28951
29557
  const weeklySlotAvailabilityManifest = createBlockManifest({
@@ -29464,11 +30070,11 @@ function resolveEventRegistrationScopedEvents(events, context) {
29464
30070
  }
29465
30071
  //#endregion
29466
30072
  //#region ../blocks/src/system/runtime/nodes/events/domain/destination.ts
29467
- function isRecord$8(value) {
30073
+ function isRecord$7(value) {
29468
30074
  return typeof value === "object" && value !== null && !Array.isArray(value);
29469
30075
  }
29470
30076
  function isRuntimeEventDestination(value) {
29471
- if (!isRecord$8(value) || typeof value.kind !== "string") return false;
30077
+ if (!isRecord$7(value) || typeof value.kind !== "string") return false;
29472
30078
  if (value.kind === "native") return (value.mode === "cms-managed" || value.mode === "system-managed") && typeof value.path === "string";
29473
30079
  return value.kind === "external" && typeof value.url === "string" && typeof value.label === "string";
29474
30080
  }
@@ -29658,8 +30264,8 @@ z.object({
29658
30264
  previewToken: z.string().nullable().optional(),
29659
30265
  stage: previewStageSchema.optional()
29660
30266
  });
29661
- function buildEventRegistrationDisplay(source) {
29662
- const selectedRun = resolveSelectedWholeSeriesRun(source);
30267
+ function buildEventRegistrationDisplayFromScopedEvents(source, scopedSourceEvents) {
30268
+ const selectedRun = resolveSelectedWholeSeriesRun(scopedSourceEvents);
29663
30269
  if (selectedRun && (!selectedRun.run.enrollmentOpen || toWholeSeriesRegistrationEvent(selectedRun) === null)) return {
29664
30270
  kind: "event-registration",
29665
30271
  state: "registration-closed",
@@ -29667,7 +30273,7 @@ function buildEventRegistrationDisplay(source) {
29667
30273
  titleText: "Registration closed",
29668
30274
  bodyText: "This run is no longer open for bookings."
29669
30275
  };
29670
- const stepLabels = buildEventRegistrationShellStepLabels(source);
30276
+ const stepLabels = buildEventRegistrationShellStepLabels(source, scopedSourceEvents);
29671
30277
  return {
29672
30278
  kind: "event-registration",
29673
30279
  state: "generic-shell",
@@ -29678,8 +30284,8 @@ function buildEventRegistrationDisplay(source) {
29678
30284
  summaryButtonText: source.buttonText?.trim() || "Register"
29679
30285
  };
29680
30286
  }
29681
- function buildEventRegistrationShellStepLabels(source) {
29682
- const selectedRun = resolveSelectedWholeSeriesRun(source);
30287
+ function buildEventRegistrationShellStepLabels(source, scopedSourceEvents) {
30288
+ const selectedRun = resolveSelectedWholeSeriesRun(scopedSourceEvents);
29683
30289
  if (selectedRun) {
29684
30290
  const selectedEvent = toWholeSeriesRegistrationEvent(selectedRun);
29685
30291
  if (!selectedEvent) return EVENT_REGISTRATION_GENERIC_SHELL_STEP_LABELS;
@@ -29689,7 +30295,7 @@ function buildEventRegistrationShellStepLabels(source) {
29689
30295
  event: selectedEvent
29690
30296
  })).map((step) => shellLabelForRegistrationStep(step, [selectedEvent]));
29691
30297
  }
29692
- const scopedEvents = scopeEventRegistrationEvents(source);
30298
+ const scopedEvents = toScopedEventRegistrationEvents(scopedSourceEvents);
29693
30299
  if (scopedEvents.length === 0) return EVENT_REGISTRATION_GENERIC_SHELL_STEP_LABELS;
29694
30300
  const preSelectedEvent = findPreSelectedEvent({
29695
30301
  scopedEvents,
@@ -29702,31 +30308,25 @@ function buildEventRegistrationShellStepLabels(source) {
29702
30308
  event: preSelectedEvent
29703
30309
  }) : false).map((step) => shellLabelForRegistrationStep(step, scopedEvents));
29704
30310
  }
29705
- function scopeEventRegistrationEvents(source) {
29706
- const scoped = scopeEventRegistrationSourceEvents(source);
29707
- return [...toRegistrationEvents(scoped), ...toWholeSeriesRegistrationEvents(scoped)];
30311
+ function toScopedEventRegistrationEvents(scopedSourceEvents) {
30312
+ return [...toRegistrationEvents(scopedSourceEvents), ...toWholeSeriesRegistrationEvents(scopedSourceEvents)];
29708
30313
  }
29709
- /**
29710
- * Every event this surface may register for, from both of its loads.
29711
- *
29712
- * The date page and the run read answer different questions and can name the
29713
- * same occurrence, so the two lists are joined and any occurrence that arrives
29714
- * twice is kept once, in the order the date page gave it.
29715
- */
29716
30314
  function scopeEventRegistrationSourceEvents(source) {
29717
- const byOccurrenceId = /* @__PURE__ */ new Map();
29718
- for (const event of [...source.events ?? [], ...source.wholeSeriesRunEvents ?? []]) if (!byOccurrenceId.has(event.occurrenceId)) byOccurrenceId.set(event.occurrenceId, event);
29719
- return resolveEventRegistrationScopedEvents([...byOccurrenceId.values()], {
30315
+ return resolveEventRegistrationScopedEvents(source.wholeSeriesRunEvents ?? source.events ?? [], {
29720
30316
  occurrenceContext: source.occurrenceContext,
29721
30317
  subject: source.subject
29722
30318
  });
29723
30319
  }
30320
+ function resolveLoadedEventRegistrationSourceEvents(source, scopedSourceEvents) {
30321
+ if (source.events == null && source.wholeSeriesRunEvents == null) return null;
30322
+ return [...scopedSourceEvents];
30323
+ }
29724
30324
  /**
29725
30325
  * The one run this surface is registering for, when the scoped events describe
29726
30326
  * exactly one. More than one run means the visitor still has to choose.
29727
30327
  */
29728
- function resolveSelectedWholeSeriesRun(source) {
29729
- const runs = groupPublicEventsByRun(scopeEventRegistrationSourceEvents(source));
30328
+ function resolveSelectedWholeSeriesRun(scopedSourceEvents) {
30329
+ const runs = groupPublicEventsByRun(scopedSourceEvents);
29730
30330
  return runs.length === 1 ? runs[0] : null;
29731
30331
  }
29732
30332
  function shellLabelForRegistrationStep(step, events) {
@@ -29760,9 +30360,10 @@ const eventRegistrationBlockRuntime = defineClientOwnedBlockRuntime({
29760
30360
  render: createZodCodec(eventRegistrationRenderSchema)
29761
30361
  },
29762
30362
  buildRender: (source) => {
29763
- const events = source.events == null ? null : scopeEventRegistrationSourceEvents(source);
30363
+ const scopedSourceEvents = scopeEventRegistrationSourceEvents(source);
30364
+ const events = resolveLoadedEventRegistrationSourceEvents(source, scopedSourceEvents);
29764
30365
  return {
29765
- display: buildEventRegistrationDisplay(source),
30366
+ display: buildEventRegistrationDisplayFromScopedEvents(source, scopedSourceEvents),
29766
30367
  hydration: {
29767
30368
  occurrenceContext: source.occurrenceContext ?? null,
29768
30369
  subject: source.subject ?? null,
@@ -29782,13 +30383,13 @@ const eventRegistrationBlockRuntime = defineClientOwnedBlockRuntime({
29782
30383
  }
29783
30384
  });
29784
30385
  function normalizeEventRegistrationEventInput(value) {
29785
- if (!isRecord$7(value)) return value;
30386
+ if (!isRecord$6(value)) return value;
29786
30387
  return {
29787
30388
  ...value,
29788
30389
  destination: resolveRuntimeEventDestination(value)
29789
30390
  };
29790
30391
  }
29791
- function isRecord$7(value) {
30392
+ function isRecord$6(value) {
29792
30393
  return typeof value === "object" && value !== null && !Array.isArray(value);
29793
30394
  }
29794
30395
  const eventRegistrationBlockDefinition = {
@@ -29963,6 +30564,11 @@ const eventRegistrationBlockDefinition = {
29963
30564
  dataLoaders: {
29964
30565
  events: {
29965
30566
  endpoint: "listPublicEvents",
30567
+ when: { $bind: {
30568
+ from: "$root.subject.isWholeSeriesBooking",
30569
+ fallback: true,
30570
+ transforms: [{ id: "boolean.not" }]
30571
+ } },
29966
30572
  params: {
29967
30573
  siteId: { $bind: { from: "$root.siteId" } },
29968
30574
  limit: "50",
@@ -29984,13 +30590,9 @@ const eventRegistrationBlockDefinition = {
29984
30590
  /**
29985
30591
  * The runs this page can register for, finished ones included.
29986
30592
  *
29987
- * The `events` page above is date-bounded, so a run whose first session has
29988
- * passed contributes no rows to it and the surface would have nothing to
29989
- * render as closed. This asks for runs instead, and only on a page whose
29990
- * subject books whole runs. It is scoped exactly like the date page — same
29991
- * series, same run subroute — because scope and grain are separate
29992
- * questions. The two lists are joined by occurrence, so a run that appears
29993
- * in both is still one run.
30593
+ * A run read is the sole Event source on a whole-series surface. It is
30594
+ * status-bounded rather than date-bounded, so completed runs can still
30595
+ * render their closed state and every run carries its complete schedule.
29994
30596
  */
29995
30597
  wholeSeriesRunEvents: {
29996
30598
  endpoint: "listPublicEvents",
@@ -32685,7 +33287,7 @@ function inferLegacyOfferingsPreviewGroupOrder(overrides) {
32685
33287
  return keys.length >= 2 ? keys : [];
32686
33288
  }
32687
33289
  function normalizeOfferingsPreviewContentForWrite(value) {
32688
- if (!isRecord$6(value)) return null;
33290
+ if (!isRecord$5(value)) return null;
32689
33291
  const groupOrderValue = value.groupOrder;
32690
33292
  if (Array.isArray(groupOrderValue)) {
32691
33293
  const normalized = normalizeOfferingsPreviewGroupOrder(groupOrderValue);
@@ -32698,7 +33300,7 @@ function normalizeOfferingsPreviewContentForWrite(value) {
32698
33300
  ...value,
32699
33301
  groupOrder: []
32700
33302
  };
32701
- const legacyOrder = inferLegacyOfferingsPreviewGroupOrder(Array.isArray(value.groupOverrides) ? value.groupOverrides.filter(isRecord$6) : []);
33303
+ const legacyOrder = inferLegacyOfferingsPreviewGroupOrder(Array.isArray(value.groupOverrides) ? value.groupOverrides.filter(isRecord$5) : []);
32702
33304
  return legacyOrder.length > 0 ? {
32703
33305
  ...value,
32704
33306
  groupOrder: legacyOrder
@@ -32712,24 +33314,11 @@ function uniqueOfferingsPreviewSourceKeys(values) {
32712
33314
  }
32713
33315
  return keys;
32714
33316
  }
32715
- function isRecord$6(value) {
33317
+ function isRecord$5(value) {
32716
33318
  return typeof value === "object" && value !== null && !Array.isArray(value);
32717
33319
  }
32718
33320
  //#endregion
32719
- //#region ../blocks/src/system/blocks/offerings-preview/index.ts
32720
- /**
32721
- * The whole-series presets, as the CSV `listPublicEvents` expects. Both
32722
- * whole-series presets share one loader: the block renders them as a single
32723
- * run group.
32724
- */
32725
- const WHOLE_SERIES_EVENT_PRESETS_PARAM = WHOLE_SERIES_EVENT_PRESET_VALUES.join(",");
32726
- const publicRouteCandidatesSchema = z.object({ routes: z.array(z.object({
32727
- entityId: z.string(),
32728
- path: z.string(),
32729
- title: z.string(),
32730
- kind: z.enum(RESOLVED_INTERNAL_LINK_ENTITY_TYPES),
32731
- contentTypeKey: z.string().nullable().optional()
32732
- })) });
33321
+ //#region ../blocks/src/system/blocks/offerings-preview/fields.ts
32733
33322
  const groupOverrideFields = [
32734
33323
  {
32735
33324
  id: "sourceKey",
@@ -32935,7 +33524,65 @@ const offeringsPreviewFields = [
32935
33524
  renderInHeader: false
32936
33525
  })
32937
33526
  ];
33527
+ function labelForOfferingPreviewSourceKey(sourceKey) {
33528
+ const eventDefinition = OFFERINGS_PREVIEW_EVENT_PRESET_DEFINITIONS.find((definition) => definition.sourceKey === sourceKey);
33529
+ if (eventDefinition) return eventDefinition.label;
33530
+ return "Courses";
33531
+ }
33532
+ //#endregion
33533
+ //#region ../blocks/src/system/blocks/offerings-preview/index.ts
33534
+ /**
33535
+ * The whole-series presets, as the CSV `listPublicEvents` expects. Both
33536
+ * whole-series presets share one loader: the block renders them as a single
33537
+ * run group.
33538
+ */
33539
+ const WHOLE_SERIES_EVENT_PRESETS_PARAM = WHOLE_SERIES_EVENT_PRESET_VALUES.join(",");
33540
+ const publicRouteCandidatesSchema = z.object({ routes: z.array(z.object({
33541
+ entityId: z.string(),
33542
+ path: z.string(),
33543
+ title: z.string(),
33544
+ kind: z.enum(RESOLVED_INTERNAL_LINK_ENTITY_TYPES),
33545
+ contentTypeKey: z.string().nullable().optional()
33546
+ })) });
32938
33547
  const offeringsPreviewEventDataBindings = Object.fromEntries(OFFERINGS_PREVIEW_EVENT_PRESET_DEFINITIONS.map((definition) => [definition.dataKey, { $bind: { from: `data.${definition.dataKey}` } }]));
33548
+ const DEFAULT_OFFERINGS_PREVIEW_DISPLAY_MODE = "preview";
33549
+ const displayModeBinding = {
33550
+ from: "content.displayMode",
33551
+ fallback: DEFAULT_OFFERINGS_PREVIEW_DISPLAY_MODE
33552
+ };
33553
+ const catalogueMode = { $bind: {
33554
+ ...displayModeBinding,
33555
+ transforms: [{
33556
+ id: "value.equals",
33557
+ options: { value: "catalogue" }
33558
+ }]
33559
+ } };
33560
+ const previewMode = { $bind: {
33561
+ ...displayModeBinding,
33562
+ transforms: [{
33563
+ id: "value.equals",
33564
+ options: { value: "preview" }
33565
+ }]
33566
+ } };
33567
+ function offeringsPreviewHeaderNodes() {
33568
+ return [text({
33569
+ as: "h2",
33570
+ className: { $bind: {
33571
+ from: "content.displayMode",
33572
+ fallback: "rb-text-center",
33573
+ transforms: [{
33574
+ id: "value.equals",
33575
+ options: { value: "catalogue" }
33576
+ }, {
33577
+ id: "ui.conditionalClass",
33578
+ options: {
33579
+ whenTrue: "rb-text-left",
33580
+ whenFalse: "rb-text-center"
33581
+ }
33582
+ }]
33583
+ } }
33584
+ }, bind("content.heading"), when("content.heading")), richText({ className: "rb-prose rb-max-w-none" }, bind("content.description"), when("content.description"))];
33585
+ }
32939
33586
  const offeringsPreviewManifest = createBlockManifest({
32940
33587
  id: "block.offerings-preview",
32941
33588
  version: "1.0.0",
@@ -32966,48 +33613,45 @@ const offeringsPreviewManifest = createBlockManifest({
32966
33613
  ],
32967
33614
  icon: "PanelsTopLeft",
32968
33615
  layout: [styledSection({
32969
- children: sectionContainer([
32970
- text({
32971
- as: "h2",
32972
- className: "rb-text-center"
32973
- }, bind("content.heading"), when("content.heading")),
32974
- richText({ className: "rb-prose rb-max-w-none" }, bind("content.description"), when("content.description")),
32975
- el("offerings-preview", {
32976
- bookingOfferingPresets: { $bind: {
32977
- from: "$root.bookingOfferingPresets",
32978
- fallback: []
32979
- } },
32980
- groupOverrides: { $bind: {
32981
- from: "content.groupOverrides",
32982
- fallback: []
32983
- } },
32984
- groupOrder: { $bind: { from: "content.groupOrder" } },
32985
- cardStyles: { $bind: { from: "content._cardStyles" } },
32986
- itemLimit: { $bind: {
32987
- from: "content.itemLimit",
32988
- fallback: "3"
32989
- } },
32990
- buttonVariant: { $bind: {
32991
- from: "content.buttonVariant",
32992
- fallback: "primary"
32993
- } },
32994
- buttonText: { $bind: {
32995
- from: "content.buttonText",
32996
- fallback: OFFERINGS_PREVIEW_DEFAULT_BUTTON_TEXT
32997
- } },
32998
- emptyMessage: { $bind: {
32999
- from: "content.emptyMessage",
33000
- fallback: OFFERINGS_PREVIEW_DEFAULT_EMPTY_MESSAGE
33001
- } },
33002
- previewStage: { $bind: {
33003
- from: "$root.previewStage",
33004
- fallback: "published"
33005
- } },
33006
- routeCandidates: { $bind: { from: "data.routeCandidates" } },
33007
- wholeSeriesRunEvents: { $bind: { from: "data.wholeSeriesRunEvents" } },
33008
- ...offeringsPreviewEventDataBindings
33009
- })
33010
- ], { gap: "lg" }),
33616
+ children: sectionContainer([el("offerings-preview", {
33617
+ displayMode: { $bind: displayModeBinding },
33618
+ pathname: { $bind: { from: "$root.pathname" } },
33619
+ catalogueEvents: { $bind: { from: "data.catalogueEvents" } },
33620
+ quickDateEvents: { $bind: { from: "data.quickDateEvents" } },
33621
+ bookingOfferingPresets: { $bind: {
33622
+ from: "$root.bookingOfferingPresets",
33623
+ fallback: []
33624
+ } },
33625
+ groupOverrides: { $bind: {
33626
+ from: "content.groupOverrides",
33627
+ fallback: []
33628
+ } },
33629
+ groupOrder: { $bind: { from: "content.groupOrder" } },
33630
+ cardStyles: { $bind: { from: "content._cardStyles" } },
33631
+ itemLimit: { $bind: {
33632
+ from: "content.itemLimit",
33633
+ fallback: "3"
33634
+ } },
33635
+ buttonVariant: { $bind: {
33636
+ from: "content.buttonVariant",
33637
+ fallback: "primary"
33638
+ } },
33639
+ buttonText: { $bind: {
33640
+ from: "content.buttonText",
33641
+ fallback: OFFERINGS_PREVIEW_DEFAULT_BUTTON_TEXT
33642
+ } },
33643
+ emptyMessage: { $bind: {
33644
+ from: "content.emptyMessage",
33645
+ fallback: OFFERINGS_PREVIEW_DEFAULT_EMPTY_MESSAGE
33646
+ } },
33647
+ previewStage: { $bind: {
33648
+ from: "$root.previewStage",
33649
+ fallback: "published"
33650
+ } },
33651
+ routeCandidates: { $bind: { from: "data.routeCandidates" } },
33652
+ wholeSeriesRunEvents: { $bind: { from: "data.wholeSeriesRunEvents" } },
33653
+ ...offeringsPreviewEventDataBindings
33654
+ }, offeringsPreviewHeaderNodes())], { gap: "lg" }),
33011
33655
  baseClass: "",
33012
33656
  spacing: "comfortable"
33013
33657
  })]
@@ -33021,16 +33665,48 @@ const offeringsPreviewBlockDefinition = {
33021
33665
  dataSchemas: {
33022
33666
  ...offeringsPreviewEventDataSchemas,
33023
33667
  routeCandidates: publicRouteCandidatesSchema.optional(),
33024
- wholeSeriesRunEvents: optionalPublicEventsArraySchema
33668
+ wholeSeriesRunEvents: optionalPublicEventsArraySchema,
33669
+ catalogueEvents: optionalPublicEventsArraySchema,
33670
+ quickDateEvents: optionalPublicEventsArraySchema
33025
33671
  },
33026
33672
  dataLoaders: {
33027
33673
  ...offeringsPreviewEventDataLoaders,
33028
33674
  routeCandidates: {
33029
33675
  endpoint: "listPublicRouteCandidates",
33676
+ when: previewMode,
33030
33677
  params: {},
33031
33678
  mode: "server"
33032
33679
  },
33033
- wholeSeriesRunEvents: wholeSeriesRunLoader()
33680
+ wholeSeriesRunEvents: wholeSeriesRunLoader(),
33681
+ catalogueEvents: {
33682
+ endpoint: "listPublicEvents",
33683
+ when: catalogueMode,
33684
+ params: {
33685
+ stage: { $bind: {
33686
+ from: "$root.previewStage",
33687
+ fallback: "published"
33688
+ } },
33689
+ detailLevel: "listing",
33690
+ readProfile: "listing-card",
33691
+ readIntent: "catalogue"
33692
+ },
33693
+ mode: "server"
33694
+ },
33695
+ quickDateEvents: {
33696
+ endpoint: "listPublicEvents",
33697
+ when: catalogueMode,
33698
+ params: {
33699
+ stage: { $bind: {
33700
+ from: "$root.previewStage",
33701
+ fallback: "published"
33702
+ } },
33703
+ detailLevel: "listing",
33704
+ readProfile: "listing-card",
33705
+ readIntent: "occurrences",
33706
+ limit: 40
33707
+ },
33708
+ mode: "server"
33709
+ }
33034
33710
  }
33035
33711
  };
33036
33712
  /**
@@ -33047,6 +33723,7 @@ function wholeSeriesRunLoader() {
33047
33723
  function eventPresetLoader(eventPresets, extraParams = {}) {
33048
33724
  return {
33049
33725
  endpoint: "listPublicEvents",
33726
+ when: previewMode,
33050
33727
  params: {
33051
33728
  siteId: { $bind: { from: "$root.siteId" } },
33052
33729
  limit: { $bind: {
@@ -33065,15 +33742,10 @@ function eventPresetLoader(eventPresets, extraParams = {}) {
33065
33742
  mode: "server"
33066
33743
  };
33067
33744
  }
33068
- function labelForOfferingPreviewSourceKey(sourceKey) {
33069
- const eventDefinition = OFFERINGS_PREVIEW_EVENT_PRESET_DEFINITIONS.find((definition) => definition.sourceKey === sourceKey);
33070
- if (eventDefinition) return eventDefinition.label;
33071
- return "Courses";
33072
- }
33073
33745
  function normalizeOfferingsPreviewBlockContent(value) {
33074
33746
  const record = normalizeOfferingsPreviewContentForWrite(value);
33075
- if (!record) return {};
33076
- const normalized = {};
33747
+ if (!record) return { displayMode: DEFAULT_OFFERINGS_PREVIEW_DISPLAY_MODE };
33748
+ const normalized = { displayMode: normalizeOfferingsPreviewDisplayMode(record.displayMode) };
33077
33749
  if (typeof record.heading === "string" || record.heading === null) normalized.heading = record.heading;
33078
33750
  if ("description" in record) normalized.description = record.description;
33079
33751
  if (isOfferingsPreviewItemLimit(record.itemLimit)) normalized.itemLimit = record.itemLimit;
@@ -33090,11 +33762,14 @@ function normalizeOfferingsPreviewBlockContent(value) {
33090
33762
  if (sectionStyles !== void 0) normalized._sectionStyles = sectionStyles;
33091
33763
  return normalized;
33092
33764
  }
33765
+ function normalizeOfferingsPreviewDisplayMode(value) {
33766
+ return value === "catalogue" ? "catalogue" : DEFAULT_OFFERINGS_PREVIEW_DISPLAY_MODE;
33767
+ }
33093
33768
  function isOfferingsPreviewItemLimit(value) {
33094
33769
  return value === "1" || value === "2" || value === "3" || value === "4" || value === "5" || value === "6" || value === null;
33095
33770
  }
33096
33771
  function normalizeOfferingsPreviewGroupOverride(value) {
33097
- if (!isRecord$5(value)) return [];
33772
+ if (!isRecord$4(value)) return [];
33098
33773
  const override = {};
33099
33774
  if (isOfferingsPreviewSourceKey(value.sourceKey) || value.sourceKey === null) override.sourceKey = value.sourceKey;
33100
33775
  if (typeof value.title === "string" || value.title === null) override.title = value.title;
@@ -33117,7 +33792,7 @@ function normalizeSectionStylesContent(value) {
33117
33792
  }
33118
33793
  function normalizeBoxStylesContent(value) {
33119
33794
  if (value === null) return null;
33120
- if (!isRecord$5(value)) return void 0;
33795
+ if (!isRecord$4(value)) return void 0;
33121
33796
  const { background: _background, ...rest } = value;
33122
33797
  const background = parseBackgroundContent(value.background);
33123
33798
  return {
@@ -33125,7 +33800,7 @@ function normalizeBoxStylesContent(value) {
33125
33800
  ...background ? { background } : {}
33126
33801
  };
33127
33802
  }
33128
- function isRecord$5(value) {
33803
+ function isRecord$4(value) {
33129
33804
  return typeof value === "object" && value !== null && !Array.isArray(value);
33130
33805
  }
33131
33806
  const embedManifest = createBlockManifest({
@@ -36304,83 +36979,13 @@ const giftingBlockDefinition = {
36304
36979
  } }
36305
36980
  };
36306
36981
  //#endregion
36307
- //#region ../blocks/src/system/blocks/newsletter-signup-destination.ts
36308
- function parseNewsletterSignupBlockId(value) {
36309
- const trimmed = parseTrimmedString(value);
36310
- return trimmed && BLOCK_ID_PATTERN.test(trimmed) ? trimmed : null;
36311
- }
36312
- function parseSignedNewsletterDestinationRef(value) {
36313
- const trimmed = parseTrimmedString(value);
36314
- return trimmed && SIGNED_DESTINATION_REF_PATTERN.test(trimmed) ? trimmed : null;
36315
- }
36316
- function buildNewsletterSignupBlockRef(blockId) {
36317
- const parsed = parseNewsletterSignupBlockId(blockId);
36318
- return parsed ? {
36319
- tag: "block_ref",
36320
- blockId: parsed
36321
- } : null;
36322
- }
36323
- function normalizeNewsletterSignupEditorValuesForDestination(values) {
36324
- const destinationProvider = parseNewsletterSignupDestinationProvider(values.destinationProvider) ?? "riverbank";
36325
- if (destinationProvider === "mailchimp") return {
36326
- destinationProvider,
36327
- mailchimpAudienceId: values.mailchimpAudienceId,
36328
- mailchimpTags: values.mailchimpTags
36329
- };
36330
- return {
36331
- destinationProvider,
36332
- listId: values.listId,
36333
- tags: values.tags
36334
- };
36335
- }
36336
- function deriveNewsletterSignupRuntimeSource(values) {
36337
- const normalized = normalizeNewsletterSignupEditorValuesForDestination(values);
36338
- if (normalized.destinationProvider === "mailchimp") return {
36339
- destinationProvider: "mailchimp",
36340
- listId: null,
36341
- tags: null
36342
- };
36343
- return {
36344
- destinationProvider: "riverbank",
36345
- listId: parseTrimmedString(normalized.listId),
36346
- tags: parseTrimmedString(normalized.tags)
36347
- };
36348
- }
36349
- function sanitizeNewsletterSignupPublicContent(content) {
36350
- if (!isRecord$4(content)) return content;
36351
- const provider = parseNewsletterSignupDestinationProvider(content.destinationProvider) ?? getProviderFromExplicitDestination(content.destination);
36352
- const { destination: _destination, destinationProvider: _destinationProvider, mailchimpAudienceId: _mailchimpAudienceId, mailchimpTags: _mailchimpTags, listId, tags, ...publicContent } = content;
36353
- const mutablePublicContent = { ...publicContent };
36354
- if (provider !== "mailchimp") {
36355
- if ("listId" in content) mutablePublicContent.listId = listId;
36356
- if ("tags" in content) mutablePublicContent.tags = tags;
36357
- }
36358
- return mutablePublicContent;
36359
- }
36360
- function getProviderFromExplicitDestination(destination) {
36361
- if (!isRecord$4(destination) || typeof destination.tag !== "string") return null;
36362
- if (destination.tag === "riverbank_list") return "riverbank";
36363
- if (destination.tag === "mailchimp_audience") return "mailchimp";
36364
- return null;
36365
- }
36366
- const BLOCK_ID_PATTERN = /^[A-Za-z0-9._:-]{1,200}$/;
36367
- const SIGNED_DESTINATION_REF_PATTERN = /^[A-Za-z0-9._~-]{16,1000}$/;
36368
- function parseTrimmedString(value) {
36369
- if (typeof value !== "string") return null;
36370
- const trimmed = value.trim();
36371
- return trimmed.length > 0 ? trimmed : null;
36372
- }
36373
- function isRecord$4(value) {
36374
- return typeof value === "object" && value !== null;
36375
- }
36376
- //#endregion
36377
36982
  //#region ../blocks/src/system/runtime/nodes/newsletter-form.types.ts
36378
36983
  /**
36379
36984
  * Get form fields based on preset configuration.
36380
36985
  */
36381
- function getPresetFields(preset, options) {
36382
- const includePhone = Boolean(options?.includePhone);
36383
- switch (preset) {
36986
+ function getGlobalFormFields(form) {
36987
+ const includePhone = form.includeOptionalPhone;
36988
+ switch (form.fields.preset) {
36384
36989
  case "emailOnly": return withOptionalPhone([{
36385
36990
  id: "email",
36386
36991
  type: "email",
@@ -36398,7 +37003,7 @@ function getPresetFields(preset, options) {
36398
37003
  id: "name",
36399
37004
  type: "text",
36400
37005
  label: "Name",
36401
- required: false,
37006
+ required: form.fields.name === "required",
36402
37007
  placeholder: "Your name"
36403
37008
  }], includePhone);
36404
37009
  case "emailFirstLast": return withOptionalPhone([
@@ -36413,14 +37018,14 @@ function getPresetFields(preset, options) {
36413
37018
  id: "firstName",
36414
37019
  type: "text",
36415
37020
  label: "First Name",
36416
- required: false,
37021
+ required: form.fields.firstName === "required",
36417
37022
  placeholder: "First name"
36418
37023
  },
36419
37024
  {
36420
37025
  id: "lastName",
36421
37026
  type: "text",
36422
37027
  label: "Last Name",
36423
- required: false,
37028
+ required: form.fields.lastName === "required",
36424
37029
  placeholder: "Last name"
36425
37030
  }
36426
37031
  ], includePhone);
@@ -36436,14 +37041,14 @@ function getPresetFields(preset, options) {
36436
37041
  id: "name",
36437
37042
  type: "text",
36438
37043
  label: "Name",
36439
- required: false,
37044
+ required: form.fields.name === "required",
36440
37045
  placeholder: "Your name"
36441
37046
  },
36442
37047
  {
36443
37048
  id: "company",
36444
37049
  type: "text",
36445
37050
  label: "Company",
36446
- required: false,
37051
+ required: form.fields.company === "required",
36447
37052
  placeholder: "Company name"
36448
37053
  }
36449
37054
  ], includePhone);
@@ -36462,14 +37067,29 @@ function withOptionalPhone(fields, includePhone) {
36462
37067
  }
36463
37068
  //#endregion
36464
37069
  //#region ../blocks/src/system/runtime/nodes/newsletter-form.interactive.ts
36465
- const newsletterPresetSchema = z.enum([
36466
- "emailOnly",
36467
- "emailName",
36468
- "emailFirstLast",
36469
- "emailNameCompany"
36470
- ]);
36471
- const newsletterDestinationProviderSchema = z.enum(["riverbank", "mailchimp"]);
36472
37070
  const newsletterButtonVariantSchema = blockButtonRoleSchema;
37071
+ const mailingListContextSchema = z.unknown().transform((value, ctx) => {
37072
+ const parsed = parseMailingListRuntimeContext(value);
37073
+ if (!parsed.ok) {
37074
+ ctx.addIssue({
37075
+ code: z.ZodIssueCode.custom,
37076
+ message: "Invalid mailing-list runtime context"
37077
+ });
37078
+ return z.NEVER;
37079
+ }
37080
+ return parsed.value;
37081
+ });
37082
+ const mailingListTargetingSchema = z.unknown().transform((value, ctx) => {
37083
+ const parsed = parseMailingListPlacementTargeting(value);
37084
+ if (!parsed.ok) {
37085
+ ctx.addIssue({
37086
+ code: z.ZodIssueCode.custom,
37087
+ message: "Invalid mailing-list targeting"
37088
+ });
37089
+ return z.NEVER;
37090
+ }
37091
+ return parsed.value;
37092
+ });
36473
37093
  const newsletterFieldSchema = z.object({
36474
37094
  id: z.string(),
36475
37095
  type: z.enum(["email", "text"]),
@@ -36477,73 +37097,73 @@ const newsletterFieldSchema = z.object({
36477
37097
  required: z.boolean(),
36478
37098
  placeholder: z.string().optional()
36479
37099
  });
36480
- const newsletterSignupBlockIdSchema = z.string().trim().min(1).transform(parseRequiredNewsletterSignupBlockId);
36481
- const signedNewsletterDestinationRefSchema = z.string().trim().min(1).transform(parseRequiredSignedNewsletterDestinationRef);
37100
+ const mailingListSignupBlockIdSchema = z.string().trim().min(1).transform(parseRequiredMailingListSignupBlockId);
37101
+ const signedMailingListSourceTokenSchema = z.string().trim().min(1).transform(parseRequiredMailingListSignedSourceToken);
36482
37102
  const newsletterSourceCommonSchema = z.object({
36483
37103
  siteId: z.string().optional(),
36484
37104
  apiBaseUrl: z.string().optional(),
36485
- preset: newsletterPresetSchema.optional(),
36486
- includePhone: z.boolean().optional(),
36487
- buttonLabel: z.string().nullable().optional(),
37105
+ mailingList: mailingListContextSchema.optional(),
37106
+ targeting: mailingListTargetingSchema.optional(),
36488
37107
  buttonVariant: newsletterButtonVariantSchema.nullable().optional(),
36489
- successMessage: z.string().nullable().optional(),
36490
- privacyNote: z.string().nullable().optional(),
36491
- tags: z.string().nullable().optional(),
36492
37108
  spamProtectionEnabled: z.boolean().optional(),
36493
37109
  className: z.string().nullable().optional()
36494
37110
  });
36495
37111
  const newsletterSourceSchema = z.discriminatedUnion("source", [
36496
37112
  newsletterSourceCommonSchema.extend({
36497
37113
  source: z.literal("block"),
36498
- blockId: z.string().nullable().optional(),
36499
- listId: z.string().nullable().optional(),
36500
- destinationProvider: newsletterDestinationProviderSchema.nullable().optional()
37114
+ blockId: z.string().nullable().optional()
36501
37115
  }),
36502
37116
  newsletterSourceCommonSchema.extend({
36503
37117
  source: z.literal("signed-footer"),
36504
- destinationRef: z.object({
36505
- tag: z.literal("signed_destination_ref"),
36506
- token: signedNewsletterDestinationRefSchema
37118
+ sourceRef: z.object({
37119
+ tag: z.literal("signed_source_ref"),
37120
+ token: signedMailingListSourceTokenSchema
36507
37121
  })
36508
37122
  }),
36509
- newsletterSourceCommonSchema.extend({
36510
- source: z.literal("legacy-direct-list"),
36511
- listId: z.string().nullable()
36512
- })
37123
+ newsletterSourceCommonSchema.extend({ source: z.literal("preview") })
36513
37124
  ]);
36514
37125
  const newsletterDisplaySchema = z.object({
36515
37126
  kind: z.literal("newsletter-form"),
37127
+ availability: z.enum(["ready", "unavailable"]),
36516
37128
  className: z.string().nullable(),
36517
37129
  buttonLabel: z.string(),
36518
37130
  buttonVariant: newsletterButtonVariantSchema,
36519
- successMessage: z.string().nullable(),
36520
37131
  privacyNote: z.string().nullable(),
36521
- fields: z.array(newsletterFieldSchema)
37132
+ fields: z.array(newsletterFieldSchema),
37133
+ choices: z.array(z.object({
37134
+ id: z.string(),
37135
+ label: z.string()
37136
+ }))
36522
37137
  });
36523
37138
  const newsletterHydrationSchema = z.object({
36524
37139
  target: z.discriminatedUnion("tag", [
36525
37140
  z.object({
36526
37141
  tag: z.literal("block-source"),
36527
- destinationRef: z.object({
37142
+ sourceRef: z.object({
36528
37143
  tag: z.literal("block_ref"),
36529
- blockId: newsletterSignupBlockIdSchema
37144
+ blockId: mailingListSignupBlockIdSchema
36530
37145
  }).nullable()
36531
37146
  }),
36532
37147
  z.object({
36533
37148
  tag: z.literal("signed-footer-source"),
36534
- destinationRef: z.object({
36535
- tag: z.literal("signed_destination_ref"),
36536
- token: signedNewsletterDestinationRefSchema
37149
+ sourceRef: z.object({
37150
+ tag: z.literal("signed_source_ref"),
37151
+ token: signedMailingListSourceTokenSchema
36537
37152
  })
36538
37153
  }),
36539
- z.object({
36540
- tag: z.literal("legacy-direct-list"),
36541
- listId: z.string().nullable()
36542
- })
37154
+ z.object({ tag: z.literal("preview") })
37155
+ ]),
37156
+ setupRevision: z.number().int().positive().nullable(),
37157
+ choiceProvider: z.enum([
37158
+ "native",
37159
+ "brevo",
37160
+ "mailchimp"
37161
+ ]).nullable(),
37162
+ choicePresentation: z.enum([
37163
+ "hidden",
37164
+ "automatic",
37165
+ "visitor_choice"
36543
37166
  ]),
36544
- preset: newsletterPresetSchema,
36545
- includePhone: z.boolean(),
36546
- tags: z.string().nullable().optional(),
36547
37167
  spamProtectionEnabled: z.boolean().optional()
36548
37168
  });
36549
37169
  const newsletterRenderSchema = z.object({
@@ -36555,16 +37175,24 @@ z.object({
36555
37175
  apiBaseUrl: z.string().optional()
36556
37176
  });
36557
37177
  function buildNewsletterDisplay(source) {
36558
- const preset = source.preset ?? "emailOnly";
36559
- const includePhone = source.includePhone ?? false;
37178
+ const context = source.mailingList;
37179
+ const form = context?.tag === "configured" ? context.form : {
37180
+ fields: { preset: "emailOnly" },
37181
+ includeOptionalPhone: false,
37182
+ buttonLabel: "Subscribe",
37183
+ privacyNote: null
37184
+ };
37185
+ const targeting = source.targeting ?? { mode: "default" };
37186
+ const choices = resolveRuntimeChoices(context, targeting);
36560
37187
  return {
36561
37188
  kind: "newsletter-form",
37189
+ availability: runtimeTargetingIsAvailable(context, targeting, choices) ? "ready" : "unavailable",
36562
37190
  className: source.className ?? null,
36563
- buttonLabel: source.buttonLabel?.trim() || "Subscribe",
37191
+ buttonLabel: form.buttonLabel,
36564
37192
  buttonVariant: source.buttonVariant ?? "primary",
36565
- successMessage: source.successMessage ?? "Thanks for subscribing!",
36566
- privacyNote: source.privacyNote ?? null,
36567
- fields: getPresetFields(preset, { includePhone })
37193
+ privacyNote: form.privacyNote,
37194
+ fields: getGlobalFormFields(form),
37195
+ choices
36568
37196
  };
36569
37197
  }
36570
37198
  const newsletterFormBlockRuntime = defineProgressiveBlockRuntime({
@@ -36574,14 +37202,15 @@ const newsletterFormBlockRuntime = defineProgressiveBlockRuntime({
36574
37202
  render: createZodCodec(newsletterRenderSchema)
36575
37203
  },
36576
37204
  buildRender: (source) => {
36577
- const runtimeSource = source.source === "block" ? deriveNewsletterSignupRuntimeSource(source) : null;
37205
+ const targeting = source.targeting ?? { mode: "default" };
37206
+ const presentation = planMailingListChoicePresentation(targeting);
36578
37207
  return {
36579
37208
  display: buildNewsletterDisplay(source),
36580
37209
  hydration: {
36581
37210
  target: projectNewsletterFormHydrationTarget(source),
36582
- preset: source.preset ?? "emailOnly",
36583
- includePhone: source.includePhone ?? false,
36584
- tags: runtimeSource ? runtimeSource.tags : source.tags,
37211
+ setupRevision: source.mailingList?.tag === "configured" ? source.mailingList.revision : null,
37212
+ choiceProvider: targeting.mode === "custom" ? targeting.provider : null,
37213
+ choicePresentation: presentation.tag,
36585
37214
  spamProtectionEnabled: source.spamProtectionEnabled
36586
37215
  }
36587
37216
  };
@@ -36590,35 +37219,87 @@ const newsletterFormBlockRuntime = defineProgressiveBlockRuntime({
36590
37219
  function projectNewsletterFormHydrationTarget(source) {
36591
37220
  switch (source.source) {
36592
37221
  case "block": {
36593
- const destinationRef = buildNewsletterSignupBlockRef(source.blockId);
36594
- if (destinationRef?.tag === "block_ref") return {
37222
+ const sourceRef = buildMailingListSignupBlockSourceRef(source.blockId);
37223
+ if (sourceRef?.tag === "block_ref") return {
36595
37224
  tag: "block-source",
36596
- destinationRef
37225
+ sourceRef
36597
37226
  };
36598
37227
  return {
36599
- tag: "legacy-direct-list",
36600
- listId: source.listId ?? null
37228
+ tag: "block-source",
37229
+ sourceRef: null
36601
37230
  };
36602
37231
  }
36603
37232
  case "signed-footer": return {
36604
37233
  tag: "signed-footer-source",
36605
- destinationRef: source.destinationRef
36606
- };
36607
- case "legacy-direct-list": return {
36608
- tag: "legacy-direct-list",
36609
- listId: source.listId
37234
+ sourceRef: source.sourceRef
36610
37235
  };
37236
+ case "preview": return { tag: "preview" };
36611
37237
  default: return assertNever$3(source);
36612
37238
  }
36613
37239
  }
36614
- function parseRequiredNewsletterSignupBlockId(value) {
36615
- const parsed = parseNewsletterSignupBlockId(value);
36616
- if (!parsed) throw new Error("Newsletter signup block destination ref requires blockId");
37240
+ function resolveRuntimeChoices(context, targeting) {
37241
+ if (context?.tag !== "configured" || targeting.mode !== "custom" || targeting.selection.tag !== "visitor_choice" || context.provider.provider !== targeting.provider) return [];
37242
+ switch (targeting.provider) {
37243
+ case "native": {
37244
+ const offered = new Set(targeting.selection.offeredTopicIds);
37245
+ return context.provider.provider === "native" ? context.provider.topics.filter((item) => offered.has(item.id)).map((item) => ({
37246
+ id: item.id,
37247
+ label: item.name
37248
+ })) : [];
37249
+ }
37250
+ case "brevo": {
37251
+ const offered = new Set(targeting.selection.offeredListIds);
37252
+ return context.provider.provider === "brevo" ? context.provider.lists.filter((item) => offered.has(item.id)).map((item) => ({
37253
+ id: String(item.id),
37254
+ label: item.name
37255
+ })) : [];
37256
+ }
37257
+ case "mailchimp": {
37258
+ const offered = new Set(targeting.selection.offeredInterestIds);
37259
+ 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) => ({
37260
+ id: item.id,
37261
+ label: item.name
37262
+ })) ?? [];
37263
+ }
37264
+ }
37265
+ }
37266
+ function runtimeTargetingIsAvailable(context, targeting, choices) {
37267
+ if (context?.tag !== "configured") return false;
37268
+ const provider = context.provider;
37269
+ if (targeting.mode === "default") switch (provider.provider) {
37270
+ case "native": return provider.topics.some((item) => item.id === provider.defaultTopicId);
37271
+ case "brevo": return provider.lists.some((item) => item.id === provider.defaultListId);
37272
+ case "mailchimp": return provider.audiences.some((item) => item.id === provider.defaultAudienceId);
37273
+ }
37274
+ if (targeting.provider !== provider.provider) return false;
37275
+ switch (targeting.provider) {
37276
+ case "native": return provider.provider === "native" && nativeTargetIsAvailable(provider, targeting, choices);
37277
+ case "brevo": return provider.provider === "brevo" && brevoTargetIsAvailable(provider, targeting, choices);
37278
+ case "mailchimp": return provider.provider === "mailchimp" && mailchimpTargetIsAvailable(provider, targeting, choices);
37279
+ }
37280
+ }
37281
+ function nativeTargetIsAvailable(provider, targeting, choices) {
37282
+ const ids = targeting.selection.tag === "fixed" ? targeting.selection.topicIds : targeting.selection.offeredTopicIds;
37283
+ return ids.every((id) => provider.topics.some((item) => item.id === id)) && (targeting.selection.tag !== "visitor_choice" || choices.length === ids.length);
37284
+ }
37285
+ function brevoTargetIsAvailable(provider, targeting, choices) {
37286
+ const ids = targeting.selection.tag === "fixed" ? targeting.selection.listIds : targeting.selection.offeredListIds;
37287
+ return ids.every((id) => provider.lists.some((item) => item.id === id)) && (targeting.selection.tag !== "visitor_choice" || choices.length === ids.length);
37288
+ }
37289
+ function mailchimpTargetIsAvailable(provider, targeting, choices) {
37290
+ const audience = provider.audiences.find((item) => item.id === targeting.audienceId);
37291
+ if (!audience) return false;
37292
+ const ids = targeting.selection.tag === "fixed" ? targeting.selection.interestIds : targeting.selection.offeredInterestIds;
37293
+ return ids.every((id) => audience.interests.some((item) => item.id === id)) && (targeting.selection.tag !== "visitor_choice" || choices.length === ids.length);
37294
+ }
37295
+ function parseRequiredMailingListSignupBlockId(value) {
37296
+ const parsed = parseMailingListBlockId(value);
37297
+ if (!parsed) throw new Error("Mailing-list signup block source ref requires blockId");
36617
37298
  return parsed;
36618
37299
  }
36619
- function parseRequiredSignedNewsletterDestinationRef(value) {
36620
- const parsed = parseSignedNewsletterDestinationRef(value);
36621
- if (!parsed) throw new Error("Newsletter signup signed destination ref requires token");
37300
+ function parseRequiredMailingListSignedSourceToken(value) {
37301
+ const parsed = parseMailingListSignedSourceToken(value);
37302
+ if (!parsed) throw new Error("Mailing-list signup signed source ref requires token");
36622
37303
  return parsed;
36623
37304
  }
36624
37305
  //#endregion
@@ -36651,65 +37332,79 @@ const editorialSplitLayout = styledSection({
36651
37332
  spacing: "comfortable",
36652
37333
  background: "surface"
36653
37334
  });
36654
- const newsletterSignupBlockDefinition = {
36655
- manifest: createBlockManifest({
36656
- id: "block.newsletter-signup",
36657
- title: "Newsletter Signup",
36658
- titleSource: "title",
36659
- description: "Newsletter subscription form with configurable field presets.",
36660
- category: "interactive",
36661
- contentSource: { kind: "inline" },
36662
- fragments: [{
36663
- fragment: newsletterCopyFragment,
36664
- fieldPriority: 0
36665
- }, {
36666
- fragment: newsletterFormFragment,
36667
- fieldPriority: 1
36668
- }],
36669
- layout: stackedLayout,
36670
- variants: {
36671
- stacked: stackedLayout,
36672
- inline: inlineLayout,
36673
- "editorial-split": editorialSplitLayout
36674
- },
36675
- defaultVariant: "stacked",
36676
- variantOptions: {
36677
- stacked: {
36678
- label: "Stacked",
36679
- description: "Vertical layout with fields stacked"
36680
- },
36681
- inline: {
36682
- label: "Inline",
36683
- description: "Horizontal layout (email + button side by side)"
36684
- },
36685
- "editorial-split": {
36686
- label: "Editorial split",
36687
- description: "Copy and form in balanced side-by-side columns, stacked on mobile."
36688
- }
36689
- },
36690
- styleTokens: {
36691
- background: "surface",
36692
- typography: "body",
36693
- spacing: "md"
37335
+ const newsletterSignupManifest = createBlockManifest({
37336
+ id: "block.newsletter-signup",
37337
+ title: "Newsletter Signup",
37338
+ titleSource: "title",
37339
+ description: "Newsletter subscription form with configurable field presets.",
37340
+ category: "interactive",
37341
+ contentSource: { kind: "inline" },
37342
+ fragments: [{
37343
+ fragment: newsletterCopyFragment,
37344
+ fieldPriority: 0
37345
+ }, {
37346
+ fragment: newsletterFormFragment,
37347
+ fieldPriority: 1
37348
+ }],
37349
+ layout: stackedLayout,
37350
+ variants: {
37351
+ stacked: stackedLayout,
37352
+ inline: inlineLayout,
37353
+ "editorial-split": editorialSplitLayout
37354
+ },
37355
+ defaultVariant: "stacked",
37356
+ variantOptions: {
37357
+ stacked: {
37358
+ label: "Stacked",
37359
+ description: "Vertical layout with fields stacked"
36694
37360
  },
36695
- behaviours: {
36696
- supportsThemeSwitching: true,
36697
- inlineEditing: false,
36698
- animation: false,
36699
- paletteHidden: false
37361
+ inline: {
37362
+ label: "Inline",
37363
+ description: "Horizontal layout (email + button side by side)"
36700
37364
  },
36701
- tags: [
36702
- "newsletter",
36703
- "signup",
36704
- "email",
36705
- "subscribe",
36706
- "subscription",
36707
- "mailchimp",
36708
- "marketing",
36709
- "lead-capture"
36710
- ],
36711
- icon: "Mail"
36712
- }),
37365
+ "editorial-split": {
37366
+ label: "Editorial split",
37367
+ description: "Copy and form in balanced side-by-side columns, stacked on mobile."
37368
+ }
37369
+ },
37370
+ styleTokens: {
37371
+ background: "surface",
37372
+ typography: "body",
37373
+ spacing: "md"
37374
+ },
37375
+ behaviours: {
37376
+ supportsThemeSwitching: true,
37377
+ inlineEditing: false,
37378
+ animation: false,
37379
+ paletteHidden: false
37380
+ },
37381
+ tags: [
37382
+ "newsletter",
37383
+ "signup",
37384
+ "email",
37385
+ "subscribe",
37386
+ "subscription",
37387
+ "mailchimp",
37388
+ "marketing",
37389
+ "lead-capture"
37390
+ ],
37391
+ icon: "Mail"
37392
+ });
37393
+ function sanitizeNewsletterSignupPublicContent(content) {
37394
+ if (typeof content !== "object" || content === null || Array.isArray(content)) return content;
37395
+ const value = content;
37396
+ return {
37397
+ title: value.title,
37398
+ subtitle: value.subtitle,
37399
+ targeting: value.targeting,
37400
+ buttonVariant: value.buttonVariant,
37401
+ mailingListSource: value.mailingListSource,
37402
+ mailingListSourceToken: value.mailingListSourceToken,
37403
+ _sectionStyles: value._sectionStyles
37404
+ };
37405
+ }
37406
+ const newsletterSignupBlockDefinition = {
37407
+ manifest: newsletterSignupManifest,
36713
37408
  runtime: newsletterFormBlockRuntime
36714
37409
  };
36715
37410
  //#endregion
@@ -37991,6 +38686,14 @@ const systemBlockDefinitions = [
37991
38686
  weight: "standard"
37992
38687
  })]
37993
38688
  })),
38689
+ withDesignCapability("block.appointment-services", appointmentServicesBlockDefinition, sectionOnly(), defineBlockDesignMetadata({
38690
+ flow: conversionFlow,
38691
+ layoutProfiles: [layoutProfile({
38692
+ variantKey: "default",
38693
+ family: "stack",
38694
+ weight: "standard"
38695
+ })]
38696
+ })),
37994
38697
  withDesignCapability("block.weekly-slot-availability", weeklySlotAvailabilityBlockDefinition, sectionOnly(), defineBlockDesignMetadata({
37995
38698
  flow: offeringsFlow,
37996
38699
  layoutProfiles: [layoutProfile({
@@ -40832,6 +41535,7 @@ function usePage(params) {
40832
41535
  theme: requireSdkTheme(site.theme, siteId),
40833
41536
  siteId,
40834
41537
  resolvedData,
41538
+ mailingList: site.mailingList,
40835
41539
  sdkConfig: site.sdkConfig ?? null,
40836
41540
  supabaseUrl: site.supabaseUrl,
40837
41541
  ...mediaAssets ? { mediaAssets } : {}