riverbankcms 0.2.0 → 0.4.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 +14 -0
- package/dist/acceptance/matrix.mjs +119 -6
- package/dist/cli/index.mjs +15262 -4476
- package/dist/cli/init-docs/content/agents-section.md +9 -2
- package/dist/cli/init-docs/content/cli-reference.md +105 -8
- package/dist/cli/init-docs/content/content-management.md +50 -7
- package/dist/cli/init-docs/content/getting-started.md +8 -10
- package/dist/cli/init-docs/content/workflow-add-block.md +8 -1
- package/dist/cli/init-docs/content/workflow-block-extensions.md +25 -9
- package/dist/cli/init-docs/content/workflow-custom-block.md +35 -8
- package/dist/cli/init-docs/content/workflow-preview-mode.md +30 -6
- package/dist/cli/init-docs/content/workflow-remote-setup.md +66 -10
- package/dist/{eventUpsertInputSchema-BUeXa6jk.mjs → eventUpsertInputSchema-Dg60PKJI.mjs} +13 -15
- package/dist/{load-config-CJUwrGHA.mjs → load-config-Co37ncZD.mjs} +1 -1
- package/dist/load-config-Dz8ygC-Z.mjs +5 -0
- package/dist/plan-CaWqAqfn.mjs +2213 -0
- package/dist/scaffold/index.mjs +5 -0
- package/dist/sdk-event-upsert-C6zsxDcf.mjs +887 -0
- package/dist/{sdk-peer-D9FHJ54i.mjs → sdk-peer-DZNUBvf5.mjs} +5 -2
- package/dist/{sdk-event-upsert-CTqsrhFT.mjs → src-BRAF1Dee.mjs} +707 -822
- package/dist/{style-lftMrun_.mjs → style-TmApGKZX.mjs} +32667 -31550
- package/dist/testing.mjs +4 -3
- package/package.json +6 -1
- package/dist/load-config-CcQyVy3r.mjs +0 -5
|
@@ -1,6 +1,4 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { z } from "zod";
|
|
3
|
-
|
|
4
2
|
//#region ../core/src/result.ts
|
|
5
3
|
function ok(value) {
|
|
6
4
|
return {
|
|
@@ -46,6 +44,7 @@ const unsafeAsEntryId = (id) => id;
|
|
|
46
44
|
const unsafeAsPreviewEntryId = (id) => id;
|
|
47
45
|
const unsafeAsEventInstanceId = (id) => id;
|
|
48
46
|
const unsafeAsSiteId = (id) => id;
|
|
47
|
+
const unsafeAsApiKeyId = (id) => id;
|
|
49
48
|
const unsafeAsSiteRouteId = (id) => id;
|
|
50
49
|
const unsafeAsPageId = (id) => id;
|
|
51
50
|
const unsafeAsPageSurfaceId = (id) => id;
|
|
@@ -155,6 +154,7 @@ const unsafeAsEncounterRecordId = (id) => id;
|
|
|
155
154
|
const unsafeAsRecordEntryId = (id) => id;
|
|
156
155
|
const unsafeAsProtectedPayloadKeyVersion = (id) => id;
|
|
157
156
|
const parseSiteId = (value) => parseUuidBrand(value, unsafeAsSiteId);
|
|
157
|
+
const parseApiKeyId = (value) => parseUuidBrand(value, unsafeAsApiKeyId);
|
|
158
158
|
const parseSiteRouteId = (value) => parseUuidBrand(value, unsafeAsSiteRouteId);
|
|
159
159
|
const parsePageId = (value) => parseUuidBrand(value, unsafeAsPageId);
|
|
160
160
|
const parsePageSurfaceId = (value) => parseUuidBrand(value, unsafeAsPageSurfaceId);
|
|
@@ -609,6 +609,7 @@ function formatCoursePriceDisplayLabel(display, options) {
|
|
|
609
609
|
case "exact": return `Course (${formatCurrencyAmount(display.amount, display.currency, options)})`;
|
|
610
610
|
case "from": return `Course (From ${formatCurrencyAmount(display.amount, display.currency, options)})`;
|
|
611
611
|
case "range": return `Course (${formatCurrencyAmount(display.minAmount, display.currency, options)} - ${formatCurrencyAmount(display.maxAmount, display.currency, options)})`;
|
|
612
|
+
case "mixed": return display.paidToAmount > display.paidFromAmount ? `Course (Free / ${formatCurrencyAmount(display.paidFromAmount, display.currency, options)} - ${formatCurrencyAmount(display.paidToAmount, display.currency, options)})` : `Course (Free / ${formatCurrencyAmount(display.paidFromAmount, display.currency, options)})`;
|
|
612
613
|
default: return assertNever(display);
|
|
613
614
|
}
|
|
614
615
|
}
|
|
@@ -639,18 +640,36 @@ function formatEventPricing(pricing, options = {}) {
|
|
|
639
640
|
|
|
640
641
|
//#endregion
|
|
641
642
|
//#region ../core/src/event-page-surface-subject.ts
|
|
643
|
+
function createEventSeriesPageSurfaceSubject(eventSeriesId, bookingScope) {
|
|
644
|
+
return bookingScope === "whole_series" ? {
|
|
645
|
+
kind: "eventSeries",
|
|
646
|
+
eventSeriesId,
|
|
647
|
+
bookingScope,
|
|
648
|
+
isWholeSeriesBooking: true
|
|
649
|
+
} : {
|
|
650
|
+
kind: "eventSeries",
|
|
651
|
+
eventSeriesId,
|
|
652
|
+
bookingScope
|
|
653
|
+
};
|
|
654
|
+
}
|
|
642
655
|
function parseEventPageSurfaceSubject(value) {
|
|
643
|
-
if (!isRecord$
|
|
656
|
+
if (!isRecord$6(value)) return err({ kind: "notObject" });
|
|
644
657
|
switch (value.kind) {
|
|
645
658
|
case "eventSeries": {
|
|
646
659
|
const eventSeriesId = parseEventSeriesId(value.eventSeriesId);
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
660
|
+
if (!eventSeriesId.ok) {
|
|
661
|
+
return err({
|
|
662
|
+
kind: "invalidEventSeriesId",
|
|
663
|
+
error: eventSeriesId.error
|
|
664
|
+
});
|
|
665
|
+
}
|
|
666
|
+
if (value.bookingScope !== "single_occurrence" && value.bookingScope !== "whole_series") {
|
|
667
|
+
return err({
|
|
668
|
+
kind: "invalidEventSeriesBookingScope",
|
|
669
|
+
bookingScope: value.bookingScope
|
|
670
|
+
});
|
|
671
|
+
}
|
|
672
|
+
return ok(createEventSeriesPageSurfaceSubject(eventSeriesId.value, value.bookingScope));
|
|
654
673
|
}
|
|
655
674
|
case "eventOffering": {
|
|
656
675
|
const eventOfferingId = parseEventOfferingId(value.eventOfferingId);
|
|
@@ -692,7 +711,7 @@ function parseEventPageSurfaceSubject(value) {
|
|
|
692
711
|
function toEventPageSurfaceSubjectWire(subject) {
|
|
693
712
|
return subject;
|
|
694
713
|
}
|
|
695
|
-
function isRecord$
|
|
714
|
+
function isRecord$6(value) {
|
|
696
715
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
697
716
|
}
|
|
698
717
|
|
|
@@ -2233,7 +2252,7 @@ function reconcileParticipantRoster(input) {
|
|
|
2233
2252
|
}
|
|
2234
2253
|
function planParticipantSubjectResolution(input) {
|
|
2235
2254
|
const existingParticipantIdByEmail = new Map(input.existingParticipantsByEmail.map((participant) => [participant.email, participant.participantId]));
|
|
2236
|
-
const duplicateSlotKeysByEmail = new Map(input.projection.issues.filter((issue
|
|
2255
|
+
const duplicateSlotKeysByEmail = new Map(input.projection.issues.filter((issue) => issue.kind === "duplicate_email_in_booking_context").map((issue) => [issue.email, issue.slotKeys]));
|
|
2237
2256
|
return input.projection.slots.map((slot) => {
|
|
2238
2257
|
if (slot.email === null) {
|
|
2239
2258
|
return {
|
|
@@ -2699,7 +2718,7 @@ const knownSocialPlatforms = [
|
|
|
2699
2718
|
"x",
|
|
2700
2719
|
"youtube"
|
|
2701
2720
|
];
|
|
2702
|
-
function isRecord$
|
|
2721
|
+
function isRecord$5(value) {
|
|
2703
2722
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
2704
2723
|
}
|
|
2705
2724
|
function trimmedOptionalString(value) {
|
|
@@ -2709,7 +2728,7 @@ function trimmedOptionalString(value) {
|
|
|
2709
2728
|
return trimmed.length > 0 ? trimmed : null;
|
|
2710
2729
|
}
|
|
2711
2730
|
function parsePublicContactDetails(value) {
|
|
2712
|
-
if (!isRecord$
|
|
2731
|
+
if (!isRecord$5(value)) {
|
|
2713
2732
|
return err({
|
|
2714
2733
|
code: "invalid_shape",
|
|
2715
2734
|
path: "contact",
|
|
@@ -2805,7 +2824,7 @@ function minutesSinceMidnight(value) {
|
|
|
2805
2824
|
}
|
|
2806
2825
|
function parseOpeningHoursDay(value, expectedWeekday, index) {
|
|
2807
2826
|
const path = `openingHours.days.${index}`;
|
|
2808
|
-
if (!isRecord$
|
|
2827
|
+
if (!isRecord$5(value) || value.weekday !== expectedWeekday) {
|
|
2809
2828
|
return err({
|
|
2810
2829
|
code: "invalid_weekdays",
|
|
2811
2830
|
path,
|
|
@@ -2826,7 +2845,7 @@ function parseOpeningHoursDay(value, expectedWeekday, index) {
|
|
|
2826
2845
|
const intervals = [];
|
|
2827
2846
|
for (const [intervalIndex, rawInterval] of value.intervals.entries()) {
|
|
2828
2847
|
const intervalPath = `${path}.intervals.${intervalIndex}`;
|
|
2829
|
-
if (!isRecord$
|
|
2848
|
+
if (!isRecord$5(rawInterval)) {
|
|
2830
2849
|
return err({
|
|
2831
2850
|
code: "invalid_interval",
|
|
2832
2851
|
path: intervalPath,
|
|
@@ -2876,7 +2895,7 @@ function parseOpeningHoursDay(value, expectedWeekday, index) {
|
|
|
2876
2895
|
});
|
|
2877
2896
|
}
|
|
2878
2897
|
function parseWeeklyOpeningHours(value) {
|
|
2879
|
-
if (!isRecord$
|
|
2898
|
+
if (!isRecord$5(value) || !Array.isArray(value.days) || value.days.length !== publicProfileWeekdays.length) {
|
|
2880
2899
|
return err({
|
|
2881
2900
|
code: "invalid_weekdays",
|
|
2882
2901
|
path: "openingHours.days",
|
|
@@ -2956,7 +2975,7 @@ function parsePublicSocialLinks(value) {
|
|
|
2956
2975
|
const ids = new Set();
|
|
2957
2976
|
for (const [index, rawLink] of value.entries()) {
|
|
2958
2977
|
const path = `socialLinks.${index}`;
|
|
2959
|
-
if (!isRecord$
|
|
2978
|
+
if (!isRecord$5(rawLink)) {
|
|
2960
2979
|
return err({
|
|
2961
2980
|
code: "invalid_social_link",
|
|
2962
2981
|
path,
|
|
@@ -3003,7 +3022,7 @@ function parsePublicSocialLinks(value) {
|
|
|
3003
3022
|
return ok(links);
|
|
3004
3023
|
}
|
|
3005
3024
|
function parsePublicProfile(value) {
|
|
3006
|
-
if (!isRecord$
|
|
3025
|
+
if (!isRecord$5(value)) {
|
|
3007
3026
|
return err({
|
|
3008
3027
|
code: "invalid_shape",
|
|
3009
3028
|
path: "",
|
|
@@ -3032,7 +3051,7 @@ function emptyPublicProfile() {
|
|
|
3032
3051
|
|
|
3033
3052
|
//#endregion
|
|
3034
3053
|
//#region ../core/src/rich-text.ts
|
|
3035
|
-
function isRecord$
|
|
3054
|
+
function isRecord$4(value) {
|
|
3036
3055
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
3037
3056
|
}
|
|
3038
3057
|
function hasOnlyKeys(value, allowed) {
|
|
@@ -3054,10 +3073,10 @@ function isDenseArrayOf(value, predicate) {
|
|
|
3054
3073
|
return true;
|
|
3055
3074
|
}
|
|
3056
3075
|
function isTipTapMark(value) {
|
|
3057
|
-
return isRecord$
|
|
3076
|
+
return isRecord$4(value) && hasOnlyKeys(value, markKeys) && typeof value.type === "string" && value.type.length > 0 && (value.attrs === undefined || isRecord$4(value.attrs));
|
|
3058
3077
|
}
|
|
3059
3078
|
function isTipTapNode(value) {
|
|
3060
|
-
if (!isRecord$
|
|
3079
|
+
if (!isRecord$4(value) || !hasOnlyKeys(value, nodeKeys) || typeof value.type !== "string" || value.type.length === 0 || value.type === "doc" || value.attrs !== undefined && !isRecord$4(value.attrs) || value.marks !== undefined && (!Array.isArray(value.marks) || !isDenseArrayOf(value.marks, isTipTapMark)) || value.content !== undefined && (!Array.isArray(value.content) || !isDenseArrayOf(value.content, isTipTapNode))) {
|
|
3061
3080
|
return false;
|
|
3062
3081
|
}
|
|
3063
3082
|
if (value.type === "text") {
|
|
@@ -3066,7 +3085,7 @@ function isTipTapNode(value) {
|
|
|
3066
3085
|
return value.text === undefined;
|
|
3067
3086
|
}
|
|
3068
3087
|
function isCanonicalRichTextDoc(value) {
|
|
3069
|
-
return isRecord$
|
|
3088
|
+
return isRecord$4(value) && hasOnlyKeys(value, rootKeys) && value.type === "doc" && Array.isArray(value.content) && isDenseArrayOf(value.content, isTipTapNode);
|
|
3070
3089
|
}
|
|
3071
3090
|
|
|
3072
3091
|
//#endregion
|
|
@@ -3074,7 +3093,7 @@ function isCanonicalRichTextDoc(value) {
|
|
|
3074
3093
|
const UUID_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/iu;
|
|
3075
3094
|
const MAILCHIMP_AUDIENCE_ID_PATTERN = /^[A-Za-z0-9_-]{1,100}$/u;
|
|
3076
3095
|
const CONTROL_CHARACTER_PATTERN = /[\u0000-\u001f\u007f]/u;
|
|
3077
|
-
function isRecord$
|
|
3096
|
+
function isRecord$3(value) {
|
|
3078
3097
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
3079
3098
|
}
|
|
3080
3099
|
function parseTrimmedString(value) {
|
|
@@ -3102,7 +3121,7 @@ function parseNewsletterSignupDestinationProvider(value) {
|
|
|
3102
3121
|
return value === "riverbank" || value === "mailchimp" ? value : null;
|
|
3103
3122
|
}
|
|
3104
3123
|
function parseNewsletterSignupAuthoringDestinationValue(value) {
|
|
3105
|
-
if (!isRecord$
|
|
3124
|
+
if (!isRecord$3(value) || typeof value.tag !== "string") {
|
|
3106
3125
|
return err({ kind: "invalid-destination-shape" });
|
|
3107
3126
|
}
|
|
3108
3127
|
switch (value.tag) {
|
|
@@ -3148,7 +3167,7 @@ function parseNewsletterSignupAuthoringDestinationValue(value) {
|
|
|
3148
3167
|
|
|
3149
3168
|
//#endregion
|
|
3150
3169
|
//#region ../core/src/footer-engagement.ts
|
|
3151
|
-
function isRecord$
|
|
3170
|
+
function isRecord$2(value) {
|
|
3152
3171
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
3153
3172
|
}
|
|
3154
3173
|
function invalid(path, message) {
|
|
@@ -3182,7 +3201,7 @@ function parseEngagementBase(value, base, path) {
|
|
|
3182
3201
|
}
|
|
3183
3202
|
const treatment = parseTreatment(value.treatment, `${path}.treatment`);
|
|
3184
3203
|
if (!treatment.ok) return treatment;
|
|
3185
|
-
if (!isRecord$
|
|
3204
|
+
if (!isRecord$2(value.source)) {
|
|
3186
3205
|
return err(invalid(`${path}.source`, "Footer engagement source is invalid."));
|
|
3187
3206
|
}
|
|
3188
3207
|
const intro = parseBoundedText(value.source.intro, `${path}.source.intro`, {
|
|
@@ -3248,7 +3267,7 @@ function parseFooterEngagementSectionValue(value, sectionBase, path) {
|
|
|
3248
3267
|
const base = parseEngagementBase(value, sectionBase, path);
|
|
3249
3268
|
if (!base.ok) return base;
|
|
3250
3269
|
const source = value.source;
|
|
3251
|
-
if (!isRecord$
|
|
3270
|
+
if (!isRecord$2(source)) {
|
|
3252
3271
|
return err(invalid(`${path}.source`, "Footer engagement source is invalid."));
|
|
3253
3272
|
}
|
|
3254
3273
|
switch (source.kind) {
|
|
@@ -3363,7 +3382,7 @@ function unsafeAsFooterModuleId(value) {
|
|
|
3363
3382
|
function parseFooterModuleId(value) {
|
|
3364
3383
|
return parseSectionId(value, "sectionId");
|
|
3365
3384
|
}
|
|
3366
|
-
function isRecord$
|
|
3385
|
+
function isRecord$1(value) {
|
|
3367
3386
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
3368
3387
|
}
|
|
3369
3388
|
function isRichText(value) {
|
|
@@ -3393,7 +3412,7 @@ function parseHeading(value, path) {
|
|
|
3393
3412
|
}
|
|
3394
3413
|
function parseFooterSection(value, index) {
|
|
3395
3414
|
const path = `sections.${index}`;
|
|
3396
|
-
if (!isRecord$
|
|
3415
|
+
if (!isRecord$1(value)) {
|
|
3397
3416
|
return err({
|
|
3398
3417
|
code: "invalid_section",
|
|
3399
3418
|
path,
|
|
@@ -3428,7 +3447,7 @@ function parseFooterSection(value, index) {
|
|
|
3428
3447
|
}
|
|
3429
3448
|
break;
|
|
3430
3449
|
case "navigation":
|
|
3431
|
-
if (isRecord$
|
|
3450
|
+
if (isRecord$1(value.source) && value.source.kind === "menu-role" && value.source.role === "main") {
|
|
3432
3451
|
return ok({
|
|
3433
3452
|
...base,
|
|
3434
3453
|
kind: "navigation",
|
|
@@ -3441,7 +3460,7 @@ function parseFooterSection(value, index) {
|
|
|
3441
3460
|
}
|
|
3442
3461
|
break;
|
|
3443
3462
|
case "legal-navigation":
|
|
3444
|
-
if (isRecord$
|
|
3463
|
+
if (isRecord$1(value.source) && value.source.kind === "menu-role" && value.source.role === "legal-policy") {
|
|
3445
3464
|
return ok({
|
|
3446
3465
|
...base,
|
|
3447
3466
|
kind: "legal-navigation",
|
|
@@ -3454,7 +3473,7 @@ function parseFooterSection(value, index) {
|
|
|
3454
3473
|
}
|
|
3455
3474
|
break;
|
|
3456
3475
|
case "business-address":
|
|
3457
|
-
if (isRecord$
|
|
3476
|
+
if (isRecord$1(value.source) && value.source.kind === "business-address") {
|
|
3458
3477
|
return ok({
|
|
3459
3478
|
...base,
|
|
3460
3479
|
kind: "business-address",
|
|
@@ -3466,7 +3485,7 @@ function parseFooterSection(value, index) {
|
|
|
3466
3485
|
case "contact":
|
|
3467
3486
|
case "opening-hours":
|
|
3468
3487
|
case "social-links":
|
|
3469
|
-
if (isRecord$
|
|
3488
|
+
if (isRecord$1(value.source) && value.source.kind === "public-profile") {
|
|
3470
3489
|
return ok({
|
|
3471
3490
|
...base,
|
|
3472
3491
|
kind: value.kind,
|
|
@@ -3518,7 +3537,7 @@ function validateFooterComposition(composition) {
|
|
|
3518
3537
|
return ok(composition);
|
|
3519
3538
|
}
|
|
3520
3539
|
function parseFooterCompositionV1(value) {
|
|
3521
|
-
if (!isRecord$
|
|
3540
|
+
if (!isRecord$1(value)) {
|
|
3522
3541
|
return err({
|
|
3523
3542
|
code: "invalid_shape",
|
|
3524
3543
|
path: "",
|
|
@@ -3551,7 +3570,7 @@ function parseFooterCompositionV1(value) {
|
|
|
3551
3570
|
});
|
|
3552
3571
|
}
|
|
3553
3572
|
function parseLegacyPlacementSection(value, index, selectedIds) {
|
|
3554
|
-
if (!isRecord$
|
|
3573
|
+
if (!isRecord$1(value)) {
|
|
3555
3574
|
return err({
|
|
3556
3575
|
code: "invalid_section",
|
|
3557
3576
|
path: `modules.${index}`,
|
|
@@ -3559,7 +3578,7 @@ function parseLegacyPlacementSection(value, index, selectedIds) {
|
|
|
3559
3578
|
});
|
|
3560
3579
|
}
|
|
3561
3580
|
const selected = typeof value.id === "string" && selectedIds.has(value.id);
|
|
3562
|
-
const source = isRecord$
|
|
3581
|
+
const source = isRecord$1(value.source) ? value.source : null;
|
|
3563
3582
|
const mapped = value.kind === "navigation" && source?.kind === "menu-role" && source.role === "footer" ? {
|
|
3564
3583
|
...value,
|
|
3565
3584
|
source: {
|
|
@@ -3578,7 +3597,7 @@ function parseLegacyPlacementSection(value, index, selectedIds) {
|
|
|
3578
3597
|
* It is intentionally not called by the runtime parser.
|
|
3579
3598
|
*/
|
|
3580
3599
|
function upgradePlacementFooterCompositionV1(value) {
|
|
3581
|
-
if (!isRecord$
|
|
3600
|
+
if (!isRecord$1(value) || value.schemaVersion !== 1 || !Array.isArray(value.modules) || !Array.isArray(value.placements)) {
|
|
3582
3601
|
return err({
|
|
3583
3602
|
code: "invalid_shape",
|
|
3584
3603
|
path: "",
|
|
@@ -3587,7 +3606,7 @@ function upgradePlacementFooterCompositionV1(value) {
|
|
|
3587
3606
|
}
|
|
3588
3607
|
const selectedIds = new Set();
|
|
3589
3608
|
for (const [index, placement] of value.placements.entries()) {
|
|
3590
|
-
if (!isRecord$
|
|
3609
|
+
if (!isRecord$1(placement) || typeof placement.moduleId !== "string") {
|
|
3591
3610
|
return err({
|
|
3592
3611
|
code: "invalid_shape",
|
|
3593
3612
|
path: `placements.${index}`,
|
|
@@ -3802,7 +3821,7 @@ const defaultSections = (legacy, options) => {
|
|
|
3802
3821
|
];
|
|
3803
3822
|
};
|
|
3804
3823
|
function normalizeLegacyFooterContentToV1(value, options = {}) {
|
|
3805
|
-
if (!isRecord$
|
|
3824
|
+
if (!isRecord$1(value)) {
|
|
3806
3825
|
return err({
|
|
3807
3826
|
code: "invalid_shape",
|
|
3808
3827
|
path: "",
|
|
@@ -3887,11 +3906,11 @@ const PRIORITY_IDS = new Set([
|
|
|
3887
3906
|
"emailaddress"
|
|
3888
3907
|
]);
|
|
3889
3908
|
const MAX_COLUMNS = 5;
|
|
3890
|
-
function isRecord
|
|
3909
|
+
function isRecord(value) {
|
|
3891
3910
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
3892
3911
|
}
|
|
3893
3912
|
function isSchemaField(value) {
|
|
3894
|
-
return isRecord
|
|
3913
|
+
return isRecord(value) && typeof value.id === "string";
|
|
3895
3914
|
}
|
|
3896
3915
|
function selectSubmissionPreviewFields(schemaFields, submissions) {
|
|
3897
3916
|
const fields = Array.isArray(schemaFields) ? schemaFields.filter(isSchemaField) : [];
|
|
@@ -3984,7 +4003,7 @@ function valueIsMeaningful(value) {
|
|
|
3984
4003
|
if (value === null || value === undefined) return false;
|
|
3985
4004
|
if (typeof value === "string") return value.trim().length > 0;
|
|
3986
4005
|
if (Array.isArray(value)) return value.length > 0;
|
|
3987
|
-
if (isRecord
|
|
4006
|
+
if (isRecord(value)) return Object.keys(value).length > 0;
|
|
3988
4007
|
return true;
|
|
3989
4008
|
}
|
|
3990
4009
|
function orderSubmissionFields(schemaFields, data) {
|
|
@@ -4473,6 +4492,82 @@ function sortStableJsonValue(value) {
|
|
|
4473
4492
|
throw new TypeError(`Stable JSON does not support ${typeof value}`);
|
|
4474
4493
|
}
|
|
4475
4494
|
|
|
4495
|
+
//#endregion
|
|
4496
|
+
//#region ../core/src/secret.ts
|
|
4497
|
+
/**
|
|
4498
|
+
* Credential values that must remain inert when they cross diagnostics,
|
|
4499
|
+
* logging, or serialization boundaries.
|
|
4500
|
+
*
|
|
4501
|
+
* The value is deliberately held behind a non-enumerable capability rather
|
|
4502
|
+
* than exposed as a property. The capability uses a global symbol so a Secret
|
|
4503
|
+
* created by another bundled copy of core remains consumable at an approved
|
|
4504
|
+
* `exposeSecret` boundary.
|
|
4505
|
+
*/
|
|
4506
|
+
const SECRET_KINDS = [
|
|
4507
|
+
"management-key",
|
|
4508
|
+
"content-key",
|
|
4509
|
+
"preview-key",
|
|
4510
|
+
"webhook-secret",
|
|
4511
|
+
"oauth-access-token",
|
|
4512
|
+
"oauth-refresh-token",
|
|
4513
|
+
"preview-session-refresh-token",
|
|
4514
|
+
"oauth-authorization-code",
|
|
4515
|
+
"oauth-pkce-verifier",
|
|
4516
|
+
"registrar-epp-code"
|
|
4517
|
+
];
|
|
4518
|
+
const secretValueSymbol = Symbol.for("riverbank.secret.value");
|
|
4519
|
+
const SECRET_INSPECT_SYMBOL = Symbol.for("nodejs.util.inspect.custom");
|
|
4520
|
+
const MANAGEMENT_KEY_PREFIX = "bld_mgmt_sk_";
|
|
4521
|
+
function placeholderFor(kind) {
|
|
4522
|
+
return `[riverbank:redacted:${kind}]`;
|
|
4523
|
+
}
|
|
4524
|
+
/** Create an opaque secret after a caller has parsed/validated its boundary. */
|
|
4525
|
+
function createSecret(kind, value) {
|
|
4526
|
+
return createSecretInternal(kind, value);
|
|
4527
|
+
}
|
|
4528
|
+
/** Construct a Secret for an already validated kind/value pair. */
|
|
4529
|
+
function createSecretInternal(kind, value) {
|
|
4530
|
+
const placeholder = placeholderFor(kind);
|
|
4531
|
+
const secret = {
|
|
4532
|
+
kind: "secret",
|
|
4533
|
+
secretType: kind,
|
|
4534
|
+
toString: () => placeholder,
|
|
4535
|
+
toJSON: () => placeholder,
|
|
4536
|
+
[SECRET_INSPECT_SYMBOL]: () => placeholder
|
|
4537
|
+
};
|
|
4538
|
+
Object.defineProperty(secret, secretValueSymbol, {
|
|
4539
|
+
configurable: false,
|
|
4540
|
+
enumerable: false,
|
|
4541
|
+
value: () => value,
|
|
4542
|
+
writable: false
|
|
4543
|
+
});
|
|
4544
|
+
return Object.freeze(secret);
|
|
4545
|
+
}
|
|
4546
|
+
/**
|
|
4547
|
+
* Parse an untrusted management API key before constructing its Secret value.
|
|
4548
|
+
*
|
|
4549
|
+
* The Secret type protects an already validated value from accidental
|
|
4550
|
+
* disclosure; this boundary is what validates the value's wire format.
|
|
4551
|
+
*/
|
|
4552
|
+
function parseManagementKey(raw) {
|
|
4553
|
+
if (typeof raw !== "string" || !raw.startsWith(MANAGEMENT_KEY_PREFIX) || raw.length === MANAGEMENT_KEY_PREFIX.length) {
|
|
4554
|
+
return err({ kind: "invalid-management-key" });
|
|
4555
|
+
}
|
|
4556
|
+
return ok(createSecretInternal("management-key", raw));
|
|
4557
|
+
}
|
|
4558
|
+
/**
|
|
4559
|
+
* The only generic plaintext exposure operation. Keep calls at named I/O
|
|
4560
|
+
* boundaries; do not pass exposed strings through domain/application layers.
|
|
4561
|
+
*/
|
|
4562
|
+
function exposeSecret(secret) {
|
|
4563
|
+
const getValue = secret[secretValueSymbol];
|
|
4564
|
+
if (typeof getValue !== "function") return failInvalidSecret();
|
|
4565
|
+
return getValue();
|
|
4566
|
+
}
|
|
4567
|
+
function failInvalidSecret() {
|
|
4568
|
+
throw new TypeError("Invalid Riverbank secret");
|
|
4569
|
+
}
|
|
4570
|
+
|
|
4476
4571
|
//#endregion
|
|
4477
4572
|
//#region ../core/src/site-route-target.ts
|
|
4478
4573
|
const SITE_ROUTE_NODE_TYPES = [
|
|
@@ -4600,885 +4695,675 @@ function disabledSelection() {
|
|
|
4600
4695
|
}
|
|
4601
4696
|
|
|
4602
4697
|
//#endregion
|
|
4603
|
-
//#region ../
|
|
4604
|
-
const
|
|
4605
|
-
"
|
|
4606
|
-
"
|
|
4607
|
-
"
|
|
4608
|
-
|
|
4609
|
-
|
|
4698
|
+
//#region ../core/src/event-series-behaviour.ts
|
|
4699
|
+
const EVENT_PRESET_VALUES = [
|
|
4700
|
+
"event",
|
|
4701
|
+
"class",
|
|
4702
|
+
"course",
|
|
4703
|
+
"term",
|
|
4704
|
+
"workshop",
|
|
4705
|
+
"retreat",
|
|
4706
|
+
"performance"
|
|
4707
|
+
];
|
|
4708
|
+
const ORDINARY_EVENT_PRESET_VALUES = [
|
|
4709
|
+
"event",
|
|
4710
|
+
"class",
|
|
4711
|
+
"workshop",
|
|
4712
|
+
"retreat",
|
|
4713
|
+
"performance"
|
|
4714
|
+
];
|
|
4715
|
+
const WHOLE_SERIES_EVENT_PRESET_VALUES = ["course", "term"];
|
|
4716
|
+
const SURFACE_EVENT_PRESET_VALUES = [
|
|
4717
|
+
"class",
|
|
4718
|
+
"course",
|
|
4719
|
+
"term",
|
|
4720
|
+
"workshop",
|
|
4721
|
+
"retreat",
|
|
4722
|
+
"performance"
|
|
4723
|
+
];
|
|
4724
|
+
const DEFAULT_EVENT_TYPE_PRESETS = [
|
|
4725
|
+
"event",
|
|
4726
|
+
"class",
|
|
4727
|
+
"course",
|
|
4728
|
+
"term",
|
|
4729
|
+
"workshop",
|
|
4730
|
+
"retreat"
|
|
4731
|
+
];
|
|
4732
|
+
const EVENT_SERIES_BEHAVIOURS = [
|
|
4733
|
+
"class",
|
|
4734
|
+
"course",
|
|
4735
|
+
"workshop",
|
|
4736
|
+
"retreat",
|
|
4737
|
+
"performance"
|
|
4738
|
+
];
|
|
4739
|
+
/** Keeps Term as Event vocabulary without introducing a second booking engine. */
|
|
4740
|
+
function eventPresetForVerticalPreset(preset) {
|
|
4741
|
+
return preset.eventPresetOverride ?? preset.canonicalBehaviour;
|
|
4742
|
+
}
|
|
4743
|
+
|
|
4744
|
+
//#endregion
|
|
4745
|
+
//#region ../core/src/booking-vertical-preset.ts
|
|
4746
|
+
const BOOKING_ENGINE_BY_BEHAVIOUR = {
|
|
4747
|
+
class: "event_series",
|
|
4748
|
+
workshop: "event_series",
|
|
4749
|
+
course: "event_series",
|
|
4750
|
+
retreat: "event_series",
|
|
4751
|
+
performance: "event_series",
|
|
4752
|
+
appointment: "appointment_service"
|
|
4753
|
+
};
|
|
4754
|
+
const BOOKING_DEFAULTS_BY_BEHAVIOUR = {
|
|
4755
|
+
class: {
|
|
4756
|
+
passes: "on",
|
|
4757
|
+
memberships: "on"
|
|
4758
|
+
},
|
|
4759
|
+
workshop: {
|
|
4760
|
+
passes: "off",
|
|
4761
|
+
memberships: "off"
|
|
4762
|
+
},
|
|
4763
|
+
course: {
|
|
4764
|
+
passes: "off",
|
|
4765
|
+
memberships: "strict"
|
|
4766
|
+
},
|
|
4767
|
+
retreat: {
|
|
4768
|
+
passes: "off",
|
|
4769
|
+
memberships: "off"
|
|
4770
|
+
},
|
|
4771
|
+
performance: {
|
|
4772
|
+
passes: "off",
|
|
4773
|
+
memberships: "off"
|
|
4774
|
+
},
|
|
4775
|
+
appointment: {
|
|
4776
|
+
passes: "off",
|
|
4777
|
+
memberships: "off"
|
|
4778
|
+
}
|
|
4779
|
+
};
|
|
4780
|
+
function verticalBookingPreset(id, verticalId, canonicalBehaviour, label, pluralLabel, description, availability = "onboarding") {
|
|
4610
4781
|
return {
|
|
4611
|
-
|
|
4612
|
-
|
|
4782
|
+
id,
|
|
4783
|
+
verticalId,
|
|
4784
|
+
canonicalBehaviour,
|
|
4785
|
+
engine: BOOKING_ENGINE_BY_BEHAVIOUR[canonicalBehaviour],
|
|
4786
|
+
availability,
|
|
4787
|
+
label,
|
|
4788
|
+
pluralLabel,
|
|
4789
|
+
description
|
|
4613
4790
|
};
|
|
4614
4791
|
}
|
|
4615
|
-
|
|
4616
|
-
|
|
4617
|
-
|
|
4618
|
-
|
|
4619
|
-
|
|
4620
|
-
|
|
4621
|
-
|
|
4622
|
-
|
|
4623
|
-
}
|
|
4624
|
-
|
|
4625
|
-
|
|
4792
|
+
|
|
4793
|
+
//#endregion
|
|
4794
|
+
//#region ../core/src/booking-vertical-definitions.ts
|
|
4795
|
+
const VERTICAL_DEFINITIONS = [
|
|
4796
|
+
{
|
|
4797
|
+
id: "yoga_wellness",
|
|
4798
|
+
label: "Yoga, meditation or mindfulness",
|
|
4799
|
+
description: "Yoga studios, wellness teachers, and movement practitioners."
|
|
4800
|
+
},
|
|
4801
|
+
{
|
|
4802
|
+
id: "fitness_pt",
|
|
4803
|
+
label: "Fitness and personal training",
|
|
4804
|
+
description: "Personal trainers, group training, and fitness programmes."
|
|
4805
|
+
},
|
|
4806
|
+
{
|
|
4807
|
+
id: "dance_school",
|
|
4808
|
+
label: "Dance school",
|
|
4809
|
+
description: "Dance classes, courses or terms, workshops, private lessons, and shows."
|
|
4810
|
+
},
|
|
4811
|
+
{
|
|
4812
|
+
id: "physiotherapy_physical_health",
|
|
4813
|
+
label: "Physiotherapy & physical health",
|
|
4814
|
+
description: "Physiotherapists, chiropractors, osteopaths, sports therapists, and rehabilitation practitioners."
|
|
4815
|
+
},
|
|
4816
|
+
{
|
|
4817
|
+
id: "massage_bodywork",
|
|
4818
|
+
label: "Massage & bodywork",
|
|
4819
|
+
description: "Massage therapists, bodyworkers, and somatic practitioners."
|
|
4820
|
+
},
|
|
4821
|
+
{
|
|
4822
|
+
id: "therapist_practitioner",
|
|
4823
|
+
label: "Therapist or practitioner",
|
|
4824
|
+
description: "Appointments, sessions, consultations, and occasional group work."
|
|
4825
|
+
},
|
|
4826
|
+
{
|
|
4827
|
+
id: "beauty_spa",
|
|
4828
|
+
label: "Beauty or spa",
|
|
4829
|
+
description: "Treatment-led businesses with optional workshops."
|
|
4830
|
+
},
|
|
4831
|
+
{
|
|
4832
|
+
id: "consultant_coach",
|
|
4833
|
+
label: "Consultant or coach",
|
|
4834
|
+
description: "One-to-one calls, workshops, courses, and retreats."
|
|
4835
|
+
},
|
|
4836
|
+
{
|
|
4837
|
+
id: "training_provider",
|
|
4838
|
+
label: "Training provider",
|
|
4839
|
+
description: "Courses, workshops, classes, appointments, and residential training."
|
|
4840
|
+
},
|
|
4841
|
+
{
|
|
4842
|
+
id: "retreat_centre",
|
|
4843
|
+
label: "Retreat centre",
|
|
4844
|
+
description: "Residential retreats, workshops, classes, courses, and private sessions."
|
|
4845
|
+
},
|
|
4846
|
+
{
|
|
4847
|
+
id: "music_school",
|
|
4848
|
+
label: "Music teacher or school",
|
|
4849
|
+
description: "Lessons, classes, terms, workshops, and performances."
|
|
4850
|
+
},
|
|
4851
|
+
{
|
|
4852
|
+
id: "voice_singing",
|
|
4853
|
+
label: "Voice & Singing",
|
|
4854
|
+
description: "Singing lessons, choirs, voice circles, workshops, and performances."
|
|
4855
|
+
},
|
|
4856
|
+
{
|
|
4857
|
+
id: "performing_arts_school",
|
|
4858
|
+
label: "Theatre or performing arts school",
|
|
4859
|
+
description: "Classes, terms, workshops, performances, and lessons."
|
|
4860
|
+
},
|
|
4861
|
+
{
|
|
4862
|
+
id: "community_wellbeing",
|
|
4863
|
+
label: "Community or wellbeing centre",
|
|
4864
|
+
description: "Groups, workshops, courses, appointments, and retreats."
|
|
4865
|
+
},
|
|
4866
|
+
{
|
|
4867
|
+
id: "venue_event_space",
|
|
4868
|
+
label: "Venue or event space",
|
|
4869
|
+
description: "Room, resource, or access bookings. Depends on the future capacity-booking domain."
|
|
4870
|
+
}
|
|
4871
|
+
];
|
|
4872
|
+
const VERTICAL_IDS = VERTICAL_DEFINITIONS.map(({ id }) => id);
|
|
4873
|
+
|
|
4874
|
+
//#endregion
|
|
4875
|
+
//#region ../core/src/booking-vertical-catalog.ts
|
|
4876
|
+
const VERTICAL_BOOKING_PRESETS = [
|
|
4877
|
+
verticalBookingPreset("yoga_wellness.classes", "yoga_wellness", "class", "Class", "Classes", "Regular scheduled classes and group sessions."),
|
|
4878
|
+
verticalBookingPreset("yoga_wellness.workshops", "yoga_wellness", "workshop", "Workshop", "Workshops", "One-off workshops, day retreats, or special sessions."),
|
|
4879
|
+
verticalBookingPreset("yoga_wellness.courses", "yoga_wellness", "course", "Course", "Courses", "Fixed-schedule courses or multi-session groups."),
|
|
4880
|
+
verticalBookingPreset("yoga_wellness.retreats", "yoga_wellness", "retreat", "Retreat", "Retreats", "Multi-day or residential retreats and immersive experiences."),
|
|
4881
|
+
verticalBookingPreset("yoga_wellness.appointments", "yoga_wellness", "appointment", "Appointment", "Appointments", "Private sessions, consultations, or one-to-one support."),
|
|
4882
|
+
verticalBookingPreset("fitness_pt.group_training", "fitness_pt", "class", "Group training", "Group training", "Regular scheduled classes and group sessions."),
|
|
4883
|
+
verticalBookingPreset("fitness_pt.personal_training", "fitness_pt", "appointment", "Personal training", "Personal training", "One-to-one sessions booked from your availability."),
|
|
4884
|
+
verticalBookingPreset("fitness_pt.programmes", "fitness_pt", "course", "Programme", "Programmes", "Multi-session programmes like bootcamps or courses."),
|
|
4885
|
+
verticalBookingPreset("fitness_pt.workshops", "fitness_pt", "workshop", "Workshop", "Workshops", "One-off workshops, events, or special sessions."),
|
|
4886
|
+
verticalBookingPreset("fitness_pt.retreats", "fitness_pt", "retreat", "Retreat", "Retreats", "Multi-day or residential fitness experiences."),
|
|
4887
|
+
verticalBookingPreset("dance_school.classes", "dance_school", "class", "Class", "Classes", "Regular group classes and weekly sessions."),
|
|
4888
|
+
{
|
|
4889
|
+
...verticalBookingPreset("dance_school.terms", "dance_school", "course", "Term", "Terms", "Multi-week term blocks or fixed-date course groups."),
|
|
4890
|
+
eventPresetOverride: "term"
|
|
4891
|
+
},
|
|
4892
|
+
verticalBookingPreset("dance_school.courses", "dance_school", "course", "Course", "Courses", "Fixed-schedule courses or multi-session groups.", "settings"),
|
|
4893
|
+
verticalBookingPreset("dance_school.workshops", "dance_school", "workshop", "Workshop", "Workshops", "One-off workshops, masterclasses, or intensives."),
|
|
4894
|
+
verticalBookingPreset("dance_school.private_lessons", "dance_school", "appointment", "Private lesson", "Private lessons", "One-to-one lessons or coaching sessions."),
|
|
4895
|
+
verticalBookingPreset("dance_school.shows", "dance_school", "performance", "Show", "Shows", "Ticketed performances, showcases, and recitals."),
|
|
4896
|
+
verticalBookingPreset("physiotherapy_physical_health.appointments", "physiotherapy_physical_health", "appointment", "Appointment", "Appointments", "Assessments, treatments, follow-ups, and rehabilitation sessions."),
|
|
4897
|
+
verticalBookingPreset("physiotherapy_physical_health.rehab_classes", "physiotherapy_physical_health", "class", "Rehab class", "Rehab classes", "Regular rehabilitation, mobility, strength, or recovery classes."),
|
|
4898
|
+
verticalBookingPreset("physiotherapy_physical_health.workshops", "physiotherapy_physical_health", "workshop", "Workshop", "Workshops", "One-off workshops, talks, or practical group sessions."),
|
|
4899
|
+
verticalBookingPreset("massage_bodywork.treatments", "massage_bodywork", "appointment", "Treatment", "Treatments", "Massage treatments, bodywork sessions, and one-to-one appointments."),
|
|
4900
|
+
verticalBookingPreset("massage_bodywork.group_sessions", "massage_bodywork", "class", "Group session", "Group sessions", "Regular bodywork, somatic movement, or self-massage sessions."),
|
|
4901
|
+
verticalBookingPreset("massage_bodywork.workshops", "massage_bodywork", "workshop", "Workshop", "Workshops", "One-off bodywork, self-massage, or somatic practice workshops."),
|
|
4902
|
+
verticalBookingPreset("therapist_practitioner.appointments", "therapist_practitioner", "appointment", "Appointment", "Appointments", "Sessions, consultations, treatments, or assessments."),
|
|
4903
|
+
verticalBookingPreset("therapist_practitioner.workshops", "therapist_practitioner", "workshop", "Workshop", "Workshops", "One-off group workshops, talks, or wellbeing sessions."),
|
|
4904
|
+
verticalBookingPreset("therapist_practitioner.retreats", "therapist_practitioner", "retreat", "Retreat", "Retreats", "Multi-day retreats or immersive group experiences."),
|
|
4905
|
+
verticalBookingPreset("beauty_spa.appointments", "beauty_spa", "appointment", "Appointment", "Appointments", "Treatments, consultations, services, and sessions."),
|
|
4906
|
+
verticalBookingPreset("beauty_spa.workshops", "beauty_spa", "workshop", "Workshop", "Workshops", "Skincare workshops, makeup lessons, demos, or group events."),
|
|
4907
|
+
verticalBookingPreset("consultant_coach.appointments", "consultant_coach", "appointment", "Appointment", "Appointments", "One-to-one calls, consultations, reviews, or coaching sessions."),
|
|
4908
|
+
verticalBookingPreset("consultant_coach.workshops", "consultant_coach", "workshop", "Workshop", "Workshops", "One-off group sessions, webinars, or strategy days."),
|
|
4909
|
+
verticalBookingPreset("consultant_coach.courses", "consultant_coach", "course", "Course", "Courses", "Fixed-schedule cohorts or multi-session group programmes."),
|
|
4910
|
+
verticalBookingPreset("consultant_coach.retreats", "consultant_coach", "retreat", "Retreat", "Retreats", "Multi-day coaching retreats or immersive experiences."),
|
|
4911
|
+
verticalBookingPreset("training_provider.courses", "training_provider", "course", "Course", "Courses", "Fixed-schedule courses, cohorts, and multi-session training."),
|
|
4912
|
+
verticalBookingPreset("training_provider.workshops", "training_provider", "workshop", "Workshop", "Workshops", "One-off workshops, masterclasses, and short training sessions."),
|
|
4913
|
+
verticalBookingPreset("training_provider.appointments", "training_provider", "appointment", "Appointment", "Appointments", "Consultations, assessments, mentoring, or office hours."),
|
|
4914
|
+
verticalBookingPreset("training_provider.classes", "training_provider", "class", "Class", "Classes", "Regular classes, sessions, or groups.", "settings"),
|
|
4915
|
+
verticalBookingPreset("training_provider.residentials", "training_provider", "retreat", "Residential", "Residentials", "Residential courses or multi-day training experiences.", "settings"),
|
|
4916
|
+
verticalBookingPreset("retreat_centre.retreats", "retreat_centre", "retreat", "Retreat", "Retreats", "Multi-day or residential retreats with richer booking needs."),
|
|
4917
|
+
verticalBookingPreset("retreat_centre.workshops", "retreat_centre", "workshop", "Workshop", "Workshops", "One-day retreats, ceremonies, workshops, or special events."),
|
|
4918
|
+
verticalBookingPreset("retreat_centre.classes", "retreat_centre", "class", "Class", "Classes", "Regular sessions like guided meditations or group practices."),
|
|
4919
|
+
verticalBookingPreset("retreat_centre.courses", "retreat_centre", "course", "Course", "Courses", "Fixed-schedule programmes or multi-session experiences."),
|
|
4920
|
+
verticalBookingPreset("retreat_centre.appointments", "retreat_centre", "appointment", "Appointment", "Appointments", "Private sessions, therapies, consultations, or add-ons."),
|
|
4921
|
+
verticalBookingPreset("music_school.lessons", "music_school", "appointment", "Lesson", "Lessons", "One-to-one lessons, consultations, or coaching sessions."),
|
|
4922
|
+
verticalBookingPreset("music_school.classes", "music_school", "class", "Class", "Classes", "Regular group classes, ensembles, or recurring sessions."),
|
|
4923
|
+
{
|
|
4924
|
+
...verticalBookingPreset("music_school.terms", "music_school", "course", "Term", "Terms", "Multi-week term blocks or fixed-date group courses."),
|
|
4925
|
+
eventPresetOverride: "term"
|
|
4926
|
+
},
|
|
4927
|
+
verticalBookingPreset("music_school.workshops", "music_school", "workshop", "Workshop", "Workshops", "One-off workshops, masterclasses, or intensives."),
|
|
4928
|
+
verticalBookingPreset("music_school.performances", "music_school", "performance", "Performance", "Performances", "Concerts, recitals, showcases, or ticketed performances."),
|
|
4929
|
+
verticalBookingPreset("voice_singing.lessons", "voice_singing", "appointment", "Voice session", "Voice sessions", "One-to-one singing lessons, voice coaching, or private practice sessions."),
|
|
4930
|
+
verticalBookingPreset("voice_singing.classes", "voice_singing", "class", "Ongoing weekly session", "Ongoing weekly sessions", "Regular choirs, voice circles, community singing, or group practice."),
|
|
4931
|
+
{
|
|
4932
|
+
...verticalBookingPreset("voice_singing.terms", "voice_singing", "course", "Singing term", "Singing terms", "Multi-week choir terms, voice courses, or fixed-date group programmes."),
|
|
4933
|
+
eventPresetOverride: "term"
|
|
4934
|
+
},
|
|
4935
|
+
verticalBookingPreset("voice_singing.workshops", "voice_singing", "workshop", "Workshop", "Workshops", "One-off singing workshops, voice journeys, or devotional singing sessions."),
|
|
4936
|
+
verticalBookingPreset("voice_singing.performances", "voice_singing", "performance", "Performance", "Performances", "Choir concerts, sharing events, recitals, or community performances."),
|
|
4937
|
+
verticalBookingPreset("performing_arts_school.classes", "performing_arts_school", "class", "Class", "Classes", "Regular group classes, rehearsals, or weekly sessions."),
|
|
4938
|
+
{
|
|
4939
|
+
...verticalBookingPreset("performing_arts_school.terms", "performing_arts_school", "course", "Term", "Terms", "Multi-week term blocks or fixed-date course groups."),
|
|
4940
|
+
eventPresetOverride: "term"
|
|
4941
|
+
},
|
|
4942
|
+
verticalBookingPreset("performing_arts_school.workshops", "performing_arts_school", "workshop", "Workshop", "Workshops", "One-off workshops, masterclasses, or intensives."),
|
|
4943
|
+
verticalBookingPreset("performing_arts_school.performances", "performing_arts_school", "performance", "Performance", "Performances", "Shows, showcases, productions, or ticketed performances."),
|
|
4944
|
+
verticalBookingPreset("performing_arts_school.lessons", "performing_arts_school", "appointment", "Lesson", "Lessons", "One-to-one lessons, coaching, or audition prep."),
|
|
4945
|
+
verticalBookingPreset("community_wellbeing.classes", "community_wellbeing", "class", "Class", "Classes", "Regular groups, sessions, practices, or weekly activities."),
|
|
4946
|
+
verticalBookingPreset("community_wellbeing.workshops", "community_wellbeing", "workshop", "Workshop", "Workshops", "One-off workshops, talks, events, or gatherings."),
|
|
4947
|
+
verticalBookingPreset("community_wellbeing.courses", "community_wellbeing", "course", "Course", "Courses", "Fixed-schedule courses, study groups, or multi-session programmes."),
|
|
4948
|
+
verticalBookingPreset("community_wellbeing.appointments", "community_wellbeing", "appointment", "Appointment", "Appointments", "One-to-one appointments, consultations, or support sessions."),
|
|
4949
|
+
verticalBookingPreset("community_wellbeing.retreats", "community_wellbeing", "retreat", "Retreat", "Retreats", "Multi-day or residential retreats."),
|
|
4950
|
+
verticalBookingPreset("venue_event_space.resource_hire", "venue_event_space", "appointment", "Hire slot", "Hire slots", "Future room, venue, and resource hire bookings.", "future")
|
|
4951
|
+
];
|
|
4952
|
+
|
|
4953
|
+
//#endregion
|
|
4954
|
+
//#region ../core/src/booking-vertical-config.ts
|
|
4955
|
+
const DEFAULT_ONBOARDING_PRESET_ID_BY_VERTICAL = {
|
|
4956
|
+
yoga_wellness: null,
|
|
4957
|
+
fitness_pt: null,
|
|
4958
|
+
dance_school: null,
|
|
4959
|
+
physiotherapy_physical_health: "physiotherapy_physical_health.appointments",
|
|
4960
|
+
massage_bodywork: "massage_bodywork.treatments",
|
|
4961
|
+
therapist_practitioner: null,
|
|
4962
|
+
beauty_spa: null,
|
|
4963
|
+
consultant_coach: null,
|
|
4964
|
+
training_provider: null,
|
|
4965
|
+
retreat_centre: null,
|
|
4966
|
+
music_school: null,
|
|
4967
|
+
voice_singing: null,
|
|
4968
|
+
performing_arts_school: null,
|
|
4969
|
+
community_wellbeing: null,
|
|
4970
|
+
venue_event_space: null
|
|
4971
|
+
};
|
|
4972
|
+
const VERTICAL_ID_SET = new Set(VERTICAL_DEFINITIONS.map(({ id }) => id));
|
|
4973
|
+
const VERTICAL_PRESET_ID_SET = new Set(VERTICAL_BOOKING_PRESETS.map((presetDefinition) => presetDefinition.id));
|
|
4974
|
+
function parseVerticalId(value) {
|
|
4975
|
+
return typeof value === "string" && VERTICAL_ID_SET.has(value) ? value : null;
|
|
4626
4976
|
}
|
|
4627
|
-
function
|
|
4628
|
-
return typeof value === "string" &&
|
|
4977
|
+
function parseVerticalPresetId(value) {
|
|
4978
|
+
return typeof value === "string" && VERTICAL_PRESET_ID_SET.has(value) ? value : null;
|
|
4629
4979
|
}
|
|
4630
|
-
function
|
|
4631
|
-
|
|
4632
|
-
|
|
4980
|
+
function getVerticalDefinition(verticalId) {
|
|
4981
|
+
const definition = VERTICAL_DEFINITIONS.find((vertical) => vertical.id === verticalId);
|
|
4982
|
+
if (definition === undefined) {
|
|
4983
|
+
throw new Error(`Unhandled vertical id: ${verticalId}`);
|
|
4633
4984
|
}
|
|
4634
|
-
|
|
4635
|
-
if (!isPositiveWholeNumber(raw.daysBeforeStart)) {
|
|
4636
|
-
return [issue$2([...path, "daysBeforeStart"], "Flexible balance daysBeforeStart must be a positive integer")];
|
|
4637
|
-
}
|
|
4638
|
-
return {
|
|
4639
|
-
kind: "relative_to_start_date",
|
|
4640
|
-
daysBeforeStart: raw.daysBeforeStart
|
|
4641
|
-
};
|
|
4642
|
-
}
|
|
4643
|
-
if (raw.kind === "fixed_calendar_date") {
|
|
4644
|
-
if (typeof raw.dueAt !== "string" || !Number.isFinite(Date.parse(raw.dueAt))) {
|
|
4645
|
-
return [issue$2([...path, "dueAt"], "Flexible balance fixed dueAt must be an ISO datetime")];
|
|
4646
|
-
}
|
|
4647
|
-
return {
|
|
4648
|
-
kind: "fixed_calendar_date",
|
|
4649
|
-
dueAt: raw.dueAt
|
|
4650
|
-
};
|
|
4651
|
-
}
|
|
4652
|
-
return [issue$2([...path, "kind"], "Flexible balance dueDateRule kind must be relative_to_start_date or fixed_calendar_date")];
|
|
4985
|
+
return definition;
|
|
4653
4986
|
}
|
|
4654
|
-
function
|
|
4655
|
-
|
|
4656
|
-
|
|
4657
|
-
|
|
4658
|
-
const issues = [];
|
|
4659
|
-
const upfrontAmountCents = raw.upfrontAmountCents;
|
|
4660
|
-
const depositRefundability = raw.depositRefundability;
|
|
4661
|
-
let validatedUpfrontAmountCents = null;
|
|
4662
|
-
let validatedDepositRefundability = null;
|
|
4663
|
-
if (!isWholeNumber$1(upfrontAmountCents) || upfrontAmountCents < 0) {
|
|
4664
|
-
issues.push(issue$2([...path, "upfrontAmountCents"], "Flexible balance upfrontAmountCents must be a non-negative integer"));
|
|
4665
|
-
} else {
|
|
4666
|
-
validatedUpfrontAmountCents = upfrontAmountCents;
|
|
4667
|
-
}
|
|
4668
|
-
if (!isDepositRefundability(depositRefundability)) {
|
|
4669
|
-
issues.push(issue$2([...path, "depositRefundability"], "Flexible balance depositRefundability must be refundable or non_refundable"));
|
|
4670
|
-
} else {
|
|
4671
|
-
validatedDepositRefundability = depositRefundability;
|
|
4672
|
-
}
|
|
4673
|
-
const dueDateRule = validateFlexibleBalanceDueDateRule(raw.dueDateRule, [...path, "dueDateRule"]);
|
|
4674
|
-
if (Array.isArray(dueDateRule)) {
|
|
4675
|
-
issues.push(...dueDateRule);
|
|
4676
|
-
}
|
|
4677
|
-
if (issues.length > 0 || Array.isArray(dueDateRule) || validatedUpfrontAmountCents === null || validatedDepositRefundability === null) {
|
|
4678
|
-
return issues;
|
|
4987
|
+
function getVerticalPresetDefinition(presetId) {
|
|
4988
|
+
const definition = VERTICAL_BOOKING_PRESETS.find((presetDefinition) => presetDefinition.id === presetId);
|
|
4989
|
+
if (definition === undefined) {
|
|
4990
|
+
throw new Error(`Unhandled vertical preset id: ${presetId}`);
|
|
4679
4991
|
}
|
|
4992
|
+
return definition;
|
|
4993
|
+
}
|
|
4994
|
+
function getDefaultOnboardingPresetForVertical(verticalId) {
|
|
4995
|
+
const presetId = DEFAULT_ONBOARDING_PRESET_ID_BY_VERTICAL[verticalId];
|
|
4996
|
+
return presetId ? getVerticalPresetDefinition(presetId) : null;
|
|
4997
|
+
}
|
|
4998
|
+
function createConfiguredSiteVerticalBookingConfig(verticalId, selectedPresetIds) {
|
|
4680
4999
|
return {
|
|
4681
|
-
|
|
4682
|
-
|
|
4683
|
-
dueDateRule
|
|
5000
|
+
verticalId,
|
|
5001
|
+
selectedPresetIds
|
|
4684
5002
|
};
|
|
4685
5003
|
}
|
|
4686
|
-
function
|
|
4687
|
-
const
|
|
4688
|
-
|
|
4689
|
-
|
|
5004
|
+
function toNonEmptyVerticalPresetIds(presetIds) {
|
|
5005
|
+
const firstPresetId = presetIds[0];
|
|
5006
|
+
return firstPresetId ? [firstPresetId, ...presetIds.slice(1)] : null;
|
|
5007
|
+
}
|
|
5008
|
+
/**
|
|
5009
|
+
* Construct a configured booking selection only when the supplied preset IDs
|
|
5010
|
+
* form a valid selection for the vertical. Invalid, cross-vertical, future,
|
|
5011
|
+
* duplicate, or empty selections return null.
|
|
5012
|
+
*/
|
|
5013
|
+
function createSiteVerticalBookingConfig(verticalId, selectedPresetIds) {
|
|
5014
|
+
const resolved = resolveSiteVerticalBookingConfig({
|
|
5015
|
+
verticalId,
|
|
5016
|
+
selectedPresetIds
|
|
5017
|
+
});
|
|
5018
|
+
return resolved.kind === "configured" ? resolved.config : null;
|
|
5019
|
+
}
|
|
5020
|
+
const DEFAULT_SITE_VERTICAL_BOOKING_CONFIG = {
|
|
5021
|
+
verticalId: null,
|
|
5022
|
+
selectedPresetIds: []
|
|
5023
|
+
};
|
|
5024
|
+
function appendUniqueIssue(issues, issue) {
|
|
5025
|
+
return issues.some((existing) => existing.code === issue.code) ? issues : [...issues, issue];
|
|
5026
|
+
}
|
|
5027
|
+
function collectRawConfigIssues(rawConfig, config) {
|
|
5028
|
+
const rawPresetValue = rawConfig.selectedPresetIds;
|
|
5029
|
+
const rawPresetIds = Array.isArray(rawPresetValue) ? rawPresetValue : [];
|
|
5030
|
+
let issues = [];
|
|
5031
|
+
if (rawConfig.verticalId != null && config.verticalId === null) {
|
|
5032
|
+
issues = appendUniqueIssue(issues, {
|
|
5033
|
+
code: "unknown_vertical",
|
|
5034
|
+
rawValue: rawConfig.verticalId
|
|
5035
|
+
});
|
|
4690
5036
|
}
|
|
4691
|
-
if (
|
|
4692
|
-
|
|
5037
|
+
if (config.verticalId === null && config.selectedPresetIds.length > 0) {
|
|
5038
|
+
issues = appendUniqueIssue(issues, { code: "missing_vertical" });
|
|
4693
5039
|
}
|
|
4694
|
-
if (!
|
|
4695
|
-
|
|
5040
|
+
if (rawPresetValue !== undefined && !Array.isArray(rawPresetValue) || rawPresetIds.some((presetId) => parseVerticalPresetId(presetId) === null)) {
|
|
5041
|
+
issues = appendUniqueIssue(issues, {
|
|
5042
|
+
code: "unknown_preset",
|
|
5043
|
+
rawValue: rawPresetValue
|
|
5044
|
+
});
|
|
4696
5045
|
}
|
|
4697
|
-
|
|
4698
|
-
|
|
4699
|
-
case "deferred_manual": return { kind: raw.kind };
|
|
4700
|
-
case "flexible_balance": {
|
|
4701
|
-
const config = validateFlexibleBalanceConfig(raw.config, [...path, "config"]);
|
|
4702
|
-
if (Array.isArray(config)) {
|
|
4703
|
-
return config;
|
|
4704
|
-
}
|
|
4705
|
-
return {
|
|
4706
|
-
kind: "flexible_balance",
|
|
4707
|
-
config
|
|
4708
|
-
};
|
|
4709
|
-
}
|
|
5046
|
+
if (rawConfig.verticalId != null && config.selectedPresetIds.length === 0) {
|
|
5047
|
+
issues = appendUniqueIssue(issues, { code: "empty_selected_presets" });
|
|
4710
5048
|
}
|
|
5049
|
+
return issues;
|
|
4711
5050
|
}
|
|
4712
|
-
function
|
|
4713
|
-
const
|
|
4714
|
-
const
|
|
4715
|
-
|
|
4716
|
-
|
|
4717
|
-
|
|
4718
|
-
|
|
4719
|
-
|
|
4720
|
-
"kind"
|
|
4721
|
-
], `Duplicate payment option kind "${option.kind}"`));
|
|
5051
|
+
function collectPresetConfigIssues(config) {
|
|
5052
|
+
const seenPresetIds = new Set();
|
|
5053
|
+
const seenBehaviours = new Set();
|
|
5054
|
+
let issues = [];
|
|
5055
|
+
for (const presetId of config.selectedPresetIds) {
|
|
5056
|
+
const presetDefinition = getVerticalPresetDefinition(presetId);
|
|
5057
|
+
if (seenPresetIds.has(presetId)) {
|
|
5058
|
+
issues = appendUniqueIssue(issues, { code: "duplicate_preset_id" });
|
|
4722
5059
|
}
|
|
4723
|
-
|
|
4724
|
-
|
|
4725
|
-
|
|
4726
|
-
|
|
4727
|
-
|
|
4728
|
-
|
|
4729
|
-
|
|
5060
|
+
seenPresetIds.add(presetId);
|
|
5061
|
+
if (config.verticalId !== null && presetDefinition.verticalId !== config.verticalId) {
|
|
5062
|
+
issues = appendUniqueIssue(issues, { code: "preset_vertical_mismatch" });
|
|
5063
|
+
}
|
|
5064
|
+
if (presetDefinition.availability === "future") {
|
|
5065
|
+
issues = appendUniqueIssue(issues, { code: "future_preset_selected" });
|
|
5066
|
+
}
|
|
5067
|
+
if (seenBehaviours.has(presetDefinition.canonicalBehaviour)) {
|
|
5068
|
+
issues = appendUniqueIssue(issues, { code: "duplicate_canonical_behaviour" });
|
|
5069
|
+
}
|
|
5070
|
+
seenBehaviours.add(presetDefinition.canonicalBehaviour);
|
|
4730
5071
|
}
|
|
4731
5072
|
return issues;
|
|
4732
5073
|
}
|
|
4733
|
-
function
|
|
4734
|
-
|
|
4735
|
-
|
|
4736
|
-
|
|
4737
|
-
paymentTerms: undefined
|
|
4738
|
-
};
|
|
5074
|
+
function getSiteVerticalConfigIssues(rawConfig, config) {
|
|
5075
|
+
let issues = collectRawConfigIssues(rawConfig, config);
|
|
5076
|
+
for (const issue of collectPresetConfigIssues(config)) {
|
|
5077
|
+
issues = appendUniqueIssue(issues, issue);
|
|
4739
5078
|
}
|
|
4740
|
-
|
|
5079
|
+
return issues;
|
|
5080
|
+
}
|
|
5081
|
+
function resolveSiteVerticalBookingConfig(rawConfig) {
|
|
5082
|
+
if (rawConfig == null) {
|
|
4741
5083
|
return {
|
|
4742
|
-
|
|
4743
|
-
|
|
5084
|
+
kind: "unconfigured",
|
|
5085
|
+
config: DEFAULT_SITE_VERTICAL_BOOKING_CONFIG
|
|
4744
5086
|
};
|
|
4745
5087
|
}
|
|
4746
|
-
|
|
5088
|
+
const verticalId = parseVerticalId(rawConfig.verticalId);
|
|
5089
|
+
const rawPresetIds = Array.isArray(rawConfig.selectedPresetIds) ? rawConfig.selectedPresetIds : [];
|
|
5090
|
+
const selectedPresetIds = rawPresetIds.map(parseVerticalPresetId).filter((presetId) => Boolean(presetId));
|
|
5091
|
+
const config = {
|
|
5092
|
+
verticalId,
|
|
5093
|
+
selectedPresetIds
|
|
5094
|
+
};
|
|
5095
|
+
if (rawConfig.verticalId == null && selectedPresetIds.length === 0) {
|
|
4747
5096
|
return {
|
|
4748
|
-
|
|
4749
|
-
|
|
5097
|
+
kind: "unconfigured",
|
|
5098
|
+
config: DEFAULT_SITE_VERTICAL_BOOKING_CONFIG
|
|
4750
5099
|
};
|
|
4751
5100
|
}
|
|
4752
|
-
|
|
4753
|
-
|
|
4754
|
-
|
|
4755
|
-
|
|
4756
|
-
|
|
4757
|
-
}
|
|
4758
|
-
const
|
|
4759
|
-
const
|
|
4760
|
-
|
|
4761
|
-
const validated = validateCollectionOption(option, index);
|
|
4762
|
-
if (Array.isArray(validated)) {
|
|
4763
|
-
issues.push(...validated);
|
|
4764
|
-
return;
|
|
4765
|
-
}
|
|
4766
|
-
options.push(validated);
|
|
4767
|
-
});
|
|
4768
|
-
issues.push(...optionCombinationIssues(options));
|
|
4769
|
-
if (issues.length > 0) {
|
|
5101
|
+
const issues = getSiteVerticalConfigIssues(rawConfig, config);
|
|
5102
|
+
if (issues.length > 0) return {
|
|
5103
|
+
kind: "invalid",
|
|
5104
|
+
config,
|
|
5105
|
+
issues
|
|
5106
|
+
};
|
|
5107
|
+
const nonEmptyPresetIds = toNonEmptyVerticalPresetIds(selectedPresetIds);
|
|
5108
|
+
const configuredConfig = verticalId !== null && nonEmptyPresetIds !== null ? createConfiguredSiteVerticalBookingConfig(verticalId, nonEmptyPresetIds) : null;
|
|
5109
|
+
if (configuredConfig === null) {
|
|
4770
5110
|
return {
|
|
4771
|
-
|
|
4772
|
-
|
|
5111
|
+
kind: "invalid",
|
|
5112
|
+
config,
|
|
5113
|
+
issues: [{ code: "empty_selected_presets" }]
|
|
4773
5114
|
};
|
|
4774
5115
|
}
|
|
4775
5116
|
return {
|
|
4776
|
-
|
|
4777
|
-
|
|
5117
|
+
kind: "configured",
|
|
5118
|
+
config: configuredConfig,
|
|
5119
|
+
selectedPresets: configuredConfig.selectedPresetIds.map(getVerticalPresetDefinition)
|
|
4778
5120
|
};
|
|
4779
5121
|
}
|
|
4780
5122
|
|
|
4781
5123
|
//#endregion
|
|
4782
|
-
//#region ../
|
|
4783
|
-
const
|
|
4784
|
-
|
|
4785
|
-
|
|
4786
|
-
|
|
4787
|
-
|
|
4788
|
-
|
|
4789
|
-
|
|
4790
|
-
|
|
4791
|
-
|
|
4792
|
-
|
|
4793
|
-
|
|
5124
|
+
//#region ../core/src/booking-verticals.ts
|
|
5125
|
+
const CANONICAL_BOOKING_BEHAVIOURS = [
|
|
5126
|
+
"class",
|
|
5127
|
+
"workshop",
|
|
5128
|
+
"course",
|
|
5129
|
+
"retreat",
|
|
5130
|
+
"performance",
|
|
5131
|
+
"appointment"
|
|
5132
|
+
];
|
|
5133
|
+
const BOOKING_ENGINES = ["event_series", "appointment_service"];
|
|
5134
|
+
const VERTICAL_PRESET_AVAILABILITIES = [
|
|
5135
|
+
"onboarding",
|
|
5136
|
+
"settings",
|
|
5137
|
+
"future"
|
|
5138
|
+
];
|
|
5139
|
+
function getVerticalPresetsForVertical(verticalId, options) {
|
|
5140
|
+
const availability = options?.availability;
|
|
5141
|
+
return VERTICAL_BOOKING_PRESETS.filter((presetDefinition) => {
|
|
5142
|
+
if (presetDefinition.verticalId !== verticalId) return false;
|
|
5143
|
+
if (!availability) return true;
|
|
5144
|
+
if (availability === "selectable") return presetDefinition.availability !== "future";
|
|
5145
|
+
return presetDefinition.availability === availability;
|
|
5146
|
+
});
|
|
4794
5147
|
}
|
|
4795
|
-
function
|
|
4796
|
-
return
|
|
5148
|
+
function getOnboardingPresetsForVertical(verticalId) {
|
|
5149
|
+
return getVerticalPresetsForVertical(verticalId, { availability: "onboarding" });
|
|
4797
5150
|
}
|
|
4798
|
-
function
|
|
4799
|
-
return
|
|
5151
|
+
function isEventSeriesCanonicalBehaviour(behaviour) {
|
|
5152
|
+
return EVENT_SERIES_BEHAVIOURS.includes(behaviour);
|
|
4800
5153
|
}
|
|
4801
|
-
function
|
|
4802
|
-
|
|
4803
|
-
const epochMs = Date.parse(value);
|
|
4804
|
-
return Number.isFinite(epochMs) && epochMs % 6e4 === 0;
|
|
5154
|
+
function isEventSeriesVerticalPresetDefinition(presetDefinition) {
|
|
5155
|
+
return isEventSeriesCanonicalBehaviour(presetDefinition.canonicalBehaviour);
|
|
4805
5156
|
}
|
|
4806
|
-
function
|
|
4807
|
-
const
|
|
4808
|
-
if (!
|
|
4809
|
-
|
|
4810
|
-
|
|
4811
|
-
|
|
4812
|
-
|
|
4813
|
-
}
|
|
4814
|
-
const name = raw.name;
|
|
4815
|
-
if (typeof name !== "string" || name.trim().length === 0) {
|
|
4816
|
-
issues.push(issue$1([...path, "name"], "Ticket name is required"));
|
|
4817
|
-
}
|
|
4818
|
-
const priceAmount = raw.priceAmount;
|
|
4819
|
-
if (!isWholeNumber(priceAmount) || priceAmount < 0) {
|
|
4820
|
-
issues.push(issue$1([...path, "priceAmount"], "Ticket priceAmount must be a non-negative integer"));
|
|
4821
|
-
}
|
|
4822
|
-
const priceCurrency = raw.priceCurrency;
|
|
4823
|
-
if (priceCurrency !== undefined && priceCurrency !== null && priceCurrency !== currency) {
|
|
4824
|
-
issues.push(issue$1([...path, "priceCurrency"], "Ticket priceCurrency must match pricing.priceCurrency"));
|
|
4825
|
-
}
|
|
4826
|
-
const sortOrder = raw.sortOrder;
|
|
4827
|
-
if (sortOrder !== undefined && (!isWholeNumber(sortOrder) || sortOrder < 0)) {
|
|
4828
|
-
issues.push(issue$1([...path, "sortOrder"], "Ticket sortOrder must be a non-negative integer"));
|
|
4829
|
-
}
|
|
4830
|
-
const capacityLimit = raw.capacityLimit;
|
|
4831
|
-
if (capacityLimit !== undefined && capacityLimit !== null && (!isWholeNumber(capacityLimit) || capacityLimit < 1)) {
|
|
4832
|
-
issues.push(issue$1([...path, "capacityLimit"], "Ticket capacityLimit must be a positive integer"));
|
|
4833
|
-
}
|
|
4834
|
-
const attendanceScope = raw.attendanceScope;
|
|
4835
|
-
if (attendanceScope !== undefined && attendanceScope !== "in_person" && attendanceScope !== "online" && attendanceScope !== "both") {
|
|
4836
|
-
issues.push(issue$1([...path, "attendanceScope"], "Ticket attendanceScope must be in_person, online, or both"));
|
|
4837
|
-
}
|
|
4838
|
-
const saleStartsAt = raw.saleStartsAt;
|
|
4839
|
-
if (saleStartsAt !== undefined && saleStartsAt !== null && (typeof saleStartsAt !== "string" || !isWholeMinuteIsoDateTime(saleStartsAt))) {
|
|
4840
|
-
issues.push(issue$1([...path, "saleStartsAt"], "Ticket saleStartsAt must be an ISO datetime aligned to a whole minute"));
|
|
4841
|
-
}
|
|
4842
|
-
const saleEndsAt = raw.saleEndsAt;
|
|
4843
|
-
if (saleEndsAt !== undefined && saleEndsAt !== null && (typeof saleEndsAt !== "string" || !isWholeMinuteIsoDateTime(saleEndsAt))) {
|
|
4844
|
-
issues.push(issue$1([...path, "saleEndsAt"], "Ticket saleEndsAt must be an ISO datetime aligned to a whole minute"));
|
|
5157
|
+
function validateEventSeriesVerticalPreset(input) {
|
|
5158
|
+
const presetId = parseVerticalPresetId(input.verticalPresetId);
|
|
5159
|
+
if (!presetId) {
|
|
5160
|
+
return {
|
|
5161
|
+
ok: false,
|
|
5162
|
+
error: "unknown_vertical_preset"
|
|
5163
|
+
};
|
|
4845
5164
|
}
|
|
4846
|
-
|
|
4847
|
-
|
|
5165
|
+
const presetDefinition = getVerticalPresetDefinition(presetId);
|
|
5166
|
+
if (!isEventSeriesVerticalPresetDefinition(presetDefinition)) {
|
|
5167
|
+
return {
|
|
5168
|
+
ok: false,
|
|
5169
|
+
error: "non_event_series_preset"
|
|
5170
|
+
};
|
|
4848
5171
|
}
|
|
4849
|
-
|
|
4850
|
-
|
|
4851
|
-
|
|
5172
|
+
if (eventPresetForVerticalPreset(presetDefinition) !== input.canonicalPreset) {
|
|
5173
|
+
return {
|
|
5174
|
+
ok: false,
|
|
5175
|
+
error: "canonical_behaviour_mismatch"
|
|
5176
|
+
};
|
|
4852
5177
|
}
|
|
4853
|
-
if (issues.length > 0) return issues;
|
|
4854
5178
|
return {
|
|
4855
|
-
|
|
4856
|
-
|
|
4857
|
-
name,
|
|
4858
|
-
description: typeof raw.description === "string" ? raw.description : null,
|
|
4859
|
-
priceAmount,
|
|
4860
|
-
isActive: typeof raw.isActive === "boolean" ? raw.isActive : true,
|
|
4861
|
-
sortOrder: isWholeNumber(sortOrder) ? sortOrder : index,
|
|
4862
|
-
capacityLimit: isWholeNumber(capacityLimit) ? capacityLimit : null,
|
|
4863
|
-
attendanceScope: attendanceScope === "in_person" || attendanceScope === "online" || attendanceScope === "both" ? attendanceScope : "both",
|
|
4864
|
-
saleStartsAt: typeof saleStartsAt === "string" ? saleStartsAt : null,
|
|
4865
|
-
saleEndsAt: typeof saleEndsAt === "string" ? saleEndsAt : null
|
|
5179
|
+
ok: true,
|
|
5180
|
+
preset: presetDefinition
|
|
4866
5181
|
};
|
|
4867
5182
|
}
|
|
4868
|
-
function
|
|
4869
|
-
|
|
4870
|
-
|
|
4871
|
-
|
|
4872
|
-
|
|
4873
|
-
|
|
4874
|
-
|
|
4875
|
-
|
|
4876
|
-
|
|
4877
|
-
|
|
4878
|
-
|
|
4879
|
-
|
|
4880
|
-
|
|
4881
|
-
|
|
4882
|
-
|
|
4883
|
-
|
|
4884
|
-
|
|
4885
|
-
|
|
4886
|
-
|
|
4887
|
-
}
|
|
4888
|
-
const parsed = validateTicketType(rawTicketType, index, currency);
|
|
4889
|
-
if (Array.isArray(parsed)) {
|
|
4890
|
-
issues.push(...parsed);
|
|
4891
|
-
return;
|
|
4892
|
-
}
|
|
4893
|
-
if (isDuplicateIdentifier) {
|
|
4894
|
-
return;
|
|
4895
|
-
}
|
|
4896
|
-
ticketTypes.push(parsed);
|
|
4897
|
-
});
|
|
4898
|
-
if (issues.length > 0) return issues;
|
|
4899
|
-
return sortSdkEventTicketTypes(ticketTypes);
|
|
4900
|
-
}
|
|
4901
|
-
function isPricingIssueArray(value) {
|
|
4902
|
-
const first = value[0];
|
|
4903
|
-
return first !== undefined && "message" in first;
|
|
4904
|
-
}
|
|
4905
|
-
function sortSdkEventTicketTypes(ticketTypes) {
|
|
4906
|
-
return [...ticketTypes].sort((left, right) => {
|
|
4907
|
-
const leftOrder = left.sortOrder ?? Number.MAX_SAFE_INTEGER;
|
|
4908
|
-
const rightOrder = right.sortOrder ?? Number.MAX_SAFE_INTEGER;
|
|
4909
|
-
if (leftOrder !== rightOrder) return leftOrder - rightOrder;
|
|
4910
|
-
const identifierCompare = left.identifier.localeCompare(right.identifier);
|
|
4911
|
-
if (identifierCompare !== 0) return identifierCompare;
|
|
4912
|
-
return left.name.localeCompare(right.name);
|
|
5183
|
+
function getSelectedCanonicalBehaviours(presetIds) {
|
|
5184
|
+
return presetIds.map((presetId) => getVerticalPresetDefinition(presetId).canonicalBehaviour);
|
|
5185
|
+
}
|
|
5186
|
+
function getSelectedPresetForBehaviour(input) {
|
|
5187
|
+
return input.presetIds.map(getVerticalPresetDefinition).find((presetDefinition) => presetDefinition.canonicalBehaviour === input.canonicalBehaviour) ?? null;
|
|
5188
|
+
}
|
|
5189
|
+
function getVerticalPresetSelectionOptions(config) {
|
|
5190
|
+
const resolved = resolveSiteVerticalBookingConfig(config);
|
|
5191
|
+
if (resolved.kind !== "configured") return [];
|
|
5192
|
+
const selectedIds = new Set(resolved.config.selectedPresetIds);
|
|
5193
|
+
const selectedBehaviours = new Set(resolved.selectedPresets.map((presetDefinition) => presetDefinition.canonicalBehaviour));
|
|
5194
|
+
return getVerticalPresetsForVertical(resolved.config.verticalId, { availability: "selectable" }).flatMap((presetDefinition) => {
|
|
5195
|
+
const checked = selectedIds.has(presetDefinition.id);
|
|
5196
|
+
if (!checked && selectedBehaviours.has(presetDefinition.canonicalBehaviour)) return [];
|
|
5197
|
+
return [{
|
|
5198
|
+
preset: presetDefinition,
|
|
5199
|
+
checked,
|
|
5200
|
+
disabledReason: checked && selectedIds.size === 1 ? "last_selected_preset" : null
|
|
5201
|
+
}];
|
|
4913
5202
|
});
|
|
4914
5203
|
}
|
|
4915
|
-
function
|
|
4916
|
-
const
|
|
4917
|
-
|
|
4918
|
-
|
|
4919
|
-
|
|
4920
|
-
|
|
4921
|
-
|
|
5204
|
+
function setVerticalPresetSelected(config, presetId, selected) {
|
|
5205
|
+
const resolved = resolveSiteVerticalBookingConfig(config);
|
|
5206
|
+
const presetDefinition = getVerticalPresetDefinition(presetId);
|
|
5207
|
+
const verticalId = resolved.config.verticalId;
|
|
5208
|
+
if (!verticalId) {
|
|
5209
|
+
return {
|
|
5210
|
+
ok: false,
|
|
5211
|
+
error: "unconfigured",
|
|
5212
|
+
config: DEFAULT_SITE_VERTICAL_BOOKING_CONFIG
|
|
5213
|
+
};
|
|
4922
5214
|
}
|
|
4923
|
-
|
|
4924
|
-
|
|
4925
|
-
function classifySdkEventPricing(rawInput) {
|
|
4926
|
-
if (rawInput === undefined || rawInput === null) {
|
|
5215
|
+
const fallbackConfig = resolved.kind === "configured" ? resolved.config : DEFAULT_SITE_VERTICAL_BOOKING_CONFIG;
|
|
5216
|
+
if (presetDefinition.verticalId !== verticalId) {
|
|
4927
5217
|
return {
|
|
4928
|
-
ok:
|
|
4929
|
-
|
|
5218
|
+
ok: false,
|
|
5219
|
+
error: "cross_vertical_preset",
|
|
5220
|
+
config: fallbackConfig
|
|
4930
5221
|
};
|
|
4931
5222
|
}
|
|
4932
|
-
if (
|
|
5223
|
+
if (presetDefinition.availability === "future") {
|
|
4933
5224
|
return {
|
|
4934
5225
|
ok: false,
|
|
4935
|
-
|
|
5226
|
+
error: "future_preset",
|
|
5227
|
+
config: fallbackConfig
|
|
4936
5228
|
};
|
|
4937
5229
|
}
|
|
4938
|
-
|
|
4939
|
-
|
|
4940
|
-
|
|
4941
|
-
|
|
4942
|
-
|
|
4943
|
-
switch (kind) {
|
|
4944
|
-
case "free": return {
|
|
4945
|
-
ok: true,
|
|
4946
|
-
pricing: {
|
|
4947
|
-
kind: "free",
|
|
4948
|
-
priceDisplay,
|
|
4949
|
-
...acceptsPasses !== undefined ? { acceptsPasses } : {},
|
|
4950
|
-
...acceptsMemberships !== undefined ? { acceptsMemberships } : {}
|
|
4951
|
-
}
|
|
5230
|
+
if (resolved.kind !== "configured") {
|
|
5231
|
+
return {
|
|
5232
|
+
ok: false,
|
|
5233
|
+
error: "unconfigured",
|
|
5234
|
+
config: DEFAULT_SITE_VERTICAL_BOOKING_CONFIG
|
|
4952
5235
|
};
|
|
4953
|
-
|
|
4954
|
-
|
|
4955
|
-
|
|
4956
|
-
|
|
4957
|
-
issues.push(issue$1(["priceAmount"], "Fixed-price events require a non-negative integer priceAmount"));
|
|
4958
|
-
}
|
|
4959
|
-
const rawPriceCurrency = record.priceCurrency;
|
|
4960
|
-
const priceCurrency = rawPriceCurrency === undefined || rawPriceCurrency === null ? "USD" : isCurrency(rawPriceCurrency) ? rawPriceCurrency : null;
|
|
4961
|
-
if (rawPriceCurrency !== undefined && rawPriceCurrency !== null && !isCurrency(rawPriceCurrency)) {
|
|
4962
|
-
issues.push(issue$1(["priceCurrency"], "priceCurrency must be a 3-letter uppercase currency code"));
|
|
4963
|
-
}
|
|
4964
|
-
if (issues.length > 0) return {
|
|
4965
|
-
ok: false,
|
|
4966
|
-
issues
|
|
4967
|
-
};
|
|
4968
|
-
if (!isWholeNumber(priceAmount) || priceCurrency === null) {
|
|
4969
|
-
return {
|
|
4970
|
-
ok: false,
|
|
4971
|
-
issues: [issue$1(["priceAmount"], "Invalid fixed-price event pricing")]
|
|
4972
|
-
};
|
|
4973
|
-
}
|
|
4974
|
-
return {
|
|
4975
|
-
ok: true,
|
|
4976
|
-
pricing: {
|
|
4977
|
-
kind: "fixed_price",
|
|
4978
|
-
priceAmount,
|
|
4979
|
-
priceCurrency,
|
|
4980
|
-
priceDisplay,
|
|
4981
|
-
...acceptsPasses !== undefined ? { acceptsPasses } : {},
|
|
4982
|
-
...acceptsMemberships !== undefined ? { acceptsMemberships } : {}
|
|
4983
|
-
}
|
|
4984
|
-
};
|
|
4985
|
-
}
|
|
4986
|
-
case "ticket_types": {
|
|
4987
|
-
const priceCurrency = record.priceCurrency;
|
|
4988
|
-
if (!isCurrency(priceCurrency)) {
|
|
4989
|
-
return {
|
|
4990
|
-
ok: false,
|
|
4991
|
-
issues: [issue$1(["priceCurrency"], "Ticket-type pricing requires a 3-letter uppercase priceCurrency")]
|
|
4992
|
-
};
|
|
4993
|
-
}
|
|
4994
|
-
const ticketTypes = record.ticketTypes === undefined || record.ticketTypes === null ? [] : validateTicketTypes(record.ticketTypes, priceCurrency);
|
|
4995
|
-
if (isPricingIssueArray(ticketTypes)) {
|
|
4996
|
-
return {
|
|
4997
|
-
ok: false,
|
|
4998
|
-
issues: ticketTypes
|
|
4999
|
-
};
|
|
5000
|
-
}
|
|
5236
|
+
}
|
|
5237
|
+
const selectedIds = new Set(resolved.config.selectedPresetIds);
|
|
5238
|
+
if (!selected) {
|
|
5239
|
+
if (selectedIds.size <= 1) {
|
|
5001
5240
|
return {
|
|
5002
|
-
ok:
|
|
5003
|
-
|
|
5004
|
-
|
|
5005
|
-
priceCurrency,
|
|
5006
|
-
priceDisplay,
|
|
5007
|
-
...acceptsPasses !== undefined ? { acceptsPasses } : {},
|
|
5008
|
-
...acceptsMemberships !== undefined ? { acceptsMemberships } : {},
|
|
5009
|
-
ticketTypes
|
|
5010
|
-
}
|
|
5241
|
+
ok: false,
|
|
5242
|
+
error: "last_selected_preset",
|
|
5243
|
+
config: resolved.config
|
|
5011
5244
|
};
|
|
5012
5245
|
}
|
|
5013
|
-
|
|
5014
|
-
|
|
5015
|
-
issues: [issue$1(["kind"], "Pricing kind must be free, fixed_price, or ticket_types")]
|
|
5016
|
-
};
|
|
5017
|
-
}
|
|
5018
|
-
}
|
|
5019
|
-
|
|
5020
|
-
//#endregion
|
|
5021
|
-
//#region ../api/src/sdk-event-schedule.ts
|
|
5022
|
-
const timeRegex = /^([01]\d|2[0-3]):([0-5]\d)(:([0-5]\d))?$/;
|
|
5023
|
-
const explicitOffsetDateTimeRegex = /^(\d{4})-(\d{2})-(\d{2})T([01]\d|2[0-3]):([0-5]\d)(?::([0-5]\d))?(\.\d{1,3})?(Z|[+-]([01]\d|2[0-3]):[0-5]\d)$/;
|
|
5024
|
-
const localDateTimeRegex = /^(\d{4})-(\d{2})-(\d{2})T([01]\d|2[0-3]):([0-5]\d)(?::([0-5]\d))?(\.\d{1,3})?$/;
|
|
5025
|
-
function issue(path, message) {
|
|
5026
|
-
return {
|
|
5027
|
-
path,
|
|
5028
|
-
message
|
|
5029
|
-
};
|
|
5030
|
-
}
|
|
5031
|
-
function isValidDateParts(year, month, day) {
|
|
5032
|
-
const date = new Date(Date.UTC(year, month - 1, day));
|
|
5033
|
-
return date.getUTCFullYear() === year && date.getUTCMonth() === month - 1 && date.getUTCDate() === day;
|
|
5034
|
-
}
|
|
5035
|
-
function isZeroFraction(fraction) {
|
|
5036
|
-
return fraction === undefined || /^\.0+$/.test(fraction);
|
|
5037
|
-
}
|
|
5038
|
-
function datePart(year, month, day) {
|
|
5039
|
-
return `${year}-${month}-${day}`;
|
|
5040
|
-
}
|
|
5041
|
-
function timePart(hours, minutes, seconds = "00") {
|
|
5042
|
-
return `${hours}:${minutes}:${seconds}`;
|
|
5043
|
-
}
|
|
5044
|
-
function parseSdkEventDateTime(value) {
|
|
5045
|
-
const explicitMatch = explicitOffsetDateTimeRegex.exec(value);
|
|
5046
|
-
if (explicitMatch) {
|
|
5047
|
-
const [, yearRaw$1, monthRaw$1, dayRaw$1, hours$1, minutes$1, seconds$1 = "00", fraction$1] = explicitMatch;
|
|
5048
|
-
if (!yearRaw$1 || !monthRaw$1 || !dayRaw$1 || !hours$1 || !minutes$1) return null;
|
|
5049
|
-
const year$1 = Number(yearRaw$1);
|
|
5050
|
-
const month$1 = Number(monthRaw$1);
|
|
5051
|
-
const day$1 = Number(dayRaw$1);
|
|
5052
|
-
if (!isValidDateParts(year$1, month$1, day$1) || !isZeroFraction(fraction$1)) return null;
|
|
5053
|
-
const epochMs$1 = Date.parse(value);
|
|
5054
|
-
if (!Number.isFinite(epochMs$1)) return null;
|
|
5246
|
+
selectedIds.delete(presetId);
|
|
5247
|
+
const nextConfig = createSiteVerticalBookingConfig(verticalId, orderPresetIdsForVertical(verticalId, selectedIds));
|
|
5055
5248
|
return {
|
|
5056
|
-
|
|
5057
|
-
|
|
5058
|
-
time: timePart(hours$1, minutes$1, seconds$1),
|
|
5059
|
-
iso: new Date(epochMs$1).toISOString()
|
|
5249
|
+
ok: true,
|
|
5250
|
+
config: nextConfig ?? DEFAULT_SITE_VERTICAL_BOOKING_CONFIG
|
|
5060
5251
|
};
|
|
5061
5252
|
}
|
|
5062
|
-
const
|
|
5063
|
-
|
|
5064
|
-
|
|
5065
|
-
|
|
5066
|
-
const year = Number(yearRaw);
|
|
5067
|
-
const month = Number(monthRaw);
|
|
5068
|
-
const day = Number(dayRaw);
|
|
5069
|
-
if (!isValidDateParts(year, month, day) || !isZeroFraction(fraction)) return null;
|
|
5070
|
-
const epochMs = Date.UTC(year, month - 1, day, Number(hours), Number(minutes), Number(seconds));
|
|
5071
|
-
return {
|
|
5072
|
-
epochMs,
|
|
5073
|
-
date: datePart(yearRaw, monthRaw, dayRaw),
|
|
5074
|
-
time: timePart(hours, minutes, seconds),
|
|
5075
|
-
iso: new Date(epochMs).toISOString()
|
|
5076
|
-
};
|
|
5077
|
-
}
|
|
5078
|
-
function durationMinutesBetween(startsAt, endsAt) {
|
|
5079
|
-
if (endsAt.epochMs <= startsAt.epochMs) return null;
|
|
5080
|
-
const durationMs = endsAt.epochMs - startsAt.epochMs;
|
|
5081
|
-
if (durationMs % 6e4 !== 0) return null;
|
|
5082
|
-
return durationMs / 6e4;
|
|
5083
|
-
}
|
|
5084
|
-
function exceedsMaxDuration(durationMinutes, options) {
|
|
5085
|
-
return options.maxDurationMinutes !== undefined && durationMinutes > options.maxDurationMinutes;
|
|
5086
|
-
}
|
|
5087
|
-
function maxDurationMessage(options) {
|
|
5088
|
-
return options.maxDurationDays === undefined ? "Duration is above the allowed maximum" : `Duration must be ${options.maxDurationDays} days or less`;
|
|
5089
|
-
}
|
|
5090
|
-
function classifyCanonicalSchedule(input, options) {
|
|
5091
|
-
const issues = [];
|
|
5092
|
-
if (options.requireCanonicalFields && input.recurrenceRule === undefined) {
|
|
5093
|
-
issues.push(issue(["recurrenceRule"], "recurrenceRule is required"));
|
|
5094
|
-
}
|
|
5095
|
-
if (input.defaultStartTime !== undefined && input.defaultStartTime !== null && !timeRegex.test(input.defaultStartTime)) {
|
|
5096
|
-
issues.push(issue(["defaultStartTime"], "Invalid time format (HH:MM or HH:MM:SS)"));
|
|
5097
|
-
}
|
|
5098
|
-
if (options.requireCanonicalFields && !input.defaultStartTime) {
|
|
5099
|
-
issues.push(issue(["defaultStartTime"], "defaultStartTime is required"));
|
|
5100
|
-
}
|
|
5101
|
-
if (options.requireCanonicalFields && input.durationMinutes === undefined) {
|
|
5102
|
-
issues.push(issue(["durationMinutes"], "durationMinutes is required"));
|
|
5103
|
-
}
|
|
5104
|
-
if (input.durationMinutes !== undefined && input.durationMinutes !== null) {
|
|
5105
|
-
if (input.durationMinutes < 1) {
|
|
5106
|
-
issues.push(issue(["durationMinutes"], "durationMinutes must be at least 1"));
|
|
5107
|
-
} else if (exceedsMaxDuration(input.durationMinutes, options)) {
|
|
5108
|
-
issues.push(issue(["durationMinutes"], maxDurationMessage(options)));
|
|
5253
|
+
for (const existingPresetId of selectedIds) {
|
|
5254
|
+
const existingPreset = getVerticalPresetDefinition(existingPresetId);
|
|
5255
|
+
if (existingPreset.canonicalBehaviour === presetDefinition.canonicalBehaviour) {
|
|
5256
|
+
selectedIds.delete(existingPresetId);
|
|
5109
5257
|
}
|
|
5110
5258
|
}
|
|
5111
|
-
|
|
5112
|
-
ok: false,
|
|
5113
|
-
issues
|
|
5114
|
-
};
|
|
5259
|
+
selectedIds.add(presetId);
|
|
5115
5260
|
return {
|
|
5116
5261
|
ok: true,
|
|
5117
|
-
|
|
5118
|
-
canonical: {
|
|
5119
|
-
recurrenceRule: input.recurrenceRule ?? null,
|
|
5120
|
-
startDate: input.startDate,
|
|
5121
|
-
timezone: input.timezone,
|
|
5122
|
-
defaultStartTime: input.defaultStartTime ?? "09:00:00",
|
|
5123
|
-
durationMinutes: input.durationMinutes ?? 60
|
|
5124
|
-
}
|
|
5262
|
+
config: createSiteVerticalBookingConfig(verticalId, orderPresetIdsForVertical(verticalId, selectedIds)) ?? resolved.config
|
|
5125
5263
|
};
|
|
5126
5264
|
}
|
|
5127
|
-
function
|
|
5128
|
-
const
|
|
5129
|
-
const
|
|
5130
|
-
|
|
5131
|
-
|
|
5132
|
-
|
|
5133
|
-
|
|
5134
|
-
|
|
5135
|
-
|
|
5136
|
-
|
|
5137
|
-
|
|
5138
|
-
|
|
5139
|
-
|
|
5140
|
-
|
|
5141
|
-
|
|
5142
|
-
|
|
5143
|
-
|
|
5144
|
-
}
|
|
5145
|
-
|
|
5146
|
-
|
|
5147
|
-
|
|
5148
|
-
|
|
5149
|
-
issues.push(issue(["endsAt"], "Use either endsAt or durationMinutes with startsAt, not both"));
|
|
5150
|
-
}
|
|
5151
|
-
const startsAt = typeof input.startsAt === "string" ? parseSdkEventDateTime(input.startsAt) : null;
|
|
5152
|
-
const endsAt = typeof input.endsAt === "string" ? parseSdkEventDateTime(input.endsAt) : null;
|
|
5153
|
-
if (hasStartsAt && !startsAt) {
|
|
5154
|
-
issues.push(issue(["startsAt"], "Invalid datetime format. Use ISO datetime format, e.g. 2026-06-01T10:00:00Z"));
|
|
5155
|
-
}
|
|
5156
|
-
if (hasEndsAt && !endsAt) {
|
|
5157
|
-
issues.push(issue(["endsAt"], "Invalid datetime format. Use ISO datetime format, e.g. 2026-06-07T10:00:00Z"));
|
|
5158
|
-
}
|
|
5159
|
-
if (startsAt && endsAt) {
|
|
5160
|
-
const durationMinutes = durationMinutesBetween(startsAt, endsAt);
|
|
5161
|
-
if (durationMinutes === null) {
|
|
5162
|
-
issues.push(issue(["endsAt"], "endsAt must be after startsAt and align to a whole minute"));
|
|
5163
|
-
} else if (exceedsMaxDuration(durationMinutes, options)) {
|
|
5164
|
-
issues.push(issue(["endsAt"], maxDurationMessage(options)));
|
|
5165
|
-
}
|
|
5166
|
-
if (issues.length === 0 && durationMinutes !== null) {
|
|
5265
|
+
function setVerticalPresetsSelected(config, presetIds) {
|
|
5266
|
+
const resolved = resolveSiteVerticalBookingConfig(config);
|
|
5267
|
+
const verticalId = resolved.config.verticalId;
|
|
5268
|
+
if (!verticalId) {
|
|
5269
|
+
return {
|
|
5270
|
+
ok: false,
|
|
5271
|
+
error: "unconfigured",
|
|
5272
|
+
config: DEFAULT_SITE_VERTICAL_BOOKING_CONFIG
|
|
5273
|
+
};
|
|
5274
|
+
}
|
|
5275
|
+
const fallbackConfig = resolved.kind === "configured" ? resolved.config : DEFAULT_SITE_VERTICAL_BOOKING_CONFIG;
|
|
5276
|
+
if (presetIds.length === 0) {
|
|
5277
|
+
return {
|
|
5278
|
+
ok: false,
|
|
5279
|
+
error: "last_selected_preset",
|
|
5280
|
+
config: fallbackConfig
|
|
5281
|
+
};
|
|
5282
|
+
}
|
|
5283
|
+
const presetByBehaviour = new Map();
|
|
5284
|
+
for (const presetId of presetIds) {
|
|
5285
|
+
const presetDefinition = getVerticalPresetDefinition(presetId);
|
|
5286
|
+
if (presetDefinition.verticalId !== verticalId) {
|
|
5167
5287
|
return {
|
|
5168
|
-
ok:
|
|
5169
|
-
|
|
5170
|
-
|
|
5171
|
-
recurrenceRule: null,
|
|
5172
|
-
startDate: startsAt.date,
|
|
5173
|
-
timezone: input.timezone,
|
|
5174
|
-
defaultStartTime: startsAt.time,
|
|
5175
|
-
durationMinutes
|
|
5176
|
-
},
|
|
5177
|
-
startsAtIso: startsAt.iso,
|
|
5178
|
-
endsAtIso: endsAt.iso
|
|
5288
|
+
ok: false,
|
|
5289
|
+
error: "cross_vertical_preset",
|
|
5290
|
+
config: fallbackConfig
|
|
5179
5291
|
};
|
|
5180
5292
|
}
|
|
5181
|
-
|
|
5182
|
-
if (startsAt && hasDateTimeDuration && input.durationMinutes !== undefined && input.durationMinutes !== null) {
|
|
5183
|
-
if (input.durationMinutes < 1) {
|
|
5184
|
-
issues.push(issue(["durationMinutes"], "durationMinutes must be at least 1"));
|
|
5185
|
-
} else if (exceedsMaxDuration(input.durationMinutes, options)) {
|
|
5186
|
-
issues.push(issue(["durationMinutes"], maxDurationMessage(options)));
|
|
5187
|
-
}
|
|
5188
|
-
if (issues.length === 0) {
|
|
5189
|
-
const endsAtMs = startsAt.epochMs + input.durationMinutes * 6e4;
|
|
5293
|
+
if (presetDefinition.availability === "future") {
|
|
5190
5294
|
return {
|
|
5191
|
-
ok:
|
|
5192
|
-
|
|
5193
|
-
|
|
5194
|
-
recurrenceRule: null,
|
|
5195
|
-
startDate: startsAt.date,
|
|
5196
|
-
timezone: input.timezone,
|
|
5197
|
-
defaultStartTime: startsAt.time,
|
|
5198
|
-
durationMinutes: input.durationMinutes
|
|
5199
|
-
},
|
|
5200
|
-
startsAtIso: startsAt.iso,
|
|
5201
|
-
endsAtIso: new Date(endsAtMs).toISOString()
|
|
5295
|
+
ok: false,
|
|
5296
|
+
error: "future_preset",
|
|
5297
|
+
config: fallbackConfig
|
|
5202
5298
|
};
|
|
5203
5299
|
}
|
|
5300
|
+
presetByBehaviour.set(presetDefinition.canonicalBehaviour, presetId);
|
|
5204
5301
|
}
|
|
5205
|
-
|
|
5206
|
-
ok: false,
|
|
5207
|
-
issues
|
|
5208
|
-
};
|
|
5209
|
-
}
|
|
5210
|
-
|
|
5211
|
-
//#endregion
|
|
5212
|
-
//#region ../api/src/sdk-event-upsert.ts
|
|
5213
|
-
const sdkEventOccurrenceWireSchema = z.object({
|
|
5214
|
-
startsAt: z.string(),
|
|
5215
|
-
endsAt: z.string(),
|
|
5216
|
-
timeZone: z.string()
|
|
5217
|
-
});
|
|
5218
|
-
const sdkEventOccurrenceInputSchema = z.object({
|
|
5219
|
-
startsAt: z.string().datetime({ offset: true }),
|
|
5220
|
-
endsAt: z.string().datetime({ offset: true }),
|
|
5221
|
-
timeZone: z.string().min(1).refine((value) => parseBookingTimeZone(value).ok, "Invalid IANA timezone")
|
|
5222
|
-
}).refine(({ startsAt, endsAt }) => Date.parse(endsAt) > Date.parse(startsAt), {
|
|
5223
|
-
path: ["endsAt"],
|
|
5224
|
-
message: "endsAt must be after startsAt"
|
|
5225
|
-
});
|
|
5226
|
-
const sdkEventUpsertOptionalFieldSchemas = {
|
|
5227
|
-
path: z.string().startsWith("/", "path must start with /").nullable().optional(),
|
|
5228
|
-
status: z.enum([
|
|
5229
|
-
"draft",
|
|
5230
|
-
"active",
|
|
5231
|
-
"archived",
|
|
5232
|
-
"cancelled",
|
|
5233
|
-
"completed"
|
|
5234
|
-
]).optional(),
|
|
5235
|
-
visibility: z.enum([
|
|
5236
|
-
"private",
|
|
5237
|
-
"public",
|
|
5238
|
-
"unlisted"
|
|
5239
|
-
]).optional(),
|
|
5240
|
-
preset: z.enum([
|
|
5241
|
-
"event",
|
|
5242
|
-
"class",
|
|
5243
|
-
"course",
|
|
5244
|
-
"workshop",
|
|
5245
|
-
"retreat",
|
|
5246
|
-
"performance"
|
|
5247
|
-
]).optional(),
|
|
5248
|
-
bookingScope: z.enum(["single_occurrence", "whole_series"]).optional(),
|
|
5249
|
-
occurrences: z.array(sdkEventOccurrenceWireSchema).optional(),
|
|
5250
|
-
capacity: z.number().int().positive().nullable().optional()
|
|
5251
|
-
};
|
|
5252
|
-
function timingIssue(path, message) {
|
|
5253
|
-
return {
|
|
5254
|
-
path,
|
|
5255
|
-
message
|
|
5256
|
-
};
|
|
5257
|
-
}
|
|
5258
|
-
function parseExplicitOccurrence(occurrence, index) {
|
|
5259
|
-
const startsAt = parseUtcIsoDateTimeString(occurrence.startsAt);
|
|
5260
|
-
const endsAt = parseUtcIsoDateTimeString(occurrence.endsAt);
|
|
5261
|
-
const timeZone = parseBookingTimeZone(occurrence.timeZone);
|
|
5262
|
-
const path = ["occurrences", String(index)];
|
|
5263
|
-
const issues = [];
|
|
5264
|
-
if (!startsAt.ok) issues.push(timingIssue([...path, "startsAt"], "startsAt must be a valid offset-aware ISO datetime"));
|
|
5265
|
-
if (!endsAt.ok) issues.push(timingIssue([...path, "endsAt"], "endsAt must be a valid offset-aware ISO datetime"));
|
|
5266
|
-
if (!timeZone.ok) issues.push(timingIssue([...path, "timeZone"], "Invalid IANA timezone"));
|
|
5267
|
-
if (!startsAt.ok || !endsAt.ok || !timeZone.ok) return {
|
|
5268
|
-
ok: false,
|
|
5269
|
-
issues
|
|
5270
|
-
};
|
|
5271
|
-
if (Date.parse(endsAt.value) <= Date.parse(startsAt.value)) {
|
|
5302
|
+
if (resolved.kind !== "configured") {
|
|
5272
5303
|
return {
|
|
5273
5304
|
ok: false,
|
|
5274
|
-
|
|
5305
|
+
error: "unconfigured",
|
|
5306
|
+
config: DEFAULT_SITE_VERTICAL_BOOKING_CONFIG
|
|
5275
5307
|
};
|
|
5276
5308
|
}
|
|
5309
|
+
const nextConfig = createSiteVerticalBookingConfig(verticalId, orderPresetIdsForVertical(verticalId, new Set(presetByBehaviour.values())));
|
|
5277
5310
|
return {
|
|
5278
5311
|
ok: true,
|
|
5279
|
-
|
|
5280
|
-
startsAt: startsAt.value,
|
|
5281
|
-
endsAt: endsAt.value,
|
|
5282
|
-
timeZone: timeZone.value
|
|
5283
|
-
}
|
|
5312
|
+
config: nextConfig ?? resolved.config
|
|
5284
5313
|
};
|
|
5285
5314
|
}
|
|
5286
|
-
|
|
5287
|
-
|
|
5288
|
-
|
|
5289
|
-
|
|
5290
|
-
|
|
5291
|
-
|
|
5292
|
-
|
|
5293
|
-
|
|
5294
|
-
|
|
5295
|
-
|
|
5296
|
-
|
|
5297
|
-
const issues = [];
|
|
5298
|
-
input.occurrences.forEach((occurrence, index) => {
|
|
5299
|
-
const parsed = parseExplicitOccurrence(occurrence, index);
|
|
5300
|
-
if (!parsed.ok) {
|
|
5301
|
-
issues.push(...parsed.issues);
|
|
5302
|
-
return;
|
|
5303
|
-
}
|
|
5304
|
-
if (starts.has(parsed.value.startsAt)) {
|
|
5305
|
-
issues.push(timingIssue([
|
|
5306
|
-
"occurrences",
|
|
5307
|
-
String(index),
|
|
5308
|
-
"startsAt"
|
|
5309
|
-
], "Occurrence start times must be unique within a series"));
|
|
5310
|
-
return;
|
|
5311
|
-
}
|
|
5312
|
-
if (timeZone && timeZone !== parsed.value.timeZone) {
|
|
5313
|
-
issues.push(timingIssue([
|
|
5314
|
-
"occurrences",
|
|
5315
|
-
String(index),
|
|
5316
|
-
"timeZone"
|
|
5317
|
-
], "All event occurrences must use the same timezone"));
|
|
5318
|
-
return;
|
|
5319
|
-
}
|
|
5320
|
-
starts.add(parsed.value.startsAt);
|
|
5321
|
-
timeZone ??= parsed.value.timeZone;
|
|
5322
|
-
occurrences.push(parsed.value);
|
|
5323
|
-
});
|
|
5324
|
-
const [first, ...rest] = occurrences;
|
|
5325
|
-
if (issues.length > 0 || !first || !timeZone) return {
|
|
5326
|
-
ok: false,
|
|
5327
|
-
issues
|
|
5328
|
-
};
|
|
5329
|
-
return {
|
|
5330
|
-
ok: true,
|
|
5331
|
-
timingAuthority: {
|
|
5332
|
-
kind: "explicit_occurrences",
|
|
5333
|
-
bookingScope: input.bookingScope,
|
|
5334
|
-
occurrences: [first, ...rest],
|
|
5335
|
-
timeZone
|
|
5336
|
-
}
|
|
5337
|
-
};
|
|
5338
|
-
}
|
|
5339
|
-
if (input.schedule === undefined || typeof input.schedule !== "object" || input.schedule === null || Array.isArray(input.schedule)) {
|
|
5315
|
+
/**
|
|
5316
|
+
* Return a configured selection in the catalog's canonical order.
|
|
5317
|
+
*
|
|
5318
|
+
* This deliberately delegates ordering and selection semantics to the core
|
|
5319
|
+
* authority instead of making dashboard planners maintain a second catalog
|
|
5320
|
+
* ordering implementation. Runtime-invalid values are reported as a closed
|
|
5321
|
+
* result so this pure helper never throws while planning.
|
|
5322
|
+
*/
|
|
5323
|
+
function canonicalizeConfiguredBookingVerticalConfig(config) {
|
|
5324
|
+
const resolved = resolveSiteVerticalBookingConfig(config);
|
|
5325
|
+
if (resolved.kind === "invalid") {
|
|
5340
5326
|
return {
|
|
5341
5327
|
ok: false,
|
|
5342
|
-
issues:
|
|
5328
|
+
issues: resolved.issues
|
|
5343
5329
|
};
|
|
5344
5330
|
}
|
|
5345
|
-
|
|
5346
|
-
return schedule.ok ? {
|
|
5347
|
-
ok: true,
|
|
5348
|
-
timingAuthority: {
|
|
5349
|
-
kind: "authored_schedule",
|
|
5350
|
-
schedule: schedule.canonical,
|
|
5351
|
-
bookingScope: input.bookingScope
|
|
5352
|
-
}
|
|
5353
|
-
} : {
|
|
5354
|
-
ok: false,
|
|
5355
|
-
issues: schedule.issues.map((issue$3) => timingIssue(["schedule", ...issue$3.path], issue$3.message))
|
|
5356
|
-
};
|
|
5357
|
-
}
|
|
5358
|
-
function parseSdkEventUpsertInput(input, options = {}) {
|
|
5359
|
-
const runRefs = new Set(input.runRefs);
|
|
5360
|
-
const runsByIdentifier = new Map(input.runs.map((run) => [run.identifier, run]));
|
|
5361
|
-
if (input.runRefs.length !== runRefs.size || runsByIdentifier.size !== input.runs.length || input.runRefs.length !== input.runs.length || input.runRefs.some((ref) => !runsByIdentifier.has(ref)) || input.runs.some((run) => run.eventRef !== input.identifier)) {
|
|
5331
|
+
if (resolved.kind === "unconfigured") {
|
|
5362
5332
|
return {
|
|
5363
5333
|
ok: false,
|
|
5364
|
-
issues: [{
|
|
5365
|
-
source: "graph",
|
|
5366
|
-
path: ["runRefs"],
|
|
5367
|
-
message: "Event runRefs and runs must form one complete parent graph",
|
|
5368
|
-
issue: { kind: "incoherentParentRunGraph" }
|
|
5369
|
-
}]
|
|
5334
|
+
issues: [{ code: "missing_vertical" }]
|
|
5370
5335
|
};
|
|
5371
5336
|
}
|
|
5372
|
-
const
|
|
5373
|
-
|
|
5374
|
-
|
|
5375
|
-
return run;
|
|
5376
|
-
};
|
|
5377
|
-
const firstRun = runForRef(input.runRefs[0]);
|
|
5378
|
-
const remainingRuns = input.runRefs.slice(1).map(runForRef);
|
|
5379
|
-
if (firstRun.bookingScope === "single_occurrence" && remainingRuns.length > 0 || remainingRuns.some((run) => run.bookingScope !== firstRun.bookingScope)) {
|
|
5337
|
+
const selection = setVerticalPresetsSelected(resolved.config, resolved.config.selectedPresetIds);
|
|
5338
|
+
if (!selection.ok) {
|
|
5339
|
+
const issue = selection.error === "cross_vertical_preset" ? { code: "preset_vertical_mismatch" } : selection.error === "future_preset" ? { code: "future_preset_selected" } : selection.error === "last_selected_preset" ? { code: "empty_selected_presets" } : { code: "missing_vertical" };
|
|
5380
5340
|
return {
|
|
5381
5341
|
ok: false,
|
|
5382
|
-
issues: [
|
|
5383
|
-
source: "graph",
|
|
5384
|
-
path: ["runs"],
|
|
5385
|
-
message: "An Event must contain one ordinary run or one or more whole-series runs",
|
|
5386
|
-
issue: { kind: "incoherentParentRunGraph" }
|
|
5387
|
-
}]
|
|
5342
|
+
issues: [issue]
|
|
5388
5343
|
};
|
|
5389
5344
|
}
|
|
5390
|
-
const
|
|
5391
|
-
|
|
5392
|
-
|
|
5393
|
-
|
|
5345
|
+
const canonical = resolveSiteVerticalBookingConfig(selection.config);
|
|
5346
|
+
return canonical.kind === "configured" ? {
|
|
5347
|
+
ok: true,
|
|
5348
|
+
config: canonical.config
|
|
5349
|
+
} : canonical.kind === "invalid" ? {
|
|
5394
5350
|
ok: false,
|
|
5395
|
-
issues
|
|
5396
|
-
}
|
|
5397
|
-
if (!firstResult.ok) return {
|
|
5351
|
+
issues: canonical.issues
|
|
5352
|
+
} : {
|
|
5398
5353
|
ok: false,
|
|
5399
|
-
issues:
|
|
5354
|
+
issues: [{ code: "missing_vertical" }]
|
|
5400
5355
|
};
|
|
5401
|
-
const parsedRemaining = remainingResults.flatMap((result) => result.ok ? [result.value] : []);
|
|
5402
|
-
return {
|
|
5403
|
-
ok: true,
|
|
5404
|
-
value: {
|
|
5405
|
-
...input,
|
|
5406
|
-
runs: [firstResult.value, ...parsedRemaining]
|
|
5407
|
-
}
|
|
5408
|
-
};
|
|
5409
|
-
}
|
|
5410
|
-
function toSdkEventRunUpsertBody(input) {
|
|
5411
|
-
const { timingAuthority, ...common } = input;
|
|
5412
|
-
switch (timingAuthority.kind) {
|
|
5413
|
-
case "authored_schedule": return {
|
|
5414
|
-
...common,
|
|
5415
|
-
...timingAuthority.bookingScope === undefined ? {} : { bookingScope: timingAuthority.bookingScope },
|
|
5416
|
-
schedule: timingAuthority.schedule
|
|
5417
|
-
};
|
|
5418
|
-
case "explicit_occurrences": return {
|
|
5419
|
-
...common,
|
|
5420
|
-
bookingScope: timingAuthority.bookingScope,
|
|
5421
|
-
occurrences: timingAuthority.occurrences
|
|
5422
|
-
};
|
|
5423
|
-
default: return timingAuthority;
|
|
5424
|
-
}
|
|
5425
5356
|
}
|
|
5426
|
-
|
|
5427
|
-
|
|
5428
|
-
|
|
5429
|
-
|
|
5430
|
-
|
|
5431
|
-
|
|
5357
|
+
/** Compare configured booking selections by catalog order, not authored array order. */
|
|
5358
|
+
function areConfiguredBookingVerticalConfigsEqual(left, right) {
|
|
5359
|
+
const canonicalLeft = canonicalizeConfiguredBookingVerticalConfig(left);
|
|
5360
|
+
const canonicalRight = canonicalizeConfiguredBookingVerticalConfig(right);
|
|
5361
|
+
if (!canonicalLeft.ok || !canonicalRight.ok) return false;
|
|
5362
|
+
return canonicalLeft.config.verticalId === canonicalRight.config.verticalId && canonicalLeft.config.selectedPresetIds.length === canonicalRight.config.selectedPresetIds.length && canonicalLeft.config.selectedPresetIds.every((presetId, index) => presetId === canonicalRight.config.selectedPresetIds[index]);
|
|
5432
5363
|
}
|
|
5433
|
-
function
|
|
5434
|
-
|
|
5435
|
-
const pricingResult = classifySdkEventPricing(input.pricing);
|
|
5436
|
-
const paymentTermsResult = classifySdkEventPaymentTerms(input.paymentTerms);
|
|
5437
|
-
const issues = [];
|
|
5438
|
-
if (timingResult.ok && pricingResult.ok && paymentTermsResult.ok) {
|
|
5439
|
-
const { schedule: _schedule, occurrences: _occurrences, bookingScope: _bookingScope, pricing: _pricing, paymentTerms: _paymentTerms, ...common } = input;
|
|
5440
|
-
return {
|
|
5441
|
-
ok: true,
|
|
5442
|
-
value: {
|
|
5443
|
-
...common,
|
|
5444
|
-
timingAuthority: timingResult.timingAuthority,
|
|
5445
|
-
pricing: pricingResult.pricing,
|
|
5446
|
-
paymentTerms: paymentTermsResult.paymentTerms
|
|
5447
|
-
}
|
|
5448
|
-
};
|
|
5449
|
-
}
|
|
5450
|
-
if (!timingResult.ok) {
|
|
5451
|
-
issues.push(...timingResult.issues.map((timing) => ({
|
|
5452
|
-
runIndex,
|
|
5453
|
-
source: "timing",
|
|
5454
|
-
path: timing.path,
|
|
5455
|
-
message: timing.message,
|
|
5456
|
-
issue: timing
|
|
5457
|
-
})));
|
|
5458
|
-
}
|
|
5459
|
-
if (!pricingResult.ok) {
|
|
5460
|
-
issues.push(...pricingResult.issues.map((pricingIssue) => ({
|
|
5461
|
-
runIndex,
|
|
5462
|
-
source: "pricing",
|
|
5463
|
-
path: pricingIssue.path,
|
|
5464
|
-
message: pricingIssue.message,
|
|
5465
|
-
issue: pricingIssue
|
|
5466
|
-
})));
|
|
5467
|
-
}
|
|
5468
|
-
if (!paymentTermsResult.ok) {
|
|
5469
|
-
issues.push(...paymentTermsResult.issues.map((paymentTermsIssue) => ({
|
|
5470
|
-
runIndex,
|
|
5471
|
-
source: "paymentTerms",
|
|
5472
|
-
path: paymentTermsIssue.path,
|
|
5473
|
-
message: paymentTermsIssue.message,
|
|
5474
|
-
issue: paymentTermsIssue
|
|
5475
|
-
})));
|
|
5476
|
-
}
|
|
5477
|
-
return {
|
|
5478
|
-
ok: false,
|
|
5479
|
-
issues
|
|
5480
|
-
};
|
|
5364
|
+
function orderPresetIdsForVertical(verticalId, presetIds) {
|
|
5365
|
+
return getVerticalPresetsForVertical(verticalId, { availability: "selectable" }).map((presetDefinition) => presetDefinition.id).filter((id) => presetIds.has(id));
|
|
5481
5366
|
}
|
|
5482
5367
|
|
|
5483
5368
|
//#endregion
|
|
5484
|
-
export {
|
|
5369
|
+
export { parseEventSeriesId as $, normalizeLocationAddressText as A, ok as At, parseUtcIsoDateTimeString as B, parseNewsletterSignupDestinationProvider as C, unsafeAsSiteScopedStoragePath as Ct, mapNonEmptyArray as D, unsafeAsUploadSessionId as Dt, fromArray as E, unsafeAsStoragePath as Et, formatLocalScheduleDate as F, parseEventPageSurfaceSubject as G, toScheduledBookingDateRange as H, formatLocalScheduleTime as I, publicEventPricingCurrency as J, formatBookingPriceDisplay as K, getBookingLocalDateKey as L, formatBookingDate as M, formatBookingDateParts as N, hasText as O, unsafeAsUserId as Ot, formatBookingTime as P, parseApiKeyId as Q, getLocalScheduleDate as R, parseNewsletterSignupAuthoringDestinationValue as S, unsafeAsSiteId as St, assertAllowedPublicUrl as T, unsafeAsStorageBucketName as Tt, toScheduledBookingDateTime as U, toBookingTimeZone as V, parseBookingTimeZone as W, formatCurrencyAmount as X, publicEventPricingType as Y, assertNever as Z, parseFooterCompositionV1 as _, unsafeAsPositiveByteCount as _t, parseVerticalPresetId as a, unsafeAsBookingRequirementId as at, parseMailchimpTagName as b, unsafeAsSafeDownloadFilename as bt, EVENT_PRESET_VALUES as c, unsafeAsDemoCapability as ct, resolvePlanningDateInput as d, unsafeAsEventOfferingId as dt, parseMediaAssetId as et, createSecret as f, unsafeAsEventSeriesId as ft, footerNavigationLayouts as g, unsafeAsOriginalFilename as gt, stableJsonStringify as h, unsafeAsOccurrenceId as ht, parseVerticalId as i, unsafeAsAppointmentResourceId as it, toLocationCoordinates as j, isPresent as k, err as kt, ORDINARY_EVENT_PRESET_VALUES as l, unsafeAsDetectedMimeType as lt, parseManagementKey as m, unsafeAsMediaAssetId as mt, canonicalizeConfiguredBookingVerticalConfig as n, parseSiteId as nt, resolveSiteVerticalBookingConfig as o, unsafeAsBookingSessionId as ot, exposeSecret as p, unsafeAsFileChecksumSha256 as pt, formatEventPricing as q, isEventSeriesVerticalPresetDefinition as r, parseUserId as rt, DEFAULT_EVENT_TYPE_PRESETS as s, unsafeAsDeclaredMimeType as st, areConfiguredBookingVerticalConfigsEqual as t, parsePageId as tt, SURFACE_EVENT_PRESET_VALUES as u, unsafeAsEntitlementId as ut, parseFooterModuleId as v, unsafeAsPositiveSeconds as vt, isCanonicalRichTextDoc as w, unsafeAsStaffMemberId as wt, parseNewsletterListId as x, unsafeAsSiteFontId as xt, parseMailchimpAudienceId as y, unsafeAsProductId as yt, getLocalScheduleTime as z };
|