riverbankcms 0.7.0 → 0.7.1

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.
@@ -3,7 +3,7 @@ import { existsSync, readFileSync } from "node:fs";
3
3
  import { dirname, join, parse } from "node:path";
4
4
 
5
5
  //#region package.json
6
- var version = "0.7.0";
6
+ var version = "0.7.1";
7
7
  var peerDependencies = { "@riverbankcms/sdk": ">=0.139.0 <1.0.0" };
8
8
  var package_default = {
9
9
  name: "riverbankcms",
@@ -79,7 +79,7 @@ var package_default = {
79
79
  //#endregion
80
80
  //#region src/version.ts
81
81
  /** The package manifest is the source-run fallback; builds inject this value. */
82
- const CLI_VERSION = typeof "0.7.0" === "string" ? "0.7.0" : version;
82
+ const CLI_VERSION = typeof "0.7.1" === "string" ? "0.7.1" : version;
83
83
  /**
84
84
  * The CLI's SDK peer range is bundled from the CLI manifest alongside its
85
85
  * version. Runtime config loading uses this value as the single compatibility
@@ -79,7 +79,6 @@ const unsafeAsBookingSessionId = (id) => id;
79
79
  const unsafeAsDatedOfferingCapacityHoldId = (value) => value;
80
80
  const unsafeAsDatedOfferingCapacityHoldAuditRef = (value) => value;
81
81
  const unsafeAsDatedOfferingRevocationAuditEventId = (value) => value;
82
- const unsafeAsDatedOfferingNonCashProjectionClaimToken = (value) => value;
83
82
  const unsafeAsDatedOfferingWaitlistCapacityEffectId = (value) => value;
84
83
  const unsafeAsDatedOfferingWaitlistCapacityEffectClaimToken = (value) => value;
85
84
  const unsafeAsDatedOfferingRunCancellationOperationId = (value) => value;
@@ -103,7 +102,6 @@ const unsafeAsCheckoutLineId = (id) => id;
103
102
  const unsafeAsAttendeeId = (id) => id;
104
103
  const unsafeAsEventRegistrationCommandId = (id) => id;
105
104
  const unsafeAsEventRegistrationCommandRowId = (id) => id;
106
- const unsafeAsEventRegistrationProjectionClaimToken = (id) => id;
107
105
  const unsafeAsCustomerPassId = (id) => id;
108
106
  const unsafeAsCustomerMembershipId = (id) => id;
109
107
  const unsafeAsAppointmentId = (id) => id;
@@ -149,7 +147,6 @@ const unsafeAsParticipantId = (id) => id;
149
147
  const unsafeAsParticipantParticipationId = (id) => id;
150
148
  const unsafeAsParticipantRosterAssertionJobId = (id) => id;
151
149
  const unsafeAsParticipantBookingQuestionsTaskId = (id) => id;
152
- const unsafeAsEventAttendeeGuestId = (id) => id;
153
150
  const unsafeAsPersonRecordId = (id) => id;
154
151
  const unsafeAsEncounterRecordId = (id) => id;
155
152
  const unsafeAsRecordEntryId = (id) => id;
@@ -186,11 +183,9 @@ const parseAppointmentReservationRunId = (value) => parseUuidBrand(value, unsafe
186
183
  const parseAttendeeId = (value) => parseUuidBrand(value, unsafeAsAttendeeId);
187
184
  const parseEventRegistrationCommandId = (value) => parseUuidBrand(value, unsafeAsEventRegistrationCommandId);
188
185
  const parseEventRegistrationCommandRowId = (value) => parseUuidBrand(value, unsafeAsEventRegistrationCommandRowId);
189
- const parseEventRegistrationProjectionClaimToken = (value) => parseUuidBrand(value, unsafeAsEventRegistrationProjectionClaimToken);
190
186
  const parseDatedOfferingCapacityHoldId = (value) => parseUuidBrand(value, unsafeAsDatedOfferingCapacityHoldId);
191
187
  const parseDatedOfferingCapacityHoldAuditRef = (value) => parseUuidBrand(value, unsafeAsDatedOfferingCapacityHoldAuditRef);
192
188
  const parseDatedOfferingRevocationAuditEventId = (value) => parseUuidBrand(value, unsafeAsDatedOfferingRevocationAuditEventId);
193
- const parseDatedOfferingNonCashProjectionClaimToken = (value) => parseUuidBrand(value, unsafeAsDatedOfferingNonCashProjectionClaimToken);
194
189
  const parseDatedOfferingWaitlistCapacityEffectId = (value) => parseUuidBrand(value, unsafeAsDatedOfferingWaitlistCapacityEffectId);
195
190
  const parseDatedOfferingWaitlistCapacityEffectClaimToken = (value) => parseUuidBrand(value, unsafeAsDatedOfferingWaitlistCapacityEffectClaimToken);
196
191
  const parseDatedOfferingRunCancellationOperationId = (value) => parseUuidBrand(value, unsafeAsDatedOfferingRunCancellationOperationId);
@@ -1303,7 +1298,7 @@ function parseEventPageSurfaceDestinationMode(value) {
1303
1298
  return value === "cms-managed" || value === "system-managed" ? value : undefined;
1304
1299
  }
1305
1300
  function parseEventPageSurfaceSubject(value) {
1306
- if (!isRecord$8(value)) return err({ kind: "notObject" });
1301
+ if (!isRecord$9(value)) return err({ kind: "notObject" });
1307
1302
  switch (value.kind) {
1308
1303
  case "eventSeries": {
1309
1304
  const eventSeriesId = parseEventSeriesId(value.eventSeriesId);
@@ -1373,7 +1368,7 @@ function parseEventSeriesIdList(values) {
1373
1368
  }
1374
1369
  return ok(parsed);
1375
1370
  }
1376
- function isRecord$8(value) {
1371
+ function isRecord$9(value) {
1377
1372
  return typeof value === "object" && value !== null && !Array.isArray(value);
1378
1373
  }
1379
1374
 
@@ -2575,41 +2570,14 @@ function parseParticipantCount(value) {
2575
2570
  }
2576
2571
  return ok(unsafeAsParticipantCount(value));
2577
2572
  }
2578
- function provisionalEventGuestParticipantIdentity(input) {
2579
- return {
2580
- kind: "event_guest",
2581
- eventAttendeeGuestId: input.eventAttendeeGuestId,
2582
- slotKey: input.slotKey
2583
- };
2584
- }
2585
- const provisionalParticipantIdentityEncoders = { event_guest: (identity) => ({
2586
- kind: identity.kind,
2587
- source: "event_guest_backfill",
2588
- eventAttendeeGuestId: identity.eventAttendeeGuestId,
2589
- slotKey: identity.slotKey
2590
- }) };
2591
- function encodeProvisionalParticipantIdentity(identity) {
2592
- return provisionalParticipantIdentityEncoders[identity.kind](identity);
2593
- }
2594
- function parseProvisionalParticipantIdentity(value) {
2595
- if (typeof value !== "object" || value === null || Array.isArray(value)) {
2596
- return err({ kind: "not_an_object" });
2597
- }
2598
- const metadata = value;
2599
- const kind = metadata.kind ?? metadata.source;
2600
- if (kind !== "event_guest" && kind !== "event_guest_backfill") {
2601
- return err({ kind: "unsupported_identity_kind" });
2602
- }
2603
- if (typeof metadata.eventAttendeeGuestId !== "string") {
2604
- return err({ kind: "missing_event_attendee_guest_id" });
2605
- }
2606
- if (typeof metadata.slotKey !== "string") {
2607
- return err({ kind: "missing_slot_key" });
2608
- }
2609
- return ok(provisionalEventGuestParticipantIdentity({
2610
- eventAttendeeGuestId: unsafeAsEventAttendeeGuestId(metadata.eventAttendeeGuestId),
2611
- slotKey: unsafeAsParticipantSlotKey(metadata.slotKey)
2612
- }));
2573
+ /**
2574
+ * The in-memory planning key for one guest place on an occurrence registration.
2575
+ *
2576
+ * Persistence uses the participation row and its explicit guest ordinal. This
2577
+ * key only lets pure roster planning correlate issues and resolutions.
2578
+ */
2579
+ function eventGuestRosterSlotKey(input) {
2580
+ return unsafeAsParticipantSlotKey(`event_guest:${input.attendeeId}:${input.guestIndex}`);
2613
2581
  }
2614
2582
  function deriveParticipantDisplayLabel(input) {
2615
2583
  const displayName = trimmedStringOrNull(input.displayName);
@@ -2735,12 +2703,15 @@ function projectEventRegistrationParticipantRoster(input) {
2735
2703
  }
2736
2704
  });
2737
2705
  const guestSlots = input.guests.map((guest) => buildSlot({
2738
- slotKey: unsafeAsParticipantSlotKey(`event_guest:${guest.guestId}`),
2706
+ slotKey: eventGuestRosterSlotKey({
2707
+ attendeeId: input.attendeeId,
2708
+ guestIndex: guest.guestIndex
2709
+ }),
2739
2710
  contact: guest,
2740
2711
  participation: {
2741
2712
  kind: "event_guest",
2742
2713
  attendeeId: input.attendeeId,
2743
- guestId: guest.guestId
2714
+ guestIndex: guest.guestIndex
2744
2715
  }
2745
2716
  }));
2746
2717
  const slots = [bookerSlot, ...guestSlots];
@@ -3239,7 +3210,7 @@ const knownSocialPlatforms = [
3239
3210
  "x",
3240
3211
  "youtube"
3241
3212
  ];
3242
- function isRecord$7(value) {
3213
+ function isRecord$8(value) {
3243
3214
  return typeof value === "object" && value !== null && !Array.isArray(value);
3244
3215
  }
3245
3216
  function trimmedOptionalString(value) {
@@ -3249,7 +3220,7 @@ function trimmedOptionalString(value) {
3249
3220
  return trimmed.length > 0 ? trimmed : null;
3250
3221
  }
3251
3222
  function parsePublicContactDetails(value) {
3252
- if (!isRecord$7(value)) {
3223
+ if (!isRecord$8(value)) {
3253
3224
  return err({
3254
3225
  code: "invalid_shape",
3255
3226
  path: "contact",
@@ -3345,7 +3316,7 @@ function minutesSinceMidnight(value) {
3345
3316
  }
3346
3317
  function parseOpeningHoursDay(value, expectedWeekday, index) {
3347
3318
  const path = `openingHours.days.${index}`;
3348
- if (!isRecord$7(value) || value.weekday !== expectedWeekday) {
3319
+ if (!isRecord$8(value) || value.weekday !== expectedWeekday) {
3349
3320
  return err({
3350
3321
  code: "invalid_weekdays",
3351
3322
  path,
@@ -3366,7 +3337,7 @@ function parseOpeningHoursDay(value, expectedWeekday, index) {
3366
3337
  const intervals = [];
3367
3338
  for (const [intervalIndex, rawInterval] of value.intervals.entries()) {
3368
3339
  const intervalPath = `${path}.intervals.${intervalIndex}`;
3369
- if (!isRecord$7(rawInterval)) {
3340
+ if (!isRecord$8(rawInterval)) {
3370
3341
  return err({
3371
3342
  code: "invalid_interval",
3372
3343
  path: intervalPath,
@@ -3416,7 +3387,7 @@ function parseOpeningHoursDay(value, expectedWeekday, index) {
3416
3387
  });
3417
3388
  }
3418
3389
  function parseWeeklyOpeningHours(value) {
3419
- if (!isRecord$7(value) || !Array.isArray(value.days) || value.days.length !== publicProfileWeekdays.length) {
3390
+ if (!isRecord$8(value) || !Array.isArray(value.days) || value.days.length !== publicProfileWeekdays.length) {
3420
3391
  return err({
3421
3392
  code: "invalid_weekdays",
3422
3393
  path: "openingHours.days",
@@ -3496,7 +3467,7 @@ function parsePublicSocialLinks(value) {
3496
3467
  const ids = new Set();
3497
3468
  for (const [index, rawLink] of value.entries()) {
3498
3469
  const path = `socialLinks.${index}`;
3499
- if (!isRecord$7(rawLink)) {
3470
+ if (!isRecord$8(rawLink)) {
3500
3471
  return err({
3501
3472
  code: "invalid_social_link",
3502
3473
  path,
@@ -3543,7 +3514,7 @@ function parsePublicSocialLinks(value) {
3543
3514
  return ok(links);
3544
3515
  }
3545
3516
  function parsePublicProfile(value) {
3546
- if (!isRecord$7(value)) {
3517
+ if (!isRecord$8(value)) {
3547
3518
  return err({
3548
3519
  code: "invalid_shape",
3549
3520
  path: "",
@@ -3558,13 +3529,67 @@ function parsePublicProfile(value) {
3558
3529
  if (!socialLinks.ok) return socialLinks;
3559
3530
  const addressVisibility = parseAddressVisibility(value.addressVisibility);
3560
3531
  if (!addressVisibility.ok) return addressVisibility;
3532
+ const contactVisibility = parseContactVisibility(value.contactVisibility);
3533
+ if (!contactVisibility.ok) return contactVisibility;
3534
+ const premises = parsePremises(value.premises);
3535
+ if (!premises.ok) return premises;
3561
3536
  return ok({
3562
3537
  contact: contact.value,
3538
+ contactVisibility: contactVisibility.value,
3563
3539
  openingHours: openingHours.value,
3564
3540
  socialLinks: socialLinks.value,
3565
- addressVisibility: addressVisibility.value
3541
+ addressVisibility: addressVisibility.value,
3542
+ premises: premises.value
3543
+ });
3544
+ }
3545
+ function parseFieldVisibility(value, path) {
3546
+ if (value === undefined || value === null) return ok("shown");
3547
+ if (value === "shown" || value === "hidden") return ok(value);
3548
+ return err({
3549
+ code: "invalid_contact_visibility",
3550
+ path,
3551
+ message: "Contact visibility must be shown or hidden."
3566
3552
  });
3567
3553
  }
3554
+ function parseContactVisibility(value) {
3555
+ if (value === undefined || value === null) {
3556
+ return ok({
3557
+ phone: "shown",
3558
+ email: "shown"
3559
+ });
3560
+ }
3561
+ if (!isRecord$8(value)) {
3562
+ return err({
3563
+ code: "invalid_contact_visibility",
3564
+ path: "contactVisibility",
3565
+ message: "Contact visibility must be an object."
3566
+ });
3567
+ }
3568
+ const phone = parseFieldVisibility(value.phone, "contactVisibility.phone");
3569
+ if (!phone.ok) return phone;
3570
+ const email = parseFieldVisibility(value.email, "contactVisibility.email");
3571
+ if (!email.ok) return email;
3572
+ return ok({
3573
+ phone: phone.value,
3574
+ email: email.value
3575
+ });
3576
+ }
3577
+ function parsePremises(value) {
3578
+ if (value === undefined || value === null) return ok(null);
3579
+ if (typeof value === "boolean") return ok(value);
3580
+ return err({
3581
+ code: "invalid_premises",
3582
+ path: "premises",
3583
+ message: "Premises must be true or false."
3584
+ });
3585
+ }
3586
+ /**
3587
+ * Resolves whether a site has premises, falling back to whether it already
3588
+ * holds a business address for every profile saved before the switch existed.
3589
+ */
3590
+ function resolvePublicProfilePremises(profile, hasBusinessAddress) {
3591
+ return profile.premises ?? hasBusinessAddress;
3592
+ }
3568
3593
  function parseAddressVisibility(value) {
3569
3594
  if (value === undefined || value === null) return ok("shown");
3570
3595
  if (value === "shown" || value === "hidden") return ok(value);
@@ -3577,15 +3602,30 @@ function parseAddressVisibility(value) {
3577
3602
  function emptyPublicProfile() {
3578
3603
  return {
3579
3604
  contact: null,
3605
+ contactVisibility: {
3606
+ phone: "shown",
3607
+ email: "shown"
3608
+ },
3580
3609
  openingHours: null,
3581
3610
  socialLinks: [],
3582
- addressVisibility: "shown"
3611
+ addressVisibility: "shown",
3612
+ premises: null
3583
3613
  };
3584
3614
  }
3585
3615
 
3616
+ //#endregion
3617
+ //#region ../core/src/public-experience.ts
3618
+ const PUBLIC_EXPERIENCES = ["website", "hosted_booking"];
3619
+ function isPublicExperience(value) {
3620
+ return typeof value === "string" && PUBLIC_EXPERIENCES.includes(value);
3621
+ }
3622
+ function parsePublicExperience(value) {
3623
+ return isPublicExperience(value) ? value : null;
3624
+ }
3625
+
3586
3626
  //#endregion
3587
3627
  //#region ../core/src/rich-text.ts
3588
- function isRecord$6(value) {
3628
+ function isRecord$7(value) {
3589
3629
  return typeof value === "object" && value !== null && !Array.isArray(value);
3590
3630
  }
3591
3631
  function hasOnlyKeys(value, allowed) {
@@ -3607,10 +3647,10 @@ function isDenseArrayOf(value, predicate) {
3607
3647
  return true;
3608
3648
  }
3609
3649
  function isTipTapMark(value) {
3610
- return isRecord$6(value) && hasOnlyKeys(value, markKeys) && typeof value.type === "string" && value.type.length > 0 && (value.attrs === undefined || isRecord$6(value.attrs));
3650
+ return isRecord$7(value) && hasOnlyKeys(value, markKeys) && typeof value.type === "string" && value.type.length > 0 && (value.attrs === undefined || isRecord$7(value.attrs));
3611
3651
  }
3612
3652
  function isTipTapNode(value) {
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))) {
3653
+ if (!isRecord$7(value) || !hasOnlyKeys(value, nodeKeys) || typeof value.type !== "string" || value.type.length === 0 || value.type === "doc" || value.attrs !== undefined && !isRecord$7(value.attrs) || value.marks !== undefined && (!Array.isArray(value.marks) || !isDenseArrayOf(value.marks, isTipTapMark)) || value.content !== undefined && (!Array.isArray(value.content) || !isDenseArrayOf(value.content, isTipTapNode))) {
3614
3654
  return false;
3615
3655
  }
3616
3656
  if (value.type === "text") {
@@ -3619,7 +3659,7 @@ function isTipTapNode(value) {
3619
3659
  return value.text === undefined;
3620
3660
  }
3621
3661
  function isCanonicalRichTextDoc(value) {
3622
- return isRecord$6(value) && hasOnlyKeys(value, rootKeys) && value.type === "doc" && Array.isArray(value.content) && isDenseArrayOf(value.content, isTipTapNode);
3662
+ return isRecord$7(value) && hasOnlyKeys(value, rootKeys) && value.type === "doc" && Array.isArray(value.content) && isDenseArrayOf(value.content, isTipTapNode);
3623
3663
  }
3624
3664
 
3625
3665
  //#endregion
@@ -3671,10 +3711,10 @@ function parseBrevoConfirmationTemplateId(value) {
3671
3711
  //#endregion
3672
3712
  //#region ../core/src/mailing-list-targeting-parser.ts
3673
3713
  function parseMailingListPlacementTargeting(value) {
3674
- if (!isRecord$5(value)) return targetingError("shape");
3714
+ if (!isRecord$6(value)) return targetingError("shape");
3675
3715
  if (value.mode === "default") return ok({ mode: "default" });
3676
3716
  if (value.mode !== "custom") return targetingError("mode");
3677
- if (!isRecord$5(value.selection)) return targetingError("selection");
3717
+ if (!isRecord$6(value.selection)) return targetingError("selection");
3678
3718
  switch (value.provider) {
3679
3719
  case "native": return parseNativeTargeting(value.selection);
3680
3720
  case "brevo": return parseBrevoTargeting(value.selection);
@@ -3683,7 +3723,7 @@ function parseMailingListPlacementTargeting(value) {
3683
3723
  }
3684
3724
  }
3685
3725
  function parseMailingListVisitorSelection(value) {
3686
- if (!isRecord$5(value)) return visitorSelectionError("shape");
3726
+ if (!isRecord$6(value)) return visitorSelectionError("shape");
3687
3727
  switch (value.provider) {
3688
3728
  case "native": {
3689
3729
  const topicIds = parseVisitorIds(value.topicIds, parseNewsletterListId);
@@ -3816,7 +3856,7 @@ function parseVisitorIds(value, parseId) {
3816
3856
  }
3817
3857
  return parsed;
3818
3858
  }
3819
- function isRecord$5(value) {
3859
+ function isRecord$6(value) {
3820
3860
  return typeof value === "object" && value !== null && !Array.isArray(value);
3821
3861
  }
3822
3862
  function targetingError(field) {
@@ -3834,7 +3874,7 @@ function visitorSelectionError(field) {
3834
3874
 
3835
3875
  //#endregion
3836
3876
  //#region ../core/src/footer-engagement.ts
3837
- function isRecord$4(value) {
3877
+ function isRecord$5(value) {
3838
3878
  return typeof value === "object" && value !== null && !Array.isArray(value);
3839
3879
  }
3840
3880
  function invalid(path, message) {
@@ -3868,7 +3908,7 @@ function parseEngagementBase(value, base, path) {
3868
3908
  }
3869
3909
  const treatment = parseTreatment(value.treatment, `${path}.treatment`);
3870
3910
  if (!treatment.ok) return treatment;
3871
- if (!isRecord$4(value.source)) {
3911
+ if (!isRecord$5(value.source)) {
3872
3912
  return err(invalid(`${path}.source`, "Footer engagement source is invalid."));
3873
3913
  }
3874
3914
  const intro = parseBoundedText(value.source.intro, `${path}.source.intro`, {
@@ -3915,7 +3955,7 @@ function parseFooterEngagementSectionValue(value, sectionBase, path) {
3915
3955
  const base = parseEngagementBase(value, sectionBase, path);
3916
3956
  if (!base.ok) return base;
3917
3957
  const source = value.source;
3918
- if (!isRecord$4(source)) {
3958
+ if (!isRecord$5(source)) {
3919
3959
  return err(invalid(`${path}.source`, "Footer engagement source is invalid."));
3920
3960
  }
3921
3961
  switch (source.kind) {
@@ -4030,7 +4070,7 @@ function unsafeAsFooterModuleId(value) {
4030
4070
  function parseFooterModuleId(value) {
4031
4071
  return parseSectionId(value, "sectionId");
4032
4072
  }
4033
- function isRecord$3(value) {
4073
+ function isRecord$4(value) {
4034
4074
  return typeof value === "object" && value !== null && !Array.isArray(value);
4035
4075
  }
4036
4076
  function isRichText(value) {
@@ -4060,7 +4100,7 @@ function parseHeading(value, path) {
4060
4100
  }
4061
4101
  function parseFooterSection(value, index) {
4062
4102
  const path = `sections.${index}`;
4063
- if (!isRecord$3(value)) {
4103
+ if (!isRecord$4(value)) {
4064
4104
  return err({
4065
4105
  code: "invalid_section",
4066
4106
  path,
@@ -4095,7 +4135,7 @@ function parseFooterSection(value, index) {
4095
4135
  }
4096
4136
  break;
4097
4137
  case "navigation":
4098
- if (isRecord$3(value.source) && value.source.kind === "menu-role" && value.source.role === "main") {
4138
+ if (isRecord$4(value.source) && value.source.kind === "menu-role" && value.source.role === "main") {
4099
4139
  return ok({
4100
4140
  ...base,
4101
4141
  kind: "navigation",
@@ -4108,7 +4148,7 @@ function parseFooterSection(value, index) {
4108
4148
  }
4109
4149
  break;
4110
4150
  case "legal-navigation":
4111
- if (isRecord$3(value.source) && value.source.kind === "menu-role" && value.source.role === "legal-policy") {
4151
+ if (isRecord$4(value.source) && value.source.kind === "menu-role" && value.source.role === "legal-policy") {
4112
4152
  return ok({
4113
4153
  ...base,
4114
4154
  kind: "legal-navigation",
@@ -4121,7 +4161,7 @@ function parseFooterSection(value, index) {
4121
4161
  }
4122
4162
  break;
4123
4163
  case "business-address":
4124
- if (isRecord$3(value.source) && value.source.kind === "business-address") {
4164
+ if (isRecord$4(value.source) && value.source.kind === "business-address") {
4125
4165
  return ok({
4126
4166
  ...base,
4127
4167
  kind: "business-address",
@@ -4133,7 +4173,7 @@ function parseFooterSection(value, index) {
4133
4173
  case "contact":
4134
4174
  case "opening-hours":
4135
4175
  case "social-links":
4136
- if (isRecord$3(value.source) && value.source.kind === "public-profile") {
4176
+ if (isRecord$4(value.source) && value.source.kind === "public-profile") {
4137
4177
  return ok({
4138
4178
  ...base,
4139
4179
  kind: value.kind,
@@ -4185,7 +4225,7 @@ function validateFooterComposition(composition) {
4185
4225
  return ok(composition);
4186
4226
  }
4187
4227
  function parseFooterCompositionV1(value) {
4188
- if (!isRecord$3(value)) {
4228
+ if (!isRecord$4(value)) {
4189
4229
  return err({
4190
4230
  code: "invalid_shape",
4191
4231
  path: "",
@@ -4218,7 +4258,7 @@ function parseFooterCompositionV1(value) {
4218
4258
  });
4219
4259
  }
4220
4260
  function parseLegacyPlacementSection(value, index, selectedIds) {
4221
- if (!isRecord$3(value)) {
4261
+ if (!isRecord$4(value)) {
4222
4262
  return err({
4223
4263
  code: "invalid_section",
4224
4264
  path: `modules.${index}`,
@@ -4226,7 +4266,7 @@ function parseLegacyPlacementSection(value, index, selectedIds) {
4226
4266
  });
4227
4267
  }
4228
4268
  const selected = typeof value.id === "string" && selectedIds.has(value.id);
4229
- const source = isRecord$3(value.source) ? value.source : null;
4269
+ const source = isRecord$4(value.source) ? value.source : null;
4230
4270
  const mapped = value.kind === "navigation" && source?.kind === "menu-role" && source.role === "footer" ? {
4231
4271
  ...value,
4232
4272
  source: {
@@ -4245,7 +4285,7 @@ function parseLegacyPlacementSection(value, index, selectedIds) {
4245
4285
  * It is intentionally not called by the runtime parser.
4246
4286
  */
4247
4287
  function upgradePlacementFooterCompositionV1(value) {
4248
- if (!isRecord$3(value) || value.schemaVersion !== 1 || !Array.isArray(value.modules) || !Array.isArray(value.placements)) {
4288
+ if (!isRecord$4(value) || value.schemaVersion !== 1 || !Array.isArray(value.modules) || !Array.isArray(value.placements)) {
4249
4289
  return err({
4250
4290
  code: "invalid_shape",
4251
4291
  path: "",
@@ -4254,7 +4294,7 @@ function upgradePlacementFooterCompositionV1(value) {
4254
4294
  }
4255
4295
  const selectedIds = new Set();
4256
4296
  for (const [index, placement] of value.placements.entries()) {
4257
- if (!isRecord$3(placement) || typeof placement.moduleId !== "string") {
4297
+ if (!isRecord$4(placement) || typeof placement.moduleId !== "string") {
4258
4298
  return err({
4259
4299
  code: "invalid_shape",
4260
4300
  path: `placements.${index}`,
@@ -4469,7 +4509,7 @@ const defaultSections = (legacy, options) => {
4469
4509
  ];
4470
4510
  };
4471
4511
  function normalizeLegacyFooterContentToV1(value, options = {}) {
4472
- if (!isRecord$3(value)) {
4512
+ if (!isRecord$4(value)) {
4473
4513
  return err({
4474
4514
  code: "invalid_shape",
4475
4515
  path: "",
@@ -4492,6 +4532,23 @@ function normalizeFooterNavigationLayout(value) {
4492
4532
 
4493
4533
  //#endregion
4494
4534
  //#region ../core/src/mailing-list-setup.ts
4535
+ function isRecord$3(value) {
4536
+ return typeof value === "object" && value !== null && !Array.isArray(value);
4537
+ }
4538
+ /** The name shown to people for each provider; the native one is the product brand. */
4539
+ function mailingListProviderName(provider) {
4540
+ switch (provider) {
4541
+ case "native": return "Macadamia";
4542
+ case "mailchimp": return "Mailchimp";
4543
+ case "brevo": return "Brevo";
4544
+ }
4545
+ }
4546
+ const DEFAULT_MAILING_LIST_SUCCESS_MESSAGE = "Thanks — you're on the list.";
4547
+ /**
4548
+ * Seeded into a new setup so every signup form carries a preferences/unsubscribe
4549
+ * reassurance out of the box. Administrators can edit or clear it.
4550
+ */
4551
+ const DEFAULT_MAILING_LIST_PRIVACY_NOTE = "Change your preferences or unsubscribe at any time.";
4495
4552
  function planMailingListFieldCompatibility(input) {
4496
4553
  const collectedFields = resolveCollectedGlobalFields(input.form);
4497
4554
  const requiredFields = resolveRequiredGlobalFields(input.form.fields);
@@ -4560,7 +4617,7 @@ function planMailingListSetupSwitch(input) {
4560
4617
  affectedPlacementCount: 0
4561
4618
  };
4562
4619
  }
4563
- if (sameProviderScope(input.previousScope, input.nextScope)) {
4620
+ if (sameMailingListProviderScope(input.previousScope, input.nextScope)) {
4564
4621
  return {
4565
4622
  tag: "update_same_scope",
4566
4623
  affectedPlacementCount: input.defaultChanged ? countDefaultMailingListPlacements(input.placementModes) : 0
@@ -4571,7 +4628,39 @@ function planMailingListSetupSwitch(input) {
4571
4628
  affectedPlacementCount: input.placementModes.length
4572
4629
  };
4573
4630
  }
4574
- function sameProviderScope(previous, next) {
4631
+ function mailingListProviderScopeFromConfigured(provider) {
4632
+ switch (provider.provider) {
4633
+ case "native": return { provider: "native" };
4634
+ case "brevo": return {
4635
+ provider: "brevo",
4636
+ accountId: provider.accountId
4637
+ };
4638
+ case "mailchimp": return {
4639
+ provider: "mailchimp",
4640
+ accountId: provider.accountId
4641
+ };
4642
+ }
4643
+ }
4644
+ function parseMailingListProviderScope(value) {
4645
+ if (!isRecord$3(value)) return err("invalid_mailing_list_provider_scope");
4646
+ if (value.provider === "native") return ok({ provider: "native" });
4647
+ if (value.provider === "brevo") {
4648
+ const accountId = parseBrevoAccountId(value.accountId);
4649
+ return accountId !== null ? ok({
4650
+ provider: "brevo",
4651
+ accountId
4652
+ }) : err("invalid_mailing_list_provider_scope");
4653
+ }
4654
+ if (value.provider === "mailchimp") {
4655
+ const accountId = parseMailchimpAccountId(value.accountId);
4656
+ return accountId !== null ? ok({
4657
+ provider: "mailchimp",
4658
+ accountId
4659
+ }) : err("invalid_mailing_list_provider_scope");
4660
+ }
4661
+ return err("invalid_mailing_list_provider_scope");
4662
+ }
4663
+ function sameMailingListProviderScope(previous, next) {
4575
4664
  if (previous.provider !== next.provider) return false;
4576
4665
  switch (previous.provider) {
4577
4666
  case "native": return true;
@@ -4594,12 +4683,15 @@ function parseMailingListGlobalForm(value) {
4594
4683
  }
4595
4684
  const buttonLabel = parseRequiredString(value.buttonLabel);
4596
4685
  if (buttonLabel === null) return formError("buttonLabel");
4686
+ const successMessage = parseRequiredString(value.successMessage);
4687
+ if (successMessage === null) return formError("successMessage");
4597
4688
  const privacyNote = parseOptionalString(value.privacyNote);
4598
4689
  if (privacyNote === undefined) return formError("privacyNote");
4599
4690
  return ok({
4600
4691
  fields,
4601
4692
  includeOptionalPhone: value.includeOptionalPhone,
4602
4693
  buttonLabel,
4694
+ successMessage,
4603
4695
  privacyNote
4604
4696
  });
4605
4697
  }
@@ -5077,11 +5169,13 @@ function parseMailingListContentVersionId(value) {
5077
5169
  function parseMailingListFooterRevision(value) {
5078
5170
  return typeof value === "number" && Number.isSafeInteger(value) && value >= 0 ? value : null;
5079
5171
  }
5080
- function buildMailingListSignupBlockSourceRef(blockId) {
5081
- const parsed = parseMailingListBlockId(blockId);
5082
- return parsed !== null ? {
5172
+ function buildMailingListSignupBlockSourceRef(blockId, contentVersionId) {
5173
+ const parsedBlockId = parseMailingListBlockId(blockId);
5174
+ const parsedContentVersionId = parseMailingListContentVersionId(contentVersionId);
5175
+ return parsedBlockId !== null && parsedContentVersionId !== null ? {
5083
5176
  tag: "block_ref",
5084
- blockId: parsed
5177
+ blockId: parsedBlockId,
5178
+ contentVersionId: parsedContentVersionId
5085
5179
  } : null;
5086
5180
  }
5087
5181
  function parseMailingListSignupProfile(form, input) {
@@ -5191,6 +5285,9 @@ function toMailingListPublicSignupResponse(result) {
5191
5285
  reason: "profile_rejected"
5192
5286
  };
5193
5287
  }
5288
+ if (result.error.tag === "subscriber_suppressed") {
5289
+ return { tag: "confirmation_required" };
5290
+ }
5194
5291
  return result.error.tag === "uncertain" ? {
5195
5292
  tag: "uncertain",
5196
5293
  reason: result.error.reason
@@ -5947,7 +6044,8 @@ function failInvalidSecret() {
5947
6044
  const SITE_ROUTE_NODE_TYPES = [
5948
6045
  "page",
5949
6046
  "content_entry",
5950
- "page_surface"
6047
+ "page_surface",
6048
+ "appointment_service"
5951
6049
  ];
5952
6050
  function parseSiteRouteTarget(input) {
5953
6051
  if (typeof input.nodeType !== "string") {
@@ -5987,6 +6085,16 @@ function parseSiteRouteTarget(input) {
5987
6085
  error: surfaceId.error
5988
6086
  });
5989
6087
  }
6088
+ case "appointment_service": {
6089
+ const serviceId = parseAppointmentServiceId(input.nodeId);
6090
+ return serviceId.ok ? ok({
6091
+ kind: "appointmentService",
6092
+ serviceId: serviceId.value
6093
+ }) : err({
6094
+ kind: "invalid-appointment-service-id",
6095
+ error: serviceId.error
6096
+ });
6097
+ }
5990
6098
  default: return err({
5991
6099
  kind: "unsupported-node-type",
5992
6100
  nodeType: input.nodeType
@@ -5998,6 +6106,7 @@ function siteRouteTargetNodeType(target) {
5998
6106
  case "page": return "page";
5999
6107
  case "contentEntry": return "content_entry";
6000
6108
  case "pageSurface": return "page_surface";
6109
+ case "appointmentService": return "appointment_service";
6001
6110
  default: return assertNever(target);
6002
6111
  }
6003
6112
  }
@@ -6006,6 +6115,7 @@ function foldSiteRouteTarget(target, cases) {
6006
6115
  case "page": return cases.page(target.pageId);
6007
6116
  case "contentEntry": return cases.contentEntry(target.entryId);
6008
6117
  case "pageSurface": return cases.pageSurface(target.surfaceId);
6118
+ case "appointmentService": return cases.appointmentService(target.serviceId);
6009
6119
  default: return assertNever(target);
6010
6120
  }
6011
6121
  }
@@ -6898,4 +7008,4 @@ function parseSiteNavigationLocations(rows) {
6898
7008
  }
6899
7009
 
6900
7010
  //#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 };
7011
+ export { formatEventPricing as $, parseFooterCompositionV1 as A, unsafeAsPositiveSeconds as At, formatBookingTime as B, unsafeAsUserId as Bt, parseMailingListContentVersionId as C, unsafeAsEventOfferingId as Ct, DEFAULT_MAILING_LIST_SUCCESS_MESSAGE as D, unsafeAsOccurrenceId as Dt, planMailingListChoicePresentation as E, unsafeAsMediaAssetId as Et, normalizeLocationAddressText as F, unsafeAsSiteScopedStoragePath as Ft, getLocalScheduleTime as G, formatLocalScheduleTime as H, ok as Ht, toLocationCoordinates as I, unsafeAsStaffMemberId as It, toScheduledBookingDateRange as J, parseUtcIsoDateTimeString as K, buildBookingUtcDateWindow as L, unsafeAsStorageBucketName as Lt, isCanonicalRichTextDoc as M, unsafeAsSafeDownloadFilename as Mt, assertAllowedPublicUrl as N, unsafeAsSiteFontId as Nt, parseMailingListProviderScope as O, unsafeAsOriginalFilename as Ot, isPresent as P, unsafeAsSiteId as Pt, formatBookingPriceDisplay as Q, formatBookingDate as R, unsafeAsStoragePath as Rt, parseMailingListBlockId as S, unsafeAsEntitlementId as St, parseMailingListRuntimeContext as T, unsafeAsFileChecksumSha256 as Tt, getBookingLocalDateKey as U, formatLocalScheduleDate as V, err as Vt, getLocalScheduleDate as W, parseBookingTimeZone as X, toScheduledBookingDateTime as Y, parseEventPageSurfaceSubject as Z, createSecret as _, unsafeAsBookingRequirementId as _t, parseVerticalId as a, resolvePricingOptionsPriceDisplay as at, stableJsonStringify as b, unsafeAsDemoCapability as bt, DEFAULT_EVENT_TYPE_PRESETS as c, formatCurrencyAmount as ct, SURFACE_EVENT_PRESET_VALUES as d, parseEventSeriesId as dt, isPublicEventPricingOptionDisplayable as et, WHOLE_SERIES_EVENT_PRESET_VALUES as f, parseMediaAssetId as ft, resolveWholeSeriesBookingPhase as g, unsafeAsAppointmentResourceId as gt, orderedLiveRunSessions as h, parseUserId as ht, isEventSeriesVerticalPresetDefinition as i, resolveExactOrFreePriceDisplay as it, parseMailingListPlacementTargeting as j, unsafeAsProductId as jt, footerNavigationLayouts as k, unsafeAsPositiveByteCount as kt, EVENT_PRESET_VALUES as l, assertNever as lt, CANCELLED_SESSION_STATUS as m, parseSiteId as mt, areConfiguredBookingVerticalConfigsEqual as n, publicEventPricingDisplay as nt, parseVerticalPresetId as o, fromArray as ot, publicReadFreshnessSeconds as p, parsePageId as pt, toBookingTimeZone as q, canonicalizeConfiguredBookingVerticalConfig as r, publicEventPricingType as rt, resolveSiteVerticalBookingConfig as s, mapNonEmptyArray as st, NAVIGATION_LOCATIONS as t, publicEventPricingCurrency as tt, ORDINARY_EVENT_PRESET_VALUES as u, parseApiKeyId as ut, exposeSecret as v, unsafeAsBookingSessionId as vt, parseMailingListSignedSourceToken as w, unsafeAsEventSeriesId as wt, buildMailingListSignupBlockSourceRef as x, unsafeAsDetectedMimeType as xt, parseManagementKey as y, unsafeAsDeclaredMimeType as yt, formatBookingDateParts as z, unsafeAsUploadSessionId as zt };