riverbankcms 0.5.0 → 0.6.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/dist/acceptance/matrix.mjs +152 -141
- package/dist/cli/index.mjs +43761 -43538
- package/dist/cli/init-docs/content/content-management.md +38 -28
- package/dist/cli/init-docs/content/workflow-preview-mode.md +1 -1
- package/dist/{eventUpsertInputSchema-7QFvBuxF.mjs → eventUpsertInputSchema-CtkDnYlQ.mjs} +34 -40
- package/dist/{load-config-DKJkcASy.mjs → load-config-DbMjs6df.mjs} +1 -1
- package/dist/load-config-NNJ0ww1N.mjs +5 -0
- package/dist/{plan-DPS1XAby.mjs → plan-DubdLR3N.mjs} +1 -1
- package/dist/scaffold/index.mjs +2 -2
- package/dist/{style-CeGQc8Oy.mjs → sdk-event-upsert-CvFCbzNc.mjs} +29415 -43448
- package/dist/{sdk-peer-sZF43pDS.mjs → sdk-peer-CyQ2so48.mjs} +2 -2
- package/dist/{src-DKm0bLEA.mjs → src-X9oZOIoK.mjs} +819 -255
- package/dist/style-aixm8BXi.mjs +14461 -0
- package/dist/testing.mjs +4 -4
- package/package.json +1 -1
- package/dist/load-config-D88VsMlB.mjs +0 -5
- package/dist/sdk-event-upsert-Dy08IXYL.mjs +0 -962
|
@@ -52,7 +52,7 @@ const unsafeAsOccurrenceId = (id) => id;
|
|
|
52
52
|
const unsafeAsEventOfferingId = (id) => id;
|
|
53
53
|
const unsafeAsEventSeriesId = (id) => id;
|
|
54
54
|
const unsafeAsEventPageRouteSlug = (value) => value;
|
|
55
|
-
const
|
|
55
|
+
const unsafeAsEventAttendeeTicketLineId = (id) => id;
|
|
56
56
|
const unsafeAsEventSeriesRegistrationId = (id) => id;
|
|
57
57
|
const unsafeAsEventCategoryId = (id) => id;
|
|
58
58
|
const unsafeAsVenueId = (id) => id;
|
|
@@ -90,8 +90,6 @@ const unsafeAsFormId = (id) => id;
|
|
|
90
90
|
const unsafeAsFormSubmissionId = (id) => id;
|
|
91
91
|
const unsafeAsPassId = (id) => id;
|
|
92
92
|
const unsafeAsMembershipId = (id) => id;
|
|
93
|
-
const unsafeAsCourseId = (id) => id;
|
|
94
|
-
const unsafeAsCourseProgramId = (id) => id;
|
|
95
93
|
const unsafeAsCustomerId = (id) => id;
|
|
96
94
|
const unsafeAsOrderId = (id) => id;
|
|
97
95
|
const unsafeAsOrderItemId = (id) => id;
|
|
@@ -103,6 +101,9 @@ const unsafeAsOrderPaymentPlanInstalmentId = (id) => id;
|
|
|
103
101
|
const unsafeAsManualPaymentOperationId = (id) => id;
|
|
104
102
|
const unsafeAsCheckoutLineId = (id) => id;
|
|
105
103
|
const unsafeAsAttendeeId = (id) => id;
|
|
104
|
+
const unsafeAsEventRegistrationCommandId = (id) => id;
|
|
105
|
+
const unsafeAsEventRegistrationCommandRowId = (id) => id;
|
|
106
|
+
const unsafeAsEventRegistrationProjectionClaimToken = (id) => id;
|
|
106
107
|
const unsafeAsCustomerPassId = (id) => id;
|
|
107
108
|
const unsafeAsCustomerMembershipId = (id) => id;
|
|
108
109
|
const unsafeAsAppointmentId = (id) => id;
|
|
@@ -160,8 +161,7 @@ const parsePageId = (value) => parseUuidBrand(value, unsafeAsPageId);
|
|
|
160
161
|
const parsePageSurfaceId = (value) => parseUuidBrand(value, unsafeAsPageSurfaceId);
|
|
161
162
|
const parseOccurrenceId = (value) => parseUuidBrand(value, unsafeAsOccurrenceId);
|
|
162
163
|
const parseEventOfferingId = (value) => parseUuidBrand(value, unsafeAsEventOfferingId);
|
|
163
|
-
const
|
|
164
|
-
const parseTicketTypeId = (value) => parseUuidBrand(value, unsafeAsTicketTypeId);
|
|
164
|
+
const parseEventAttendeeTicketLineId = (value) => parseUuidBrand(value, unsafeAsEventAttendeeTicketLineId);
|
|
165
165
|
const parsePricingOptionId = (value) => parseUuidBrand(value, unsafeAsPricingOptionId);
|
|
166
166
|
const parseUserId = (value) => parseUuidBrand(value, unsafeAsUserId);
|
|
167
167
|
const parseProductId = (value) => parseUuidBrand(value, unsafeAsProductId);
|
|
@@ -175,7 +175,8 @@ const parseNavigationMenuId = (value) => parseUuidBrand(value, unsafeAsNavigatio
|
|
|
175
175
|
const parseNavigationItemId = (value) => parseUuidBrand(value, unsafeAsNavigationItemId);
|
|
176
176
|
const parsePassId = (value) => parseUuidBrand(value, unsafeAsPassId);
|
|
177
177
|
const parseMembershipId = (value) => parseUuidBrand(value, unsafeAsMembershipId);
|
|
178
|
-
const
|
|
178
|
+
const asRetiredCourseId = (id) => id;
|
|
179
|
+
const parseRetiredCourseId = (value) => parseUuidBrand(value, asRetiredCourseId);
|
|
179
180
|
const parseFormId = (value) => parseUuidBrand(value, unsafeAsFormId);
|
|
180
181
|
const parseAppointmentId = (value) => parseUuidBrand(value, unsafeAsAppointmentId);
|
|
181
182
|
const parseAppointmentServiceId = (value) => parseUuidBrand(value, unsafeAsAppointmentServiceId);
|
|
@@ -183,6 +184,9 @@ const parseAppointmentResourceId = (value) => parseUuidBrand(value, unsafeAsAppo
|
|
|
183
184
|
const parseAppointmentReservationId = (value) => parseUuidBrand(value, unsafeAsAppointmentReservationId);
|
|
184
185
|
const parseAppointmentReservationRunId = (value) => parseUuidBrand(value, unsafeAsAppointmentReservationRunId);
|
|
185
186
|
const parseAttendeeId = (value) => parseUuidBrand(value, unsafeAsAttendeeId);
|
|
187
|
+
const parseEventRegistrationCommandId = (value) => parseUuidBrand(value, unsafeAsEventRegistrationCommandId);
|
|
188
|
+
const parseEventRegistrationCommandRowId = (value) => parseUuidBrand(value, unsafeAsEventRegistrationCommandRowId);
|
|
189
|
+
const parseEventRegistrationProjectionClaimToken = (value) => parseUuidBrand(value, unsafeAsEventRegistrationProjectionClaimToken);
|
|
186
190
|
const parseDatedOfferingCapacityHoldId = (value) => parseUuidBrand(value, unsafeAsDatedOfferingCapacityHoldId);
|
|
187
191
|
const parseDatedOfferingCapacityHoldAuditRef = (value) => parseUuidBrand(value, unsafeAsDatedOfferingCapacityHoldAuditRef);
|
|
188
192
|
const parseDatedOfferingRevocationAuditEventId = (value) => parseUuidBrand(value, unsafeAsDatedOfferingRevocationAuditEventId);
|
|
@@ -475,6 +479,44 @@ function assertNever(value, message = "Unexpected variant") {
|
|
|
475
479
|
throw new Error(`${message}: ${String(value)}`);
|
|
476
480
|
}
|
|
477
481
|
|
|
482
|
+
//#endregion
|
|
483
|
+
//#region ../core/src/event-booking-offer.ts
|
|
484
|
+
const EVENT_PRICING_STRATEGIES = [
|
|
485
|
+
"free",
|
|
486
|
+
"single",
|
|
487
|
+
"tiered",
|
|
488
|
+
"sliding_scale"
|
|
489
|
+
];
|
|
490
|
+
function createEventBookingOffer(input) {
|
|
491
|
+
if (input.bookingRequirement === "none") {
|
|
492
|
+
return input.pricing.kind === "free" ? ok({
|
|
493
|
+
bookingRequirement: "none",
|
|
494
|
+
pricing: input.pricing,
|
|
495
|
+
paymentTerms: input.paymentTerms
|
|
496
|
+
}) : err({ kind: "paid_without_registration" });
|
|
497
|
+
}
|
|
498
|
+
if (input.bookingRequirement === "required") {
|
|
499
|
+
return ok({
|
|
500
|
+
bookingRequirement: "required",
|
|
501
|
+
pricing: input.pricing,
|
|
502
|
+
paymentTerms: input.paymentTerms
|
|
503
|
+
});
|
|
504
|
+
}
|
|
505
|
+
return err({
|
|
506
|
+
kind: "invalid_booking_requirement",
|
|
507
|
+
value: input.bookingRequirement
|
|
508
|
+
});
|
|
509
|
+
}
|
|
510
|
+
function eventPricingRequiresPayment(pricing) {
|
|
511
|
+
switch (pricing.kind) {
|
|
512
|
+
case "free": return false;
|
|
513
|
+
case "single": return true;
|
|
514
|
+
case "tiered": return pricing.options.some((option) => option.priceAmount > 0);
|
|
515
|
+
case "sliding_scale": return pricing.maximum > 0;
|
|
516
|
+
default: return assertNever(pricing);
|
|
517
|
+
}
|
|
518
|
+
}
|
|
519
|
+
|
|
478
520
|
//#endregion
|
|
479
521
|
//#region ../core/src/currency-format.ts
|
|
480
522
|
const CURRENCY_CODE_INPUT_PATTERN = /^[A-Za-z]{3}$/;
|
|
@@ -538,62 +580,559 @@ function formatCurrencyAmount(amountCents, currency, options) {
|
|
|
538
580
|
}
|
|
539
581
|
}
|
|
540
582
|
|
|
583
|
+
//#endregion
|
|
584
|
+
//#region ../core/src/money-input.ts
|
|
585
|
+
function unsafeAsCents(value) {
|
|
586
|
+
return value;
|
|
587
|
+
}
|
|
588
|
+
function unsafeAsNonNegativeCents(value) {
|
|
589
|
+
return value;
|
|
590
|
+
}
|
|
591
|
+
function unsafeAsPositiveCents(value) {
|
|
592
|
+
return value;
|
|
593
|
+
}
|
|
594
|
+
function unwrapCents(value) {
|
|
595
|
+
return value;
|
|
596
|
+
}
|
|
597
|
+
function isWholeCents(value) {
|
|
598
|
+
return Number.isFinite(value) && Number.isInteger(value);
|
|
599
|
+
}
|
|
600
|
+
function isNonNegativeWholeCents(value) {
|
|
601
|
+
return isWholeCents(value) && value >= 0;
|
|
602
|
+
}
|
|
603
|
+
function isPositiveWholeCents(value) {
|
|
604
|
+
return isWholeCents(value) && value > 0;
|
|
605
|
+
}
|
|
606
|
+
/**
|
|
607
|
+
* Normalizes a derived amount to signed whole cents. Use parse*Cents for
|
|
608
|
+
* untrusted input that should fail instead of truncating or clamping.
|
|
609
|
+
*/
|
|
610
|
+
function toCents(value) {
|
|
611
|
+
return unsafeAsCents(Math.trunc(value));
|
|
612
|
+
}
|
|
613
|
+
/**
|
|
614
|
+
* Normalizes a derived amount to non-negative whole cents. This intentionally
|
|
615
|
+
* clamps negatives to zero; use parseNonNegativeCents for validation.
|
|
616
|
+
*/
|
|
617
|
+
function toNonNegativeCents(value) {
|
|
618
|
+
return unsafeAsNonNegativeCents(Math.max(0, Math.trunc(value)));
|
|
619
|
+
}
|
|
620
|
+
function parseCents(value) {
|
|
621
|
+
if (!Number.isFinite(value)) {
|
|
622
|
+
return err({ kind: "invalid-number" });
|
|
623
|
+
}
|
|
624
|
+
if (!Number.isInteger(value)) {
|
|
625
|
+
return err({ kind: "not-whole-cents" });
|
|
626
|
+
}
|
|
627
|
+
return ok(unsafeAsCents(value));
|
|
628
|
+
}
|
|
629
|
+
function parseNonNegativeCents(value) {
|
|
630
|
+
const parsed = parseCents(value);
|
|
631
|
+
if (!parsed.ok) {
|
|
632
|
+
return parsed;
|
|
633
|
+
}
|
|
634
|
+
if (parsed.value < 0) {
|
|
635
|
+
return err({ kind: "negative" });
|
|
636
|
+
}
|
|
637
|
+
return ok(unsafeAsNonNegativeCents(parsed.value));
|
|
638
|
+
}
|
|
639
|
+
function parsePositiveCents(value) {
|
|
640
|
+
const parsed = parseNonNegativeCents(value);
|
|
641
|
+
if (!parsed.ok) {
|
|
642
|
+
return parsed;
|
|
643
|
+
}
|
|
644
|
+
if (parsed.value === 0) {
|
|
645
|
+
return err({ kind: "zero-not-allowed" });
|
|
646
|
+
}
|
|
647
|
+
return ok(unsafeAsPositiveCents(parsed.value));
|
|
648
|
+
}
|
|
649
|
+
function normalizeMoneyInput(value, options) {
|
|
650
|
+
return (options.stripFormatting === true ? value.replace(/[^0-9.-]/g, "") : value).trim();
|
|
651
|
+
}
|
|
652
|
+
function parseFiniteMoneyAmount(value, options) {
|
|
653
|
+
const normalized = normalizeMoneyInput(value, options);
|
|
654
|
+
if (normalized.length === 0) {
|
|
655
|
+
return err({ kind: "empty" });
|
|
656
|
+
}
|
|
657
|
+
const amount = Number(normalized);
|
|
658
|
+
if (!Number.isFinite(amount)) {
|
|
659
|
+
return err({ kind: "invalid-number" });
|
|
660
|
+
}
|
|
661
|
+
return ok(amount);
|
|
662
|
+
}
|
|
663
|
+
function roundMoneyAmountToCents(amount) {
|
|
664
|
+
return Math.round(amount * 100);
|
|
665
|
+
}
|
|
666
|
+
function parsePositiveMoneyInputToCents(value, options = {}) {
|
|
667
|
+
const parsed = parseFiniteMoneyAmount(value, options);
|
|
668
|
+
if (!parsed.ok) {
|
|
669
|
+
return parsed;
|
|
670
|
+
}
|
|
671
|
+
if (parsed.value < 0) {
|
|
672
|
+
return err({ kind: "negative" });
|
|
673
|
+
}
|
|
674
|
+
const amountCents = roundMoneyAmountToCents(parsed.value);
|
|
675
|
+
if (amountCents <= 0) {
|
|
676
|
+
return err({ kind: "zero-not-allowed" });
|
|
677
|
+
}
|
|
678
|
+
return ok(unsafeAsPositiveCents(amountCents));
|
|
679
|
+
}
|
|
680
|
+
function parseNonNegativeMoneyInputToCents(value, options = {}) {
|
|
681
|
+
const parsed = parseFiniteMoneyAmount(value, options);
|
|
682
|
+
if (!parsed.ok) {
|
|
683
|
+
return parsed;
|
|
684
|
+
}
|
|
685
|
+
if (parsed.value < 0) {
|
|
686
|
+
return err({ kind: "negative" });
|
|
687
|
+
}
|
|
688
|
+
return ok(unsafeAsNonNegativeCents(roundMoneyAmountToCents(parsed.value)));
|
|
689
|
+
}
|
|
690
|
+
function parseMoneyInputToCents(value, options = {}) {
|
|
691
|
+
const parsed = parseFiniteMoneyAmount(value, options);
|
|
692
|
+
if (!parsed.ok) {
|
|
693
|
+
return null;
|
|
694
|
+
}
|
|
695
|
+
if (options.allowZero === true ? parsed.value < 0 : parsed.value <= 0) {
|
|
696
|
+
return null;
|
|
697
|
+
}
|
|
698
|
+
return roundMoneyAmountToCents(parsed.value);
|
|
699
|
+
}
|
|
700
|
+
function formatMoneyInputFromCents(amountCents) {
|
|
701
|
+
if (typeof amountCents !== "number" || !Number.isFinite(amountCents)) {
|
|
702
|
+
return "";
|
|
703
|
+
}
|
|
704
|
+
const sign = amountCents < 0 ? "-" : "";
|
|
705
|
+
const absoluteCents = Math.abs(Math.trunc(amountCents));
|
|
706
|
+
const units = Math.floor(absoluteCents / 100);
|
|
707
|
+
const fractional = String(absoluteCents % 100).padStart(2, "0");
|
|
708
|
+
return `${sign}${units}.${fractional}`;
|
|
709
|
+
}
|
|
710
|
+
|
|
711
|
+
//#endregion
|
|
712
|
+
//#region ../core/src/non-empty-array.ts
|
|
713
|
+
function fromArray(items) {
|
|
714
|
+
const [first, ...rest] = items;
|
|
715
|
+
if (first === undefined) {
|
|
716
|
+
return err("empty_array");
|
|
717
|
+
}
|
|
718
|
+
return ok([first, ...rest]);
|
|
719
|
+
}
|
|
720
|
+
function mapNonEmptyArray(items, map) {
|
|
721
|
+
return items.map(map);
|
|
722
|
+
}
|
|
723
|
+
function fromReadonlyArray(items) {
|
|
724
|
+
const [first, ...rest] = items;
|
|
725
|
+
return first === undefined ? err("empty_array") : ok([first, ...rest]);
|
|
726
|
+
}
|
|
727
|
+
function mapReadonlyNonEmptyArray(items, map) {
|
|
728
|
+
const [first, ...rest] = items;
|
|
729
|
+
return [map(first, 0), ...rest.map((item, index) => map(item, index + 1))];
|
|
730
|
+
}
|
|
731
|
+
|
|
732
|
+
//#endregion
|
|
733
|
+
//#region ../core/src/event-booking-offer-parser.ts
|
|
734
|
+
function oneIssue(issue) {
|
|
735
|
+
return [issue];
|
|
736
|
+
}
|
|
737
|
+
function isNullish(value) {
|
|
738
|
+
return value === null || value === undefined;
|
|
739
|
+
}
|
|
740
|
+
function parseOptionIdentity(input) {
|
|
741
|
+
const id = parsePricingOptionId(input.id);
|
|
742
|
+
if (!id.ok) return err({ kind: "invalid_id" });
|
|
743
|
+
if (typeof input.name !== "string" || input.name.trim().length === 0) {
|
|
744
|
+
return err({ kind: "invalid_name" });
|
|
745
|
+
}
|
|
746
|
+
if (!isNullish(input.description) && typeof input.description !== "string") {
|
|
747
|
+
return err({ kind: "invalid_description" });
|
|
748
|
+
}
|
|
749
|
+
return ok({
|
|
750
|
+
id: id.value,
|
|
751
|
+
name: input.name.trim(),
|
|
752
|
+
description: input.description ?? null
|
|
753
|
+
});
|
|
754
|
+
}
|
|
755
|
+
function parseOptionCommercials(input) {
|
|
756
|
+
const price = typeof input.priceAmount === "number" ? parseNonNegativeCents(input.priceAmount) : err({ kind: "invalid-number" });
|
|
757
|
+
if (!price.ok) return err({
|
|
758
|
+
kind: "invalid_price",
|
|
759
|
+
error: price.error
|
|
760
|
+
});
|
|
761
|
+
if (typeof input.isActive !== "boolean") return err({ kind: "invalid_active" });
|
|
762
|
+
if (typeof input.sortOrder !== "number" || !Number.isInteger(input.sortOrder) || input.sortOrder < 0) {
|
|
763
|
+
return err({ kind: "invalid_sort_order" });
|
|
764
|
+
}
|
|
765
|
+
if (!isNullish(input.capacityLimit) && (typeof input.capacityLimit !== "number" || !Number.isInteger(input.capacityLimit) || input.capacityLimit < 1)) {
|
|
766
|
+
return err({ kind: "invalid_capacity" });
|
|
767
|
+
}
|
|
768
|
+
return ok({
|
|
769
|
+
priceAmount: price.value,
|
|
770
|
+
isActive: input.isActive,
|
|
771
|
+
sortOrder: input.sortOrder,
|
|
772
|
+
capacityLimit: input.capacityLimit ?? null
|
|
773
|
+
});
|
|
774
|
+
}
|
|
775
|
+
function parseNullableIsoDateTime(value, issue) {
|
|
776
|
+
if (isNullish(value)) return ok(null);
|
|
777
|
+
if (typeof value !== "string" || !Number.isFinite(Date.parse(value))) {
|
|
778
|
+
return err({ kind: issue });
|
|
779
|
+
}
|
|
780
|
+
return ok(value);
|
|
781
|
+
}
|
|
782
|
+
function parseAttendanceScope(value) {
|
|
783
|
+
if (value === "in_person" || value === "online" || value === "both") {
|
|
784
|
+
return ok(value);
|
|
785
|
+
}
|
|
786
|
+
return err({ kind: "invalid_attendance_scope" });
|
|
787
|
+
}
|
|
788
|
+
function parseRecipeIdentifier(value) {
|
|
789
|
+
if (isNullish(value)) return ok(null);
|
|
790
|
+
if (typeof value !== "string" || !/^[a-z0-9][a-z0-9-]*$/.test(value)) {
|
|
791
|
+
return err({ kind: "invalid_recipe_identifier" });
|
|
792
|
+
}
|
|
793
|
+
return ok(value);
|
|
794
|
+
}
|
|
795
|
+
function parseOptionAvailability(input) {
|
|
796
|
+
const start = parseNullableIsoDateTime(input.saleStartsAt, "invalid_sale_start");
|
|
797
|
+
if (!start.ok) return start;
|
|
798
|
+
const end = parseNullableIsoDateTime(input.saleEndsAt, "invalid_sale_end");
|
|
799
|
+
if (!end.ok) return end;
|
|
800
|
+
if (start.value !== null && end.value !== null && Date.parse(end.value) <= Date.parse(start.value)) {
|
|
801
|
+
return err({ kind: "invalid_sale_window" });
|
|
802
|
+
}
|
|
803
|
+
const scope = parseAttendanceScope(input.attendanceScope);
|
|
804
|
+
if (!scope.ok) return scope;
|
|
805
|
+
const recipe = parseRecipeIdentifier(input.recipeIdentifier);
|
|
806
|
+
if (!recipe.ok) return recipe;
|
|
807
|
+
return ok({
|
|
808
|
+
saleStartsAt: start.value,
|
|
809
|
+
saleEndsAt: end.value,
|
|
810
|
+
attendanceScope: scope.value,
|
|
811
|
+
recipeIdentifier: recipe.value
|
|
812
|
+
});
|
|
813
|
+
}
|
|
814
|
+
function parseEventPricingOption(input) {
|
|
815
|
+
const identity = parseOptionIdentity(input);
|
|
816
|
+
const commercials = parseOptionCommercials(input);
|
|
817
|
+
const availability = parseOptionAvailability(input);
|
|
818
|
+
const issues = [
|
|
819
|
+
identity,
|
|
820
|
+
commercials,
|
|
821
|
+
availability
|
|
822
|
+
].flatMap((result) => result.ok ? [] : [result.error]);
|
|
823
|
+
if (!identity.ok || !commercials.ok || !availability.ok) {
|
|
824
|
+
const nonEmptyIssues = fromReadonlyArray(issues);
|
|
825
|
+
return err(nonEmptyIssues.ok ? nonEmptyIssues.value : oneIssue({ kind: "invalid_object" }));
|
|
826
|
+
}
|
|
827
|
+
return ok({
|
|
828
|
+
...identity.value,
|
|
829
|
+
...commercials.value,
|
|
830
|
+
...availability.value
|
|
831
|
+
});
|
|
832
|
+
}
|
|
833
|
+
function unexpectedValues(input, fields) {
|
|
834
|
+
return fields.flatMap((field) => isNullish(input[field]) ? [] : [{
|
|
835
|
+
kind: "unexpected_value",
|
|
836
|
+
field
|
|
837
|
+
}]);
|
|
838
|
+
}
|
|
839
|
+
function invalidOptionsIssue(value) {
|
|
840
|
+
return Array.isArray(value) && value.length === 0 ? [] : [{
|
|
841
|
+
kind: "unexpected_value",
|
|
842
|
+
field: "options"
|
|
843
|
+
}];
|
|
844
|
+
}
|
|
845
|
+
function parseFreePricing(input, currency) {
|
|
846
|
+
const issues = [...unexpectedValues(input, [
|
|
847
|
+
"singlePriceAmount",
|
|
848
|
+
"slidingMinAmount",
|
|
849
|
+
"slidingMaxAmount",
|
|
850
|
+
"slidingSuggestedAmount"
|
|
851
|
+
]), ...invalidOptionsIssue(input.options)];
|
|
852
|
+
const nonEmptyIssues = fromReadonlyArray(issues);
|
|
853
|
+
return nonEmptyIssues.ok ? err(nonEmptyIssues.value) : ok({
|
|
854
|
+
kind: "free",
|
|
855
|
+
currency
|
|
856
|
+
});
|
|
857
|
+
}
|
|
858
|
+
function parseSinglePricing(input, currency) {
|
|
859
|
+
const amount = typeof input.singlePriceAmount === "number" ? parsePositiveCents(input.singlePriceAmount) : err({ kind: "invalid-number" });
|
|
860
|
+
const issues = [
|
|
861
|
+
...unexpectedValues(input, [
|
|
862
|
+
"slidingMinAmount",
|
|
863
|
+
"slidingMaxAmount",
|
|
864
|
+
"slidingSuggestedAmount"
|
|
865
|
+
]),
|
|
866
|
+
...invalidOptionsIssue(input.options),
|
|
867
|
+
...amount.ok ? [] : [{
|
|
868
|
+
kind: "invalid_amount",
|
|
869
|
+
field: "singlePriceAmount",
|
|
870
|
+
error: amount.error
|
|
871
|
+
}]
|
|
872
|
+
];
|
|
873
|
+
const nonEmptyIssues = fromReadonlyArray(issues);
|
|
874
|
+
if (nonEmptyIssues.ok) return err(nonEmptyIssues.value);
|
|
875
|
+
if (!amount.ok) return err(oneIssue({
|
|
876
|
+
kind: "invalid_amount",
|
|
877
|
+
field: "singlePriceAmount",
|
|
878
|
+
error: amount.error
|
|
879
|
+
}));
|
|
880
|
+
return ok({
|
|
881
|
+
kind: "single",
|
|
882
|
+
currency,
|
|
883
|
+
amount: amount.value
|
|
884
|
+
});
|
|
885
|
+
}
|
|
886
|
+
function parseRawPricingOption(raw, index) {
|
|
887
|
+
if (typeof raw !== "object" || raw === null || Array.isArray(raw)) {
|
|
888
|
+
return err(oneIssue({
|
|
889
|
+
kind: "invalid_option",
|
|
890
|
+
index,
|
|
891
|
+
issue: { kind: "invalid_object" }
|
|
892
|
+
}));
|
|
893
|
+
}
|
|
894
|
+
const parsed = parseEventPricingOption(raw);
|
|
895
|
+
if (parsed.ok) return parsed;
|
|
896
|
+
const [first, ...rest] = parsed.error;
|
|
897
|
+
return err([{
|
|
898
|
+
kind: "invalid_option",
|
|
899
|
+
index,
|
|
900
|
+
issue: first
|
|
901
|
+
}, ...rest.map((issue) => ({
|
|
902
|
+
kind: "invalid_option",
|
|
903
|
+
index,
|
|
904
|
+
issue
|
|
905
|
+
}))]);
|
|
906
|
+
}
|
|
907
|
+
function parseTieredPricing(input, currency) {
|
|
908
|
+
const structuralIssues = unexpectedValues(input, [
|
|
909
|
+
"singlePriceAmount",
|
|
910
|
+
"slidingMinAmount",
|
|
911
|
+
"slidingMaxAmount",
|
|
912
|
+
"slidingSuggestedAmount"
|
|
913
|
+
]);
|
|
914
|
+
if (!Array.isArray(input.options) || input.options.length === 0) {
|
|
915
|
+
return err([{ kind: "tiered_options_empty" }, ...structuralIssues]);
|
|
916
|
+
}
|
|
917
|
+
const parsed = input.options.map(parseRawPricingOption);
|
|
918
|
+
const issues = [...structuralIssues, ...parsed.flatMap((result) => result.ok ? [] : result.error)];
|
|
919
|
+
const nonEmptyIssues = fromReadonlyArray(issues);
|
|
920
|
+
if (nonEmptyIssues.ok) return err(nonEmptyIssues.value);
|
|
921
|
+
const options = parsed.flatMap((result) => result.ok ? [result.value] : []);
|
|
922
|
+
const nonEmptyOptions = fromReadonlyArray(options);
|
|
923
|
+
if (!nonEmptyOptions.ok) {
|
|
924
|
+
return err(oneIssue({ kind: "tiered_options_empty" }));
|
|
925
|
+
}
|
|
926
|
+
return ok({
|
|
927
|
+
kind: "tiered",
|
|
928
|
+
currency,
|
|
929
|
+
options: nonEmptyOptions.value
|
|
930
|
+
});
|
|
931
|
+
}
|
|
932
|
+
function amountIssue(field, result) {
|
|
933
|
+
return result.ok ? [] : [{
|
|
934
|
+
kind: "invalid_amount",
|
|
935
|
+
field,
|
|
936
|
+
error: result.error
|
|
937
|
+
}];
|
|
938
|
+
}
|
|
939
|
+
function slidingRangeIssues(minimum, maximum, suggested) {
|
|
940
|
+
if (!minimum.ok || !maximum.ok || !suggested.ok) return [];
|
|
941
|
+
const issues = [];
|
|
942
|
+
if (minimum.value > maximum.value) issues.push({ kind: "invalid_sliding_range" });
|
|
943
|
+
if (suggested.value !== null && (suggested.value < minimum.value || suggested.value > maximum.value)) {
|
|
944
|
+
issues.push({ kind: "suggested_outside_range" });
|
|
945
|
+
}
|
|
946
|
+
return issues;
|
|
947
|
+
}
|
|
948
|
+
function parseSlidingPricing(input, currency) {
|
|
949
|
+
const minimum = typeof input.slidingMinAmount === "number" ? parseNonNegativeCents(input.slidingMinAmount) : err({ kind: "invalid-number" });
|
|
950
|
+
const maximum = typeof input.slidingMaxAmount === "number" ? parsePositiveCents(input.slidingMaxAmount) : err({ kind: "invalid-number" });
|
|
951
|
+
const suggested = isNullish(input.slidingSuggestedAmount) ? ok(null) : typeof input.slidingSuggestedAmount === "number" ? parseNonNegativeCents(input.slidingSuggestedAmount) : err({ kind: "invalid-number" });
|
|
952
|
+
const issues = [
|
|
953
|
+
...unexpectedValues(input, ["singlePriceAmount"]),
|
|
954
|
+
...invalidOptionsIssue(input.options),
|
|
955
|
+
...amountIssue("slidingMinAmount", minimum),
|
|
956
|
+
...amountIssue("slidingMaxAmount", maximum),
|
|
957
|
+
...amountIssue("slidingSuggestedAmount", suggested),
|
|
958
|
+
...slidingRangeIssues(minimum, maximum, suggested)
|
|
959
|
+
];
|
|
960
|
+
const nonEmptyIssues = fromReadonlyArray(issues);
|
|
961
|
+
if (nonEmptyIssues.ok) return err(nonEmptyIssues.value);
|
|
962
|
+
if (!minimum.ok) return err(oneIssue({
|
|
963
|
+
kind: "invalid_amount",
|
|
964
|
+
field: "slidingMinAmount",
|
|
965
|
+
error: minimum.error
|
|
966
|
+
}));
|
|
967
|
+
if (!maximum.ok) return err(oneIssue({
|
|
968
|
+
kind: "invalid_amount",
|
|
969
|
+
field: "slidingMaxAmount",
|
|
970
|
+
error: maximum.error
|
|
971
|
+
}));
|
|
972
|
+
if (!suggested.ok) return err(oneIssue({
|
|
973
|
+
kind: "invalid_amount",
|
|
974
|
+
field: "slidingSuggestedAmount",
|
|
975
|
+
error: suggested.error
|
|
976
|
+
}));
|
|
977
|
+
return ok({
|
|
978
|
+
kind: "sliding_scale",
|
|
979
|
+
currency,
|
|
980
|
+
minimum: minimum.value,
|
|
981
|
+
maximum: maximum.value,
|
|
982
|
+
suggested: suggested.value
|
|
983
|
+
});
|
|
984
|
+
}
|
|
985
|
+
function parseEventPricing(input) {
|
|
986
|
+
if (!isEventPricingStrategy(input.pricingStrategy)) {
|
|
987
|
+
return err(oneIssue({
|
|
988
|
+
kind: "invalid_strategy",
|
|
989
|
+
value: input.pricingStrategy
|
|
990
|
+
}));
|
|
991
|
+
}
|
|
992
|
+
const currency = typeof input.currency === "string" ? parseCurrencyCode(input.currency) : null;
|
|
993
|
+
if (currency === null) return err(oneIssue({
|
|
994
|
+
kind: "invalid_currency",
|
|
995
|
+
value: input.currency
|
|
996
|
+
}));
|
|
997
|
+
switch (input.pricingStrategy) {
|
|
998
|
+
case "free": return parseFreePricing(input, currency);
|
|
999
|
+
case "single": return parseSinglePricing(input, currency);
|
|
1000
|
+
case "tiered": return parseTieredPricing(input, currency);
|
|
1001
|
+
case "sliding_scale": return parseSlidingPricing(input, currency);
|
|
1002
|
+
default: return assertNever(input.pricingStrategy);
|
|
1003
|
+
}
|
|
1004
|
+
}
|
|
1005
|
+
function isEventPricingStrategy(value) {
|
|
1006
|
+
return EVENT_PRICING_STRATEGIES.some((strategy) => strategy === value);
|
|
1007
|
+
}
|
|
1008
|
+
|
|
1009
|
+
//#endregion
|
|
1010
|
+
//#region ../core/src/planner.ts
|
|
1011
|
+
function plannerNoChange(details = {}) {
|
|
1012
|
+
return {
|
|
1013
|
+
...details,
|
|
1014
|
+
tag: "no_change"
|
|
1015
|
+
};
|
|
1016
|
+
}
|
|
1017
|
+
function plannerUpdate(details = {}) {
|
|
1018
|
+
return {
|
|
1019
|
+
...details,
|
|
1020
|
+
tag: "update"
|
|
1021
|
+
};
|
|
1022
|
+
}
|
|
1023
|
+
/** Identity at runtime; marks a planner-derived patch as shallow readonly. */
|
|
1024
|
+
function plannerPatch(patch) {
|
|
1025
|
+
return patch;
|
|
1026
|
+
}
|
|
1027
|
+
/** Default clock for production callers that do not inject time explicitly. */
|
|
1028
|
+
const systemPlanningClock = { now: () => Date.now() };
|
|
1029
|
+
/** Resolve current time from an explicit timestamp, injected clock, or system clock. */
|
|
1030
|
+
function resolvePlanningNow(input = {}) {
|
|
1031
|
+
if (typeof input.now === "number") {
|
|
1032
|
+
return input.now;
|
|
1033
|
+
}
|
|
1034
|
+
return (input.clock ?? systemPlanningClock).now();
|
|
1035
|
+
}
|
|
1036
|
+
/** Resolve current time as a Date from an explicit Date, injected clock, or system clock. */
|
|
1037
|
+
function resolvePlanningNowDate(input = {}) {
|
|
1038
|
+
if (input.now !== undefined) {
|
|
1039
|
+
return input.now;
|
|
1040
|
+
}
|
|
1041
|
+
return new Date((input.clock ?? systemPlanningClock).now());
|
|
1042
|
+
}
|
|
1043
|
+
/**
|
|
1044
|
+
* Resolve a Date-compatible planner input.
|
|
1045
|
+
*
|
|
1046
|
+
* Precedence: bare Date, `{ now }`, `{ clock }`, then `systemPlanningClock`.
|
|
1047
|
+
* Explicit Date inputs are returned by reference, not cloned.
|
|
1048
|
+
*/
|
|
1049
|
+
function resolvePlanningDateInput(input = {}) {
|
|
1050
|
+
if (input instanceof Date) {
|
|
1051
|
+
return input;
|
|
1052
|
+
}
|
|
1053
|
+
return resolvePlanningNowDate(input);
|
|
1054
|
+
}
|
|
1055
|
+
/** Build an enabled selection while preserving literal value types. */
|
|
1056
|
+
function enabledSelection(value) {
|
|
1057
|
+
return {
|
|
1058
|
+
enabled: true,
|
|
1059
|
+
value
|
|
1060
|
+
};
|
|
1061
|
+
}
|
|
1062
|
+
/** Build a disabled selection. */
|
|
1063
|
+
function disabledSelection() {
|
|
1064
|
+
return { enabled: false };
|
|
1065
|
+
}
|
|
1066
|
+
|
|
541
1067
|
//#endregion
|
|
542
1068
|
//#region ../core/src/event-pricing.ts
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
"
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
1069
|
+
/** Capacity closes booking, but does not make an otherwise eligible price unknown. */
|
|
1070
|
+
function isPublicEventPricingOptionDisplayable(option) {
|
|
1071
|
+
if (!option.isActive) return false;
|
|
1072
|
+
if (option.availability.kind === "available") return true;
|
|
1073
|
+
const reason = option.availability.reason;
|
|
1074
|
+
switch (reason) {
|
|
1075
|
+
case "sold_out":
|
|
1076
|
+
case "overallCapacityUnavailable": return true;
|
|
1077
|
+
case "inactive":
|
|
1078
|
+
case "not_on_sale_yet":
|
|
1079
|
+
case "sale_ended":
|
|
1080
|
+
case "attendance_mode_unavailable": return false;
|
|
1081
|
+
default: return assertNever(reason);
|
|
1082
|
+
}
|
|
550
1083
|
}
|
|
551
1084
|
function publicEventPricingDisplay(pricing) {
|
|
552
|
-
|
|
1085
|
+
switch (pricing.kind) {
|
|
1086
|
+
case "free":
|
|
1087
|
+
case "single":
|
|
1088
|
+
case "sliding_scale": return canonicalEventPricingDisplay(pricing);
|
|
1089
|
+
case "tiered": return resolvePricingOptionsPriceDisplay({
|
|
1090
|
+
options: pricing.options.filter(isPublicEventPricingOptionDisplayable),
|
|
1091
|
+
currency: pricing.currency,
|
|
1092
|
+
purpose: "configured"
|
|
1093
|
+
});
|
|
1094
|
+
case "external": return {
|
|
1095
|
+
kind: "external",
|
|
1096
|
+
label: pricing.label
|
|
1097
|
+
};
|
|
1098
|
+
}
|
|
553
1099
|
}
|
|
554
1100
|
function publicEventPricingType(pricing) {
|
|
555
1101
|
switch (pricing.kind) {
|
|
556
|
-
case "
|
|
557
|
-
case "
|
|
558
|
-
case "
|
|
559
|
-
case "
|
|
560
|
-
case "sliding_scale":
|
|
561
|
-
case "whole_series": return isFreePublicEventPriceDisplay(pricing.display) ? "free" : "paid";
|
|
1102
|
+
case "free": return "free";
|
|
1103
|
+
case "single":
|
|
1104
|
+
case "tiered":
|
|
1105
|
+
case "sliding_scale": return isFreePublicEventPriceDisplay(publicEventPricingDisplay(pricing)) ? "free" : "paid";
|
|
562
1106
|
case "external": return "external";
|
|
563
1107
|
}
|
|
564
1108
|
}
|
|
565
1109
|
function publicEventPricingCurrency(pricing) {
|
|
566
1110
|
switch (pricing.kind) {
|
|
567
|
-
case "
|
|
568
|
-
case "
|
|
569
|
-
case "
|
|
570
|
-
case "ticket_types":
|
|
1111
|
+
case "free":
|
|
1112
|
+
case "single":
|
|
1113
|
+
case "tiered":
|
|
571
1114
|
case "sliding_scale": return pricing.currency;
|
|
572
|
-
case "whole_series": return pricing.display.display.currency;
|
|
573
1115
|
case "external": return null;
|
|
574
1116
|
}
|
|
575
1117
|
}
|
|
576
1118
|
function publicEventScalarPrice(pricing) {
|
|
577
|
-
return pricing.kind === "
|
|
1119
|
+
return pricing.kind === "single" ? pricing.amount : null;
|
|
578
1120
|
}
|
|
579
|
-
function
|
|
1121
|
+
function publicEventPricingStrategy(pricing) {
|
|
580
1122
|
switch (pricing.kind) {
|
|
581
|
-
case "
|
|
582
|
-
case "
|
|
583
|
-
case "
|
|
1123
|
+
case "single": return "single";
|
|
1124
|
+
case "free": return "free";
|
|
1125
|
+
case "tiered": return "tiered";
|
|
584
1126
|
case "sliding_scale": return "sliding_scale";
|
|
585
|
-
case "drop_in":
|
|
586
|
-
case "whole_series":
|
|
587
1127
|
case "external": return undefined;
|
|
588
1128
|
}
|
|
589
1129
|
}
|
|
590
|
-
function
|
|
591
|
-
return pricing.kind === "
|
|
1130
|
+
function publicEventPricingOptions(pricing) {
|
|
1131
|
+
return pricing.kind === "tiered" ? pricing.options : [];
|
|
592
1132
|
}
|
|
593
1133
|
function isFreePublicEventPriceDisplay(display) {
|
|
594
1134
|
switch (display.kind) {
|
|
595
1135
|
case "free": return true;
|
|
596
|
-
case "whole_series": return display.display.kind === "free";
|
|
597
1136
|
case "exact":
|
|
598
1137
|
case "from":
|
|
599
1138
|
case "range":
|
|
@@ -602,15 +1141,96 @@ function isFreePublicEventPriceDisplay(display) {
|
|
|
602
1141
|
case "external": return false;
|
|
603
1142
|
}
|
|
604
1143
|
}
|
|
605
|
-
function
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
1144
|
+
function resolvePricingOptionSaleWindowState(option, now) {
|
|
1145
|
+
if (option.saleStartsAt !== null && option.saleStartsAt !== undefined && new Date(option.saleStartsAt) > now) {
|
|
1146
|
+
return "not_started";
|
|
1147
|
+
}
|
|
1148
|
+
if (option.saleEndsAt !== null && option.saleEndsAt !== undefined && new Date(option.saleEndsAt) < now) {
|
|
1149
|
+
return "ended";
|
|
1150
|
+
}
|
|
1151
|
+
return "open";
|
|
1152
|
+
}
|
|
1153
|
+
function resolveExactOrFreePriceDisplay(amount, currency) {
|
|
1154
|
+
const resolvedAmount = amount ?? 0;
|
|
1155
|
+
return resolvedAmount > 0 ? {
|
|
1156
|
+
kind: "exact",
|
|
1157
|
+
amount: resolvedAmount,
|
|
1158
|
+
currency
|
|
1159
|
+
} : {
|
|
1160
|
+
kind: "free",
|
|
1161
|
+
currency
|
|
1162
|
+
};
|
|
1163
|
+
}
|
|
1164
|
+
function resolvePricingOptionsPriceDisplay(input) {
|
|
1165
|
+
const now = input.purpose === "available_now" ? resolvePlanningNowDate({
|
|
1166
|
+
now: input.now,
|
|
1167
|
+
clock: input.clock
|
|
1168
|
+
}) : null;
|
|
1169
|
+
const amounts = input.options.filter((option) => option.isActive && (now === null || resolvePricingOptionSaleWindowState(option, now) === "open")).map((option) => option.priceAmount);
|
|
1170
|
+
return amounts.length === 0 ? {
|
|
1171
|
+
kind: "unavailable",
|
|
1172
|
+
currency: input.currency
|
|
1173
|
+
} : amountRangeDisplay(amounts, input.currency);
|
|
1174
|
+
}
|
|
1175
|
+
function amountRangeDisplay(amounts, currency) {
|
|
1176
|
+
const minAmount = Math.min(...amounts);
|
|
1177
|
+
const maxAmount = Math.max(...amounts);
|
|
1178
|
+
if (minAmount === maxAmount) {
|
|
1179
|
+
return minAmount === 0 ? {
|
|
1180
|
+
kind: "free",
|
|
1181
|
+
currency
|
|
1182
|
+
} : {
|
|
1183
|
+
kind: "exact",
|
|
1184
|
+
amount: minAmount,
|
|
1185
|
+
currency
|
|
1186
|
+
};
|
|
1187
|
+
}
|
|
1188
|
+
if (minAmount === 0) {
|
|
1189
|
+
const paidAmounts = amounts.filter((amount) => amount > 0);
|
|
1190
|
+
return {
|
|
1191
|
+
kind: "mixed",
|
|
1192
|
+
paidFromAmount: Math.min(...paidAmounts),
|
|
1193
|
+
paidToAmount: maxAmount,
|
|
1194
|
+
currency
|
|
1195
|
+
};
|
|
1196
|
+
}
|
|
1197
|
+
return {
|
|
1198
|
+
kind: "range",
|
|
1199
|
+
minAmount,
|
|
1200
|
+
maxAmount,
|
|
1201
|
+
currency
|
|
1202
|
+
};
|
|
1203
|
+
}
|
|
1204
|
+
/** Derives configured price presentation directly from canonical pricing. */
|
|
1205
|
+
function canonicalEventPricingDisplay(pricing) {
|
|
1206
|
+
switch (pricing.kind) {
|
|
1207
|
+
case "free": return {
|
|
1208
|
+
kind: "free",
|
|
1209
|
+
currency: pricing.currency
|
|
1210
|
+
};
|
|
1211
|
+
case "single": return {
|
|
1212
|
+
kind: "exact",
|
|
1213
|
+
amount: pricing.amount,
|
|
1214
|
+
currency: pricing.currency
|
|
1215
|
+
};
|
|
1216
|
+
case "sliding_scale": return pricing.minimum === pricing.maximum ? {
|
|
1217
|
+
kind: "exact",
|
|
1218
|
+
amount: pricing.maximum,
|
|
1219
|
+
currency: pricing.currency
|
|
1220
|
+
} : {
|
|
1221
|
+
kind: "range",
|
|
1222
|
+
minAmount: pricing.minimum,
|
|
1223
|
+
maxAmount: pricing.maximum,
|
|
1224
|
+
currency: pricing.currency
|
|
1225
|
+
};
|
|
1226
|
+
case "tiered": {
|
|
1227
|
+
return resolvePricingOptionsPriceDisplay({
|
|
1228
|
+
options: pricing.options,
|
|
1229
|
+
currency: pricing.currency,
|
|
1230
|
+
purpose: "configured"
|
|
1231
|
+
});
|
|
1232
|
+
}
|
|
1233
|
+
default: return assertNever(pricing);
|
|
614
1234
|
}
|
|
615
1235
|
}
|
|
616
1236
|
/**
|
|
@@ -629,7 +1249,6 @@ function formatBookingPriceDisplay(display, options = {}) {
|
|
|
629
1249
|
case "mixed": return display.paidToAmount > display.paidFromAmount ? `Free / ${formatCurrencyAmount(display.paidFromAmount, display.currency, options)} - ${formatCurrencyAmount(display.paidToAmount, display.currency, options)}` : `Free / ${formatCurrencyAmount(display.paidFromAmount, display.currency, options)}`;
|
|
630
1250
|
case "unavailable": return "Pricing unavailable";
|
|
631
1251
|
case "external": return display.label;
|
|
632
|
-
case "whole_series": return formatCoursePriceDisplayLabel(display.display, options);
|
|
633
1252
|
default: return assertNever(display);
|
|
634
1253
|
}
|
|
635
1254
|
}
|
|
@@ -640,17 +1259,48 @@ function formatEventPricing(pricing, options = {}) {
|
|
|
640
1259
|
|
|
641
1260
|
//#endregion
|
|
642
1261
|
//#region ../core/src/event-page-surface-subject.ts
|
|
643
|
-
function createEventSeriesPageSurfaceSubject(eventSeriesId, bookingScope) {
|
|
1262
|
+
function createEventSeriesPageSurfaceSubject(eventSeriesId, bookingScope, eventDestinationMode) {
|
|
1263
|
+
const mode = eventDestinationMode ? { eventDestinationMode } : {};
|
|
644
1264
|
return bookingScope === "whole_series" ? {
|
|
645
1265
|
kind: "eventSeries",
|
|
646
1266
|
eventSeriesId,
|
|
647
1267
|
bookingScope,
|
|
648
|
-
isWholeSeriesBooking: true
|
|
1268
|
+
isWholeSeriesBooking: true,
|
|
1269
|
+
...mode
|
|
649
1270
|
} : {
|
|
650
1271
|
kind: "eventSeries",
|
|
651
1272
|
eventSeriesId,
|
|
652
|
-
bookingScope
|
|
1273
|
+
bookingScope,
|
|
1274
|
+
...mode
|
|
1275
|
+
};
|
|
1276
|
+
}
|
|
1277
|
+
function createEventOfferingPageSurfaceSubject(eventOfferingId, input) {
|
|
1278
|
+
const mode = input.eventDestinationMode ? { eventDestinationMode: input.eventDestinationMode } : {};
|
|
1279
|
+
const identity = {
|
|
1280
|
+
kind: "eventOffering",
|
|
1281
|
+
eventOfferingId,
|
|
1282
|
+
catalogueEventSeriesIds: input.catalogueEventSeriesIds
|
|
653
1283
|
};
|
|
1284
|
+
return input.bookingScope === "whole_series" ? {
|
|
1285
|
+
...identity,
|
|
1286
|
+
bookingScope: input.bookingScope,
|
|
1287
|
+
isWholeSeriesBooking: true,
|
|
1288
|
+
...mode
|
|
1289
|
+
} : {
|
|
1290
|
+
...identity,
|
|
1291
|
+
bookingScope: input.bookingScope,
|
|
1292
|
+
...mode
|
|
1293
|
+
};
|
|
1294
|
+
}
|
|
1295
|
+
/**
|
|
1296
|
+
* Read the destination mode off a wire value.
|
|
1297
|
+
*
|
|
1298
|
+
* Absent or unrecognised reads as absent, which leaves the routed Event record
|
|
1299
|
+
* as the only authority — the behaviour every caller had before the field
|
|
1300
|
+
* existed.
|
|
1301
|
+
*/
|
|
1302
|
+
function parseEventPageSurfaceDestinationMode(value) {
|
|
1303
|
+
return value === "cms-managed" || value === "system-managed" ? value : undefined;
|
|
654
1304
|
}
|
|
655
1305
|
function parseEventPageSurfaceSubject(value) {
|
|
656
1306
|
if (!isRecord$6(value)) return err({ kind: "notObject" });
|
|
@@ -669,7 +1319,7 @@ function parseEventPageSurfaceSubject(value) {
|
|
|
669
1319
|
bookingScope: value.bookingScope
|
|
670
1320
|
});
|
|
671
1321
|
}
|
|
672
|
-
return ok(createEventSeriesPageSurfaceSubject(eventSeriesId.value, value.bookingScope));
|
|
1322
|
+
return ok(createEventSeriesPageSurfaceSubject(eventSeriesId.value, value.bookingScope, parseEventPageSurfaceDestinationMode(value.eventDestinationMode)));
|
|
673
1323
|
}
|
|
674
1324
|
case "eventOffering": {
|
|
675
1325
|
const eventOfferingId = parseEventOfferingId(value.eventOfferingId);
|
|
@@ -679,28 +1329,26 @@ function parseEventPageSurfaceSubject(value) {
|
|
|
679
1329
|
error: eventOfferingId.error
|
|
680
1330
|
});
|
|
681
1331
|
}
|
|
682
|
-
if (!Array.isArray(value.
|
|
1332
|
+
if (!Array.isArray(value.catalogueEventSeriesIds)) {
|
|
683
1333
|
return err({
|
|
684
|
-
kind: "
|
|
685
|
-
|
|
1334
|
+
kind: "invalidEventOfferingCatalogue",
|
|
1335
|
+
catalogue: value.catalogueEventSeriesIds
|
|
686
1336
|
});
|
|
687
1337
|
}
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
kind: "invalidEventSeriesId",
|
|
694
|
-
error: eventSeriesId.error
|
|
695
|
-
});
|
|
696
|
-
}
|
|
697
|
-
parsedSeriesIds.push(eventSeriesId.value);
|
|
1338
|
+
if (value.bookingScope !== "single_occurrence" && value.bookingScope !== "whole_series") {
|
|
1339
|
+
return err({
|
|
1340
|
+
kind: "invalidEventOfferingBookingScope",
|
|
1341
|
+
bookingScope: value.bookingScope
|
|
1342
|
+
});
|
|
698
1343
|
}
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
1344
|
+
const parsedCatalogueIds = parseEventSeriesIdList(value.catalogueEventSeriesIds);
|
|
1345
|
+
if (!parsedCatalogueIds.ok) return parsedCatalogueIds;
|
|
1346
|
+
const eventDestinationMode = parseEventPageSurfaceDestinationMode(value.eventDestinationMode);
|
|
1347
|
+
return ok(createEventOfferingPageSurfaceSubject(eventOfferingId.value, {
|
|
1348
|
+
catalogueEventSeriesIds: parsedCatalogueIds.value,
|
|
1349
|
+
bookingScope: value.bookingScope,
|
|
1350
|
+
...eventDestinationMode ? { eventDestinationMode } : {}
|
|
1351
|
+
}));
|
|
704
1352
|
}
|
|
705
1353
|
default: return err({
|
|
706
1354
|
kind: "unsupportedSubject",
|
|
@@ -711,136 +1359,22 @@ function parseEventPageSurfaceSubject(value) {
|
|
|
711
1359
|
function toEventPageSurfaceSubjectWire(subject) {
|
|
712
1360
|
return subject;
|
|
713
1361
|
}
|
|
714
|
-
function
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
}
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
}
|
|
726
|
-
function unsafeAsPositiveCents(value) {
|
|
727
|
-
return value;
|
|
728
|
-
}
|
|
729
|
-
function unwrapCents(value) {
|
|
730
|
-
return value;
|
|
731
|
-
}
|
|
732
|
-
function isWholeCents(value) {
|
|
733
|
-
return Number.isFinite(value) && Number.isInteger(value);
|
|
734
|
-
}
|
|
735
|
-
function isNonNegativeWholeCents(value) {
|
|
736
|
-
return isWholeCents(value) && value >= 0;
|
|
737
|
-
}
|
|
738
|
-
function isPositiveWholeCents(value) {
|
|
739
|
-
return isWholeCents(value) && value > 0;
|
|
740
|
-
}
|
|
741
|
-
/**
|
|
742
|
-
* Normalizes a derived amount to signed whole cents. Use parse*Cents for
|
|
743
|
-
* untrusted input that should fail instead of truncating or clamping.
|
|
744
|
-
*/
|
|
745
|
-
function toCents(value) {
|
|
746
|
-
return unsafeAsCents(Math.trunc(value));
|
|
747
|
-
}
|
|
748
|
-
/**
|
|
749
|
-
* Normalizes a derived amount to non-negative whole cents. This intentionally
|
|
750
|
-
* clamps negatives to zero; use parseNonNegativeCents for validation.
|
|
751
|
-
*/
|
|
752
|
-
function toNonNegativeCents(value) {
|
|
753
|
-
return unsafeAsNonNegativeCents(Math.max(0, Math.trunc(value)));
|
|
754
|
-
}
|
|
755
|
-
function parseCents(value) {
|
|
756
|
-
if (!Number.isFinite(value)) {
|
|
757
|
-
return err({ kind: "invalid-number" });
|
|
758
|
-
}
|
|
759
|
-
if (!Number.isInteger(value)) {
|
|
760
|
-
return err({ kind: "not-whole-cents" });
|
|
761
|
-
}
|
|
762
|
-
return ok(unsafeAsCents(value));
|
|
763
|
-
}
|
|
764
|
-
function parseNonNegativeCents(value) {
|
|
765
|
-
const parsed = parseCents(value);
|
|
766
|
-
if (!parsed.ok) {
|
|
767
|
-
return parsed;
|
|
768
|
-
}
|
|
769
|
-
if (parsed.value < 0) {
|
|
770
|
-
return err({ kind: "negative" });
|
|
771
|
-
}
|
|
772
|
-
return ok(unsafeAsNonNegativeCents(parsed.value));
|
|
773
|
-
}
|
|
774
|
-
function parsePositiveCents(value) {
|
|
775
|
-
const parsed = parseNonNegativeCents(value);
|
|
776
|
-
if (!parsed.ok) {
|
|
777
|
-
return parsed;
|
|
778
|
-
}
|
|
779
|
-
if (parsed.value === 0) {
|
|
780
|
-
return err({ kind: "zero-not-allowed" });
|
|
781
|
-
}
|
|
782
|
-
return ok(unsafeAsPositiveCents(parsed.value));
|
|
783
|
-
}
|
|
784
|
-
function normalizeMoneyInput(value, options) {
|
|
785
|
-
return (options.stripFormatting === true ? value.replace(/[^0-9.-]/g, "") : value).trim();
|
|
786
|
-
}
|
|
787
|
-
function parseFiniteMoneyAmount(value, options) {
|
|
788
|
-
const normalized = normalizeMoneyInput(value, options);
|
|
789
|
-
if (normalized.length === 0) {
|
|
790
|
-
return err({ kind: "empty" });
|
|
791
|
-
}
|
|
792
|
-
const amount = Number(normalized);
|
|
793
|
-
if (!Number.isFinite(amount)) {
|
|
794
|
-
return err({ kind: "invalid-number" });
|
|
795
|
-
}
|
|
796
|
-
return ok(amount);
|
|
797
|
-
}
|
|
798
|
-
function roundMoneyAmountToCents(amount) {
|
|
799
|
-
return Math.round(amount * 100);
|
|
800
|
-
}
|
|
801
|
-
function parsePositiveMoneyInputToCents(value, options = {}) {
|
|
802
|
-
const parsed = parseFiniteMoneyAmount(value, options);
|
|
803
|
-
if (!parsed.ok) {
|
|
804
|
-
return parsed;
|
|
805
|
-
}
|
|
806
|
-
if (parsed.value < 0) {
|
|
807
|
-
return err({ kind: "negative" });
|
|
808
|
-
}
|
|
809
|
-
const amountCents = roundMoneyAmountToCents(parsed.value);
|
|
810
|
-
if (amountCents <= 0) {
|
|
811
|
-
return err({ kind: "zero-not-allowed" });
|
|
812
|
-
}
|
|
813
|
-
return ok(unsafeAsPositiveCents(amountCents));
|
|
814
|
-
}
|
|
815
|
-
function parseNonNegativeMoneyInputToCents(value, options = {}) {
|
|
816
|
-
const parsed = parseFiniteMoneyAmount(value, options);
|
|
817
|
-
if (!parsed.ok) {
|
|
818
|
-
return parsed;
|
|
819
|
-
}
|
|
820
|
-
if (parsed.value < 0) {
|
|
821
|
-
return err({ kind: "negative" });
|
|
822
|
-
}
|
|
823
|
-
return ok(unsafeAsNonNegativeCents(roundMoneyAmountToCents(parsed.value)));
|
|
824
|
-
}
|
|
825
|
-
function parseMoneyInputToCents(value, options = {}) {
|
|
826
|
-
const parsed = parseFiniteMoneyAmount(value, options);
|
|
827
|
-
if (!parsed.ok) {
|
|
828
|
-
return null;
|
|
829
|
-
}
|
|
830
|
-
if (options.allowZero === true ? parsed.value < 0 : parsed.value <= 0) {
|
|
831
|
-
return null;
|
|
1362
|
+
function parseEventSeriesIdList(values) {
|
|
1363
|
+
const parsed = [];
|
|
1364
|
+
for (const value of values) {
|
|
1365
|
+
const eventSeriesId = parseEventSeriesId(value);
|
|
1366
|
+
if (!eventSeriesId.ok) {
|
|
1367
|
+
return err({
|
|
1368
|
+
kind: "invalidEventSeriesId",
|
|
1369
|
+
error: eventSeriesId.error
|
|
1370
|
+
});
|
|
1371
|
+
}
|
|
1372
|
+
parsed.push(eventSeriesId.value);
|
|
832
1373
|
}
|
|
833
|
-
return
|
|
1374
|
+
return ok(parsed);
|
|
834
1375
|
}
|
|
835
|
-
function
|
|
836
|
-
|
|
837
|
-
return "";
|
|
838
|
-
}
|
|
839
|
-
const sign = amountCents < 0 ? "-" : "";
|
|
840
|
-
const absoluteCents = Math.abs(Math.trunc(amountCents));
|
|
841
|
-
const units = Math.floor(absoluteCents / 100);
|
|
842
|
-
const fractional = String(absoluteCents % 100).padStart(2, "0");
|
|
843
|
-
return `${sign}${units}.${fractional}`;
|
|
1376
|
+
function isRecord$6(value) {
|
|
1377
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
844
1378
|
}
|
|
845
1379
|
|
|
846
1380
|
//#endregion
|
|
@@ -1824,19 +2358,6 @@ function unwrapNarrow(result) {
|
|
|
1824
2358
|
return result.value;
|
|
1825
2359
|
}
|
|
1826
2360
|
|
|
1827
|
-
//#endregion
|
|
1828
|
-
//#region ../core/src/non-empty-array.ts
|
|
1829
|
-
function fromArray(items) {
|
|
1830
|
-
const [first, ...rest] = items;
|
|
1831
|
-
if (first === undefined) {
|
|
1832
|
-
return err("empty_array");
|
|
1833
|
-
}
|
|
1834
|
-
return ok([first, ...rest]);
|
|
1835
|
-
}
|
|
1836
|
-
function mapNonEmptyArray(items, map) {
|
|
1837
|
-
return items.map(map);
|
|
1838
|
-
}
|
|
1839
|
-
|
|
1840
2361
|
//#endregion
|
|
1841
2362
|
//#region ../core/src/editor-state.ts
|
|
1842
2363
|
const unsafeAsEditorSiteId = (value) => value;
|
|
@@ -4653,61 +5174,104 @@ function foldSiteRouteTarget(target, cases) {
|
|
|
4653
5174
|
}
|
|
4654
5175
|
|
|
4655
5176
|
//#endregion
|
|
4656
|
-
//#region ../core/src/
|
|
4657
|
-
|
|
4658
|
-
|
|
4659
|
-
|
|
4660
|
-
|
|
4661
|
-
|
|
5177
|
+
//#region ../core/src/whole-series-run-schedule.ts
|
|
5178
|
+
/**
|
|
5179
|
+
* A session that is not happening.
|
|
5180
|
+
*
|
|
5181
|
+
* Exported because every surface that renders a run's schedule has to mark the
|
|
5182
|
+
* same weeks, and a second copy of the string is a second chance to disagree.
|
|
5183
|
+
*/
|
|
5184
|
+
const CANCELLED_SESSION_STATUS = "cancelled";
|
|
5185
|
+
/** A run the organiser called off. Spelled the same, about a different row. */
|
|
5186
|
+
const CANCELLED_SERIES_STATUS = "cancelled";
|
|
5187
|
+
function sessionStartMs(session) {
|
|
5188
|
+
return Date.parse(session.startsAt);
|
|
4662
5189
|
}
|
|
4663
|
-
|
|
4664
|
-
|
|
4665
|
-
|
|
4666
|
-
|
|
4667
|
-
|
|
5190
|
+
/**
|
|
5191
|
+
* The run's sessions that still count, in schedule order.
|
|
5192
|
+
*
|
|
5193
|
+
* Sessions whose start cannot be read are dropped rather than sorted to an
|
|
5194
|
+
* arbitrary end: an unparseable date is not a date a visitor can attend.
|
|
5195
|
+
*/
|
|
5196
|
+
function orderedLiveRunSessions(sessions) {
|
|
5197
|
+
return sessions.filter((session) => session.status !== CANCELLED_SESSION_STATUS && Number.isFinite(sessionStartMs(session))).sort((left, right) => sessionStartMs(left) - sessionStartMs(right));
|
|
4668
5198
|
}
|
|
4669
|
-
/**
|
|
4670
|
-
function
|
|
4671
|
-
|
|
5199
|
+
/** When the first session a visitor could attend begins. */
|
|
5200
|
+
function firstLiveRunSessionStartMs(sessions) {
|
|
5201
|
+
const first = orderedLiveRunSessions(sessions)[0];
|
|
5202
|
+
return first === undefined ? null : sessionStartMs(first);
|
|
4672
5203
|
}
|
|
4673
|
-
/**
|
|
4674
|
-
|
|
4675
|
-
|
|
4676
|
-
|
|
4677
|
-
|
|
4678
|
-
|
|
4679
|
-
|
|
4680
|
-
|
|
5204
|
+
/**
|
|
5205
|
+
* Booking stays open until the run's first surviving session has started.
|
|
5206
|
+
*
|
|
5207
|
+
* There has to be something left to sell: a run with no schedule at all, or one
|
|
5208
|
+
* whose every session has been cancelled, is closed rather than open. Adding a
|
|
5209
|
+
* future session opens it again, subject to the usual status, visibility and
|
|
5210
|
+
* admission rules.
|
|
5211
|
+
*/
|
|
5212
|
+
function isWholeSeriesRegistrationOpen(sessions, now) {
|
|
5213
|
+
const firstStart = firstLiveRunSessionStartMs(sessions);
|
|
5214
|
+
return firstStart !== null && firstStart > now.getTime();
|
|
4681
5215
|
}
|
|
4682
|
-
/**
|
|
4683
|
-
|
|
4684
|
-
|
|
4685
|
-
|
|
4686
|
-
|
|
4687
|
-
|
|
5216
|
+
/**
|
|
5217
|
+
* Where the run sits in its own life, independent of any one session.
|
|
5218
|
+
*
|
|
5219
|
+
* A cancelled or completed series status says so whatever the dates read.
|
|
5220
|
+
* Otherwise the surviving schedule decides: before the first session it is
|
|
5221
|
+
* upcoming, after the last session ends it is complete, and in between it is
|
|
5222
|
+
* under way. A run with nothing left to attend is `upcoming` and never
|
|
5223
|
+
* `in_progress` — an empty schedule has not started.
|
|
5224
|
+
*/
|
|
5225
|
+
function resolveWholeSeriesRunLifecycle(input, now) {
|
|
5226
|
+
if (input.seriesStatus === CANCELLED_SERIES_STATUS) return "cancelled";
|
|
5227
|
+
if (input.seriesStatus === "completed") return "completed";
|
|
5228
|
+
const live = orderedLiveRunSessions(input.sessions);
|
|
5229
|
+
const first = live[0];
|
|
5230
|
+
const last = live[live.length - 1];
|
|
5231
|
+
if (first === undefined || last === undefined) return "upcoming";
|
|
5232
|
+
if (sessionStartMs(first) > now.getTime()) return "upcoming";
|
|
5233
|
+
return Date.parse(last.endsAt) < now.getTime() ? "completed" : "in_progress";
|
|
4688
5234
|
}
|
|
4689
5235
|
/**
|
|
4690
|
-
*
|
|
5236
|
+
* Whether a prepared run is still inviting bookings at render time.
|
|
4691
5237
|
*
|
|
4692
|
-
*
|
|
4693
|
-
*
|
|
5238
|
+
* `lifecycle` and `enrollmentOpen` come from the projection; the first session
|
|
5239
|
+
* start is the one fact re-read against `now`, because that is the boundary a
|
|
5240
|
+
* cached page can cross while it sits in front of a visitor.
|
|
4694
5241
|
*/
|
|
4695
|
-
function
|
|
4696
|
-
|
|
4697
|
-
return
|
|
5242
|
+
function resolveWholeSeriesBookingPhase(run, now) {
|
|
5243
|
+
switch (run.lifecycle) {
|
|
5244
|
+
case "cancelled": return {
|
|
5245
|
+
kind: "closed",
|
|
5246
|
+
reason: "cancelled"
|
|
5247
|
+
};
|
|
5248
|
+
case "in_progress": return {
|
|
5249
|
+
kind: "closed",
|
|
5250
|
+
reason: "in-progress"
|
|
5251
|
+
};
|
|
5252
|
+
case "completed": return {
|
|
5253
|
+
kind: "closed",
|
|
5254
|
+
reason: "completed"
|
|
5255
|
+
};
|
|
5256
|
+
case "upcoming": {
|
|
5257
|
+
if (firstLiveRunSessionStartMs(run.sessions) === null) {
|
|
5258
|
+
return {
|
|
5259
|
+
kind: "closed",
|
|
5260
|
+
reason: "no-sessions"
|
|
5261
|
+
};
|
|
5262
|
+
}
|
|
5263
|
+
if (!isWholeSeriesRegistrationOpen(run.sessions, now)) {
|
|
5264
|
+
return {
|
|
5265
|
+
kind: "closed",
|
|
5266
|
+
reason: "in-progress"
|
|
5267
|
+
};
|
|
5268
|
+
}
|
|
5269
|
+
return run.enrollmentOpen ? { kind: "open" } : {
|
|
5270
|
+
kind: "closed",
|
|
5271
|
+
reason: "enrolment-closed"
|
|
5272
|
+
};
|
|
5273
|
+
}
|
|
4698
5274
|
}
|
|
4699
|
-
return resolvePlanningNowDate(input);
|
|
4700
|
-
}
|
|
4701
|
-
/** Build an enabled selection while preserving literal value types. */
|
|
4702
|
-
function enabledSelection(value) {
|
|
4703
|
-
return {
|
|
4704
|
-
enabled: true,
|
|
4705
|
-
value
|
|
4706
|
-
};
|
|
4707
|
-
}
|
|
4708
|
-
/** Build a disabled selection. */
|
|
4709
|
-
function disabledSelection() {
|
|
4710
|
-
return { enabled: false };
|
|
4711
5275
|
}
|
|
4712
5276
|
|
|
4713
5277
|
//#endregion
|
|
@@ -5382,4 +5946,4 @@ function orderPresetIdsForVertical(verticalId, presetIds) {
|
|
|
5382
5946
|
}
|
|
5383
5947
|
|
|
5384
5948
|
//#endregion
|
|
5385
|
-
export {
|
|
5949
|
+
export { fromArray as $, normalizeLocationAddressText as A, unsafeAsStorageBucketName as At, getLocalScheduleTime as B, parseMailchimpTagName as C, unsafeAsPositiveSeconds as Ct, isCanonicalRichTextDoc as D, unsafeAsSiteId as Dt, parseNewsletterSignupDestinationProvider as E, unsafeAsSiteFontId as Et, formatBookingTime as F, ok as Ft, parseBookingTimeZone as G, toBookingTimeZone as H, formatLocalScheduleDate as I, formatEventPricing as J, parseEventPageSurfaceSubject as K, formatLocalScheduleTime as L, buildBookingUtcDateWindow as M, unsafeAsUploadSessionId as Mt, formatBookingDate as N, unsafeAsUserId as Nt, assertAllowedPublicUrl as O, unsafeAsSiteScopedStoragePath as Ot, formatBookingDateParts as P, err as Pt, publicEventPricingType as Q, getBookingLocalDateKey as R, parseMailchimpAudienceId as S, unsafeAsPositiveByteCount as St, parseNewsletterSignupAuthoringDestinationValue as T, unsafeAsSafeDownloadFilename as Tt, toScheduledBookingDateRange as U, parseUtcIsoDateTimeString as V, toScheduledBookingDateTime as W, publicEventPricingCurrency as X, isPublicEventPricingOptionDisplayable as Y, publicEventPricingDisplay as Z, parseManagementKey as _, unsafeAsEventSeriesId as _t, parseVerticalPresetId as a, parseMediaAssetId as at, parseFooterCompositionV1 as b, unsafeAsOccurrenceId as bt, EVENT_PRESET_VALUES as c, parseUserId as ct, WHOLE_SERIES_EVENT_PRESET_VALUES as d, unsafeAsBookingSessionId as dt, mapNonEmptyArray as et, CANCELLED_SESSION_STATUS as f, unsafeAsDeclaredMimeType as ft, exposeSecret as g, unsafeAsEventOfferingId as gt, createSecret as h, unsafeAsEntitlementId as ht, parseVerticalId as i, parseEventSeriesId as it, toLocationCoordinates as j, unsafeAsStoragePath as jt, isPresent as k, unsafeAsStaffMemberId as kt, ORDINARY_EVENT_PRESET_VALUES as l, unsafeAsAppointmentResourceId as lt, resolveWholeSeriesBookingPhase as m, unsafeAsDetectedMimeType as mt, canonicalizeConfiguredBookingVerticalConfig as n, assertNever as nt, resolveSiteVerticalBookingConfig as o, parsePageId as ot, orderedLiveRunSessions as p, unsafeAsDemoCapability as pt, formatBookingPriceDisplay as q, isEventSeriesVerticalPresetDefinition as r, parseApiKeyId as rt, DEFAULT_EVENT_TYPE_PRESETS as s, parseSiteId as st, areConfiguredBookingVerticalConfigsEqual as t, formatCurrencyAmount as tt, SURFACE_EVENT_PRESET_VALUES as u, unsafeAsBookingRequirementId as ut, stableJsonStringify as v, unsafeAsFileChecksumSha256 as vt, parseNewsletterListId as w, unsafeAsProductId as wt, parseFooterModuleId as x, unsafeAsOriginalFilename as xt, footerNavigationLayouts as y, unsafeAsMediaAssetId as yt, getLocalScheduleDate as z };
|