riverbankcms 0.18.0 → 0.20.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.
@@ -94,6 +94,8 @@ const unsafeAsFormSubmissionId = (id) => id;
94
94
  const unsafeAsPassId = (id) => id;
95
95
  const unsafeAsMembershipId = (id) => id;
96
96
  const unsafeAsCustomerId = (id) => id;
97
+ const unsafeAsSiteTermsVersionId = (id) => id;
98
+ const unsafeAsSiteTermsAgreementId = (id) => id;
97
99
  const unsafeAsOrderRetirementOperationId = (id) => id;
98
100
  const unsafeAsOrderId = (id) => id;
99
101
  const unsafeAsOrderItemId = (id) => id;
@@ -198,6 +200,8 @@ const parseDatedOfferingWaitlistCapacityEffectId = (value) => parseUuidBrand(val
198
200
  const parseDatedOfferingWaitlistCapacityEffectClaimToken = (value) => parseUuidBrand(value, unsafeAsDatedOfferingWaitlistCapacityEffectClaimToken);
199
201
  const parseDatedOfferingRunCancellationOperationId = (value) => parseUuidBrand(value, unsafeAsDatedOfferingRunCancellationOperationId);
200
202
  const parseCustomerId = (value) => parseUuidBrand(value, unsafeAsCustomerId);
203
+ const parseSiteTermsVersionId = (value) => parseUuidBrand(value, unsafeAsSiteTermsVersionId);
204
+ const parseSiteTermsAgreementId = (value) => parseUuidBrand(value, unsafeAsSiteTermsAgreementId);
201
205
  const parseCustomerPassId = (value) => parseUuidBrand(value, unsafeAsCustomerPassId);
202
206
  const parseCustomerMembershipId = (value) => parseUuidBrand(value, unsafeAsCustomerMembershipId);
203
207
  const parseEventSeriesId = (value) => parseUuidBrand(value, unsafeAsEventSeriesId);
@@ -1478,7 +1482,7 @@ function resolveFlexibleBalanceDueAt(input) {
1478
1482
  switch (input.dueDateRule.kind) {
1479
1483
  case "relative_to_start_date": return subtractDaysFromIso(input.bookingStartAt, input.dueDateRule.daysBeforeStart);
1480
1484
  case "fixed_calendar_date": return input.dueDateRule.dueAt;
1481
- default: return assertNever$4(input.dueDateRule);
1485
+ default: return assertNever$5(input.dueDateRule);
1482
1486
  }
1483
1487
  }
1484
1488
  function calculateFlexibleBalanceMinimumPaymentCents(input) {
@@ -1533,7 +1537,7 @@ function resolveFlexibleBalancePaymentEligibility(input) {
1533
1537
  suggestedPayFullAmountCents: input.remainingBalanceCents
1534
1538
  };
1535
1539
  }
1536
- default: return assertNever$4(input.status);
1540
+ default: return assertNever$5(input.status);
1537
1541
  }
1538
1542
  }
1539
1543
  function isFlexibleBalanceReminderSuppressed(input) {
@@ -1569,7 +1573,7 @@ function buildFlexibleBalanceReminderSchedule(input) {
1569
1573
  })
1570
1574
  }));
1571
1575
  }
1572
- function assertNever$4(value) {
1576
+ function assertNever$5(value) {
1573
1577
  throw new Error(`Unexpected value: ${String(value)}`);
1574
1578
  }
1575
1579
 
@@ -1601,6 +1605,10 @@ function createOriginalPaymentRefundCommand(options) {
1601
1605
  protectedDepositOverride
1602
1606
  };
1603
1607
  }
1608
+ /** Narrow an already typed run or schedule write response to its accepted receipt. */
1609
+ function isAcceptedEventScheduleUpdate(response) {
1610
+ return "kind" in response && response.kind === "accepted_unavailable";
1611
+ }
1604
1612
 
1605
1613
  //#endregion
1606
1614
  //#region ../core/src/booking-instalments.ts
@@ -1609,7 +1617,7 @@ function isDepositBearingBookingInstalmentPlan(plan) {
1609
1617
  case "deposit_plus_one_remainder":
1610
1618
  case "deposit_plus_scheduled_instalments": return true;
1611
1619
  case "equal_scheduled_instalments": return false;
1612
- default: return assertNever$3(plan);
1620
+ default: return assertNever$4(plan);
1613
1621
  }
1614
1622
  }
1615
1623
  function getBookingInstalmentDepositRefundability(plan) {
@@ -1642,7 +1650,7 @@ function addIntervalPreservingMonthAnchor(isoString, intervalCount, intervalUnit
1642
1650
  date.setUTCDate(date.getUTCDate() + intervalCount * 7);
1643
1651
  return date.toISOString();
1644
1652
  case "month": return addMonthsPreservingAnchorDay(date, intervalCount).toISOString();
1645
- default: return assertNever$3(intervalUnit);
1653
+ default: return assertNever$4(intervalUnit);
1646
1654
  }
1647
1655
  }
1648
1656
  function getBookingInstalmentPlanIntervalCount(plan) {
@@ -1650,7 +1658,7 @@ function getBookingInstalmentPlanIntervalCount(plan) {
1650
1658
  case "deposit_plus_one_remainder": return plan.remainderDueCount;
1651
1659
  case "deposit_plus_scheduled_instalments":
1652
1660
  case "equal_scheduled_instalments": return plan.intervalCount;
1653
- default: return assertNever$3(plan);
1661
+ default: return assertNever$4(plan);
1654
1662
  }
1655
1663
  }
1656
1664
  function omitZeroAmountFutureRows(rows) {
@@ -1674,7 +1682,7 @@ function formatOrderInstalmentStatusLabel(input) {
1674
1682
  case "cancelled": return "Cancelled";
1675
1683
  case "cancelled_as_paid_early": return "Paid early";
1676
1684
  case "refunded": return "Refunded";
1677
- default: return assertNever$3(input.status);
1685
+ default: return assertNever$4(input.status);
1678
1686
  }
1679
1687
  }
1680
1688
  const NEXT_DUE_STATUSES = new Set(["scheduled", "failed"]);
@@ -1695,7 +1703,7 @@ function addMonthsPreservingAnchorDay(date, intervalCount) {
1695
1703
  nextMonth.setUTCDate(Math.min(anchorDay, lastDayOfTargetMonth));
1696
1704
  return nextMonth;
1697
1705
  }
1698
- function assertNever$3(value) {
1706
+ function assertNever$4(value) {
1699
1707
  throw new Error(`Unexpected value: ${String(value)}`);
1700
1708
  }
1701
1709
 
@@ -1830,6 +1838,51 @@ function validateBookingPaymentOptionKinds(kinds) {
1830
1838
  return ok(undefined);
1831
1839
  }
1832
1840
 
1841
+ //#endregion
1842
+ //#region ../core/src/date-time-formatter.ts
1843
+ /** Shared Intl engine, internal to core's date presentation owners. */
1844
+ const FORMATTER_CACHE_LIMIT$1 = 100;
1845
+ const dateTimeFormatterCache = new Map();
1846
+ /** Intl's boolean clock option can select h11/h24 on other ICU builds. */
1847
+ function resolveClockCycle(format) {
1848
+ const { hour12, ...explicitFormat } = format;
1849
+ return hour12 === undefined ? explicitFormat : {
1850
+ ...explicitFormat,
1851
+ hourCycle: hour12 ? "h12" : "h23"
1852
+ };
1853
+ }
1854
+ function getDateTimeFormatter(locale, timeZone, format) {
1855
+ const explicitFormat = resolveClockCycle(format);
1856
+ const formatKey = JSON.stringify(Object.entries(explicitFormat).sort(([left], [right]) => left.localeCompare(right)));
1857
+ const key = `${locale}\u0000${timeZone}\u0000${formatKey}`;
1858
+ const cached = dateTimeFormatterCache.get(key);
1859
+ if (cached !== undefined) {
1860
+ return cached;
1861
+ }
1862
+ if (typeof locale !== "string" || Intl.DateTimeFormat.supportedLocalesOf([locale]).length === 0) {
1863
+ throw new RangeError("An explicit supported locale is required");
1864
+ }
1865
+ if (typeof timeZone !== "string" || timeZone.length === 0) {
1866
+ throw new RangeError("An explicit time zone is required");
1867
+ }
1868
+ const formatter = new Intl.DateTimeFormat(locale, {
1869
+ ...explicitFormat,
1870
+ timeZone
1871
+ });
1872
+ if (dateTimeFormatterCache.size >= FORMATTER_CACHE_LIMIT$1) {
1873
+ dateTimeFormatterCache.clear();
1874
+ }
1875
+ dateTimeFormatterCache.set(key, formatter);
1876
+ return formatter;
1877
+ }
1878
+ function formatIntlRange(formatter, start, end) {
1879
+ const nativeFormatRange = Reflect.get(formatter, "formatRange");
1880
+ if (typeof nativeFormatRange !== "function") throw new Error("Intl.DateTimeFormat.formatRange is required");
1881
+ const output = Reflect.apply(nativeFormatRange, formatter, [start, end]);
1882
+ if (typeof output !== "string") throw new Error("Intl.DateTimeFormat.formatRange returned a non-string value");
1883
+ return output;
1884
+ }
1885
+
1833
1886
  //#endregion
1834
1887
  //#region ../core/src/booking-time-zone.ts
1835
1888
  const TIME_ZONE_VALIDITY_CACHE_LIMIT = 100;
@@ -1864,7 +1917,7 @@ function parseBookingTimeZone(timeZone) {
1864
1917
 
1865
1918
  //#endregion
1866
1919
  //#region ../core/src/booking-date-time.ts
1867
- const DEFAULT_LOCALE = "en-US";
1920
+ const DEFAULT_LOCALE$1 = "en-US";
1868
1921
  const FALLBACK_TIMEZONE = "UTC";
1869
1922
  const DEFAULT_BOOKING_DATE_TIME_FORMAT = {
1870
1923
  weekday: "short",
@@ -1879,8 +1932,6 @@ const START_OF_DAY = {
1879
1932
  second: 0
1880
1933
  };
1881
1934
  const LOCAL_DATE_PATTERN = /^(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})$/u;
1882
- const DATE_TIME_FORMATTER_CACHE_LIMIT = 100;
1883
- const dateTimeFormatterCache = new Map();
1884
1935
  function getSafeTimeZone(timeZone) {
1885
1936
  const normalized = typeof timeZone === "string" ? timeZone.trim() : "";
1886
1937
  return normalized.length > 0 && isValidBookingTimeZone(normalized) ? normalized : FALLBACK_TIMEZONE;
@@ -1897,24 +1948,7 @@ function formatParseError(error) {
1897
1948
  }
1898
1949
  }
1899
1950
  function formatToPartsLookup(date, locale, timeZone, format) {
1900
- return new Map(getCachedDateTimeFormatter(locale, timeZone, format).formatToParts(date).map((part) => [part.type, part.value]));
1901
- }
1902
- function getCachedDateTimeFormatter(locale, timeZone, format) {
1903
- const formatKey = JSON.stringify(Object.entries(format).sort(([left], [right]) => left.localeCompare(right)));
1904
- const key = `${locale}\u0000${timeZone}\u0000${formatKey}`;
1905
- const cached = dateTimeFormatterCache.get(key);
1906
- if (cached !== undefined) {
1907
- return cached;
1908
- }
1909
- const formatter = new Intl.DateTimeFormat(locale, {
1910
- ...format,
1911
- timeZone
1912
- });
1913
- if (dateTimeFormatterCache.size >= DATE_TIME_FORMATTER_CACHE_LIMIT) {
1914
- dateTimeFormatterCache.clear();
1915
- }
1916
- dateTimeFormatterCache.set(key, formatter);
1917
- return formatter;
1951
+ return new Map(getDateTimeFormatter(locale, timeZone, format).formatToParts(date).map((part) => [part.type, part.value]));
1918
1952
  }
1919
1953
  function getZonedDateTimeParts(date, timeZone) {
1920
1954
  const parts = formatToPartsLookup(date, "en-GB", timeZone, {
@@ -1924,7 +1958,7 @@ function getZonedDateTimeParts(date, timeZone) {
1924
1958
  hour: "2-digit",
1925
1959
  minute: "2-digit",
1926
1960
  second: "2-digit",
1927
- hour12: false
1961
+ hourCycle: "h23"
1928
1962
  });
1929
1963
  const read = (type) => Number(parts.get(type) ?? "");
1930
1964
  return {
@@ -1958,6 +1992,11 @@ function resolveBookingTimeZoneId(...candidates) {
1958
1992
  }
1959
1993
  return FALLBACK_TIMEZONE;
1960
1994
  }
1995
+ /** Canonical business-zone admission, shared by server and public wire boundaries. */
1996
+ const DEFAULT_BOOKING_BUSINESS_TIME_ZONE = "Europe/London";
1997
+ function resolveBookingBusinessTimeZone(value) {
1998
+ return resolveBookingTimeZoneId(value, DEFAULT_BOOKING_BUSINESS_TIME_ZONE);
1999
+ }
1961
2000
  function parseUtcIsoDateTimeString(value) {
1962
2001
  const normalized = value.trim();
1963
2002
  const hasUtcSuffix = normalized.endsWith("Z");
@@ -2056,7 +2095,7 @@ function formatBookingInstant(isoString, options) {
2056
2095
  if (Number.isNaN(date.getTime())) {
2057
2096
  return isoString;
2058
2097
  }
2059
- return getCachedDateTimeFormatter(options.locale ?? DEFAULT_LOCALE, getSafeTimeZone(options.timeZone), options.format).format(date);
2098
+ return getDateTimeFormatter(options.locale ?? DEFAULT_LOCALE$1, getSafeTimeZone(options.timeZone), options.format).format(date);
2060
2099
  }
2061
2100
  function formatBookingDate(input, format = {
2062
2101
  weekday: "long",
@@ -2073,7 +2112,7 @@ function formatBookingDate(input, format = {
2073
2112
  function formatBookingTime(input, format = {
2074
2113
  hour: "numeric",
2075
2114
  minute: "2-digit",
2076
- hour12: true
2115
+ hourCycle: "h12"
2077
2116
  }) {
2078
2117
  return formatBookingInstant(input.startsAt, {
2079
2118
  locale: input.locale,
@@ -2096,7 +2135,7 @@ function formatBookingCompactEnglishTime(input) {
2096
2135
  const parts = formatToPartsLookup(date, input.locale ?? "en-GB", input.timeZone, {
2097
2136
  hour: "numeric",
2098
2137
  minute: "2-digit",
2099
- hour12: true
2138
+ hourCycle: "h12"
2100
2139
  });
2101
2140
  const hour = parts.get("hour");
2102
2141
  const minute = parts.get("minute");
@@ -2132,7 +2171,7 @@ function formatBookingDateParts(input, options) {
2132
2171
  time: formatBookingTime(input, options?.timeFormat ?? {
2133
2172
  hour: "numeric",
2134
2173
  minute: "2-digit",
2135
- hour12: true
2174
+ hourCycle: "h12"
2136
2175
  })
2137
2176
  };
2138
2177
  }
@@ -2242,6 +2281,8 @@ function formatBookingRange(input, options) {
2242
2281
  });
2243
2282
  return `${startDate}, ${startTime} - ${endDate}, ${endTime}`;
2244
2283
  }
2284
+ /** Canonical schedule text at command and SDK boundaries: HH:MM or HH:MM:SS. */
2285
+ const localScheduleTimePattern = /^([01]\d|2[0-3]):[0-5]\d(:[0-5]\d)?$/u;
2245
2286
  function parseLocalScheduleTime(value) {
2246
2287
  const match = /^(?<hour>\d{1,2}):(?<minute>\d{2})(?::(?<second>\d{2}))?$/u.exec(value.trim());
2247
2288
  if (match === null || match.groups === undefined) {
@@ -2329,6 +2370,538 @@ function localScheduleDateTimeToUtcDate(localDate, localTime, timeZone) {
2329
2370
  return new Date(guess);
2330
2371
  }
2331
2372
 
2373
+ //#endregion
2374
+ //#region ../core/src/date-display.ts
2375
+ /** Site-language policy, shared by customer emails and public event adapters. */
2376
+ function inferSiteDateLocale(siteTimeZone) {
2377
+ if (siteTimeZone === null || siteTimeZone === undefined || siteTimeZone === "") return "en-US";
2378
+ if (siteTimeZone === "Europe/Dublin") return "en-IE";
2379
+ if (siteTimeZone.startsWith("Europe/")) return "en-GB";
2380
+ if (siteTimeZone.startsWith("Australia/")) return "en-AU";
2381
+ if (siteTimeZone === "Pacific/Auckland" || siteTimeZone === "Pacific/Chatham") return "en-NZ";
2382
+ return "en-US";
2383
+ }
2384
+ /** Callers resolve the facts once; all clocks consume the same label decision. */
2385
+ function resolveBookingDisplayContext(input) {
2386
+ return {
2387
+ context: {
2388
+ locale: input.locale,
2389
+ timeZone: input.sessionTimeZone
2390
+ },
2391
+ zone: input.surface === "email" && input.isOnline || input.sessionTimeZone !== input.siteTimeZone ? "always" : "omit"
2392
+ };
2393
+ }
2394
+ const SDK_STANDARD_TIME_FORMAT = {
2395
+ hour: "numeric",
2396
+ minute: "2-digit"
2397
+ };
2398
+ /** Public SDK option policy lives beside the display profile, never in its adapter. */
2399
+ function sdkPresentationProfile(context, dateFormat$1, time = {}) {
2400
+ const compact = time.timePresentation !== "standard" && new Intl.Locale(context.locale).language === "en";
2401
+ const standard = time.timePresentation === "standard" ? time.format ?? SDK_STANDARD_TIME_FORMAT : SDK_STANDARD_TIME_FORMAT;
2402
+ return {
2403
+ kind: "sdk",
2404
+ dateFormat: dateFormat$1,
2405
+ clock: compact ? {
2406
+ kind: "compact",
2407
+ localeSource: "context"
2408
+ } : {
2409
+ kind: "intl",
2410
+ format: standard
2411
+ },
2412
+ zone: "omit"
2413
+ };
2414
+ }
2415
+ function formatDate(value, context, options = {}) {
2416
+ return presentDate(value, context, displayProfile(options, "omit"));
2417
+ }
2418
+ function formatDateRange(start, endInclusive, context, options = {}) {
2419
+ const first = {
2420
+ kind: "civil",
2421
+ value: projectDate(start, context)
2422
+ };
2423
+ const last = {
2424
+ kind: "civil",
2425
+ value: projectDate(endInclusive, context)
2426
+ };
2427
+ const preserveOrder = options.order === "session" && compareLocalScheduleDates(first.value, last.value) > 0;
2428
+ const profile = displayProfile(preserveOrder ? {
2429
+ ...options,
2430
+ year: "numeric"
2431
+ } : options, "omit");
2432
+ return preserveOrder ? `${presentDate(first, context, profile)} – ${presentDate(last, context, profile)}` : presentDateRange(first, last, context, profile);
2433
+ }
2434
+ function formatDateTime(value, context, options) {
2435
+ return presentDateTime(value, context, displayProfile(options, options.zone));
2436
+ }
2437
+ function formatDateTimeRange(span, context, options) {
2438
+ return presentDateTimeRange(span, context, displayProfile(options, options.zone));
2439
+ }
2440
+ function formatTime(value, context, options) {
2441
+ return presentTime(value, context, displayProfile({}, options.zone));
2442
+ }
2443
+ function formatTimeRange(start, end, context, options) {
2444
+ return presentTimeRange({
2445
+ start,
2446
+ end
2447
+ }, context, displayProfile({}, options.zone));
2448
+ }
2449
+ /** Sole date projection/typography owner, used by display and scheduled adapters. */
2450
+ function presentDate(value, context, profile) {
2451
+ const date = dateCarrier(value);
2452
+ const zone = value.kind === "civil" ? "UTC" : context.timeZone;
2453
+ const output = getDateTimeFormatter(context.locale, zone, profile.dateFormat).format(date);
2454
+ if (profile.kind === "sdk") return output;
2455
+ const normalized = normalizeDisplay(output);
2456
+ return profile.dateFormat.weekday && new Intl.Locale(context.locale).language === "en" ? normalized.replace(/^([^,]+), /u, "$1 ") : normalized;
2457
+ }
2458
+ function presentDateRange(start, end, context, profile) {
2459
+ if (dateCarrier(end).getTime() < dateCarrier(start).getTime()) throw new RangeError("Date range end must be on or after its start");
2460
+ if (profile.kind === "display" && compareLocalScheduleDates(projectDate(start, context), projectDate(end, context)) === 0) {
2461
+ return presentDate(start, context, profile);
2462
+ }
2463
+ const output = formatIntlRange(getDateTimeFormatter(context.locale, start.kind === "civil" ? "UTC" : context.timeZone, profile.dateFormat), dateCarrier(start), dateCarrier(end));
2464
+ if (profile.kind === "sdk") return output;
2465
+ const startDate = projectDate(start, context);
2466
+ const endDate = projectDate(end, context);
2467
+ const separator = startDate.month === endDate.month && startDate.year === endDate.year ? "–" : " – ";
2468
+ return normalizeDisplay(output).replace(/\s*–\s*/gu, separator);
2469
+ }
2470
+ function presentTime(value, context, profile) {
2471
+ const clock = presentClock(value, context, profile.clock);
2472
+ return profile.zone === "always" ? `${clock} ${zoneLabel(value, context)}` : clock;
2473
+ }
2474
+ function presentDateTime(value, context, profile) {
2475
+ if (profile.clock.kind === "intl") {
2476
+ return getDateTimeFormatter(context.locale, context.timeZone, combinedFormat(profile.dateFormat, profile.clock)).format(new Date(value));
2477
+ }
2478
+ return `${presentDate({
2479
+ kind: "instant",
2480
+ value
2481
+ }, context, profile)}, ${presentTime(value, context, profile)}`;
2482
+ }
2483
+ function presentTimeRange(span, context, profile) {
2484
+ assertOrdered(span.start, span.end);
2485
+ if (profile.clock.kind === "intl") return presentNativeRange(span, context, profile.clock.format);
2486
+ const startClock = presentClock(span.start, context, profile.clock);
2487
+ const endClock = presentClock(span.end, context, profile.clock);
2488
+ const equal = profile.kind === "sdk" ? startClock === endClock : span.start === span.end;
2489
+ const clocks = equal ? startClock : `${startClock}–${endClock}`;
2490
+ const labels = decideRangeLabels(span, context, profile);
2491
+ switch (labels.kind) {
2492
+ case "omit": return clocks;
2493
+ case "shared": return `${clocks} ${labels.label}`;
2494
+ case "endpoints": return `${startClock} ${labels.start} – ${endClock} ${labels.end}`;
2495
+ }
2496
+ }
2497
+ function presentDateTimeRange(span, context, profile) {
2498
+ assertOrdered(span.start, span.end);
2499
+ if (profile.clock.kind === "intl") return presentNativeRange(span, context, combinedFormat(profile.dateFormat, profile.clock));
2500
+ const startDate = getLocalScheduleDate(new Date(span.start), context.timeZone);
2501
+ const endDate = getLocalScheduleDate(new Date(span.end), context.timeZone);
2502
+ if (compareLocalScheduleDates(startDate, endDate) === 0) {
2503
+ return `${presentDate({
2504
+ kind: "instant",
2505
+ value: span.start
2506
+ }, context, profile)}, ${presentTimeRange(span, context, profile)}`;
2507
+ }
2508
+ const labels = decideRangeLabels(span, context, profile);
2509
+ const startProfile = profile.kind === "display" && startDate.year === endDate.year ? {
2510
+ ...profile,
2511
+ dateFormat: {
2512
+ ...profile.dateFormat,
2513
+ year: undefined
2514
+ }
2515
+ } : profile;
2516
+ const start = presentRangeEndpoint(span.start, context, startProfile, labels.kind === "endpoints" ? labels.start : undefined);
2517
+ const end = presentRangeEndpoint(span.end, context, profile, labels.kind === "endpoints" ? labels.end : undefined);
2518
+ const separator = profile.kind === "sdk" ? "–" : " – ";
2519
+ return profile.kind === "sdk" && start === end ? start : `${start}${separator}${end}`;
2520
+ }
2521
+ /** Offset changes override ordinary venue omission for the approved display profile. */
2522
+ function decideRangeLabels(span, context, profile) {
2523
+ if (profile.kind === "sdk") return { kind: "omit" };
2524
+ const offsetsDiffer = offsetToken(span.start, context) !== offsetToken(span.end, context);
2525
+ if (!offsetsDiffer && profile.zone === "omit") return { kind: "omit" };
2526
+ const start = zoneLabel(span.start, context);
2527
+ const end = zoneLabel(span.end, context);
2528
+ return offsetsDiffer || start !== end ? {
2529
+ kind: "endpoints",
2530
+ start,
2531
+ end
2532
+ } : {
2533
+ kind: "shared",
2534
+ label: start
2535
+ };
2536
+ }
2537
+ function presentRangeEndpoint(value, context, profile, label) {
2538
+ const date = presentDate({
2539
+ kind: "instant",
2540
+ value
2541
+ }, context, profile);
2542
+ const time = label === undefined ? presentTime(value, context, profile) : `${presentClock(value, context, profile.clock)} ${label}`;
2543
+ return `${date}, ${time}`;
2544
+ }
2545
+ function presentNativeRange(span, context, format) {
2546
+ return formatIntlRange(getDateTimeFormatter(context.locale, context.timeZone, format), new Date(span.start), new Date(span.end));
2547
+ }
2548
+ function combinedFormat(date, clock) {
2549
+ return {
2550
+ ...date,
2551
+ ...clock.format
2552
+ };
2553
+ }
2554
+ function displayProfile(options, zone) {
2555
+ return {
2556
+ kind: "display",
2557
+ dateFormat: dateFormat(options),
2558
+ clock: {
2559
+ kind: "compact",
2560
+ localeSource: "product"
2561
+ },
2562
+ zone
2563
+ };
2564
+ }
2565
+ function dateCarrier(value) {
2566
+ return value.kind === "civil" ? civilCarrier(value.value) : new Date(value.value);
2567
+ }
2568
+ function presentClock(value, context, clock) {
2569
+ return clock.kind === "compact" ? formatBookingCompactEnglishTime({
2570
+ startsAt: value,
2571
+ timeZone: context.timeZone,
2572
+ locale: clock.localeSource === "product" ? "en-GB" : context.locale
2573
+ }) : getDateTimeFormatter(context.locale, context.timeZone, clock.format).format(new Date(value));
2574
+ }
2575
+ function offsetToken(value, context) {
2576
+ return getDateTimeFormatter("en-GB", context.timeZone, { timeZoneName: "longOffset" }).formatToParts(new Date(value)).find((part) => part.type === "timeZoneName")?.value ?? "";
2577
+ }
2578
+ function formatTimeAgo(value, now, context) {
2579
+ return formatRelative(value, now, context, "past");
2580
+ }
2581
+ function formatTimeUntil(value, now, context) {
2582
+ return formatRelative(value, now, context, "future");
2583
+ }
2584
+ function dateFormat(options) {
2585
+ const style = options.date ?? "long";
2586
+ return {
2587
+ day: style === "omit" ? undefined : style === "numeric" ? "2-digit" : "numeric",
2588
+ month: style === "omit" ? undefined : style === "numeric" ? "2-digit" : style,
2589
+ ...options.year === "omit" ? {} : { year: "numeric" },
2590
+ ...options.weekday ? { weekday: options.weekday } : {}
2591
+ };
2592
+ }
2593
+ function civilCarrier(value) {
2594
+ return new Date(Date.UTC(value.year, value.month - 1, value.day, 12));
2595
+ }
2596
+ function projectDate(value, context) {
2597
+ return value.kind === "civil" ? value.value : getLocalScheduleDate(new Date(value.value), context.timeZone);
2598
+ }
2599
+ function normalizeDisplay(value) {
2600
+ return value.replace(/[\u00a0\u202f]/gu, " ");
2601
+ }
2602
+ function zoneToken(value, context, locale) {
2603
+ return getDateTimeFormatter(locale, context.timeZone, { timeZoneName: "short" }).formatToParts(new Date(value)).find((part) => part.type === "timeZoneName")?.value ?? "";
2604
+ }
2605
+ function zoneLabel(value, context) {
2606
+ const tokens = [zoneToken(value, context, "en-GB"), zoneToken(value, context, "en-US")];
2607
+ const abbreviation = tokens.find((token) => /^[A-Z]{2,6}$/u.test(token));
2608
+ return abbreviation ?? formatTimeZoneCityLabel(context.timeZone);
2609
+ }
2610
+ function assertOrdered(start, end) {
2611
+ if (new Date(end).getTime() < new Date(start).getTime()) {
2612
+ throw new RangeError("Date/time range end must be on or after its start");
2613
+ }
2614
+ }
2615
+ function calendarDayDifference(value, now, context) {
2616
+ return compareLocalScheduleDates(getLocalScheduleDate(new Date(value), context.timeZone), getLocalScheduleDate(new Date(now), context.timeZone)) / 864e5;
2617
+ }
2618
+ function formatRelative(value, now, context, direction) {
2619
+ const seconds = (new Date(value).getTime() - new Date(now).getTime()) / 1e3;
2620
+ const magnitude = Math.abs(seconds);
2621
+ if (direction === "past" && seconds > 0 && seconds <= 120) return relativeNearNow(context.locale, "past");
2622
+ if (magnitude < 60) return relativeNearNow(context.locale, direction);
2623
+ const days = calendarDayDifference(value, now, context);
2624
+ if (Math.abs(days) > 7) return formatDate({
2625
+ kind: "instant",
2626
+ value
2627
+ }, context);
2628
+ const formatter = new Intl.RelativeTimeFormat(context.locale, { numeric: "auto" });
2629
+ if (days !== 0) return formatter.format(days, "day");
2630
+ const unit = magnitude < 3600 ? "minute" : "hour";
2631
+ const signedCount = Math.sign(seconds) * Math.floor(magnitude / (unit === "minute" ? 60 : 3600));
2632
+ return formatter.format(signedCount, unit);
2633
+ }
2634
+ function relativeNearNow(locale, direction) {
2635
+ if (new Intl.Locale(locale).language === "en") return direction === "past" ? "just now" : "in a moment";
2636
+ return new Intl.RelativeTimeFormat(locale, { numeric: "auto" }).format(0, "second");
2637
+ }
2638
+ /** Same city vocabulary as clock-label fallback, for explanatory timezone copy. */
2639
+ function formatTimeZoneCityLabel(timeZone) {
2640
+ return timeZone.startsWith("Etc/") ? "local time" : `${timeZone.split("/").slice(-1)[0]?.replace(/_/gu, " ") ?? timeZone} time`;
2641
+ }
2642
+
2643
+ //#endregion
2644
+ //#region ../core/src/scheduled-date-time.ts
2645
+ const DEFAULT_LOCALE = "en-GB";
2646
+ const FORMATTER_CACHE_LIMIT = 100;
2647
+ const DEFAULT_DATE_FORMAT = {
2648
+ weekday: "long",
2649
+ year: "numeric",
2650
+ month: "long",
2651
+ day: "numeric"
2652
+ };
2653
+ const DEFAULT_RANGE_DATE_FORMAT = {
2654
+ weekday: "short",
2655
+ year: "numeric",
2656
+ month: "short",
2657
+ day: "numeric"
2658
+ };
2659
+ const parsedLocaleCache = new Map();
2660
+ var RiverbankDateTimeFormatError = class extends Error {
2661
+ constructor(details) {
2662
+ super(formatErrorMessage(details));
2663
+ this.name = "RiverbankDateTimeFormatError";
2664
+ this.code = details.code;
2665
+ this.field = details.field;
2666
+ this.value = details.value;
2667
+ }
2668
+ };
2669
+ /** Public contract adapters: admission/options here, all presentation in date-display. */
2670
+ function formatScheduledDate(input, options = {}) {
2671
+ const parsed = parseDateTime(input);
2672
+ return presentDate({
2673
+ kind: "instant",
2674
+ value: parsed.startsAt
2675
+ }, scheduledContext(parsed), sdkPresentationProfile(scheduledContext(parsed), options.format ?? DEFAULT_DATE_FORMAT));
2676
+ }
2677
+ function formatScheduledTime(input, options = {}) {
2678
+ const parsed = parseDateTime(input);
2679
+ return presentTime(parsed.startsAt, scheduledContext(parsed), sdkPresentationProfile(scheduledContext(parsed), {}, options));
2680
+ }
2681
+ function formatScheduledDateTime(input, options = {}) {
2682
+ const parsed = parseDateTime(input);
2683
+ return presentDateTime(parsed.startsAt, scheduledContext(parsed), sdkPresentationProfile(scheduledContext(parsed), options.dateFormat ?? DEFAULT_DATE_FORMAT, options.time));
2684
+ }
2685
+ function formatScheduledDateRange(input, options = {}) {
2686
+ const parsed = parseDateRange(input);
2687
+ return presentDateRange({
2688
+ kind: "instant",
2689
+ value: parsed.startsAt
2690
+ }, {
2691
+ kind: "instant",
2692
+ value: parsed.endsAt
2693
+ }, scheduledContext(parsed), sdkPresentationProfile(scheduledContext(parsed), options.format ?? DEFAULT_RANGE_DATE_FORMAT));
2694
+ }
2695
+ function formatScheduledTimeRange(input, options = {}) {
2696
+ const parsed = parseDateRange(input);
2697
+ return presentTimeRange({
2698
+ start: parsed.startsAt,
2699
+ end: parsed.endsAt
2700
+ }, scheduledContext(parsed), sdkPresentationProfile(scheduledContext(parsed), {}, options));
2701
+ }
2702
+ function formatScheduledDateTimeRange(input, options = {}) {
2703
+ const parsed = parseDateRange(input);
2704
+ return presentDateTimeRange({
2705
+ start: parsed.startsAt,
2706
+ end: parsed.endsAt
2707
+ }, scheduledContext(parsed), sdkPresentationProfile(scheduledContext(parsed), options.dateFormat ?? DEFAULT_RANGE_DATE_FORMAT, options.time));
2708
+ }
2709
+ function scheduledContext(input) {
2710
+ return {
2711
+ locale: input.locale,
2712
+ timeZone: input.timeZone
2713
+ };
2714
+ }
2715
+ function parseDateTime(input) {
2716
+ const locale = parseLocale(input.locale);
2717
+ assertStringTimeZone(input.timeZone);
2718
+ const result = parseScheduledBookingDateTime({
2719
+ ...input,
2720
+ locale: locale.locale
2721
+ });
2722
+ if (result.ok) {
2723
+ return {
2724
+ ...result.value,
2725
+ locale: locale.locale
2726
+ };
2727
+ }
2728
+ throw fromCoreParseError(result);
2729
+ }
2730
+ function parseDateRange(input) {
2731
+ const locale = parseLocale(input.locale);
2732
+ assertStringTimeZone(input.timeZone);
2733
+ assertRequiredEnd(input.endsAt);
2734
+ const result = parseScheduledBookingDateRange({
2735
+ ...input,
2736
+ locale: locale.locale
2737
+ });
2738
+ if (!result.ok) {
2739
+ throw fromCoreParseError(result);
2740
+ }
2741
+ if (result.value.endsAt === null || result.value.endsAt === undefined) {
2742
+ throw new RiverbankDateTimeFormatError({
2743
+ code: "missing_ends_at",
2744
+ field: "endsAt",
2745
+ value: ""
2746
+ });
2747
+ }
2748
+ if (new Date(result.value.endsAt).getTime() < new Date(result.value.startsAt).getTime()) {
2749
+ throw new RiverbankDateTimeFormatError({
2750
+ code: "end_before_start",
2751
+ field: "endsAt",
2752
+ value: input.endsAt
2753
+ });
2754
+ }
2755
+ return {
2756
+ ...result.value,
2757
+ endsAt: result.value.endsAt,
2758
+ locale: locale.locale
2759
+ };
2760
+ }
2761
+ function assertRequiredEnd(value) {
2762
+ if (value === null || value === undefined) {
2763
+ throw new RiverbankDateTimeFormatError({
2764
+ code: "missing_ends_at",
2765
+ field: "endsAt",
2766
+ value: ""
2767
+ });
2768
+ }
2769
+ }
2770
+ function parseLocale(value) {
2771
+ const locale = value === undefined ? DEFAULT_LOCALE : value;
2772
+ if (typeof locale !== "string") {
2773
+ throw new RiverbankDateTimeFormatError({
2774
+ code: "invalid_locale",
2775
+ field: "locale",
2776
+ value: ""
2777
+ });
2778
+ }
2779
+ const cached = parsedLocaleCache.get(locale);
2780
+ if (cached !== undefined) {
2781
+ return cached;
2782
+ }
2783
+ try {
2784
+ const parsed = new Intl.Locale(locale);
2785
+ const canonical = parsed.toString();
2786
+ if (Intl.DateTimeFormat.supportedLocalesOf([canonical]).length === 0) {
2787
+ throw new RangeError("Unsupported locale");
2788
+ }
2789
+ const result = { locale: canonical };
2790
+ if (parsedLocaleCache.size >= FORMATTER_CACHE_LIMIT) {
2791
+ parsedLocaleCache.clear();
2792
+ }
2793
+ parsedLocaleCache.set(locale, result);
2794
+ return result;
2795
+ } catch {
2796
+ throw new RiverbankDateTimeFormatError({
2797
+ code: "invalid_locale",
2798
+ field: "locale",
2799
+ value: typeof value === "string" ? value : ""
2800
+ });
2801
+ }
2802
+ }
2803
+ function assertStringTimeZone(value) {
2804
+ if (typeof value !== "string") {
2805
+ throw new RiverbankDateTimeFormatError({
2806
+ code: "invalid_time_zone",
2807
+ field: "timeZone",
2808
+ value: ""
2809
+ });
2810
+ }
2811
+ }
2812
+ function fromCoreParseError(error) {
2813
+ switch (error.reason) {
2814
+ case "invalid_starts_at": return new RiverbankDateTimeFormatError({
2815
+ code: error.reason,
2816
+ field: error.field,
2817
+ value: typeof error.value === "string" ? error.value : ""
2818
+ });
2819
+ case "invalid_ends_at": return new RiverbankDateTimeFormatError({
2820
+ code: error.reason,
2821
+ field: error.field,
2822
+ value: typeof error.value === "string" ? error.value : ""
2823
+ });
2824
+ case "invalid_time_zone": return new RiverbankDateTimeFormatError({
2825
+ code: error.reason,
2826
+ field: error.field,
2827
+ value: error.value
2828
+ });
2829
+ default: return assertNever$3(error);
2830
+ }
2831
+ }
2832
+ function formatErrorMessage(details) {
2833
+ switch (details.code) {
2834
+ case "invalid_starts_at": return `Expected startsAt to be a valid offset-aware ISO datetime, received "${details.value}"`;
2835
+ case "invalid_ends_at": return `Expected endsAt to be a valid offset-aware ISO datetime, received "${details.value}"`;
2836
+ case "missing_ends_at": return "Expected endsAt to be provided for a scheduled range";
2837
+ case "invalid_time_zone": return `Expected timeZone to be a valid IANA timezone, received "${details.value}"`;
2838
+ case "invalid_locale": return `Expected locale to be a supported BCP 47 locale, received "${details.value}"`;
2839
+ case "end_before_start": return `Expected endsAt to be on or after startsAt, received "${details.value}"`;
2840
+ default: return assertNever$3(details);
2841
+ }
2842
+ }
2843
+ function assertNever$3(value) {
2844
+ throw new Error(`Unhandled date-time variant: ${String(value)}`);
2845
+ }
2846
+
2847
+ //#endregion
2848
+ //#region ../core/src/site-terms.ts
2849
+ function isSiteTermsMode(value) {
2850
+ return value === "site" || value === "off";
2851
+ }
2852
+ function siteTermsApplies(operation, items) {
2853
+ if (operation === "join_waitlist" || operation === "pay_existing_order") return false;
2854
+ return items.some((item) => item.kind === "event" || item.kind === "appointment" ? item.siteTermsMode === "site" : true);
2855
+ }
2856
+ /** History, rather than the head, survives stopping and re-publishing. */
2857
+ function nextSiteTermsVersionNumbering(history, askAgain) {
2858
+ const latest = history.reduce((highest, version) => highest === null || version.number > highest.number ? version : highest, null);
2859
+ return latest !== null ? {
2860
+ number: latest.number + 1,
2861
+ generation: latest.generation + (askAgain ? 1 : 0)
2862
+ } : {
2863
+ number: 1,
2864
+ generation: 1
2865
+ };
2866
+ }
2867
+ function qualifyingSiteTermsAcceptance(input) {
2868
+ return input.identity.kind === "verified" && input.prior !== null && input.prior.customerId === input.identity.customerId && input.prior.termsId === input.state.termsId && input.prior.generation === input.state.current.generation ? input.prior : null;
2869
+ }
2870
+ function admitSiteTerms(input) {
2871
+ if (!input.applies || input.state.kind === "none") return ok({ kind: "not_asked" });
2872
+ const version = input.state.current;
2873
+ const acceptance = qualifyingSiteTermsAcceptance({
2874
+ ...input,
2875
+ state: input.state
2876
+ });
2877
+ if (acceptance !== null) return ok({
2878
+ kind: "already_agreed",
2879
+ version,
2880
+ reliedOn: acceptance
2881
+ });
2882
+ if (input.submission.shown.kind !== "checkbox" || input.submission.shown.versionId !== version.id) {
2883
+ return err({
2884
+ kind: "terms_changed",
2885
+ current: version
2886
+ });
2887
+ }
2888
+ if (!input.submission.agreed) return err({
2889
+ kind: "agreement_missing",
2890
+ current: version
2891
+ });
2892
+ return ok({
2893
+ kind: "ticked",
2894
+ version,
2895
+ agreedAt: input.now,
2896
+ identifiedBy: input.identity.kind === "verified" ? "signed_in" : "checkout_email"
2897
+ });
2898
+ }
2899
+ /** Customer copy for a checkout's terms box, shared by the client check and the server's rejection. */
2900
+ const SITE_TERMS_NOT_TICKED_MESSAGE = "Please agree to the booking terms to continue.";
2901
+ function siteTermsChangedMessage(businessName) {
2902
+ return `${businessName} updated their booking terms while you were booking. Please have a look and tick the box again.`;
2903
+ }
2904
+
2332
2905
  //#endregion
2333
2906
  //#region ../core/src/appointment-service-categories.ts
2334
2907
  /**
@@ -3735,6 +4308,32 @@ function parsePublicSocialLinks(value) {
3735
4308
  }
3736
4309
  return ok(links);
3737
4310
  }
4311
+ /** Historical stored profiles read absent description as null. */
4312
+ function parsePublicBusinessDescription(value) {
4313
+ const description = trimmedOptionalString(value);
4314
+ return description === undefined || description !== null && description.length > 2e3 ? err({
4315
+ code: "invalid_business_description",
4316
+ path: "businessDescription",
4317
+ message: "Use a business description of 2,000 characters or fewer."
4318
+ }) : ok(description);
4319
+ }
4320
+ /** Public copy and visibility preferences share historical absent-field defaults. */
4321
+ function parsePublicProfilePresentation(value) {
4322
+ const businessDescription = parsePublicBusinessDescription(value.businessDescription);
4323
+ if (!businessDescription.ok) return businessDescription;
4324
+ const addressVisibility = parseAddressVisibility(value.addressVisibility);
4325
+ if (!addressVisibility.ok) return addressVisibility;
4326
+ const contactVisibility = parseContactVisibility(value.contactVisibility);
4327
+ if (!contactVisibility.ok) return contactVisibility;
4328
+ const premises = parsePremises(value.premises);
4329
+ if (!premises.ok) return premises;
4330
+ return ok({
4331
+ businessDescription: businessDescription.value,
4332
+ addressVisibility: addressVisibility.value,
4333
+ contactVisibility: contactVisibility.value,
4334
+ premises: premises.value
4335
+ });
4336
+ }
3738
4337
  function parsePublicProfile(value) {
3739
4338
  if (!isRecord$8(value)) {
3740
4339
  return err({
@@ -3743,25 +4342,19 @@ function parsePublicProfile(value) {
3743
4342
  message: "Public profile must be an object."
3744
4343
  });
3745
4344
  }
4345
+ const presentation = parsePublicProfilePresentation(value);
4346
+ if (!presentation.ok) return presentation;
3746
4347
  const contact = value.contact === null ? ok(null) : parsePublicContactDetails(value.contact);
3747
4348
  if (!contact.ok) return contact;
3748
4349
  const openingHours = value.openingHours === null ? ok(null) : parseWeeklyOpeningHours(value.openingHours);
3749
4350
  if (!openingHours.ok) return openingHours;
3750
4351
  const socialLinks = parsePublicSocialLinks(value.socialLinks);
3751
4352
  if (!socialLinks.ok) return socialLinks;
3752
- const addressVisibility = parseAddressVisibility(value.addressVisibility);
3753
- if (!addressVisibility.ok) return addressVisibility;
3754
- const contactVisibility = parseContactVisibility(value.contactVisibility);
3755
- if (!contactVisibility.ok) return contactVisibility;
3756
- const premises = parsePremises(value.premises);
3757
- if (!premises.ok) return premises;
3758
4353
  return ok({
4354
+ ...presentation.value,
3759
4355
  contact: contact.value,
3760
- contactVisibility: contactVisibility.value,
3761
4356
  openingHours: openingHours.value,
3762
- socialLinks: socialLinks.value,
3763
- addressVisibility: addressVisibility.value,
3764
- premises: premises.value
4357
+ socialLinks: socialLinks.value
3765
4358
  });
3766
4359
  }
3767
4360
  function parseFieldVisibility(value, path) {
@@ -3855,6 +4448,7 @@ function parseAddressVisibility(value) {
3855
4448
  }
3856
4449
  function emptyPublicProfile() {
3857
4450
  return {
4451
+ businessDescription: null,
3858
4452
  contact: null,
3859
4453
  contactVisibility: {
3860
4454
  phone: "shown",
@@ -4405,6 +4999,18 @@ function parseFooterEngagementSectionValue(value, sectionBase, path) {
4405
4999
  }
4406
5000
  }
4407
5001
 
5002
+ //#endregion
5003
+ //#region ../core/src/footer-navigation-layout.ts
5004
+ const footerNavigationLayouts = ["stack", "inline-wrap"];
5005
+ const defaultFooterNavigationLayout = "inline-wrap";
5006
+ function normalizeFooterNavigationLayout(value) {
5007
+ return value === "stack" ? "stack" : defaultFooterNavigationLayout;
5008
+ }
5009
+ /** The arrangement chooses presentation; the stored preference remains intact. */
5010
+ function effectiveFooterNavigationLayout(arrangement, stored) {
5011
+ return arrangement === "centred" ? "inline-wrap" : stored;
5012
+ }
5013
+
4408
5014
  //#endregion
4409
5015
  //#region ../core/src/footer-arrangement.ts
4410
5016
  const sectionKinds = [
@@ -4419,85 +5025,119 @@ const sectionKinds = [
4419
5025
  "legal-navigation",
4420
5026
  "legal"
4421
5027
  ];
4422
- const sectionKindOrder = new Map(sectionKinds.map((kind, index) => [kind, index]));
4423
- const visualGroupOrder = [
5028
+ const centredSectionKinds = [
5029
+ "brand",
5030
+ "supporting-text",
5031
+ "navigation",
5032
+ "contact",
5033
+ "business-address",
5034
+ "opening-hours",
5035
+ "social-links",
5036
+ "engagement",
5037
+ "legal",
5038
+ "legal-navigation"
5039
+ ];
5040
+ /** Section ownership is exhaustive for both arrangements; content never changes placement. */
5041
+ const rowSectionGroups = {
5042
+ brand: "identity",
5043
+ "supporting-text": "identity",
5044
+ "social-links": "identity",
5045
+ navigation: "main-navigation",
5046
+ "business-address": "visit",
5047
+ contact: "visit",
5048
+ "opening-hours": "visit",
5049
+ engagement: "engagement",
5050
+ "legal-navigation": "legal-ending",
5051
+ legal: "legal-ending"
5052
+ };
5053
+ const centredSectionGroups = {
5054
+ ...rowSectionGroups,
5055
+ "social-links": "social"
5056
+ };
5057
+ const rowGroupOrder = [
4424
5058
  "identity",
4425
5059
  "main-navigation",
4426
5060
  "visit",
4427
5061
  "engagement",
4428
5062
  "legal-ending"
4429
5063
  ];
4430
- function groupFor(kind) {
4431
- switch (kind) {
4432
- case "brand":
4433
- case "supporting-text":
4434
- case "social-links": return "identity";
4435
- case "navigation": return "main-navigation";
4436
- case "business-address":
4437
- case "contact":
4438
- case "opening-hours": return "visit";
4439
- case "engagement": return "engagement";
4440
- case "legal-navigation":
4441
- case "legal": return "legal-ending";
4442
- }
5064
+ const centredGroupOrder = [
5065
+ "identity",
5066
+ "main-navigation",
5067
+ "visit",
5068
+ "social",
5069
+ "engagement",
5070
+ "legal-ending"
5071
+ ];
5072
+ const groupSlots = {
5073
+ identity: "head",
5074
+ "main-navigation": "navigation",
5075
+ visit: "contact",
5076
+ social: "social",
5077
+ engagement: "panel",
5078
+ "legal-ending": "base"
5079
+ };
5080
+ const regionSlots = {
5081
+ "engagement-row": "panel",
5082
+ main: "stack",
5083
+ bottom: "base"
5084
+ };
5085
+ /** One policy owner for the served footer and its composition preview. */
5086
+ function footerArrangementPolicy(input) {
5087
+ const arrangement = input.arrangement ?? "simple";
5088
+ return {
5089
+ arrangement,
5090
+ kind: arrangement === "centred" || input.legalRegion === "separate" ? "separate-legal-region" : "integrated-legal",
5091
+ navigationLayout: effectiveFooterNavigationLayout(arrangement, normalizeFooterNavigationLayout(input.storedNavigationLayout))
5092
+ };
4443
5093
  }
4444
- function buildGroups(sections, navigationLayout) {
4445
- const groups = new Map();
4446
- for (const section of sections) {
4447
- const kind = groupFor(section.kind);
4448
- groups.set(kind, [...groups.get(kind) ?? [], section]);
4449
- }
4450
- return visualGroupOrder.flatMap((kind) => {
4451
- const groupSections = groups.get(kind);
4452
- return groupSections === undefined || groupSections.length === 0 ? [] : [{
5094
+ function buildGroups(sections, policy) {
5095
+ const centred = policy.arrangement === "centred";
5096
+ const order = centred ? centredGroupOrder : rowGroupOrder;
5097
+ const owners = centred ? centredSectionGroups : rowSectionGroups;
5098
+ const sectionOrder = centred ? centredSectionKinds : sectionKinds;
5099
+ return order.flatMap((kind) => {
5100
+ const groupSections = sections.filter((section) => owners[section.kind] === kind).sort((left, right) => sectionOrder.indexOf(left.kind) - sectionOrder.indexOf(right.kind));
5101
+ return groupSections.length === 0 ? [] : [{
4453
5102
  kind,
4454
- width: kind === "main-navigation" && navigationLayout === "inline-wrap" ? "full-row" : "column",
4455
- sections: [...groupSections].sort((left, right) => (sectionKindOrder.get(left.kind) ?? Number.MAX_SAFE_INTEGER) - (sectionKindOrder.get(right.kind) ?? Number.MAX_SAFE_INTEGER))
5103
+ width: kind === "main-navigation" && policy.navigationLayout === "inline-wrap" ? "full-row" : "column",
5104
+ sections: groupSections,
5105
+ ...centred ? { slot: groupSlots[kind] } : {}
4456
5106
  }];
4457
5107
  });
4458
5108
  }
4459
5109
  function membershipKey(sections) {
4460
5110
  const tokens = sections.map((section) => ({
4461
- rank: sectionKindOrder.get(section.kind) ?? Number.MAX_SAFE_INTEGER,
5111
+ rank: sectionKinds.indexOf(section.kind),
4462
5112
  value: section.kind === "engagement" ? `engagement:${section.treatment}` : section.kind
4463
- }));
4464
- tokens.sort((left, right) => left.rank - right.rank);
5113
+ })).sort((left, right) => left.rank - right.rank);
4465
5114
  return tokens.map(({ value }) => value).join("+") || "empty";
4466
5115
  }
5116
+ function renderRegion(kind, groups, policy) {
5117
+ if (groups.length === 0) return [];
5118
+ const columns = kind !== "main" || policy.arrangement === "centred" ? 1 : Math.max(1, Math.min(groups.filter((group) => group.width === "column").length, 5));
5119
+ return [{
5120
+ kind,
5121
+ columns,
5122
+ groups,
5123
+ ...policy.arrangement === "centred" ? { slot: regionSlots[kind] } : {}
5124
+ }];
5125
+ }
4467
5126
  function planFooterArrangement(renderableSections, policy) {
4468
5127
  const selectedSections = renderableSections.map(({ section }) => section);
4469
- const groups = buildGroups(selectedSections, policy.navigationLayout);
4470
- const engagement = selectedSections.find((section) => section.kind === "engagement");
4471
- const separateEngagement = engagement?.treatment === "separate-row" ? groups.filter((group) => group.kind === "engagement") : [];
4472
- const layoutGroups = separateEngagement.length === 0 ? groups : groups.filter((group) => group.kind !== "engagement");
4473
- const legalGroups = layoutGroups.filter((group) => group.kind === "legal-ending");
4474
- const unsortedMainGroups = policy.kind === "separate-legal-region" ? layoutGroups.filter((group) => group.kind !== "legal-ending") : layoutGroups;
4475
- const mainGroups = [...unsortedMainGroups.filter((group) => group.width === "column"), ...unsortedMainGroups.filter((group) => group.width === "full-row")];
4476
- const regions = [];
4477
- if (separateEngagement.length > 0) {
4478
- regions.push({
4479
- kind: "engagement-row",
4480
- columns: 1,
4481
- groups: separateEngagement
4482
- });
4483
- }
4484
- if (mainGroups.length > 0) {
4485
- regions.push({
4486
- kind: "main",
4487
- columns: Math.max(1, Math.min(mainGroups.filter((group) => group.width === "column").length, 5)),
4488
- groups: mainGroups
4489
- });
4490
- }
4491
- if (policy.kind === "separate-legal-region" && legalGroups.length > 0) {
4492
- regions.push({
4493
- kind: "bottom",
4494
- columns: 1,
4495
- groups: legalGroups
4496
- });
4497
- }
5128
+ const groups = buildGroups(selectedSections, policy);
5129
+ const separateEngagement = groups.filter((group) => group.kind === "engagement" && group.sections.some((section) => section.kind === "engagement" && section.treatment === "separate-row"));
5130
+ const layoutGroups = groups.filter((group) => !separateEngagement.includes(group));
5131
+ const legalGroups = policy.kind === "separate-legal-region" ? layoutGroups.filter((group) => group.kind === "legal-ending") : [];
5132
+ const unsortedMainGroups = layoutGroups.filter((group) => !legalGroups.includes(group));
5133
+ const mainGroups = policy.arrangement === "centred" ? unsortedMainGroups : [...unsortedMainGroups.filter((group) => group.width === "column"), ...unsortedMainGroups.filter((group) => group.width === "full-row")];
4498
5134
  return {
4499
5135
  membershipKey: membershipKey(selectedSections),
4500
- regions,
5136
+ regions: [
5137
+ ...renderRegion("engagement-row", separateEngagement, policy),
5138
+ ...renderRegion("main", mainGroups, policy),
5139
+ ...renderRegion("bottom", legalGroups, policy)
5140
+ ],
4501
5141
  mobileGroups: separateEngagement.length === 0 ? groups : [...separateEngagement, ...layoutGroups]
4502
5142
  };
4503
5143
  }
@@ -4962,14 +5602,6 @@ function normalizeLegacyFooterContentToV1(value, options = {}) {
4962
5602
  });
4963
5603
  }
4964
5604
 
4965
- //#endregion
4966
- //#region ../core/src/footer-navigation-layout.ts
4967
- const footerNavigationLayouts = ["stack", "inline-wrap"];
4968
- const defaultFooterNavigationLayout = "inline-wrap";
4969
- function normalizeFooterNavigationLayout(value) {
4970
- return value === "stack" ? "stack" : defaultFooterNavigationLayout;
4971
- }
4972
-
4973
5605
  //#endregion
4974
5606
  //#region ../core/src/mailing-list-setup.ts
4975
5607
  function isRecord$3(value) {
@@ -5033,6 +5665,7 @@ function resolveCollectedGlobalFields(form) {
5033
5665
  switch (form.fields.preset) {
5034
5666
  case "emailOnly": return phone;
5035
5667
  case "emailName": return ["name", ...phone];
5668
+ case "emailFirstName": return ["first_name", ...phone];
5036
5669
  case "emailFirstLast": return [
5037
5670
  "first_name",
5038
5671
  "last_name",
@@ -5049,6 +5682,7 @@ function resolveRequiredGlobalFields(fields) {
5049
5682
  switch (fields.preset) {
5050
5683
  case "emailOnly": return [];
5051
5684
  case "emailName": return fields.name === "required" ? ["name"] : [];
5685
+ case "emailFirstName": return fields.firstName === "required" ? ["first_name"] : [];
5052
5686
  case "emailFirstLast": return [...fields.firstName === "required" ? ["first_name"] : [], ...fields.lastName === "required" ? ["last_name"] : []];
5053
5687
  case "emailNameCompany": return [...fields.name === "required" ? ["name"] : [], ...fields.company === "required" ? ["company"] : []];
5054
5688
  }
@@ -5253,6 +5887,10 @@ function parseGlobalFields(value) {
5253
5887
  preset: "emailName",
5254
5888
  name: value.name
5255
5889
  } : null;
5890
+ case "emailFirstName": return isRequirement(value.firstName) ? {
5891
+ preset: "emailFirstName",
5892
+ firstName: value.firstName
5893
+ } : null;
5256
5894
  case "emailFirstLast": return isRequirement(value.firstName) && isRequirement(value.lastName) ? {
5257
5895
  preset: "emailFirstLast",
5258
5896
  firstName: value.firstName,
@@ -5640,36 +6278,85 @@ function parseMailingListSignupProfile(form, input) {
5640
6278
  }
5641
6279
  } : name;
5642
6280
  }
5643
- case "emailFirstLast": {
6281
+ case "emailFirstName": {
5644
6282
  const firstName = resolveProfileField("first_name", form.fields.firstName, input.firstName);
5645
- if (!firstName.ok) return firstName;
5646
- const lastName = resolveProfileField("last_name", form.fields.lastName, input.lastName);
5647
- return lastName.ok ? {
6283
+ return firstName.ok ? {
5648
6284
  ok: true,
5649
6285
  value: {
5650
- preset: "emailFirstLast",
6286
+ preset: "emailFirstName",
5651
6287
  email,
5652
6288
  phone,
5653
- firstName: firstName.value,
5654
- lastName: lastName.value
6289
+ firstName: firstName.value
5655
6290
  }
5656
- } : lastName;
6291
+ } : firstName;
5657
6292
  }
5658
- case "emailNameCompany": {
5659
- const name = resolveProfileField("name", form.fields.name, input.name);
5660
- if (!name.ok) return name;
5661
- const company = resolveProfileField("company", form.fields.company, input.company);
5662
- return company.ok ? {
5663
- ok: true,
5664
- value: {
5665
- preset: "emailNameCompany",
5666
- email,
5667
- phone,
5668
- name: name.value,
5669
- company: company.value
5670
- }
5671
- } : company;
6293
+ case "emailFirstLast": return parseFirstLastProfile(form.fields, {
6294
+ email,
6295
+ phone
6296
+ }, input);
6297
+ case "emailNameCompany": return parseNameCompanyProfile(form.fields, {
6298
+ email,
6299
+ phone
6300
+ }, input);
6301
+ }
6302
+ }
6303
+ function parseFirstLastProfile(fields, base, input) {
6304
+ const firstName = resolveProfileField("first_name", fields.firstName, input.firstName);
6305
+ if (!firstName.ok) return firstName;
6306
+ const lastName = resolveProfileField("last_name", fields.lastName, input.lastName);
6307
+ return lastName.ok ? {
6308
+ ok: true,
6309
+ value: {
6310
+ ...base,
6311
+ preset: "emailFirstLast",
6312
+ firstName: firstName.value,
6313
+ lastName: lastName.value
5672
6314
  }
6315
+ } : lastName;
6316
+ }
6317
+ function parseNameCompanyProfile(fields, base, input) {
6318
+ const name = resolveProfileField("name", fields.name, input.name);
6319
+ if (!name.ok) return name;
6320
+ const company = resolveProfileField("company", fields.company, input.company);
6321
+ return company.ok ? {
6322
+ ok: true,
6323
+ value: {
6324
+ ...base,
6325
+ preset: "emailNameCompany",
6326
+ name: name.value,
6327
+ company: company.value
6328
+ }
6329
+ } : company;
6330
+ }
6331
+ /** One projection consumed by native signup and external provider adapters. */
6332
+ function mailingListSignupProfileValues(profile) {
6333
+ const empty = {
6334
+ name: null,
6335
+ first_name: null,
6336
+ last_name: null,
6337
+ company: null,
6338
+ phone: profile.phone.tag === "collected" ? profile.phone.value : null
6339
+ };
6340
+ switch (profile.preset) {
6341
+ case "emailOnly": return empty;
6342
+ case "emailName": return {
6343
+ ...empty,
6344
+ name: profile.name.value
6345
+ };
6346
+ case "emailFirstName": return {
6347
+ ...empty,
6348
+ first_name: profile.firstName.value
6349
+ };
6350
+ case "emailFirstLast": return {
6351
+ ...empty,
6352
+ first_name: profile.firstName.value,
6353
+ last_name: profile.lastName.value
6354
+ };
6355
+ case "emailNameCompany": return {
6356
+ ...empty,
6357
+ name: profile.name.value,
6358
+ company: profile.company.value
6359
+ };
5673
6360
  }
5674
6361
  }
5675
6362
  function resolveProfileField(field, requirement, input) {
@@ -6061,7 +6748,7 @@ function resolveExportDateRange(range, today) {
6061
6748
  ok: true,
6062
6749
  window: {
6063
6750
  from: unsafeAsMoneyDateString("1970-01-01"),
6064
- to: unsafeAsMoneyDateString(formatDate(today))
6751
+ to: unsafeAsMoneyDateString(formatDate$1(today))
6065
6752
  }
6066
6753
  };
6067
6754
  }
@@ -6072,8 +6759,8 @@ function resolveExportDateRange(range, today) {
6072
6759
  return {
6073
6760
  ok: true,
6074
6761
  window: {
6075
- from: unsafeAsMoneyDateString(formatDate(from)),
6076
- to: unsafeAsMoneyDateString(formatDate(to))
6762
+ from: unsafeAsMoneyDateString(formatDate$1(from)),
6763
+ to: unsafeAsMoneyDateString(formatDate$1(to))
6077
6764
  }
6078
6765
  };
6079
6766
  }
@@ -6089,7 +6776,7 @@ function parseExportDate(value, field) {
6089
6776
  };
6090
6777
  }
6091
6778
  const date = new Date(`${value}T00:00:00.000Z`);
6092
- if (Number.isNaN(date.getTime()) || formatDate(date) !== value) {
6779
+ if (Number.isNaN(date.getTime()) || formatDate$1(date) !== value) {
6093
6780
  return {
6094
6781
  ok: false,
6095
6782
  error: {
@@ -6115,7 +6802,7 @@ function presetDays(preset) {
6115
6802
  function floorUtcDate(date) {
6116
6803
  return new Date(Date.UTC(date.getUTCFullYear(), date.getUTCMonth(), date.getUTCDate()));
6117
6804
  }
6118
- function formatDate(date) {
6805
+ function formatDate$1(date) {
6119
6806
  return date.toISOString().slice(0, 10);
6120
6807
  }
6121
6808
  function describeExportDateBasis(basis) {
@@ -6582,6 +7269,11 @@ function firstLiveRunSessionStartMs(sessions) {
6582
7269
  const first = orderedLiveRunSessions(sessions)[0];
6583
7270
  return first === undefined ? null : sessionStartMs(first);
6584
7271
  }
7272
+ /** The next surviving session at or after the render clock, not the first week. */
7273
+ function nextLiveRunSessionStartMs(sessions, now) {
7274
+ const next = orderedLiveRunSessions(sessions).find((session) => sessionStartMs(session) >= now.getTime());
7275
+ return next === undefined ? null : sessionStartMs(next);
7276
+ }
6585
7277
  /**
6586
7278
  * Booking stays open until the run's first surviving session has started.
6587
7279
  *
@@ -7579,4 +8271,4 @@ function mergeBag(stored, patch, options) {
7579
8271
  }
7580
8272
 
7581
8273
  //#endregion
7582
- export { toBookingTimeZone as $, unsafeAsCheckoutLineId as $t, footerNavigationLayouts as A, parsePositiveCents as At, normalizeLocationAddressText as B, parseCustomerId as Bt, parseMailingListBlockId as C, unsafeAsUploadSessionId as Cn, publicEventPricingType as Ct, assessMailingListTargeting as D, mapNonEmptyArray as Dt, parseMailingListRuntimeContext as E, ok as En, fromArray as Et, sameMailingListPlacementTargeting as F, formatCurrencyAmount as Ft, formatBookingDateParts as G, parseMembershipId as Gt, selectCategoryView as H, parseDatedOfferingCapacityHoldId as Ht, isCanonicalRichTextDoc as I, parseCurrencyCode as It, formatLocalScheduleTime as J, parseSiteId as Jt, formatBookingTime as K, parsePageId as Kt, parsePublicContactDetails as L, assertNever as Lt, parseMailingListPlacementTargeting as M, toNonNegativeCents as Mt, DEFAULT_MAILING_LIST_TARGETING as N, unsafeAsNonNegativeCents as Nt, DEFAULT_MAILING_LIST_SUCCESS_MESSAGE as O, isNonNegativeWholeCents as Ot, planMailingListChoicePresentation as P, unsafeAsPositiveCents as Pt, parseUtcIsoDateTimeString as Q, unsafeAsBookingSessionId as Qt, assertAllowedPublicUrl as R, formatDurationUnitLabel as Rt, buildMailingListSignupBlockSourceRef as S, unsafeAsStoragePath as Sn, publicEventPricingOptions as St, parseMailingListSignedSourceToken as T, err as Tn, resolvePricingOptionsPriceDisplay as Tt, buildBookingUtcDateWindow as U, parseEventSeriesId as Ut, toLocationCoordinates as V, parseDatedOfferingCapacityHoldAuditRef as Vt, formatBookingDate as W, parseMediaAssetId as Wt, getLocalScheduleDate as X, unsafeAsAppointmentResourceId as Xt, getBookingLocalDateKey as Y, parseUserId as Yt, getLocalScheduleTime as Z, unsafeAsBookingRequirementId as Zt, resolveWholeSeriesBookingPhase as _, unsafeAsSiteFontId as _n, formatBookingPriceDisplay as _t, canonicalizeConfiguredBookingVerticalConfig as a, unsafeAsEventOfferingId as an, isSettleableFlexibleBalanceContractStatus as at, parseManagementKey as b, unsafeAsStaffMemberId as bn, publicEventPricingCurrency as bt, resolveSiteVerticalBookingConfig as c, unsafeAsMediaAssetId as cn, clampConfiguredAmount as ct, ORDINARY_EVENT_PRESET_VALUES as d, unsafeAsPassId as dn, getBookingInstalmentProtectedDepositAmountCents as dt, unsafeAsDeclaredMimeType as en, toScheduledBookingDateRange as et, SURFACE_EVENT_PRESET_VALUES as f, unsafeAsPositiveByteCount as fn, omitZeroAmountFutureRows as ft, orderedLiveRunSessions as g, unsafeAsSafeDownloadFilename as gn, parseEventPageSurfaceSubject as gt, CANCELLED_SESSION_STATUS as h, unsafeAsProductId as hn, resolveFlexibleBalanceInitialPaymentCents as ht, areConfiguredBookingVerticalConfigsEqual as i, unsafeAsEventInstanceId as in, isLiveOrderInstalmentPlanStatus as it, parseFooterCompositionV1 as j, toCents as jt, parseMailingListProviderScope as k, parseNonNegativeCents as kt, DEFAULT_EVENT_TYPE_PRESETS as l, unsafeAsOccurrenceId as ln, getBookingInstalmentDepositRefundability as lt, publicReadFreshnessSeconds as m, unsafeAsPricingOptionId as mn, resolveFlexibleBalanceDueAt as mt, EVENT_PRESET_VOCABULARY as n, unsafeAsDetectedMimeType as nn, parseBookingTimeZone as nt, parseVerticalId as o, unsafeAsEventSeriesId as on, isSettleableOrderInstalmentPlanStatus as ot, WHOLE_SERIES_EVENT_PRESET_VALUES as p, unsafeAsPositiveSeconds as pn, splitAmountWithRemainderInFirst as pt, formatLocalScheduleDate as q, parseRetiredCourseId as qt, NAVIGATION_LOCATIONS as r, unsafeAsEntitlementId as rn, validateBookingPaymentOptionKinds as rt, parseVerticalPresetId as s, unsafeAsFileChecksumSha256 as sn, addIntervalPreservingMonthAnchor as st, resolveEventCatalogue as t, unsafeAsDemoCapability as tn, toScheduledBookingDateTime as tt, EVENT_PRESET_VALUES as u, unsafeAsOriginalFilename as un, getBookingInstalmentPlanIntervalCount as ut, createSecret as v, unsafeAsSiteId as vn, formatEventPricing as vt, parseMailingListContentVersionId as w, unsafeAsUserId as wn, resolveExactOrFreePriceDisplay as wt, stableJsonStringify as x, unsafeAsStorageBucketName as xn, publicEventPricingDisplay as xt, exposeSecret as y, unsafeAsSiteScopedStoragePath as yn, isPublicEventPricingOptionDisplayable as yt, isPresent as z, parseApiKeyId as zt };
8274
+ export { getBookingLocalDateKey as $, parsePageId as $t, assessMailingListTargeting as A, unsafeAsStoragePath as An, resolveExactOrFreePriceDisplay as At, parsePublicContactDetails as B, unsafeAsNonNegativeCents as Bt, stableJsonStringify as C, unsafeAsProductId as Cn, formatBookingPriceDisplay as Ct, parseMailingListContentVersionId as D, unsafeAsSiteScopedStoragePath as Dn, publicEventPricingDisplay as Dt, parseMailingListBlockId as E, unsafeAsSiteId as En, publicEventPricingCurrency as Et, parseMailingListPlacementTargeting as F, isNonNegativeWholeCents as Ft, selectCategoryView as G, formatDurationUnitLabel as Gt, isPresent as H, formatCurrencyAmount as Ht, DEFAULT_MAILING_LIST_TARGETING as I, parseNonNegativeCents as It, formatBookingDate as J, parseDatedOfferingCapacityHoldAuditRef as Jt, inferSiteDateLocale as K, parseApiKeyId as Kt, planMailingListChoicePresentation as L, parsePositiveCents as Lt, parseMailingListProviderScope as M, unsafeAsUserId as Mn, fromArray as Mt, parseFooterCompositionV1 as N, err as Nn, mapNonEmptyArray as Nt, parseMailingListSignedSourceToken as O, unsafeAsStaffMemberId as On, publicEventPricingOptions as Ot, footerNavigationLayouts as P, ok as Pn, mapReadonlyNonEmptyArray as Pt, formatLocalScheduleTime as Q, parseMembershipId as Qt, sameMailingListPlacementTargeting as R, toCents as Rt, parseManagementKey as S, unsafeAsPricingOptionId as Sn, parseEventPageSurfaceSubject as St, buildMailingListSignupBlockSourceRef as T, unsafeAsSiteFontId as Tn, isPublicEventPricingOptionDisplayable as Tt, normalizeLocationAddressText as U, parseCurrencyCode as Ut, assertAllowedPublicUrl as V, unsafeAsPositiveCents as Vt, toLocationCoordinates as W, assertNever as Wt, formatBookingTime as X, parseEventSeriesId as Xt, formatBookingDateParts as Y, parseDatedOfferingCapacityHoldId as Yt, formatLocalScheduleDate as Z, parseMediaAssetId as Zt, orderedLiveRunSessions as _, unsafeAsOccurrenceId as _n, omitZeroAmountFutureRows as _t, canonicalizeConfiguredBookingVerticalConfig as a, unsafeAsBookingRequirementId as an, toScheduledBookingDateRange as at, createSecret as b, unsafeAsPositiveByteCount as bn, resolveFlexibleBalanceInitialPaymentCents as bt, resolveSiteVerticalBookingConfig as c, unsafeAsDeclaredMimeType as cn, validateBookingPaymentOptionKinds as ct, ORDINARY_EVENT_PRESET_VALUES as d, unsafeAsEntitlementId as dn, isSettleableOrderInstalmentPlanStatus as dt, parseRetiredCourseId as en, getLocalScheduleDate as et, SURFACE_EVENT_PRESET_VALUES as f, unsafeAsEventInstanceId as fn, addIntervalPreservingMonthAnchor as ft, nextLiveRunSessionStartMs as g, unsafeAsMediaAssetId as gn, getBookingInstalmentProtectedDepositAmountCents as gt, CANCELLED_SESSION_STATUS as h, unsafeAsFileChecksumSha256 as hn, getBookingInstalmentPlanIntervalCount as ht, areConfiguredBookingVerticalConfigsEqual as i, unsafeAsAppointmentResourceId as in, toBookingTimeZone as it, DEFAULT_MAILING_LIST_SUCCESS_MESSAGE as j, unsafeAsUploadSessionId as jn, resolvePricingOptionsPriceDisplay as jt, parseMailingListRuntimeContext as k, unsafeAsStorageBucketName as kn, publicEventPricingType as kt, DEFAULT_EVENT_TYPE_PRESETS as l, unsafeAsDemoCapability as ln, isLiveOrderInstalmentPlanStatus as lt, publicReadFreshnessSeconds as m, unsafeAsEventSeriesId as mn, getBookingInstalmentDepositRefundability as mt, EVENT_PRESET_VOCABULARY as n, parseSiteTermsVersionId as nn, localScheduleTimePattern as nt, parseVerticalId as o, unsafeAsBookingSessionId as on, toScheduledBookingDateTime as ot, WHOLE_SERIES_EVENT_PRESET_VALUES as p, unsafeAsEventOfferingId as pn, clampConfiguredAmount as pt, buildBookingUtcDateWindow as q, parseCustomerId as qt, NAVIGATION_LOCATIONS as r, parseUserId as rn, parseUtcIsoDateTimeString as rt, parseVerticalPresetId as s, unsafeAsCheckoutLineId as sn, parseBookingTimeZone as st, resolveEventCatalogue as t, parseSiteId as tn, getLocalScheduleTime as tt, EVENT_PRESET_VALUES as u, unsafeAsDetectedMimeType as un, isSettleableFlexibleBalanceContractStatus as ut, resolveWholeSeriesBookingPhase as v, unsafeAsOriginalFilename as vn, splitAmountWithRemainderInFirst as vt, slugify as w, unsafeAsSafeDownloadFilename as wn, formatEventPricing as wt, exposeSecret as x, unsafeAsPositiveSeconds as xn, deriveTotalTicketQuantity as xt, resolveWholeSeriesRunLifecycle as y, unsafeAsPassId as yn, resolveFlexibleBalanceDueAt as yt, isCanonicalRichTextDoc as z, toNonNegativeCents as zt };