riverbankcms 0.21.0 → 0.22.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/acceptance/matrix.mjs +3 -3
- package/dist/cli/index.mjs +1691 -866
- package/dist/cli/init-docs/content/getting-started.md +17 -1
- package/dist/{entryCommands-DSQZqznu.mjs → entryCommands-CoGu0G2R.mjs} +1624 -1529
- package/dist/{eventUpsertInputSchema-zCTx0fdF.mjs → eventUpsertInputSchema-B0ubhUu5.mjs} +8 -2
- package/dist/load-config-Kg45qY-N.mjs +4 -0
- package/dist/{load-config-B68gtN45.mjs → load-config-j58KSqxl.mjs} +3 -3
- package/dist/{plan-CkMANumv.mjs → plan-C4IphkPc.mjs} +8 -3
- package/dist/scaffold/index.mjs +2 -2
- package/dist/{sdk-event-upsert-CrOuJrxo.mjs → sdk-event-upsert-BqjzK2zT.mjs} +461 -35
- package/dist/{src-BENi4Q5k.mjs → src-zzwJUef5.mjs} +154 -17
- package/dist/testing.mjs +4 -4
- package/package.json +2 -2
- package/dist/load-config-ghkXBKim.mjs +0 -4
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { C as stableStringify,
|
|
3
|
-
import {
|
|
4
|
-
import { n as CliSdkPeerError, t as loadConfigFile } from "./load-config-
|
|
2
|
+
import { $t as footerPreferencesSchema, C as stableStringify, Ct as themeSchema, D as evaluateSiteCommandBatchExposure, Fn as buildMediaIdentifierFromFilename, Gn as normalizeAmbiguousStoragePathForSite, Hn as getExtensionForMimeType, J as createContentEntryRef, Jn as MAX_MANAGEMENT_MEDIA_UPLOAD_BYTES, N as entryIdentifierForCommand, O as evaluateSiteCommandExposure, Qt as pageDesignIntentSchemaVersion, Rn as resolveMediaIdentifier, T as parseAndPlanCommandBatchForTypes, Un as getMimeType, Ut as buttonStyleSelectionSchema, Zt as pageDesignBoundaryTreatmentSchema, it as slugPart, k as getSiteCommandMetadata, lr as asPageFlowPresetId, m as isInternalResolvedLinkValue, n as sdkCourseBookingSettingsSchema, or as asAppearancePresetId, q as createCommandRef, qn as FILE_SIZE_LIMITS, r as sdkEventOccurrenceInputSchema, s as RESOLVED_INTERNAL_LINK_ENTITY_TYPES, tt as createSiteCommandSiteRef, ur as asSiteStyleId, w as createDryRunReport, wn as getDirectImageUrlForSiteScopedPath, y as eventVisibilitySchema, z as eventBookingOfferIntentSchema, zn as slugifyMediaIdentifier } from "./sdk-event-upsert-BqjzK2zT.mjs";
|
|
3
|
+
import { Bn as ok, Cn as unsafeAsOccurrenceId, M as parseFooterCompositionV1, Pn as unsafeAsStaffMemberId, S as parseManagementKey, Xt as assertNever, a as canonicalizeConfiguredBookingVerticalConfig, an as parsePageId, b as createSecret, bn as unsafeAsEventSeriesId, c as resolveSiteVerticalBookingConfig, i as areConfiguredBookingVerticalConfigsEqual, l as DEFAULT_EVENT_TYPE_PRESETS, m as publicReadFreshnessSeconds, o as parseVerticalId, s as parseVerticalPresetId, sn as parseSiteId, t as resolveEventCatalogue, u as EVENT_PRESET_VALUES, un as unsafeAsAppointmentResourceId, x as exposeSecret, yn as unsafeAsEventOfferingId, zn as err } from "./src-zzwJUef5.mjs";
|
|
4
|
+
import { n as CliSdkPeerError, t as loadConfigFile } from "./load-config-j58KSqxl.mjs";
|
|
5
5
|
import { Command } from "commander";
|
|
6
6
|
import { AsyncLocalStorage } from "node:async_hooks";
|
|
7
7
|
import * as fs$1 from "fs/promises";
|
|
@@ -4434,6 +4434,31 @@ function parsePublicProductCategorySelector(value) {
|
|
|
4434
4434
|
return undefined;
|
|
4435
4435
|
}
|
|
4436
4436
|
|
|
4437
|
+
//#endregion
|
|
4438
|
+
//#region ../api/src/apiEndpointTypes.ts
|
|
4439
|
+
function allowedDemoMutationPolicy() {
|
|
4440
|
+
return {
|
|
4441
|
+
classification: "allowed",
|
|
4442
|
+
publicMessage: ""
|
|
4443
|
+
};
|
|
4444
|
+
}
|
|
4445
|
+
function blockedDemoMutationPolicy(classification, publicMessage) {
|
|
4446
|
+
if (publicMessage.trim() === "") {
|
|
4447
|
+
throw new Error(`Demo mutation policy ${classification} requires publicMessage.`);
|
|
4448
|
+
}
|
|
4449
|
+
return {
|
|
4450
|
+
classification,
|
|
4451
|
+
publicMessage
|
|
4452
|
+
};
|
|
4453
|
+
}
|
|
4454
|
+
|
|
4455
|
+
//#endregion
|
|
4456
|
+
//#region ../api/src/demoMutationPolicies.ts
|
|
4457
|
+
const DEMO_MUTATION_BLOCKED_HIDDEN = blockedDemoMutationPolicy("blocked-hidden", "This action is not available in demo sites.");
|
|
4458
|
+
const DEMO_MUTATION_BLOCKED_DISABLED = blockedDemoMutationPolicy("blocked-disabled", "This setting is disabled in demo sites.");
|
|
4459
|
+
const DEMO_MUTATION_INTERNAL_ONLY = blockedDemoMutationPolicy("internal-only", "This internal operation is not available in demo sites.");
|
|
4460
|
+
const DEMO_MUTATION_ALLOWED = allowedDemoMutationPolicy();
|
|
4461
|
+
|
|
4437
4462
|
//#endregion
|
|
4438
4463
|
//#region ../api/src/defineEndpointDefinitions.ts
|
|
4439
4464
|
/**
|
|
@@ -4444,6 +4469,50 @@ function defineEndpointDefinitions(definitions) {
|
|
|
4444
4469
|
return definitions;
|
|
4445
4470
|
}
|
|
4446
4471
|
|
|
4472
|
+
//#endregion
|
|
4473
|
+
//#region ../api/src/bookingAttentionEndpointDefinitions.ts
|
|
4474
|
+
const BOOKING_ATTENTION_ENDPOINT_DEFINITIONS = defineEndpointDefinitions({
|
|
4475
|
+
dismissBookingAttention: {
|
|
4476
|
+
demoMutation: DEMO_MUTATION_ALLOWED,
|
|
4477
|
+
path: "/sites/{siteId}/bookings/attention/dismiss",
|
|
4478
|
+
method: "POST",
|
|
4479
|
+
tags: ["site-{siteId}"],
|
|
4480
|
+
auth: "user",
|
|
4481
|
+
responseKind: "json",
|
|
4482
|
+
errors: [
|
|
4483
|
+
"validation:invalid_input",
|
|
4484
|
+
"auth:forbidden",
|
|
4485
|
+
"server:internal_error"
|
|
4486
|
+
]
|
|
4487
|
+
},
|
|
4488
|
+
resendWaitlistInvite: {
|
|
4489
|
+
path: "/sites/{siteId}/bookings/waitlist-entries/{entryId}/invite",
|
|
4490
|
+
method: "POST",
|
|
4491
|
+
tags: ["site-{siteId}"],
|
|
4492
|
+
auth: "user",
|
|
4493
|
+
responseKind: "json",
|
|
4494
|
+
errors: [
|
|
4495
|
+
"validation:invalid_input",
|
|
4496
|
+
"resource:not_found",
|
|
4497
|
+
"resource:conflict",
|
|
4498
|
+
"server:internal_error"
|
|
4499
|
+
]
|
|
4500
|
+
},
|
|
4501
|
+
resendAppointmentPaymentRequest: {
|
|
4502
|
+
demoMutation: DEMO_MUTATION_BLOCKED_DISABLED,
|
|
4503
|
+
path: "/sites/{siteId}/bookings/appointment-payment-requests/{requestId}/resend",
|
|
4504
|
+
method: "POST",
|
|
4505
|
+
tags: ["site-{siteId}"],
|
|
4506
|
+
auth: "user",
|
|
4507
|
+
responseKind: "json",
|
|
4508
|
+
errors: [
|
|
4509
|
+
"validation:invalid_input",
|
|
4510
|
+
"auth:forbidden",
|
|
4511
|
+
"server:internal_error"
|
|
4512
|
+
]
|
|
4513
|
+
}
|
|
4514
|
+
});
|
|
4515
|
+
|
|
4447
4516
|
//#endregion
|
|
4448
4517
|
//#region ../api/src/previewAuthEndpointDefinitions.ts
|
|
4449
4518
|
const PREVIEW_AUTH_ENDPOINT_DEFINITIONS = defineEndpointDefinitions({
|
|
@@ -4470,31 +4539,6 @@ const PREVIEW_AUTH_ENDPOINT_DEFINITIONS = defineEndpointDefinitions({
|
|
|
4470
4539
|
}
|
|
4471
4540
|
});
|
|
4472
4541
|
|
|
4473
|
-
//#endregion
|
|
4474
|
-
//#region ../api/src/apiEndpointTypes.ts
|
|
4475
|
-
function allowedDemoMutationPolicy() {
|
|
4476
|
-
return {
|
|
4477
|
-
classification: "allowed",
|
|
4478
|
-
publicMessage: ""
|
|
4479
|
-
};
|
|
4480
|
-
}
|
|
4481
|
-
function blockedDemoMutationPolicy(classification, publicMessage) {
|
|
4482
|
-
if (publicMessage.trim() === "") {
|
|
4483
|
-
throw new Error(`Demo mutation policy ${classification} requires publicMessage.`);
|
|
4484
|
-
}
|
|
4485
|
-
return {
|
|
4486
|
-
classification,
|
|
4487
|
-
publicMessage
|
|
4488
|
-
};
|
|
4489
|
-
}
|
|
4490
|
-
|
|
4491
|
-
//#endregion
|
|
4492
|
-
//#region ../api/src/demoMutationPolicies.ts
|
|
4493
|
-
const DEMO_MUTATION_BLOCKED_HIDDEN = blockedDemoMutationPolicy("blocked-hidden", "This action is not available in demo sites.");
|
|
4494
|
-
const DEMO_MUTATION_BLOCKED_DISABLED = blockedDemoMutationPolicy("blocked-disabled", "This setting is disabled in demo sites.");
|
|
4495
|
-
const DEMO_MUTATION_INTERNAL_ONLY = blockedDemoMutationPolicy("internal-only", "This internal operation is not available in demo sites.");
|
|
4496
|
-
const DEMO_MUTATION_ALLOWED = allowedDemoMutationPolicy();
|
|
4497
|
-
|
|
4498
4542
|
//#endregion
|
|
4499
4543
|
//#region ../api/src/siteLifecycleEndpoints.ts
|
|
4500
4544
|
const SITE_LIFECYCLE_ENDPOINT_DEFINITIONS = defineEndpointDefinitions({
|
|
@@ -4827,6 +4871,42 @@ const SDK_ENTRY_READ_ENDPOINTS = {
|
|
|
4827
4871
|
}
|
|
4828
4872
|
};
|
|
4829
4873
|
|
|
4874
|
+
//#endregion
|
|
4875
|
+
//#region ../api/src/appointmentRequestEndpointDefinitions.ts
|
|
4876
|
+
const APPOINTMENT_REQUEST_ENDPOINT_DEFINITIONS = defineEndpointDefinitions({
|
|
4877
|
+
approveAppointmentRequest: {
|
|
4878
|
+
path: "/sites/{siteId}/bookings/appointments/{appointmentId}/approve",
|
|
4879
|
+
method: "POST",
|
|
4880
|
+
auth: "user",
|
|
4881
|
+
responseKind: "json",
|
|
4882
|
+
errors: [
|
|
4883
|
+
"validation:invalid_input",
|
|
4884
|
+
"resource:not_found",
|
|
4885
|
+
"auth:forbidden",
|
|
4886
|
+
"server:internal_error"
|
|
4887
|
+
]
|
|
4888
|
+
},
|
|
4889
|
+
declineAppointmentRequest: {
|
|
4890
|
+
path: "/sites/{siteId}/bookings/appointments/{appointmentId}/decline",
|
|
4891
|
+
method: "POST",
|
|
4892
|
+
auth: "user",
|
|
4893
|
+
responseKind: "json",
|
|
4894
|
+
errors: [
|
|
4895
|
+
"validation:invalid_input",
|
|
4896
|
+
"resource:not_found",
|
|
4897
|
+
"auth:forbidden",
|
|
4898
|
+
"server:internal_error"
|
|
4899
|
+
]
|
|
4900
|
+
},
|
|
4901
|
+
listWaitingAppointmentRequests: {
|
|
4902
|
+
path: "/sites/{siteId}/bookings/appointments/requests",
|
|
4903
|
+
method: "GET",
|
|
4904
|
+
auth: "user",
|
|
4905
|
+
responseKind: "json",
|
|
4906
|
+
errors: ["auth:forbidden", "server:internal_error"]
|
|
4907
|
+
}
|
|
4908
|
+
});
|
|
4909
|
+
|
|
4830
4910
|
//#endregion
|
|
4831
4911
|
//#region ../api/src/appointmentServiceCategoryEndpoints.ts
|
|
4832
4912
|
const APPOINTMENT_SERVICE_CATEGORY_ENDPOINT_DEFINITIONS = defineEndpointDefinitions({
|
|
@@ -4911,6 +4991,7 @@ const APPOINTMENT_SERVICE_CATEGORY_ENDPOINT_DEFINITIONS = defineEndpointDefiniti
|
|
|
4911
4991
|
//#region ../api/src/appointmentEndpointDefinitions.ts
|
|
4912
4992
|
const APPOINTMENT_ENDPOINT_DEFINITIONS = defineEndpointDefinitions({
|
|
4913
4993
|
...APPOINTMENT_SERVICE_CATEGORY_ENDPOINT_DEFINITIONS,
|
|
4994
|
+
...APPOINTMENT_REQUEST_ENDPOINT_DEFINITIONS,
|
|
4914
4995
|
listAppointmentResources: {
|
|
4915
4996
|
path: "/sites/{siteId}/bookings/resources",
|
|
4916
4997
|
method: "GET",
|
|
@@ -6883,6 +6964,14 @@ const GIFTING_ENDPOINT_DEFINITIONS = defineEndpointDefinitions({
|
|
|
6883
6964
|
auth: "public",
|
|
6884
6965
|
responseKind: "json"
|
|
6885
6966
|
},
|
|
6967
|
+
previewGiftCardClaimPublic: {
|
|
6968
|
+
path: "/public/sites/{siteId}/gifting/claim",
|
|
6969
|
+
method: "GET",
|
|
6970
|
+
tags: [TAG_TEMPLATES.publicGifting],
|
|
6971
|
+
errors: [],
|
|
6972
|
+
auth: "public",
|
|
6973
|
+
responseKind: "json"
|
|
6974
|
+
},
|
|
6886
6975
|
claimGiftCardPublic: {
|
|
6887
6976
|
path: "/public/sites/{siteId}/gifting/claim",
|
|
6888
6977
|
method: "POST",
|
|
@@ -10631,6 +10720,14 @@ const ENDPOINT_DEFINITIONS = {
|
|
|
10631
10720
|
auth: "public",
|
|
10632
10721
|
responseKind: "text"
|
|
10633
10722
|
},
|
|
10723
|
+
getPublicAppointmentCalendarIcs: {
|
|
10724
|
+
path: "/public/sites/{siteId}/bookings/appointments/{token}/appointment.ics",
|
|
10725
|
+
method: "GET",
|
|
10726
|
+
tags: ["public-appointment-calendar-{siteId}"],
|
|
10727
|
+
auth: "public",
|
|
10728
|
+
responseKind: "text",
|
|
10729
|
+
errors: ["resource:not_found", "server:internal_error"]
|
|
10730
|
+
},
|
|
10634
10731
|
getPublicBookingCalendarFeedIcs: {
|
|
10635
10732
|
path: "/public/sites/{siteId}/bookings/calendar-feeds/{token}/feed.ics",
|
|
10636
10733
|
method: "GET",
|
|
@@ -10967,19 +11064,7 @@ const ENDPOINT_DEFINITIONS = {
|
|
|
10967
11064
|
responseKind: "json",
|
|
10968
11065
|
errors: ["validation:invalid_input", "server:internal_error"]
|
|
10969
11066
|
},
|
|
10970
|
-
|
|
10971
|
-
path: "/sites/{siteId}/bookings/waitlist-entries/{entryId}/invite",
|
|
10972
|
-
method: "POST",
|
|
10973
|
-
tags: ["site-{siteId}"],
|
|
10974
|
-
auth: "user",
|
|
10975
|
-
responseKind: "json",
|
|
10976
|
-
errors: [
|
|
10977
|
-
"validation:invalid_input",
|
|
10978
|
-
"resource:not_found",
|
|
10979
|
-
"resource:conflict",
|
|
10980
|
-
"server:internal_error"
|
|
10981
|
-
]
|
|
10982
|
-
},
|
|
11067
|
+
...BOOKING_ATTENTION_ENDPOINT_DEFINITIONS,
|
|
10983
11068
|
cancelWaitlistEntry: {
|
|
10984
11069
|
path: "/sites/{siteId}/bookings/waitlist-entries/{entryId}/cancel",
|
|
10985
11070
|
method: "POST",
|
|
@@ -13834,1653 +13919,1648 @@ function createBearerAPIClient(token, baseUrl) {
|
|
|
13834
13919
|
}
|
|
13835
13920
|
|
|
13836
13921
|
//#endregion
|
|
13837
|
-
//#region src/
|
|
13838
|
-
|
|
13839
|
-
|
|
13840
|
-
|
|
13841
|
-
function getPushConfigSync(result) {
|
|
13842
|
-
if (result.configSync) return result.configSync;
|
|
13843
|
-
if (result.sync) return result.sync;
|
|
13844
|
-
return null;
|
|
13845
|
-
}
|
|
13846
|
-
function collectPushConfigSyncErrors(sync) {
|
|
13847
|
-
return [
|
|
13848
|
-
...sync.contentTypes.errors.map((message) => ({
|
|
13849
|
-
area: "contentTypes",
|
|
13850
|
-
message
|
|
13851
|
-
})),
|
|
13852
|
-
...sync.customBlocks.errors.map((message) => ({
|
|
13853
|
-
area: "customBlocks",
|
|
13854
|
-
message
|
|
13855
|
-
})),
|
|
13856
|
-
...sync.templateBlocks.errors.map((message) => ({
|
|
13857
|
-
area: "templateBlocks",
|
|
13858
|
-
message
|
|
13859
|
-
})),
|
|
13860
|
-
...sync.settings.error ? [{
|
|
13861
|
-
area: "settings",
|
|
13862
|
-
message: sync.settings.error
|
|
13863
|
-
}] : [],
|
|
13864
|
-
...!isConfigSync(sync) ? sync.pages.errors.map((message) => ({
|
|
13865
|
-
area: "pages",
|
|
13866
|
-
message
|
|
13867
|
-
})) : [],
|
|
13868
|
-
...!isConfigSync(sync) ? sync.entries.errors.map((message) => ({
|
|
13869
|
-
area: "entries",
|
|
13870
|
-
message
|
|
13871
|
-
})) : [],
|
|
13872
|
-
...!isConfigSync(sync) ? sync.navigationMenus.errors.map((message) => ({
|
|
13873
|
-
area: "navigationMenus",
|
|
13874
|
-
message
|
|
13875
|
-
})) : []
|
|
13876
|
-
];
|
|
13877
|
-
}
|
|
13878
|
-
function collectPushConfigSyncWarnings(sync) {
|
|
13879
|
-
return [
|
|
13880
|
-
...sync.contentTypes.warnings.map((message) => ({
|
|
13881
|
-
area: "contentTypes",
|
|
13882
|
-
message
|
|
13883
|
-
})),
|
|
13884
|
-
...sync.customBlocks.warnings.map((message) => ({
|
|
13885
|
-
area: "customBlocks",
|
|
13886
|
-
message
|
|
13887
|
-
})),
|
|
13888
|
-
...sync.templateBlocks.warnings.map((message) => ({
|
|
13889
|
-
area: "templateBlocks",
|
|
13890
|
-
message
|
|
13891
|
-
})),
|
|
13892
|
-
...isConfigSync(sync) ? sync.ignoredDocumentSections.map((section) => ({
|
|
13893
|
-
area: "ignoredDocuments",
|
|
13894
|
-
message: formatIgnoredDocumentSyncSectionWarning(section)
|
|
13895
|
-
})) : [
|
|
13896
|
-
...sync.pages.warnings.map((message) => ({
|
|
13897
|
-
area: "pages",
|
|
13898
|
-
message
|
|
13899
|
-
})),
|
|
13900
|
-
...sync.entries.warnings.map((message) => ({
|
|
13901
|
-
area: "entries",
|
|
13902
|
-
message
|
|
13903
|
-
})),
|
|
13904
|
-
...sync.navigationMenus.warnings.map((message) => ({
|
|
13905
|
-
area: "navigationMenus",
|
|
13906
|
-
message
|
|
13907
|
-
}))
|
|
13908
|
-
]
|
|
13909
|
-
];
|
|
13922
|
+
//#region ../api/src/appointmentServiceVisibility.ts
|
|
13923
|
+
const APPOINTMENT_SERVICE_VISIBILITY_VALUES = ["public", "unlisted"];
|
|
13924
|
+
function isAppointmentServiceVisibility(value) {
|
|
13925
|
+
return value === "public" || value === "unlisted";
|
|
13910
13926
|
}
|
|
13911
|
-
|
|
13912
|
-
|
|
13913
|
-
|
|
13914
|
-
|
|
13915
|
-
|
|
13916
|
-
|
|
13927
|
+
|
|
13928
|
+
//#endregion
|
|
13929
|
+
//#region ../api/src/appointmentServiceLocation.ts
|
|
13930
|
+
const APPOINTMENT_SERVICE_LOCATION_TYPE_VALUES = [
|
|
13931
|
+
"virtual",
|
|
13932
|
+
"in_person",
|
|
13933
|
+
"hybrid"
|
|
13934
|
+
];
|
|
13935
|
+
function isAppointmentServiceLocationType(value) {
|
|
13936
|
+
return value === "virtual" || value === "in_person" || value === "hybrid";
|
|
13917
13937
|
}
|
|
13918
|
-
|
|
13919
|
-
|
|
13920
|
-
|
|
13921
|
-
|
|
13922
|
-
|
|
13923
|
-
|
|
13924
|
-
|
|
13925
|
-
|
|
13926
|
-
|
|
13938
|
+
|
|
13939
|
+
//#endregion
|
|
13940
|
+
//#region ../api/src/bookingOperations.ts
|
|
13941
|
+
const APPOINTMENT_RESERVATION_STATUS_VALUES = [
|
|
13942
|
+
"active",
|
|
13943
|
+
"paused",
|
|
13944
|
+
"ended"
|
|
13945
|
+
];
|
|
13946
|
+
|
|
13947
|
+
//#endregion
|
|
13948
|
+
//#region ../api/src/utils/isRecord.ts
|
|
13949
|
+
function isRecord$5(value) {
|
|
13950
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
13927
13951
|
}
|
|
13928
13952
|
|
|
13929
13953
|
//#endregion
|
|
13930
|
-
//#region src/
|
|
13954
|
+
//#region ../api/src/utils/error-propagation.ts
|
|
13931
13955
|
/**
|
|
13932
|
-
*
|
|
13933
|
-
*
|
|
13934
|
-
*
|
|
13956
|
+
* Extracts an error message from an API error response body.
|
|
13957
|
+
* Checks for our standard API error format: { error: { message: "..." } }
|
|
13958
|
+
* Also handles legacy formats with `error` or `message` as strings.
|
|
13935
13959
|
*/
|
|
13936
|
-
|
|
13937
|
-
|
|
13938
|
-
|
|
13939
|
-
}
|
|
13940
|
-
function isRecord$6(value) {
|
|
13941
|
-
return typeof value === "object" && value !== null;
|
|
13942
|
-
}
|
|
13943
|
-
function parseErrorDetails(value) {
|
|
13944
|
-
if (!value) return {};
|
|
13945
|
-
if (typeof value === "string") {
|
|
13946
|
-
return { message: value };
|
|
13947
|
-
}
|
|
13948
|
-
if (!isRecord$6(value)) return {};
|
|
13949
|
-
const message = typeof value.message === "string" ? value.message : undefined;
|
|
13950
|
-
const fieldErrorsRaw = value.fieldErrors;
|
|
13951
|
-
const fieldErrors = Array.isArray(fieldErrorsRaw) ? fieldErrorsRaw.filter(isRecord$6).map((d) => ({
|
|
13952
|
-
field: typeof d.field === "string" ? d.field : "",
|
|
13953
|
-
message: typeof d.message === "string" ? d.message : ""
|
|
13954
|
-
})).filter((d) => d.field.length > 0 && d.message.length > 0) : undefined;
|
|
13955
|
-
return {
|
|
13956
|
-
message,
|
|
13957
|
-
fieldErrors
|
|
13958
|
-
};
|
|
13959
|
-
}
|
|
13960
|
-
function formatFieldErrors(fieldErrors) {
|
|
13961
|
-
if (!fieldErrors || fieldErrors.length === 0) return "";
|
|
13962
|
-
return ":\n" + fieldErrors.map((d) => ` - ${d.field}: ${d.message}`).join("\n");
|
|
13963
|
-
}
|
|
13964
|
-
function getDashboardErrorMessage(responseJson, fallback) {
|
|
13965
|
-
if (!responseJson) return fallback;
|
|
13966
|
-
if (isRecord$6(responseJson) && "error" in responseJson) {
|
|
13967
|
-
const parsed = parseErrorDetails(responseJson.error);
|
|
13968
|
-
const msg = parsed.message ?? fallback;
|
|
13969
|
-
return msg + formatFieldErrors(parsed.fieldErrors);
|
|
13960
|
+
function extractApiErrorMessage(body) {
|
|
13961
|
+
if (!isRecord$5(body)) {
|
|
13962
|
+
return null;
|
|
13970
13963
|
}
|
|
13971
|
-
|
|
13972
|
-
|
|
13973
|
-
|
|
13974
|
-
* Push config to dashboard API
|
|
13975
|
-
*/
|
|
13976
|
-
async function pushToDashboard(dashboardUrl, siteId, apiKey, config, options) {
|
|
13977
|
-
const normalizedDashboardUrl = dashboardUrl.replace(/\/+$/, "");
|
|
13978
|
-
const baseUrl = `${normalizedDashboardUrl}/api`;
|
|
13979
|
-
const path$3 = `/sites/${siteId}/sdk-config`;
|
|
13980
|
-
const pushUrl = `${baseUrl}${path$3}`;
|
|
13981
|
-
const requestInfo = {
|
|
13982
|
-
method: "POST",
|
|
13983
|
-
intent: "write",
|
|
13984
|
-
path: path$3,
|
|
13985
|
-
url: pushUrl,
|
|
13986
|
-
baseUrl,
|
|
13987
|
-
dashboardUrl: normalizedDashboardUrl,
|
|
13988
|
-
siteId
|
|
13989
|
-
};
|
|
13990
|
-
const client = createBearerAPIClient(exposeSecret(apiKey), baseUrl);
|
|
13991
|
-
const timeoutMs = currentRequestTimeoutMs() ?? 3e4;
|
|
13992
|
-
try {
|
|
13993
|
-
return unwrapResponse(await client({
|
|
13994
|
-
endpoint: "pushSdkConfig",
|
|
13995
|
-
params: { siteId },
|
|
13996
|
-
body: {
|
|
13997
|
-
config,
|
|
13998
|
-
forceContentTypes: options?.forceContentTypes ?? false,
|
|
13999
|
-
mode: options?.dryRun ? "preview" : "apply",
|
|
14000
|
-
expectedMailingListProviderScope: options?.expectedMailingListProviderScope ?? undefined
|
|
14001
|
-
},
|
|
14002
|
-
options: { signal: AbortSignal.timeout(timeoutMs) }
|
|
14003
|
-
}));
|
|
14004
|
-
} catch (error) {
|
|
14005
|
-
if (error instanceof ApiEnvelopeError) {
|
|
14006
|
-
const details = {
|
|
14007
|
-
success: false,
|
|
14008
|
-
error: {
|
|
14009
|
-
code: error.code,
|
|
14010
|
-
message: error.message,
|
|
14011
|
-
fieldErrors: error.fieldErrors
|
|
14012
|
-
}
|
|
14013
|
-
};
|
|
14014
|
-
const errorMessage = getDashboardErrorMessage(details, error.message);
|
|
14015
|
-
throw new ManagementApiError(errorMessage, "sdk:http-error", details, error.status, requestInfo);
|
|
14016
|
-
}
|
|
14017
|
-
if (error instanceof ApiRequestError) {
|
|
14018
|
-
const errorMessage = getDashboardErrorMessage(error.body, `Dashboard returned ${error.status}`);
|
|
14019
|
-
throw new ManagementApiError(errorMessage, "sdk:http-error", error.body, error.status, requestInfo);
|
|
13964
|
+
if (isRecord$5(body.error)) {
|
|
13965
|
+
if (typeof body.error.message === "string" && body.error.message.trim().length > 0) {
|
|
13966
|
+
return body.error.message;
|
|
14020
13967
|
}
|
|
14021
|
-
throw managementTransportError(error, timeoutMs, requestInfo);
|
|
14022
|
-
}
|
|
14023
|
-
}
|
|
14024
|
-
function validatePushConfig(rawConfig) {
|
|
14025
|
-
const parsed = riverbankSiteConfigSchema.safeParse(rawConfig);
|
|
14026
|
-
if (!parsed.success) {
|
|
14027
|
-
const issues = parsed.error.issues.map((issue) => `${issue.path.join(".")}: ${issue.message}`).join("; ");
|
|
14028
|
-
throw new Error(`Invalid config: ${issues}`);
|
|
14029
13968
|
}
|
|
14030
|
-
|
|
14031
|
-
|
|
14032
|
-
throw new Error(`Field ID conflicts detected in blockFieldExtensions: ${conflicts.map((conflict) => conflict.message).join("; ")}`);
|
|
13969
|
+
if (typeof body.error === "string" && body.error.trim().length > 0) {
|
|
13970
|
+
return body.error;
|
|
14033
13971
|
}
|
|
14034
|
-
|
|
14035
|
-
|
|
14036
|
-
async function loadValidatedPushConfig(target, configPath) {
|
|
14037
|
-
return validatePushConfig(await withRiverbankEnv(target, () => loadConfigFile(configPath)));
|
|
14038
|
-
}
|
|
14039
|
-
function analyzePushConfigResponse(response, dryRun) {
|
|
14040
|
-
const resultMode = response.mode ?? "apply";
|
|
14041
|
-
if (dryRun && resultMode !== "preview") {
|
|
14042
|
-
return {
|
|
14043
|
-
success: false,
|
|
14044
|
-
kind: "unsafe-preview",
|
|
14045
|
-
receivedMode: response.mode
|
|
14046
|
-
};
|
|
13972
|
+
if (typeof body.message === "string" && body.message.trim().length > 0) {
|
|
13973
|
+
return body.message;
|
|
14047
13974
|
}
|
|
14048
|
-
|
|
14049
|
-
if (!sync) return {
|
|
14050
|
-
success: true,
|
|
14051
|
-
resultMode,
|
|
14052
|
-
sync: null,
|
|
14053
|
-
warnings: []
|
|
14054
|
-
};
|
|
14055
|
-
const errors = collectPushConfigSyncErrors(sync);
|
|
14056
|
-
if (errors.length > 0 || sync.success === false) return {
|
|
14057
|
-
success: false,
|
|
14058
|
-
kind: "sync-errors",
|
|
14059
|
-
sync,
|
|
14060
|
-
errors
|
|
14061
|
-
};
|
|
14062
|
-
return {
|
|
14063
|
-
success: true,
|
|
14064
|
-
resultMode,
|
|
14065
|
-
sync,
|
|
14066
|
-
warnings: collectPushConfigSyncWarnings(sync)
|
|
14067
|
-
};
|
|
13975
|
+
return null;
|
|
14068
13976
|
}
|
|
14069
|
-
|
|
14070
|
-
|
|
14071
|
-
|
|
14072
|
-
|
|
14073
|
-
|
|
14074
|
-
|
|
14075
|
-
|
|
14076
|
-
|
|
14077
|
-
|
|
14078
|
-
|
|
14079
|
-
|
|
14080
|
-
|
|
14081
|
-
|
|
14082
|
-
|
|
14083
|
-
|
|
14084
|
-
|
|
14085
|
-
|
|
14086
|
-
|
|
14087
|
-
|
|
14088
|
-
|
|
14089
|
-
|
|
14090
|
-
|
|
14091
|
-
|
|
14092
|
-
|
|
14093
|
-
}
|
|
14094
|
-
return { success: false };
|
|
14095
|
-
}
|
|
14096
|
-
if (mode === "human" && analysis.warnings.length > 0) {
|
|
14097
|
-
output.warn("SDK config sync reported warnings", { warnings: groupPushConfigSyncWarningsForHuman(analysis.warnings) });
|
|
14098
|
-
}
|
|
14099
|
-
if (mode === "human" && analysis.sync) {
|
|
14100
|
-
output.info(analysis.resultMode === "preview" ? "SDK config preview summary" : "SDK config sync summary", analysis.resultMode === "preview" ? buildPreviewSummary(analysis.sync) : analysis.sync);
|
|
14101
|
-
}
|
|
14102
|
-
if (mode === "json") {
|
|
14103
|
-
output.json({
|
|
14104
|
-
success: true,
|
|
14105
|
-
...analysis.resultMode === "preview" ? {
|
|
14106
|
-
dryRun: true,
|
|
14107
|
-
mode: analysis.resultMode
|
|
14108
|
-
} : {},
|
|
14109
|
-
sync: analysis.sync
|
|
13977
|
+
/**
|
|
13978
|
+
* Normalizes various error types and throws with an appropriate message.
|
|
13979
|
+
*
|
|
13980
|
+
* - For ApiRequestError: extracts message from body.error or body.message
|
|
13981
|
+
* - For Error instances: re-throws as-is
|
|
13982
|
+
* - For unknown errors: wraps with fallback message
|
|
13983
|
+
*
|
|
13984
|
+
* @param error - The error to normalize
|
|
13985
|
+
* @param fallback - Fallback message if no specific message can be extracted
|
|
13986
|
+
* @throws {Error} Always throws - this function never returns
|
|
13987
|
+
*/
|
|
13988
|
+
function normalizeApiError(error, fallback) {
|
|
13989
|
+
if (error instanceof ApiRequestError) {
|
|
13990
|
+
const message = extractApiErrorMessage(error.body);
|
|
13991
|
+
throw new ApiRequestError(message ?? fallback, {
|
|
13992
|
+
endpoint: error.endpoint,
|
|
13993
|
+
status: error.status,
|
|
13994
|
+
method: error.method,
|
|
13995
|
+
auth: error.auth,
|
|
13996
|
+
requestId: error.requestId,
|
|
13997
|
+
body: error.body,
|
|
13998
|
+
cause: error,
|
|
13999
|
+
errorCode: error.errorCode,
|
|
14000
|
+
retryAfterMs: error.retryAfterMs
|
|
14110
14001
|
});
|
|
14111
|
-
} else {
|
|
14112
|
-
output.success(analysis.resultMode === "preview" ? "Config preview completed. No changes were applied." : "Config pushed successfully!");
|
|
14113
14002
|
}
|
|
14114
|
-
|
|
14115
|
-
|
|
14116
|
-
/** Main push-config action, retained for `push --with-config`. */
|
|
14117
|
-
async function pushConfigAction(output, options) {
|
|
14118
|
-
const target = options.target;
|
|
14119
|
-
try {
|
|
14120
|
-
if (!options.isJsonOutput) {
|
|
14121
|
-
output.info("push-config target", {
|
|
14122
|
-
env: target,
|
|
14123
|
-
dashboardUrl: options.dashboard,
|
|
14124
|
-
configPath: options.config ?? DEFAULT_CONFIG_PATH
|
|
14125
|
-
});
|
|
14126
|
-
output.info("Validating config...");
|
|
14127
|
-
}
|
|
14128
|
-
const config = await loadValidatedPushConfig(target, options.config);
|
|
14129
|
-
const apiKey = resolveManagementApiKey(output, options.apiKey, target);
|
|
14130
|
-
const response = await pushToDashboard(options.dashboard, config.siteId, apiKey, config, {
|
|
14131
|
-
forceContentTypes: options.forceContentTypes ?? false,
|
|
14132
|
-
dryRun: options.dryRun ?? false
|
|
14133
|
-
});
|
|
14134
|
-
return renderPushConfigResponse(output, response, options.isJsonOutput ? "json" : "human", options.dryRun ?? false);
|
|
14135
|
-
} catch (error) {
|
|
14136
|
-
handleCommandError(error, output, {
|
|
14137
|
-
target,
|
|
14138
|
-
dashboardUrl: options.dashboard
|
|
14139
|
-
});
|
|
14140
|
-
return { success: false };
|
|
14003
|
+
if (error instanceof Error) {
|
|
14004
|
+
throw error;
|
|
14141
14005
|
}
|
|
14142
|
-
|
|
14143
|
-
function resolveDashboardUrlValue(cliOption, target) {
|
|
14144
|
-
const envVar = getEnvVarName("DASHBOARD_URL", target);
|
|
14145
|
-
const url = cliOption || process.env[envVar];
|
|
14146
|
-
if (!url) {
|
|
14147
|
-
return {
|
|
14148
|
-
ok: false,
|
|
14149
|
-
error: {
|
|
14150
|
-
message: "Dashboard URL is required",
|
|
14151
|
-
details: { suggestion: `Provide --dashboard <url> or set ${envVar} environment variable.` }
|
|
14152
|
-
}
|
|
14153
|
-
};
|
|
14154
|
-
}
|
|
14155
|
-
return {
|
|
14156
|
-
ok: true,
|
|
14157
|
-
value: url
|
|
14158
|
-
};
|
|
14159
|
-
}
|
|
14160
|
-
/**
|
|
14161
|
-
* Resolve management API key from CLI option or environment variable.
|
|
14162
|
-
*/
|
|
14163
|
-
function resolveManagementApiKeyValue(cliOption, target) {
|
|
14164
|
-
const envVar = getEnvVarName("MGMT_API_KEY", target);
|
|
14165
|
-
const apiKey = cliOption || process.env[envVar];
|
|
14166
|
-
if (!apiKey) {
|
|
14167
|
-
return {
|
|
14168
|
-
ok: false,
|
|
14169
|
-
error: {
|
|
14170
|
-
message: "Management API key is required",
|
|
14171
|
-
details: { suggestion: `Provide --api-key <key> or set ${envVar} environment variable.` }
|
|
14172
|
-
}
|
|
14173
|
-
};
|
|
14174
|
-
}
|
|
14175
|
-
const parsedApiKey = parseManagementKey(apiKey);
|
|
14176
|
-
if (!parsedApiKey.ok) {
|
|
14177
|
-
return {
|
|
14178
|
-
ok: false,
|
|
14179
|
-
error: {
|
|
14180
|
-
message: "Invalid management API key format",
|
|
14181
|
-
details: {
|
|
14182
|
-
expected: "Key starting with bld_mgmt_sk_",
|
|
14183
|
-
suggestion: `Check your ${envVar} environment variable.`
|
|
14184
|
-
}
|
|
14185
|
-
}
|
|
14186
|
-
};
|
|
14187
|
-
}
|
|
14188
|
-
return {
|
|
14189
|
-
ok: true,
|
|
14190
|
-
value: parsedApiKey.value
|
|
14191
|
-
};
|
|
14192
|
-
}
|
|
14193
|
-
function resolveManagementApiKey(output, cliOption, target) {
|
|
14194
|
-
const result = resolveManagementApiKeyValue(cliOption, target);
|
|
14195
|
-
if (!result.ok) output.error(result.error.message, result.error.details);
|
|
14196
|
-
return result.value;
|
|
14197
|
-
}
|
|
14198
|
-
function unwrapPushConfigTargetValue(result) {
|
|
14199
|
-
if (result.ok) return result.value;
|
|
14200
|
-
const suggestion = result.error.details.suggestion;
|
|
14201
|
-
throw new Error(`${result.error.message}${typeof suggestion === "string" ? `. ${suggestion}` : ""}`);
|
|
14202
|
-
}
|
|
14203
|
-
async function loadPushConfigTarget(target, options) {
|
|
14204
|
-
return {
|
|
14205
|
-
target,
|
|
14206
|
-
dashboard: unwrapPushConfigTargetValue(resolveDashboardUrlValue(options.dashboard, target)),
|
|
14207
|
-
apiKey: unwrapPushConfigTargetValue(resolveManagementApiKeyValue(options.apiKey, target))
|
|
14208
|
-
};
|
|
14209
|
-
}
|
|
14210
|
-
async function preparePushConfigTarget(context, options) {
|
|
14211
|
-
const config = await loadValidatedPushConfig(context.target, options.config);
|
|
14212
|
-
const preview = await pushToDashboard(context.dashboard, config.siteId, context.apiKey, config, {
|
|
14213
|
-
forceContentTypes: options.forceContentTypes ?? false,
|
|
14214
|
-
dryRun: true
|
|
14215
|
-
});
|
|
14216
|
-
if (!analyzePushConfigResponse(preview, true).success) {
|
|
14217
|
-
throw new Error(`Config preview failed for ${context.target} environment.`);
|
|
14218
|
-
}
|
|
14219
|
-
return {
|
|
14220
|
-
config,
|
|
14221
|
-
preview,
|
|
14222
|
-
forceContentTypes: options.forceContentTypes ?? false
|
|
14223
|
-
};
|
|
14224
|
-
}
|
|
14225
|
-
/**
|
|
14226
|
-
* Re-preview a prepared config value without reloading the local config file.
|
|
14227
|
-
* The caller uses this immediately before applying a multi-target plan so a
|
|
14228
|
-
* dashboard-side schema change is classified as stale before any mutation.
|
|
14229
|
-
*/
|
|
14230
|
-
async function revalidatePushConfigPlan(context, plan) {
|
|
14231
|
-
try {
|
|
14232
|
-
const current = await pushToDashboard(context.dashboard, plan.config.siteId, context.apiKey, plan.config, {
|
|
14233
|
-
forceContentTypes: plan.forceContentTypes,
|
|
14234
|
-
dryRun: true
|
|
14235
|
-
});
|
|
14236
|
-
return stableStringify(current) === stableStringify(plan.preview) ? { ok: true } : {
|
|
14237
|
-
ok: false,
|
|
14238
|
-
error: new Error("SDK config changed after preflight; the push plan is stale.")
|
|
14239
|
-
};
|
|
14240
|
-
} catch (error) {
|
|
14241
|
-
return {
|
|
14242
|
-
ok: false,
|
|
14243
|
-
error: error instanceof Error ? error : new Error(String(error))
|
|
14244
|
-
};
|
|
14245
|
-
}
|
|
14246
|
-
}
|
|
14247
|
-
async function applyPushConfigPlan(output, context, plan, options, isJsonOutput) {
|
|
14248
|
-
const response = options.dryRun ? plan.preview : await pushToDashboard(context.dashboard, plan.config.siteId, context.apiKey, plan.config, {
|
|
14249
|
-
forceContentTypes: plan.forceContentTypes,
|
|
14250
|
-
dryRun: false,
|
|
14251
|
-
expectedMailingListProviderScope: plan.preview.mailingListProviderScope
|
|
14252
|
-
});
|
|
14253
|
-
return {
|
|
14254
|
-
response,
|
|
14255
|
-
action: isJsonOutput ? { success: analyzePushConfigResponse(response, options.dryRun ?? false).success } : renderPushConfigResponse(output, response, "human", options.dryRun ?? false)
|
|
14256
|
-
};
|
|
14257
|
-
}
|
|
14258
|
-
function isPushConfigApplySuccess(result) {
|
|
14259
|
-
return result.action.success;
|
|
14260
|
-
}
|
|
14261
|
-
function renderPushConfigJsonResult(output, execution, dryRun) {
|
|
14262
|
-
output.json({
|
|
14263
|
-
success: execution.succeeded,
|
|
14264
|
-
dryRun,
|
|
14265
|
-
environments: mapEnvironmentExecutionOutcomesJson(execution, ({ result }) => ({
|
|
14266
|
-
response: result.response,
|
|
14267
|
-
success: true
|
|
14268
|
-
}), ({ result, message }) => ({
|
|
14269
|
-
response: result.response,
|
|
14270
|
-
success: false,
|
|
14271
|
-
error: message
|
|
14272
|
-
}))
|
|
14273
|
-
});
|
|
14274
|
-
}
|
|
14275
|
-
function getProductionPushConfigCommandDeps() {
|
|
14276
|
-
return {
|
|
14277
|
-
getOutputContext,
|
|
14278
|
-
planEnvironmentExecution,
|
|
14279
|
-
executeEnvironmentPlan,
|
|
14280
|
-
confirmAction,
|
|
14281
|
-
loadTarget: loadPushConfigTarget,
|
|
14282
|
-
prepareTarget: preparePushConfigTarget,
|
|
14283
|
-
revalidatePlan: revalidatePushConfigPlan,
|
|
14284
|
-
applyPlan: applyPushConfigPlan
|
|
14285
|
-
};
|
|
14286
|
-
}
|
|
14287
|
-
/**
|
|
14288
|
-
* push-config command definition
|
|
14289
|
-
*/
|
|
14290
|
-
const createPushConfigCommand = (deps = getProductionPushConfigCommandDeps()) => new Command("push-config").description("Push SDK config to dashboard").option("--api-key <key>", "Management API key (or set RIVERBANK_*_MGMT_API_KEY)").option("--dashboard <url>", "Dashboard URL (or set RIVERBANK_*_DASHBOARD_URL env var)").option("--config <path>", `Path to config file (default: ${DEFAULT_CONFIG_PATH})`).option("--force-content-types", "Force overwrite content type schema even if modified in dashboard after last SDK sync").option("--dry-run", "Preview SDK config changes without updating the dashboard").option("--yes", "Confirm applying config changes across all selected environments").addHelpText("after", `
|
|
14291
|
-
Description:
|
|
14292
|
-
Syncs your local riverbank.config.ts schema/config to the CMS dashboard, including:
|
|
14293
|
-
- Custom blocks
|
|
14294
|
-
- Block field extensions
|
|
14295
|
-
- Block field options
|
|
14296
|
-
- Content type definitions and templates
|
|
14297
|
-
- SDK-managed site settings and footer blocks
|
|
14298
|
-
|
|
14299
|
-
Pages, content entries, and navigation menus are content documents.
|
|
14300
|
-
Sync them with "riverbankcms push pages", "riverbankcms push entries",
|
|
14301
|
-
or "riverbankcms push navigation".
|
|
14302
|
-
|
|
14303
|
-
Examples:
|
|
14304
|
-
$ pnpm exec riverbankcms push-config
|
|
14305
|
-
$ pnpm exec riverbankcms push-config --dry-run
|
|
14306
|
-
$ pnpm exec riverbankcms push-config --env=remote
|
|
14307
|
-
$ pnpm exec riverbankcms push-config --api-key bld_mgmt_sk_... --dashboard https://www.riverbankcms.com
|
|
14308
|
-
$ pnpm exec riverbankcms push-config --config ./src/riverbank.config.ts
|
|
14309
|
-
`).action(async (options, command) => {
|
|
14310
|
-
const { output, selection, globalOpts } = deps.getOutputContext(command);
|
|
14311
|
-
const isJsonOutput = globalOpts.json ?? false;
|
|
14312
|
-
const authorization = requireEnvironmentWriteAuthorization({
|
|
14313
|
-
...options,
|
|
14314
|
-
json: isJsonOutput,
|
|
14315
|
-
interactive: process.stdin.isTTY === true
|
|
14316
|
-
}, output);
|
|
14317
|
-
if (!authorization) return;
|
|
14318
|
-
const planned = deps.planEnvironmentExecution({
|
|
14319
|
-
command: "push-config",
|
|
14320
|
-
selection: "both",
|
|
14321
|
-
failure: "abort",
|
|
14322
|
-
confirmation: "command",
|
|
14323
|
-
revalidation: "required"
|
|
14324
|
-
}, selection);
|
|
14325
|
-
if (!planned.ok) return output.error(planned.error.message, { suggestion: planned.error.suggestion });
|
|
14326
|
-
const execution = await deps.executeEnvironmentPlan(planned.value, {
|
|
14327
|
-
loadTarget: (target) => deps.loadTarget(target, options),
|
|
14328
|
-
preflight: async (_target, loaded) => deps.prepareTarget(loaded.context, {
|
|
14329
|
-
...options,
|
|
14330
|
-
dashboard: loaded.context.dashboard
|
|
14331
|
-
}),
|
|
14332
|
-
confirm: async ({ ready }) => {
|
|
14333
|
-
if (authorization.kind === "preauthorized") return true;
|
|
14334
|
-
for (const { target, context } of ready) {
|
|
14335
|
-
const sync = getPushConfigSync(context.preview);
|
|
14336
|
-
output.info(`${target} config preview`, sync ? buildPreviewSummary(sync) : { changes: "No sync summary returned" });
|
|
14337
|
-
}
|
|
14338
|
-
return deps.confirmAction(`Apply SDK config to ${ready.length} environment${ready.length === 1 ? "" : "s"} (${ready.map(({ target }) => target).join(" then ")})?`, { yes: options.yes }, false);
|
|
14339
|
-
},
|
|
14340
|
-
revalidate: async (_target, loaded, prepared) => options.dryRun ? { ok: true } : deps.revalidatePlan(loaded.context, prepared.context),
|
|
14341
|
-
apply: async (target, loaded, prepared) => {
|
|
14342
|
-
if (planned.value.targets.length > 1) output.info(`\n--- Pushing to ${target} environment ---`);
|
|
14343
|
-
return deps.applyPlan(output, loaded.context, prepared.context, {
|
|
14344
|
-
...options,
|
|
14345
|
-
dashboard: loaded.context.dashboard
|
|
14346
|
-
}, isJsonOutput);
|
|
14347
|
-
},
|
|
14348
|
-
isSuccess: isPushConfigApplySuccess,
|
|
14349
|
-
formatFailure: formatPushConfigActionFailure
|
|
14350
|
-
}, output);
|
|
14351
|
-
if (isJsonOutput) renderPushConfigJsonResult(output, execution, options.dryRun ?? false);
|
|
14352
|
-
});
|
|
14353
|
-
|
|
14354
|
-
//#endregion
|
|
14355
|
-
//#region ../api/src/utils/isRecord.ts
|
|
14356
|
-
function isRecord$5(value) {
|
|
14357
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
14358
|
-
}
|
|
14359
|
-
|
|
14360
|
-
//#endregion
|
|
14361
|
-
//#region ../api/src/utils/error-propagation.ts
|
|
14362
|
-
/**
|
|
14363
|
-
* Extracts an error message from an API error response body.
|
|
14364
|
-
* Checks for our standard API error format: { error: { message: "..." } }
|
|
14365
|
-
* Also handles legacy formats with `error` or `message` as strings.
|
|
14366
|
-
*/
|
|
14367
|
-
function extractApiErrorMessage(body) {
|
|
14368
|
-
if (!isRecord$5(body)) {
|
|
14369
|
-
return null;
|
|
14370
|
-
}
|
|
14371
|
-
if (isRecord$5(body.error)) {
|
|
14372
|
-
if (typeof body.error.message === "string" && body.error.message.trim().length > 0) {
|
|
14373
|
-
return body.error.message;
|
|
14374
|
-
}
|
|
14375
|
-
}
|
|
14376
|
-
if (typeof body.error === "string" && body.error.trim().length > 0) {
|
|
14377
|
-
return body.error;
|
|
14378
|
-
}
|
|
14379
|
-
if (typeof body.message === "string" && body.message.trim().length > 0) {
|
|
14380
|
-
return body.message;
|
|
14381
|
-
}
|
|
14382
|
-
return null;
|
|
14383
|
-
}
|
|
14384
|
-
/**
|
|
14385
|
-
* Normalizes various error types and throws with an appropriate message.
|
|
14386
|
-
*
|
|
14387
|
-
* - For ApiRequestError: extracts message from body.error or body.message
|
|
14388
|
-
* - For Error instances: re-throws as-is
|
|
14389
|
-
* - For unknown errors: wraps with fallback message
|
|
14390
|
-
*
|
|
14391
|
-
* @param error - The error to normalize
|
|
14392
|
-
* @param fallback - Fallback message if no specific message can be extracted
|
|
14393
|
-
* @throws {Error} Always throws - this function never returns
|
|
14394
|
-
*/
|
|
14395
|
-
function normalizeApiError(error, fallback) {
|
|
14396
|
-
if (error instanceof ApiRequestError) {
|
|
14397
|
-
const message = extractApiErrorMessage(error.body);
|
|
14398
|
-
throw new ApiRequestError(message ?? fallback, {
|
|
14399
|
-
endpoint: error.endpoint,
|
|
14400
|
-
status: error.status,
|
|
14401
|
-
method: error.method,
|
|
14402
|
-
auth: error.auth,
|
|
14403
|
-
requestId: error.requestId,
|
|
14404
|
-
body: error.body,
|
|
14405
|
-
cause: error,
|
|
14406
|
-
errorCode: error.errorCode,
|
|
14407
|
-
retryAfterMs: error.retryAfterMs
|
|
14408
|
-
});
|
|
14409
|
-
}
|
|
14410
|
-
if (error instanceof Error) {
|
|
14411
|
-
throw error;
|
|
14412
|
-
}
|
|
14413
|
-
throw new Error(fallback);
|
|
14414
|
-
}
|
|
14415
|
-
|
|
14416
|
-
//#endregion
|
|
14417
|
-
//#region ../api/src/navigationMenuValidation.ts
|
|
14418
|
-
function hasChildren(item) {
|
|
14419
|
-
return Array.isArray(item.children) && item.children.length > 0;
|
|
14420
|
-
}
|
|
14421
|
-
function isCartLinkMissingVisibleLabel(link$1) {
|
|
14422
|
-
return link$1?.kind === "cart" && link$1.label?.kind === "icon-with-label" && (typeof link$1.label.label !== "string" || link$1.label.label.trim().length === 0);
|
|
14423
|
-
}
|
|
14424
|
-
/**
|
|
14425
|
-
* Recursively collects portal-link constraints for a single menu tree.
|
|
14426
|
-
* Rules:
|
|
14427
|
-
* - At most one portal link per menu.
|
|
14428
|
-
* - Portal links must be root-level.
|
|
14429
|
-
* - Portal links cannot have children.
|
|
14430
|
-
*/
|
|
14431
|
-
function collectPortalLinkStats(items, depth = 0) {
|
|
14432
|
-
const result = {
|
|
14433
|
-
count: 0,
|
|
14434
|
-
nestedLabels: [],
|
|
14435
|
-
withChildrenLabels: []
|
|
14436
|
-
};
|
|
14437
|
-
for (const item of items) {
|
|
14438
|
-
const isPortal = item.link?.kind === "portal";
|
|
14439
|
-
if (isPortal) {
|
|
14440
|
-
result.count += 1;
|
|
14441
|
-
if (depth > 0) {
|
|
14442
|
-
result.nestedLabels.push(item.label);
|
|
14443
|
-
}
|
|
14444
|
-
if (hasChildren(item)) {
|
|
14445
|
-
result.withChildrenLabels.push(item.label);
|
|
14446
|
-
}
|
|
14447
|
-
}
|
|
14448
|
-
if (hasChildren(item)) {
|
|
14449
|
-
const childItems = (item.children ?? []).filter(isNavigationMenuValidationItem);
|
|
14450
|
-
const child = collectPortalLinkStats(childItems, depth + 1);
|
|
14451
|
-
result.count += child.count;
|
|
14452
|
-
result.nestedLabels.push(...child.nestedLabels);
|
|
14453
|
-
result.withChildrenLabels.push(...child.withChildrenLabels);
|
|
14454
|
-
}
|
|
14455
|
-
}
|
|
14456
|
-
return result;
|
|
14457
|
-
}
|
|
14458
|
-
/**
|
|
14459
|
-
* Recursively collects cart-link constraints for a single menu tree.
|
|
14460
|
-
* Rules:
|
|
14461
|
-
* - Cart links must be root-level.
|
|
14462
|
-
* - Cart links cannot have children.
|
|
14463
|
-
* - Cart links cannot be CTA items.
|
|
14464
|
-
* - Cart links using `icon-with-label` must provide visible text.
|
|
14465
|
-
*/
|
|
14466
|
-
function collectCartLinkStats(items, depth = 0) {
|
|
14467
|
-
const result = {
|
|
14468
|
-
count: 0,
|
|
14469
|
-
nestedLabels: [],
|
|
14470
|
-
withChildrenLabels: [],
|
|
14471
|
-
ctaLabels: [],
|
|
14472
|
-
missingVisibleTextLabels: []
|
|
14473
|
-
};
|
|
14474
|
-
for (const item of items) {
|
|
14475
|
-
const isCart = item.link?.kind === "cart";
|
|
14476
|
-
if (isCart) {
|
|
14477
|
-
result.count += 1;
|
|
14478
|
-
if (depth > 0) {
|
|
14479
|
-
result.nestedLabels.push(item.label);
|
|
14480
|
-
}
|
|
14481
|
-
if (hasChildren(item)) {
|
|
14482
|
-
result.withChildrenLabels.push(item.label);
|
|
14483
|
-
}
|
|
14484
|
-
if (item.isCta === true) {
|
|
14485
|
-
result.ctaLabels.push(item.label);
|
|
14486
|
-
}
|
|
14487
|
-
if (isCartLinkMissingVisibleLabel(item.link ?? null)) {
|
|
14488
|
-
result.missingVisibleTextLabels.push(item.label);
|
|
14489
|
-
}
|
|
14490
|
-
}
|
|
14491
|
-
if (hasChildren(item)) {
|
|
14492
|
-
const childItems = (item.children ?? []).filter(isNavigationMenuValidationItem);
|
|
14493
|
-
const child = collectCartLinkStats(childItems, depth + 1);
|
|
14494
|
-
result.count += child.count;
|
|
14495
|
-
result.nestedLabels.push(...child.nestedLabels);
|
|
14496
|
-
result.withChildrenLabels.push(...child.withChildrenLabels);
|
|
14497
|
-
result.ctaLabels.push(...child.ctaLabels);
|
|
14498
|
-
result.missingVisibleTextLabels.push(...child.missingVisibleTextLabels);
|
|
14499
|
-
}
|
|
14500
|
-
}
|
|
14501
|
-
return result;
|
|
14502
|
-
}
|
|
14503
|
-
function isNavigationMenuValidationItem(value) {
|
|
14504
|
-
if (!isRecord$5(value)) return false;
|
|
14505
|
-
return typeof value.label === "string";
|
|
14006
|
+
throw new Error(fallback);
|
|
14506
14007
|
}
|
|
14507
14008
|
|
|
14508
14009
|
//#endregion
|
|
14509
|
-
//#region ../api/src/
|
|
14510
|
-
const
|
|
14511
|
-
|
|
14512
|
-
|
|
14513
|
-
|
|
14514
|
-
|
|
14515
|
-
|
|
14516
|
-
|
|
14517
|
-
|
|
14518
|
-
|
|
14519
|
-
|
|
14520
|
-
|
|
14521
|
-
|
|
14522
|
-
|
|
14010
|
+
//#region ../api/src/bookingActivity.ts
|
|
14011
|
+
const BOOKING_ACTIVITY_CATEGORY_VALUES = [
|
|
14012
|
+
"booking",
|
|
14013
|
+
"purchase",
|
|
14014
|
+
"cancellation",
|
|
14015
|
+
"refund",
|
|
14016
|
+
"transfer",
|
|
14017
|
+
"failure"
|
|
14018
|
+
];
|
|
14019
|
+
const BOOKING_ACTIVITY_TYPE_VALUES = [
|
|
14020
|
+
"appointment_booked",
|
|
14021
|
+
"appointment_requested",
|
|
14022
|
+
"appointment_moved",
|
|
14023
|
+
"waitlist_joined",
|
|
14024
|
+
"balance_paid",
|
|
14025
|
+
"appointment_cancelled",
|
|
14026
|
+
"booking_closure_created",
|
|
14027
|
+
"booking_closure_removed",
|
|
14028
|
+
"event_registration",
|
|
14029
|
+
"event_registration_cancelled",
|
|
14030
|
+
"event_series_registration",
|
|
14031
|
+
"event_series_registration_cancelled",
|
|
14032
|
+
"pass_purchased",
|
|
14033
|
+
"membership_started",
|
|
14034
|
+
"membership_paused",
|
|
14035
|
+
"membership_cancelled",
|
|
14036
|
+
"event_refund",
|
|
14037
|
+
"pass_refund",
|
|
14038
|
+
"membership_refund",
|
|
14039
|
+
"event_series_registration_refund",
|
|
14040
|
+
"ticket_transfer",
|
|
14041
|
+
"payment_failed"
|
|
14042
|
+
];
|
|
14523
14043
|
|
|
14524
14044
|
//#endregion
|
|
14525
|
-
//#region ../api/src/
|
|
14526
|
-
|
|
14527
|
-
|
|
14528
|
-
|
|
14529
|
-
|
|
14530
|
-
|
|
14531
|
-
|
|
14532
|
-
|
|
14533
|
-
|
|
14534
|
-
|
|
14535
|
-
|
|
14536
|
-
|
|
14537
|
-
|
|
14538
|
-
|
|
14539
|
-
|
|
14540
|
-
|
|
14541
|
-
|
|
14542
|
-
|
|
14543
|
-
|
|
14544
|
-
|
|
14545
|
-
|
|
14546
|
-
|
|
14547
|
-
|
|
14548
|
-
|
|
14549
|
-
|
|
14550
|
-
|
|
14551
|
-
|
|
14552
|
-
|
|
14553
|
-
|
|
14554
|
-
|
|
14555
|
-
|
|
14556
|
-
|
|
14557
|
-
|
|
14558
|
-
|
|
14559
|
-
|
|
14560
|
-
|
|
14561
|
-
|
|
14562
|
-
|
|
14563
|
-
|
|
14564
|
-
|
|
14565
|
-
|
|
14566
|
-
|
|
14567
|
-
|
|
14568
|
-
|
|
14569
|
-
|
|
14570
|
-
|
|
14571
|
-
url: null,
|
|
14572
|
-
error: "Link is required for non-dropdown items"
|
|
14573
|
-
};
|
|
14574
|
-
}
|
|
14575
|
-
switch (link$1.kind) {
|
|
14576
|
-
case "page": return resolvePageLink(link$1.identifier, pageMap);
|
|
14577
|
-
case "entry": return resolveEntryLink(link$1.contentType, link$1.identifier, entryMap);
|
|
14578
|
-
case "external": return {
|
|
14579
|
-
urlType: "external",
|
|
14580
|
-
url: {
|
|
14581
|
-
kind: "external",
|
|
14582
|
-
href: link$1.href
|
|
14583
|
-
},
|
|
14584
|
-
error: null
|
|
14585
|
-
};
|
|
14586
|
-
case "url": return {
|
|
14587
|
-
urlType: "url",
|
|
14588
|
-
url: {
|
|
14589
|
-
kind: "url",
|
|
14590
|
-
href: link$1.href
|
|
14591
|
-
},
|
|
14592
|
-
error: null
|
|
14593
|
-
};
|
|
14594
|
-
case "portal": return {
|
|
14595
|
-
urlType: "portal",
|
|
14596
|
-
url: {
|
|
14597
|
-
kind: "portal",
|
|
14598
|
-
...link$1.path ? { path: link$1.path } : {},
|
|
14599
|
-
...link$1.labels ? { labels: link$1.labels } : {}
|
|
14600
|
-
},
|
|
14601
|
-
error: null
|
|
14602
|
-
};
|
|
14603
|
-
case "cart": return {
|
|
14604
|
-
urlType: "cart",
|
|
14605
|
-
url: {
|
|
14606
|
-
kind: "cart",
|
|
14607
|
-
badge: link$1.badge,
|
|
14608
|
-
label: link$1.label
|
|
14609
|
-
},
|
|
14610
|
-
error: null
|
|
14611
|
-
};
|
|
14612
|
-
default: return {
|
|
14613
|
-
urlType: "url",
|
|
14614
|
-
url: null,
|
|
14615
|
-
error: "Unknown link type"
|
|
14616
|
-
};
|
|
14617
|
-
}
|
|
14618
|
-
}
|
|
14619
|
-
/**
|
|
14620
|
-
* Resolve a page identifier to a canonical page link payload.
|
|
14621
|
-
*/
|
|
14622
|
-
function resolvePageLink(identifier, pageMap) {
|
|
14623
|
-
const pageInfo = pageMap.get(identifier);
|
|
14624
|
-
if (!pageInfo) {
|
|
14625
|
-
return {
|
|
14626
|
-
urlType: "internal",
|
|
14627
|
-
url: null,
|
|
14628
|
-
error: `Page '${identifier}' not found`
|
|
14629
|
-
};
|
|
14630
|
-
}
|
|
14631
|
-
return {
|
|
14632
|
-
urlType: "internal",
|
|
14633
|
-
url: {
|
|
14634
|
-
kind: "page",
|
|
14635
|
-
identifier
|
|
14636
|
-
},
|
|
14637
|
-
error: null
|
|
14638
|
-
};
|
|
14639
|
-
}
|
|
14640
|
-
/**
|
|
14641
|
-
* Resolve an entry identifier to a canonical entry link payload.
|
|
14642
|
-
*/
|
|
14643
|
-
function resolveEntryLink(contentType, identifier, entryMap) {
|
|
14644
|
-
const entryInfo = entryMap.get(`${contentType}:${identifier}`);
|
|
14645
|
-
if (!entryInfo) {
|
|
14646
|
-
return {
|
|
14647
|
-
urlType: "internal",
|
|
14648
|
-
url: null,
|
|
14649
|
-
error: `Entry '${contentType}:${identifier}' not found or not routable`
|
|
14650
|
-
};
|
|
14651
|
-
}
|
|
14652
|
-
return {
|
|
14653
|
-
urlType: "internal",
|
|
14654
|
-
url: {
|
|
14655
|
-
kind: "entry",
|
|
14656
|
-
contentType,
|
|
14657
|
-
identifier
|
|
14658
|
-
},
|
|
14659
|
-
error: null
|
|
14660
|
-
};
|
|
14661
|
-
}
|
|
14045
|
+
//#region ../api/src/bookingRecords.ts
|
|
14046
|
+
const CUSTOMER_PASS_STATUS_VALUES = [
|
|
14047
|
+
"active",
|
|
14048
|
+
"exhausted",
|
|
14049
|
+
"expired",
|
|
14050
|
+
"refunded"
|
|
14051
|
+
];
|
|
14052
|
+
const CUSTOMER_MEMBERSHIP_STATUS_VALUES = [
|
|
14053
|
+
"active",
|
|
14054
|
+
"trialing",
|
|
14055
|
+
"past_due",
|
|
14056
|
+
"paused",
|
|
14057
|
+
"canceled"
|
|
14058
|
+
];
|
|
14059
|
+
const BOOKING_LEDGER_KIND_VALUES = [
|
|
14060
|
+
"appointment",
|
|
14061
|
+
"event_registration",
|
|
14062
|
+
"event_series_registration"
|
|
14063
|
+
];
|
|
14064
|
+
const BOOKING_LEDGER_DATE_BASIS_VALUES = [
|
|
14065
|
+
"created_at",
|
|
14066
|
+
"scheduled_at",
|
|
14067
|
+
"cancelled_at"
|
|
14068
|
+
];
|
|
14069
|
+
const BOOKING_LEDGER_PERIOD_PRESET_VALUES = [
|
|
14070
|
+
"7d",
|
|
14071
|
+
"30d",
|
|
14072
|
+
"90d",
|
|
14073
|
+
"365d",
|
|
14074
|
+
"all"
|
|
14075
|
+
];
|
|
14076
|
+
const BOOKING_LEDGER_CANCELLATION_MODE_VALUES = [
|
|
14077
|
+
"exclude_cancelled",
|
|
14078
|
+
"include_cancelled",
|
|
14079
|
+
"cancelled_only"
|
|
14080
|
+
];
|
|
14081
|
+
const BOOKING_QUESTION_ANSWER_EXPORT_SCOPES = {
|
|
14082
|
+
site: "site",
|
|
14083
|
+
appointments: "appointments",
|
|
14084
|
+
eventSeries: "event_series"
|
|
14085
|
+
};
|
|
14086
|
+
const BOOKING_QUESTION_ANSWER_EXPORT_SCOPE_VALUES = [
|
|
14087
|
+
BOOKING_QUESTION_ANSWER_EXPORT_SCOPES.site,
|
|
14088
|
+
BOOKING_QUESTION_ANSWER_EXPORT_SCOPES.appointments,
|
|
14089
|
+
BOOKING_QUESTION_ANSWER_EXPORT_SCOPES.eventSeries
|
|
14090
|
+
];
|
|
14662
14091
|
|
|
14663
14092
|
//#endregion
|
|
14664
|
-
//#region ../api/src/
|
|
14665
|
-
|
|
14666
|
-
|
|
14093
|
+
//#region ../api/src/bookingPeople.ts
|
|
14094
|
+
const PEOPLE_LIST_SORT_VALUES = [
|
|
14095
|
+
"name",
|
|
14096
|
+
"passes",
|
|
14097
|
+
"memberships",
|
|
14098
|
+
"lastBooking",
|
|
14099
|
+
"added"
|
|
14100
|
+
];
|
|
14101
|
+
const PEOPLE_LIST_FILTER_VALUES = [
|
|
14102
|
+
"everyone",
|
|
14103
|
+
"hasBought",
|
|
14104
|
+
"hasActivePass",
|
|
14105
|
+
"hasMembership",
|
|
14106
|
+
"guests",
|
|
14107
|
+
"nothingBought"
|
|
14108
|
+
];
|
|
14109
|
+
/** What can be attached to a person, in the order the dialog lists it. */
|
|
14110
|
+
const PERSON_DELETION_BLOCKER_CATEGORY_VALUES = [
|
|
14111
|
+
"bookings",
|
|
14112
|
+
"guest_places",
|
|
14113
|
+
"waitlist_places",
|
|
14114
|
+
"payments",
|
|
14115
|
+
"orders",
|
|
14116
|
+
"passes",
|
|
14117
|
+
"memberships",
|
|
14118
|
+
"credit",
|
|
14119
|
+
"digital_products",
|
|
14120
|
+
"discounts",
|
|
14121
|
+
"gifts",
|
|
14122
|
+
"notes",
|
|
14123
|
+
"forms",
|
|
14124
|
+
"terms_agreements",
|
|
14125
|
+
"uploads",
|
|
14126
|
+
"contact_corrections"
|
|
14127
|
+
];
|
|
14128
|
+
|
|
14129
|
+
//#endregion
|
|
14130
|
+
//#region ../api/src/wholeSeriesRuns.ts
|
|
14131
|
+
const DELETE_EVENT_OFFERING_CONFLICT_REASONS = [
|
|
14132
|
+
"has_registrations",
|
|
14133
|
+
"has_payments",
|
|
14134
|
+
"published_page",
|
|
14135
|
+
"inconsistent_ownership"
|
|
14136
|
+
];
|
|
14137
|
+
function isDeleteEventOfferingConflictReason(value) {
|
|
14138
|
+
return DELETE_EVENT_OFFERING_CONFLICT_REASONS.some((reason) => reason === value);
|
|
14667
14139
|
}
|
|
14668
|
-
|
|
14669
|
-
|
|
14670
|
-
|
|
14671
|
-
|
|
14672
|
-
|
|
14673
|
-
|
|
14674
|
-
if (commandLink) {
|
|
14675
|
-
commandItem.link = commandLink;
|
|
14676
|
-
}
|
|
14677
|
-
if (item.children && item.children.length > 0) {
|
|
14678
|
-
commandItem.children = item.children.map(normalizeNavigationConfigItemForCommand);
|
|
14679
|
-
}
|
|
14680
|
-
return commandItem;
|
|
14140
|
+
|
|
14141
|
+
//#endregion
|
|
14142
|
+
//#region ../api/src/bookings.ts
|
|
14143
|
+
const BOOKING_QUESTION_REQUIREMENT_SCOPES = ["before_booking_booking_level", "after_booking_due_by_participant"];
|
|
14144
|
+
function isAfterBookingQuestionRequirementScope(scope) {
|
|
14145
|
+
return scope === "after_booking_due_by_participant";
|
|
14681
14146
|
}
|
|
14682
|
-
function
|
|
14683
|
-
|
|
14684
|
-
return {
|
|
14685
|
-
label: item.label,
|
|
14686
|
-
...link$1 ? { link: link$1 } : {},
|
|
14687
|
-
...item.isCta !== undefined ? { isCta: item.isCta } : {},
|
|
14688
|
-
...item.children && item.children.length > 0 ? { children: item.children.map(navigationCommandItemToManagementInput) } : {}
|
|
14689
|
-
};
|
|
14147
|
+
function isBeforeBookingQuestionRequirementScope(scope) {
|
|
14148
|
+
return scope === "before_booking_booking_level";
|
|
14690
14149
|
}
|
|
14691
|
-
function
|
|
14692
|
-
|
|
14693
|
-
|
|
14150
|
+
async function getBookingSettingsRequest(apiClient, siteId, options) {
|
|
14151
|
+
try {
|
|
14152
|
+
const response = unwrapResponse(await apiClient({
|
|
14153
|
+
endpoint: "getBookingSettings",
|
|
14154
|
+
params: { siteId },
|
|
14155
|
+
options
|
|
14156
|
+
}));
|
|
14157
|
+
return response.settings;
|
|
14158
|
+
} catch (error) {
|
|
14159
|
+
normalizeApiError(error, "Failed to load booking settings");
|
|
14160
|
+
}
|
|
14694
14161
|
}
|
|
14695
|
-
function
|
|
14696
|
-
|
|
14697
|
-
|
|
14698
|
-
|
|
14699
|
-
|
|
14700
|
-
|
|
14701
|
-
|
|
14702
|
-
|
|
14703
|
-
|
|
14162
|
+
async function updateBookingSettingsRequest(apiClient, siteId, payload, options) {
|
|
14163
|
+
try {
|
|
14164
|
+
const response = unwrapResponse(await apiClient({
|
|
14165
|
+
endpoint: "updateBookingSettings",
|
|
14166
|
+
params: { siteId },
|
|
14167
|
+
body: payload,
|
|
14168
|
+
options
|
|
14169
|
+
}));
|
|
14170
|
+
return response.settings;
|
|
14171
|
+
} catch (error) {
|
|
14172
|
+
normalizeApiError(error, "Failed to save booking settings");
|
|
14173
|
+
}
|
|
14704
14174
|
}
|
|
14705
|
-
function
|
|
14706
|
-
|
|
14707
|
-
|
|
14708
|
-
|
|
14709
|
-
|
|
14710
|
-
|
|
14711
|
-
|
|
14712
|
-
|
|
14713
|
-
|
|
14714
|
-
|
|
14715
|
-
};
|
|
14716
|
-
case "external": return {
|
|
14717
|
-
kind: "external",
|
|
14718
|
-
href: link$1.href
|
|
14719
|
-
};
|
|
14720
|
-
case "url": return {
|
|
14721
|
-
kind: "url",
|
|
14722
|
-
href: link$1.href
|
|
14723
|
-
};
|
|
14724
|
-
case "portal": return {
|
|
14725
|
-
kind: "portal",
|
|
14726
|
-
...link$1.path ? { path: link$1.path } : {},
|
|
14727
|
-
...portalLabelsForCommand(link$1.labels)
|
|
14728
|
-
};
|
|
14729
|
-
case "cart": return {
|
|
14730
|
-
kind: "cart",
|
|
14731
|
-
badge: link$1.badge,
|
|
14732
|
-
label: link$1.label
|
|
14733
|
-
};
|
|
14734
|
-
default: return assertNever(link$1);
|
|
14175
|
+
async function createAppointmentRequest(apiClient, siteId, payload, options) {
|
|
14176
|
+
try {
|
|
14177
|
+
return unwrapResponse(await apiClient({
|
|
14178
|
+
endpoint: "createAppointment",
|
|
14179
|
+
params: { siteId },
|
|
14180
|
+
body: payload,
|
|
14181
|
+
options
|
|
14182
|
+
}));
|
|
14183
|
+
} catch (error) {
|
|
14184
|
+
normalizeApiError(error, "Failed to create appointment");
|
|
14735
14185
|
}
|
|
14736
14186
|
}
|
|
14737
|
-
function
|
|
14738
|
-
|
|
14739
|
-
|
|
14740
|
-
|
|
14741
|
-
|
|
14742
|
-
|
|
14743
|
-
|
|
14187
|
+
async function deleteEventOfferingRequest(apiClient, siteId, eventOfferingId, options) {
|
|
14188
|
+
try {
|
|
14189
|
+
return unwrapResponse(await apiClient({
|
|
14190
|
+
endpoint: "deleteEventOffering",
|
|
14191
|
+
params: {
|
|
14192
|
+
siteId,
|
|
14193
|
+
eventOfferingId
|
|
14194
|
+
},
|
|
14195
|
+
options
|
|
14196
|
+
}));
|
|
14197
|
+
} catch (error) {
|
|
14198
|
+
normalizeApiError(error, "Failed to delete event offering");
|
|
14199
|
+
}
|
|
14744
14200
|
}
|
|
14745
|
-
function
|
|
14746
|
-
|
|
14747
|
-
|
|
14748
|
-
|
|
14749
|
-
|
|
14750
|
-
|
|
14751
|
-
|
|
14752
|
-
|
|
14753
|
-
|
|
14754
|
-
|
|
14755
|
-
|
|
14756
|
-
|
|
14757
|
-
|
|
14758
|
-
case "internal": return internalNavigationLinkToLocalLink(item);
|
|
14759
|
-
case "external": return {
|
|
14760
|
-
kind: "external",
|
|
14761
|
-
href: link$1.href
|
|
14762
|
-
};
|
|
14763
|
-
case "url": return {
|
|
14764
|
-
kind: "url",
|
|
14765
|
-
href: link$1.href
|
|
14766
|
-
};
|
|
14767
|
-
case "portal": return {
|
|
14768
|
-
kind: "portal",
|
|
14769
|
-
...link$1.path ? { path: link$1.path } : {},
|
|
14770
|
-
...link$1.labels ? { labels: link$1.labels } : {}
|
|
14771
|
-
};
|
|
14772
|
-
case "cart": return {
|
|
14773
|
-
kind: "cart",
|
|
14774
|
-
badge: link$1.badge,
|
|
14775
|
-
label: link$1.label
|
|
14776
|
-
};
|
|
14777
|
-
default: return assertNever(link$1);
|
|
14201
|
+
async function addEventSeriesRegistrationRequest(apiClient, siteId, eventSeriesId, payload, options) {
|
|
14202
|
+
try {
|
|
14203
|
+
return unwrapResponse(await apiClient({
|
|
14204
|
+
endpoint: "addEventSeriesRegistration",
|
|
14205
|
+
params: {
|
|
14206
|
+
siteId,
|
|
14207
|
+
seriesId: eventSeriesId
|
|
14208
|
+
},
|
|
14209
|
+
body: payload,
|
|
14210
|
+
options
|
|
14211
|
+
}));
|
|
14212
|
+
} catch (error) {
|
|
14213
|
+
normalizeApiError(error, "Failed to add Event run registration");
|
|
14778
14214
|
}
|
|
14779
14215
|
}
|
|
14780
|
-
function
|
|
14781
|
-
|
|
14782
|
-
|
|
14783
|
-
|
|
14784
|
-
|
|
14785
|
-
|
|
14786
|
-
|
|
14787
|
-
|
|
14788
|
-
}
|
|
14789
|
-
|
|
14790
|
-
|
|
14791
|
-
|
|
14792
|
-
|
|
14793
|
-
|
|
14794
|
-
identifier: item.resolvedIdentifier
|
|
14795
|
-
};
|
|
14796
|
-
}
|
|
14216
|
+
async function updateEventSeriesRegistrationRequest(apiClient, siteId, eventSeriesId, registrationId, payload, options) {
|
|
14217
|
+
try {
|
|
14218
|
+
return unwrapResponse(await apiClient({
|
|
14219
|
+
endpoint: "updateEventSeriesRegistration",
|
|
14220
|
+
params: {
|
|
14221
|
+
siteId,
|
|
14222
|
+
seriesId: eventSeriesId,
|
|
14223
|
+
registrationId
|
|
14224
|
+
},
|
|
14225
|
+
body: payload,
|
|
14226
|
+
options
|
|
14227
|
+
}));
|
|
14228
|
+
} catch (error) {
|
|
14229
|
+
normalizeApiError(error, "Failed to update Event run registration");
|
|
14797
14230
|
}
|
|
14798
|
-
|
|
14799
|
-
|
|
14800
|
-
|
|
14801
|
-
|
|
14802
|
-
|
|
14231
|
+
}
|
|
14232
|
+
async function addEventOccurrenceAttendeeRequest(apiClient, siteId, seriesId, occurrenceId, payload, options) {
|
|
14233
|
+
try {
|
|
14234
|
+
return unwrapResponse(await apiClient({
|
|
14235
|
+
endpoint: "addEventOccurrenceAttendee",
|
|
14236
|
+
params: {
|
|
14237
|
+
siteId,
|
|
14238
|
+
seriesId,
|
|
14239
|
+
occurrenceId
|
|
14240
|
+
},
|
|
14241
|
+
body: payload,
|
|
14242
|
+
options
|
|
14243
|
+
}));
|
|
14244
|
+
} catch (error) {
|
|
14245
|
+
normalizeApiError(error, "Failed to add attendee");
|
|
14803
14246
|
}
|
|
14804
|
-
return undefined;
|
|
14805
14247
|
}
|
|
14806
14248
|
|
|
14807
14249
|
//#endregion
|
|
14808
|
-
//#region
|
|
14809
|
-
|
|
14810
|
-
|
|
14811
|
-
* Falls back to an internal route payload for legacy rows or incomplete data.
|
|
14812
|
-
*/
|
|
14813
|
-
function toRoutableLinkPayload(item) {
|
|
14814
|
-
switch (item.entityType) {
|
|
14815
|
-
case "page":
|
|
14816
|
-
if (item.identifier) {
|
|
14817
|
-
return {
|
|
14818
|
-
kind: "page",
|
|
14819
|
-
identifier: item.identifier
|
|
14820
|
-
};
|
|
14821
|
-
}
|
|
14822
|
-
return toResolvedInternalPayload(item);
|
|
14823
|
-
case "content":
|
|
14824
|
-
if (item.identifier && item.contentTypeKey) {
|
|
14825
|
-
return {
|
|
14826
|
-
kind: "entry",
|
|
14827
|
-
contentType: item.contentTypeKey,
|
|
14828
|
-
identifier: item.identifier
|
|
14829
|
-
};
|
|
14830
|
-
}
|
|
14831
|
-
return toResolvedInternalPayload(item);
|
|
14832
|
-
case "pageSurface": return toResolvedInternalPayload(item);
|
|
14833
|
-
default: return assertNever(item);
|
|
14834
|
-
}
|
|
14250
|
+
//#region src/push-config-sync.ts
|
|
14251
|
+
function isConfigSync(sync) {
|
|
14252
|
+
return "ignoredDocumentSections" in sync;
|
|
14835
14253
|
}
|
|
14836
|
-
function
|
|
14837
|
-
return
|
|
14838
|
-
|
|
14839
|
-
|
|
14840
|
-
entityId: item.entityId,
|
|
14841
|
-
entityType: item.entityType,
|
|
14842
|
-
href: item.path ?? "/",
|
|
14843
|
-
title: item.title ?? item.draftTitle ?? "Untitled",
|
|
14844
|
-
typeLabel: item.typeLabel,
|
|
14845
|
-
contentTypeKey: item.contentTypeKey,
|
|
14846
|
-
contentTypeName: item.contentTypeName,
|
|
14847
|
-
updatedAt: item.updatedAt ?? null
|
|
14848
|
-
};
|
|
14254
|
+
function getPushConfigSync(result) {
|
|
14255
|
+
if (result.configSync) return result.configSync;
|
|
14256
|
+
if (result.sync) return result.sync;
|
|
14257
|
+
return null;
|
|
14849
14258
|
}
|
|
14850
|
-
|
|
14851
|
-
|
|
14852
|
-
|
|
14853
|
-
|
|
14854
|
-
|
|
14855
|
-
|
|
14856
|
-
|
|
14857
|
-
|
|
14858
|
-
|
|
14859
|
-
|
|
14860
|
-
|
|
14861
|
-
|
|
14862
|
-
|
|
14863
|
-
|
|
14259
|
+
function collectPushConfigSyncErrors(sync) {
|
|
14260
|
+
return [
|
|
14261
|
+
...sync.contentTypes.errors.map((message) => ({
|
|
14262
|
+
area: "contentTypes",
|
|
14263
|
+
message
|
|
14264
|
+
})),
|
|
14265
|
+
...sync.customBlocks.errors.map((message) => ({
|
|
14266
|
+
area: "customBlocks",
|
|
14267
|
+
message
|
|
14268
|
+
})),
|
|
14269
|
+
...sync.templateBlocks.errors.map((message) => ({
|
|
14270
|
+
area: "templateBlocks",
|
|
14271
|
+
message
|
|
14272
|
+
})),
|
|
14273
|
+
...sync.settings.error ? [{
|
|
14274
|
+
area: "settings",
|
|
14275
|
+
message: sync.settings.error
|
|
14276
|
+
}] : [],
|
|
14277
|
+
...!isConfigSync(sync) ? sync.pages.errors.map((message) => ({
|
|
14278
|
+
area: "pages",
|
|
14279
|
+
message
|
|
14280
|
+
})) : [],
|
|
14281
|
+
...!isConfigSync(sync) ? sync.entries.errors.map((message) => ({
|
|
14282
|
+
area: "entries",
|
|
14283
|
+
message
|
|
14284
|
+
})) : [],
|
|
14285
|
+
...!isConfigSync(sync) ? sync.navigationMenus.errors.map((message) => ({
|
|
14286
|
+
area: "navigationMenus",
|
|
14287
|
+
message
|
|
14288
|
+
})) : []
|
|
14289
|
+
];
|
|
14864
14290
|
}
|
|
14865
|
-
|
|
14866
|
-
|
|
14867
|
-
|
|
14868
|
-
|
|
14869
|
-
|
|
14870
|
-
|
|
14871
|
-
|
|
14291
|
+
function collectPushConfigSyncWarnings(sync) {
|
|
14292
|
+
return [
|
|
14293
|
+
...sync.contentTypes.warnings.map((message) => ({
|
|
14294
|
+
area: "contentTypes",
|
|
14295
|
+
message
|
|
14296
|
+
})),
|
|
14297
|
+
...sync.customBlocks.warnings.map((message) => ({
|
|
14298
|
+
area: "customBlocks",
|
|
14299
|
+
message
|
|
14300
|
+
})),
|
|
14301
|
+
...sync.templateBlocks.warnings.map((message) => ({
|
|
14302
|
+
area: "templateBlocks",
|
|
14303
|
+
message
|
|
14304
|
+
})),
|
|
14305
|
+
...isConfigSync(sync) ? sync.ignoredDocumentSections.map((section) => ({
|
|
14306
|
+
area: "ignoredDocuments",
|
|
14307
|
+
message: formatIgnoredDocumentSyncSectionWarning(section)
|
|
14308
|
+
})) : [
|
|
14309
|
+
...sync.pages.warnings.map((message) => ({
|
|
14310
|
+
area: "pages",
|
|
14311
|
+
message
|
|
14312
|
+
})),
|
|
14313
|
+
...sync.entries.warnings.map((message) => ({
|
|
14314
|
+
area: "entries",
|
|
14315
|
+
message
|
|
14316
|
+
})),
|
|
14317
|
+
...sync.navigationMenus.warnings.map((message) => ({
|
|
14318
|
+
area: "navigationMenus",
|
|
14319
|
+
message
|
|
14320
|
+
}))
|
|
14321
|
+
]
|
|
14322
|
+
];
|
|
14323
|
+
}
|
|
14324
|
+
function groupPushConfigSyncWarningsForHuman(warnings) {
|
|
14325
|
+
const ignoredDocumentCount = warnings.filter((warning) => warning.area === "ignoredDocuments").length;
|
|
14326
|
+
return [...warnings.filter((warning) => warning.area !== "ignoredDocuments"), ...ignoredDocumentCount > 0 ? [{
|
|
14327
|
+
area: "ignoredDocuments",
|
|
14328
|
+
message: `${ignoredDocumentCount} document content section${ignoredDocumentCount === 1 ? "" : "s"} ignored by push-config. Use \`riverbankcms push\` for pages, entries, and navigation content.`
|
|
14329
|
+
}] : []];
|
|
14330
|
+
}
|
|
14331
|
+
function buildPreviewSummary(sync) {
|
|
14332
|
+
return { wouldConsider: {
|
|
14333
|
+
contentTypes: sync.contentTypes.skipped,
|
|
14334
|
+
customBlocks: sync.customBlocks.skipped,
|
|
14335
|
+
templateBlocks: sync.templateBlocks.skipped,
|
|
14336
|
+
settings: sync.settings.updated,
|
|
14337
|
+
...isConfigSync(sync) ? { footerBlocks: sync.footerBlocks.skipped } : {},
|
|
14338
|
+
...isConfigSync(sync) ? { ignoredDocumentSections: sync.ignoredDocumentSections } : {}
|
|
14339
|
+
} };
|
|
14872
14340
|
}
|
|
14873
|
-
const managedDomainAddOnPricingSnapshotSchema = z.object({
|
|
14874
|
-
formulaVersion: z.literal("managed-domain-overage-v1"),
|
|
14875
|
-
conversionRule: z.literal("usd-to-gbp-1-to-1-v1"),
|
|
14876
|
-
registrarAnnualCost: z.number().nonnegative(),
|
|
14877
|
-
registrarCurrency: z.literal("USD"),
|
|
14878
|
-
includedAnnualAllowance: z.number().nonnegative(),
|
|
14879
|
-
includedAnnualAllowanceCurrency: z.literal("USD"),
|
|
14880
|
-
monthlyAmountCents: z.number().int().nonnegative(),
|
|
14881
|
-
monthlyCurrency: z.literal("gbp")
|
|
14882
|
-
});
|
|
14883
14341
|
|
|
14884
14342
|
//#endregion
|
|
14885
|
-
//#region
|
|
14343
|
+
//#region src/push-config.ts
|
|
14886
14344
|
/**
|
|
14887
|
-
*
|
|
14345
|
+
* push-config CLI command
|
|
14346
|
+
*
|
|
14347
|
+
* Pushes SDK site configuration from a riverbank.config.ts file to the dashboard API.
|
|
14888
14348
|
*/
|
|
14889
|
-
const
|
|
14890
|
-
|
|
14891
|
-
|
|
14892
|
-
|
|
14893
|
-
|
|
14894
|
-
|
|
14349
|
+
const DEFAULT_CONFIG_PATH = "./riverbank.config.ts";
|
|
14350
|
+
function formatPushConfigActionFailure() {
|
|
14351
|
+
return "push-config failed";
|
|
14352
|
+
}
|
|
14353
|
+
function isRecord$6(value) {
|
|
14354
|
+
return typeof value === "object" && value !== null;
|
|
14355
|
+
}
|
|
14356
|
+
function parseErrorDetails(value) {
|
|
14357
|
+
if (!value) return {};
|
|
14358
|
+
if (typeof value === "string") {
|
|
14359
|
+
return { message: value };
|
|
14360
|
+
}
|
|
14361
|
+
if (!isRecord$6(value)) return {};
|
|
14362
|
+
const message = typeof value.message === "string" ? value.message : undefined;
|
|
14363
|
+
const fieldErrorsRaw = value.fieldErrors;
|
|
14364
|
+
const fieldErrors = Array.isArray(fieldErrorsRaw) ? fieldErrorsRaw.filter(isRecord$6).map((d) => ({
|
|
14365
|
+
field: typeof d.field === "string" ? d.field : "",
|
|
14366
|
+
message: typeof d.message === "string" ? d.message : ""
|
|
14367
|
+
})).filter((d) => d.field.length > 0 && d.message.length > 0) : undefined;
|
|
14368
|
+
return {
|
|
14369
|
+
message,
|
|
14370
|
+
fieldErrors
|
|
14371
|
+
};
|
|
14372
|
+
}
|
|
14373
|
+
function formatFieldErrors(fieldErrors) {
|
|
14374
|
+
if (!fieldErrors || fieldErrors.length === 0) return "";
|
|
14375
|
+
return ":\n" + fieldErrors.map((d) => ` - ${d.field}: ${d.message}`).join("\n");
|
|
14376
|
+
}
|
|
14377
|
+
function getDashboardErrorMessage(responseJson, fallback) {
|
|
14378
|
+
if (!responseJson) return fallback;
|
|
14379
|
+
if (isRecord$6(responseJson) && "error" in responseJson) {
|
|
14380
|
+
const parsed = parseErrorDetails(responseJson.error);
|
|
14381
|
+
const msg = parsed.message ?? fallback;
|
|
14382
|
+
return msg + formatFieldErrors(parsed.fieldErrors);
|
|
14383
|
+
}
|
|
14384
|
+
return fallback;
|
|
14385
|
+
}
|
|
14895
14386
|
/**
|
|
14896
|
-
*
|
|
14387
|
+
* Push config to dashboard API
|
|
14897
14388
|
*/
|
|
14898
|
-
|
|
14899
|
-
|
|
14900
|
-
|
|
14901
|
-
|
|
14902
|
-
|
|
14903
|
-
|
|
14904
|
-
|
|
14389
|
+
async function pushToDashboard(dashboardUrl, siteId, apiKey, config, options) {
|
|
14390
|
+
const normalizedDashboardUrl = dashboardUrl.replace(/\/+$/, "");
|
|
14391
|
+
const baseUrl = `${normalizedDashboardUrl}/api`;
|
|
14392
|
+
const path$3 = `/sites/${siteId}/sdk-config`;
|
|
14393
|
+
const pushUrl = `${baseUrl}${path$3}`;
|
|
14394
|
+
const requestInfo = {
|
|
14395
|
+
method: "POST",
|
|
14396
|
+
intent: "write",
|
|
14397
|
+
path: path$3,
|
|
14398
|
+
url: pushUrl,
|
|
14399
|
+
baseUrl,
|
|
14400
|
+
dashboardUrl: normalizedDashboardUrl,
|
|
14401
|
+
siteId
|
|
14402
|
+
};
|
|
14403
|
+
const client = createBearerAPIClient(exposeSecret(apiKey), baseUrl);
|
|
14404
|
+
const timeoutMs = currentRequestTimeoutMs() ?? 3e4;
|
|
14905
14405
|
try {
|
|
14906
|
-
return unwrapResponse(await
|
|
14907
|
-
endpoint: "
|
|
14406
|
+
return unwrapResponse(await client({
|
|
14407
|
+
endpoint: "pushSdkConfig",
|
|
14908
14408
|
params: { siteId },
|
|
14909
|
-
|
|
14409
|
+
body: {
|
|
14410
|
+
config,
|
|
14411
|
+
forceContentTypes: options?.forceContentTypes ?? false,
|
|
14412
|
+
mode: options?.dryRun ? "preview" : "apply",
|
|
14413
|
+
expectedMailingListProviderScope: options?.expectedMailingListProviderScope ?? undefined
|
|
14414
|
+
},
|
|
14415
|
+
options: { signal: AbortSignal.timeout(timeoutMs) }
|
|
14910
14416
|
}));
|
|
14911
14417
|
} catch (error) {
|
|
14912
|
-
|
|
14418
|
+
if (error instanceof ApiEnvelopeError) {
|
|
14419
|
+
const details = {
|
|
14420
|
+
success: false,
|
|
14421
|
+
error: {
|
|
14422
|
+
code: error.code,
|
|
14423
|
+
message: error.message,
|
|
14424
|
+
fieldErrors: error.fieldErrors
|
|
14425
|
+
}
|
|
14426
|
+
};
|
|
14427
|
+
const errorMessage = getDashboardErrorMessage(details, error.message);
|
|
14428
|
+
throw new ManagementApiError(errorMessage, "sdk:http-error", details, error.status, requestInfo);
|
|
14429
|
+
}
|
|
14430
|
+
if (error instanceof ApiRequestError) {
|
|
14431
|
+
const errorMessage = getDashboardErrorMessage(error.body, `Dashboard returned ${error.status}`);
|
|
14432
|
+
throw new ManagementApiError(errorMessage, "sdk:http-error", error.body, error.status, requestInfo);
|
|
14433
|
+
}
|
|
14434
|
+
throw managementTransportError(error, timeoutMs, requestInfo);
|
|
14913
14435
|
}
|
|
14914
14436
|
}
|
|
14915
|
-
|
|
14916
|
-
|
|
14917
|
-
|
|
14918
|
-
|
|
14919
|
-
|
|
14920
|
-
|
|
14921
|
-
|
|
14922
|
-
|
|
14923
|
-
|
|
14924
|
-
return normalizeApiError(error, "Failed to invite collaborator");
|
|
14437
|
+
function validatePushConfig(rawConfig) {
|
|
14438
|
+
const parsed = riverbankSiteConfigSchema.safeParse(rawConfig);
|
|
14439
|
+
if (!parsed.success) {
|
|
14440
|
+
const issues = parsed.error.issues.map((issue) => `${issue.path.join(".")}: ${issue.message}`).join("; ");
|
|
14441
|
+
throw new Error(`Invalid config: ${issues}`);
|
|
14442
|
+
}
|
|
14443
|
+
const conflicts = validateFieldIdConflicts(parsed.data.blockFieldExtensions);
|
|
14444
|
+
if (conflicts.length > 0) {
|
|
14445
|
+
throw new Error(`Field ID conflicts detected in blockFieldExtensions: ${conflicts.map((conflict) => conflict.message).join("; ")}`);
|
|
14925
14446
|
}
|
|
14447
|
+
return parsed.data;
|
|
14926
14448
|
}
|
|
14927
|
-
async function
|
|
14928
|
-
|
|
14929
|
-
|
|
14930
|
-
|
|
14931
|
-
|
|
14932
|
-
|
|
14933
|
-
|
|
14934
|
-
|
|
14935
|
-
|
|
14936
|
-
|
|
14937
|
-
}
|
|
14938
|
-
} catch (error) {
|
|
14939
|
-
return normalizeApiError(error, "Failed to update member role");
|
|
14449
|
+
async function loadValidatedPushConfig(target, configPath) {
|
|
14450
|
+
return validatePushConfig(await withRiverbankEnv(target, () => loadConfigFile(configPath)));
|
|
14451
|
+
}
|
|
14452
|
+
function analyzePushConfigResponse(response, dryRun) {
|
|
14453
|
+
const resultMode = response.mode ?? "apply";
|
|
14454
|
+
if (dryRun && resultMode !== "preview") {
|
|
14455
|
+
return {
|
|
14456
|
+
success: false,
|
|
14457
|
+
kind: "unsafe-preview",
|
|
14458
|
+
receivedMode: response.mode
|
|
14459
|
+
};
|
|
14940
14460
|
}
|
|
14461
|
+
const sync = getPushConfigSync(response);
|
|
14462
|
+
if (!sync) return {
|
|
14463
|
+
success: true,
|
|
14464
|
+
resultMode,
|
|
14465
|
+
sync: null,
|
|
14466
|
+
warnings: []
|
|
14467
|
+
};
|
|
14468
|
+
const errors = collectPushConfigSyncErrors(sync);
|
|
14469
|
+
if (errors.length > 0 || sync.success === false) return {
|
|
14470
|
+
success: false,
|
|
14471
|
+
kind: "sync-errors",
|
|
14472
|
+
sync,
|
|
14473
|
+
errors
|
|
14474
|
+
};
|
|
14475
|
+
return {
|
|
14476
|
+
success: true,
|
|
14477
|
+
resultMode,
|
|
14478
|
+
sync,
|
|
14479
|
+
warnings: collectPushConfigSyncWarnings(sync)
|
|
14480
|
+
};
|
|
14941
14481
|
}
|
|
14942
|
-
|
|
14943
|
-
|
|
14944
|
-
|
|
14945
|
-
|
|
14946
|
-
|
|
14947
|
-
|
|
14948
|
-
|
|
14949
|
-
|
|
14950
|
-
|
|
14951
|
-
|
|
14482
|
+
function renderPushConfigResponse(output, response, mode, dryRun) {
|
|
14483
|
+
const analysis = analyzePushConfigResponse(response, dryRun);
|
|
14484
|
+
if (!analysis.success) {
|
|
14485
|
+
if (analysis.kind === "unsafe-preview") {
|
|
14486
|
+
output.fail("Config preview was not confirmed by the dashboard; no safe dry-run result is available.", {
|
|
14487
|
+
expectedMode: "preview",
|
|
14488
|
+
receivedMode: analysis.receivedMode ?? null,
|
|
14489
|
+
suggestion: "Update the dashboard API before relying on push-config --dry-run."
|
|
14490
|
+
});
|
|
14491
|
+
return { success: false };
|
|
14492
|
+
}
|
|
14493
|
+
if (mode === "json") {
|
|
14494
|
+
output.markFailed();
|
|
14495
|
+
output.json({
|
|
14496
|
+
success: false,
|
|
14497
|
+
sync: analysis.sync,
|
|
14498
|
+
errors: analysis.errors
|
|
14499
|
+
});
|
|
14500
|
+
} else {
|
|
14501
|
+
output.markFailed();
|
|
14502
|
+
output.fail("Config pushed, but SDK config sync reported errors", {
|
|
14503
|
+
errors: analysis.errors,
|
|
14504
|
+
summary: analysis.sync
|
|
14505
|
+
});
|
|
14506
|
+
}
|
|
14507
|
+
return { success: false };
|
|
14508
|
+
}
|
|
14509
|
+
if (mode === "human" && analysis.warnings.length > 0) {
|
|
14510
|
+
output.warn("SDK config sync reported warnings", { warnings: groupPushConfigSyncWarningsForHuman(analysis.warnings) });
|
|
14511
|
+
}
|
|
14512
|
+
if (mode === "human" && analysis.sync) {
|
|
14513
|
+
output.info(analysis.resultMode === "preview" ? "SDK config preview summary" : "SDK config sync summary", analysis.resultMode === "preview" ? buildPreviewSummary(analysis.sync) : analysis.sync);
|
|
14514
|
+
}
|
|
14515
|
+
if (mode === "json") {
|
|
14516
|
+
output.json({
|
|
14517
|
+
success: true,
|
|
14518
|
+
...analysis.resultMode === "preview" ? {
|
|
14519
|
+
dryRun: true,
|
|
14520
|
+
mode: analysis.resultMode
|
|
14521
|
+
} : {},
|
|
14522
|
+
sync: analysis.sync
|
|
14523
|
+
});
|
|
14524
|
+
} else {
|
|
14525
|
+
output.success(analysis.resultMode === "preview" ? "Config preview completed. No changes were applied." : "Config pushed successfully!");
|
|
14952
14526
|
}
|
|
14527
|
+
return { success: true };
|
|
14953
14528
|
}
|
|
14954
|
-
|
|
14529
|
+
/** Main push-config action, retained for `push --with-config`. */
|
|
14530
|
+
async function pushConfigAction(output, options) {
|
|
14531
|
+
const target = options.target;
|
|
14955
14532
|
try {
|
|
14956
|
-
|
|
14957
|
-
|
|
14958
|
-
|
|
14959
|
-
|
|
14960
|
-
|
|
14533
|
+
if (!options.isJsonOutput) {
|
|
14534
|
+
output.info("push-config target", {
|
|
14535
|
+
env: target,
|
|
14536
|
+
dashboardUrl: options.dashboard,
|
|
14537
|
+
configPath: options.config ?? DEFAULT_CONFIG_PATH
|
|
14538
|
+
});
|
|
14539
|
+
output.info("Validating config...");
|
|
14540
|
+
}
|
|
14541
|
+
const config = await loadValidatedPushConfig(target, options.config);
|
|
14542
|
+
const apiKey = resolveManagementApiKey(output, options.apiKey, target);
|
|
14543
|
+
const response = await pushToDashboard(options.dashboard, config.siteId, apiKey, config, {
|
|
14544
|
+
forceContentTypes: options.forceContentTypes ?? false,
|
|
14545
|
+
dryRun: options.dryRun ?? false
|
|
14546
|
+
});
|
|
14547
|
+
return renderPushConfigResponse(output, response, options.isJsonOutput ? "json" : "human", options.dryRun ?? false);
|
|
14961
14548
|
} catch (error) {
|
|
14962
|
-
|
|
14549
|
+
handleCommandError(error, output, {
|
|
14550
|
+
target,
|
|
14551
|
+
dashboardUrl: options.dashboard
|
|
14552
|
+
});
|
|
14553
|
+
return { success: false };
|
|
14554
|
+
}
|
|
14555
|
+
}
|
|
14556
|
+
function resolveDashboardUrlValue(cliOption, target) {
|
|
14557
|
+
const envVar = getEnvVarName("DASHBOARD_URL", target);
|
|
14558
|
+
const url = cliOption || process.env[envVar];
|
|
14559
|
+
if (!url) {
|
|
14560
|
+
return {
|
|
14561
|
+
ok: false,
|
|
14562
|
+
error: {
|
|
14563
|
+
message: "Dashboard URL is required",
|
|
14564
|
+
details: { suggestion: `Provide --dashboard <url> or set ${envVar} environment variable.` }
|
|
14565
|
+
}
|
|
14566
|
+
};
|
|
14567
|
+
}
|
|
14568
|
+
return {
|
|
14569
|
+
ok: true,
|
|
14570
|
+
value: url
|
|
14571
|
+
};
|
|
14572
|
+
}
|
|
14573
|
+
/**
|
|
14574
|
+
* Resolve management API key from CLI option or environment variable.
|
|
14575
|
+
*/
|
|
14576
|
+
function resolveManagementApiKeyValue(cliOption, target) {
|
|
14577
|
+
const envVar = getEnvVarName("MGMT_API_KEY", target);
|
|
14578
|
+
const apiKey = cliOption || process.env[envVar];
|
|
14579
|
+
if (!apiKey) {
|
|
14580
|
+
return {
|
|
14581
|
+
ok: false,
|
|
14582
|
+
error: {
|
|
14583
|
+
message: "Management API key is required",
|
|
14584
|
+
details: { suggestion: `Provide --api-key <key> or set ${envVar} environment variable.` }
|
|
14585
|
+
}
|
|
14586
|
+
};
|
|
14587
|
+
}
|
|
14588
|
+
const parsedApiKey = parseManagementKey(apiKey);
|
|
14589
|
+
if (!parsedApiKey.ok) {
|
|
14590
|
+
return {
|
|
14591
|
+
ok: false,
|
|
14592
|
+
error: {
|
|
14593
|
+
message: "Invalid management API key format",
|
|
14594
|
+
details: {
|
|
14595
|
+
expected: "Key starting with bld_mgmt_sk_",
|
|
14596
|
+
suggestion: `Check your ${envVar} environment variable.`
|
|
14597
|
+
}
|
|
14598
|
+
}
|
|
14599
|
+
};
|
|
14963
14600
|
}
|
|
14601
|
+
return {
|
|
14602
|
+
ok: true,
|
|
14603
|
+
value: parsedApiKey.value
|
|
14604
|
+
};
|
|
14964
14605
|
}
|
|
14965
|
-
|
|
14966
|
-
|
|
14967
|
-
|
|
14968
|
-
|
|
14969
|
-
params: {
|
|
14970
|
-
siteId,
|
|
14971
|
-
memberId
|
|
14972
|
-
},
|
|
14973
|
-
options
|
|
14974
|
-
}));
|
|
14975
|
-
} catch (error) {
|
|
14976
|
-
return normalizeApiError(error, "Failed to remove collaborator");
|
|
14977
|
-
}
|
|
14606
|
+
function resolveManagementApiKey(output, cliOption, target) {
|
|
14607
|
+
const result = resolveManagementApiKeyValue(cliOption, target);
|
|
14608
|
+
if (!result.ok) output.error(result.error.message, result.error.details);
|
|
14609
|
+
return result.value;
|
|
14978
14610
|
}
|
|
14979
|
-
|
|
14980
|
-
|
|
14981
|
-
|
|
14982
|
-
|
|
14983
|
-
params: {
|
|
14984
|
-
siteId,
|
|
14985
|
-
invitationId
|
|
14986
|
-
},
|
|
14987
|
-
options
|
|
14988
|
-
}));
|
|
14989
|
-
} catch (error) {
|
|
14990
|
-
return normalizeApiError(error, "Failed to revoke invitation");
|
|
14991
|
-
}
|
|
14611
|
+
function unwrapPushConfigTargetValue(result) {
|
|
14612
|
+
if (result.ok) return result.value;
|
|
14613
|
+
const suggestion = result.error.details.suggestion;
|
|
14614
|
+
throw new Error(`${result.error.message}${typeof suggestion === "string" ? `. ${suggestion}` : ""}`);
|
|
14992
14615
|
}
|
|
14993
|
-
async function
|
|
14994
|
-
|
|
14995
|
-
|
|
14996
|
-
|
|
14997
|
-
|
|
14998
|
-
|
|
14999
|
-
invitationId
|
|
15000
|
-
},
|
|
15001
|
-
options
|
|
15002
|
-
}));
|
|
15003
|
-
} catch (error) {
|
|
15004
|
-
return normalizeApiError(error, "Failed to resend invitation");
|
|
15005
|
-
}
|
|
14616
|
+
async function loadPushConfigTarget(target, options) {
|
|
14617
|
+
return {
|
|
14618
|
+
target,
|
|
14619
|
+
dashboard: unwrapPushConfigTargetValue(resolveDashboardUrlValue(options.dashboard, target)),
|
|
14620
|
+
apiKey: unwrapPushConfigTargetValue(resolveManagementApiKeyValue(options.apiKey, target))
|
|
14621
|
+
};
|
|
15006
14622
|
}
|
|
15007
|
-
async function
|
|
15008
|
-
|
|
15009
|
-
|
|
15010
|
-
|
|
15011
|
-
|
|
15012
|
-
|
|
15013
|
-
|
|
15014
|
-
})
|
|
15015
|
-
} catch (error) {
|
|
15016
|
-
return normalizeApiError(error, "Failed to transfer ownership");
|
|
14623
|
+
async function preparePushConfigTarget(context, options) {
|
|
14624
|
+
const config = await loadValidatedPushConfig(context.target, options.config);
|
|
14625
|
+
const preview = await pushToDashboard(context.dashboard, config.siteId, context.apiKey, config, {
|
|
14626
|
+
forceContentTypes: options.forceContentTypes ?? false,
|
|
14627
|
+
dryRun: true
|
|
14628
|
+
});
|
|
14629
|
+
if (!analyzePushConfigResponse(preview, true).success) {
|
|
14630
|
+
throw new Error(`Config preview failed for ${context.target} environment.`);
|
|
15017
14631
|
}
|
|
14632
|
+
return {
|
|
14633
|
+
config,
|
|
14634
|
+
preview,
|
|
14635
|
+
forceContentTypes: options.forceContentTypes ?? false
|
|
14636
|
+
};
|
|
15018
14637
|
}
|
|
15019
|
-
|
|
14638
|
+
/**
|
|
14639
|
+
* Re-preview a prepared config value without reloading the local config file.
|
|
14640
|
+
* The caller uses this immediately before applying a multi-target plan so a
|
|
14641
|
+
* dashboard-side schema change is classified as stale before any mutation.
|
|
14642
|
+
*/
|
|
14643
|
+
async function revalidatePushConfigPlan(context, plan) {
|
|
15020
14644
|
try {
|
|
15021
|
-
|
|
15022
|
-
|
|
15023
|
-
|
|
15024
|
-
|
|
15025
|
-
|
|
14645
|
+
const current = await pushToDashboard(context.dashboard, plan.config.siteId, context.apiKey, plan.config, {
|
|
14646
|
+
forceContentTypes: plan.forceContentTypes,
|
|
14647
|
+
dryRun: true
|
|
14648
|
+
});
|
|
14649
|
+
return stableStringify(current) === stableStringify(plan.preview) ? { ok: true } : {
|
|
14650
|
+
ok: false,
|
|
14651
|
+
error: new Error("SDK config changed after preflight; the push plan is stale.")
|
|
14652
|
+
};
|
|
15026
14653
|
} catch (error) {
|
|
15027
|
-
return
|
|
14654
|
+
return {
|
|
14655
|
+
ok: false,
|
|
14656
|
+
error: error instanceof Error ? error : new Error(String(error))
|
|
14657
|
+
};
|
|
15028
14658
|
}
|
|
15029
14659
|
}
|
|
15030
|
-
|
|
15031
|
-
|
|
15032
|
-
|
|
15033
|
-
|
|
15034
|
-
|
|
14660
|
+
async function applyPushConfigPlan(output, context, plan, options, isJsonOutput) {
|
|
14661
|
+
const response = options.dryRun ? plan.preview : await pushToDashboard(context.dashboard, plan.config.siteId, context.apiKey, plan.config, {
|
|
14662
|
+
forceContentTypes: plan.forceContentTypes,
|
|
14663
|
+
dryRun: false,
|
|
14664
|
+
expectedMailingListProviderScope: plan.preview.mailingListProviderScope
|
|
14665
|
+
});
|
|
14666
|
+
return {
|
|
14667
|
+
response,
|
|
14668
|
+
action: isJsonOutput ? { success: analyzePushConfigResponse(response, options.dryRun ?? false).success } : renderPushConfigResponse(output, response, "human", options.dryRun ?? false)
|
|
14669
|
+
};
|
|
15035
14670
|
}
|
|
15036
|
-
function
|
|
15037
|
-
|
|
15038
|
-
return roleAtLeast$1(role, minimum);
|
|
14671
|
+
function isPushConfigApplySuccess(result) {
|
|
14672
|
+
return result.action.success;
|
|
15039
14673
|
}
|
|
15040
|
-
function
|
|
15041
|
-
|
|
14674
|
+
function renderPushConfigJsonResult(output, execution, dryRun) {
|
|
14675
|
+
output.json({
|
|
14676
|
+
success: execution.succeeded,
|
|
14677
|
+
dryRun,
|
|
14678
|
+
environments: mapEnvironmentExecutionOutcomesJson(execution, ({ result }) => ({
|
|
14679
|
+
response: result.response,
|
|
14680
|
+
success: true
|
|
14681
|
+
}), ({ result, message }) => ({
|
|
14682
|
+
response: result.response,
|
|
14683
|
+
success: false,
|
|
14684
|
+
error: message
|
|
14685
|
+
}))
|
|
14686
|
+
});
|
|
15042
14687
|
}
|
|
15043
|
-
function
|
|
15044
|
-
|
|
15045
|
-
|
|
14688
|
+
function getProductionPushConfigCommandDeps() {
|
|
14689
|
+
return {
|
|
14690
|
+
getOutputContext,
|
|
14691
|
+
planEnvironmentExecution,
|
|
14692
|
+
executeEnvironmentPlan,
|
|
14693
|
+
confirmAction,
|
|
14694
|
+
loadTarget: loadPushConfigTarget,
|
|
14695
|
+
prepareTarget: preparePushConfigTarget,
|
|
14696
|
+
revalidatePlan: revalidatePushConfigPlan,
|
|
14697
|
+
applyPlan: applyPushConfigPlan
|
|
14698
|
+
};
|
|
15046
14699
|
}
|
|
15047
|
-
|
|
15048
|
-
|
|
15049
|
-
|
|
15050
|
-
|
|
15051
|
-
|
|
15052
|
-
|
|
15053
|
-
|
|
15054
|
-
|
|
15055
|
-
|
|
15056
|
-
|
|
15057
|
-
|
|
15058
|
-
|
|
15059
|
-
|
|
15060
|
-
|
|
14700
|
+
/**
|
|
14701
|
+
* push-config command definition
|
|
14702
|
+
*/
|
|
14703
|
+
const createPushConfigCommand = (deps = getProductionPushConfigCommandDeps()) => new Command("push-config").description("Push SDK config to dashboard").option("--api-key <key>", "Management API key (or set RIVERBANK_*_MGMT_API_KEY)").option("--dashboard <url>", "Dashboard URL (or set RIVERBANK_*_DASHBOARD_URL env var)").option("--config <path>", `Path to config file (default: ${DEFAULT_CONFIG_PATH})`).option("--force-content-types", "Force overwrite content type schema even if modified in dashboard after last SDK sync").option("--dry-run", "Preview SDK config changes without updating the dashboard").option("--yes", "Confirm applying config changes across all selected environments").addHelpText("after", `
|
|
14704
|
+
Description:
|
|
14705
|
+
Syncs your local riverbank.config.ts schema/config to the CMS dashboard, including:
|
|
14706
|
+
- Custom blocks
|
|
14707
|
+
- Block field extensions
|
|
14708
|
+
- Block field options
|
|
14709
|
+
- Content type definitions and templates
|
|
14710
|
+
- SDK-managed site settings and footer blocks
|
|
14711
|
+
|
|
14712
|
+
Pages, content entries, and navigation menus are content documents.
|
|
14713
|
+
Sync them with "riverbankcms push pages", "riverbankcms push entries",
|
|
14714
|
+
or "riverbankcms push navigation".
|
|
14715
|
+
|
|
14716
|
+
Examples:
|
|
14717
|
+
$ pnpm exec riverbankcms push-config
|
|
14718
|
+
$ pnpm exec riverbankcms push-config --dry-run
|
|
14719
|
+
$ pnpm exec riverbankcms push-config --env=remote
|
|
14720
|
+
$ pnpm exec riverbankcms push-config --api-key bld_mgmt_sk_... --dashboard https://www.riverbankcms.com
|
|
14721
|
+
$ pnpm exec riverbankcms push-config --config ./src/riverbank.config.ts
|
|
14722
|
+
`).action(async (options, command) => {
|
|
14723
|
+
const { output, selection, globalOpts } = deps.getOutputContext(command);
|
|
14724
|
+
const isJsonOutput = globalOpts.json ?? false;
|
|
14725
|
+
const authorization = requireEnvironmentWriteAuthorization({
|
|
14726
|
+
...options,
|
|
14727
|
+
json: isJsonOutput,
|
|
14728
|
+
interactive: process.stdin.isTTY === true
|
|
14729
|
+
}, output);
|
|
14730
|
+
if (!authorization) return;
|
|
14731
|
+
const planned = deps.planEnvironmentExecution({
|
|
14732
|
+
command: "push-config",
|
|
14733
|
+
selection: "both",
|
|
14734
|
+
failure: "abort",
|
|
14735
|
+
confirmation: "command",
|
|
14736
|
+
revalidation: "required"
|
|
14737
|
+
}, selection);
|
|
14738
|
+
if (!planned.ok) return output.error(planned.error.message, { suggestion: planned.error.suggestion });
|
|
14739
|
+
const execution = await deps.executeEnvironmentPlan(planned.value, {
|
|
14740
|
+
loadTarget: (target) => deps.loadTarget(target, options),
|
|
14741
|
+
preflight: async (_target, loaded) => deps.prepareTarget(loaded.context, {
|
|
14742
|
+
...options,
|
|
14743
|
+
dashboard: loaded.context.dashboard
|
|
14744
|
+
}),
|
|
14745
|
+
confirm: async ({ ready }) => {
|
|
14746
|
+
if (authorization.kind === "preauthorized") return true;
|
|
14747
|
+
for (const { target, context } of ready) {
|
|
14748
|
+
const sync = getPushConfigSync(context.preview);
|
|
14749
|
+
output.info(`${target} config preview`, sync ? buildPreviewSummary(sync) : { changes: "No sync summary returned" });
|
|
14750
|
+
}
|
|
14751
|
+
return deps.confirmAction(`Apply SDK config to ${ready.length} environment${ready.length === 1 ? "" : "s"} (${ready.map(({ target }) => target).join(" then ")})?`, { yes: options.yes }, false);
|
|
14752
|
+
},
|
|
14753
|
+
revalidate: async (_target, loaded, prepared) => options.dryRun ? { ok: true } : deps.revalidatePlan(loaded.context, prepared.context),
|
|
14754
|
+
apply: async (target, loaded, prepared) => {
|
|
14755
|
+
if (planned.value.targets.length > 1) output.info(`\n--- Pushing to ${target} environment ---`);
|
|
14756
|
+
return deps.applyPlan(output, loaded.context, prepared.context, {
|
|
14757
|
+
...options,
|
|
14758
|
+
dashboard: loaded.context.dashboard
|
|
14759
|
+
}, isJsonOutput);
|
|
14760
|
+
},
|
|
14761
|
+
isSuccess: isPushConfigApplySuccess,
|
|
14762
|
+
formatFailure: formatPushConfigActionFailure
|
|
14763
|
+
}, output);
|
|
14764
|
+
if (isJsonOutput) renderPushConfigJsonResult(output, execution, options.dryRun ?? false);
|
|
14765
|
+
});
|
|
14766
|
+
|
|
14767
|
+
//#endregion
|
|
14768
|
+
//#region ../api/src/navigationMenuValidation.ts
|
|
14769
|
+
function hasChildren(item) {
|
|
14770
|
+
return Array.isArray(item.children) && item.children.length > 0;
|
|
14771
|
+
}
|
|
14772
|
+
function isCartLinkMissingVisibleLabel(link$1) {
|
|
14773
|
+
return link$1?.kind === "cart" && link$1.label?.kind === "icon-with-label" && (typeof link$1.label.label !== "string" || link$1.label.label.trim().length === 0);
|
|
14774
|
+
}
|
|
14775
|
+
/**
|
|
14776
|
+
* Recursively collects portal-link constraints for a single menu tree.
|
|
14777
|
+
* Rules:
|
|
14778
|
+
* - At most one portal link per menu.
|
|
14779
|
+
* - Portal links must be root-level.
|
|
14780
|
+
* - Portal links cannot have children.
|
|
14781
|
+
*/
|
|
14782
|
+
function collectPortalLinkStats(items, depth = 0) {
|
|
14783
|
+
const result = {
|
|
14784
|
+
count: 0,
|
|
14785
|
+
nestedLabels: [],
|
|
14786
|
+
withChildrenLabels: []
|
|
14787
|
+
};
|
|
14788
|
+
for (const item of items) {
|
|
14789
|
+
const isPortal = item.link?.kind === "portal";
|
|
14790
|
+
if (isPortal) {
|
|
14791
|
+
result.count += 1;
|
|
14792
|
+
if (depth > 0) {
|
|
14793
|
+
result.nestedLabels.push(item.label);
|
|
15061
14794
|
}
|
|
15062
|
-
if (
|
|
15063
|
-
|
|
15064
|
-
kind: "appointments",
|
|
15065
|
-
staffMemberId
|
|
15066
|
-
};
|
|
14795
|
+
if (hasChildren(item)) {
|
|
14796
|
+
result.withChildrenLabels.push(item.label);
|
|
15067
14797
|
}
|
|
15068
|
-
|
|
15069
|
-
|
|
15070
|
-
|
|
15071
|
-
|
|
15072
|
-
|
|
14798
|
+
}
|
|
14799
|
+
if (hasChildren(item)) {
|
|
14800
|
+
const childItems = (item.children ?? []).filter(isNavigationMenuValidationItem);
|
|
14801
|
+
const child = collectPortalLinkStats(childItems, depth + 1);
|
|
14802
|
+
result.count += child.count;
|
|
14803
|
+
result.nestedLabels.push(...child.nestedLabels);
|
|
14804
|
+
result.withChildrenLabels.push(...child.withChildrenLabels);
|
|
14805
|
+
}
|
|
14806
|
+
}
|
|
14807
|
+
return result;
|
|
14808
|
+
}
|
|
14809
|
+
/**
|
|
14810
|
+
* Recursively collects cart-link constraints for a single menu tree.
|
|
14811
|
+
* Rules:
|
|
14812
|
+
* - Cart links must be root-level.
|
|
14813
|
+
* - Cart links cannot have children.
|
|
14814
|
+
* - Cart links cannot be CTA items.
|
|
14815
|
+
* - Cart links using `icon-with-label` must provide visible text.
|
|
14816
|
+
*/
|
|
14817
|
+
function collectCartLinkStats(items, depth = 0) {
|
|
14818
|
+
const result = {
|
|
14819
|
+
count: 0,
|
|
14820
|
+
nestedLabels: [],
|
|
14821
|
+
withChildrenLabels: [],
|
|
14822
|
+
ctaLabels: [],
|
|
14823
|
+
missingVisibleTextLabels: []
|
|
14824
|
+
};
|
|
14825
|
+
for (const item of items) {
|
|
14826
|
+
const isCart = item.link?.kind === "cart";
|
|
14827
|
+
if (isCart) {
|
|
14828
|
+
result.count += 1;
|
|
14829
|
+
if (depth > 0) {
|
|
14830
|
+
result.nestedLabels.push(item.label);
|
|
15073
14831
|
}
|
|
15074
|
-
if (
|
|
15075
|
-
|
|
15076
|
-
kind: "hybrid",
|
|
15077
|
-
staffMemberId
|
|
15078
|
-
};
|
|
14832
|
+
if (hasChildren(item)) {
|
|
14833
|
+
result.withChildrenLabels.push(item.label);
|
|
15079
14834
|
}
|
|
15080
|
-
if (
|
|
15081
|
-
|
|
15082
|
-
kind: "event_ops_only",
|
|
15083
|
-
staffMemberId
|
|
15084
|
-
};
|
|
14835
|
+
if (item.isCta === true) {
|
|
14836
|
+
result.ctaLabels.push(item.label);
|
|
15085
14837
|
}
|
|
15086
|
-
|
|
15087
|
-
|
|
15088
|
-
|
|
15089
|
-
|
|
14838
|
+
if (isCartLinkMissingVisibleLabel(item.link ?? null)) {
|
|
14839
|
+
result.missingVisibleTextLabels.push(item.label);
|
|
14840
|
+
}
|
|
14841
|
+
}
|
|
14842
|
+
if (hasChildren(item)) {
|
|
14843
|
+
const childItems = (item.children ?? []).filter(isNavigationMenuValidationItem);
|
|
14844
|
+
const child = collectCartLinkStats(childItems, depth + 1);
|
|
14845
|
+
result.count += child.count;
|
|
14846
|
+
result.nestedLabels.push(...child.nestedLabels);
|
|
14847
|
+
result.withChildrenLabels.push(...child.withChildrenLabels);
|
|
14848
|
+
result.ctaLabels.push(...child.ctaLabels);
|
|
14849
|
+
result.missingVisibleTextLabels.push(...child.missingVisibleTextLabels);
|
|
14850
|
+
}
|
|
15090
14851
|
}
|
|
14852
|
+
return result;
|
|
15091
14853
|
}
|
|
15092
|
-
function
|
|
15093
|
-
|
|
14854
|
+
function isNavigationMenuValidationItem(value) {
|
|
14855
|
+
if (!isRecord$5(value)) return false;
|
|
14856
|
+
return typeof value.label === "string";
|
|
15094
14857
|
}
|
|
15095
|
-
|
|
15096
|
-
|
|
14858
|
+
|
|
14859
|
+
//#endregion
|
|
14860
|
+
//#region ../api/src/navigation/bookingPortalLink.ts
|
|
14861
|
+
const YOUR_BOOKINGS_LABEL = "Your bookings";
|
|
14862
|
+
/**
|
|
14863
|
+
* The booking page's link to the customer portal, in the header and the
|
|
14864
|
+
* footer alike. It is a portal link, so renderers apply the portal link rules
|
|
14865
|
+
* (including the portal-enabled gate); the label stays the same signed in.
|
|
14866
|
+
*/
|
|
14867
|
+
const YOUR_BOOKINGS_PORTAL_LINK = {
|
|
14868
|
+
kind: "portal",
|
|
14869
|
+
labels: {
|
|
14870
|
+
loggedOut: YOUR_BOOKINGS_LABEL,
|
|
14871
|
+
loggedIn: YOUR_BOOKINGS_LABEL
|
|
14872
|
+
}
|
|
14873
|
+
};
|
|
14874
|
+
|
|
14875
|
+
//#endregion
|
|
14876
|
+
//#region ../api/src/navigation/linkResolver.ts
|
|
14877
|
+
/**
|
|
14878
|
+
* Type guard for internal links.
|
|
14879
|
+
* Checks if a link payload is an internal link with entityId and entityType.
|
|
14880
|
+
*/
|
|
14881
|
+
function isInternalLink(link$1) {
|
|
14882
|
+
if (!link$1 || typeof link$1 !== "object") {
|
|
14883
|
+
return false;
|
|
14884
|
+
}
|
|
14885
|
+
return link$1.kind === "internal" && "entityId" in link$1 && "entityType" in link$1 && typeof link$1.entityId === "string" && typeof link$1.entityType === "string";
|
|
15097
14886
|
}
|
|
15098
|
-
|
|
15099
|
-
|
|
14887
|
+
/**
|
|
14888
|
+
* Narrow internal link to page type.
|
|
14889
|
+
*/
|
|
14890
|
+
function isPageLink(link$1) {
|
|
14891
|
+
return link$1.entityType === "page";
|
|
15100
14892
|
}
|
|
15101
|
-
|
|
15102
|
-
|
|
14893
|
+
/**
|
|
14894
|
+
* Narrow internal link to content/entry type.
|
|
14895
|
+
*/
|
|
14896
|
+
function isEntryLink(link$1) {
|
|
14897
|
+
return link$1.entityType === "content";
|
|
15103
14898
|
}
|
|
15104
|
-
|
|
15105
|
-
|
|
15106
|
-
|
|
15107
|
-
|
|
15108
|
-
|
|
15109
|
-
|
|
15110
|
-
|
|
14899
|
+
/**
|
|
14900
|
+
* Resolve a navigation link input to URL type and link payload.
|
|
14901
|
+
*
|
|
14902
|
+
* Supports identifier-based page/entry links and validates them against routable
|
|
14903
|
+
* lookup maps before persisting.
|
|
14904
|
+
*
|
|
14905
|
+
* @param link - The navigation link input from SDK config or API
|
|
14906
|
+
* @param options - Resolution options (isDropdown flag)
|
|
14907
|
+
* @param pageMap - Map of page identifier -> PageNavLinkInfo
|
|
14908
|
+
* @param entryMap - Map of `contentType:identifier` -> EntryNavLinkInfo
|
|
14909
|
+
* @returns Resolved link with urlType, url payload, and any error
|
|
14910
|
+
*/
|
|
14911
|
+
function resolveNavigationLinkInput(link$1, options, pageMap, entryMap) {
|
|
14912
|
+
if (options.isDropdown) {
|
|
14913
|
+
return {
|
|
14914
|
+
urlType: "dropdown",
|
|
14915
|
+
url: null,
|
|
14916
|
+
error: null
|
|
14917
|
+
};
|
|
14918
|
+
}
|
|
14919
|
+
if (!link$1) {
|
|
14920
|
+
return {
|
|
14921
|
+
urlType: "url",
|
|
14922
|
+
url: null,
|
|
14923
|
+
error: "Link is required for non-dropdown items"
|
|
14924
|
+
};
|
|
14925
|
+
}
|
|
14926
|
+
switch (link$1.kind) {
|
|
14927
|
+
case "page": return resolvePageLink(link$1.identifier, pageMap);
|
|
14928
|
+
case "entry": return resolveEntryLink(link$1.contentType, link$1.identifier, entryMap);
|
|
14929
|
+
case "external": return {
|
|
14930
|
+
urlType: "external",
|
|
14931
|
+
url: {
|
|
14932
|
+
kind: "external",
|
|
14933
|
+
href: link$1.href
|
|
14934
|
+
},
|
|
14935
|
+
error: null
|
|
14936
|
+
};
|
|
14937
|
+
case "url": return {
|
|
14938
|
+
urlType: "url",
|
|
14939
|
+
url: {
|
|
14940
|
+
kind: "url",
|
|
14941
|
+
href: link$1.href
|
|
14942
|
+
},
|
|
14943
|
+
error: null
|
|
14944
|
+
};
|
|
14945
|
+
case "portal": return {
|
|
14946
|
+
urlType: "portal",
|
|
14947
|
+
url: {
|
|
14948
|
+
kind: "portal",
|
|
14949
|
+
...link$1.path ? { path: link$1.path } : {},
|
|
14950
|
+
...link$1.labels ? { labels: link$1.labels } : {}
|
|
14951
|
+
},
|
|
14952
|
+
error: null
|
|
14953
|
+
};
|
|
14954
|
+
case "cart": return {
|
|
14955
|
+
urlType: "cart",
|
|
14956
|
+
url: {
|
|
14957
|
+
kind: "cart",
|
|
14958
|
+
badge: link$1.badge,
|
|
14959
|
+
label: link$1.label
|
|
14960
|
+
},
|
|
14961
|
+
error: null
|
|
14962
|
+
};
|
|
14963
|
+
default: return {
|
|
14964
|
+
urlType: "url",
|
|
14965
|
+
url: null,
|
|
14966
|
+
error: "Unknown link type"
|
|
14967
|
+
};
|
|
15111
14968
|
}
|
|
15112
14969
|
}
|
|
15113
|
-
|
|
15114
|
-
|
|
15115
|
-
|
|
15116
|
-
|
|
15117
|
-
|
|
15118
|
-
|
|
15119
|
-
|
|
15120
|
-
|
|
15121
|
-
|
|
15122
|
-
}
|
|
15123
|
-
|
|
15124
|
-
if (!input.membershipRole || !input.linkedStaffMemberId) {
|
|
15125
|
-
return { kind: "none" };
|
|
14970
|
+
/**
|
|
14971
|
+
* Resolve a page identifier to a canonical page link payload.
|
|
14972
|
+
*/
|
|
14973
|
+
function resolvePageLink(identifier, pageMap) {
|
|
14974
|
+
const pageInfo = pageMap.get(identifier);
|
|
14975
|
+
if (!pageInfo) {
|
|
14976
|
+
return {
|
|
14977
|
+
urlType: "internal",
|
|
14978
|
+
url: null,
|
|
14979
|
+
error: `Page '${identifier}' not found`
|
|
14980
|
+
};
|
|
15126
14981
|
}
|
|
15127
|
-
const staffMemberId = unsafeAsStaffMemberId(input.linkedStaffMemberId);
|
|
15128
14982
|
return {
|
|
15129
|
-
|
|
15130
|
-
|
|
15131
|
-
|
|
14983
|
+
urlType: "internal",
|
|
14984
|
+
url: {
|
|
14985
|
+
kind: "page",
|
|
14986
|
+
identifier
|
|
14987
|
+
},
|
|
14988
|
+
error: null
|
|
15132
14989
|
};
|
|
15133
14990
|
}
|
|
15134
|
-
|
|
15135
|
-
|
|
15136
|
-
|
|
15137
|
-
|
|
15138
|
-
}
|
|
15139
|
-
|
|
15140
|
-
|
|
15141
|
-
|
|
15142
|
-
|
|
15143
|
-
|
|
15144
|
-
|
|
15145
|
-
}
|
|
15146
|
-
|
|
15147
|
-
|
|
14991
|
+
/**
|
|
14992
|
+
* Resolve an entry identifier to a canonical entry link payload.
|
|
14993
|
+
*/
|
|
14994
|
+
function resolveEntryLink(contentType, identifier, entryMap) {
|
|
14995
|
+
const entryInfo = entryMap.get(`${contentType}:${identifier}`);
|
|
14996
|
+
if (!entryInfo) {
|
|
14997
|
+
return {
|
|
14998
|
+
urlType: "internal",
|
|
14999
|
+
url: null,
|
|
15000
|
+
error: `Entry '${contentType}:${identifier}' not found or not routable`
|
|
15001
|
+
};
|
|
15002
|
+
}
|
|
15003
|
+
return {
|
|
15004
|
+
urlType: "internal",
|
|
15005
|
+
url: {
|
|
15006
|
+
kind: "entry",
|
|
15007
|
+
contentType,
|
|
15008
|
+
identifier
|
|
15009
|
+
},
|
|
15010
|
+
error: null
|
|
15011
|
+
};
|
|
15148
15012
|
}
|
|
15149
|
-
|
|
15150
|
-
|
|
15151
|
-
|
|
15013
|
+
|
|
15014
|
+
//#endregion
|
|
15015
|
+
//#region ../api/src/navigation/boundaries.ts
|
|
15016
|
+
function isNavigationDropdownSentinel(link$1) {
|
|
15017
|
+
return link$1?.kind === "dropdown";
|
|
15152
15018
|
}
|
|
15153
|
-
function
|
|
15154
|
-
|
|
15155
|
-
|
|
15019
|
+
function normalizeNavigationConfigItemForCommand(item) {
|
|
15020
|
+
const commandItem = { label: item.label };
|
|
15021
|
+
if (item.isCta !== undefined) {
|
|
15022
|
+
commandItem.isCta = item.isCta;
|
|
15156
15023
|
}
|
|
15157
|
-
|
|
15158
|
-
|
|
15024
|
+
const commandLink = normalizeNavigationAuthoringLinkForCommand(item.link);
|
|
15025
|
+
if (commandLink) {
|
|
15026
|
+
commandItem.link = commandLink;
|
|
15159
15027
|
}
|
|
15160
|
-
|
|
15161
|
-
|
|
15162
|
-
|
|
15163
|
-
return
|
|
15164
|
-
}
|
|
15165
|
-
function isEventOpsOnlyUser(sites) {
|
|
15166
|
-
if (sites.length === 0) return false;
|
|
15167
|
-
return sites.every((site) => deriveStaffScope(site).kind === "event_ops_only");
|
|
15028
|
+
if (item.children && item.children.length > 0) {
|
|
15029
|
+
commandItem.children = item.children.map(normalizeNavigationConfigItemForCommand);
|
|
15030
|
+
}
|
|
15031
|
+
return commandItem;
|
|
15168
15032
|
}
|
|
15169
|
-
function
|
|
15170
|
-
const
|
|
15171
|
-
const staffHomeDestination = deriveStaffSiteHomeDestination(staffScope);
|
|
15172
|
-
const isStaffScopedMembership = isStaffScoped(staffScope);
|
|
15033
|
+
function navigationCommandItemToManagementInput(item) {
|
|
15034
|
+
const link$1 = normalizeNavigationAuthoringLinkForCommand(item.link);
|
|
15173
15035
|
return {
|
|
15174
|
-
|
|
15175
|
-
|
|
15176
|
-
|
|
15177
|
-
|
|
15036
|
+
label: item.label,
|
|
15037
|
+
...link$1 ? { link: link$1 } : {},
|
|
15038
|
+
...item.isCta !== undefined ? { isCta: item.isCta } : {},
|
|
15039
|
+
...item.children && item.children.length > 0 ? { children: item.children.map(navigationCommandItemToManagementInput) } : {}
|
|
15178
15040
|
};
|
|
15179
15041
|
}
|
|
15180
|
-
function
|
|
15181
|
-
|
|
15042
|
+
function normalizeNavigationAuthoringLinkForCommand(link$1) {
|
|
15043
|
+
if (!link$1 || isNavigationDropdownSentinel(link$1)) return undefined;
|
|
15044
|
+
return navigationAuthoringLinkToCommandLink(link$1);
|
|
15182
15045
|
}
|
|
15183
|
-
function
|
|
15184
|
-
const
|
|
15185
|
-
|
|
15186
|
-
|
|
15187
|
-
|
|
15188
|
-
|
|
15046
|
+
function navigationApiItemToSdkLocalItem(item) {
|
|
15047
|
+
const link$1 = navigationReadLinkToLocalLink(item);
|
|
15048
|
+
const localItem = {
|
|
15049
|
+
label: item.label,
|
|
15050
|
+
...item.isCta ? { isCta: true } : {},
|
|
15051
|
+
...link$1 ? { link: link$1 } : {},
|
|
15052
|
+
...item.children && item.children.length > 0 ? { children: item.children.map(navigationApiItemToSdkLocalItem) } : {}
|
|
15053
|
+
};
|
|
15054
|
+
return localItem;
|
|
15189
15055
|
}
|
|
15190
|
-
|
|
15191
|
-
|
|
15192
|
-
|
|
15193
|
-
|
|
15194
|
-
|
|
15195
|
-
|
|
15056
|
+
function navigationAuthoringLinkToCommandLink(link$1) {
|
|
15057
|
+
switch (link$1.kind) {
|
|
15058
|
+
case "page": return {
|
|
15059
|
+
kind: "page",
|
|
15060
|
+
identifier: link$1.identifier
|
|
15061
|
+
};
|
|
15062
|
+
case "entry": return {
|
|
15063
|
+
kind: "entry",
|
|
15064
|
+
contentType: link$1.contentType,
|
|
15065
|
+
identifier: link$1.identifier
|
|
15066
|
+
};
|
|
15067
|
+
case "external": return {
|
|
15068
|
+
kind: "external",
|
|
15069
|
+
href: link$1.href
|
|
15070
|
+
};
|
|
15071
|
+
case "url": return {
|
|
15072
|
+
kind: "url",
|
|
15073
|
+
href: link$1.href
|
|
15074
|
+
};
|
|
15075
|
+
case "portal": return {
|
|
15076
|
+
kind: "portal",
|
|
15077
|
+
...link$1.path ? { path: link$1.path } : {},
|
|
15078
|
+
...portalLabelsForCommand(link$1.labels)
|
|
15079
|
+
};
|
|
15080
|
+
case "cart": return {
|
|
15081
|
+
kind: "cart",
|
|
15082
|
+
badge: link$1.badge,
|
|
15083
|
+
label: link$1.label
|
|
15084
|
+
};
|
|
15085
|
+
default: return assertNever(link$1);
|
|
15086
|
+
}
|
|
15196
15087
|
}
|
|
15197
|
-
|
|
15198
|
-
|
|
15199
|
-
|
|
15200
|
-
|
|
15201
|
-
|
|
15202
|
-
|
|
15203
|
-
|
|
15204
|
-
|
|
15205
|
-
function
|
|
15206
|
-
|
|
15088
|
+
function portalLabelsForCommand(labels) {
|
|
15089
|
+
if (!labels) return {};
|
|
15090
|
+
const commandLabels = {
|
|
15091
|
+
...typeof labels.loggedOut === "string" ? { loggedOut: labels.loggedOut } : {},
|
|
15092
|
+
...typeof labels.loggedIn === "string" ? { loggedIn: labels.loggedIn } : {}
|
|
15093
|
+
};
|
|
15094
|
+
return Object.keys(commandLabels).length > 0 ? { labels: commandLabels } : {};
|
|
15095
|
+
}
|
|
15096
|
+
function navigationReadLinkToLocalLink(item) {
|
|
15097
|
+
const { link: link$1 } = item;
|
|
15098
|
+
if (!link$1) return undefined;
|
|
15099
|
+
switch (link$1.kind) {
|
|
15100
|
+
case "page": return {
|
|
15101
|
+
kind: "page",
|
|
15102
|
+
identifier: link$1.identifier
|
|
15103
|
+
};
|
|
15104
|
+
case "entry": return {
|
|
15105
|
+
kind: "entry",
|
|
15106
|
+
contentType: link$1.contentType,
|
|
15107
|
+
identifier: link$1.identifier
|
|
15108
|
+
};
|
|
15109
|
+
case "internal": return internalNavigationLinkToLocalLink(item);
|
|
15110
|
+
case "external": return {
|
|
15111
|
+
kind: "external",
|
|
15112
|
+
href: link$1.href
|
|
15113
|
+
};
|
|
15114
|
+
case "url": return {
|
|
15115
|
+
kind: "url",
|
|
15116
|
+
href: link$1.href
|
|
15117
|
+
};
|
|
15118
|
+
case "portal": return {
|
|
15119
|
+
kind: "portal",
|
|
15120
|
+
...link$1.path ? { path: link$1.path } : {},
|
|
15121
|
+
...link$1.labels ? { labels: link$1.labels } : {}
|
|
15122
|
+
};
|
|
15123
|
+
case "cart": return {
|
|
15124
|
+
kind: "cart",
|
|
15125
|
+
badge: link$1.badge,
|
|
15126
|
+
label: link$1.label
|
|
15127
|
+
};
|
|
15128
|
+
default: return assertNever(link$1);
|
|
15129
|
+
}
|
|
15130
|
+
}
|
|
15131
|
+
function internalNavigationLinkToLocalLink(item) {
|
|
15132
|
+
const { link: link$1 } = item;
|
|
15133
|
+
if (!link$1 || link$1.kind !== "internal") return undefined;
|
|
15134
|
+
if (item.resolvedIdentifier) {
|
|
15135
|
+
if (link$1.entityType === "page") {
|
|
15136
|
+
return {
|
|
15137
|
+
kind: "page",
|
|
15138
|
+
identifier: item.resolvedIdentifier
|
|
15139
|
+
};
|
|
15140
|
+
}
|
|
15141
|
+
if (link$1.entityType === "content" && item.resolvedContentType) {
|
|
15142
|
+
return {
|
|
15143
|
+
kind: "entry",
|
|
15144
|
+
contentType: item.resolvedContentType,
|
|
15145
|
+
identifier: item.resolvedIdentifier
|
|
15146
|
+
};
|
|
15147
|
+
}
|
|
15148
|
+
}
|
|
15149
|
+
if (link$1.href) {
|
|
15150
|
+
return {
|
|
15151
|
+
kind: "url",
|
|
15152
|
+
href: link$1.href
|
|
15153
|
+
};
|
|
15154
|
+
}
|
|
15155
|
+
return undefined;
|
|
15207
15156
|
}
|
|
15208
15157
|
|
|
15209
15158
|
//#endregion
|
|
15210
|
-
//#region ../api/src/
|
|
15211
|
-
|
|
15212
|
-
|
|
15213
|
-
|
|
15214
|
-
|
|
15215
|
-
|
|
15216
|
-
|
|
15217
|
-
|
|
15218
|
-
|
|
15219
|
-
|
|
15220
|
-
|
|
15221
|
-
|
|
15222
|
-
|
|
15223
|
-
|
|
15224
|
-
|
|
15225
|
-
|
|
15226
|
-
|
|
15227
|
-
|
|
15228
|
-
|
|
15229
|
-
|
|
15230
|
-
|
|
15231
|
-
|
|
15232
|
-
|
|
15233
|
-
|
|
15234
|
-
|
|
15235
|
-
|
|
15236
|
-
|
|
15237
|
-
|
|
15238
|
-
|
|
15239
|
-
|
|
15240
|
-
|
|
15241
|
-
|
|
15242
|
-
|
|
15243
|
-
|
|
15244
|
-
|
|
15245
|
-
|
|
15246
|
-
|
|
15247
|
-
|
|
15248
|
-
|
|
15249
|
-
|
|
15250
|
-
|
|
15251
|
-
|
|
15252
|
-
];
|
|
15253
|
-
const CUSTOMER_MEMBERSHIP_STATUS_VALUES = [
|
|
15254
|
-
"active",
|
|
15255
|
-
"trialing",
|
|
15256
|
-
"past_due",
|
|
15257
|
-
"paused",
|
|
15258
|
-
"canceled"
|
|
15259
|
-
];
|
|
15260
|
-
const BOOKING_ACTIVITY_CATEGORY_VALUES = [
|
|
15261
|
-
"booking",
|
|
15262
|
-
"purchase",
|
|
15263
|
-
"cancellation",
|
|
15264
|
-
"refund",
|
|
15265
|
-
"transfer",
|
|
15266
|
-
"failure"
|
|
15267
|
-
];
|
|
15268
|
-
const BOOKING_ACTIVITY_TYPE_VALUES = [
|
|
15269
|
-
"appointment_booked",
|
|
15270
|
-
"appointment_cancelled",
|
|
15271
|
-
"booking_closure_created",
|
|
15272
|
-
"booking_closure_removed",
|
|
15273
|
-
"event_registration",
|
|
15274
|
-
"event_registration_cancelled",
|
|
15275
|
-
"event_series_registration",
|
|
15276
|
-
"event_series_registration_cancelled",
|
|
15277
|
-
"pass_purchased",
|
|
15278
|
-
"membership_started",
|
|
15279
|
-
"membership_paused",
|
|
15280
|
-
"membership_cancelled",
|
|
15281
|
-
"event_refund",
|
|
15282
|
-
"pass_refund",
|
|
15283
|
-
"membership_refund",
|
|
15284
|
-
"event_series_registration_refund",
|
|
15285
|
-
"ticket_transfer",
|
|
15286
|
-
"payment_failed"
|
|
15287
|
-
];
|
|
15288
|
-
const BOOKING_LEDGER_KIND_VALUES = [
|
|
15289
|
-
"appointment",
|
|
15290
|
-
"event_registration",
|
|
15291
|
-
"event_series_registration"
|
|
15292
|
-
];
|
|
15293
|
-
const BOOKING_LEDGER_DATE_BASIS_VALUES = [
|
|
15294
|
-
"created_at",
|
|
15295
|
-
"scheduled_at",
|
|
15296
|
-
"cancelled_at"
|
|
15297
|
-
];
|
|
15298
|
-
const BOOKING_LEDGER_PERIOD_PRESET_VALUES = [
|
|
15299
|
-
"7d",
|
|
15300
|
-
"30d",
|
|
15301
|
-
"90d",
|
|
15302
|
-
"365d",
|
|
15303
|
-
"all"
|
|
15304
|
-
];
|
|
15305
|
-
const BOOKING_LEDGER_CANCELLATION_MODE_VALUES = [
|
|
15306
|
-
"exclude_cancelled",
|
|
15307
|
-
"include_cancelled",
|
|
15308
|
-
"cancelled_only"
|
|
15309
|
-
];
|
|
15310
|
-
const BOOKING_QUESTION_ANSWER_EXPORT_SCOPES = {
|
|
15311
|
-
site: "site",
|
|
15312
|
-
appointments: "appointments",
|
|
15313
|
-
eventSeries: "event_series"
|
|
15314
|
-
};
|
|
15315
|
-
const BOOKING_QUESTION_ANSWER_EXPORT_SCOPE_VALUES = [
|
|
15316
|
-
BOOKING_QUESTION_ANSWER_EXPORT_SCOPES.site,
|
|
15317
|
-
BOOKING_QUESTION_ANSWER_EXPORT_SCOPES.appointments,
|
|
15318
|
-
BOOKING_QUESTION_ANSWER_EXPORT_SCOPES.eventSeries
|
|
15319
|
-
];
|
|
15320
|
-
|
|
15321
|
-
//#endregion
|
|
15322
|
-
//#region ../api/src/bookingPeople.ts
|
|
15323
|
-
const PEOPLE_LIST_SORT_VALUES = [
|
|
15324
|
-
"name",
|
|
15325
|
-
"passes",
|
|
15326
|
-
"memberships",
|
|
15327
|
-
"lastBooking",
|
|
15328
|
-
"added"
|
|
15329
|
-
];
|
|
15330
|
-
const PEOPLE_LIST_FILTER_VALUES = [
|
|
15331
|
-
"everyone",
|
|
15332
|
-
"hasBought",
|
|
15333
|
-
"hasActivePass",
|
|
15334
|
-
"hasMembership",
|
|
15335
|
-
"guests",
|
|
15336
|
-
"nothingBought"
|
|
15337
|
-
];
|
|
15338
|
-
/** What can be attached to a person, in the order the dialog lists it. */
|
|
15339
|
-
const PERSON_DELETION_BLOCKER_CATEGORY_VALUES = [
|
|
15340
|
-
"bookings",
|
|
15341
|
-
"guest_places",
|
|
15342
|
-
"waitlist_places",
|
|
15343
|
-
"payments",
|
|
15344
|
-
"orders",
|
|
15345
|
-
"passes",
|
|
15346
|
-
"memberships",
|
|
15347
|
-
"credit",
|
|
15348
|
-
"digital_products",
|
|
15349
|
-
"discounts",
|
|
15350
|
-
"gifts",
|
|
15351
|
-
"notes",
|
|
15352
|
-
"forms",
|
|
15353
|
-
"terms_agreements",
|
|
15354
|
-
"uploads",
|
|
15355
|
-
"contact_corrections"
|
|
15356
|
-
];
|
|
15159
|
+
//#region ../api/src/navigation/routableLink.ts
|
|
15160
|
+
/**
|
|
15161
|
+
* Prefer identifier-based links when available so links survive route/path updates.
|
|
15162
|
+
* Falls back to an internal route payload for legacy rows or incomplete data.
|
|
15163
|
+
*/
|
|
15164
|
+
function toRoutableLinkPayload(item) {
|
|
15165
|
+
switch (item.entityType) {
|
|
15166
|
+
case "page":
|
|
15167
|
+
if (item.identifier) {
|
|
15168
|
+
return {
|
|
15169
|
+
kind: "page",
|
|
15170
|
+
identifier: item.identifier
|
|
15171
|
+
};
|
|
15172
|
+
}
|
|
15173
|
+
return toResolvedInternalPayload(item);
|
|
15174
|
+
case "content":
|
|
15175
|
+
if (item.identifier && item.contentTypeKey) {
|
|
15176
|
+
return {
|
|
15177
|
+
kind: "entry",
|
|
15178
|
+
contentType: item.contentTypeKey,
|
|
15179
|
+
identifier: item.identifier
|
|
15180
|
+
};
|
|
15181
|
+
}
|
|
15182
|
+
return toResolvedInternalPayload(item);
|
|
15183
|
+
case "pageSurface": return toResolvedInternalPayload(item);
|
|
15184
|
+
default: return assertNever(item);
|
|
15185
|
+
}
|
|
15186
|
+
}
|
|
15187
|
+
function toResolvedInternalPayload(item) {
|
|
15188
|
+
return {
|
|
15189
|
+
kind: "internal",
|
|
15190
|
+
routeId: item.routeId,
|
|
15191
|
+
entityId: item.entityId,
|
|
15192
|
+
entityType: item.entityType,
|
|
15193
|
+
href: item.path ?? "/",
|
|
15194
|
+
title: item.title ?? item.draftTitle ?? "Untitled",
|
|
15195
|
+
typeLabel: item.typeLabel,
|
|
15196
|
+
contentTypeKey: item.contentTypeKey,
|
|
15197
|
+
contentTypeName: item.contentTypeName,
|
|
15198
|
+
updatedAt: item.updatedAt ?? null
|
|
15199
|
+
};
|
|
15200
|
+
}
|
|
15357
15201
|
|
|
15358
15202
|
//#endregion
|
|
15359
|
-
//#region ../api/src/
|
|
15360
|
-
const
|
|
15361
|
-
"
|
|
15362
|
-
"
|
|
15363
|
-
"
|
|
15364
|
-
"
|
|
15365
|
-
|
|
15366
|
-
|
|
15367
|
-
|
|
15203
|
+
//#region ../api/src/navigation/linkUrlType.ts
|
|
15204
|
+
const LINK_PAYLOAD_NAVIGATION_URL_TYPES = {
|
|
15205
|
+
page: "internal",
|
|
15206
|
+
entry: "internal",
|
|
15207
|
+
internal: "internal",
|
|
15208
|
+
external: "external",
|
|
15209
|
+
url: "url",
|
|
15210
|
+
portal: "portal",
|
|
15211
|
+
cart: "cart"
|
|
15212
|
+
};
|
|
15213
|
+
function linkPayloadKindToNavigationUrlType(kind) {
|
|
15214
|
+
return LINK_PAYLOAD_NAVIGATION_URL_TYPES[kind];
|
|
15368
15215
|
}
|
|
15369
15216
|
|
|
15370
15217
|
//#endregion
|
|
15371
|
-
//#region ../api/src/
|
|
15372
|
-
const
|
|
15373
|
-
|
|
15374
|
-
"
|
|
15375
|
-
"
|
|
15376
|
-
"no_show"
|
|
15377
|
-
];
|
|
15378
|
-
const BOOKING_QUESTION_REQUIREMENT_SCOPES = ["before_booking_booking_level", "after_booking_due_by_participant"];
|
|
15379
|
-
function isAfterBookingQuestionRequirementScope(scope) {
|
|
15380
|
-
return scope === "after_booking_due_by_participant";
|
|
15218
|
+
//#region ../api/src/accessAdmin.ts
|
|
15219
|
+
const STAFF_OPERATIONAL_CAPABILITIES = ["appointments", "event_ops"];
|
|
15220
|
+
function getDefaultStaffCapabilities(surfaces) {
|
|
15221
|
+
if (surfaces.events && !surfaces.appointments) return ["event_ops"];
|
|
15222
|
+
return ["appointments"];
|
|
15381
15223
|
}
|
|
15382
|
-
|
|
15383
|
-
|
|
15224
|
+
const managedDomainAddOnPricingSnapshotSchema = z.object({
|
|
15225
|
+
formulaVersion: z.literal("managed-domain-overage-v1"),
|
|
15226
|
+
conversionRule: z.literal("usd-to-gbp-1-to-1-v1"),
|
|
15227
|
+
registrarAnnualCost: z.number().nonnegative(),
|
|
15228
|
+
registrarCurrency: z.literal("USD"),
|
|
15229
|
+
includedAnnualAllowance: z.number().nonnegative(),
|
|
15230
|
+
includedAnnualAllowanceCurrency: z.literal("USD"),
|
|
15231
|
+
monthlyAmountCents: z.number().int().nonnegative(),
|
|
15232
|
+
monthlyCurrency: z.literal("gbp")
|
|
15233
|
+
});
|
|
15234
|
+
|
|
15235
|
+
//#endregion
|
|
15236
|
+
//#region ../api/src/siteMembers.ts
|
|
15237
|
+
/**
|
|
15238
|
+
* Higher number means more permissions.
|
|
15239
|
+
*/
|
|
15240
|
+
const SITE_ROLE_PERMISSION_LEVEL = {
|
|
15241
|
+
team_member: 0,
|
|
15242
|
+
editor: 1,
|
|
15243
|
+
administrator: 2,
|
|
15244
|
+
owner: 3
|
|
15245
|
+
};
|
|
15246
|
+
/**
|
|
15247
|
+
* Lower number sorts earlier in UI role pickers.
|
|
15248
|
+
*/
|
|
15249
|
+
const SITE_ROLE_DISPLAY_ORDER = {
|
|
15250
|
+
owner: 0,
|
|
15251
|
+
administrator: 1,
|
|
15252
|
+
editor: 2,
|
|
15253
|
+
team_member: 3
|
|
15254
|
+
};
|
|
15255
|
+
async function getSiteMembers(apiClient, { siteId, options }) {
|
|
15256
|
+
try {
|
|
15257
|
+
return unwrapResponse(await apiClient({
|
|
15258
|
+
endpoint: "listSiteMembers",
|
|
15259
|
+
params: { siteId },
|
|
15260
|
+
options
|
|
15261
|
+
}));
|
|
15262
|
+
} catch (error) {
|
|
15263
|
+
return normalizeApiError(error, "Failed to load site members");
|
|
15264
|
+
}
|
|
15384
15265
|
}
|
|
15385
|
-
async function
|
|
15266
|
+
async function inviteSiteMember(apiClient, { siteId, body, options }) {
|
|
15386
15267
|
try {
|
|
15387
|
-
|
|
15388
|
-
endpoint: "
|
|
15268
|
+
return unwrapResponse(await apiClient({
|
|
15269
|
+
endpoint: "inviteSiteMember",
|
|
15389
15270
|
params: { siteId },
|
|
15271
|
+
body,
|
|
15390
15272
|
options
|
|
15391
15273
|
}));
|
|
15392
|
-
return response.settings;
|
|
15393
15274
|
} catch (error) {
|
|
15394
|
-
normalizeApiError(error, "Failed to
|
|
15275
|
+
return normalizeApiError(error, "Failed to invite collaborator");
|
|
15395
15276
|
}
|
|
15396
15277
|
}
|
|
15397
|
-
async function
|
|
15278
|
+
async function updateSiteMemberRole(apiClient, { siteId, memberId, body, options }) {
|
|
15398
15279
|
try {
|
|
15399
|
-
|
|
15400
|
-
endpoint: "
|
|
15280
|
+
return unwrapResponse(await apiClient({
|
|
15281
|
+
endpoint: "updateSiteMemberRole",
|
|
15282
|
+
params: {
|
|
15283
|
+
siteId,
|
|
15284
|
+
memberId
|
|
15285
|
+
},
|
|
15286
|
+
body,
|
|
15287
|
+
options
|
|
15288
|
+
}));
|
|
15289
|
+
} catch (error) {
|
|
15290
|
+
return normalizeApiError(error, "Failed to update member role");
|
|
15291
|
+
}
|
|
15292
|
+
}
|
|
15293
|
+
async function linkCurrentUserPractitioner(apiClient, { siteId, body, options }) {
|
|
15294
|
+
try {
|
|
15295
|
+
return unwrapResponse(await apiClient({
|
|
15296
|
+
endpoint: "linkCurrentUserPractitioner",
|
|
15401
15297
|
params: { siteId },
|
|
15402
|
-
body
|
|
15298
|
+
body,
|
|
15403
15299
|
options
|
|
15404
15300
|
}));
|
|
15405
|
-
return response.settings;
|
|
15406
15301
|
} catch (error) {
|
|
15407
|
-
normalizeApiError(error, "Failed to
|
|
15302
|
+
return normalizeApiError(error, "Failed to link practitioner profile");
|
|
15408
15303
|
}
|
|
15409
15304
|
}
|
|
15410
|
-
async function
|
|
15305
|
+
async function unlinkCurrentUserPractitioner(apiClient, { siteId, options }) {
|
|
15411
15306
|
try {
|
|
15412
15307
|
return unwrapResponse(await apiClient({
|
|
15413
|
-
endpoint: "
|
|
15308
|
+
endpoint: "unlinkCurrentUserPractitioner",
|
|
15414
15309
|
params: { siteId },
|
|
15415
|
-
body: payload,
|
|
15416
15310
|
options
|
|
15417
15311
|
}));
|
|
15418
15312
|
} catch (error) {
|
|
15419
|
-
normalizeApiError(error, "Failed to
|
|
15313
|
+
return normalizeApiError(error, "Failed to unlink practitioner profile");
|
|
15314
|
+
}
|
|
15315
|
+
}
|
|
15316
|
+
async function removeSiteMember(apiClient, { siteId, memberId, options }) {
|
|
15317
|
+
try {
|
|
15318
|
+
return unwrapResponse(await apiClient({
|
|
15319
|
+
endpoint: "removeSiteMember",
|
|
15320
|
+
params: {
|
|
15321
|
+
siteId,
|
|
15322
|
+
memberId
|
|
15323
|
+
},
|
|
15324
|
+
options
|
|
15325
|
+
}));
|
|
15326
|
+
} catch (error) {
|
|
15327
|
+
return normalizeApiError(error, "Failed to remove collaborator");
|
|
15420
15328
|
}
|
|
15421
15329
|
}
|
|
15422
|
-
async function
|
|
15330
|
+
async function revokeSiteInvitation(apiClient, { siteId, invitationId, options }) {
|
|
15423
15331
|
try {
|
|
15424
15332
|
return unwrapResponse(await apiClient({
|
|
15425
|
-
endpoint: "
|
|
15333
|
+
endpoint: "revokeSiteInvitation",
|
|
15426
15334
|
params: {
|
|
15427
15335
|
siteId,
|
|
15428
|
-
|
|
15336
|
+
invitationId
|
|
15429
15337
|
},
|
|
15430
15338
|
options
|
|
15431
15339
|
}));
|
|
15432
15340
|
} catch (error) {
|
|
15433
|
-
normalizeApiError(error, "Failed to
|
|
15341
|
+
return normalizeApiError(error, "Failed to revoke invitation");
|
|
15434
15342
|
}
|
|
15435
15343
|
}
|
|
15436
|
-
async function
|
|
15344
|
+
async function resendSiteInvitation(apiClient, { siteId, invitationId, options }) {
|
|
15437
15345
|
try {
|
|
15438
15346
|
return unwrapResponse(await apiClient({
|
|
15439
|
-
endpoint: "
|
|
15347
|
+
endpoint: "resendSiteInvitation",
|
|
15440
15348
|
params: {
|
|
15441
15349
|
siteId,
|
|
15442
|
-
|
|
15350
|
+
invitationId
|
|
15443
15351
|
},
|
|
15444
|
-
body: payload,
|
|
15445
15352
|
options
|
|
15446
15353
|
}));
|
|
15447
15354
|
} catch (error) {
|
|
15448
|
-
normalizeApiError(error, "Failed to
|
|
15355
|
+
return normalizeApiError(error, "Failed to resend invitation");
|
|
15449
15356
|
}
|
|
15450
15357
|
}
|
|
15451
|
-
async function
|
|
15358
|
+
async function transferSiteOwnership(apiClient, { siteId, body, options }) {
|
|
15452
15359
|
try {
|
|
15453
15360
|
return unwrapResponse(await apiClient({
|
|
15454
|
-
endpoint: "
|
|
15455
|
-
params: {
|
|
15456
|
-
|
|
15457
|
-
seriesId: eventSeriesId,
|
|
15458
|
-
registrationId
|
|
15459
|
-
},
|
|
15460
|
-
body: payload,
|
|
15361
|
+
endpoint: "transferSiteOwnership",
|
|
15362
|
+
params: { siteId },
|
|
15363
|
+
body,
|
|
15461
15364
|
options
|
|
15462
15365
|
}));
|
|
15463
15366
|
} catch (error) {
|
|
15464
|
-
normalizeApiError(error, "Failed to
|
|
15367
|
+
return normalizeApiError(error, "Failed to transfer ownership");
|
|
15465
15368
|
}
|
|
15466
15369
|
}
|
|
15467
|
-
async function
|
|
15370
|
+
async function acceptSiteInvitation(apiClient, { body, options }) {
|
|
15468
15371
|
try {
|
|
15469
15372
|
return unwrapResponse(await apiClient({
|
|
15470
|
-
endpoint: "
|
|
15471
|
-
|
|
15472
|
-
siteId,
|
|
15473
|
-
seriesId,
|
|
15474
|
-
occurrenceId
|
|
15475
|
-
},
|
|
15476
|
-
body: payload,
|
|
15373
|
+
endpoint: "acceptSiteInvitation",
|
|
15374
|
+
body,
|
|
15477
15375
|
options
|
|
15478
15376
|
}));
|
|
15479
15377
|
} catch (error) {
|
|
15480
|
-
normalizeApiError(error, "Failed to
|
|
15378
|
+
return normalizeApiError(error, "Failed to accept invitation");
|
|
15379
|
+
}
|
|
15380
|
+
}
|
|
15381
|
+
|
|
15382
|
+
//#endregion
|
|
15383
|
+
//#region ../api/src/staffAccess.ts
|
|
15384
|
+
function roleAtLeast$1(role, minimum) {
|
|
15385
|
+
return SITE_ROLE_PERMISSION_LEVEL[role] >= SITE_ROLE_PERMISSION_LEVEL[minimum];
|
|
15386
|
+
}
|
|
15387
|
+
function roleAtLeastOrNone(role, minimum) {
|
|
15388
|
+
if (!role) return false;
|
|
15389
|
+
return roleAtLeast$1(role, minimum);
|
|
15390
|
+
}
|
|
15391
|
+
function appointmentResourceIdFromStaffMemberId(staffMemberId) {
|
|
15392
|
+
return unsafeAsAppointmentResourceId(staffMemberId);
|
|
15393
|
+
}
|
|
15394
|
+
function normalizeStaffCapabilities(capabilities) {
|
|
15395
|
+
if (!capabilities) return null;
|
|
15396
|
+
return STAFF_OPERATIONAL_CAPABILITIES.filter((capability) => capabilities.includes(capability));
|
|
15397
|
+
}
|
|
15398
|
+
function deriveStaffScope(input) {
|
|
15399
|
+
const staffMemberId = input.linkedStaffMemberId ? unsafeAsStaffMemberId(input.linkedStaffMemberId) : null;
|
|
15400
|
+
const persistedCapabilities = normalizeStaffCapabilities(input.staffCapabilities);
|
|
15401
|
+
switch (input.membershipRole) {
|
|
15402
|
+
case null:
|
|
15403
|
+
case "owner":
|
|
15404
|
+
case "administrator":
|
|
15405
|
+
case "editor": return { kind: "none" };
|
|
15406
|
+
case "team_member":
|
|
15407
|
+
if (!staffMemberId) {
|
|
15408
|
+
return {
|
|
15409
|
+
kind: "staff_setup_required",
|
|
15410
|
+
reason: "missing_staff_member"
|
|
15411
|
+
};
|
|
15412
|
+
}
|
|
15413
|
+
if (persistedCapabilities === null) {
|
|
15414
|
+
return {
|
|
15415
|
+
kind: "appointments",
|
|
15416
|
+
staffMemberId
|
|
15417
|
+
};
|
|
15418
|
+
}
|
|
15419
|
+
if (persistedCapabilities.length === 0) {
|
|
15420
|
+
return {
|
|
15421
|
+
kind: "staff_setup_required",
|
|
15422
|
+
reason: "missing_capabilities"
|
|
15423
|
+
};
|
|
15424
|
+
}
|
|
15425
|
+
if (persistedCapabilities.includes("appointments") && persistedCapabilities.includes("event_ops")) {
|
|
15426
|
+
return {
|
|
15427
|
+
kind: "hybrid",
|
|
15428
|
+
staffMemberId
|
|
15429
|
+
};
|
|
15430
|
+
}
|
|
15431
|
+
if (persistedCapabilities.includes("event_ops")) {
|
|
15432
|
+
return {
|
|
15433
|
+
kind: "event_ops_only",
|
|
15434
|
+
staffMemberId
|
|
15435
|
+
};
|
|
15436
|
+
}
|
|
15437
|
+
return {
|
|
15438
|
+
kind: "appointments",
|
|
15439
|
+
staffMemberId
|
|
15440
|
+
};
|
|
15441
|
+
}
|
|
15442
|
+
}
|
|
15443
|
+
function isStaffScoped(scope) {
|
|
15444
|
+
return scope.kind !== "none";
|
|
15445
|
+
}
|
|
15446
|
+
function isTeamMemberScope(scope) {
|
|
15447
|
+
return scope.kind === "appointments" || scope.kind === "staff_setup_required";
|
|
15448
|
+
}
|
|
15449
|
+
function hasAppointmentStaffAccess(scope) {
|
|
15450
|
+
return scope.kind === "appointments" || scope.kind === "hybrid";
|
|
15451
|
+
}
|
|
15452
|
+
function hasEventOpsAccess(scope) {
|
|
15453
|
+
return scope.kind === "event_ops_only" || scope.kind === "hybrid";
|
|
15454
|
+
}
|
|
15455
|
+
function getLinkedStaffMemberId(scope) {
|
|
15456
|
+
switch (scope.kind) {
|
|
15457
|
+
case "appointments":
|
|
15458
|
+
case "event_ops_only":
|
|
15459
|
+
case "hybrid": return scope.staffMemberId;
|
|
15460
|
+
case "none":
|
|
15461
|
+
case "staff_setup_required": return null;
|
|
15462
|
+
}
|
|
15463
|
+
}
|
|
15464
|
+
function getOwnAppointmentResourceId(scope) {
|
|
15465
|
+
const staffMemberId = getLinkedStaffMemberId(scope);
|
|
15466
|
+
return staffMemberId ? appointmentResourceIdFromStaffMemberId(staffMemberId) : null;
|
|
15467
|
+
}
|
|
15468
|
+
function canAccessOwnAppointmentResource(scope, resourceId) {
|
|
15469
|
+
const ownResourceId = getOwnAppointmentResourceId(scope);
|
|
15470
|
+
if (!ownResourceId) return false;
|
|
15471
|
+
const normalizedResourceId = typeof resourceId === "string" ? unsafeAsAppointmentResourceId(resourceId) : resourceId;
|
|
15472
|
+
return ownResourceId === normalizedResourceId;
|
|
15473
|
+
}
|
|
15474
|
+
function deriveLinkedAppointmentIdentity(input) {
|
|
15475
|
+
if (!input.membershipRole || !input.linkedStaffMemberId) {
|
|
15476
|
+
return { kind: "none" };
|
|
15477
|
+
}
|
|
15478
|
+
const staffMemberId = unsafeAsStaffMemberId(input.linkedStaffMemberId);
|
|
15479
|
+
return {
|
|
15480
|
+
kind: "linked",
|
|
15481
|
+
staffMemberId,
|
|
15482
|
+
resourceId: appointmentResourceIdFromStaffMemberId(staffMemberId)
|
|
15483
|
+
};
|
|
15484
|
+
}
|
|
15485
|
+
function getIdentityStaffMemberId(identity) {
|
|
15486
|
+
switch (identity.kind) {
|
|
15487
|
+
case "linked": return identity.staffMemberId;
|
|
15488
|
+
case "none": return null;
|
|
15489
|
+
}
|
|
15490
|
+
}
|
|
15491
|
+
function getIdentityStaffMemberIdValue(identity) {
|
|
15492
|
+
return getIdentityStaffMemberId(identity);
|
|
15493
|
+
}
|
|
15494
|
+
function getIdentityAppointmentResourceId(identity) {
|
|
15495
|
+
return identity.kind === "linked" ? identity.resourceId : null;
|
|
15496
|
+
}
|
|
15497
|
+
function getIdentityAppointmentResourceIdValue(identity) {
|
|
15498
|
+
return getIdentityAppointmentResourceId(identity);
|
|
15499
|
+
}
|
|
15500
|
+
function isLinkedAppointmentResource(identity, resourceId) {
|
|
15501
|
+
if (identity.kind !== "linked") return false;
|
|
15502
|
+
return identity.resourceId === resourceId;
|
|
15503
|
+
}
|
|
15504
|
+
function deriveStaffSiteHomeDestination(staffScope) {
|
|
15505
|
+
if (hasAppointmentStaffAccess(staffScope)) {
|
|
15506
|
+
return { kind: "staff_appointments" };
|
|
15507
|
+
}
|
|
15508
|
+
if (hasEventOpsAccess(staffScope)) {
|
|
15509
|
+
return { kind: "ops_site_events" };
|
|
15481
15510
|
}
|
|
15511
|
+
return { kind: "dashboard_site_home" };
|
|
15512
|
+
}
|
|
15513
|
+
function canUseAppointmentsLabel(staffScope) {
|
|
15514
|
+
return staffScope.kind === "appointments" || staffScope.kind === "hybrid" || staffScope.kind === "staff_setup_required";
|
|
15515
|
+
}
|
|
15516
|
+
function isEventOpsOnlyUser(sites) {
|
|
15517
|
+
if (sites.length === 0) return false;
|
|
15518
|
+
return sites.every((site) => deriveStaffScope(site).kind === "event_ops_only");
|
|
15519
|
+
}
|
|
15520
|
+
function deriveSiteListingNavigationPlan(site) {
|
|
15521
|
+
const staffScope = deriveStaffScope(site);
|
|
15522
|
+
const staffHomeDestination = deriveStaffSiteHomeDestination(staffScope);
|
|
15523
|
+
const isStaffScopedMembership = isStaffScoped(staffScope);
|
|
15524
|
+
return {
|
|
15525
|
+
canUseDashboard: roleAtLeastOrNone(site.membershipRole, "editor") || isStaffScopedMembership,
|
|
15526
|
+
primaryDestination: isStaffScopedMembership ? staffHomeDestination : { kind: "dashboard_site_open" },
|
|
15527
|
+
manageDestination: isStaffScopedMembership ? staffHomeDestination : { kind: "dashboard_site_home" },
|
|
15528
|
+
manageLabel: staffScope.kind === "event_ops_only" ? "Ops" : canUseAppointmentsLabel(staffScope) ? "Appointments" : "Manage"
|
|
15529
|
+
};
|
|
15530
|
+
}
|
|
15531
|
+
function shouldRedirectStaffScopeToOps(staffScope) {
|
|
15532
|
+
return staffScope.kind === "event_ops_only";
|
|
15533
|
+
}
|
|
15534
|
+
function deriveOpsLandingDestination(sites) {
|
|
15535
|
+
const onlySite = sites.length === 1 ? sites[0] : undefined;
|
|
15536
|
+
return onlySite?.id ? {
|
|
15537
|
+
kind: "ops_site_events",
|
|
15538
|
+
siteId: onlySite.id
|
|
15539
|
+
} : { kind: "ops_home" };
|
|
15482
15540
|
}
|
|
15483
15541
|
|
|
15542
|
+
//#endregion
|
|
15543
|
+
//#region ../api/src/common/cancellation.ts
|
|
15544
|
+
const STAFF_CANCELLATION_CONTEXT_UI_CATEGORIES = [
|
|
15545
|
+
"customer_requested",
|
|
15546
|
+
"schedule_conflict",
|
|
15547
|
+
"duplicate_or_admin_error",
|
|
15548
|
+
"non_payment",
|
|
15549
|
+
"policy_or_eligibility",
|
|
15550
|
+
"other"
|
|
15551
|
+
];
|
|
15552
|
+
const STAFF_CANCELLATION_CONTEXT_CATEGORIES = [...STAFF_CANCELLATION_CONTEXT_UI_CATEGORIES, "legacy_client_unspecified"];
|
|
15553
|
+
|
|
15554
|
+
//#endregion
|
|
15555
|
+
//#region ../api/src/siteOperations.ts
|
|
15556
|
+
const FLEXIBLE_BALANCE_ADMIN_ACTION_SOURCES = [
|
|
15557
|
+
"order_detail",
|
|
15558
|
+
"payment_issues_queue",
|
|
15559
|
+
"appointment_detail",
|
|
15560
|
+
"event_attendee_detail",
|
|
15561
|
+
"event_series_registration_detail"
|
|
15562
|
+
];
|
|
15563
|
+
|
|
15484
15564
|
//#endregion
|
|
15485
15565
|
//#region ../api/src/navigation/visibility.ts
|
|
15486
15566
|
/**
|
|
@@ -16291,19 +16371,8 @@ function buildDashboardBookingsNavPlan(modules, ctx, catalogue) {
|
|
|
16291
16371
|
if (policy.kind === "staff_scoped") {
|
|
16292
16372
|
return buildStaffScopedBookingsNavPlan(policy);
|
|
16293
16373
|
}
|
|
16294
|
-
const groups = [{
|
|
16295
|
-
label: "Overview",
|
|
16296
|
-
items: [{
|
|
16297
|
-
label: "Dashboard",
|
|
16298
|
-
destination: { kind: "bookings_dashboard" },
|
|
16299
|
-
keywords: [
|
|
16300
|
-
"overview",
|
|
16301
|
-
"summary",
|
|
16302
|
-
"activity"
|
|
16303
|
-
]
|
|
16304
|
-
}]
|
|
16305
|
-
}];
|
|
16306
16374
|
const hasEnabledModules = modules.appointments || modules.events;
|
|
16375
|
+
const groups = [buildOverviewNavGroup(hasEnabledModules)];
|
|
16307
16376
|
groups.push({
|
|
16308
16377
|
label: "Events",
|
|
16309
16378
|
items: modules.events ? [
|
|
@@ -16520,6 +16589,32 @@ function buildDashboardBookingsNavPlan(modules, ctx, catalogue) {
|
|
|
16520
16589
|
}
|
|
16521
16590
|
return groups;
|
|
16522
16591
|
}
|
|
16592
|
+
/** At a glance: the bookings Overview, and the Timetable once anything can be booked. */
|
|
16593
|
+
function buildOverviewNavGroup(hasEnabledModules) {
|
|
16594
|
+
return {
|
|
16595
|
+
label: "At a glance",
|
|
16596
|
+
items: [{
|
|
16597
|
+
label: "Overview",
|
|
16598
|
+
destination: { kind: "bookings_dashboard" },
|
|
16599
|
+
keywords: [
|
|
16600
|
+
"overview",
|
|
16601
|
+
"summary",
|
|
16602
|
+
"activity"
|
|
16603
|
+
]
|
|
16604
|
+
}, ...hasEnabledModules ? [{
|
|
16605
|
+
label: "Timetable",
|
|
16606
|
+
destination: { kind: "timetable" },
|
|
16607
|
+
keywords: [
|
|
16608
|
+
"schedule",
|
|
16609
|
+
"calendar",
|
|
16610
|
+
"week",
|
|
16611
|
+
"diary",
|
|
16612
|
+
"what's on",
|
|
16613
|
+
"clashes"
|
|
16614
|
+
]
|
|
16615
|
+
}] : []]
|
|
16616
|
+
};
|
|
16617
|
+
}
|
|
16523
16618
|
function buildStaffScopedBookingsNavPlan(policy) {
|
|
16524
16619
|
if (policy.appointments.kind !== "calendar") return [];
|
|
16525
16620
|
const appointmentItems = [{
|
|
@@ -23107,4 +23202,4 @@ function optionalTitle(data) {
|
|
|
23107
23202
|
}
|
|
23108
23203
|
|
|
23109
23204
|
//#endregion
|
|
23110
|
-
export { buildStorageUrl as $, createClientForEnv as $t, formatDiffDetail as A, createOutput as An, convertPulledFooterToLocal as At, formatFieldPath as B, linkPayloadSchema as Bn,
|
|
23205
|
+
export { buildStorageUrl as $, createClientForEnv as $t, formatDiffDetail as A, createOutput as An, convertPulledFooterToLocal as At, formatFieldPath as B, linkPayloadSchema as Bn, extractApiErrorMessage as Bt, reportFromOutcomes as C, planEnvironmentWriteAuthorization as Cn, readJsonFile as Ct, normalizeRemoteHomepageReference as D, formatError as Dn, duplicateNavigationIdentityMessage as Dt, createEmptySyncResult as E, ErrorCodes as En, isMediaValue as Et, calculatePullDiff as F, loadSdkEnvironment as Fn, applyPushConfigPlan as Ft, prepareLocalMediaForPush as G, createBearerAPIClient as Gt, syncLocalMediaForPush as H, sdkConfiguredBookingVerticalConfigSchema as Hn, isRecord$5 as Ht, buildJsonDiff as I, createManagementClient as In, createPushConfigCommand as It, writeBinaryFileAtomic as J, deriveCmsApiUrlFromDashboardUrl as Jt, admitBinaryWriteDestination as K, ApiEnvelopeError as Kt, calculateEntryDiffs as L, ManagementApiError as Ln, loadPushConfigTarget as Lt, hasPendingChanges as M, getEnvironmentDebugInfo as Mn, convertPulledPagesToLocal as Mt, findChangedFieldsWithValues as N, isLocalConfigured as Nn, navigationCommandItemToManagementInput as Nt, calculateDiff as O, transportSuggestion as On, navigationIdentity as Ot, formatHumanDiff as P, isRemoteConfigured as Pn, normalizeNavigationConfigItemForCommand as Pt, walkLocalContentMedia as Q, confirmAction as Qt, buildFieldChanges as R, sdkEventScheduleSchema as Rn, preparePushConfigTarget as Rt, planCliCommandBatchForBindings as S, planEnvironmentExecution as Sn, listFiles as St, executeResidualSettingsSyncPlan as T, withRiverbankEnv as Tn, writeJsonFileAtomic as Tt, syncLocalMediaForPull as U, APPOINTMENT_SERVICE_VISIBILITY_VALUES as Ut, revalidateLocalMediaPushPlan as V, portablePageDesignSchema as Vn, normalizeApiError as Vt, applyLocalMediaPush as W, ApiRequestError as Wt, normalizeMediaIdentifierInput as X, mapEnvironmentExecutionOutcomesJson as Xt, sha256 as Y, API_ENDPOINTS as Yt, normalizeMediaValueToIdentifierOnly as Z, bindRequestTimeout as Zt, commandExecutionToReport as _, withConfirmation as _n, readMetadataFile as _t, executeEntryCommandPlanAsReport as a, formatDateShort as an, readEventCategoriesMeta as at, executeCompiledCliCommandPlanAsReport as b, executeEnvironmentPlan as bn, ensureDir$1 as bt, titleFromEntryData as c, handleCommandError as cn, readFormsMeta as ct, unsafeAsCliEntryIdentifier as d, parseJsonArray as dn, readSettingsMeta as dt, createCommandContext as en, downloadMedia as et, unsafeAsCliFormSlug as f, parseJsonData as fn, readVenuesMeta as ft, commandCompileFailureToReport as g, withCommandContext as gn, metadataTargetFromEnvironment as gt, mutationCounterForEffect as h, runWithInvocation as hn, metadataFilePath as ht, entrySiteCommandTypes as i, currentInvocationEnvelope as in, readAllMeta as it, formatDiffSummary as j, getEnvPrefix as jn, convertPulledNavigationToLocal as jt, calculateFooterDiff as k, CliExit as kn, navigationIdentityMap as kt, unsafeAsCliBlockIdentifier as l, managementClientConfigFor as ln, readNavigationMeta as lt, commandReportToSyncResult as m, resolveCommandPath as mn, ensureMetadataDir as mt, entryCommandCompileErrorToReport as n, createListCommand as nn, findLegacyEntryFiles as nt, executeEntryCommandPlanAsSyncResult as o, getOutputContext as on, readEventsMeta as ot, unsafeAsCliPageIdentifier as p, parsePaginationOptions as pn, writeMediaManifest as pt, readStableBinaryFile as q, unwrapResponse as qt, entryCommandReportToSyncResult as r, createOutputForCommand as rn, readEntries as rt, syncResultForEntryCommandCompileError as s, getOutputOnlyContext as sn, readFooterMeta as st, compileEntryPushCommands as t, createGetCommand as tn, extractMediaIdentifiers as tt, unsafeAsCliContentTypeKey as u, mapEntryForOutput as un, readPagesMeta as ut, commandReportHasFailures as v, withErrorHandling as vn, writeMetadataFile as vt, mergeManySyncResults as w, requireEnvironmentWriteAuthorization as wn, writeJsonFile$1 as wt, formatManagementError as x, parseEnvironmentSelection as xn, fileExists$1 as xt, executeCliCommandPlan as y, withOutputOnlyErrorHandling as yn, writeMetadataRecordMap as yt, formatFieldChange as z, sdkSettingsUpdateSchema as zn, revalidatePushConfigPlan as zt };
|