riverbankcms 0.2.0 → 0.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/acceptance/matrix.mjs +62 -6
- package/dist/cli/index.mjs +11820 -2431
- package/dist/cli/init-docs/content/agents-section.md +9 -2
- package/dist/cli/init-docs/content/cli-reference.md +69 -5
- package/dist/cli/init-docs/content/content-management.md +12 -3
- 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-CzMX5BZI.mjs} +8 -7
- package/dist/load-config-226EuL6w.mjs +5 -0
- package/dist/{load-config-CJUwrGHA.mjs → load-config-Boa0tkmk.mjs} +1 -1
- package/dist/plan-2c4zVtlW.mjs +2213 -0
- package/dist/scaffold/index.mjs +5 -0
- package/dist/sdk-event-upsert-BCZUdIrL.mjs +887 -0
- package/dist/{sdk-peer-D9FHJ54i.mjs → sdk-peer-DiZYsNXj.mjs} +5 -2
- package/dist/{sdk-event-upsert-CTqsrhFT.mjs → src-XEJYAKbQ.mjs} +138 -925
- package/dist/{style-lftMrun_.mjs → style-3BjmymXd.mjs} +25860 -25137
- 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);
|
|
@@ -639,18 +639,36 @@ function formatEventPricing(pricing, options = {}) {
|
|
|
639
639
|
|
|
640
640
|
//#endregion
|
|
641
641
|
//#region ../core/src/event-page-surface-subject.ts
|
|
642
|
+
function createEventSeriesPageSurfaceSubject(eventSeriesId, bookingScope) {
|
|
643
|
+
return bookingScope === "whole_series" ? {
|
|
644
|
+
kind: "eventSeries",
|
|
645
|
+
eventSeriesId,
|
|
646
|
+
bookingScope,
|
|
647
|
+
isWholeSeriesBooking: true
|
|
648
|
+
} : {
|
|
649
|
+
kind: "eventSeries",
|
|
650
|
+
eventSeriesId,
|
|
651
|
+
bookingScope
|
|
652
|
+
};
|
|
653
|
+
}
|
|
642
654
|
function parseEventPageSurfaceSubject(value) {
|
|
643
|
-
if (!isRecord$
|
|
655
|
+
if (!isRecord$6(value)) return err({ kind: "notObject" });
|
|
644
656
|
switch (value.kind) {
|
|
645
657
|
case "eventSeries": {
|
|
646
658
|
const eventSeriesId = parseEventSeriesId(value.eventSeriesId);
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
659
|
+
if (!eventSeriesId.ok) {
|
|
660
|
+
return err({
|
|
661
|
+
kind: "invalidEventSeriesId",
|
|
662
|
+
error: eventSeriesId.error
|
|
663
|
+
});
|
|
664
|
+
}
|
|
665
|
+
if (value.bookingScope !== "single_occurrence" && value.bookingScope !== "whole_series") {
|
|
666
|
+
return err({
|
|
667
|
+
kind: "invalidEventSeriesBookingScope",
|
|
668
|
+
bookingScope: value.bookingScope
|
|
669
|
+
});
|
|
670
|
+
}
|
|
671
|
+
return ok(createEventSeriesPageSurfaceSubject(eventSeriesId.value, value.bookingScope));
|
|
654
672
|
}
|
|
655
673
|
case "eventOffering": {
|
|
656
674
|
const eventOfferingId = parseEventOfferingId(value.eventOfferingId);
|
|
@@ -692,7 +710,7 @@ function parseEventPageSurfaceSubject(value) {
|
|
|
692
710
|
function toEventPageSurfaceSubjectWire(subject) {
|
|
693
711
|
return subject;
|
|
694
712
|
}
|
|
695
|
-
function isRecord$
|
|
713
|
+
function isRecord$6(value) {
|
|
696
714
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
697
715
|
}
|
|
698
716
|
|
|
@@ -2233,7 +2251,7 @@ function reconcileParticipantRoster(input) {
|
|
|
2233
2251
|
}
|
|
2234
2252
|
function planParticipantSubjectResolution(input) {
|
|
2235
2253
|
const existingParticipantIdByEmail = new Map(input.existingParticipantsByEmail.map((participant) => [participant.email, participant.participantId]));
|
|
2236
|
-
const duplicateSlotKeysByEmail = new Map(input.projection.issues.filter((issue
|
|
2254
|
+
const duplicateSlotKeysByEmail = new Map(input.projection.issues.filter((issue) => issue.kind === "duplicate_email_in_booking_context").map((issue) => [issue.email, issue.slotKeys]));
|
|
2237
2255
|
return input.projection.slots.map((slot) => {
|
|
2238
2256
|
if (slot.email === null) {
|
|
2239
2257
|
return {
|
|
@@ -2699,7 +2717,7 @@ const knownSocialPlatforms = [
|
|
|
2699
2717
|
"x",
|
|
2700
2718
|
"youtube"
|
|
2701
2719
|
];
|
|
2702
|
-
function isRecord$
|
|
2720
|
+
function isRecord$5(value) {
|
|
2703
2721
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
2704
2722
|
}
|
|
2705
2723
|
function trimmedOptionalString(value) {
|
|
@@ -2709,7 +2727,7 @@ function trimmedOptionalString(value) {
|
|
|
2709
2727
|
return trimmed.length > 0 ? trimmed : null;
|
|
2710
2728
|
}
|
|
2711
2729
|
function parsePublicContactDetails(value) {
|
|
2712
|
-
if (!isRecord$
|
|
2730
|
+
if (!isRecord$5(value)) {
|
|
2713
2731
|
return err({
|
|
2714
2732
|
code: "invalid_shape",
|
|
2715
2733
|
path: "contact",
|
|
@@ -2805,7 +2823,7 @@ function minutesSinceMidnight(value) {
|
|
|
2805
2823
|
}
|
|
2806
2824
|
function parseOpeningHoursDay(value, expectedWeekday, index) {
|
|
2807
2825
|
const path = `openingHours.days.${index}`;
|
|
2808
|
-
if (!isRecord$
|
|
2826
|
+
if (!isRecord$5(value) || value.weekday !== expectedWeekday) {
|
|
2809
2827
|
return err({
|
|
2810
2828
|
code: "invalid_weekdays",
|
|
2811
2829
|
path,
|
|
@@ -2826,7 +2844,7 @@ function parseOpeningHoursDay(value, expectedWeekday, index) {
|
|
|
2826
2844
|
const intervals = [];
|
|
2827
2845
|
for (const [intervalIndex, rawInterval] of value.intervals.entries()) {
|
|
2828
2846
|
const intervalPath = `${path}.intervals.${intervalIndex}`;
|
|
2829
|
-
if (!isRecord$
|
|
2847
|
+
if (!isRecord$5(rawInterval)) {
|
|
2830
2848
|
return err({
|
|
2831
2849
|
code: "invalid_interval",
|
|
2832
2850
|
path: intervalPath,
|
|
@@ -2876,7 +2894,7 @@ function parseOpeningHoursDay(value, expectedWeekday, index) {
|
|
|
2876
2894
|
});
|
|
2877
2895
|
}
|
|
2878
2896
|
function parseWeeklyOpeningHours(value) {
|
|
2879
|
-
if (!isRecord$
|
|
2897
|
+
if (!isRecord$5(value) || !Array.isArray(value.days) || value.days.length !== publicProfileWeekdays.length) {
|
|
2880
2898
|
return err({
|
|
2881
2899
|
code: "invalid_weekdays",
|
|
2882
2900
|
path: "openingHours.days",
|
|
@@ -2956,7 +2974,7 @@ function parsePublicSocialLinks(value) {
|
|
|
2956
2974
|
const ids = new Set();
|
|
2957
2975
|
for (const [index, rawLink] of value.entries()) {
|
|
2958
2976
|
const path = `socialLinks.${index}`;
|
|
2959
|
-
if (!isRecord$
|
|
2977
|
+
if (!isRecord$5(rawLink)) {
|
|
2960
2978
|
return err({
|
|
2961
2979
|
code: "invalid_social_link",
|
|
2962
2980
|
path,
|
|
@@ -3003,7 +3021,7 @@ function parsePublicSocialLinks(value) {
|
|
|
3003
3021
|
return ok(links);
|
|
3004
3022
|
}
|
|
3005
3023
|
function parsePublicProfile(value) {
|
|
3006
|
-
if (!isRecord$
|
|
3024
|
+
if (!isRecord$5(value)) {
|
|
3007
3025
|
return err({
|
|
3008
3026
|
code: "invalid_shape",
|
|
3009
3027
|
path: "",
|
|
@@ -3032,7 +3050,7 @@ function emptyPublicProfile() {
|
|
|
3032
3050
|
|
|
3033
3051
|
//#endregion
|
|
3034
3052
|
//#region ../core/src/rich-text.ts
|
|
3035
|
-
function isRecord$
|
|
3053
|
+
function isRecord$4(value) {
|
|
3036
3054
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
3037
3055
|
}
|
|
3038
3056
|
function hasOnlyKeys(value, allowed) {
|
|
@@ -3054,10 +3072,10 @@ function isDenseArrayOf(value, predicate) {
|
|
|
3054
3072
|
return true;
|
|
3055
3073
|
}
|
|
3056
3074
|
function isTipTapMark(value) {
|
|
3057
|
-
return isRecord$
|
|
3075
|
+
return isRecord$4(value) && hasOnlyKeys(value, markKeys) && typeof value.type === "string" && value.type.length > 0 && (value.attrs === undefined || isRecord$4(value.attrs));
|
|
3058
3076
|
}
|
|
3059
3077
|
function isTipTapNode(value) {
|
|
3060
|
-
if (!isRecord$
|
|
3078
|
+
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
3079
|
return false;
|
|
3062
3080
|
}
|
|
3063
3081
|
if (value.type === "text") {
|
|
@@ -3066,7 +3084,7 @@ function isTipTapNode(value) {
|
|
|
3066
3084
|
return value.text === undefined;
|
|
3067
3085
|
}
|
|
3068
3086
|
function isCanonicalRichTextDoc(value) {
|
|
3069
|
-
return isRecord$
|
|
3087
|
+
return isRecord$4(value) && hasOnlyKeys(value, rootKeys) && value.type === "doc" && Array.isArray(value.content) && isDenseArrayOf(value.content, isTipTapNode);
|
|
3070
3088
|
}
|
|
3071
3089
|
|
|
3072
3090
|
//#endregion
|
|
@@ -3074,7 +3092,7 @@ function isCanonicalRichTextDoc(value) {
|
|
|
3074
3092
|
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
3093
|
const MAILCHIMP_AUDIENCE_ID_PATTERN = /^[A-Za-z0-9_-]{1,100}$/u;
|
|
3076
3094
|
const CONTROL_CHARACTER_PATTERN = /[\u0000-\u001f\u007f]/u;
|
|
3077
|
-
function isRecord$
|
|
3095
|
+
function isRecord$3(value) {
|
|
3078
3096
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
3079
3097
|
}
|
|
3080
3098
|
function parseTrimmedString(value) {
|
|
@@ -3102,7 +3120,7 @@ function parseNewsletterSignupDestinationProvider(value) {
|
|
|
3102
3120
|
return value === "riverbank" || value === "mailchimp" ? value : null;
|
|
3103
3121
|
}
|
|
3104
3122
|
function parseNewsletterSignupAuthoringDestinationValue(value) {
|
|
3105
|
-
if (!isRecord$
|
|
3123
|
+
if (!isRecord$3(value) || typeof value.tag !== "string") {
|
|
3106
3124
|
return err({ kind: "invalid-destination-shape" });
|
|
3107
3125
|
}
|
|
3108
3126
|
switch (value.tag) {
|
|
@@ -3148,7 +3166,7 @@ function parseNewsletterSignupAuthoringDestinationValue(value) {
|
|
|
3148
3166
|
|
|
3149
3167
|
//#endregion
|
|
3150
3168
|
//#region ../core/src/footer-engagement.ts
|
|
3151
|
-
function isRecord$
|
|
3169
|
+
function isRecord$2(value) {
|
|
3152
3170
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
3153
3171
|
}
|
|
3154
3172
|
function invalid(path, message) {
|
|
@@ -3182,7 +3200,7 @@ function parseEngagementBase(value, base, path) {
|
|
|
3182
3200
|
}
|
|
3183
3201
|
const treatment = parseTreatment(value.treatment, `${path}.treatment`);
|
|
3184
3202
|
if (!treatment.ok) return treatment;
|
|
3185
|
-
if (!isRecord$
|
|
3203
|
+
if (!isRecord$2(value.source)) {
|
|
3186
3204
|
return err(invalid(`${path}.source`, "Footer engagement source is invalid."));
|
|
3187
3205
|
}
|
|
3188
3206
|
const intro = parseBoundedText(value.source.intro, `${path}.source.intro`, {
|
|
@@ -3248,7 +3266,7 @@ function parseFooterEngagementSectionValue(value, sectionBase, path) {
|
|
|
3248
3266
|
const base = parseEngagementBase(value, sectionBase, path);
|
|
3249
3267
|
if (!base.ok) return base;
|
|
3250
3268
|
const source = value.source;
|
|
3251
|
-
if (!isRecord$
|
|
3269
|
+
if (!isRecord$2(source)) {
|
|
3252
3270
|
return err(invalid(`${path}.source`, "Footer engagement source is invalid."));
|
|
3253
3271
|
}
|
|
3254
3272
|
switch (source.kind) {
|
|
@@ -3363,7 +3381,7 @@ function unsafeAsFooterModuleId(value) {
|
|
|
3363
3381
|
function parseFooterModuleId(value) {
|
|
3364
3382
|
return parseSectionId(value, "sectionId");
|
|
3365
3383
|
}
|
|
3366
|
-
function isRecord$
|
|
3384
|
+
function isRecord$1(value) {
|
|
3367
3385
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
3368
3386
|
}
|
|
3369
3387
|
function isRichText(value) {
|
|
@@ -3393,7 +3411,7 @@ function parseHeading(value, path) {
|
|
|
3393
3411
|
}
|
|
3394
3412
|
function parseFooterSection(value, index) {
|
|
3395
3413
|
const path = `sections.${index}`;
|
|
3396
|
-
if (!isRecord$
|
|
3414
|
+
if (!isRecord$1(value)) {
|
|
3397
3415
|
return err({
|
|
3398
3416
|
code: "invalid_section",
|
|
3399
3417
|
path,
|
|
@@ -3428,7 +3446,7 @@ function parseFooterSection(value, index) {
|
|
|
3428
3446
|
}
|
|
3429
3447
|
break;
|
|
3430
3448
|
case "navigation":
|
|
3431
|
-
if (isRecord$
|
|
3449
|
+
if (isRecord$1(value.source) && value.source.kind === "menu-role" && value.source.role === "main") {
|
|
3432
3450
|
return ok({
|
|
3433
3451
|
...base,
|
|
3434
3452
|
kind: "navigation",
|
|
@@ -3441,7 +3459,7 @@ function parseFooterSection(value, index) {
|
|
|
3441
3459
|
}
|
|
3442
3460
|
break;
|
|
3443
3461
|
case "legal-navigation":
|
|
3444
|
-
if (isRecord$
|
|
3462
|
+
if (isRecord$1(value.source) && value.source.kind === "menu-role" && value.source.role === "legal-policy") {
|
|
3445
3463
|
return ok({
|
|
3446
3464
|
...base,
|
|
3447
3465
|
kind: "legal-navigation",
|
|
@@ -3454,7 +3472,7 @@ function parseFooterSection(value, index) {
|
|
|
3454
3472
|
}
|
|
3455
3473
|
break;
|
|
3456
3474
|
case "business-address":
|
|
3457
|
-
if (isRecord$
|
|
3475
|
+
if (isRecord$1(value.source) && value.source.kind === "business-address") {
|
|
3458
3476
|
return ok({
|
|
3459
3477
|
...base,
|
|
3460
3478
|
kind: "business-address",
|
|
@@ -3466,7 +3484,7 @@ function parseFooterSection(value, index) {
|
|
|
3466
3484
|
case "contact":
|
|
3467
3485
|
case "opening-hours":
|
|
3468
3486
|
case "social-links":
|
|
3469
|
-
if (isRecord$
|
|
3487
|
+
if (isRecord$1(value.source) && value.source.kind === "public-profile") {
|
|
3470
3488
|
return ok({
|
|
3471
3489
|
...base,
|
|
3472
3490
|
kind: value.kind,
|
|
@@ -3518,7 +3536,7 @@ function validateFooterComposition(composition) {
|
|
|
3518
3536
|
return ok(composition);
|
|
3519
3537
|
}
|
|
3520
3538
|
function parseFooterCompositionV1(value) {
|
|
3521
|
-
if (!isRecord$
|
|
3539
|
+
if (!isRecord$1(value)) {
|
|
3522
3540
|
return err({
|
|
3523
3541
|
code: "invalid_shape",
|
|
3524
3542
|
path: "",
|
|
@@ -3551,7 +3569,7 @@ function parseFooterCompositionV1(value) {
|
|
|
3551
3569
|
});
|
|
3552
3570
|
}
|
|
3553
3571
|
function parseLegacyPlacementSection(value, index, selectedIds) {
|
|
3554
|
-
if (!isRecord$
|
|
3572
|
+
if (!isRecord$1(value)) {
|
|
3555
3573
|
return err({
|
|
3556
3574
|
code: "invalid_section",
|
|
3557
3575
|
path: `modules.${index}`,
|
|
@@ -3559,7 +3577,7 @@ function parseLegacyPlacementSection(value, index, selectedIds) {
|
|
|
3559
3577
|
});
|
|
3560
3578
|
}
|
|
3561
3579
|
const selected = typeof value.id === "string" && selectedIds.has(value.id);
|
|
3562
|
-
const source = isRecord$
|
|
3580
|
+
const source = isRecord$1(value.source) ? value.source : null;
|
|
3563
3581
|
const mapped = value.kind === "navigation" && source?.kind === "menu-role" && source.role === "footer" ? {
|
|
3564
3582
|
...value,
|
|
3565
3583
|
source: {
|
|
@@ -3578,7 +3596,7 @@ function parseLegacyPlacementSection(value, index, selectedIds) {
|
|
|
3578
3596
|
* It is intentionally not called by the runtime parser.
|
|
3579
3597
|
*/
|
|
3580
3598
|
function upgradePlacementFooterCompositionV1(value) {
|
|
3581
|
-
if (!isRecord$
|
|
3599
|
+
if (!isRecord$1(value) || value.schemaVersion !== 1 || !Array.isArray(value.modules) || !Array.isArray(value.placements)) {
|
|
3582
3600
|
return err({
|
|
3583
3601
|
code: "invalid_shape",
|
|
3584
3602
|
path: "",
|
|
@@ -3587,7 +3605,7 @@ function upgradePlacementFooterCompositionV1(value) {
|
|
|
3587
3605
|
}
|
|
3588
3606
|
const selectedIds = new Set();
|
|
3589
3607
|
for (const [index, placement] of value.placements.entries()) {
|
|
3590
|
-
if (!isRecord$
|
|
3608
|
+
if (!isRecord$1(placement) || typeof placement.moduleId !== "string") {
|
|
3591
3609
|
return err({
|
|
3592
3610
|
code: "invalid_shape",
|
|
3593
3611
|
path: `placements.${index}`,
|
|
@@ -3802,7 +3820,7 @@ const defaultSections = (legacy, options) => {
|
|
|
3802
3820
|
];
|
|
3803
3821
|
};
|
|
3804
3822
|
function normalizeLegacyFooterContentToV1(value, options = {}) {
|
|
3805
|
-
if (!isRecord$
|
|
3823
|
+
if (!isRecord$1(value)) {
|
|
3806
3824
|
return err({
|
|
3807
3825
|
code: "invalid_shape",
|
|
3808
3826
|
path: "",
|
|
@@ -3887,11 +3905,11 @@ const PRIORITY_IDS = new Set([
|
|
|
3887
3905
|
"emailaddress"
|
|
3888
3906
|
]);
|
|
3889
3907
|
const MAX_COLUMNS = 5;
|
|
3890
|
-
function isRecord
|
|
3908
|
+
function isRecord(value) {
|
|
3891
3909
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
3892
3910
|
}
|
|
3893
3911
|
function isSchemaField(value) {
|
|
3894
|
-
return isRecord
|
|
3912
|
+
return isRecord(value) && typeof value.id === "string";
|
|
3895
3913
|
}
|
|
3896
3914
|
function selectSubmissionPreviewFields(schemaFields, submissions) {
|
|
3897
3915
|
const fields = Array.isArray(schemaFields) ? schemaFields.filter(isSchemaField) : [];
|
|
@@ -3984,7 +4002,7 @@ function valueIsMeaningful(value) {
|
|
|
3984
4002
|
if (value === null || value === undefined) return false;
|
|
3985
4003
|
if (typeof value === "string") return value.trim().length > 0;
|
|
3986
4004
|
if (Array.isArray(value)) return value.length > 0;
|
|
3987
|
-
if (isRecord
|
|
4005
|
+
if (isRecord(value)) return Object.keys(value).length > 0;
|
|
3988
4006
|
return true;
|
|
3989
4007
|
}
|
|
3990
4008
|
function orderSubmissionFields(schemaFields, data) {
|
|
@@ -4473,6 +4491,82 @@ function sortStableJsonValue(value) {
|
|
|
4473
4491
|
throw new TypeError(`Stable JSON does not support ${typeof value}`);
|
|
4474
4492
|
}
|
|
4475
4493
|
|
|
4494
|
+
//#endregion
|
|
4495
|
+
//#region ../core/src/secret.ts
|
|
4496
|
+
/**
|
|
4497
|
+
* Credential values that must remain inert when they cross diagnostics,
|
|
4498
|
+
* logging, or serialization boundaries.
|
|
4499
|
+
*
|
|
4500
|
+
* The value is deliberately held behind a non-enumerable capability rather
|
|
4501
|
+
* than exposed as a property. The capability uses a global symbol so a Secret
|
|
4502
|
+
* created by another bundled copy of core remains consumable at an approved
|
|
4503
|
+
* `exposeSecret` boundary.
|
|
4504
|
+
*/
|
|
4505
|
+
const SECRET_KINDS = [
|
|
4506
|
+
"management-key",
|
|
4507
|
+
"content-key",
|
|
4508
|
+
"preview-key",
|
|
4509
|
+
"webhook-secret",
|
|
4510
|
+
"oauth-access-token",
|
|
4511
|
+
"oauth-refresh-token",
|
|
4512
|
+
"preview-session-refresh-token",
|
|
4513
|
+
"oauth-authorization-code",
|
|
4514
|
+
"oauth-pkce-verifier",
|
|
4515
|
+
"registrar-epp-code"
|
|
4516
|
+
];
|
|
4517
|
+
const secretValueSymbol = Symbol.for("riverbank.secret.value");
|
|
4518
|
+
const SECRET_INSPECT_SYMBOL = Symbol.for("nodejs.util.inspect.custom");
|
|
4519
|
+
const MANAGEMENT_KEY_PREFIX = "bld_mgmt_sk_";
|
|
4520
|
+
function placeholderFor(kind) {
|
|
4521
|
+
return `[riverbank:redacted:${kind}]`;
|
|
4522
|
+
}
|
|
4523
|
+
/** Create an opaque secret after a caller has parsed/validated its boundary. */
|
|
4524
|
+
function createSecret(kind, value) {
|
|
4525
|
+
return createSecretInternal(kind, value);
|
|
4526
|
+
}
|
|
4527
|
+
/** Construct a Secret for an already validated kind/value pair. */
|
|
4528
|
+
function createSecretInternal(kind, value) {
|
|
4529
|
+
const placeholder = placeholderFor(kind);
|
|
4530
|
+
const secret = {
|
|
4531
|
+
kind: "secret",
|
|
4532
|
+
secretType: kind,
|
|
4533
|
+
toString: () => placeholder,
|
|
4534
|
+
toJSON: () => placeholder,
|
|
4535
|
+
[SECRET_INSPECT_SYMBOL]: () => placeholder
|
|
4536
|
+
};
|
|
4537
|
+
Object.defineProperty(secret, secretValueSymbol, {
|
|
4538
|
+
configurable: false,
|
|
4539
|
+
enumerable: false,
|
|
4540
|
+
value: () => value,
|
|
4541
|
+
writable: false
|
|
4542
|
+
});
|
|
4543
|
+
return Object.freeze(secret);
|
|
4544
|
+
}
|
|
4545
|
+
/**
|
|
4546
|
+
* Parse an untrusted management API key before constructing its Secret value.
|
|
4547
|
+
*
|
|
4548
|
+
* The Secret type protects an already validated value from accidental
|
|
4549
|
+
* disclosure; this boundary is what validates the value's wire format.
|
|
4550
|
+
*/
|
|
4551
|
+
function parseManagementKey(raw) {
|
|
4552
|
+
if (typeof raw !== "string" || !raw.startsWith(MANAGEMENT_KEY_PREFIX) || raw.length === MANAGEMENT_KEY_PREFIX.length) {
|
|
4553
|
+
return err({ kind: "invalid-management-key" });
|
|
4554
|
+
}
|
|
4555
|
+
return ok(createSecretInternal("management-key", raw));
|
|
4556
|
+
}
|
|
4557
|
+
/**
|
|
4558
|
+
* The only generic plaintext exposure operation. Keep calls at named I/O
|
|
4559
|
+
* boundaries; do not pass exposed strings through domain/application layers.
|
|
4560
|
+
*/
|
|
4561
|
+
function exposeSecret(secret) {
|
|
4562
|
+
const getValue = secret[secretValueSymbol];
|
|
4563
|
+
if (typeof getValue !== "function") return failInvalidSecret();
|
|
4564
|
+
return getValue();
|
|
4565
|
+
}
|
|
4566
|
+
function failInvalidSecret() {
|
|
4567
|
+
throw new TypeError("Invalid Riverbank secret");
|
|
4568
|
+
}
|
|
4569
|
+
|
|
4476
4570
|
//#endregion
|
|
4477
4571
|
//#region ../core/src/site-route-target.ts
|
|
4478
4572
|
const SITE_ROUTE_NODE_TYPES = [
|
|
@@ -4600,885 +4694,4 @@ function disabledSelection() {
|
|
|
4600
4694
|
}
|
|
4601
4695
|
|
|
4602
4696
|
//#endregion
|
|
4603
|
-
|
|
4604
|
-
const supportedKinds = new Set([
|
|
4605
|
-
"upfront",
|
|
4606
|
-
"deferred_manual",
|
|
4607
|
-
"flexible_balance"
|
|
4608
|
-
]);
|
|
4609
|
-
function issue$2(path, message) {
|
|
4610
|
-
return {
|
|
4611
|
-
path,
|
|
4612
|
-
message
|
|
4613
|
-
};
|
|
4614
|
-
}
|
|
4615
|
-
function isRecord$1(value) {
|
|
4616
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
4617
|
-
}
|
|
4618
|
-
function isWholeNumber$1(value) {
|
|
4619
|
-
return typeof value === "number" && Number.isInteger(value);
|
|
4620
|
-
}
|
|
4621
|
-
function isPositiveWholeNumber(value) {
|
|
4622
|
-
return isWholeNumber$1(value) && value > 0;
|
|
4623
|
-
}
|
|
4624
|
-
function isDepositRefundability(value) {
|
|
4625
|
-
return value === "refundable" || value === "non_refundable";
|
|
4626
|
-
}
|
|
4627
|
-
function isSupportedOptionKind(value) {
|
|
4628
|
-
return typeof value === "string" && supportedKinds.has(value);
|
|
4629
|
-
}
|
|
4630
|
-
function validateFlexibleBalanceDueDateRule(raw, path) {
|
|
4631
|
-
if (!isRecord$1(raw)) {
|
|
4632
|
-
return [issue$2(path, "Flexible balance dueDateRule must be an object")];
|
|
4633
|
-
}
|
|
4634
|
-
if (raw.kind === "relative_to_start_date") {
|
|
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")];
|
|
4653
|
-
}
|
|
4654
|
-
function validateFlexibleBalanceConfig(raw, path) {
|
|
4655
|
-
if (!isRecord$1(raw)) {
|
|
4656
|
-
return [issue$2(path, "Flexible balance config must be an object")];
|
|
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;
|
|
4679
|
-
}
|
|
4680
|
-
return {
|
|
4681
|
-
upfrontAmountCents: validatedUpfrontAmountCents,
|
|
4682
|
-
depositRefundability: validatedDepositRefundability,
|
|
4683
|
-
dueDateRule
|
|
4684
|
-
};
|
|
4685
|
-
}
|
|
4686
|
-
function validateCollectionOption(raw, index) {
|
|
4687
|
-
const path = ["allowedOptions", String(index)];
|
|
4688
|
-
if (!isRecord$1(raw)) {
|
|
4689
|
-
return [issue$2(path, "Payment option must be an object")];
|
|
4690
|
-
}
|
|
4691
|
-
if (raw.kind === "instalment_plan") {
|
|
4692
|
-
return [issue$2([...path, "kind"], "Event instalment plans are not supported yet. Use upfront, deferred_manual, or flexible_balance.")];
|
|
4693
|
-
}
|
|
4694
|
-
if (!isSupportedOptionKind(raw.kind)) {
|
|
4695
|
-
return [issue$2([...path, "kind"], "Payment option kind must be upfront, deferred_manual, or flexible_balance")];
|
|
4696
|
-
}
|
|
4697
|
-
switch (raw.kind) {
|
|
4698
|
-
case "upfront":
|
|
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
|
-
}
|
|
4710
|
-
}
|
|
4711
|
-
}
|
|
4712
|
-
function optionCombinationIssues(options) {
|
|
4713
|
-
const issues = [];
|
|
4714
|
-
const seenKinds = new Set();
|
|
4715
|
-
options.forEach((option, index) => {
|
|
4716
|
-
if (seenKinds.has(option.kind)) {
|
|
4717
|
-
issues.push(issue$2([
|
|
4718
|
-
"allowedOptions",
|
|
4719
|
-
String(index),
|
|
4720
|
-
"kind"
|
|
4721
|
-
], `Duplicate payment option kind "${option.kind}"`));
|
|
4722
|
-
}
|
|
4723
|
-
seenKinds.add(option.kind);
|
|
4724
|
-
});
|
|
4725
|
-
if (options.length > 2) {
|
|
4726
|
-
issues.push(issue$2(["allowedOptions"], "Event payment terms can offer one option, or a choice between upfront and one alternative option"));
|
|
4727
|
-
}
|
|
4728
|
-
if (options.length === 2 && (!seenKinds.has("upfront") || !seenKinds.has("deferred_manual") && !seenKinds.has("flexible_balance"))) {
|
|
4729
|
-
issues.push(issue$2(["allowedOptions"], "Event payment terms with two options must combine upfront with deferred_manual or flexible_balance"));
|
|
4730
|
-
}
|
|
4731
|
-
return issues;
|
|
4732
|
-
}
|
|
4733
|
-
function classifySdkEventPaymentTerms(rawInput) {
|
|
4734
|
-
if (rawInput === undefined || rawInput === null) {
|
|
4735
|
-
return {
|
|
4736
|
-
ok: true,
|
|
4737
|
-
paymentTerms: undefined
|
|
4738
|
-
};
|
|
4739
|
-
}
|
|
4740
|
-
if (!isRecord$1(rawInput)) {
|
|
4741
|
-
return {
|
|
4742
|
-
ok: false,
|
|
4743
|
-
issues: [issue$2([], "Event paymentTerms must be an object")]
|
|
4744
|
-
};
|
|
4745
|
-
}
|
|
4746
|
-
if (!Array.isArray(rawInput.allowedOptions)) {
|
|
4747
|
-
return {
|
|
4748
|
-
ok: false,
|
|
4749
|
-
issues: [issue$2(["allowedOptions"], "Event paymentTerms.allowedOptions is required")]
|
|
4750
|
-
};
|
|
4751
|
-
}
|
|
4752
|
-
if (rawInput.allowedOptions.length === 0) {
|
|
4753
|
-
return {
|
|
4754
|
-
ok: false,
|
|
4755
|
-
issues: [issue$2(["allowedOptions"], "Event paymentTerms.allowedOptions must include at least one option")]
|
|
4756
|
-
};
|
|
4757
|
-
}
|
|
4758
|
-
const options = [];
|
|
4759
|
-
const issues = [];
|
|
4760
|
-
rawInput.allowedOptions.forEach((option, index) => {
|
|
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) {
|
|
4770
|
-
return {
|
|
4771
|
-
ok: false,
|
|
4772
|
-
issues
|
|
4773
|
-
};
|
|
4774
|
-
}
|
|
4775
|
-
return {
|
|
4776
|
-
ok: true,
|
|
4777
|
-
paymentTerms: { allowedOptions: options }
|
|
4778
|
-
};
|
|
4779
|
-
}
|
|
4780
|
-
|
|
4781
|
-
//#endregion
|
|
4782
|
-
//#region ../api/src/sdk-event-pricing.ts
|
|
4783
|
-
const identifierRegex = /^[a-z0-9][a-z0-9-]*$/;
|
|
4784
|
-
const currencyRegex = /^[A-Z]{3}$/;
|
|
4785
|
-
const isoDateTimeRegex = /^\d{4}-\d{2}-\d{2}T.+/;
|
|
4786
|
-
function issue$1(path, message) {
|
|
4787
|
-
return {
|
|
4788
|
-
path,
|
|
4789
|
-
message
|
|
4790
|
-
};
|
|
4791
|
-
}
|
|
4792
|
-
function isRecord(value) {
|
|
4793
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
4794
|
-
}
|
|
4795
|
-
function isCurrency(value) {
|
|
4796
|
-
return typeof value === "string" && currencyRegex.test(value);
|
|
4797
|
-
}
|
|
4798
|
-
function isWholeNumber(value) {
|
|
4799
|
-
return typeof value === "number" && Number.isInteger(value);
|
|
4800
|
-
}
|
|
4801
|
-
function isWholeMinuteIsoDateTime(value) {
|
|
4802
|
-
if (!isoDateTimeRegex.test(value)) return false;
|
|
4803
|
-
const epochMs = Date.parse(value);
|
|
4804
|
-
return Number.isFinite(epochMs) && epochMs % 6e4 === 0;
|
|
4805
|
-
}
|
|
4806
|
-
function validateTicketType(raw, index, currency) {
|
|
4807
|
-
const path = ["ticketTypes", String(index)];
|
|
4808
|
-
if (!isRecord(raw)) return [issue$1(path, "Ticket type must be an object")];
|
|
4809
|
-
const issues = [];
|
|
4810
|
-
const identifier = raw.identifier;
|
|
4811
|
-
if (typeof identifier !== "string" || !identifierRegex.test(identifier)) {
|
|
4812
|
-
issues.push(issue$1([...path, "identifier"], "Ticket identifier must use lowercase letters, numbers, and hyphens"));
|
|
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"));
|
|
4845
|
-
}
|
|
4846
|
-
if (typeof saleStartsAt === "string" && typeof saleEndsAt === "string" && Date.parse(saleEndsAt) <= Date.parse(saleStartsAt)) {
|
|
4847
|
-
issues.push(issue$1([...path, "saleEndsAt"], "Ticket saleEndsAt must be after saleStartsAt"));
|
|
4848
|
-
}
|
|
4849
|
-
const cmsTicketTypeId = raw.cmsTicketTypeId;
|
|
4850
|
-
if (cmsTicketTypeId !== undefined && cmsTicketTypeId !== null && (typeof cmsTicketTypeId !== "string" || cmsTicketTypeId.length === 0)) {
|
|
4851
|
-
issues.push(issue$1([...path, "cmsTicketTypeId"], "Ticket cmsTicketTypeId must be a non-empty string"));
|
|
4852
|
-
}
|
|
4853
|
-
if (issues.length > 0) return issues;
|
|
4854
|
-
return {
|
|
4855
|
-
identifier,
|
|
4856
|
-
...cmsTicketTypeId ? { cmsTicketTypeId } : {},
|
|
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
|
|
4866
|
-
};
|
|
4867
|
-
}
|
|
4868
|
-
function validateTicketTypes(rawTicketTypes, currency) {
|
|
4869
|
-
if (!Array.isArray(rawTicketTypes) || rawTicketTypes.length === 0) {
|
|
4870
|
-
return [issue$1(["ticketTypes"], "Ticket-type pricing requires at least one ticket type")];
|
|
4871
|
-
}
|
|
4872
|
-
const issues = [];
|
|
4873
|
-
const ticketTypes = [];
|
|
4874
|
-
const seenIdentifiers = new Set();
|
|
4875
|
-
rawTicketTypes.forEach((rawTicketType, index) => {
|
|
4876
|
-
const rawIdentifier = isRecord(rawTicketType) ? rawTicketType.identifier : undefined;
|
|
4877
|
-
const hasValidIdentifier = typeof rawIdentifier === "string" && identifierRegex.test(rawIdentifier);
|
|
4878
|
-
const isDuplicateIdentifier = hasValidIdentifier && seenIdentifiers.has(rawIdentifier);
|
|
4879
|
-
if (isDuplicateIdentifier) {
|
|
4880
|
-
issues.push(issue$1([
|
|
4881
|
-
"ticketTypes",
|
|
4882
|
-
String(index),
|
|
4883
|
-
"identifier"
|
|
4884
|
-
], `Duplicate ticket identifier "${rawIdentifier}"`));
|
|
4885
|
-
} else if (hasValidIdentifier) {
|
|
4886
|
-
seenIdentifiers.add(rawIdentifier);
|
|
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);
|
|
4913
|
-
});
|
|
4914
|
-
}
|
|
4915
|
-
function suggestSdkEventTicketIdentifier(name, usedIdentifiers) {
|
|
4916
|
-
const base = name.trim().toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "") || "ticket";
|
|
4917
|
-
let candidate = base;
|
|
4918
|
-
let suffix = 2;
|
|
4919
|
-
while (usedIdentifiers.has(candidate)) {
|
|
4920
|
-
candidate = `${base}-${suffix}`;
|
|
4921
|
-
suffix += 1;
|
|
4922
|
-
}
|
|
4923
|
-
return candidate;
|
|
4924
|
-
}
|
|
4925
|
-
function classifySdkEventPricing(rawInput) {
|
|
4926
|
-
if (rawInput === undefined || rawInput === null) {
|
|
4927
|
-
return {
|
|
4928
|
-
ok: true,
|
|
4929
|
-
pricing: { kind: "free" }
|
|
4930
|
-
};
|
|
4931
|
-
}
|
|
4932
|
-
if (!isRecord(rawInput)) {
|
|
4933
|
-
return {
|
|
4934
|
-
ok: false,
|
|
4935
|
-
issues: [issue$1([], "Pricing must be an object")]
|
|
4936
|
-
};
|
|
4937
|
-
}
|
|
4938
|
-
const record = rawInput;
|
|
4939
|
-
const kind = typeof record.kind === "string" ? record.kind : record.pricingMode === "ticket_types" ? "ticket_types" : record.pricingMode === "fixed_price" ? "fixed_price" : record.pricingMode === "free" ? "free" : record.requiresPayment || typeof record.priceAmount === "number" && record.priceAmount > 0 ? "fixed_price" : "free";
|
|
4940
|
-
const acceptsPasses = typeof record.acceptsPasses === "boolean" ? record.acceptsPasses : undefined;
|
|
4941
|
-
const acceptsMemberships = typeof record.acceptsMemberships === "boolean" ? record.acceptsMemberships : undefined;
|
|
4942
|
-
const priceDisplay = typeof record.priceDisplay === "string" ? record.priceDisplay : null;
|
|
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
|
-
}
|
|
4952
|
-
};
|
|
4953
|
-
case "fixed_price": {
|
|
4954
|
-
const issues = [];
|
|
4955
|
-
const priceAmount = record.priceAmount;
|
|
4956
|
-
if (!isWholeNumber(priceAmount) || priceAmount < 0) {
|
|
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
|
-
}
|
|
5001
|
-
return {
|
|
5002
|
-
ok: true,
|
|
5003
|
-
pricing: {
|
|
5004
|
-
kind: "ticket_types",
|
|
5005
|
-
priceCurrency,
|
|
5006
|
-
priceDisplay,
|
|
5007
|
-
...acceptsPasses !== undefined ? { acceptsPasses } : {},
|
|
5008
|
-
...acceptsMemberships !== undefined ? { acceptsMemberships } : {},
|
|
5009
|
-
ticketTypes
|
|
5010
|
-
}
|
|
5011
|
-
};
|
|
5012
|
-
}
|
|
5013
|
-
default: return {
|
|
5014
|
-
ok: false,
|
|
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;
|
|
5055
|
-
return {
|
|
5056
|
-
epochMs: epochMs$1,
|
|
5057
|
-
date: datePart(yearRaw$1, monthRaw$1, dayRaw$1),
|
|
5058
|
-
time: timePart(hours$1, minutes$1, seconds$1),
|
|
5059
|
-
iso: new Date(epochMs$1).toISOString()
|
|
5060
|
-
};
|
|
5061
|
-
}
|
|
5062
|
-
const localMatch = localDateTimeRegex.exec(value);
|
|
5063
|
-
if (!localMatch) return null;
|
|
5064
|
-
const [, yearRaw, monthRaw, dayRaw, hours, minutes, seconds = "00", fraction] = localMatch;
|
|
5065
|
-
if (!yearRaw || !monthRaw || !dayRaw || !hours || !minutes) return null;
|
|
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)));
|
|
5109
|
-
}
|
|
5110
|
-
}
|
|
5111
|
-
if (issues.length > 0) return {
|
|
5112
|
-
ok: false,
|
|
5113
|
-
issues
|
|
5114
|
-
};
|
|
5115
|
-
return {
|
|
5116
|
-
ok: true,
|
|
5117
|
-
kind: "canonical",
|
|
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
|
-
}
|
|
5125
|
-
};
|
|
5126
|
-
}
|
|
5127
|
-
function classifySdkEventSchedule(input, options = {}) {
|
|
5128
|
-
const issues = [];
|
|
5129
|
-
const hasStartsAt = input.startsAt !== undefined && input.startsAt !== null;
|
|
5130
|
-
const hasEndsAt = input.endsAt !== undefined && input.endsAt !== null;
|
|
5131
|
-
const hasDateTimeDuration = hasStartsAt && input.durationMinutes !== undefined && input.durationMinutes !== null;
|
|
5132
|
-
const hasCanonicalStart = input.startDate !== undefined || input.defaultStartTime !== undefined;
|
|
5133
|
-
if (!hasStartsAt && !hasEndsAt) {
|
|
5134
|
-
return classifyCanonicalSchedule(input, options);
|
|
5135
|
-
}
|
|
5136
|
-
if (hasStartsAt && hasCanonicalStart) {
|
|
5137
|
-
issues.push(issue(["startsAt"], "Use either startsAt or startDate/defaultStartTime, not both"));
|
|
5138
|
-
}
|
|
5139
|
-
if (input.recurrenceRule) {
|
|
5140
|
-
issues.push(issue(["recurrenceRule"], "startsAt schedules are only supported for one-off events"));
|
|
5141
|
-
}
|
|
5142
|
-
if (!hasStartsAt) {
|
|
5143
|
-
issues.push(issue(["startsAt"], "startsAt is required when endsAt is provided"));
|
|
5144
|
-
}
|
|
5145
|
-
if (!hasEndsAt && !hasDateTimeDuration) {
|
|
5146
|
-
issues.push(issue(["endsAt"], "Provide either endsAt or durationMinutes with startsAt"));
|
|
5147
|
-
}
|
|
5148
|
-
if (hasEndsAt && hasDateTimeDuration) {
|
|
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) {
|
|
5167
|
-
return {
|
|
5168
|
-
ok: true,
|
|
5169
|
-
kind: "start-end",
|
|
5170
|
-
canonical: {
|
|
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
|
|
5179
|
-
};
|
|
5180
|
-
}
|
|
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;
|
|
5190
|
-
return {
|
|
5191
|
-
ok: true,
|
|
5192
|
-
kind: "start-duration",
|
|
5193
|
-
canonical: {
|
|
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()
|
|
5202
|
-
};
|
|
5203
|
-
}
|
|
5204
|
-
}
|
|
5205
|
-
return {
|
|
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)) {
|
|
5272
|
-
return {
|
|
5273
|
-
ok: false,
|
|
5274
|
-
issues: [timingIssue([...path, "endsAt"], "endsAt must be after startsAt")]
|
|
5275
|
-
};
|
|
5276
|
-
}
|
|
5277
|
-
return {
|
|
5278
|
-
ok: true,
|
|
5279
|
-
value: {
|
|
5280
|
-
startsAt: startsAt.value,
|
|
5281
|
-
endsAt: endsAt.value,
|
|
5282
|
-
timeZone: timeZone.value
|
|
5283
|
-
}
|
|
5284
|
-
};
|
|
5285
|
-
}
|
|
5286
|
-
function classifySdkEventTiming(input, options = {}) {
|
|
5287
|
-
if (input.occurrences && input.occurrences.length > 0) {
|
|
5288
|
-
if (input.bookingScope === undefined) {
|
|
5289
|
-
return {
|
|
5290
|
-
ok: false,
|
|
5291
|
-
issues: [timingIssue(["bookingScope"], "bookingScope is required when occurrences are provided")]
|
|
5292
|
-
};
|
|
5293
|
-
}
|
|
5294
|
-
const occurrences = [];
|
|
5295
|
-
const starts = new Set();
|
|
5296
|
-
let timeZone;
|
|
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)) {
|
|
5340
|
-
return {
|
|
5341
|
-
ok: false,
|
|
5342
|
-
issues: [timingIssue(["schedule"], "Either schedule or a non-empty occurrences list is required")]
|
|
5343
|
-
};
|
|
5344
|
-
}
|
|
5345
|
-
const schedule = classifySdkEventSchedule(input.schedule, options);
|
|
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)) {
|
|
5362
|
-
return {
|
|
5363
|
-
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
|
-
}]
|
|
5370
|
-
};
|
|
5371
|
-
}
|
|
5372
|
-
const runForRef = (ref) => {
|
|
5373
|
-
const run = runsByIdentifier.get(ref);
|
|
5374
|
-
if (!run) throw new Error("Unreachable missing Event run after graph validation");
|
|
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)) {
|
|
5380
|
-
return {
|
|
5381
|
-
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
|
-
}]
|
|
5388
|
-
};
|
|
5389
|
-
}
|
|
5390
|
-
const firstResult = parseSdkEventRunUpsertInput(firstRun, 0, options);
|
|
5391
|
-
const remainingResults = remainingRuns.map((run, index) => parseSdkEventRunUpsertInput(run, index + 1, options));
|
|
5392
|
-
const issues = [firstResult, ...remainingResults].flatMap((result) => result.ok ? [] : result.issues);
|
|
5393
|
-
if (issues.length > 0) return {
|
|
5394
|
-
ok: false,
|
|
5395
|
-
issues
|
|
5396
|
-
};
|
|
5397
|
-
if (!firstResult.ok) return {
|
|
5398
|
-
ok: false,
|
|
5399
|
-
issues: firstResult.issues
|
|
5400
|
-
};
|
|
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
|
-
}
|
|
5426
|
-
function toSdkEventUpsertBody(input) {
|
|
5427
|
-
const [first, ...rest] = input.runs;
|
|
5428
|
-
return {
|
|
5429
|
-
...input,
|
|
5430
|
-
runs: [toSdkEventRunUpsertBody(first), ...rest.map(toSdkEventRunUpsertBody)]
|
|
5431
|
-
};
|
|
5432
|
-
}
|
|
5433
|
-
function parseSdkEventRunUpsertInput(input, runIndex, options) {
|
|
5434
|
-
const timingResult = classifySdkEventTiming(input, options.schedule);
|
|
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
|
-
};
|
|
5481
|
-
}
|
|
5482
|
-
|
|
5483
|
-
//#endregion
|
|
5484
|
-
export { unsafeAsDetectedMimeType as $, getBookingLocalDateKey as A, publicEventPricingCurrency as B, normalizeLocationAddressText as C, formatBookingTime as D, formatBookingDateParts as E, toScheduledBookingDateRange as F, parseMediaAssetId as G, formatCurrencyAmount as H, toScheduledBookingDateTime as I, unsafeAsAppointmentResourceId as J, parsePageId as K, parseEventPageSurfaceSubject as L, getLocalScheduleTime as M, parseUtcIsoDateTimeString as N, formatLocalScheduleDate as O, toBookingTimeZone as P, unsafeAsDemoCapability as Q, formatBookingPriceDisplay as R, isPresent as S, formatBookingDate as T, assertNever as U, publicEventPricingType as V, parseEventSeriesId as W, unsafeAsBookingSessionId as X, unsafeAsBookingRequirementId as Y, unsafeAsDeclaredMimeType as Z, isCanonicalRichTextDoc as _, unsafeAsUploadSessionId as _t, classifySdkEventPricing as a, unsafeAsOccurrenceId as at, mapNonEmptyArray as b, ok as bt, stableJsonStringify as c, unsafeAsPositiveSeconds as ct, parseFooterModuleId as d, unsafeAsSiteFontId as dt, unsafeAsEntitlementId as et, parseMailchimpAudienceId as f, unsafeAsSiteId as ft, parseNewsletterSignupDestinationProvider as g, unsafeAsStoragePath as gt, parseNewsletterSignupAuthoringDestinationValue as h, unsafeAsStorageBucketName as ht, classifySdkEventSchedule as i, unsafeAsMediaAssetId as it, getLocalScheduleDate as j, formatLocalScheduleTime as k, footerNavigationLayouts as l, unsafeAsProductId as lt, parseNewsletterListId as m, unsafeAsStaffMemberId as mt, sdkEventOccurrenceInputSchema as n, unsafeAsEventSeriesId as nt, classifySdkEventPaymentTerms as o, unsafeAsOriginalFilename as ot, parseMailchimpTagName as p, unsafeAsSiteScopedStoragePath as pt, parseSiteId as q, sdkEventUpsertOptionalFieldSchemas as r, unsafeAsFileChecksumSha256 as rt, resolvePlanningDateInput as s, unsafeAsPositiveByteCount as st, classifySdkEventTiming as t, unsafeAsEventOfferingId as tt, parseFooterCompositionV1 as u, unsafeAsSafeDownloadFilename as ut, assertAllowedPublicUrl as v, unsafeAsUserId as vt, toLocationCoordinates as w, hasText as x, fromArray as y, err as yt, formatEventPricing as z };
|
|
4697
|
+
export { unsafeAsDetectedMimeType as $, parseUtcIsoDateTimeString as A, formatCurrencyAmount as B, formatBookingDateParts as C, getBookingLocalDateKey as D, formatLocalScheduleTime as E, parseEventPageSurfaceSubject as F, parsePageId as G, parseApiKeyId as H, formatBookingPriceDisplay as I, unsafeAsAppointmentResourceId as J, parseSiteId as K, formatEventPricing as L, toScheduledBookingDateRange as M, toScheduledBookingDateTime as N, getLocalScheduleDate as O, parseBookingTimeZone as P, unsafeAsDemoCapability as Q, publicEventPricingCurrency as R, formatBookingDate as S, formatLocalScheduleDate as T, parseEventSeriesId as U, assertNever as V, parseMediaAssetId as W, unsafeAsBookingSessionId as X, unsafeAsBookingRequirementId as Y, unsafeAsDeclaredMimeType as Z, mapNonEmptyArray as _, unsafeAsUploadSessionId as _t, stableJsonStringify as a, unsafeAsOccurrenceId as at, normalizeLocationAddressText as b, ok as bt, parseFooterModuleId as c, unsafeAsPositiveSeconds as ct, parseNewsletterListId as d, unsafeAsSiteFontId as dt, unsafeAsEntitlementId as et, parseNewsletterSignupAuthoringDestinationValue as f, unsafeAsSiteId as ft, fromArray as g, unsafeAsStoragePath as gt, assertAllowedPublicUrl as h, unsafeAsStorageBucketName as ht, parseManagementKey as i, unsafeAsMediaAssetId as it, toBookingTimeZone as j, getLocalScheduleTime as k, parseMailchimpAudienceId as l, unsafeAsProductId as lt, isCanonicalRichTextDoc as m, unsafeAsStaffMemberId as mt, createSecret as n, unsafeAsEventSeriesId as nt, footerNavigationLayouts as o, unsafeAsOriginalFilename as ot, parseNewsletterSignupDestinationProvider as p, unsafeAsSiteScopedStoragePath as pt, parseUserId as q, exposeSecret as r, unsafeAsFileChecksumSha256 as rt, parseFooterCompositionV1 as s, unsafeAsPositiveByteCount as st, resolvePlanningDateInput as t, unsafeAsEventOfferingId as tt, parseMailchimpTagName as u, unsafeAsSafeDownloadFilename as ut, hasText as v, unsafeAsUserId as vt, formatBookingTime as w, toLocationCoordinates as x, isPresent as y, err as yt, publicEventPricingType as z };
|