riverbankcms 0.6.0 → 0.7.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.
@@ -814,20 +814,20 @@ function parseOptionAvailability(input) {
814
814
  function parseEventPricingOption(input) {
815
815
  const identity = parseOptionIdentity(input);
816
816
  const commercials = parseOptionCommercials(input);
817
- const availability = parseOptionAvailability(input);
817
+ const availability$1 = parseOptionAvailability(input);
818
818
  const issues = [
819
819
  identity,
820
820
  commercials,
821
- availability
821
+ availability$1
822
822
  ].flatMap((result) => result.ok ? [] : [result.error]);
823
- if (!identity.ok || !commercials.ok || !availability.ok) {
823
+ if (!identity.ok || !commercials.ok || !availability$1.ok) {
824
824
  const nonEmptyIssues = fromReadonlyArray(issues);
825
825
  return err(nonEmptyIssues.ok ? nonEmptyIssues.value : oneIssue({ kind: "invalid_object" }));
826
826
  }
827
827
  return ok({
828
828
  ...identity.value,
829
829
  ...commercials.value,
830
- ...availability.value
830
+ ...availability$1.value
831
831
  });
832
832
  }
833
833
  function unexpectedValues(input, fields) {
@@ -1303,7 +1303,7 @@ function parseEventPageSurfaceDestinationMode(value) {
1303
1303
  return value === "cms-managed" || value === "system-managed" ? value : undefined;
1304
1304
  }
1305
1305
  function parseEventPageSurfaceSubject(value) {
1306
- if (!isRecord$6(value)) return err({ kind: "notObject" });
1306
+ if (!isRecord$8(value)) return err({ kind: "notObject" });
1307
1307
  switch (value.kind) {
1308
1308
  case "eventSeries": {
1309
1309
  const eventSeriesId = parseEventSeriesId(value.eventSeriesId);
@@ -1373,7 +1373,7 @@ function parseEventSeriesIdList(values) {
1373
1373
  }
1374
1374
  return ok(parsed);
1375
1375
  }
1376
- function isRecord$6(value) {
1376
+ function isRecord$8(value) {
1377
1377
  return typeof value === "object" && value !== null && !Array.isArray(value);
1378
1378
  }
1379
1379
 
@@ -3239,7 +3239,7 @@ const knownSocialPlatforms = [
3239
3239
  "x",
3240
3240
  "youtube"
3241
3241
  ];
3242
- function isRecord$5(value) {
3242
+ function isRecord$7(value) {
3243
3243
  return typeof value === "object" && value !== null && !Array.isArray(value);
3244
3244
  }
3245
3245
  function trimmedOptionalString(value) {
@@ -3249,7 +3249,7 @@ function trimmedOptionalString(value) {
3249
3249
  return trimmed.length > 0 ? trimmed : null;
3250
3250
  }
3251
3251
  function parsePublicContactDetails(value) {
3252
- if (!isRecord$5(value)) {
3252
+ if (!isRecord$7(value)) {
3253
3253
  return err({
3254
3254
  code: "invalid_shape",
3255
3255
  path: "contact",
@@ -3345,7 +3345,7 @@ function minutesSinceMidnight(value) {
3345
3345
  }
3346
3346
  function parseOpeningHoursDay(value, expectedWeekday, index) {
3347
3347
  const path = `openingHours.days.${index}`;
3348
- if (!isRecord$5(value) || value.weekday !== expectedWeekday) {
3348
+ if (!isRecord$7(value) || value.weekday !== expectedWeekday) {
3349
3349
  return err({
3350
3350
  code: "invalid_weekdays",
3351
3351
  path,
@@ -3366,7 +3366,7 @@ function parseOpeningHoursDay(value, expectedWeekday, index) {
3366
3366
  const intervals = [];
3367
3367
  for (const [intervalIndex, rawInterval] of value.intervals.entries()) {
3368
3368
  const intervalPath = `${path}.intervals.${intervalIndex}`;
3369
- if (!isRecord$5(rawInterval)) {
3369
+ if (!isRecord$7(rawInterval)) {
3370
3370
  return err({
3371
3371
  code: "invalid_interval",
3372
3372
  path: intervalPath,
@@ -3416,7 +3416,7 @@ function parseOpeningHoursDay(value, expectedWeekday, index) {
3416
3416
  });
3417
3417
  }
3418
3418
  function parseWeeklyOpeningHours(value) {
3419
- if (!isRecord$5(value) || !Array.isArray(value.days) || value.days.length !== publicProfileWeekdays.length) {
3419
+ if (!isRecord$7(value) || !Array.isArray(value.days) || value.days.length !== publicProfileWeekdays.length) {
3420
3420
  return err({
3421
3421
  code: "invalid_weekdays",
3422
3422
  path: "openingHours.days",
@@ -3496,7 +3496,7 @@ function parsePublicSocialLinks(value) {
3496
3496
  const ids = new Set();
3497
3497
  for (const [index, rawLink] of value.entries()) {
3498
3498
  const path = `socialLinks.${index}`;
3499
- if (!isRecord$5(rawLink)) {
3499
+ if (!isRecord$7(rawLink)) {
3500
3500
  return err({
3501
3501
  code: "invalid_social_link",
3502
3502
  path,
@@ -3543,7 +3543,7 @@ function parsePublicSocialLinks(value) {
3543
3543
  return ok(links);
3544
3544
  }
3545
3545
  function parsePublicProfile(value) {
3546
- if (!isRecord$5(value)) {
3546
+ if (!isRecord$7(value)) {
3547
3547
  return err({
3548
3548
  code: "invalid_shape",
3549
3549
  path: "",
@@ -3556,23 +3556,36 @@ function parsePublicProfile(value) {
3556
3556
  if (!openingHours.ok) return openingHours;
3557
3557
  const socialLinks = parsePublicSocialLinks(value.socialLinks);
3558
3558
  if (!socialLinks.ok) return socialLinks;
3559
+ const addressVisibility = parseAddressVisibility(value.addressVisibility);
3560
+ if (!addressVisibility.ok) return addressVisibility;
3559
3561
  return ok({
3560
3562
  contact: contact.value,
3561
3563
  openingHours: openingHours.value,
3562
- socialLinks: socialLinks.value
3564
+ socialLinks: socialLinks.value,
3565
+ addressVisibility: addressVisibility.value
3566
+ });
3567
+ }
3568
+ function parseAddressVisibility(value) {
3569
+ if (value === undefined || value === null) return ok("shown");
3570
+ if (value === "shown" || value === "hidden") return ok(value);
3571
+ return err({
3572
+ code: "invalid_address_visibility",
3573
+ path: "addressVisibility",
3574
+ message: "Address visibility must be shown or hidden."
3563
3575
  });
3564
3576
  }
3565
3577
  function emptyPublicProfile() {
3566
3578
  return {
3567
3579
  contact: null,
3568
3580
  openingHours: null,
3569
- socialLinks: []
3581
+ socialLinks: [],
3582
+ addressVisibility: "shown"
3570
3583
  };
3571
3584
  }
3572
3585
 
3573
3586
  //#endregion
3574
3587
  //#region ../core/src/rich-text.ts
3575
- function isRecord$4(value) {
3588
+ function isRecord$6(value) {
3576
3589
  return typeof value === "object" && value !== null && !Array.isArray(value);
3577
3590
  }
3578
3591
  function hasOnlyKeys(value, allowed) {
@@ -3594,10 +3607,10 @@ function isDenseArrayOf(value, predicate) {
3594
3607
  return true;
3595
3608
  }
3596
3609
  function isTipTapMark(value) {
3597
- return isRecord$4(value) && hasOnlyKeys(value, markKeys) && typeof value.type === "string" && value.type.length > 0 && (value.attrs === undefined || isRecord$4(value.attrs));
3610
+ return isRecord$6(value) && hasOnlyKeys(value, markKeys) && typeof value.type === "string" && value.type.length > 0 && (value.attrs === undefined || isRecord$6(value.attrs));
3598
3611
  }
3599
3612
  function isTipTapNode(value) {
3600
- if (!isRecord$4(value) || !hasOnlyKeys(value, nodeKeys) || typeof value.type !== "string" || value.type.length === 0 || value.type === "doc" || value.attrs !== undefined && !isRecord$4(value.attrs) || value.marks !== undefined && (!Array.isArray(value.marks) || !isDenseArrayOf(value.marks, isTipTapMark)) || value.content !== undefined && (!Array.isArray(value.content) || !isDenseArrayOf(value.content, isTipTapNode))) {
3613
+ if (!isRecord$6(value) || !hasOnlyKeys(value, nodeKeys) || typeof value.type !== "string" || value.type.length === 0 || value.type === "doc" || value.attrs !== undefined && !isRecord$6(value.attrs) || value.marks !== undefined && (!Array.isArray(value.marks) || !isDenseArrayOf(value.marks, isTipTapMark)) || value.content !== undefined && (!Array.isArray(value.content) || !isDenseArrayOf(value.content, isTipTapNode))) {
3601
3614
  return false;
3602
3615
  }
3603
3616
  if (value.type === "text") {
@@ -3606,89 +3619,222 @@ function isTipTapNode(value) {
3606
3619
  return value.text === undefined;
3607
3620
  }
3608
3621
  function isCanonicalRichTextDoc(value) {
3609
- return isRecord$4(value) && hasOnlyKeys(value, rootKeys) && value.type === "doc" && Array.isArray(value.content) && isDenseArrayOf(value.content, isTipTapNode);
3622
+ return isRecord$6(value) && hasOnlyKeys(value, rootKeys) && value.type === "doc" && Array.isArray(value.content) && isDenseArrayOf(value.content, isTipTapNode);
3610
3623
  }
3611
3624
 
3612
3625
  //#endregion
3613
- //#region ../core/src/newsletter-signup-destination.ts
3626
+ //#region ../core/src/mailing-list-identifiers.ts
3614
3627
  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;
3615
- const MAILCHIMP_AUDIENCE_ID_PATTERN = /^[A-Za-z0-9_-]{1,100}$/u;
3628
+ const OPAQUE_ID_PATTERN = /^[A-Za-z0-9_-]{1,100}$/u;
3616
3629
  const CONTROL_CHARACTER_PATTERN = /[\u0000-\u001f\u007f]/u;
3617
- function isRecord$3(value) {
3618
- return typeof value === "object" && value !== null && !Array.isArray(value);
3619
- }
3620
- function parseTrimmedString(value) {
3630
+ function parseTrimmedMatchingString(value, pattern) {
3621
3631
  if (typeof value !== "string") return null;
3622
3632
  const trimmed = value.trim();
3623
- return trimmed.length > 0 ? trimmed : null;
3633
+ return pattern.test(trimmed) ? trimmed : null;
3634
+ }
3635
+ function parsePositiveInteger(value) {
3636
+ return typeof value === "number" && Number.isSafeInteger(value) && value > 0 ? value : null;
3624
3637
  }
3625
3638
  function parseNewsletterListId(value) {
3626
- const trimmed = parseTrimmedString(value);
3627
- return trimmed !== null && UUID_PATTERN.test(trimmed) ? trimmed : null;
3639
+ return parseTrimmedMatchingString(value, UUID_PATTERN);
3628
3640
  }
3629
3641
  function parseMailchimpConnectionId(value) {
3630
- const trimmed = parseTrimmedString(value);
3631
- return trimmed !== null && UUID_PATTERN.test(trimmed) ? trimmed : null;
3642
+ return parseTrimmedMatchingString(value, UUID_PATTERN);
3643
+ }
3644
+ function parseMailchimpAccountId(value) {
3645
+ return parseTrimmedMatchingString(value, OPAQUE_ID_PATTERN);
3632
3646
  }
3633
3647
  function parseMailchimpAudienceId(value) {
3634
- const trimmed = parseTrimmedString(value);
3635
- return trimmed !== null && MAILCHIMP_AUDIENCE_ID_PATTERN.test(trimmed) ? trimmed : null;
3648
+ return parseTrimmedMatchingString(value, OPAQUE_ID_PATTERN);
3649
+ }
3650
+ function parseMailchimpInterestId(value) {
3651
+ return parseTrimmedMatchingString(value, OPAQUE_ID_PATTERN);
3636
3652
  }
3637
3653
  function parseMailchimpTagName(value) {
3638
- const trimmed = parseTrimmedString(value);
3639
- return trimmed !== null && trimmed.length <= 100 && !CONTROL_CHARACTER_PATTERN.test(trimmed) ? trimmed : null;
3640
- }
3641
- function parseNewsletterSignupDestinationProvider(value) {
3642
- return value === "riverbank" || value === "mailchimp" ? value : null;
3643
- }
3644
- function parseNewsletterSignupAuthoringDestinationValue(value) {
3645
- if (!isRecord$3(value) || typeof value.tag !== "string") {
3646
- return err({ kind: "invalid-destination-shape" });
3647
- }
3648
- switch (value.tag) {
3649
- case "riverbank_list": {
3650
- const listId = parseNewsletterListId(value.listId);
3651
- return listId === null ? err({ kind: "invalid-newsletter-list-id" }) : ok({
3652
- tag: "riverbank_list",
3653
- listId
3654
+ if (typeof value !== "string") return null;
3655
+ const trimmed = value.trim();
3656
+ return trimmed.length > 0 && trimmed.length <= 100 && !CONTROL_CHARACTER_PATTERN.test(trimmed) ? trimmed : null;
3657
+ }
3658
+ function parseBrevoConnectionId(value) {
3659
+ return parseTrimmedMatchingString(value, UUID_PATTERN);
3660
+ }
3661
+ function parseBrevoAccountId(value) {
3662
+ return parseTrimmedMatchingString(value, OPAQUE_ID_PATTERN);
3663
+ }
3664
+ function parseBrevoListId(value) {
3665
+ return parsePositiveInteger(value);
3666
+ }
3667
+ function parseBrevoConfirmationTemplateId(value) {
3668
+ return parsePositiveInteger(value);
3669
+ }
3670
+
3671
+ //#endregion
3672
+ //#region ../core/src/mailing-list-targeting-parser.ts
3673
+ function parseMailingListPlacementTargeting(value) {
3674
+ if (!isRecord$5(value)) return targetingError("shape");
3675
+ if (value.mode === "default") return ok({ mode: "default" });
3676
+ if (value.mode !== "custom") return targetingError("mode");
3677
+ if (!isRecord$5(value.selection)) return targetingError("selection");
3678
+ switch (value.provider) {
3679
+ case "native": return parseNativeTargeting(value.selection);
3680
+ case "brevo": return parseBrevoTargeting(value.selection);
3681
+ case "mailchimp": return parseMailchimpTargeting(value, value.selection);
3682
+ default: return targetingError("provider");
3683
+ }
3684
+ }
3685
+ function parseMailingListVisitorSelection(value) {
3686
+ if (!isRecord$5(value)) return visitorSelectionError("shape");
3687
+ switch (value.provider) {
3688
+ case "native": {
3689
+ const topicIds = parseVisitorIds(value.topicIds, parseNewsletterListId);
3690
+ return topicIds === null ? visitorSelectionError("targets") : ok({
3691
+ provider: "native",
3692
+ topicIds
3654
3693
  });
3655
3694
  }
3656
- case "mailchimp_audience": {
3657
- const connectionId = parseMailchimpConnectionId(value.connectionId);
3658
- if (connectionId === null) {
3659
- return err({ kind: "invalid-mailchimp-connection-id" });
3695
+ case "brevo": {
3696
+ const listIds = parseVisitorIds(value.listIds, parseBrevoListId);
3697
+ return listIds === null ? visitorSelectionError("targets") : ok({
3698
+ provider: "brevo",
3699
+ listIds
3700
+ });
3701
+ }
3702
+ case "mailchimp": {
3703
+ const interestIds = parseVisitorIds(value.interestIds, parseMailchimpInterestId);
3704
+ return interestIds === null ? visitorSelectionError("targets") : ok({
3705
+ provider: "mailchimp",
3706
+ interestIds
3707
+ });
3708
+ }
3709
+ default: return visitorSelectionError("provider");
3710
+ }
3711
+ }
3712
+ function parseNativeTargeting(selection) {
3713
+ if (selection.tag === "fixed") {
3714
+ const topicIds = parseNonEmptyUniqueIds(selection.topicIds, parseNewsletterListId);
3715
+ return topicIds.ok ? ok({
3716
+ mode: "custom",
3717
+ provider: "native",
3718
+ selection: {
3719
+ tag: "fixed",
3720
+ topicIds: topicIds.value
3660
3721
  }
3661
- const audienceId = parseMailchimpAudienceId(value.audienceId);
3662
- if (audienceId === null) {
3663
- return err({ kind: "invalid-mailchimp-audience-id" });
3722
+ }) : topicIds;
3723
+ }
3724
+ if (selection.tag === "visitor_choice") {
3725
+ const offeredTopicIds = parseNonEmptyUniqueIds(selection.offeredTopicIds, parseNewsletterListId);
3726
+ return offeredTopicIds.ok ? ok({
3727
+ mode: "custom",
3728
+ provider: "native",
3729
+ selection: {
3730
+ tag: "visitor_choice",
3731
+ offeredTopicIds: offeredTopicIds.value
3664
3732
  }
3665
- const rawTags = value.tags ?? [];
3666
- if (!Array.isArray(rawTags)) {
3667
- return err({ kind: "invalid-mailchimp-tags" });
3733
+ }) : offeredTopicIds;
3734
+ }
3735
+ return targetingError("selection");
3736
+ }
3737
+ function parseBrevoTargeting(selection) {
3738
+ if (selection.tag === "fixed") {
3739
+ const listIds = parseNonEmptyUniqueIds(selection.listIds, parseBrevoListId);
3740
+ return listIds.ok ? ok({
3741
+ mode: "custom",
3742
+ provider: "brevo",
3743
+ selection: {
3744
+ tag: "fixed",
3745
+ listIds: listIds.value
3668
3746
  }
3669
- const tags = [];
3670
- for (const valueTag of rawTags) {
3671
- const tag = parseMailchimpTagName(valueTag);
3672
- if (tag === null) return err({ kind: "invalid-mailchimp-tags" });
3673
- tags.push(tag);
3747
+ }) : listIds;
3748
+ }
3749
+ if (selection.tag === "visitor_choice") {
3750
+ const offeredListIds = parseNonEmptyUniqueIds(selection.offeredListIds, parseBrevoListId);
3751
+ return offeredListIds.ok ? ok({
3752
+ mode: "custom",
3753
+ provider: "brevo",
3754
+ selection: {
3755
+ tag: "visitor_choice",
3756
+ offeredListIds: offeredListIds.value
3674
3757
  }
3675
- return ok({
3676
- tag: "mailchimp_audience",
3677
- connectionId,
3678
- audienceId,
3679
- tags
3680
- });
3681
- }
3682
- default: return err({
3683
- kind: "invalid-destination-tag",
3684
- value: value.tag
3685
- });
3758
+ }) : offeredListIds;
3759
+ }
3760
+ return targetingError("selection");
3761
+ }
3762
+ function parseMailchimpTargeting(value, selection) {
3763
+ const audienceId = parseMailchimpAudienceId(value.audienceId);
3764
+ if (audienceId === null) return targetingError("audienceId");
3765
+ if (selection.tag === "fixed") {
3766
+ const interestIds = parseUniqueIds(selection.interestIds, parseMailchimpInterestId);
3767
+ return interestIds.ok ? ok({
3768
+ mode: "custom",
3769
+ provider: "mailchimp",
3770
+ audienceId,
3771
+ selection: {
3772
+ tag: "fixed",
3773
+ interestIds: interestIds.value
3774
+ }
3775
+ }) : interestIds;
3776
+ }
3777
+ if (selection.tag === "visitor_choice") {
3778
+ const offeredInterestIds = parseNonEmptyUniqueIds(selection.offeredInterestIds, parseMailchimpInterestId);
3779
+ return offeredInterestIds.ok ? ok({
3780
+ mode: "custom",
3781
+ provider: "mailchimp",
3782
+ audienceId,
3783
+ selection: {
3784
+ tag: "visitor_choice",
3785
+ offeredInterestIds: offeredInterestIds.value
3786
+ }
3787
+ }) : offeredInterestIds;
3686
3788
  }
3789
+ return targetingError("selection");
3790
+ }
3791
+ function parseNonEmptyUniqueIds(value, parseId) {
3792
+ const parsed = parseUniqueIds(value, parseId);
3793
+ if (!parsed.ok) return parsed;
3794
+ const [first, ...rest] = parsed.value;
3795
+ return first === undefined ? targetingError("targets") : ok([first, ...rest]);
3796
+ }
3797
+ function parseUniqueIds(value, parseId) {
3798
+ if (!Array.isArray(value)) return targetingError("targets");
3799
+ const parsed = [];
3800
+ for (const item of value) {
3801
+ const id = parseId(item);
3802
+ if (id === null) return targetingError("targets");
3803
+ if (parsed.includes(id)) return targetingError("duplicate_target");
3804
+ parsed.push(id);
3805
+ }
3806
+ return ok(parsed);
3807
+ }
3808
+ function parseVisitorIds(value, parseId) {
3809
+ if (!Array.isArray(value) || value.length === 0) return null;
3810
+ const parsed = [];
3811
+ for (const item of value) {
3812
+ const id = parseId(item);
3813
+ if (id === null) return null;
3814
+ if (parsed.includes(id)) return null;
3815
+ parsed.push(id);
3816
+ }
3817
+ return parsed;
3818
+ }
3819
+ function isRecord$5(value) {
3820
+ return typeof value === "object" && value !== null && !Array.isArray(value);
3821
+ }
3822
+ function targetingError(field) {
3823
+ return err({
3824
+ tag: "invalid_mailing_list_targeting",
3825
+ field
3826
+ });
3827
+ }
3828
+ function visitorSelectionError(field) {
3829
+ return err({
3830
+ tag: "invalid_mailing_list_visitor_selection",
3831
+ field
3832
+ });
3687
3833
  }
3688
3834
 
3689
3835
  //#endregion
3690
3836
  //#region ../core/src/footer-engagement.ts
3691
- function isRecord$2(value) {
3837
+ function isRecord$4(value) {
3692
3838
  return typeof value === "object" && value !== null && !Array.isArray(value);
3693
3839
  }
3694
3840
  function invalid(path, message) {
@@ -3722,7 +3868,7 @@ function parseEngagementBase(value, base, path) {
3722
3868
  }
3723
3869
  const treatment = parseTreatment(value.treatment, `${path}.treatment`);
3724
3870
  if (!treatment.ok) return treatment;
3725
- if (!isRecord$2(value.source)) {
3871
+ if (!isRecord$4(value.source)) {
3726
3872
  return err(invalid(`${path}.source`, "Footer engagement source is invalid."));
3727
3873
  }
3728
3874
  const intro = parseBoundedText(value.source.intro, `${path}.source.intro`, {
@@ -3751,36 +3897,17 @@ function parseStandardContact(source, base, path) {
3751
3897
  }
3752
3898
  function parseMailingList(source, base, path) {
3753
3899
  const { intro, ...section } = base;
3754
- const destination = parseNewsletterSignupAuthoringDestinationValue(source.destination);
3755
- if (!destination.ok) {
3756
- return err(invalid(`${path}.destination`, "Footer mailing-list destination is invalid."));
3757
- }
3758
- const buttonLabel = parseBoundedText(source.buttonLabel, `${path}.buttonLabel`, { maxLength: 40 });
3759
- if (!buttonLabel.ok) return buttonLabel;
3760
- const successMessage = parseBoundedText(source.successMessage, `${path}.successMessage`, { maxLength: 160 });
3761
- if (!successMessage.ok) return successMessage;
3762
- const privacyNote = parseBoundedText(source.privacyNote, `${path}.privacyNote`, {
3763
- maxLength: 240,
3764
- optional: true
3765
- });
3766
- if (!privacyNote.ok) return privacyNote;
3767
- if (buttonLabel.value === null || successMessage.value === null) {
3768
- return err(invalid(path, "Footer mailing-list copy cannot be empty."));
3769
- }
3770
- if (source.fieldPreset !== "emailOnly") {
3771
- return err(invalid(`${path}.fieldPreset`, "Footer mailing-list signup must be email-only."));
3900
+ const targeting = parseMailingListPlacementTargeting(source.targeting);
3901
+ if (!targeting.ok) {
3902
+ return err(invalid(`${path}.targeting`, "Footer mailing-list targeting is invalid."));
3772
3903
  }
3773
3904
  return ok({
3774
3905
  ...section,
3775
3906
  kind: "engagement",
3776
3907
  source: {
3777
3908
  kind: "mailing-list",
3778
- destination: destination.value,
3779
- intro,
3780
- buttonLabel: buttonLabel.value,
3781
- successMessage: successMessage.value,
3782
- privacyNote: privacyNote.value,
3783
- fieldPreset: "emailOnly"
3909
+ targeting: targeting.value,
3910
+ intro
3784
3911
  }
3785
3912
  });
3786
3913
  }
@@ -3788,7 +3915,7 @@ function parseFooterEngagementSectionValue(value, sectionBase, path) {
3788
3915
  const base = parseEngagementBase(value, sectionBase, path);
3789
3916
  if (!base.ok) return base;
3790
3917
  const source = value.source;
3791
- if (!isRecord$2(source)) {
3918
+ if (!isRecord$4(source)) {
3792
3919
  return err(invalid(`${path}.source`, "Footer engagement source is invalid."));
3793
3920
  }
3794
3921
  switch (source.kind) {
@@ -3903,7 +4030,7 @@ function unsafeAsFooterModuleId(value) {
3903
4030
  function parseFooterModuleId(value) {
3904
4031
  return parseSectionId(value, "sectionId");
3905
4032
  }
3906
- function isRecord$1(value) {
4033
+ function isRecord$3(value) {
3907
4034
  return typeof value === "object" && value !== null && !Array.isArray(value);
3908
4035
  }
3909
4036
  function isRichText(value) {
@@ -3933,7 +4060,7 @@ function parseHeading(value, path) {
3933
4060
  }
3934
4061
  function parseFooterSection(value, index) {
3935
4062
  const path = `sections.${index}`;
3936
- if (!isRecord$1(value)) {
4063
+ if (!isRecord$3(value)) {
3937
4064
  return err({
3938
4065
  code: "invalid_section",
3939
4066
  path,
@@ -3968,7 +4095,7 @@ function parseFooterSection(value, index) {
3968
4095
  }
3969
4096
  break;
3970
4097
  case "navigation":
3971
- if (isRecord$1(value.source) && value.source.kind === "menu-role" && value.source.role === "main") {
4098
+ if (isRecord$3(value.source) && value.source.kind === "menu-role" && value.source.role === "main") {
3972
4099
  return ok({
3973
4100
  ...base,
3974
4101
  kind: "navigation",
@@ -3981,7 +4108,7 @@ function parseFooterSection(value, index) {
3981
4108
  }
3982
4109
  break;
3983
4110
  case "legal-navigation":
3984
- if (isRecord$1(value.source) && value.source.kind === "menu-role" && value.source.role === "legal-policy") {
4111
+ if (isRecord$3(value.source) && value.source.kind === "menu-role" && value.source.role === "legal-policy") {
3985
4112
  return ok({
3986
4113
  ...base,
3987
4114
  kind: "legal-navigation",
@@ -3994,7 +4121,7 @@ function parseFooterSection(value, index) {
3994
4121
  }
3995
4122
  break;
3996
4123
  case "business-address":
3997
- if (isRecord$1(value.source) && value.source.kind === "business-address") {
4124
+ if (isRecord$3(value.source) && value.source.kind === "business-address") {
3998
4125
  return ok({
3999
4126
  ...base,
4000
4127
  kind: "business-address",
@@ -4006,7 +4133,7 @@ function parseFooterSection(value, index) {
4006
4133
  case "contact":
4007
4134
  case "opening-hours":
4008
4135
  case "social-links":
4009
- if (isRecord$1(value.source) && value.source.kind === "public-profile") {
4136
+ if (isRecord$3(value.source) && value.source.kind === "public-profile") {
4010
4137
  return ok({
4011
4138
  ...base,
4012
4139
  kind: value.kind,
@@ -4058,7 +4185,7 @@ function validateFooterComposition(composition) {
4058
4185
  return ok(composition);
4059
4186
  }
4060
4187
  function parseFooterCompositionV1(value) {
4061
- if (!isRecord$1(value)) {
4188
+ if (!isRecord$3(value)) {
4062
4189
  return err({
4063
4190
  code: "invalid_shape",
4064
4191
  path: "",
@@ -4091,7 +4218,7 @@ function parseFooterCompositionV1(value) {
4091
4218
  });
4092
4219
  }
4093
4220
  function parseLegacyPlacementSection(value, index, selectedIds) {
4094
- if (!isRecord$1(value)) {
4221
+ if (!isRecord$3(value)) {
4095
4222
  return err({
4096
4223
  code: "invalid_section",
4097
4224
  path: `modules.${index}`,
@@ -4099,7 +4226,7 @@ function parseLegacyPlacementSection(value, index, selectedIds) {
4099
4226
  });
4100
4227
  }
4101
4228
  const selected = typeof value.id === "string" && selectedIds.has(value.id);
4102
- const source = isRecord$1(value.source) ? value.source : null;
4229
+ const source = isRecord$3(value.source) ? value.source : null;
4103
4230
  const mapped = value.kind === "navigation" && source?.kind === "menu-role" && source.role === "footer" ? {
4104
4231
  ...value,
4105
4232
  source: {
@@ -4118,7 +4245,7 @@ function parseLegacyPlacementSection(value, index, selectedIds) {
4118
4245
  * It is intentionally not called by the runtime parser.
4119
4246
  */
4120
4247
  function upgradePlacementFooterCompositionV1(value) {
4121
- if (!isRecord$1(value) || value.schemaVersion !== 1 || !Array.isArray(value.modules) || !Array.isArray(value.placements)) {
4248
+ if (!isRecord$3(value) || value.schemaVersion !== 1 || !Array.isArray(value.modules) || !Array.isArray(value.placements)) {
4122
4249
  return err({
4123
4250
  code: "invalid_shape",
4124
4251
  path: "",
@@ -4127,7 +4254,7 @@ function upgradePlacementFooterCompositionV1(value) {
4127
4254
  }
4128
4255
  const selectedIds = new Set();
4129
4256
  for (const [index, placement] of value.placements.entries()) {
4130
- if (!isRecord$1(placement) || typeof placement.moduleId !== "string") {
4257
+ if (!isRecord$3(placement) || typeof placement.moduleId !== "string") {
4131
4258
  return err({
4132
4259
  code: "invalid_shape",
4133
4260
  path: `placements.${index}`,
@@ -4230,8 +4357,8 @@ function repairTargetFor(section) {
4230
4357
  };
4231
4358
  }
4232
4359
  }
4233
- function resolveFooterSections(composition, availability) {
4234
- return composition.sections.filter((section) => section.selected).map((section) => isSectionRenderable(section, availability) ? {
4360
+ function resolveFooterSections(composition, availability$1) {
4361
+ return composition.sections.filter((section) => section.selected).map((section) => isSectionRenderable(section, availability$1) ? {
4235
4362
  kind: "renderable",
4236
4363
  section
4237
4364
  } : {
@@ -4240,16 +4367,16 @@ function resolveFooterSections(composition, availability) {
4240
4367
  repairTarget: repairTargetFor(section)
4241
4368
  });
4242
4369
  }
4243
- function isSectionRenderable(section, availability) {
4370
+ function isSectionRenderable(section, availability$1) {
4244
4371
  switch (section.kind) {
4245
- case "brand": return section.showLogo && availability.hasLogo || section.showTitle;
4246
- case "navigation": return availability.hasMainNavigation;
4247
- case "legal-navigation": return availability.hasLegalPolicyNavigation;
4248
- case "business-address": return availability.hasBusinessAddress;
4249
- case "contact": return availability.hasContact;
4250
- case "opening-hours": return availability.hasOpeningHours;
4251
- case "social-links": return availability.hasSocialLinks;
4252
- case "engagement": return availability.hasEngagementSource;
4372
+ case "brand": return section.showLogo && availability$1.hasLogo || section.showTitle;
4373
+ case "navigation": return availability$1.hasMainNavigation;
4374
+ case "legal-navigation": return availability$1.hasLegalPolicyNavigation;
4375
+ case "business-address": return availability$1.hasBusinessAddress;
4376
+ case "contact": return availability$1.hasContact;
4377
+ case "opening-hours": return availability$1.hasOpeningHours;
4378
+ case "social-links": return availability$1.hasSocialLinks;
4379
+ case "engagement": return availability$1.hasEngagementSource;
4253
4380
  case "supporting-text":
4254
4381
  case "legal": return section.content !== null;
4255
4382
  }
@@ -4342,7 +4469,7 @@ const defaultSections = (legacy, options) => {
4342
4469
  ];
4343
4470
  };
4344
4471
  function normalizeLegacyFooterContentToV1(value, options = {}) {
4345
- if (!isRecord$1(value)) {
4472
+ if (!isRecord$3(value)) {
4346
4473
  return err({
4347
4474
  code: "invalid_shape",
4348
4475
  path: "",
@@ -4363,6 +4490,716 @@ function normalizeFooterNavigationLayout(value) {
4363
4490
  return value === "stack" ? "stack" : defaultFooterNavigationLayout;
4364
4491
  }
4365
4492
 
4493
+ //#endregion
4494
+ //#region ../core/src/mailing-list-setup.ts
4495
+ function planMailingListFieldCompatibility(input) {
4496
+ const collectedFields = resolveCollectedGlobalFields(input.form);
4497
+ const requiredFields = resolveRequiredGlobalFields(input.form.fields);
4498
+ const issues = [];
4499
+ for (const destination of input.destinations) {
4500
+ for (const field of destination.requiredFields) {
4501
+ if (!collectedFields.includes(field)) {
4502
+ issues.push({
4503
+ tag: "destination_requires_uncollected_field",
4504
+ destinationKey: destination.destinationKey,
4505
+ field
4506
+ });
4507
+ } else if (!requiredFields.includes(field)) {
4508
+ issues.push({
4509
+ tag: "destination_requires_site_required_field",
4510
+ destinationKey: destination.destinationKey,
4511
+ field
4512
+ });
4513
+ }
4514
+ }
4515
+ for (const field of collectedFields) {
4516
+ if (!destination.acceptedFields.includes(field)) {
4517
+ issues.push({
4518
+ tag: requiredFields.includes(field) ? "destination_rejects_required_site_field" : "destination_rejects_collected_site_field",
4519
+ destinationKey: destination.destinationKey,
4520
+ field
4521
+ });
4522
+ }
4523
+ }
4524
+ }
4525
+ const [first, ...rest] = issues;
4526
+ return first === undefined ? { tag: "compatible" } : {
4527
+ tag: "blocked",
4528
+ issues: [first, ...rest]
4529
+ };
4530
+ }
4531
+ function resolveCollectedGlobalFields(form) {
4532
+ const phone = form.includeOptionalPhone ? ["phone"] : [];
4533
+ switch (form.fields.preset) {
4534
+ case "emailOnly": return phone;
4535
+ case "emailName": return ["name", ...phone];
4536
+ case "emailFirstLast": return [
4537
+ "first_name",
4538
+ "last_name",
4539
+ ...phone
4540
+ ];
4541
+ case "emailNameCompany": return [
4542
+ "name",
4543
+ "company",
4544
+ ...phone
4545
+ ];
4546
+ }
4547
+ }
4548
+ function resolveRequiredGlobalFields(fields) {
4549
+ switch (fields.preset) {
4550
+ case "emailOnly": return [];
4551
+ case "emailName": return fields.name === "required" ? ["name"] : [];
4552
+ case "emailFirstLast": return [...fields.firstName === "required" ? ["first_name"] : [], ...fields.lastName === "required" ? ["last_name"] : []];
4553
+ case "emailNameCompany": return [...fields.name === "required" ? ["name"] : [], ...fields.company === "required" ? ["company"] : []];
4554
+ }
4555
+ }
4556
+ function planMailingListSetupSwitch(input) {
4557
+ if (input.previousScope === null) {
4558
+ return {
4559
+ tag: "activate",
4560
+ affectedPlacementCount: 0
4561
+ };
4562
+ }
4563
+ if (sameProviderScope(input.previousScope, input.nextScope)) {
4564
+ return {
4565
+ tag: "update_same_scope",
4566
+ affectedPlacementCount: input.defaultChanged ? countDefaultMailingListPlacements(input.placementModes) : 0
4567
+ };
4568
+ }
4569
+ return {
4570
+ tag: "replace_scope",
4571
+ affectedPlacementCount: input.placementModes.length
4572
+ };
4573
+ }
4574
+ function sameProviderScope(previous, next) {
4575
+ if (previous.provider !== next.provider) return false;
4576
+ switch (previous.provider) {
4577
+ case "native": return true;
4578
+ case "mailchimp": return next.provider === "mailchimp" && previous.accountId === next.accountId;
4579
+ case "brevo": return next.provider === "brevo" && previous.accountId === next.accountId;
4580
+ }
4581
+ }
4582
+ function countDefaultMailingListPlacements(placementModes) {
4583
+ return placementModes.filter((mode) => mode === "default").length;
4584
+ }
4585
+
4586
+ //#endregion
4587
+ //#region ../core/src/mailing-list-setup-parser.ts
4588
+ function parseMailingListGlobalForm(value) {
4589
+ if (!isRecord$2(value)) return formError("shape");
4590
+ const fields = parseGlobalFields(value.fields);
4591
+ if (fields === null) return formError("fields");
4592
+ if (typeof value.includeOptionalPhone !== "boolean") {
4593
+ return formError("includeOptionalPhone");
4594
+ }
4595
+ const buttonLabel = parseRequiredString(value.buttonLabel);
4596
+ if (buttonLabel === null) return formError("buttonLabel");
4597
+ const privacyNote = parseOptionalString(value.privacyNote);
4598
+ if (privacyNote === undefined) return formError("privacyNote");
4599
+ return ok({
4600
+ fields,
4601
+ includeOptionalPhone: value.includeOptionalPhone,
4602
+ buttonLabel,
4603
+ privacyNote
4604
+ });
4605
+ }
4606
+ function parseMailingListSiteSetup(value) {
4607
+ if (!isRecord$2(value)) return setupError("shape");
4608
+ const siteId = parseSiteId(value.siteId);
4609
+ if (!siteId.ok) return setupError("siteId");
4610
+ if (value.tag === "not_configured") {
4611
+ return ok({
4612
+ tag: "not_configured",
4613
+ siteId: siteId.value
4614
+ });
4615
+ }
4616
+ if (value.tag !== "configured") return setupError("tag");
4617
+ const revision = parseMailingListSetupRevision(value.revision);
4618
+ if (revision === null) return setupError("revision");
4619
+ const form = parseMailingListGlobalForm(value.form);
4620
+ if (!form.ok) return setupError("form");
4621
+ const provider = parseConfiguredMailingListProvider(value.provider);
4622
+ if (!provider.ok) return provider;
4623
+ return ok({
4624
+ tag: "configured",
4625
+ siteId: siteId.value,
4626
+ revision,
4627
+ form: form.value,
4628
+ provider: provider.value
4629
+ });
4630
+ }
4631
+ function parseConfiguredMailingListProvider(value) {
4632
+ if (!isRecord$2(value) || typeof value.provider !== "string") {
4633
+ return setupError("provider");
4634
+ }
4635
+ switch (value.provider) {
4636
+ case "native": {
4637
+ const defaultTopicId = parseNewsletterListId(value.defaultTopicId);
4638
+ if (defaultTopicId === null) return setupError("defaultTargetId");
4639
+ const confirmation = parseToggleConfirmation(value.confirmation);
4640
+ return confirmation === null ? setupError("confirmation") : ok({
4641
+ provider: "native",
4642
+ defaultTopicId,
4643
+ confirmation
4644
+ });
4645
+ }
4646
+ case "mailchimp": {
4647
+ const connectionId = parseMailchimpConnectionId(value.connectionId);
4648
+ if (connectionId === null) return setupError("connectionId");
4649
+ const accountId = parseMailchimpAccountId(value.accountId);
4650
+ if (accountId === null) return setupError("accountId");
4651
+ const defaultAudienceId = parseMailchimpAudienceId(value.defaultAudienceId);
4652
+ if (defaultAudienceId === null) return setupError("defaultTargetId");
4653
+ const confirmation = parseToggleConfirmation(value.confirmation);
4654
+ return confirmation === null ? setupError("confirmation") : ok({
4655
+ provider: "mailchimp",
4656
+ connectionId,
4657
+ accountId,
4658
+ defaultAudienceId,
4659
+ confirmation
4660
+ });
4661
+ }
4662
+ case "brevo": {
4663
+ const connectionId = parseBrevoConnectionId(value.connectionId);
4664
+ if (connectionId === null) return setupError("connectionId");
4665
+ const accountId = parseBrevoAccountId(value.accountId);
4666
+ if (accountId === null) return setupError("accountId");
4667
+ const defaultListId = parseBrevoListId(value.defaultListId);
4668
+ if (defaultListId === null) return setupError("defaultTargetId");
4669
+ const confirmation = parseBrevoConfirmation(value.confirmation);
4670
+ return confirmation === null ? setupError("confirmation") : ok({
4671
+ provider: "brevo",
4672
+ connectionId,
4673
+ accountId,
4674
+ defaultListId,
4675
+ confirmation
4676
+ });
4677
+ }
4678
+ default: return setupError("provider");
4679
+ }
4680
+ }
4681
+ function parseGlobalFields(value) {
4682
+ if (!isRecord$2(value) || typeof value.preset !== "string") return null;
4683
+ switch (value.preset) {
4684
+ case "emailOnly": return { preset: "emailOnly" };
4685
+ case "emailName": return isRequirement(value.name) ? {
4686
+ preset: "emailName",
4687
+ name: value.name
4688
+ } : null;
4689
+ case "emailFirstLast": return isRequirement(value.firstName) && isRequirement(value.lastName) ? {
4690
+ preset: "emailFirstLast",
4691
+ firstName: value.firstName,
4692
+ lastName: value.lastName
4693
+ } : null;
4694
+ case "emailNameCompany": return isRequirement(value.name) && isRequirement(value.company) ? {
4695
+ preset: "emailNameCompany",
4696
+ name: value.name,
4697
+ company: value.company
4698
+ } : null;
4699
+ default: return null;
4700
+ }
4701
+ }
4702
+ function parseToggleConfirmation(value) {
4703
+ if (!isRecord$2(value)) return null;
4704
+ return value.tag === "on" || value.tag === "off" ? { tag: value.tag } : null;
4705
+ }
4706
+ function parseBrevoConfirmation(value) {
4707
+ if (!isRecord$2(value)) return null;
4708
+ if (value.tag === "off") return { tag: "off" };
4709
+ if (value.tag !== "on") return null;
4710
+ const templateId = parseBrevoConfirmationTemplateId(value.templateId);
4711
+ const redirectUrl = parseHttpUrl(value.redirectUrl);
4712
+ return templateId === null || redirectUrl === null ? null : {
4713
+ tag: "on",
4714
+ templateId,
4715
+ redirectUrl
4716
+ };
4717
+ }
4718
+ function parseMailingListSetupRevision(value) {
4719
+ return typeof value === "number" && Number.isSafeInteger(value) && value > 0 ? value : null;
4720
+ }
4721
+ function parseHttpUrl(value) {
4722
+ if (typeof value !== "string") return null;
4723
+ try {
4724
+ const url = new URL(value);
4725
+ return url.protocol === "http:" || url.protocol === "https:" ? url.toString() : null;
4726
+ } catch {
4727
+ return null;
4728
+ }
4729
+ }
4730
+ function parseRequiredString(value) {
4731
+ if (typeof value !== "string") return null;
4732
+ const trimmed = value.trim();
4733
+ return trimmed.length > 0 ? trimmed : null;
4734
+ }
4735
+ function parseOptionalString(value) {
4736
+ if (value === null || value === undefined) return null;
4737
+ if (typeof value !== "string") return undefined;
4738
+ const trimmed = value.trim();
4739
+ return trimmed.length > 0 ? trimmed : null;
4740
+ }
4741
+ function isRequirement(value) {
4742
+ return value === "required" || value === "optional";
4743
+ }
4744
+ function isRecord$2(value) {
4745
+ return typeof value === "object" && value !== null && !Array.isArray(value);
4746
+ }
4747
+ function formError(field) {
4748
+ return err({
4749
+ tag: "invalid_mailing_list_global_form",
4750
+ field
4751
+ });
4752
+ }
4753
+ function setupError(field) {
4754
+ return err({
4755
+ tag: "invalid_mailing_list_setup",
4756
+ field
4757
+ });
4758
+ }
4759
+
4760
+ //#endregion
4761
+ //#region ../core/src/mailing-list-targeting.ts
4762
+ function sameMailingListPlacementTargeting(left, right) {
4763
+ if (left.mode !== right.mode) return false;
4764
+ if (left.mode === "default" || right.mode === "default") return true;
4765
+ if (left.provider !== right.provider || left.selection.tag !== right.selection.tag) return false;
4766
+ if (left.provider === "mailchimp" && (right.provider !== "mailchimp" || left.audienceId !== right.audienceId)) return false;
4767
+ return sameIds(targetingSelectionIds(left), targetingSelectionIds(right));
4768
+ }
4769
+ function targetingSelectionIds(targeting) {
4770
+ switch (targeting.provider) {
4771
+ case "native": return targeting.selection.tag === "fixed" ? targeting.selection.topicIds : targeting.selection.offeredTopicIds;
4772
+ case "brevo": return targeting.selection.tag === "fixed" ? targeting.selection.listIds : targeting.selection.offeredListIds;
4773
+ case "mailchimp": return targeting.selection.tag === "fixed" ? targeting.selection.interestIds : targeting.selection.offeredInterestIds;
4774
+ }
4775
+ }
4776
+ function sameIds(left, right) {
4777
+ const leftIds = new Set(left);
4778
+ const rightIds = new Set(right);
4779
+ return leftIds.size === left.length && rightIds.size === right.length && leftIds.size === rightIds.size && [...leftIds].every((id) => rightIds.has(id));
4780
+ }
4781
+ function resolveMailingListTarget(input) {
4782
+ if (input.targeting.mode === "default") {
4783
+ if (input.visitorSelection !== undefined) {
4784
+ return err({ tag: "visitor_selection_not_allowed" });
4785
+ }
4786
+ return ok(resolveDefaultTarget(input.setup));
4787
+ }
4788
+ if (input.targeting.provider !== input.setup.provider) {
4789
+ return err({
4790
+ tag: "targeting_provider_mismatch",
4791
+ setupProvider: input.setup.provider,
4792
+ targetingProvider: input.targeting.provider
4793
+ });
4794
+ }
4795
+ return resolveCustomTarget(input.targeting, input.visitorSelection);
4796
+ }
4797
+ function resolveDefaultTarget(setup) {
4798
+ switch (setup.provider) {
4799
+ case "native": return {
4800
+ provider: "native",
4801
+ topicIds: [setup.defaultTopicId]
4802
+ };
4803
+ case "brevo": return {
4804
+ provider: "brevo",
4805
+ listIds: [setup.defaultListId]
4806
+ };
4807
+ case "mailchimp": return {
4808
+ provider: "mailchimp",
4809
+ audienceId: setup.defaultAudienceId,
4810
+ interestIds: []
4811
+ };
4812
+ }
4813
+ }
4814
+ function resolveCustomTarget(targeting, visitorSelection) {
4815
+ switch (targeting.provider) {
4816
+ case "native": return resolveNativeTarget(targeting, visitorSelection);
4817
+ case "brevo": return resolveBrevoTarget(targeting, visitorSelection);
4818
+ case "mailchimp": return resolveMailchimpTarget(targeting, visitorSelection);
4819
+ }
4820
+ }
4821
+ function resolveNativeTarget(targeting, selection) {
4822
+ if (targeting.selection.tag === "fixed") {
4823
+ return selection === undefined ? ok({
4824
+ provider: "native",
4825
+ topicIds: targeting.selection.topicIds
4826
+ }) : err({ tag: "visitor_selection_not_allowed" });
4827
+ }
4828
+ if (selection === undefined) {
4829
+ return targeting.selection.offeredTopicIds.length === 1 ? ok({
4830
+ provider: "native",
4831
+ topicIds: targeting.selection.offeredTopicIds
4832
+ }) : err({ tag: "visitor_selection_required" });
4833
+ }
4834
+ if (selection.provider !== "native") {
4835
+ return err({ tag: "visitor_selection_provider_mismatch" });
4836
+ }
4837
+ const selected = selectedNonEmptySubset(selection.topicIds, targeting.selection.offeredTopicIds);
4838
+ return selected.ok ? ok({
4839
+ provider: "native",
4840
+ topicIds: selected.value
4841
+ }) : err({ tag: "visitor_selection_outside_offer" });
4842
+ }
4843
+ function resolveBrevoTarget(targeting, selection) {
4844
+ if (targeting.selection.tag === "fixed") {
4845
+ return selection === undefined ? ok({
4846
+ provider: "brevo",
4847
+ listIds: targeting.selection.listIds
4848
+ }) : err({ tag: "visitor_selection_not_allowed" });
4849
+ }
4850
+ if (selection === undefined) {
4851
+ return targeting.selection.offeredListIds.length === 1 ? ok({
4852
+ provider: "brevo",
4853
+ listIds: targeting.selection.offeredListIds
4854
+ }) : err({ tag: "visitor_selection_required" });
4855
+ }
4856
+ if (selection.provider !== "brevo") {
4857
+ return err({ tag: "visitor_selection_provider_mismatch" });
4858
+ }
4859
+ const selected = selectedNonEmptySubset(selection.listIds, targeting.selection.offeredListIds);
4860
+ return selected.ok ? ok({
4861
+ provider: "brevo",
4862
+ listIds: selected.value
4863
+ }) : err({ tag: "visitor_selection_outside_offer" });
4864
+ }
4865
+ function resolveMailchimpTarget(targeting, selection) {
4866
+ if (targeting.selection.tag === "fixed") {
4867
+ return selection === undefined ? ok({
4868
+ provider: "mailchimp",
4869
+ audienceId: targeting.audienceId,
4870
+ interestIds: targeting.selection.interestIds
4871
+ }) : err({ tag: "visitor_selection_not_allowed" });
4872
+ }
4873
+ if (selection === undefined) {
4874
+ return targeting.selection.offeredInterestIds.length === 1 ? ok({
4875
+ provider: "mailchimp",
4876
+ audienceId: targeting.audienceId,
4877
+ interestIds: targeting.selection.offeredInterestIds
4878
+ }) : err({ tag: "visitor_selection_required" });
4879
+ }
4880
+ if (selection.provider !== "mailchimp") {
4881
+ return err({ tag: "visitor_selection_provider_mismatch" });
4882
+ }
4883
+ const subset = selectedNonEmptySubset(selection.interestIds, targeting.selection.offeredInterestIds);
4884
+ return subset.ok ? ok({
4885
+ provider: "mailchimp",
4886
+ audienceId: targeting.audienceId,
4887
+ interestIds: subset.value
4888
+ }) : err({ tag: "visitor_selection_outside_offer" });
4889
+ }
4890
+ function selectedNonEmptySubset(selected, offered) {
4891
+ const normalized = [...new Set(selected)];
4892
+ const [first, ...rest] = normalized;
4893
+ if (first === undefined || normalized.some((item) => !offered.includes(item))) {
4894
+ return err("invalid_selection");
4895
+ }
4896
+ return ok([first, ...rest]);
4897
+ }
4898
+ function planMailingListChoicePresentation(targeting) {
4899
+ if (targeting.mode === "default" || targeting.selection.tag === "fixed") {
4900
+ return { tag: "hidden" };
4901
+ }
4902
+ switch (targeting.provider) {
4903
+ case "native": return targeting.selection.offeredTopicIds.length === 1 ? {
4904
+ tag: "automatic",
4905
+ selection: {
4906
+ provider: "native",
4907
+ topicIds: targeting.selection.offeredTopicIds
4908
+ }
4909
+ } : {
4910
+ tag: "visitor_choice",
4911
+ provider: "native",
4912
+ optionCount: targeting.selection.offeredTopicIds.length
4913
+ };
4914
+ case "brevo": return targeting.selection.offeredListIds.length === 1 ? {
4915
+ tag: "automatic",
4916
+ selection: {
4917
+ provider: "brevo",
4918
+ listIds: targeting.selection.offeredListIds
4919
+ }
4920
+ } : {
4921
+ tag: "visitor_choice",
4922
+ provider: "brevo",
4923
+ optionCount: targeting.selection.offeredListIds.length
4924
+ };
4925
+ case "mailchimp": return targeting.selection.offeredInterestIds.length === 1 ? {
4926
+ tag: "automatic",
4927
+ selection: {
4928
+ provider: "mailchimp",
4929
+ interestIds: targeting.selection.offeredInterestIds
4930
+ }
4931
+ } : {
4932
+ tag: "visitor_choice",
4933
+ provider: "mailchimp",
4934
+ optionCount: targeting.selection.offeredInterestIds.length
4935
+ };
4936
+ }
4937
+ }
4938
+
4939
+ //#endregion
4940
+ //#region ../core/src/mailing-list-runtime.ts
4941
+ function parseMailingListRuntimeContext(value) {
4942
+ if (!isRecord$1(value)) return invalidRuntimeContext();
4943
+ if (value.tag === "not_configured") return ok({ tag: "not_configured" });
4944
+ if (value.tag !== "configured") return invalidRuntimeContext();
4945
+ const revision = parseMailingListSetupRevision(value.revision);
4946
+ const form = parseMailingListGlobalForm(value.form);
4947
+ const provider = parseRuntimeProvider(value.provider);
4948
+ if (revision === null || !form.ok || provider === null) {
4949
+ return invalidRuntimeContext();
4950
+ }
4951
+ return ok({
4952
+ tag: "configured",
4953
+ revision,
4954
+ form: form.value,
4955
+ provider
4956
+ });
4957
+ }
4958
+ function parseRuntimeProvider(value) {
4959
+ if (!isRecord$1(value)) return null;
4960
+ switch (value.provider) {
4961
+ case "native": return parseNativeRuntimeProvider(value);
4962
+ case "brevo": return parseBrevoRuntimeProvider(value);
4963
+ case "mailchimp": return parseMailchimpRuntimeProvider(value);
4964
+ default: return null;
4965
+ }
4966
+ }
4967
+ function parseNativeRuntimeProvider(value) {
4968
+ const defaultTopicId = parseNewsletterListId(value.defaultTopicId);
4969
+ const topics = parseNamedItems(value.topics, parseNewsletterListId);
4970
+ return defaultTopicId !== null && topics !== null ? {
4971
+ provider: "native",
4972
+ defaultTopicId,
4973
+ topics
4974
+ } : null;
4975
+ }
4976
+ function parseBrevoRuntimeProvider(value) {
4977
+ const defaultListId = parseBrevoListId(value.defaultListId);
4978
+ const lists = parseNamedItems(value.lists, parseBrevoListId);
4979
+ return defaultListId !== null && lists !== null ? {
4980
+ provider: "brevo",
4981
+ defaultListId,
4982
+ lists
4983
+ } : null;
4984
+ }
4985
+ function parseMailchimpRuntimeProvider(value) {
4986
+ if (!Array.isArray(value.audiences)) return null;
4987
+ const defaultAudienceId = parseMailchimpAudienceId(value.defaultAudienceId);
4988
+ if (defaultAudienceId === null) return null;
4989
+ const audiences = [];
4990
+ for (const audience of value.audiences) {
4991
+ if (!isRecord$1(audience)) return null;
4992
+ const id = parseMailchimpAudienceId(audience.id);
4993
+ const name = parseName(audience.name);
4994
+ if (id === null || name === null || !Array.isArray(audience.interests)) {
4995
+ return null;
4996
+ }
4997
+ const interests = parseMailchimpInterests(audience.interests);
4998
+ if (interests === null) return null;
4999
+ audiences.push({
5000
+ id,
5001
+ name,
5002
+ interests
5003
+ });
5004
+ }
5005
+ return {
5006
+ provider: "mailchimp",
5007
+ defaultAudienceId,
5008
+ audiences
5009
+ };
5010
+ }
5011
+ function parseMailchimpInterests(value) {
5012
+ const interests = [];
5013
+ for (const interest of value) {
5014
+ if (!isRecord$1(interest)) return null;
5015
+ const id = parseMailchimpInterestId(interest.id);
5016
+ const name = parseName(interest.name);
5017
+ const categoryName = parseName(interest.categoryName);
5018
+ if (id === null || name === null || categoryName === null) return null;
5019
+ interests.push({
5020
+ id,
5021
+ name,
5022
+ categoryName
5023
+ });
5024
+ }
5025
+ return interests;
5026
+ }
5027
+ function parseNamedItems(value, parseId) {
5028
+ if (!Array.isArray(value)) return null;
5029
+ const items = [];
5030
+ for (const item of value) {
5031
+ if (!isRecord$1(item)) return null;
5032
+ const id = parseId(item.id);
5033
+ const name = parseName(item.name);
5034
+ if (id === null || name === null) return null;
5035
+ items.push({
5036
+ id,
5037
+ name
5038
+ });
5039
+ }
5040
+ return items;
5041
+ }
5042
+ function parseName(value) {
5043
+ if (typeof value !== "string") return null;
5044
+ const name = value.trim();
5045
+ return name.length > 0 ? name : null;
5046
+ }
5047
+ function isRecord$1(value) {
5048
+ return typeof value === "object" && value !== null && !Array.isArray(value);
5049
+ }
5050
+ function invalidRuntimeContext() {
5051
+ return err({ tag: "invalid_mailing_list_runtime_context" });
5052
+ }
5053
+
5054
+ //#endregion
5055
+ //#region ../core/src/mailing-list-submission.ts
5056
+ const MAILING_LIST_SUBMISSION_BUDGET_MS = 1e4;
5057
+ function parseMailingListBlockId(value) {
5058
+ const parsed = typeof value === "string" ? value.trim() : "";
5059
+ 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;
5060
+ }
5061
+ function parseMailingListSignedSourceToken(value) {
5062
+ const parsed = typeof value === "string" ? value.trim() : "";
5063
+ return /^[A-Za-z0-9._~-]{16,1000}$/u.test(parsed) ? parsed : null;
5064
+ }
5065
+ function parseMailingListSdkFooterBlockId(value) {
5066
+ const parsed = typeof value === "string" ? value.trim() : "";
5067
+ return /^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$/u.test(parsed) ? parsed : null;
5068
+ }
5069
+ function parseMailingListSdkContentHash(value) {
5070
+ const parsed = typeof value === "string" ? value.trim() : "";
5071
+ return /^[a-f0-9]{16}$/iu.test(parsed) ? parsed : null;
5072
+ }
5073
+ function parseMailingListContentVersionId(value) {
5074
+ const parsed = parseContentVersionId(value);
5075
+ return parsed.ok ? parsed.value : null;
5076
+ }
5077
+ function parseMailingListFooterRevision(value) {
5078
+ return typeof value === "number" && Number.isSafeInteger(value) && value >= 0 ? value : null;
5079
+ }
5080
+ function buildMailingListSignupBlockSourceRef(blockId) {
5081
+ const parsed = parseMailingListBlockId(blockId);
5082
+ return parsed !== null ? {
5083
+ tag: "block_ref",
5084
+ blockId: parsed
5085
+ } : null;
5086
+ }
5087
+ function parseMailingListSignupProfile(form, input) {
5088
+ const email = normalizeSubscriberEmail(input.email);
5089
+ if (email === null) return {
5090
+ ok: false,
5091
+ error: { tag: "invalid_email" }
5092
+ };
5093
+ const phone = form.includeOptionalPhone ? {
5094
+ tag: "collected",
5095
+ value: normalizeOptionalValue(input.phone)
5096
+ } : { tag: "not_collected" };
5097
+ switch (form.fields.preset) {
5098
+ case "emailOnly": return {
5099
+ ok: true,
5100
+ value: {
5101
+ preset: "emailOnly",
5102
+ email,
5103
+ phone
5104
+ }
5105
+ };
5106
+ case "emailName": {
5107
+ const name = resolveProfileField("name", form.fields.name, input.name);
5108
+ return name.ok ? {
5109
+ ok: true,
5110
+ value: {
5111
+ preset: "emailName",
5112
+ email,
5113
+ phone,
5114
+ name: name.value
5115
+ }
5116
+ } : name;
5117
+ }
5118
+ case "emailFirstLast": {
5119
+ const firstName = resolveProfileField("first_name", form.fields.firstName, input.firstName);
5120
+ if (!firstName.ok) return firstName;
5121
+ const lastName = resolveProfileField("last_name", form.fields.lastName, input.lastName);
5122
+ return lastName.ok ? {
5123
+ ok: true,
5124
+ value: {
5125
+ preset: "emailFirstLast",
5126
+ email,
5127
+ phone,
5128
+ firstName: firstName.value,
5129
+ lastName: lastName.value
5130
+ }
5131
+ } : lastName;
5132
+ }
5133
+ case "emailNameCompany": {
5134
+ const name = resolveProfileField("name", form.fields.name, input.name);
5135
+ if (!name.ok) return name;
5136
+ const company = resolveProfileField("company", form.fields.company, input.company);
5137
+ return company.ok ? {
5138
+ ok: true,
5139
+ value: {
5140
+ preset: "emailNameCompany",
5141
+ email,
5142
+ phone,
5143
+ name: name.value,
5144
+ company: company.value
5145
+ }
5146
+ } : company;
5147
+ }
5148
+ }
5149
+ }
5150
+ function resolveProfileField(field, requirement, input) {
5151
+ const value = normalizeOptionalValue(input);
5152
+ if (requirement === "required") {
5153
+ return value === null ? {
5154
+ ok: false,
5155
+ error: {
5156
+ tag: "required_field_missing",
5157
+ field
5158
+ }
5159
+ } : {
5160
+ ok: true,
5161
+ value: {
5162
+ requirement: "required",
5163
+ value
5164
+ }
5165
+ };
5166
+ }
5167
+ return {
5168
+ ok: true,
5169
+ value: {
5170
+ requirement: "optional",
5171
+ value
5172
+ }
5173
+ };
5174
+ }
5175
+ function normalizeSubscriberEmail(value) {
5176
+ const normalized = value.trim().toLowerCase();
5177
+ const at = normalized.indexOf("@");
5178
+ return normalized.length <= 320 && at > 0 && at === normalized.lastIndexOf("@") && at < normalized.length - 1 ? normalized : null;
5179
+ }
5180
+ function normalizeOptionalValue(value) {
5181
+ const normalized = value?.trim() ?? "";
5182
+ return normalized.length > 0 ? normalized : null;
5183
+ }
5184
+ function toMailingListPublicSignupResponse(result) {
5185
+ if (result.ok) {
5186
+ return result.acknowledgement.tag === "subscribed" ? { tag: "subscribed" } : { tag: "confirmation_required" };
5187
+ }
5188
+ if (result.error.tag === "profile_rejected") {
5189
+ return {
5190
+ tag: "invalid_input",
5191
+ reason: "profile_rejected"
5192
+ };
5193
+ }
5194
+ return result.error.tag === "uncertain" ? {
5195
+ tag: "uncertain",
5196
+ reason: result.error.reason
5197
+ } : {
5198
+ tag: "unavailable",
5199
+ reason: result.error.tag
5200
+ };
5201
+ }
5202
+
4366
5203
  //#endregion
4367
5204
  //#region ../core/src/number-input.ts
4368
5205
  /**
@@ -5274,6 +6111,63 @@ function resolveWholeSeriesBookingPhase(run, now) {
5274
6111
  }
5275
6112
  }
5276
6113
 
6114
+ //#endregion
6115
+ //#region ../core/src/public-read-cache-contract.ts
6116
+ const PUBLIC_AVAILABILITY_FRESHNESS_SECONDS = 60;
6117
+ const PUBLIC_CONTENT_FRESHNESS_SECONDS = 3600;
6118
+ function availability(transport) {
6119
+ return {
6120
+ kind: "live",
6121
+ reason: "booking_correctness",
6122
+ freshness: "availability",
6123
+ transport
6124
+ };
6125
+ }
6126
+ function content(tags, transport = "direct") {
6127
+ return {
6128
+ kind: "tagged",
6129
+ tags,
6130
+ freshness: "content",
6131
+ transport
6132
+ };
6133
+ }
6134
+ /** The single classification authority for cacheable public reads. */
6135
+ const PUBLIC_READ_CACHE_POLICIES = {
6136
+ listPublishedEntries: content(["published_content"]),
6137
+ getPublishedEntryPreview: content(["published_content"]),
6138
+ listPublicEvents: availability("direct"),
6139
+ listPublicEventCalendarWindow: availability("direct"),
6140
+ listPublicRecurringSchedule: availability("batch_only"),
6141
+ getPublicEventDetails: availability("batch_only"),
6142
+ resolveEventSubroute: availability("direct"),
6143
+ listPublicRouteCandidates: content(["published_content"], "batch_only"),
6144
+ listPublicTeamMembers: content(["public_team_members"]),
6145
+ listPublicProducts: availability("direct"),
6146
+ listPublicPasses: availability("direct"),
6147
+ listPublicMemberships: availability("direct"),
6148
+ getPublicFormById: content(["site_forms"]),
6149
+ getPublicBookingServices: availability("direct"),
6150
+ getPublicBookingService: availability("direct"),
6151
+ getPublicBookingResourcesReference: availability("direct"),
6152
+ getPublicBookingAvailableDates: availability("direct"),
6153
+ getPublicBookingAvailableSlots: availability("direct"),
6154
+ getPublicWeeklySlotAvailability: availability("direct"),
6155
+ listPublicForms: content(["site_forms"]),
6156
+ checkRedirect: content([]),
6157
+ getPublicRoutableContent: content(["published_content"]),
6158
+ getPublicSitemapRoutes: content(["published_content"])
6159
+ };
6160
+ const PUBLIC_READ_ENDPOINT_SET = new Set(Object.keys(PUBLIC_READ_CACHE_POLICIES));
6161
+ function isPublicReadEndpoint(endpoint) {
6162
+ return PUBLIC_READ_ENDPOINT_SET.has(endpoint);
6163
+ }
6164
+ function getPublicReadCachePolicy(endpoint) {
6165
+ return PUBLIC_READ_CACHE_POLICIES[endpoint];
6166
+ }
6167
+ function publicReadFreshnessSeconds(endpoint) {
6168
+ return getPublicReadCachePolicy(endpoint).freshness === "availability" ? PUBLIC_AVAILABILITY_FRESHNESS_SECONDS : PUBLIC_CONTENT_FRESHNESS_SECONDS;
6169
+ }
6170
+
5277
6171
  //#endregion
5278
6172
  //#region ../core/src/event-series-behaviour.ts
5279
6173
  const EVENT_PRESET_VALUES = [
@@ -5357,13 +6251,13 @@ const BOOKING_DEFAULTS_BY_BEHAVIOUR = {
5357
6251
  memberships: "off"
5358
6252
  }
5359
6253
  };
5360
- function verticalBookingPreset(id, verticalId, canonicalBehaviour, label, pluralLabel, description, availability = "onboarding") {
6254
+ function verticalBookingPreset(id, verticalId, canonicalBehaviour, label, pluralLabel, description, availability$1 = "onboarding") {
5361
6255
  return {
5362
6256
  id,
5363
6257
  verticalId,
5364
6258
  canonicalBehaviour,
5365
6259
  engine: BOOKING_ENGINE_BY_BEHAVIOUR[canonicalBehaviour],
5366
- availability,
6260
+ availability: availability$1,
5367
6261
  label,
5368
6262
  pluralLabel,
5369
6263
  description
@@ -5717,12 +6611,12 @@ const VERTICAL_PRESET_AVAILABILITIES = [
5717
6611
  "future"
5718
6612
  ];
5719
6613
  function getVerticalPresetsForVertical(verticalId, options) {
5720
- const availability = options?.availability;
6614
+ const availability$1 = options?.availability;
5721
6615
  return VERTICAL_BOOKING_PRESETS.filter((presetDefinition) => {
5722
6616
  if (presetDefinition.verticalId !== verticalId) return false;
5723
- if (!availability) return true;
5724
- if (availability === "selectable") return presetDefinition.availability !== "future";
5725
- return presetDefinition.availability === availability;
6617
+ if (!availability$1) return true;
6618
+ if (availability$1 === "selectable") return presetDefinition.availability !== "future";
6619
+ return presetDefinition.availability === availability$1;
5726
6620
  });
5727
6621
  }
5728
6622
  function getOnboardingPresetsForVertical(verticalId) {
@@ -5946,4 +6840,62 @@ function orderPresetIdsForVertical(verticalId, presetIds) {
5946
6840
  }
5947
6841
 
5948
6842
  //#endregion
5949
- export { fromArray as $, normalizeLocationAddressText as A, unsafeAsStorageBucketName as At, getLocalScheduleTime as B, parseMailchimpTagName as C, unsafeAsPositiveSeconds as Ct, isCanonicalRichTextDoc as D, unsafeAsSiteId as Dt, parseNewsletterSignupDestinationProvider as E, unsafeAsSiteFontId as Et, formatBookingTime as F, ok as Ft, parseBookingTimeZone as G, toBookingTimeZone as H, formatLocalScheduleDate as I, formatEventPricing as J, parseEventPageSurfaceSubject as K, formatLocalScheduleTime as L, buildBookingUtcDateWindow as M, unsafeAsUploadSessionId as Mt, formatBookingDate as N, unsafeAsUserId as Nt, assertAllowedPublicUrl as O, unsafeAsSiteScopedStoragePath as Ot, formatBookingDateParts as P, err as Pt, publicEventPricingType as Q, getBookingLocalDateKey as R, parseMailchimpAudienceId as S, unsafeAsPositiveByteCount as St, parseNewsletterSignupAuthoringDestinationValue as T, unsafeAsSafeDownloadFilename as Tt, toScheduledBookingDateRange as U, parseUtcIsoDateTimeString as V, toScheduledBookingDateTime as W, publicEventPricingCurrency as X, isPublicEventPricingOptionDisplayable as Y, publicEventPricingDisplay as Z, parseManagementKey as _, unsafeAsEventSeriesId as _t, parseVerticalPresetId as a, parseMediaAssetId as at, parseFooterCompositionV1 as b, unsafeAsOccurrenceId as bt, EVENT_PRESET_VALUES as c, parseUserId as ct, WHOLE_SERIES_EVENT_PRESET_VALUES as d, unsafeAsBookingSessionId as dt, mapNonEmptyArray as et, CANCELLED_SESSION_STATUS as f, unsafeAsDeclaredMimeType as ft, exposeSecret as g, unsafeAsEventOfferingId as gt, createSecret as h, unsafeAsEntitlementId as ht, parseVerticalId as i, parseEventSeriesId as it, toLocationCoordinates as j, unsafeAsStoragePath as jt, isPresent as k, unsafeAsStaffMemberId as kt, ORDINARY_EVENT_PRESET_VALUES as l, unsafeAsAppointmentResourceId as lt, resolveWholeSeriesBookingPhase as m, unsafeAsDetectedMimeType as mt, canonicalizeConfiguredBookingVerticalConfig as n, assertNever as nt, resolveSiteVerticalBookingConfig as o, parsePageId as ot, orderedLiveRunSessions as p, unsafeAsDemoCapability as pt, formatBookingPriceDisplay as q, isEventSeriesVerticalPresetDefinition as r, parseApiKeyId as rt, DEFAULT_EVENT_TYPE_PRESETS as s, parseSiteId as st, areConfiguredBookingVerticalConfigsEqual as t, formatCurrencyAmount as tt, SURFACE_EVENT_PRESET_VALUES as u, unsafeAsBookingRequirementId as ut, stableJsonStringify as v, unsafeAsFileChecksumSha256 as vt, parseNewsletterListId as w, unsafeAsProductId as wt, parseFooterModuleId as x, unsafeAsOriginalFilename as xt, footerNavigationLayouts as y, unsafeAsMediaAssetId as yt, getLocalScheduleDate as z };
6843
+ //#region ../core/src/navigation-locations.ts
6844
+ /** The only places a site's navigation can be displayed. */
6845
+ const NAVIGATION_LOCATIONS = [
6846
+ "main",
6847
+ "footer",
6848
+ "legal"
6849
+ ];
6850
+ const NAVIGATION_LOCATION_DESCRIPTORS = {
6851
+ main: {
6852
+ location: "main",
6853
+ label: "Main navigation",
6854
+ description: "Links shown in the site header when header navigation is enabled."
6855
+ },
6856
+ footer: {
6857
+ location: "footer",
6858
+ label: "Footer links",
6859
+ description: "Links shown in the main footer links section when it is enabled."
6860
+ },
6861
+ legal: {
6862
+ location: "legal",
6863
+ label: "Legal links",
6864
+ description: "Smaller footer links for privacy, terms and other policies."
6865
+ }
6866
+ };
6867
+ function isNavigationLocation(value) {
6868
+ return typeof value === "string" && NAVIGATION_LOCATIONS.includes(value);
6869
+ }
6870
+ /** Parse all rows for one site into a complete fixed-location record. */
6871
+ function parseSiteNavigationLocations(rows) {
6872
+ const locations = {};
6873
+ for (const [index, row] of rows.entries()) {
6874
+ if (!isNavigationLocation(row.location)) {
6875
+ return err({
6876
+ kind: "invalid_location",
6877
+ index,
6878
+ value: row.location
6879
+ });
6880
+ }
6881
+ if (Object.prototype.hasOwnProperty.call(locations, row.location)) {
6882
+ return err({
6883
+ kind: "duplicate_location",
6884
+ location: row.location
6885
+ });
6886
+ }
6887
+ locations[row.location] = row.value;
6888
+ }
6889
+ for (const location of NAVIGATION_LOCATIONS) {
6890
+ if (!Object.prototype.hasOwnProperty.call(locations, location)) {
6891
+ return err({
6892
+ kind: "missing_location",
6893
+ location
6894
+ });
6895
+ }
6896
+ }
6897
+ return ok(locations);
6898
+ }
6899
+
6900
+ //#endregion
6901
+ export { publicEventPricingDisplay as $, assertAllowedPublicUrl as A, unsafeAsSiteFontId as At, getBookingLocalDateKey as B, parseMailingListSignedSourceToken as C, unsafeAsMediaAssetId as Ct, parseFooterCompositionV1 as D, unsafeAsPositiveSeconds as Dt, footerNavigationLayouts as E, unsafeAsPositiveByteCount as Et, formatBookingDate as F, unsafeAsStoragePath as Ft, toScheduledBookingDateRange as G, getLocalScheduleTime as H, formatBookingDateParts as I, unsafeAsUploadSessionId as It, parseEventPageSurfaceSubject as J, toScheduledBookingDateTime as K, formatBookingTime as L, unsafeAsUserId as Lt, normalizeLocationAddressText as M, unsafeAsSiteScopedStoragePath as Mt, toLocationCoordinates as N, unsafeAsStaffMemberId as Nt, parseMailingListPlacementTargeting as O, unsafeAsProductId as Ot, buildBookingUtcDateWindow as P, unsafeAsStorageBucketName as Pt, publicEventPricingCurrency as Q, formatLocalScheduleDate as R, err as Rt, parseMailingListBlockId as S, unsafeAsFileChecksumSha256 as St, planMailingListChoicePresentation as T, unsafeAsOriginalFilename as Tt, parseUtcIsoDateTimeString as U, getLocalScheduleDate as V, toBookingTimeZone as W, formatEventPricing as X, formatBookingPriceDisplay as Y, isPublicEventPricingOptionDisplayable as Z, createSecret as _, unsafeAsDemoCapability as _t, parseVerticalId as a, formatCurrencyAmount as at, stableJsonStringify as b, unsafeAsEventOfferingId as bt, DEFAULT_EVENT_TYPE_PRESETS as c, parseEventSeriesId as ct, SURFACE_EVENT_PRESET_VALUES as d, parseSiteId as dt, publicEventPricingType as et, WHOLE_SERIES_EVENT_PRESET_VALUES as f, parseUserId as ft, resolveWholeSeriesBookingPhase as g, unsafeAsDeclaredMimeType as gt, orderedLiveRunSessions as h, unsafeAsBookingSessionId as ht, isEventSeriesVerticalPresetDefinition as i, mapNonEmptyArray as it, isPresent as j, unsafeAsSiteId as jt, isCanonicalRichTextDoc as k, unsafeAsSafeDownloadFilename as kt, EVENT_PRESET_VALUES as l, parseMediaAssetId as lt, CANCELLED_SESSION_STATUS as m, unsafeAsBookingRequirementId as mt, areConfiguredBookingVerticalConfigsEqual as n, resolvePricingOptionsPriceDisplay as nt, parseVerticalPresetId as o, assertNever as ot, publicReadFreshnessSeconds as p, unsafeAsAppointmentResourceId as pt, parseBookingTimeZone as q, canonicalizeConfiguredBookingVerticalConfig as r, fromArray as rt, resolveSiteVerticalBookingConfig as s, parseApiKeyId as st, NAVIGATION_LOCATIONS as t, resolveExactOrFreePriceDisplay as tt, ORDINARY_EVENT_PRESET_VALUES as u, parsePageId as ut, exposeSecret as v, unsafeAsDetectedMimeType as vt, parseMailingListRuntimeContext as w, unsafeAsOccurrenceId as wt, buildMailingListSignupBlockSourceRef as x, unsafeAsEventSeriesId as xt, parseManagementKey as y, unsafeAsEntitlementId as yt, formatLocalScheduleTime as z, ok as zt };