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.
- package/README.md +23 -0
- package/dist/acceptance/matrix.mjs +3 -3
- package/dist/cli/index.mjs +2316 -105306
- package/dist/cli/init-docs/content/workflow-content-types.md +25 -0
- package/dist/entryCommands-8kDjOex0.mjs +111695 -0
- package/dist/{eventUpsertInputSchema-BNSzkEID.mjs → eventUpsertInputSchema-VVBVGA6x.mjs} +333 -547
- package/dist/load-config-Cut7_6t_.mjs +4 -0
- package/dist/{load-config-BXspoah3.mjs → load-config-gqUEjIyB.mjs} +2 -2
- package/dist/{plan-DxAHLs05.mjs → plan-HAEMHc5T.mjs} +1 -1
- package/dist/{sdk-event-upsert-BamyD2d6.mjs → richTextSchema-DTvrDDF5.mjs} +13342 -11388
- package/dist/scaffold/index.mjs +2 -2
- package/dist/{src-BhrSG8A4.mjs → src-Dixcv4PB.mjs} +831 -139
- package/dist/testing.mjs +5 -4
- package/package.json +1 -1
- package/dist/load-config-s-HOXD6E.mjs +0 -4
- package/dist/local-sync-B3naCvSc.mjs +0 -4634
|
@@ -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$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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(
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
4423
|
-
|
|
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
|
-
|
|
4431
|
-
|
|
4432
|
-
|
|
4433
|
-
|
|
4434
|
-
|
|
4435
|
-
|
|
4436
|
-
|
|
4437
|
-
|
|
4438
|
-
|
|
4439
|
-
|
|
4440
|
-
|
|
4441
|
-
|
|
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,
|
|
4445
|
-
const
|
|
4446
|
-
|
|
4447
|
-
|
|
4448
|
-
|
|
4449
|
-
|
|
4450
|
-
|
|
4451
|
-
|
|
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:
|
|
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:
|
|
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
|
|
4470
|
-
const
|
|
4471
|
-
const
|
|
4472
|
-
const
|
|
4473
|
-
const
|
|
4474
|
-
const
|
|
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 "
|
|
6281
|
+
case "emailFirstName": {
|
|
5644
6282
|
const firstName = resolveProfileField("first_name", form.fields.firstName, input.firstName);
|
|
5645
|
-
|
|
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: "
|
|
6286
|
+
preset: "emailFirstName",
|
|
5651
6287
|
email,
|
|
5652
6288
|
phone,
|
|
5653
|
-
firstName: firstName.value
|
|
5654
|
-
lastName: lastName.value
|
|
6289
|
+
firstName: firstName.value
|
|
5655
6290
|
}
|
|
5656
|
-
} :
|
|
6291
|
+
} : firstName;
|
|
5657
6292
|
}
|
|
5658
|
-
case "
|
|
5659
|
-
|
|
5660
|
-
|
|
5661
|
-
|
|
5662
|
-
|
|
5663
|
-
|
|
5664
|
-
|
|
5665
|
-
|
|
5666
|
-
|
|
5667
|
-
|
|
5668
|
-
|
|
5669
|
-
|
|
5670
|
-
|
|
5671
|
-
|
|
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 {
|
|
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 };
|