riverbankcms 0.17.1 → 0.19.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.
@@ -1,4 +1,6 @@
1
1
  #!/usr/bin/env node
2
+ import { z } from "zod";
3
+
2
4
  //#region ../core/src/result.ts
3
5
  function ok(value) {
4
6
  return {
@@ -1476,7 +1478,7 @@ function resolveFlexibleBalanceDueAt(input) {
1476
1478
  switch (input.dueDateRule.kind) {
1477
1479
  case "relative_to_start_date": return subtractDaysFromIso(input.bookingStartAt, input.dueDateRule.daysBeforeStart);
1478
1480
  case "fixed_calendar_date": return input.dueDateRule.dueAt;
1479
- default: return assertNever$4(input.dueDateRule);
1481
+ default: return assertNever$5(input.dueDateRule);
1480
1482
  }
1481
1483
  }
1482
1484
  function calculateFlexibleBalanceMinimumPaymentCents(input) {
@@ -1531,7 +1533,7 @@ function resolveFlexibleBalancePaymentEligibility(input) {
1531
1533
  suggestedPayFullAmountCents: input.remainingBalanceCents
1532
1534
  };
1533
1535
  }
1534
- default: return assertNever$4(input.status);
1536
+ default: return assertNever$5(input.status);
1535
1537
  }
1536
1538
  }
1537
1539
  function isFlexibleBalanceReminderSuppressed(input) {
@@ -1567,7 +1569,7 @@ function buildFlexibleBalanceReminderSchedule(input) {
1567
1569
  })
1568
1570
  }));
1569
1571
  }
1570
- function assertNever$4(value) {
1572
+ function assertNever$5(value) {
1571
1573
  throw new Error(`Unexpected value: ${String(value)}`);
1572
1574
  }
1573
1575
 
@@ -1599,6 +1601,10 @@ function createOriginalPaymentRefundCommand(options) {
1599
1601
  protectedDepositOverride
1600
1602
  };
1601
1603
  }
1604
+ /** Narrow an already typed schedule-write response by its receipt discriminant. */
1605
+ function isAcceptedEventScheduleUpdate(response) {
1606
+ return "kind" in response && response.kind === "accepted_unavailable";
1607
+ }
1602
1608
 
1603
1609
  //#endregion
1604
1610
  //#region ../core/src/booking-instalments.ts
@@ -1607,7 +1613,7 @@ function isDepositBearingBookingInstalmentPlan(plan) {
1607
1613
  case "deposit_plus_one_remainder":
1608
1614
  case "deposit_plus_scheduled_instalments": return true;
1609
1615
  case "equal_scheduled_instalments": return false;
1610
- default: return assertNever$3(plan);
1616
+ default: return assertNever$4(plan);
1611
1617
  }
1612
1618
  }
1613
1619
  function getBookingInstalmentDepositRefundability(plan) {
@@ -1640,7 +1646,7 @@ function addIntervalPreservingMonthAnchor(isoString, intervalCount, intervalUnit
1640
1646
  date.setUTCDate(date.getUTCDate() + intervalCount * 7);
1641
1647
  return date.toISOString();
1642
1648
  case "month": return addMonthsPreservingAnchorDay(date, intervalCount).toISOString();
1643
- default: return assertNever$3(intervalUnit);
1649
+ default: return assertNever$4(intervalUnit);
1644
1650
  }
1645
1651
  }
1646
1652
  function getBookingInstalmentPlanIntervalCount(plan) {
@@ -1648,7 +1654,7 @@ function getBookingInstalmentPlanIntervalCount(plan) {
1648
1654
  case "deposit_plus_one_remainder": return plan.remainderDueCount;
1649
1655
  case "deposit_plus_scheduled_instalments":
1650
1656
  case "equal_scheduled_instalments": return plan.intervalCount;
1651
- default: return assertNever$3(plan);
1657
+ default: return assertNever$4(plan);
1652
1658
  }
1653
1659
  }
1654
1660
  function omitZeroAmountFutureRows(rows) {
@@ -1672,7 +1678,7 @@ function formatOrderInstalmentStatusLabel(input) {
1672
1678
  case "cancelled": return "Cancelled";
1673
1679
  case "cancelled_as_paid_early": return "Paid early";
1674
1680
  case "refunded": return "Refunded";
1675
- default: return assertNever$3(input.status);
1681
+ default: return assertNever$4(input.status);
1676
1682
  }
1677
1683
  }
1678
1684
  const NEXT_DUE_STATUSES = new Set(["scheduled", "failed"]);
@@ -1693,7 +1699,7 @@ function addMonthsPreservingAnchorDay(date, intervalCount) {
1693
1699
  nextMonth.setUTCDate(Math.min(anchorDay, lastDayOfTargetMonth));
1694
1700
  return nextMonth;
1695
1701
  }
1696
- function assertNever$3(value) {
1702
+ function assertNever$4(value) {
1697
1703
  throw new Error(`Unexpected value: ${String(value)}`);
1698
1704
  }
1699
1705
 
@@ -1828,6 +1834,42 @@ function validateBookingPaymentOptionKinds(kinds) {
1828
1834
  return ok(undefined);
1829
1835
  }
1830
1836
 
1837
+ //#endregion
1838
+ //#region ../core/src/date-time-formatter.ts
1839
+ /** Shared Intl engine, internal to core's date presentation owners. */
1840
+ const FORMATTER_CACHE_LIMIT$1 = 100;
1841
+ const dateTimeFormatterCache = new Map();
1842
+ function getDateTimeFormatter(locale, timeZone, format) {
1843
+ const formatKey = JSON.stringify(Object.entries(format).sort(([left], [right]) => left.localeCompare(right)));
1844
+ const key = `${locale}\u0000${timeZone}\u0000${formatKey}`;
1845
+ const cached = dateTimeFormatterCache.get(key);
1846
+ if (cached !== undefined) {
1847
+ return cached;
1848
+ }
1849
+ if (typeof locale !== "string" || Intl.DateTimeFormat.supportedLocalesOf([locale]).length === 0) {
1850
+ throw new RangeError("An explicit supported locale is required");
1851
+ }
1852
+ if (typeof timeZone !== "string" || timeZone.length === 0) {
1853
+ throw new RangeError("An explicit time zone is required");
1854
+ }
1855
+ const formatter = new Intl.DateTimeFormat(locale, {
1856
+ ...format,
1857
+ timeZone
1858
+ });
1859
+ if (dateTimeFormatterCache.size >= FORMATTER_CACHE_LIMIT$1) {
1860
+ dateTimeFormatterCache.clear();
1861
+ }
1862
+ dateTimeFormatterCache.set(key, formatter);
1863
+ return formatter;
1864
+ }
1865
+ function formatIntlRange(formatter, start, end) {
1866
+ const nativeFormatRange = Reflect.get(formatter, "formatRange");
1867
+ if (typeof nativeFormatRange !== "function") throw new Error("Intl.DateTimeFormat.formatRange is required");
1868
+ const output = Reflect.apply(nativeFormatRange, formatter, [start, end]);
1869
+ if (typeof output !== "string") throw new Error("Intl.DateTimeFormat.formatRange returned a non-string value");
1870
+ return output;
1871
+ }
1872
+
1831
1873
  //#endregion
1832
1874
  //#region ../core/src/booking-time-zone.ts
1833
1875
  const TIME_ZONE_VALIDITY_CACHE_LIMIT = 100;
@@ -1862,7 +1904,7 @@ function parseBookingTimeZone(timeZone) {
1862
1904
 
1863
1905
  //#endregion
1864
1906
  //#region ../core/src/booking-date-time.ts
1865
- const DEFAULT_LOCALE = "en-US";
1907
+ const DEFAULT_LOCALE$1 = "en-US";
1866
1908
  const FALLBACK_TIMEZONE = "UTC";
1867
1909
  const DEFAULT_BOOKING_DATE_TIME_FORMAT = {
1868
1910
  weekday: "short",
@@ -1877,8 +1919,6 @@ const START_OF_DAY = {
1877
1919
  second: 0
1878
1920
  };
1879
1921
  const LOCAL_DATE_PATTERN = /^(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})$/u;
1880
- const DATE_TIME_FORMATTER_CACHE_LIMIT = 100;
1881
- const dateTimeFormatterCache = new Map();
1882
1922
  function getSafeTimeZone(timeZone) {
1883
1923
  const normalized = typeof timeZone === "string" ? timeZone.trim() : "";
1884
1924
  return normalized.length > 0 && isValidBookingTimeZone(normalized) ? normalized : FALLBACK_TIMEZONE;
@@ -1895,24 +1935,7 @@ function formatParseError(error) {
1895
1935
  }
1896
1936
  }
1897
1937
  function formatToPartsLookup(date, locale, timeZone, format) {
1898
- return new Map(getCachedDateTimeFormatter(locale, timeZone, format).formatToParts(date).map((part) => [part.type, part.value]));
1899
- }
1900
- function getCachedDateTimeFormatter(locale, timeZone, format) {
1901
- const formatKey = JSON.stringify(Object.entries(format).sort(([left], [right]) => left.localeCompare(right)));
1902
- const key = `${locale}\u0000${timeZone}\u0000${formatKey}`;
1903
- const cached = dateTimeFormatterCache.get(key);
1904
- if (cached !== undefined) {
1905
- return cached;
1906
- }
1907
- const formatter = new Intl.DateTimeFormat(locale, {
1908
- ...format,
1909
- timeZone
1910
- });
1911
- if (dateTimeFormatterCache.size >= DATE_TIME_FORMATTER_CACHE_LIMIT) {
1912
- dateTimeFormatterCache.clear();
1913
- }
1914
- dateTimeFormatterCache.set(key, formatter);
1915
- return formatter;
1938
+ return new Map(getDateTimeFormatter(locale, timeZone, format).formatToParts(date).map((part) => [part.type, part.value]));
1916
1939
  }
1917
1940
  function getZonedDateTimeParts(date, timeZone) {
1918
1941
  const parts = formatToPartsLookup(date, "en-GB", timeZone, {
@@ -2054,7 +2077,7 @@ function formatBookingInstant(isoString, options) {
2054
2077
  if (Number.isNaN(date.getTime())) {
2055
2078
  return isoString;
2056
2079
  }
2057
- return getCachedDateTimeFormatter(options.locale ?? DEFAULT_LOCALE, getSafeTimeZone(options.timeZone), options.format).format(date);
2080
+ return getDateTimeFormatter(options.locale ?? DEFAULT_LOCALE$1, getSafeTimeZone(options.timeZone), options.format).format(date);
2058
2081
  }
2059
2082
  function formatBookingDate(input, format = {
2060
2083
  weekday: "long",
@@ -2327,6 +2350,469 @@ function localScheduleDateTimeToUtcDate(localDate, localTime, timeZone) {
2327
2350
  return new Date(guess);
2328
2351
  }
2329
2352
 
2353
+ //#endregion
2354
+ //#region ../core/src/date-display.ts
2355
+ /** Site-language policy, shared by customer emails and public event adapters. */
2356
+ function inferSiteDateLocale(siteTimeZone) {
2357
+ if (siteTimeZone === null || siteTimeZone === undefined || siteTimeZone === "") return "en-US";
2358
+ if (siteTimeZone === "Europe/Dublin") return "en-IE";
2359
+ if (siteTimeZone.startsWith("Europe/")) return "en-GB";
2360
+ if (siteTimeZone.startsWith("Australia/")) return "en-AU";
2361
+ if (siteTimeZone === "Pacific/Auckland" || siteTimeZone === "Pacific/Chatham") return "en-NZ";
2362
+ return "en-US";
2363
+ }
2364
+ /** Callers resolve the facts once; all clocks consume the same label decision. */
2365
+ function resolveBookingDisplayContext(input) {
2366
+ return {
2367
+ context: {
2368
+ locale: input.locale,
2369
+ timeZone: input.sessionTimeZone
2370
+ },
2371
+ zone: input.surface === "email" && input.isOnline || input.sessionTimeZone !== input.siteTimeZone ? "always" : "omit"
2372
+ };
2373
+ }
2374
+ const SDK_STANDARD_TIME_FORMAT = {
2375
+ hour: "numeric",
2376
+ minute: "2-digit"
2377
+ };
2378
+ /** Public SDK option policy lives beside the display profile, never in its adapter. */
2379
+ function sdkPresentationProfile(context, dateFormat$1, time = {}) {
2380
+ const compact = time.timePresentation !== "standard" && new Intl.Locale(context.locale).language === "en";
2381
+ const standard = time.timePresentation === "standard" ? time.format ?? SDK_STANDARD_TIME_FORMAT : SDK_STANDARD_TIME_FORMAT;
2382
+ return {
2383
+ kind: "sdk",
2384
+ dateFormat: dateFormat$1,
2385
+ clock: compact ? {
2386
+ kind: "compact",
2387
+ localeSource: "context"
2388
+ } : {
2389
+ kind: "intl",
2390
+ format: standard
2391
+ },
2392
+ zone: "omit"
2393
+ };
2394
+ }
2395
+ function formatDate(value, context, options = {}) {
2396
+ return presentDate(value, context, displayProfile(options, "omit"));
2397
+ }
2398
+ function formatDateRange(start, endInclusive, context, options = {}) {
2399
+ return presentDateRange({
2400
+ kind: "civil",
2401
+ value: projectDate(start, context)
2402
+ }, {
2403
+ kind: "civil",
2404
+ value: projectDate(endInclusive, context)
2405
+ }, context, displayProfile(options, "omit"));
2406
+ }
2407
+ function formatDateTime(value, context, options) {
2408
+ return presentDateTime(value, context, displayProfile(options, options.zone));
2409
+ }
2410
+ function formatDateTimeRange(span, context, options) {
2411
+ return presentDateTimeRange(span, context, displayProfile(options, options.zone));
2412
+ }
2413
+ function formatTime(value, context, options) {
2414
+ return presentTime(value, context, displayProfile({}, options.zone));
2415
+ }
2416
+ function formatTimeRange(start, end, context, options) {
2417
+ return presentTimeRange({
2418
+ start,
2419
+ end
2420
+ }, context, displayProfile({}, options.zone));
2421
+ }
2422
+ /** Sole date projection/typography owner, used by display and scheduled adapters. */
2423
+ function presentDate(value, context, profile) {
2424
+ const date = dateCarrier(value);
2425
+ const zone = value.kind === "civil" ? "UTC" : context.timeZone;
2426
+ const output = getDateTimeFormatter(context.locale, zone, profile.dateFormat).format(date);
2427
+ if (profile.kind === "sdk") return output;
2428
+ const normalized = normalizeDisplay(output);
2429
+ return profile.dateFormat.weekday && new Intl.Locale(context.locale).language === "en" ? normalized.replace(/^([^,]+), /u, "$1 ") : normalized;
2430
+ }
2431
+ function presentDateRange(start, end, context, profile) {
2432
+ if (dateCarrier(end).getTime() < dateCarrier(start).getTime()) throw new RangeError("Date range end must be on or after its start");
2433
+ if (profile.kind === "display" && compareLocalScheduleDates(projectDate(start, context), projectDate(end, context)) === 0) {
2434
+ return presentDate(start, context, profile);
2435
+ }
2436
+ const output = formatIntlRange(getDateTimeFormatter(context.locale, start.kind === "civil" ? "UTC" : context.timeZone, profile.dateFormat), dateCarrier(start), dateCarrier(end));
2437
+ if (profile.kind === "sdk") return output;
2438
+ const startDate = projectDate(start, context);
2439
+ const endDate = projectDate(end, context);
2440
+ const separator = startDate.month === endDate.month && startDate.year === endDate.year ? "–" : " – ";
2441
+ return normalizeDisplay(output).replace(/\s*–\s*/gu, separator);
2442
+ }
2443
+ function presentTime(value, context, profile) {
2444
+ const clock = presentClock(value, context, profile.clock);
2445
+ return profile.zone === "always" ? `${clock} ${zoneLabel(value, context)}` : clock;
2446
+ }
2447
+ function presentDateTime(value, context, profile) {
2448
+ if (profile.clock.kind === "intl") {
2449
+ return getDateTimeFormatter(context.locale, context.timeZone, combinedFormat(profile.dateFormat, profile.clock)).format(new Date(value));
2450
+ }
2451
+ return `${presentDate({
2452
+ kind: "instant",
2453
+ value
2454
+ }, context, profile)}, ${presentTime(value, context, profile)}`;
2455
+ }
2456
+ function presentTimeRange(span, context, profile) {
2457
+ assertOrdered(span.start, span.end);
2458
+ if (profile.clock.kind === "intl") return presentNativeRange(span, context, profile.clock.format);
2459
+ const startClock = presentClock(span.start, context, profile.clock);
2460
+ const endClock = presentClock(span.end, context, profile.clock);
2461
+ const equal = profile.kind === "sdk" ? startClock === endClock : span.start === span.end;
2462
+ const clocks = equal ? startClock : `${startClock}–${endClock}`;
2463
+ const labels = decideRangeLabels(span, context, profile);
2464
+ switch (labels.kind) {
2465
+ case "omit": return clocks;
2466
+ case "shared": return `${clocks} ${labels.label}`;
2467
+ case "endpoints": return `${startClock} ${labels.start} – ${endClock} ${labels.end}`;
2468
+ }
2469
+ }
2470
+ function presentDateTimeRange(span, context, profile) {
2471
+ assertOrdered(span.start, span.end);
2472
+ if (profile.clock.kind === "intl") return presentNativeRange(span, context, combinedFormat(profile.dateFormat, profile.clock));
2473
+ const startDate = getLocalScheduleDate(new Date(span.start), context.timeZone);
2474
+ const endDate = getLocalScheduleDate(new Date(span.end), context.timeZone);
2475
+ if (compareLocalScheduleDates(startDate, endDate) === 0) {
2476
+ return `${presentDate({
2477
+ kind: "instant",
2478
+ value: span.start
2479
+ }, context, profile)}, ${presentTimeRange(span, context, profile)}`;
2480
+ }
2481
+ const labels = decideRangeLabels(span, context, profile);
2482
+ const startProfile = profile.kind === "display" && startDate.year === endDate.year ? {
2483
+ ...profile,
2484
+ dateFormat: {
2485
+ ...profile.dateFormat,
2486
+ year: undefined
2487
+ }
2488
+ } : profile;
2489
+ const start = presentRangeEndpoint(span.start, context, startProfile, labels.kind === "endpoints" ? labels.start : undefined);
2490
+ const end = presentRangeEndpoint(span.end, context, profile, labels.kind === "endpoints" ? labels.end : undefined);
2491
+ const separator = profile.kind === "sdk" ? "–" : " – ";
2492
+ return profile.kind === "sdk" && start === end ? start : `${start}${separator}${end}`;
2493
+ }
2494
+ /** Offset changes override ordinary venue omission for the approved display profile. */
2495
+ function decideRangeLabels(span, context, profile) {
2496
+ if (profile.kind === "sdk") return { kind: "omit" };
2497
+ const offsetsDiffer = offsetToken(span.start, context) !== offsetToken(span.end, context);
2498
+ if (!offsetsDiffer && profile.zone === "omit") return { kind: "omit" };
2499
+ const start = zoneLabel(span.start, context);
2500
+ const end = zoneLabel(span.end, context);
2501
+ return offsetsDiffer || start !== end ? {
2502
+ kind: "endpoints",
2503
+ start,
2504
+ end
2505
+ } : {
2506
+ kind: "shared",
2507
+ label: start
2508
+ };
2509
+ }
2510
+ function presentRangeEndpoint(value, context, profile, label) {
2511
+ const date = presentDate({
2512
+ kind: "instant",
2513
+ value
2514
+ }, context, profile);
2515
+ const time = label === undefined ? presentTime(value, context, profile) : `${presentClock(value, context, profile.clock)} ${label}`;
2516
+ return `${date}, ${time}`;
2517
+ }
2518
+ function presentNativeRange(span, context, format) {
2519
+ return formatIntlRange(getDateTimeFormatter(context.locale, context.timeZone, format), new Date(span.start), new Date(span.end));
2520
+ }
2521
+ function combinedFormat(date, clock) {
2522
+ return {
2523
+ ...date,
2524
+ ...clock.format
2525
+ };
2526
+ }
2527
+ function displayProfile(options, zone) {
2528
+ return {
2529
+ kind: "display",
2530
+ dateFormat: dateFormat(options),
2531
+ clock: {
2532
+ kind: "compact",
2533
+ localeSource: "product"
2534
+ },
2535
+ zone
2536
+ };
2537
+ }
2538
+ function dateCarrier(value) {
2539
+ return value.kind === "civil" ? civilCarrier(value.value) : new Date(value.value);
2540
+ }
2541
+ function presentClock(value, context, clock) {
2542
+ return clock.kind === "compact" ? formatBookingCompactEnglishTime({
2543
+ startsAt: value,
2544
+ timeZone: context.timeZone,
2545
+ locale: clock.localeSource === "product" ? "en-GB" : context.locale
2546
+ }) : getDateTimeFormatter(context.locale, context.timeZone, clock.format).format(new Date(value));
2547
+ }
2548
+ function offsetToken(value, context) {
2549
+ return getDateTimeFormatter("en-GB", context.timeZone, { timeZoneName: "longOffset" }).formatToParts(new Date(value)).find((part) => part.type === "timeZoneName")?.value ?? "";
2550
+ }
2551
+ function formatTimeAgo(value, now, context) {
2552
+ return formatRelative(value, now, context, "past");
2553
+ }
2554
+ function formatTimeUntil(value, now, context) {
2555
+ return formatRelative(value, now, context, "future");
2556
+ }
2557
+ function dateFormat(options) {
2558
+ const style = options.date ?? "long";
2559
+ return {
2560
+ day: style === "numeric" ? "2-digit" : "numeric",
2561
+ month: style === "numeric" ? "2-digit" : style,
2562
+ ...options.year === "omit" ? {} : { year: "numeric" },
2563
+ ...options.weekday ? { weekday: options.weekday } : {}
2564
+ };
2565
+ }
2566
+ function civilCarrier(value) {
2567
+ return new Date(Date.UTC(value.year, value.month - 1, value.day, 12));
2568
+ }
2569
+ function projectDate(value, context) {
2570
+ return value.kind === "civil" ? value.value : getLocalScheduleDate(new Date(value.value), context.timeZone);
2571
+ }
2572
+ function normalizeDisplay(value) {
2573
+ return value.replace(/[\u00a0\u202f]/gu, " ");
2574
+ }
2575
+ function zoneToken(value, context, locale) {
2576
+ return getDateTimeFormatter(locale, context.timeZone, { timeZoneName: "short" }).formatToParts(new Date(value)).find((part) => part.type === "timeZoneName")?.value ?? "";
2577
+ }
2578
+ function zoneLabel(value, context) {
2579
+ const tokens = [zoneToken(value, context, "en-GB"), zoneToken(value, context, "en-US")];
2580
+ const abbreviation = tokens.find((token) => /^[A-Z]{2,6}$/u.test(token));
2581
+ const fallback = context.timeZone.startsWith("Etc/") ? "local time" : `${context.timeZone.split("/").slice(-1)[0]?.replace(/_/gu, " ") ?? context.timeZone} time`;
2582
+ return abbreviation ?? fallback;
2583
+ }
2584
+ function assertOrdered(start, end) {
2585
+ if (new Date(end).getTime() < new Date(start).getTime()) {
2586
+ throw new RangeError("Date/time range end must be on or after its start");
2587
+ }
2588
+ }
2589
+ function calendarDayDifference(value, now, context) {
2590
+ return compareLocalScheduleDates(getLocalScheduleDate(new Date(value), context.timeZone), getLocalScheduleDate(new Date(now), context.timeZone)) / 864e5;
2591
+ }
2592
+ function formatRelative(value, now, context, direction) {
2593
+ const seconds = (new Date(value).getTime() - new Date(now).getTime()) / 1e3;
2594
+ const magnitude = Math.abs(seconds);
2595
+ if (magnitude < 60) return relativeNearNow(context.locale, direction);
2596
+ const days = calendarDayDifference(value, now, context);
2597
+ if (Math.abs(days) > 7) return formatDate({
2598
+ kind: "instant",
2599
+ value
2600
+ }, context);
2601
+ const formatter = new Intl.RelativeTimeFormat(context.locale, { numeric: "auto" });
2602
+ if (days !== 0) return formatter.format(days, "day");
2603
+ const unit = magnitude < 3600 ? "minute" : "hour";
2604
+ const signedCount = Math.sign(seconds) * Math.floor(magnitude / (unit === "minute" ? 60 : 3600));
2605
+ return formatter.format(signedCount, unit);
2606
+ }
2607
+ function relativeNearNow(locale, direction) {
2608
+ if (new Intl.Locale(locale).language === "en") return direction === "past" ? "just now" : "in a moment";
2609
+ return new Intl.RelativeTimeFormat(locale, { numeric: "auto" }).format(0, "second");
2610
+ }
2611
+
2612
+ //#endregion
2613
+ //#region ../core/src/scheduled-date-time.ts
2614
+ const DEFAULT_LOCALE = "en-GB";
2615
+ const FORMATTER_CACHE_LIMIT = 100;
2616
+ const DEFAULT_DATE_FORMAT = {
2617
+ weekday: "long",
2618
+ year: "numeric",
2619
+ month: "long",
2620
+ day: "numeric"
2621
+ };
2622
+ const DEFAULT_RANGE_DATE_FORMAT = {
2623
+ weekday: "short",
2624
+ year: "numeric",
2625
+ month: "short",
2626
+ day: "numeric"
2627
+ };
2628
+ const parsedLocaleCache = new Map();
2629
+ var RiverbankDateTimeFormatError = class extends Error {
2630
+ constructor(details) {
2631
+ super(formatErrorMessage(details));
2632
+ this.name = "RiverbankDateTimeFormatError";
2633
+ this.code = details.code;
2634
+ this.field = details.field;
2635
+ this.value = details.value;
2636
+ }
2637
+ };
2638
+ /** Public contract adapters: admission/options here, all presentation in date-display. */
2639
+ function formatScheduledDate(input, options = {}) {
2640
+ const parsed = parseDateTime(input);
2641
+ return presentDate({
2642
+ kind: "instant",
2643
+ value: parsed.startsAt
2644
+ }, scheduledContext(parsed), sdkPresentationProfile(scheduledContext(parsed), options.format ?? DEFAULT_DATE_FORMAT));
2645
+ }
2646
+ function formatScheduledTime(input, options = {}) {
2647
+ const parsed = parseDateTime(input);
2648
+ return presentTime(parsed.startsAt, scheduledContext(parsed), sdkPresentationProfile(scheduledContext(parsed), {}, options));
2649
+ }
2650
+ function formatScheduledDateTime(input, options = {}) {
2651
+ const parsed = parseDateTime(input);
2652
+ return presentDateTime(parsed.startsAt, scheduledContext(parsed), sdkPresentationProfile(scheduledContext(parsed), options.dateFormat ?? DEFAULT_DATE_FORMAT, options.time));
2653
+ }
2654
+ function formatScheduledDateRange(input, options = {}) {
2655
+ const parsed = parseDateRange(input);
2656
+ return presentDateRange({
2657
+ kind: "instant",
2658
+ value: parsed.startsAt
2659
+ }, {
2660
+ kind: "instant",
2661
+ value: parsed.endsAt
2662
+ }, scheduledContext(parsed), sdkPresentationProfile(scheduledContext(parsed), options.format ?? DEFAULT_RANGE_DATE_FORMAT));
2663
+ }
2664
+ function formatScheduledTimeRange(input, options = {}) {
2665
+ const parsed = parseDateRange(input);
2666
+ return presentTimeRange({
2667
+ start: parsed.startsAt,
2668
+ end: parsed.endsAt
2669
+ }, scheduledContext(parsed), sdkPresentationProfile(scheduledContext(parsed), {}, options));
2670
+ }
2671
+ function formatScheduledDateTimeRange(input, options = {}) {
2672
+ const parsed = parseDateRange(input);
2673
+ return presentDateTimeRange({
2674
+ start: parsed.startsAt,
2675
+ end: parsed.endsAt
2676
+ }, scheduledContext(parsed), sdkPresentationProfile(scheduledContext(parsed), options.dateFormat ?? DEFAULT_RANGE_DATE_FORMAT, options.time));
2677
+ }
2678
+ function scheduledContext(input) {
2679
+ return {
2680
+ locale: input.locale,
2681
+ timeZone: input.timeZone
2682
+ };
2683
+ }
2684
+ function parseDateTime(input) {
2685
+ const locale = parseLocale(input.locale);
2686
+ assertStringTimeZone(input.timeZone);
2687
+ const result = parseScheduledBookingDateTime({
2688
+ ...input,
2689
+ locale: locale.locale
2690
+ });
2691
+ if (result.ok) {
2692
+ return {
2693
+ ...result.value,
2694
+ locale: locale.locale
2695
+ };
2696
+ }
2697
+ throw fromCoreParseError(result);
2698
+ }
2699
+ function parseDateRange(input) {
2700
+ const locale = parseLocale(input.locale);
2701
+ assertStringTimeZone(input.timeZone);
2702
+ assertRequiredEnd(input.endsAt);
2703
+ const result = parseScheduledBookingDateRange({
2704
+ ...input,
2705
+ locale: locale.locale
2706
+ });
2707
+ if (!result.ok) {
2708
+ throw fromCoreParseError(result);
2709
+ }
2710
+ if (result.value.endsAt === null || result.value.endsAt === undefined) {
2711
+ throw new RiverbankDateTimeFormatError({
2712
+ code: "missing_ends_at",
2713
+ field: "endsAt",
2714
+ value: ""
2715
+ });
2716
+ }
2717
+ if (new Date(result.value.endsAt).getTime() < new Date(result.value.startsAt).getTime()) {
2718
+ throw new RiverbankDateTimeFormatError({
2719
+ code: "end_before_start",
2720
+ field: "endsAt",
2721
+ value: input.endsAt
2722
+ });
2723
+ }
2724
+ return {
2725
+ ...result.value,
2726
+ endsAt: result.value.endsAt,
2727
+ locale: locale.locale
2728
+ };
2729
+ }
2730
+ function assertRequiredEnd(value) {
2731
+ if (value === null || value === undefined) {
2732
+ throw new RiverbankDateTimeFormatError({
2733
+ code: "missing_ends_at",
2734
+ field: "endsAt",
2735
+ value: ""
2736
+ });
2737
+ }
2738
+ }
2739
+ function parseLocale(value) {
2740
+ const locale = value === undefined ? DEFAULT_LOCALE : value;
2741
+ if (typeof locale !== "string") {
2742
+ throw new RiverbankDateTimeFormatError({
2743
+ code: "invalid_locale",
2744
+ field: "locale",
2745
+ value: ""
2746
+ });
2747
+ }
2748
+ const cached = parsedLocaleCache.get(locale);
2749
+ if (cached !== undefined) {
2750
+ return cached;
2751
+ }
2752
+ try {
2753
+ const parsed = new Intl.Locale(locale);
2754
+ const canonical = parsed.toString();
2755
+ if (Intl.DateTimeFormat.supportedLocalesOf([canonical]).length === 0) {
2756
+ throw new RangeError("Unsupported locale");
2757
+ }
2758
+ const result = { locale: canonical };
2759
+ if (parsedLocaleCache.size >= FORMATTER_CACHE_LIMIT) {
2760
+ parsedLocaleCache.clear();
2761
+ }
2762
+ parsedLocaleCache.set(locale, result);
2763
+ return result;
2764
+ } catch {
2765
+ throw new RiverbankDateTimeFormatError({
2766
+ code: "invalid_locale",
2767
+ field: "locale",
2768
+ value: typeof value === "string" ? value : ""
2769
+ });
2770
+ }
2771
+ }
2772
+ function assertStringTimeZone(value) {
2773
+ if (typeof value !== "string") {
2774
+ throw new RiverbankDateTimeFormatError({
2775
+ code: "invalid_time_zone",
2776
+ field: "timeZone",
2777
+ value: ""
2778
+ });
2779
+ }
2780
+ }
2781
+ function fromCoreParseError(error) {
2782
+ switch (error.reason) {
2783
+ case "invalid_starts_at": return new RiverbankDateTimeFormatError({
2784
+ code: error.reason,
2785
+ field: error.field,
2786
+ value: typeof error.value === "string" ? error.value : ""
2787
+ });
2788
+ case "invalid_ends_at": return new RiverbankDateTimeFormatError({
2789
+ code: error.reason,
2790
+ field: error.field,
2791
+ value: typeof error.value === "string" ? error.value : ""
2792
+ });
2793
+ case "invalid_time_zone": return new RiverbankDateTimeFormatError({
2794
+ code: error.reason,
2795
+ field: error.field,
2796
+ value: error.value
2797
+ });
2798
+ default: return assertNever$3(error);
2799
+ }
2800
+ }
2801
+ function formatErrorMessage(details) {
2802
+ switch (details.code) {
2803
+ case "invalid_starts_at": return `Expected startsAt to be a valid offset-aware ISO datetime, received "${details.value}"`;
2804
+ case "invalid_ends_at": return `Expected endsAt to be a valid offset-aware ISO datetime, received "${details.value}"`;
2805
+ case "missing_ends_at": return "Expected endsAt to be provided for a scheduled range";
2806
+ case "invalid_time_zone": return `Expected timeZone to be a valid IANA timezone, received "${details.value}"`;
2807
+ case "invalid_locale": return `Expected locale to be a supported BCP 47 locale, received "${details.value}"`;
2808
+ case "end_before_start": return `Expected endsAt to be on or after startsAt, received "${details.value}"`;
2809
+ default: return assertNever$3(details);
2810
+ }
2811
+ }
2812
+ function assertNever$3(value) {
2813
+ throw new Error(`Unhandled date-time variant: ${String(value)}`);
2814
+ }
2815
+
2330
2816
  //#endregion
2331
2817
  //#region ../core/src/appointment-service-categories.ts
2332
2818
  /**
@@ -3733,6 +4219,32 @@ function parsePublicSocialLinks(value) {
3733
4219
  }
3734
4220
  return ok(links);
3735
4221
  }
4222
+ /** Historical stored profiles read absent description as null. */
4223
+ function parsePublicBusinessDescription(value) {
4224
+ const description = trimmedOptionalString(value);
4225
+ return description === undefined || description !== null && description.length > 2e3 ? err({
4226
+ code: "invalid_business_description",
4227
+ path: "businessDescription",
4228
+ message: "Use a business description of 2,000 characters or fewer."
4229
+ }) : ok(description);
4230
+ }
4231
+ /** Public copy and visibility preferences share historical absent-field defaults. */
4232
+ function parsePublicProfilePresentation(value) {
4233
+ const businessDescription = parsePublicBusinessDescription(value.businessDescription);
4234
+ if (!businessDescription.ok) return businessDescription;
4235
+ const addressVisibility = parseAddressVisibility(value.addressVisibility);
4236
+ if (!addressVisibility.ok) return addressVisibility;
4237
+ const contactVisibility = parseContactVisibility(value.contactVisibility);
4238
+ if (!contactVisibility.ok) return contactVisibility;
4239
+ const premises = parsePremises(value.premises);
4240
+ if (!premises.ok) return premises;
4241
+ return ok({
4242
+ businessDescription: businessDescription.value,
4243
+ addressVisibility: addressVisibility.value,
4244
+ contactVisibility: contactVisibility.value,
4245
+ premises: premises.value
4246
+ });
4247
+ }
3736
4248
  function parsePublicProfile(value) {
3737
4249
  if (!isRecord$8(value)) {
3738
4250
  return err({
@@ -3741,25 +4253,19 @@ function parsePublicProfile(value) {
3741
4253
  message: "Public profile must be an object."
3742
4254
  });
3743
4255
  }
4256
+ const presentation = parsePublicProfilePresentation(value);
4257
+ if (!presentation.ok) return presentation;
3744
4258
  const contact = value.contact === null ? ok(null) : parsePublicContactDetails(value.contact);
3745
4259
  if (!contact.ok) return contact;
3746
4260
  const openingHours = value.openingHours === null ? ok(null) : parseWeeklyOpeningHours(value.openingHours);
3747
4261
  if (!openingHours.ok) return openingHours;
3748
4262
  const socialLinks = parsePublicSocialLinks(value.socialLinks);
3749
4263
  if (!socialLinks.ok) return socialLinks;
3750
- const addressVisibility = parseAddressVisibility(value.addressVisibility);
3751
- if (!addressVisibility.ok) return addressVisibility;
3752
- const contactVisibility = parseContactVisibility(value.contactVisibility);
3753
- if (!contactVisibility.ok) return contactVisibility;
3754
- const premises = parsePremises(value.premises);
3755
- if (!premises.ok) return premises;
3756
4264
  return ok({
4265
+ ...presentation.value,
3757
4266
  contact: contact.value,
3758
- contactVisibility: contactVisibility.value,
3759
4267
  openingHours: openingHours.value,
3760
- socialLinks: socialLinks.value,
3761
- addressVisibility: addressVisibility.value,
3762
- premises: premises.value
4268
+ socialLinks: socialLinks.value
3763
4269
  });
3764
4270
  }
3765
4271
  function parseFieldVisibility(value, path) {
@@ -3853,6 +4359,7 @@ function parseAddressVisibility(value) {
3853
4359
  }
3854
4360
  function emptyPublicProfile() {
3855
4361
  return {
4362
+ businessDescription: null,
3856
4363
  contact: null,
3857
4364
  contactVisibility: {
3858
4365
  phone: "shown",
@@ -3928,7 +4435,7 @@ function parsePositiveInteger(value) {
3928
4435
  return typeof value === "number" && Number.isSafeInteger(value) && value > 0 ? value : null;
3929
4436
  }
3930
4437
  function parseNewsletterListId(value) {
3931
- return parseTrimmedMatchingString(value, UUID_PATTERN);
4438
+ return parseTrimmedMatchingString(typeof value === "string" ? value.toLowerCase() : value, UUID_PATTERN);
3932
4439
  }
3933
4440
  function parseMailchimpConnectionId(value) {
3934
4441
  return parseTrimmedMatchingString(value, UUID_PATTERN);
@@ -3972,7 +4479,7 @@ function sameMailingListPlacementTargeting(left, right) {
3972
4479
  }
3973
4480
  function mailingListTargetingChoiceIds(targeting) {
3974
4481
  switch (targeting.provider) {
3975
- case "native": return targeting.selection.tag === "fixed" ? targeting.selection.topicIds : targeting.selection.offeredTopicIds;
4482
+ case "native": return targeting.selection.tag === "general" ? [] : targeting.selection.tag === "fixed" ? targeting.selection.topicIds : targeting.selection.offeredTopicIds;
3976
4483
  case "brevo": return targeting.selection.tag === "fixed" ? targeting.selection.listIds : targeting.selection.offeredListIds;
3977
4484
  case "mailchimp": return targeting.selection.tag === "fixed" ? targeting.selection.interestIds : targeting.selection.offeredInterestIds;
3978
4485
  }
@@ -4002,7 +4509,10 @@ function resolveDefaultTarget(setup) {
4002
4509
  switch (setup.provider) {
4003
4510
  case "native": return {
4004
4511
  provider: "native",
4005
- topicIds: [setup.defaultTopicId]
4512
+ audience: setup.defaultAudience.kind === "general" ? { kind: "general" } : {
4513
+ kind: "topics",
4514
+ topicIds: [setup.defaultAudience.topicId]
4515
+ }
4006
4516
  };
4007
4517
  case "brevo": return {
4008
4518
  provider: "brevo",
@@ -4023,16 +4533,28 @@ function resolveCustomTarget(targeting, visitorSelection) {
4023
4533
  }
4024
4534
  }
4025
4535
  function resolveNativeTarget(targeting, selection) {
4536
+ if (targeting.selection.tag === "general") {
4537
+ return selection === undefined ? ok({
4538
+ provider: "native",
4539
+ audience: { kind: "general" }
4540
+ }) : err({ tag: "visitor_selection_not_allowed" });
4541
+ }
4026
4542
  if (targeting.selection.tag === "fixed") {
4027
4543
  return selection === undefined ? ok({
4028
4544
  provider: "native",
4029
- topicIds: targeting.selection.topicIds
4545
+ audience: {
4546
+ kind: "topics",
4547
+ topicIds: targeting.selection.topicIds
4548
+ }
4030
4549
  }) : err({ tag: "visitor_selection_not_allowed" });
4031
4550
  }
4032
4551
  if (selection === undefined) {
4033
4552
  return targeting.selection.offeredTopicIds.length === 1 ? ok({
4034
4553
  provider: "native",
4035
- topicIds: targeting.selection.offeredTopicIds
4554
+ audience: {
4555
+ kind: "topics",
4556
+ topicIds: targeting.selection.offeredTopicIds
4557
+ }
4036
4558
  }) : err({ tag: "visitor_selection_required" });
4037
4559
  }
4038
4560
  if (selection.provider !== "native") {
@@ -4041,7 +4563,10 @@ function resolveNativeTarget(targeting, selection) {
4041
4563
  const selected = selectedNonEmptySubset(selection.topicIds, targeting.selection.offeredTopicIds);
4042
4564
  return selected.ok ? ok({
4043
4565
  provider: "native",
4044
- topicIds: selected.value
4566
+ audience: {
4567
+ kind: "topics",
4568
+ topicIds: selected.value
4569
+ }
4045
4570
  }) : err({ tag: "visitor_selection_outside_offer" });
4046
4571
  }
4047
4572
  function resolveBrevoTarget(targeting, selection) {
@@ -4100,45 +4625,34 @@ function selectedNonEmptySubset(selected, offered) {
4100
4625
  return ok([first, ...rest]);
4101
4626
  }
4102
4627
  function planMailingListChoicePresentation(targeting) {
4103
- if (targeting.mode === "default" || targeting.selection.tag === "fixed") {
4628
+ if (targeting.mode === "default" || targeting.selection.tag === "fixed" || targeting.selection.tag === "general") {
4104
4629
  return { tag: "hidden" };
4105
4630
  }
4106
4631
  switch (targeting.provider) {
4107
- case "native": return targeting.selection.offeredTopicIds.length === 1 ? {
4108
- tag: "automatic",
4109
- selection: {
4110
- provider: "native",
4111
- topicIds: targeting.selection.offeredTopicIds
4112
- }
4113
- } : {
4114
- tag: "visitor_choice",
4632
+ case "native": return presentVisitorSelection({
4115
4633
  provider: "native",
4116
- optionCount: targeting.selection.offeredTopicIds.length
4117
- };
4118
- case "brevo": return targeting.selection.offeredListIds.length === 1 ? {
4119
- tag: "automatic",
4120
- selection: {
4121
- provider: "brevo",
4122
- listIds: targeting.selection.offeredListIds
4123
- }
4124
- } : {
4125
- tag: "visitor_choice",
4634
+ topicIds: targeting.selection.offeredTopicIds
4635
+ }, targeting.selection.offeredTopicIds.length);
4636
+ case "brevo": return presentVisitorSelection({
4126
4637
  provider: "brevo",
4127
- optionCount: targeting.selection.offeredListIds.length
4128
- };
4129
- case "mailchimp": return targeting.selection.offeredInterestIds.length === 1 ? {
4130
- tag: "automatic",
4131
- selection: {
4132
- provider: "mailchimp",
4133
- interestIds: targeting.selection.offeredInterestIds
4134
- }
4135
- } : {
4136
- tag: "visitor_choice",
4638
+ listIds: targeting.selection.offeredListIds
4639
+ }, targeting.selection.offeredListIds.length);
4640
+ case "mailchimp": return presentVisitorSelection({
4137
4641
  provider: "mailchimp",
4138
- optionCount: targeting.selection.offeredInterestIds.length
4139
- };
4642
+ interestIds: targeting.selection.offeredInterestIds
4643
+ }, targeting.selection.offeredInterestIds.length);
4140
4644
  }
4141
4645
  }
4646
+ function presentVisitorSelection(selection, optionCount) {
4647
+ return optionCount === 1 ? {
4648
+ tag: "automatic",
4649
+ selection
4650
+ } : {
4651
+ tag: "visitor_choice",
4652
+ provider: selection.provider,
4653
+ optionCount
4654
+ };
4655
+ }
4142
4656
 
4143
4657
  //#endregion
4144
4658
  //#region ../core/src/mailing-list-targeting-parser.ts
@@ -4159,6 +4673,7 @@ function parseMailingListVisitorSelection(value) {
4159
4673
  if (!isRecord$6(value)) return visitorSelectionError("shape");
4160
4674
  switch (value.provider) {
4161
4675
  case "native": {
4676
+ if (Object.keys(value).some((key) => key !== "provider" && key !== "topicIds")) return visitorSelectionError("shape");
4162
4677
  const topicIds = parseVisitorIds(value.topicIds, parseNewsletterListId);
4163
4678
  return topicIds === null ? visitorSelectionError("targets") : ok({
4164
4679
  provider: "native",
@@ -4183,6 +4698,13 @@ function parseMailingListVisitorSelection(value) {
4183
4698
  }
4184
4699
  }
4185
4700
  function parseNativeTargeting(selection) {
4701
+ if (selection.tag === "general") {
4702
+ return Object.keys(selection).length === 1 ? ok({
4703
+ mode: "custom",
4704
+ provider: "native",
4705
+ selection: { tag: "general" }
4706
+ }) : targetingError("selection");
4707
+ }
4186
4708
  if (selection.tag === "fixed") {
4187
4709
  const topicIds = parseNonEmptyUniqueIds(selection.topicIds, parseNewsletterListId);
4188
4710
  return topicIds.ok ? ok({
@@ -4388,6 +4910,18 @@ function parseFooterEngagementSectionValue(value, sectionBase, path) {
4388
4910
  }
4389
4911
  }
4390
4912
 
4913
+ //#endregion
4914
+ //#region ../core/src/footer-navigation-layout.ts
4915
+ const footerNavigationLayouts = ["stack", "inline-wrap"];
4916
+ const defaultFooterNavigationLayout = "inline-wrap";
4917
+ function normalizeFooterNavigationLayout(value) {
4918
+ return value === "stack" ? "stack" : defaultFooterNavigationLayout;
4919
+ }
4920
+ /** The arrangement chooses presentation; the stored preference remains intact. */
4921
+ function effectiveFooterNavigationLayout(arrangement, stored) {
4922
+ return arrangement === "centred" ? "inline-wrap" : stored;
4923
+ }
4924
+
4391
4925
  //#endregion
4392
4926
  //#region ../core/src/footer-arrangement.ts
4393
4927
  const sectionKinds = [
@@ -4402,85 +4936,119 @@ const sectionKinds = [
4402
4936
  "legal-navigation",
4403
4937
  "legal"
4404
4938
  ];
4405
- const sectionKindOrder = new Map(sectionKinds.map((kind, index) => [kind, index]));
4406
- const visualGroupOrder = [
4939
+ const centredSectionKinds = [
4940
+ "brand",
4941
+ "supporting-text",
4942
+ "navigation",
4943
+ "contact",
4944
+ "business-address",
4945
+ "opening-hours",
4946
+ "social-links",
4947
+ "engagement",
4948
+ "legal",
4949
+ "legal-navigation"
4950
+ ];
4951
+ /** Section ownership is exhaustive for both arrangements; content never changes placement. */
4952
+ const rowSectionGroups = {
4953
+ brand: "identity",
4954
+ "supporting-text": "identity",
4955
+ "social-links": "identity",
4956
+ navigation: "main-navigation",
4957
+ "business-address": "visit",
4958
+ contact: "visit",
4959
+ "opening-hours": "visit",
4960
+ engagement: "engagement",
4961
+ "legal-navigation": "legal-ending",
4962
+ legal: "legal-ending"
4963
+ };
4964
+ const centredSectionGroups = {
4965
+ ...rowSectionGroups,
4966
+ "social-links": "social"
4967
+ };
4968
+ const rowGroupOrder = [
4407
4969
  "identity",
4408
4970
  "main-navigation",
4409
4971
  "visit",
4410
4972
  "engagement",
4411
4973
  "legal-ending"
4412
4974
  ];
4413
- function groupFor(kind) {
4414
- switch (kind) {
4415
- case "brand":
4416
- case "supporting-text":
4417
- case "social-links": return "identity";
4418
- case "navigation": return "main-navigation";
4419
- case "business-address":
4420
- case "contact":
4421
- case "opening-hours": return "visit";
4422
- case "engagement": return "engagement";
4423
- case "legal-navigation":
4424
- case "legal": return "legal-ending";
4425
- }
4975
+ const centredGroupOrder = [
4976
+ "identity",
4977
+ "main-navigation",
4978
+ "visit",
4979
+ "social",
4980
+ "engagement",
4981
+ "legal-ending"
4982
+ ];
4983
+ const groupSlots = {
4984
+ identity: "head",
4985
+ "main-navigation": "navigation",
4986
+ visit: "contact",
4987
+ social: "social",
4988
+ engagement: "panel",
4989
+ "legal-ending": "base"
4990
+ };
4991
+ const regionSlots = {
4992
+ "engagement-row": "panel",
4993
+ main: "stack",
4994
+ bottom: "base"
4995
+ };
4996
+ /** One policy owner for the served footer and its composition preview. */
4997
+ function footerArrangementPolicy(input) {
4998
+ const arrangement = input.arrangement ?? "simple";
4999
+ return {
5000
+ arrangement,
5001
+ kind: arrangement === "centred" || input.legalRegion === "separate" ? "separate-legal-region" : "integrated-legal",
5002
+ navigationLayout: effectiveFooterNavigationLayout(arrangement, normalizeFooterNavigationLayout(input.storedNavigationLayout))
5003
+ };
4426
5004
  }
4427
- function buildGroups(sections, navigationLayout) {
4428
- const groups = new Map();
4429
- for (const section of sections) {
4430
- const kind = groupFor(section.kind);
4431
- groups.set(kind, [...groups.get(kind) ?? [], section]);
4432
- }
4433
- return visualGroupOrder.flatMap((kind) => {
4434
- const groupSections = groups.get(kind);
4435
- return groupSections === undefined || groupSections.length === 0 ? [] : [{
5005
+ function buildGroups(sections, policy) {
5006
+ const centred = policy.arrangement === "centred";
5007
+ const order = centred ? centredGroupOrder : rowGroupOrder;
5008
+ const owners = centred ? centredSectionGroups : rowSectionGroups;
5009
+ const sectionOrder = centred ? centredSectionKinds : sectionKinds;
5010
+ return order.flatMap((kind) => {
5011
+ const groupSections = sections.filter((section) => owners[section.kind] === kind).sort((left, right) => sectionOrder.indexOf(left.kind) - sectionOrder.indexOf(right.kind));
5012
+ return groupSections.length === 0 ? [] : [{
4436
5013
  kind,
4437
- width: kind === "main-navigation" && navigationLayout === "inline-wrap" ? "full-row" : "column",
4438
- sections: [...groupSections].sort((left, right) => (sectionKindOrder.get(left.kind) ?? Number.MAX_SAFE_INTEGER) - (sectionKindOrder.get(right.kind) ?? Number.MAX_SAFE_INTEGER))
5014
+ width: kind === "main-navigation" && policy.navigationLayout === "inline-wrap" ? "full-row" : "column",
5015
+ sections: groupSections,
5016
+ ...centred ? { slot: groupSlots[kind] } : {}
4439
5017
  }];
4440
5018
  });
4441
5019
  }
4442
5020
  function membershipKey(sections) {
4443
5021
  const tokens = sections.map((section) => ({
4444
- rank: sectionKindOrder.get(section.kind) ?? Number.MAX_SAFE_INTEGER,
5022
+ rank: sectionKinds.indexOf(section.kind),
4445
5023
  value: section.kind === "engagement" ? `engagement:${section.treatment}` : section.kind
4446
- }));
4447
- tokens.sort((left, right) => left.rank - right.rank);
5024
+ })).sort((left, right) => left.rank - right.rank);
4448
5025
  return tokens.map(({ value }) => value).join("+") || "empty";
4449
5026
  }
5027
+ function renderRegion(kind, groups, policy) {
5028
+ if (groups.length === 0) return [];
5029
+ const columns = kind !== "main" || policy.arrangement === "centred" ? 1 : Math.max(1, Math.min(groups.filter((group) => group.width === "column").length, 5));
5030
+ return [{
5031
+ kind,
5032
+ columns,
5033
+ groups,
5034
+ ...policy.arrangement === "centred" ? { slot: regionSlots[kind] } : {}
5035
+ }];
5036
+ }
4450
5037
  function planFooterArrangement(renderableSections, policy) {
4451
5038
  const selectedSections = renderableSections.map(({ section }) => section);
4452
- const groups = buildGroups(selectedSections, policy.navigationLayout);
4453
- const engagement = selectedSections.find((section) => section.kind === "engagement");
4454
- const separateEngagement = engagement?.treatment === "separate-row" ? groups.filter((group) => group.kind === "engagement") : [];
4455
- const layoutGroups = separateEngagement.length === 0 ? groups : groups.filter((group) => group.kind !== "engagement");
4456
- const legalGroups = layoutGroups.filter((group) => group.kind === "legal-ending");
4457
- const unsortedMainGroups = policy.kind === "separate-legal-region" ? layoutGroups.filter((group) => group.kind !== "legal-ending") : layoutGroups;
4458
- const mainGroups = [...unsortedMainGroups.filter((group) => group.width === "column"), ...unsortedMainGroups.filter((group) => group.width === "full-row")];
4459
- const regions = [];
4460
- if (separateEngagement.length > 0) {
4461
- regions.push({
4462
- kind: "engagement-row",
4463
- columns: 1,
4464
- groups: separateEngagement
4465
- });
4466
- }
4467
- if (mainGroups.length > 0) {
4468
- regions.push({
4469
- kind: "main",
4470
- columns: Math.max(1, Math.min(mainGroups.filter((group) => group.width === "column").length, 5)),
4471
- groups: mainGroups
4472
- });
4473
- }
4474
- if (policy.kind === "separate-legal-region" && legalGroups.length > 0) {
4475
- regions.push({
4476
- kind: "bottom",
4477
- columns: 1,
4478
- groups: legalGroups
4479
- });
4480
- }
5039
+ const groups = buildGroups(selectedSections, policy);
5040
+ const separateEngagement = groups.filter((group) => group.kind === "engagement" && group.sections.some((section) => section.kind === "engagement" && section.treatment === "separate-row"));
5041
+ const layoutGroups = groups.filter((group) => !separateEngagement.includes(group));
5042
+ const legalGroups = policy.kind === "separate-legal-region" ? layoutGroups.filter((group) => group.kind === "legal-ending") : [];
5043
+ const unsortedMainGroups = layoutGroups.filter((group) => !legalGroups.includes(group));
5044
+ const mainGroups = policy.arrangement === "centred" ? unsortedMainGroups : [...unsortedMainGroups.filter((group) => group.width === "column"), ...unsortedMainGroups.filter((group) => group.width === "full-row")];
4481
5045
  return {
4482
5046
  membershipKey: membershipKey(selectedSections),
4483
- regions,
5047
+ regions: [
5048
+ ...renderRegion("engagement-row", separateEngagement, policy),
5049
+ ...renderRegion("main", mainGroups, policy),
5050
+ ...renderRegion("bottom", legalGroups, policy)
5051
+ ],
4484
5052
  mobileGroups: separateEngagement.length === 0 ? groups : [...separateEngagement, ...layoutGroups]
4485
5053
  };
4486
5054
  }
@@ -4945,14 +5513,6 @@ function normalizeLegacyFooterContentToV1(value, options = {}) {
4945
5513
  });
4946
5514
  }
4947
5515
 
4948
- //#endregion
4949
- //#region ../core/src/footer-navigation-layout.ts
4950
- const footerNavigationLayouts = ["stack", "inline-wrap"];
4951
- const defaultFooterNavigationLayout = "inline-wrap";
4952
- function normalizeFooterNavigationLayout(value) {
4953
- return value === "stack" ? "stack" : defaultFooterNavigationLayout;
4954
- }
4955
-
4956
5516
  //#endregion
4957
5517
  //#region ../core/src/mailing-list-setup.ts
4958
5518
  function isRecord$3(value) {
@@ -4966,6 +5526,9 @@ function mailingListProviderName(provider) {
4966
5526
  case "brevo": return "Brevo";
4967
5527
  }
4968
5528
  }
5529
+ function sameNewsletterAudience(left, right) {
5530
+ return left.kind === right.kind && (left.kind === "general" || right.kind === "topic" && left.topicId === right.topicId);
5531
+ }
4969
5532
  const DEFAULT_MAILING_LIST_SUCCESS_MESSAGE = "Thanks — you're on the list.";
4970
5533
  /**
4971
5534
  * Seeded into a new setup so every signup form carries a preferences/unsubscribe
@@ -5013,6 +5576,7 @@ function resolveCollectedGlobalFields(form) {
5013
5576
  switch (form.fields.preset) {
5014
5577
  case "emailOnly": return phone;
5015
5578
  case "emailName": return ["name", ...phone];
5579
+ case "emailFirstName": return ["first_name", ...phone];
5016
5580
  case "emailFirstLast": return [
5017
5581
  "first_name",
5018
5582
  "last_name",
@@ -5029,6 +5593,7 @@ function resolveRequiredGlobalFields(fields) {
5029
5593
  switch (fields.preset) {
5030
5594
  case "emailOnly": return [];
5031
5595
  case "emailName": return fields.name === "required" ? ["name"] : [];
5596
+ case "emailFirstName": return fields.firstName === "required" ? ["first_name"] : [];
5032
5597
  case "emailFirstLast": return [...fields.firstName === "required" ? ["first_name"] : [], ...fields.lastName === "required" ? ["last_name"] : []];
5033
5598
  case "emailNameCompany": return [...fields.name === "required" ? ["name"] : [], ...fields.company === "required" ? ["company"] : []];
5034
5599
  }
@@ -5097,6 +5662,15 @@ function countDefaultMailingListPlacements(placementModes) {
5097
5662
 
5098
5663
  //#endregion
5099
5664
  //#region ../core/src/mailing-list-setup-parser.ts
5665
+ const newsletterAudienceSchema = z.unknown().transform((value, ctx) => {
5666
+ const parsed = parseNewsletterAudience(value);
5667
+ if (parsed.ok) return parsed.value;
5668
+ ctx.addIssue({
5669
+ code: "custom",
5670
+ message: "Choose a valid newsletter audience."
5671
+ });
5672
+ return z.NEVER;
5673
+ });
5100
5674
  function parseMailingListGlobalForm(value) {
5101
5675
  if (!isRecord$2(value)) return formError("shape");
5102
5676
  const fields = parseGlobalFields(value.fields);
@@ -5153,51 +5727,69 @@ function parseConfiguredMailingListProvider(value) {
5153
5727
  return setupError("provider");
5154
5728
  }
5155
5729
  switch (value.provider) {
5156
- case "native": {
5157
- const defaultTopicId = parseNewsletterListId(value.defaultTopicId);
5158
- if (defaultTopicId === null) return setupError("defaultTargetId");
5159
- const confirmation = parseToggleConfirmation(value.confirmation);
5160
- return confirmation === null ? setupError("confirmation") : ok({
5161
- provider: "native",
5162
- defaultTopicId,
5163
- confirmation
5164
- });
5165
- }
5166
- case "mailchimp": {
5167
- const connectionId = parseMailchimpConnectionId(value.connectionId);
5168
- if (connectionId === null) return setupError("connectionId");
5169
- const accountId = parseMailchimpAccountId(value.accountId);
5170
- if (accountId === null) return setupError("accountId");
5171
- const defaultAudienceId = parseMailchimpAudienceId(value.defaultAudienceId);
5172
- if (defaultAudienceId === null) return setupError("defaultTargetId");
5173
- const confirmation = parseToggleConfirmation(value.confirmation);
5174
- return confirmation === null ? setupError("confirmation") : ok({
5175
- provider: "mailchimp",
5176
- connectionId,
5177
- accountId,
5178
- defaultAudienceId,
5179
- confirmation
5180
- });
5181
- }
5182
- case "brevo": {
5183
- const connectionId = parseBrevoConnectionId(value.connectionId);
5184
- if (connectionId === null) return setupError("connectionId");
5185
- const accountId = parseBrevoAccountId(value.accountId);
5186
- if (accountId === null) return setupError("accountId");
5187
- const defaultListId = parseBrevoListId(value.defaultListId);
5188
- if (defaultListId === null) return setupError("defaultTargetId");
5189
- const confirmation = parseBrevoConfirmation(value.confirmation);
5190
- return confirmation === null ? setupError("confirmation") : ok({
5191
- provider: "brevo",
5192
- connectionId,
5193
- accountId,
5194
- defaultListId,
5195
- confirmation
5196
- });
5197
- }
5730
+ case "native": return parseNativeSetup(value);
5731
+ case "mailchimp": return parseMailchimpSetup(value);
5732
+ case "brevo": return parseBrevoSetup(value);
5198
5733
  default: return setupError("provider");
5199
5734
  }
5200
5735
  }
5736
+ function parseNativeSetup(value) {
5737
+ if ("defaultTopicId" in value) return setupError("defaultTargetId");
5738
+ const defaultAudience = parseNewsletterAudience(value.defaultAudience);
5739
+ if (!defaultAudience.ok) return setupError("defaultTargetId");
5740
+ const confirmation = parseToggleConfirmation(value.confirmation);
5741
+ return confirmation === null ? setupError("confirmation") : ok({
5742
+ provider: "native",
5743
+ defaultAudience: defaultAudience.value,
5744
+ confirmation
5745
+ });
5746
+ }
5747
+ function parseMailchimpSetup(value) {
5748
+ const connectionId = parseMailchimpConnectionId(value.connectionId);
5749
+ if (connectionId === null) return setupError("connectionId");
5750
+ const accountId = parseMailchimpAccountId(value.accountId);
5751
+ if (accountId === null) return setupError("accountId");
5752
+ const defaultAudienceId = parseMailchimpAudienceId(value.defaultAudienceId);
5753
+ if (defaultAudienceId === null) return setupError("defaultTargetId");
5754
+ const confirmation = parseToggleConfirmation(value.confirmation);
5755
+ return confirmation === null ? setupError("confirmation") : ok({
5756
+ provider: "mailchimp",
5757
+ connectionId,
5758
+ accountId,
5759
+ defaultAudienceId,
5760
+ confirmation
5761
+ });
5762
+ }
5763
+ function parseBrevoSetup(value) {
5764
+ const connectionId = parseBrevoConnectionId(value.connectionId);
5765
+ if (connectionId === null) return setupError("connectionId");
5766
+ const accountId = parseBrevoAccountId(value.accountId);
5767
+ if (accountId === null) return setupError("accountId");
5768
+ const defaultListId = parseBrevoListId(value.defaultListId);
5769
+ if (defaultListId === null) return setupError("defaultTargetId");
5770
+ const confirmation = parseBrevoConfirmation(value.confirmation);
5771
+ return confirmation === null ? setupError("confirmation") : ok({
5772
+ provider: "brevo",
5773
+ connectionId,
5774
+ accountId,
5775
+ defaultListId,
5776
+ confirmation
5777
+ });
5778
+ }
5779
+ function parseNewsletterAudience(value) {
5780
+ if (!isRecord$2(value)) return err("invalid_newsletter_audience");
5781
+ if (value.kind === "general" && Object.keys(value).length === 1) {
5782
+ return ok({ kind: "general" });
5783
+ }
5784
+ if (value.kind !== "topic" || Object.keys(value).some((key) => key !== "kind" && key !== "topicId")) {
5785
+ return err("invalid_newsletter_audience");
5786
+ }
5787
+ const topicId = parseNewsletterListId(value.topicId);
5788
+ return topicId === null ? err("invalid_newsletter_audience") : ok({
5789
+ kind: "topic",
5790
+ topicId
5791
+ });
5792
+ }
5201
5793
  function parseGlobalFields(value) {
5202
5794
  if (!isRecord$2(value) || typeof value.preset !== "string") return null;
5203
5795
  switch (value.preset) {
@@ -5206,6 +5798,10 @@ function parseGlobalFields(value) {
5206
5798
  preset: "emailName",
5207
5799
  name: value.name
5208
5800
  } : null;
5801
+ case "emailFirstName": return isRequirement(value.firstName) ? {
5802
+ preset: "emailFirstName",
5803
+ firstName: value.firstName
5804
+ } : null;
5209
5805
  case "emailFirstLast": return isRequirement(value.firstName) && isRequirement(value.lastName) ? {
5210
5806
  preset: "emailFirstLast",
5211
5807
  firstName: value.firstName,
@@ -5281,7 +5877,13 @@ function setupError(field) {
5281
5877
  //#region ../core/src/mailing-list-targeting-availability.ts
5282
5878
  function resolveMailingListDefaultDestination(provider) {
5283
5879
  switch (provider.provider) {
5284
- case "native": return provider.topics.find((item) => item.id === provider.defaultTopicId) ?? null;
5880
+ case "native": {
5881
+ const audience = provider.defaultAudience;
5882
+ return audience.kind === "general" ? {
5883
+ id: "general",
5884
+ name: "General mailing list"
5885
+ } : provider.topics.find((item) => item.id === audience.topicId) ?? null;
5886
+ }
5285
5887
  case "brevo": return provider.lists.find((item) => item.id === provider.defaultListId) ?? null;
5286
5888
  case "mailchimp": return provider.audiences.find((item) => item.id === provider.defaultAudienceId) ?? null;
5287
5889
  }
@@ -5446,11 +6048,12 @@ function parseRuntimeProvider(value) {
5446
6048
  }
5447
6049
  }
5448
6050
  function parseNativeRuntimeProvider(value) {
5449
- const defaultTopicId = parseNewsletterListId(value.defaultTopicId);
6051
+ if ("defaultTopicId" in value) return null;
6052
+ const defaultAudience = parseNewsletterAudience(value.defaultAudience);
5450
6053
  const topics = parseNamedItems(value.topics, parseNewsletterListId);
5451
- return defaultTopicId !== null && topics !== null ? {
6054
+ return defaultAudience.ok && topics !== null ? {
5452
6055
  provider: "native",
5453
- defaultTopicId,
6056
+ defaultAudience: defaultAudience.value,
5454
6057
  topics
5455
6058
  } : null;
5456
6059
  }
@@ -5586,36 +6189,85 @@ function parseMailingListSignupProfile(form, input) {
5586
6189
  }
5587
6190
  } : name;
5588
6191
  }
5589
- case "emailFirstLast": {
6192
+ case "emailFirstName": {
5590
6193
  const firstName = resolveProfileField("first_name", form.fields.firstName, input.firstName);
5591
- if (!firstName.ok) return firstName;
5592
- const lastName = resolveProfileField("last_name", form.fields.lastName, input.lastName);
5593
- return lastName.ok ? {
6194
+ return firstName.ok ? {
5594
6195
  ok: true,
5595
6196
  value: {
5596
- preset: "emailFirstLast",
6197
+ preset: "emailFirstName",
5597
6198
  email,
5598
6199
  phone,
5599
- firstName: firstName.value,
5600
- lastName: lastName.value
6200
+ firstName: firstName.value
5601
6201
  }
5602
- } : lastName;
6202
+ } : firstName;
5603
6203
  }
5604
- case "emailNameCompany": {
5605
- const name = resolveProfileField("name", form.fields.name, input.name);
5606
- if (!name.ok) return name;
5607
- const company = resolveProfileField("company", form.fields.company, input.company);
5608
- return company.ok ? {
5609
- ok: true,
5610
- value: {
5611
- preset: "emailNameCompany",
5612
- email,
5613
- phone,
5614
- name: name.value,
5615
- company: company.value
5616
- }
5617
- } : company;
6204
+ case "emailFirstLast": return parseFirstLastProfile(form.fields, {
6205
+ email,
6206
+ phone
6207
+ }, input);
6208
+ case "emailNameCompany": return parseNameCompanyProfile(form.fields, {
6209
+ email,
6210
+ phone
6211
+ }, input);
6212
+ }
6213
+ }
6214
+ function parseFirstLastProfile(fields, base, input) {
6215
+ const firstName = resolveProfileField("first_name", fields.firstName, input.firstName);
6216
+ if (!firstName.ok) return firstName;
6217
+ const lastName = resolveProfileField("last_name", fields.lastName, input.lastName);
6218
+ return lastName.ok ? {
6219
+ ok: true,
6220
+ value: {
6221
+ ...base,
6222
+ preset: "emailFirstLast",
6223
+ firstName: firstName.value,
6224
+ lastName: lastName.value
5618
6225
  }
6226
+ } : lastName;
6227
+ }
6228
+ function parseNameCompanyProfile(fields, base, input) {
6229
+ const name = resolveProfileField("name", fields.name, input.name);
6230
+ if (!name.ok) return name;
6231
+ const company = resolveProfileField("company", fields.company, input.company);
6232
+ return company.ok ? {
6233
+ ok: true,
6234
+ value: {
6235
+ ...base,
6236
+ preset: "emailNameCompany",
6237
+ name: name.value,
6238
+ company: company.value
6239
+ }
6240
+ } : company;
6241
+ }
6242
+ /** One projection consumed by native signup and external provider adapters. */
6243
+ function mailingListSignupProfileValues(profile) {
6244
+ const empty = {
6245
+ name: null,
6246
+ first_name: null,
6247
+ last_name: null,
6248
+ company: null,
6249
+ phone: profile.phone.tag === "collected" ? profile.phone.value : null
6250
+ };
6251
+ switch (profile.preset) {
6252
+ case "emailOnly": return empty;
6253
+ case "emailName": return {
6254
+ ...empty,
6255
+ name: profile.name.value
6256
+ };
6257
+ case "emailFirstName": return {
6258
+ ...empty,
6259
+ first_name: profile.firstName.value
6260
+ };
6261
+ case "emailFirstLast": return {
6262
+ ...empty,
6263
+ first_name: profile.firstName.value,
6264
+ last_name: profile.lastName.value
6265
+ };
6266
+ case "emailNameCompany": return {
6267
+ ...empty,
6268
+ name: profile.name.value,
6269
+ company: profile.company.value
6270
+ };
5619
6271
  }
5620
6272
  }
5621
6273
  function resolveProfileField(field, requirement, input) {
@@ -6007,7 +6659,7 @@ function resolveExportDateRange(range, today) {
6007
6659
  ok: true,
6008
6660
  window: {
6009
6661
  from: unsafeAsMoneyDateString("1970-01-01"),
6010
- to: unsafeAsMoneyDateString(formatDate(today))
6662
+ to: unsafeAsMoneyDateString(formatDate$1(today))
6011
6663
  }
6012
6664
  };
6013
6665
  }
@@ -6018,8 +6670,8 @@ function resolveExportDateRange(range, today) {
6018
6670
  return {
6019
6671
  ok: true,
6020
6672
  window: {
6021
- from: unsafeAsMoneyDateString(formatDate(from)),
6022
- to: unsafeAsMoneyDateString(formatDate(to))
6673
+ from: unsafeAsMoneyDateString(formatDate$1(from)),
6674
+ to: unsafeAsMoneyDateString(formatDate$1(to))
6023
6675
  }
6024
6676
  };
6025
6677
  }
@@ -6035,7 +6687,7 @@ function parseExportDate(value, field) {
6035
6687
  };
6036
6688
  }
6037
6689
  const date = new Date(`${value}T00:00:00.000Z`);
6038
- if (Number.isNaN(date.getTime()) || formatDate(date) !== value) {
6690
+ if (Number.isNaN(date.getTime()) || formatDate$1(date) !== value) {
6039
6691
  return {
6040
6692
  ok: false,
6041
6693
  error: {
@@ -6061,7 +6713,7 @@ function presetDays(preset) {
6061
6713
  function floorUtcDate(date) {
6062
6714
  return new Date(Date.UTC(date.getUTCFullYear(), date.getUTCMonth(), date.getUTCDate()));
6063
6715
  }
6064
- function formatDate(date) {
6716
+ function formatDate$1(date) {
6065
6717
  return date.toISOString().slice(0, 10);
6066
6718
  }
6067
6719
  function describeExportDateBasis(basis) {
@@ -7525,4 +8177,4 @@ function mergeBag(stored, patch, options) {
7525
8177
  }
7526
8178
 
7527
8179
  //#endregion
7528
- 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 };
8180
+ export { parseUtcIsoDateTimeString as $, unsafeAsBookingSessionId as $t, parseFooterCompositionV1 as A, parseNonNegativeCents as At, normalizeLocationAddressText as B, parseApiKeyId as Bt, parseMailingListBlockId as C, unsafeAsStoragePath as Cn, publicEventPricingOptions as Ct, assessMailingListTargeting as D, ok as Dn, fromArray as Dt, parseMailingListRuntimeContext as E, err as En, resolvePricingOptionsPriceDisplay as Et, sameMailingListPlacementTargeting as F, unsafeAsPositiveCents as Ft, formatBookingDate as G, parseMediaAssetId as Gt, selectCategoryView as H, parseDatedOfferingCapacityHoldAuditRef as Ht, isCanonicalRichTextDoc as I, formatCurrencyAmount as It, formatLocalScheduleDate as J, parseRetiredCourseId as Jt, formatBookingDateParts as K, parseMembershipId as Kt, parsePublicContactDetails as L, parseCurrencyCode as Lt, parseMailingListPlacementTargeting as M, toCents as Mt, DEFAULT_MAILING_LIST_TARGETING as N, toNonNegativeCents as Nt, DEFAULT_MAILING_LIST_SUCCESS_MESSAGE as O, mapNonEmptyArray as Ot, planMailingListChoicePresentation as P, unsafeAsNonNegativeCents as Pt, getLocalScheduleTime as Q, unsafeAsBookingRequirementId as Qt, assertAllowedPublicUrl as R, assertNever as Rt, buildMailingListSignupBlockSourceRef as S, unsafeAsStorageBucketName as Sn, publicEventPricingDisplay as St, parseMailingListSignedSourceToken as T, unsafeAsUserId as Tn, resolveExactOrFreePriceDisplay as Tt, inferSiteDateLocale as U, parseDatedOfferingCapacityHoldId as Ut, toLocationCoordinates as V, parseCustomerId as Vt, buildBookingUtcDateWindow as W, parseEventSeriesId as Wt, getBookingLocalDateKey as X, parseUserId as Xt, formatLocalScheduleTime as Y, parseSiteId as Yt, getLocalScheduleDate as Z, unsafeAsAppointmentResourceId as Zt, resolveWholeSeriesBookingPhase as _, unsafeAsSafeDownloadFilename as _n, parseEventPageSurfaceSubject as _t, canonicalizeConfiguredBookingVerticalConfig as a, unsafeAsEventInstanceId as an, isLiveOrderInstalmentPlanStatus as at, parseManagementKey as b, unsafeAsSiteScopedStoragePath as bn, isPublicEventPricingOptionDisplayable as bt, resolveSiteVerticalBookingConfig as c, unsafeAsFileChecksumSha256 as cn, addIntervalPreservingMonthAnchor as ct, ORDINARY_EVENT_PRESET_VALUES as d, unsafeAsOriginalFilename as dn, getBookingInstalmentPlanIntervalCount as dt, unsafeAsCheckoutLineId as en, toBookingTimeZone as et, SURFACE_EVENT_PRESET_VALUES as f, unsafeAsPassId as fn, getBookingInstalmentProtectedDepositAmountCents as ft, orderedLiveRunSessions as g, unsafeAsProductId as gn, resolveFlexibleBalanceInitialPaymentCents as gt, CANCELLED_SESSION_STATUS as h, unsafeAsPricingOptionId as hn, resolveFlexibleBalanceDueAt as ht, areConfiguredBookingVerticalConfigsEqual as i, unsafeAsEntitlementId as in, validateBookingPaymentOptionKinds as it, footerNavigationLayouts as j, parsePositiveCents as jt, parseMailingListProviderScope as k, isNonNegativeWholeCents as kt, DEFAULT_EVENT_TYPE_PRESETS as l, unsafeAsMediaAssetId as ln, clampConfiguredAmount as lt, publicReadFreshnessSeconds as m, unsafeAsPositiveSeconds as mn, splitAmountWithRemainderInFirst as mt, EVENT_PRESET_VOCABULARY as n, unsafeAsDemoCapability as nn, toScheduledBookingDateTime as nt, parseVerticalId as o, unsafeAsEventOfferingId as on, isSettleableFlexibleBalanceContractStatus as ot, WHOLE_SERIES_EVENT_PRESET_VALUES as p, unsafeAsPositiveByteCount as pn, omitZeroAmountFutureRows as pt, formatBookingTime as q, parsePageId as qt, NAVIGATION_LOCATIONS as r, unsafeAsDetectedMimeType as rn, parseBookingTimeZone as rt, parseVerticalPresetId as s, unsafeAsEventSeriesId as sn, isSettleableOrderInstalmentPlanStatus as st, resolveEventCatalogue as t, unsafeAsDeclaredMimeType as tn, toScheduledBookingDateRange as tt, EVENT_PRESET_VALUES as u, unsafeAsOccurrenceId as un, getBookingInstalmentDepositRefundability as ut, createSecret as v, unsafeAsSiteFontId as vn, formatBookingPriceDisplay as vt, parseMailingListContentVersionId as w, unsafeAsUploadSessionId as wn, publicEventPricingType as wt, stableJsonStringify as x, unsafeAsStaffMemberId as xn, publicEventPricingCurrency as xt, exposeSecret as y, unsafeAsSiteId as yn, formatEventPricing as yt, isPresent as z, formatDurationUnitLabel as zt };