riverbankcms 0.28.0 → 0.29.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 +2680 -5701
- package/dist/{entryCommands-Bus1LjSx.mjs → entryCommands-5kz7nIFG.mjs} +2993 -302
- package/dist/{eventUpsertInputSchema-COwkcsq7.mjs → eventUpsertInputSchema-DEN6k1oM.mjs} +363 -39
- package/dist/load-config-DkoaFO1t.mjs +4 -0
- package/dist/{load-config-9DBnYiBm.mjs → load-config-JcidEUjc.mjs} +16 -6
- package/dist/{plan-DU4T2YGq.mjs → plan-BppGb0gS.mjs} +1 -1
- package/dist/scaffold/index.mjs +2 -2
- package/dist/{sdk-event-upsert-BrJvTQ-D.mjs → sdk-event-upsert-CXc13KPT.mjs} +9751 -3545
- package/dist/{src-D9-RAM-l.mjs → src-4CBCWovR.mjs} +668 -480
- package/dist/testing.mjs +4 -4
- package/package.json +1 -1
- package/dist/load-config-DX04ekBQ.mjs +0 -4
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { A as evaluateSiteCommandExposure, E as parseAndPlanCommandBatchForTypes, F as entryIdentifierForCommand, It as themeSchema, K as bookingQuestionAttachmentSchema,
|
|
3
|
-
import {
|
|
4
|
-
import { n as CliSdkPeerError, t as loadConfigFile } from "./load-config-
|
|
2
|
+
import { $n as slugifyMediaIdentifier, $t as footerLookIdValues, A as evaluateSiteCommandExposure, Bt as BREAKPOINT_XL, E as parseAndPlanCommandBatchForTypes, F as entryIdentifierForCommand, In as parseAspectRatioValue, It as themeSchema, K as bookingQuestionAttachmentSchema, Lr as parseSurfaceTree, Lt as BREAKPOINT_LG, Qn as resolveMediaIdentifier, Qt as asFooterLookId, Rt as BREAKPOINT_MD, T as createDryRunReport, V as eventBookingOfferIntentSchema, X as createCommandRef, Yn as buildMediaIdentifierFromFilename, Z as createContentEntryRef, _r as asAppearancePresetId, an as pageDesignBoundaryTreatmentSchema, ar as normalizeAmbiguousStoragePathForSite, br as asPageFlowPresetId, cr as MAX_MANAGEMENT_MEDIA_UPLOAD_BYTES, ct as require_dist, en as asHeaderLayoutId, gr as arrangementModes, in as buttonStyleSelectionSchema, it as createSiteCommandSiteRef, j as getSiteCommandMetadata, k as evaluateSiteCommandBatchExposure, m as isInternalResolvedLinkValue, n as sdkCourseBookingSettingsSchema, nn as headerLayoutIdValues, nr as getExtensionForMimeType, on as pageDesignIntentSchemaVersion, r as sdkEventOccurrenceInputSchema, rn as headerStyleIdValues, rr as getMimeType, s as RESOLVED_INTERNAL_LINK_ENTITY_TYPES, sn as footerPreferencesSchema, sr as FILE_SIZE_LIMITS, st as slugPart, tn as asHeaderStyleId, w as stableStringify, x as resolvePageUpsertSeo, xr as asSiteStyleId, xt as headerPreferencesSchema, y as eventVisibilitySchema, zn as getDirectImageUrlForSiteScopedPath, zt as BREAKPOINT_SM } from "./sdk-event-upsert-CXc13KPT.mjs";
|
|
3
|
+
import { $ as locationValueSchema, D as parseManagementKey, E as exposeSecret, J as assertAllowedPublicUrl, Kn as parsePageId, Ot as resolveBookingTimeZoneId, R as parseMailingListProviderScope, T as createSecret, Tt as parseBookingLocalDate, Yn as parseSiteId, a as resolveEventCatalogue, bn as unsafeAsAppointmentResourceId, c as areConfiguredBookingVerticalConfigsEqual, ct as formatDate, d as parseVerticalPresetId, f as resolveSiteVerticalBookingConfig, gn as assertNever, ir as unsafeAsOccurrenceId, l as canonicalizeConfiguredBookingVerticalConfig, m as EVENT_PRESET_VALUES, n as committedWriteProofSchema, nr as unsafeAsEventOfferingId, or as err, p as DEFAULT_EVENT_TYPE_PRESETS, rr as unsafeAsEventSeriesId, sr as ok, t as OFFLINE_PAYMENT_METHODS, u as parseVerticalId, v as getPublicReadCachePolicy, xn as unsafeAsStaffMemberId, y as publicReadFreshnessSeconds, z as parseFooterCompositionV1 } from "./src-4CBCWovR.mjs";
|
|
4
|
+
import { n as CliSdkPeerError, t as loadConfigFile } from "./load-config-JcidEUjc.mjs";
|
|
5
5
|
import { Command } from "commander";
|
|
6
6
|
import { AsyncLocalStorage } from "node:async_hooks";
|
|
7
7
|
import * as fs$1 from "fs/promises";
|
|
8
8
|
import * as path$1 from "path";
|
|
9
9
|
import * as readline from "readline";
|
|
10
|
-
import { z } from "zod";
|
|
10
|
+
import { safeParse, z } from "zod";
|
|
11
11
|
import { constants } from "node:fs";
|
|
12
12
|
import { basename, dirname as dirname$1, join } from "node:path";
|
|
13
13
|
import { riverbankSiteConfigSchema, validateFieldIdConflicts } from "@riverbankcms/sdk/config/validation";
|
|
@@ -82,20 +82,20 @@ function isApiError(result) {
|
|
|
82
82
|
function isApiSuccess(result) {
|
|
83
83
|
return result.success === true;
|
|
84
84
|
}
|
|
85
|
-
function isRecord$
|
|
85
|
+
function isRecord$11(value) {
|
|
86
86
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
87
87
|
}
|
|
88
88
|
function isFieldError$1(value) {
|
|
89
|
-
return isRecord$
|
|
89
|
+
return isRecord$11(value) && typeof value.field === "string" && typeof value.code === "string" && typeof value.message === "string" && (value.capacityOverride === undefined || isCapacityOverrideRefusal(value.capacityOverride));
|
|
90
90
|
}
|
|
91
91
|
function fieldErrorsFromUnknown(value) {
|
|
92
92
|
if (!Array.isArray(value)) return null;
|
|
93
93
|
return value.every(isFieldError$1) ? value : null;
|
|
94
94
|
}
|
|
95
95
|
function extractEnvelopeErrorObject(body) {
|
|
96
|
-
if (!isRecord$
|
|
96
|
+
if (!isRecord$11(body)) return null;
|
|
97
97
|
const error = body.error;
|
|
98
|
-
if (!isRecord$
|
|
98
|
+
if (!isRecord$11(error)) return null;
|
|
99
99
|
if ("success" in body && body.success !== false) {
|
|
100
100
|
return null;
|
|
101
101
|
}
|
|
@@ -306,7 +306,7 @@ function userFacingZodErrorMap(issue) {
|
|
|
306
306
|
return getUserFacingValidationMessage(issue);
|
|
307
307
|
}
|
|
308
308
|
function safeParseUserInput(schema, input) {
|
|
309
|
-
return
|
|
309
|
+
return safeParse(schema, input, { error: userFacingZodErrorMap });
|
|
310
310
|
}
|
|
311
311
|
function toFieldErrors(zodError, options = {}) {
|
|
312
312
|
return zodError.issues.map((issue) => ({
|
|
@@ -339,6 +339,24 @@ function toFlattenedFieldErrors(zodError, options) {
|
|
|
339
339
|
//#region ../api/src/retentionPolicies.ts
|
|
340
340
|
/** Pure policy catalogue shared by retention execution and its API projections. */
|
|
341
341
|
const retentionPolicyRegistry = {
|
|
342
|
+
assistant_messages: {
|
|
343
|
+
dataClass: "assistant_messages",
|
|
344
|
+
policyKind: "platform_auto_delete",
|
|
345
|
+
trigger: "created_at",
|
|
346
|
+
window: {
|
|
347
|
+
kind: "relative",
|
|
348
|
+
unit: "months",
|
|
349
|
+
value: 12
|
|
350
|
+
},
|
|
351
|
+
action: "hard_delete",
|
|
352
|
+
automation: {
|
|
353
|
+
kind: "scheduled",
|
|
354
|
+
jobKind: "assistant_conversation_cleanup",
|
|
355
|
+
executionModes: ["dry_run", "scheduled"],
|
|
356
|
+
executionSubstrate: "vercel_cron"
|
|
357
|
+
},
|
|
358
|
+
rationale: "Each assistant message expires after twelve months, regardless of later conversation activity. Empty conversations and their choices are removed too."
|
|
359
|
+
},
|
|
342
360
|
portal_sessions: {
|
|
343
361
|
dataClass: "portal_sessions",
|
|
344
362
|
policyKind: "platform_auto_delete",
|
|
@@ -810,7 +828,9 @@ const PREVIEW_BLOCKED_REASONS = [
|
|
|
810
828
|
"duplicate",
|
|
811
829
|
"run_closed",
|
|
812
830
|
"past_date",
|
|
813
|
-
"invalid_schedule"
|
|
831
|
+
"invalid_schedule",
|
|
832
|
+
"stale",
|
|
833
|
+
"invalid_state"
|
|
814
834
|
];
|
|
815
835
|
|
|
816
836
|
//#endregion
|
|
@@ -859,6 +879,7 @@ const pageCommitReasonSchema = z.enum([
|
|
|
859
879
|
"invalid-document",
|
|
860
880
|
"invalid-history",
|
|
861
881
|
"path-unavailable",
|
|
882
|
+
"homepage-publication-required",
|
|
862
883
|
"invalid-page-version",
|
|
863
884
|
"invalid-block-reference",
|
|
864
885
|
"invalid-content-reference",
|
|
@@ -905,6 +926,7 @@ const reasonMessages = {
|
|
|
905
926
|
"invalid-revision": "The saved page version could not be read. Reload the page before continuing.",
|
|
906
927
|
"invalid-document": "The saved page content could not be read. Reload the page before continuing.",
|
|
907
928
|
"invalid-history": "The saved page history could not be read. Reload the page before continuing.",
|
|
929
|
+
"homepage-publication-required": "Publish this page first. Your current homepage is live, so the page that replaces it has to be published too.",
|
|
908
930
|
"path-unavailable": "This address is already in use. Choose a different page address.",
|
|
909
931
|
"invalid-page-version": "This saved page version could not be read. Choose another version.",
|
|
910
932
|
"invalid-block-reference": "A saved block is missing from this page. Reload the page before continuing.",
|
|
@@ -934,6 +956,44 @@ function pageCommitReasonFromTag(tag) {
|
|
|
934
956
|
//#endregion
|
|
935
957
|
//#region ../api/src/pageJournalEndpoints.ts
|
|
936
958
|
const PAGE_JOURNAL_ENDPOINT_DEFINITIONS = {
|
|
959
|
+
discardAssistantPage: {
|
|
960
|
+
path: "/sites/{siteId}/pages/{pageId}/assistant-discard",
|
|
961
|
+
method: "POST",
|
|
962
|
+
auth: "user",
|
|
963
|
+
responseKind: "json",
|
|
964
|
+
editLease: "unleased-editor",
|
|
965
|
+
errors: [
|
|
966
|
+
"validation:invalid_input",
|
|
967
|
+
"auth:forbidden",
|
|
968
|
+
"server:internal_error"
|
|
969
|
+
]
|
|
970
|
+
},
|
|
971
|
+
constructAssistantPage: {
|
|
972
|
+
path: "/sites/{siteId}/pages/assistant",
|
|
973
|
+
method: "POST",
|
|
974
|
+
auth: "user",
|
|
975
|
+
responseKind: "json",
|
|
976
|
+
editLease: "unleased-editor",
|
|
977
|
+
errors: [
|
|
978
|
+
"validation:invalid_input",
|
|
979
|
+
"auth:forbidden",
|
|
980
|
+
"resource:conflict",
|
|
981
|
+
"server:internal_error"
|
|
982
|
+
]
|
|
983
|
+
},
|
|
984
|
+
commitPageChangeBatch: {
|
|
985
|
+
path: "/sites/{siteId}/pages/{pageId}/journal",
|
|
986
|
+
method: "PUT",
|
|
987
|
+
auth: "user",
|
|
988
|
+
responseKind: "json",
|
|
989
|
+
editLease: "managed",
|
|
990
|
+
errors: [
|
|
991
|
+
"validation:invalid_input",
|
|
992
|
+
"resource:not_found",
|
|
993
|
+
"resource:conflict",
|
|
994
|
+
"server:internal_error"
|
|
995
|
+
]
|
|
996
|
+
},
|
|
937
997
|
getPagePublishReadinessFacts: {
|
|
938
998
|
path: "/sites/{siteId}/pages/readiness-facts",
|
|
939
999
|
method: "POST",
|
|
@@ -2836,48 +2896,6 @@ const NEWSLETTER_ENDPOINT_DEFINITIONS = defineEndpointDefinitions({
|
|
|
2836
2896
|
auth: "user",
|
|
2837
2897
|
responseKind: "json"
|
|
2838
2898
|
},
|
|
2839
|
-
listNewsletterTemplates: {
|
|
2840
|
-
path: "/sites/{siteId}/newsletter/templates",
|
|
2841
|
-
method: "GET",
|
|
2842
|
-
tags: [TAG_TEMPLATES.site, TAG_TEMPLATES.siteNewsletter],
|
|
2843
|
-
auth: "user",
|
|
2844
|
-
responseKind: "json"
|
|
2845
|
-
},
|
|
2846
|
-
createNewsletterTemplate: {
|
|
2847
|
-
path: "/sites/{siteId}/newsletter/templates",
|
|
2848
|
-
method: "POST",
|
|
2849
|
-
tags: [TAG_TEMPLATES.site, TAG_TEMPLATES.siteNewsletter],
|
|
2850
|
-
auth: "user",
|
|
2851
|
-
responseKind: "json"
|
|
2852
|
-
},
|
|
2853
|
-
getNewsletterTemplate: {
|
|
2854
|
-
path: "/sites/{siteId}/newsletter/templates/{templateId}",
|
|
2855
|
-
method: "GET",
|
|
2856
|
-
tags: [TAG_TEMPLATES.site, TAG_TEMPLATES.siteNewsletter],
|
|
2857
|
-
auth: "user",
|
|
2858
|
-
responseKind: "json"
|
|
2859
|
-
},
|
|
2860
|
-
updateNewsletterTemplate: {
|
|
2861
|
-
path: "/sites/{siteId}/newsletter/templates/{templateId}",
|
|
2862
|
-
method: "PATCH",
|
|
2863
|
-
tags: [TAG_TEMPLATES.site, TAG_TEMPLATES.siteNewsletter],
|
|
2864
|
-
auth: "user",
|
|
2865
|
-
responseKind: "json"
|
|
2866
|
-
},
|
|
2867
|
-
deleteNewsletterTemplate: {
|
|
2868
|
-
path: "/sites/{siteId}/newsletter/templates/{templateId}",
|
|
2869
|
-
method: "DELETE",
|
|
2870
|
-
tags: [TAG_TEMPLATES.site, TAG_TEMPLATES.siteNewsletter],
|
|
2871
|
-
auth: "user",
|
|
2872
|
-
responseKind: "json"
|
|
2873
|
-
},
|
|
2874
|
-
previewNewsletterTemplate: {
|
|
2875
|
-
path: "/sites/{siteId}/newsletter/templates/{templateId}/preview",
|
|
2876
|
-
method: "POST",
|
|
2877
|
-
tags: [TAG_TEMPLATES.site, TAG_TEMPLATES.siteNewsletter],
|
|
2878
|
-
auth: "user",
|
|
2879
|
-
responseKind: "json"
|
|
2880
|
-
},
|
|
2881
2899
|
listNewsletterCampaigns: {
|
|
2882
2900
|
path: "/sites/{siteId}/newsletter/campaigns",
|
|
2883
2901
|
method: "GET",
|
|
@@ -2935,6 +2953,38 @@ const NEWSLETTER_ENDPOINT_DEFINITIONS = defineEndpointDefinitions({
|
|
|
2935
2953
|
responseKind: "json",
|
|
2936
2954
|
demoMutation: DEMO_MUTATION_BLOCKED_HIDDEN
|
|
2937
2955
|
},
|
|
2956
|
+
listNewsletterWebsitePicker: {
|
|
2957
|
+
path: "/sites/{siteId}/newsletter/website-picker",
|
|
2958
|
+
method: "GET",
|
|
2959
|
+
auth: "user",
|
|
2960
|
+
responseKind: "json",
|
|
2961
|
+
tags: [TAG_TEMPLATES.site, TAG_TEMPLATES.siteNewsletter],
|
|
2962
|
+
errors: []
|
|
2963
|
+
},
|
|
2964
|
+
renderNewsletterCampaignDraft: {
|
|
2965
|
+
path: "/sites/{siteId}/newsletter/campaigns/{campaignId}/render-draft",
|
|
2966
|
+
method: "POST",
|
|
2967
|
+
auth: "user",
|
|
2968
|
+
responseKind: "json",
|
|
2969
|
+
tags: [TAG_TEMPLATES.site, TAG_TEMPLATES.siteNewsletter],
|
|
2970
|
+
errors: []
|
|
2971
|
+
},
|
|
2972
|
+
reviewNewsletterCampaign: {
|
|
2973
|
+
path: "/sites/{siteId}/newsletter/campaigns/{campaignId}/review",
|
|
2974
|
+
method: "POST",
|
|
2975
|
+
auth: "user",
|
|
2976
|
+
responseKind: "json",
|
|
2977
|
+
tags: [TAG_TEMPLATES.site, TAG_TEMPLATES.siteNewsletter],
|
|
2978
|
+
errors: []
|
|
2979
|
+
},
|
|
2980
|
+
duplicateNewsletterCampaign: {
|
|
2981
|
+
path: "/sites/{siteId}/newsletter/campaigns/{campaignId}/duplicate",
|
|
2982
|
+
method: "POST",
|
|
2983
|
+
auth: "user",
|
|
2984
|
+
responseKind: "json",
|
|
2985
|
+
tags: [TAG_TEMPLATES.site, TAG_TEMPLATES.siteNewsletter],
|
|
2986
|
+
errors: []
|
|
2987
|
+
},
|
|
2938
2988
|
scheduleNewsletterCampaign: {
|
|
2939
2989
|
path: "/sites/{siteId}/newsletter/campaigns/{campaignId}/schedule",
|
|
2940
2990
|
method: "POST",
|
|
@@ -3499,25 +3549,6 @@ const EVENT_MANAGEMENT_ENDPOINT_DEFINITIONS = defineEndpointDefinitions({
|
|
|
3499
3549
|
"server:internal_error"
|
|
3500
3550
|
]
|
|
3501
3551
|
},
|
|
3502
|
-
updateEventAddress: {
|
|
3503
|
-
editLease: "managed",
|
|
3504
|
-
path: "/sites/{siteId}/bookings/events/{eventId}/address",
|
|
3505
|
-
method: "PATCH",
|
|
3506
|
-
tags: [
|
|
3507
|
-
"site-{siteId}",
|
|
3508
|
-
"event-series-{siteId}",
|
|
3509
|
-
TAG_TEMPLATES.siteRoutableContent,
|
|
3510
|
-
TAG_TEMPLATES.siteRedirects
|
|
3511
|
-
],
|
|
3512
|
-
auth: "user",
|
|
3513
|
-
responseKind: "json",
|
|
3514
|
-
errors: [
|
|
3515
|
-
"validation:invalid_input",
|
|
3516
|
-
"resource:not_found",
|
|
3517
|
-
"resource:conflict",
|
|
3518
|
-
"server:internal_error"
|
|
3519
|
-
]
|
|
3520
|
-
},
|
|
3521
3552
|
createWholeSeriesRun: {
|
|
3522
3553
|
path: "/sites/{siteId}/bookings/events/{eventId}/runs",
|
|
3523
3554
|
method: "POST",
|
|
@@ -4097,6 +4128,14 @@ const BILLING_ENDPOINT_DEFINITIONS = defineEndpointDefinitions({
|
|
|
4097
4128
|
auth: "admin",
|
|
4098
4129
|
responseKind: "json"
|
|
4099
4130
|
},
|
|
4131
|
+
adminGrantTrial: {
|
|
4132
|
+
path: "/admin/billing/trial-grant",
|
|
4133
|
+
method: "POST",
|
|
4134
|
+
tags: ["admin", "billing"],
|
|
4135
|
+
auth: "admin",
|
|
4136
|
+
responseKind: "json",
|
|
4137
|
+
errors: []
|
|
4138
|
+
},
|
|
4100
4139
|
changePlan: {
|
|
4101
4140
|
path: "/billing/plan/change",
|
|
4102
4141
|
method: "POST",
|
|
@@ -4484,6 +4523,54 @@ const PAGE_SURFACE_EDITOR_ENDPOINT_DEFINITIONS = {
|
|
|
4484
4523
|
* person when nothing is attached to them.
|
|
4485
4524
|
*/
|
|
4486
4525
|
const BOOKING_PEOPLE_ENDPOINT_DEFINITIONS = defineEndpointDefinitions({
|
|
4526
|
+
previewPersonRemoval: {
|
|
4527
|
+
path: "/sites/{siteId}/bookings/people/{personId}/removal-preview",
|
|
4528
|
+
method: "GET",
|
|
4529
|
+
auth: "user",
|
|
4530
|
+
responseKind: "json",
|
|
4531
|
+
tags: ["site-{siteId}", "customers-{siteId}"],
|
|
4532
|
+
errors: [
|
|
4533
|
+
"auth:forbidden",
|
|
4534
|
+
"resource:not_found",
|
|
4535
|
+
"server:internal_error"
|
|
4536
|
+
]
|
|
4537
|
+
},
|
|
4538
|
+
removePerson: {
|
|
4539
|
+
path: "/sites/{siteId}/bookings/people/{personId}/removal",
|
|
4540
|
+
method: "POST",
|
|
4541
|
+
auth: "user",
|
|
4542
|
+
responseKind: "json",
|
|
4543
|
+
tags: ["site-{siteId}", "customers-{siteId}"],
|
|
4544
|
+
errors: [
|
|
4545
|
+
"auth:forbidden",
|
|
4546
|
+
"resource:not_found",
|
|
4547
|
+
"server:internal_error"
|
|
4548
|
+
]
|
|
4549
|
+
},
|
|
4550
|
+
getPersonRemovalStatus: {
|
|
4551
|
+
path: "/sites/{siteId}/bookings/people/{personId}/removal",
|
|
4552
|
+
method: "GET",
|
|
4553
|
+
auth: "user",
|
|
4554
|
+
responseKind: "json",
|
|
4555
|
+
tags: ["site-{siteId}", "customers-{siteId}"],
|
|
4556
|
+
errors: [
|
|
4557
|
+
"auth:forbidden",
|
|
4558
|
+
"resource:not_found",
|
|
4559
|
+
"server:internal_error"
|
|
4560
|
+
]
|
|
4561
|
+
},
|
|
4562
|
+
exportPersonData: {
|
|
4563
|
+
path: "/sites/{siteId}/bookings/people/{personId}/export.json",
|
|
4564
|
+
method: "GET",
|
|
4565
|
+
auth: "user",
|
|
4566
|
+
responseKind: "blob",
|
|
4567
|
+
tags: ["site-{siteId}", "customers-{siteId}"],
|
|
4568
|
+
errors: [
|
|
4569
|
+
"auth:forbidden",
|
|
4570
|
+
"resource:not_found",
|
|
4571
|
+
"server:internal_error"
|
|
4572
|
+
]
|
|
4573
|
+
},
|
|
4487
4574
|
getBookingPerson: {
|
|
4488
4575
|
path: "/sites/{siteId}/bookings/people/{personId}",
|
|
4489
4576
|
method: "GET",
|
|
@@ -4605,20 +4692,22 @@ const ENDPOINT_DEFINITIONS = {
|
|
|
4605
4692
|
auth: "user",
|
|
4606
4693
|
responseKind: "json"
|
|
4607
4694
|
},
|
|
4608
|
-
|
|
4609
|
-
path: "/
|
|
4695
|
+
assistantConversation: {
|
|
4696
|
+
path: "/assistant/conversation",
|
|
4610
4697
|
method: "POST",
|
|
4611
4698
|
auth: "user",
|
|
4612
|
-
responseKind: "
|
|
4699
|
+
responseKind: "json",
|
|
4700
|
+
errors: []
|
|
4613
4701
|
},
|
|
4614
|
-
|
|
4615
|
-
path: "/
|
|
4616
|
-
method: "
|
|
4702
|
+
assistantNewConversation: {
|
|
4703
|
+
path: "/assistant/conversation/new",
|
|
4704
|
+
method: "POST",
|
|
4617
4705
|
auth: "user",
|
|
4618
|
-
responseKind: "
|
|
4706
|
+
responseKind: "json",
|
|
4707
|
+
errors: []
|
|
4619
4708
|
},
|
|
4620
|
-
|
|
4621
|
-
path: "/assistant/
|
|
4709
|
+
assistantOutcome: {
|
|
4710
|
+
path: "/assistant/outcome",
|
|
4622
4711
|
method: "POST",
|
|
4623
4712
|
auth: "user",
|
|
4624
4713
|
responseKind: "json",
|
|
@@ -4645,42 +4734,6 @@ const ENDPOINT_DEFINITIONS = {
|
|
|
4645
4734
|
responseKind: "stream",
|
|
4646
4735
|
errors: []
|
|
4647
4736
|
},
|
|
4648
|
-
aiPlaygroundPageConvert: {
|
|
4649
|
-
path: "/sites/{siteId}/ai/playground/page-convert",
|
|
4650
|
-
method: "POST",
|
|
4651
|
-
auth: "user",
|
|
4652
|
-
responseKind: "json"
|
|
4653
|
-
},
|
|
4654
|
-
aiPlaygroundPageConvertJobsCreate: {
|
|
4655
|
-
path: "/sites/{siteId}/ai/playground/page-convert/jobs",
|
|
4656
|
-
method: "POST",
|
|
4657
|
-
auth: "user",
|
|
4658
|
-
responseKind: "json"
|
|
4659
|
-
},
|
|
4660
|
-
aiPlaygroundPageConvertJobsList: {
|
|
4661
|
-
path: "/sites/{siteId}/ai/playground/page-convert/jobs",
|
|
4662
|
-
method: "GET",
|
|
4663
|
-
auth: "user",
|
|
4664
|
-
responseKind: "json"
|
|
4665
|
-
},
|
|
4666
|
-
aiPlaygroundPageConvertJobGet: {
|
|
4667
|
-
path: "/sites/{siteId}/ai/playground/page-convert/jobs/{jobId}",
|
|
4668
|
-
method: "GET",
|
|
4669
|
-
auth: "user",
|
|
4670
|
-
responseKind: "json"
|
|
4671
|
-
},
|
|
4672
|
-
aiPlaygroundPageConvertJobRun: {
|
|
4673
|
-
path: "/sites/{siteId}/ai/playground/page-convert/jobs/{jobId}/run",
|
|
4674
|
-
method: "POST",
|
|
4675
|
-
auth: "user",
|
|
4676
|
-
responseKind: "json"
|
|
4677
|
-
},
|
|
4678
|
-
aiPlaygroundPageConvertJobEvents: {
|
|
4679
|
-
path: "/sites/{siteId}/ai/playground/page-convert/jobs/{jobId}/events",
|
|
4680
|
-
method: "GET",
|
|
4681
|
-
auth: "user",
|
|
4682
|
-
responseKind: "json"
|
|
4683
|
-
},
|
|
4684
4737
|
aiGenerateSite: {
|
|
4685
4738
|
path: "/sites/{siteId}/ai/generate",
|
|
4686
4739
|
method: "POST",
|
|
@@ -5463,6 +5516,32 @@ const ENDPOINT_DEFINITIONS = {
|
|
|
5463
5516
|
auth: "user",
|
|
5464
5517
|
responseKind: "json"
|
|
5465
5518
|
},
|
|
5519
|
+
getRouteAddress: {
|
|
5520
|
+
path: "/sites/{siteId}/content-address/{targetKind}/{targetId}",
|
|
5521
|
+
method: "GET",
|
|
5522
|
+
tags: [TAG_TEMPLATES.site, TAG_TEMPLATES.siteRoutableContent],
|
|
5523
|
+
auth: "user",
|
|
5524
|
+
responseKind: "json",
|
|
5525
|
+
errors: ["resource:not_found", "resource:conflict"]
|
|
5526
|
+
},
|
|
5527
|
+
changeRouteAddress: {
|
|
5528
|
+
editLease: "managed",
|
|
5529
|
+
path: "/sites/{siteId}/content-address/{targetKind}/{targetId}",
|
|
5530
|
+
method: "PATCH",
|
|
5531
|
+
tags: [
|
|
5532
|
+
TAG_TEMPLATES.site,
|
|
5533
|
+
TAG_TEMPLATES.siteRoutableContent,
|
|
5534
|
+
TAG_TEMPLATES.siteRedirects
|
|
5535
|
+
],
|
|
5536
|
+
auth: "user",
|
|
5537
|
+
responseKind: "json",
|
|
5538
|
+
errors: [
|
|
5539
|
+
"validation:invalid_input",
|
|
5540
|
+
"resource:not_found",
|
|
5541
|
+
"resource:conflict",
|
|
5542
|
+
"server:internal_error"
|
|
5543
|
+
]
|
|
5544
|
+
},
|
|
5466
5545
|
updateRouteMetadata: {
|
|
5467
5546
|
editLease: "managed",
|
|
5468
5547
|
path: "/sites/{siteId}/routes/{routeId}/metadata",
|
|
@@ -5653,6 +5732,30 @@ const ENDPOINT_DEFINITIONS = {
|
|
|
5653
5732
|
auth: "user",
|
|
5654
5733
|
responseKind: "json"
|
|
5655
5734
|
},
|
|
5735
|
+
previewWebsiteOperation: {
|
|
5736
|
+
path: "/sites/{siteId}/website/preview",
|
|
5737
|
+
method: "POST",
|
|
5738
|
+
auth: "user",
|
|
5739
|
+
responseKind: "json",
|
|
5740
|
+
errors: [
|
|
5741
|
+
"validation:invalid_input",
|
|
5742
|
+
"auth:insufficient_permissions",
|
|
5743
|
+
"server:internal_error"
|
|
5744
|
+
]
|
|
5745
|
+
},
|
|
5746
|
+
confirmWebsiteOperation: {
|
|
5747
|
+
editLease: "unleased-editor",
|
|
5748
|
+
path: "/sites/{siteId}/website/confirm",
|
|
5749
|
+
method: "POST",
|
|
5750
|
+
auth: "user",
|
|
5751
|
+
responseKind: "json",
|
|
5752
|
+
tags: [TAG_TEMPLATES.site, TAG_TEMPLATES.siteSettings],
|
|
5753
|
+
errors: [
|
|
5754
|
+
"validation:invalid_input",
|
|
5755
|
+
"auth:insufficient_permissions",
|
|
5756
|
+
"server:internal_error"
|
|
5757
|
+
]
|
|
5758
|
+
},
|
|
5656
5759
|
previewSiteBrandingOperation: {
|
|
5657
5760
|
path: "/sites/{siteId}/settings/general/preview",
|
|
5658
5761
|
method: "POST",
|
|
@@ -5870,6 +5973,20 @@ const ENDPOINT_DEFINITIONS = {
|
|
|
5870
5973
|
errors: ["resource:not_found", "server:internal_error"],
|
|
5871
5974
|
responseKind: "json"
|
|
5872
5975
|
},
|
|
5976
|
+
updateFooterCredit: {
|
|
5977
|
+
path: "/sites/{siteId}/settings/footer-credit",
|
|
5978
|
+
method: "POST",
|
|
5979
|
+
tags: [TAG_TEMPLATES.site, TAG_TEMPLATES.siteSettings],
|
|
5980
|
+
auth: "user",
|
|
5981
|
+
demoMutation: DEMO_MUTATION_ALLOWED,
|
|
5982
|
+
errors: [
|
|
5983
|
+
"auth:forbidden",
|
|
5984
|
+
"validation:invalid_input",
|
|
5985
|
+
"resource:not_found",
|
|
5986
|
+
"server:internal_error"
|
|
5987
|
+
],
|
|
5988
|
+
responseKind: "json"
|
|
5989
|
+
},
|
|
5873
5990
|
updateFooterComposition: {
|
|
5874
5991
|
editLease: "unleased-editor",
|
|
5875
5992
|
path: "/sites/{siteId}/settings/footer-composition",
|
|
@@ -6227,15 +6344,27 @@ const ENDPOINT_DEFINITIONS = {
|
|
|
6227
6344
|
auth: "user",
|
|
6228
6345
|
responseKind: "json"
|
|
6229
6346
|
},
|
|
6230
|
-
|
|
6231
|
-
path: "/account/
|
|
6347
|
+
accountAnswerHeardFrom: {
|
|
6348
|
+
path: "/account/heard-from",
|
|
6232
6349
|
method: "POST",
|
|
6233
6350
|
tags: ["account"],
|
|
6234
6351
|
auth: "user",
|
|
6352
|
+
errors: [
|
|
6353
|
+
"auth:unauthenticated",
|
|
6354
|
+
"validation:invalid_input",
|
|
6355
|
+
"server:internal_error"
|
|
6356
|
+
],
|
|
6235
6357
|
responseKind: "json"
|
|
6236
6358
|
},
|
|
6237
|
-
|
|
6238
|
-
path: "/
|
|
6359
|
+
accountRevokeSessions: {
|
|
6360
|
+
path: "/account/sessions/revoke",
|
|
6361
|
+
method: "POST",
|
|
6362
|
+
tags: ["account"],
|
|
6363
|
+
auth: "user",
|
|
6364
|
+
responseKind: "json"
|
|
6365
|
+
},
|
|
6366
|
+
adminListInvites: {
|
|
6367
|
+
path: "/admin/invites",
|
|
6239
6368
|
method: "GET",
|
|
6240
6369
|
tags: ["admin", "invites"],
|
|
6241
6370
|
auth: "admin",
|
|
@@ -8799,14 +8928,6 @@ const ENDPOINT_DEFINITIONS = {
|
|
|
8799
8928
|
auth: "user",
|
|
8800
8929
|
responseKind: "json"
|
|
8801
8930
|
},
|
|
8802
|
-
aiPlaygroundApply: {
|
|
8803
|
-
editLease: "unleased-editor",
|
|
8804
|
-
path: "/sites/{siteId}/ai/playground/apply",
|
|
8805
|
-
method: "POST",
|
|
8806
|
-
tags: ["site-{siteId}"],
|
|
8807
|
-
auth: "user",
|
|
8808
|
-
responseKind: "json"
|
|
8809
|
-
},
|
|
8810
8931
|
billingStatus: {
|
|
8811
8932
|
path: "/billing/status",
|
|
8812
8933
|
method: "GET",
|
|
@@ -9759,6 +9880,7 @@ const ENDPOINT_DEFINITIONS = {
|
|
|
9759
9880
|
responseKind: "json",
|
|
9760
9881
|
errors: [
|
|
9761
9882
|
"resource:not_found",
|
|
9883
|
+
"resource:conflict",
|
|
9762
9884
|
"validation:invalid_input",
|
|
9763
9885
|
"server:internal_error"
|
|
9764
9886
|
]
|
|
@@ -10250,17 +10372,6 @@ const blockLocationFields = {
|
|
|
10250
10372
|
blockIdentifier: z.string().nullable(),
|
|
10251
10373
|
stage: z.enum(["draft", "published"])
|
|
10252
10374
|
};
|
|
10253
|
-
const newsletterUsageLocationSchemas = [z.object({
|
|
10254
|
-
kind: z.literal("newsletterTemplate"),
|
|
10255
|
-
siteId: identifierSchema,
|
|
10256
|
-
templateId: identifierSchema,
|
|
10257
|
-
templateName: z.string()
|
|
10258
|
-
}), z.object({
|
|
10259
|
-
kind: z.literal("newsletterCampaign"),
|
|
10260
|
-
siteId: identifierSchema,
|
|
10261
|
-
campaignId: identifierSchema,
|
|
10262
|
-
campaignName: z.string()
|
|
10263
|
-
})];
|
|
10264
10375
|
const mediaUsageLocationSchema = z.discriminatedUnion("kind", [
|
|
10265
10376
|
z.object({
|
|
10266
10377
|
kind: z.literal("appointmentService"),
|
|
@@ -10268,7 +10379,12 @@ const mediaUsageLocationSchema = z.discriminatedUnion("kind", [
|
|
|
10268
10379
|
serviceId: identifierSchema,
|
|
10269
10380
|
serviceTitle: z.string()
|
|
10270
10381
|
}),
|
|
10271
|
-
|
|
10382
|
+
z.object({
|
|
10383
|
+
kind: z.literal("newsletterCampaign"),
|
|
10384
|
+
siteId: identifierSchema,
|
|
10385
|
+
campaignId: identifierSchema,
|
|
10386
|
+
campaignName: z.string()
|
|
10387
|
+
}),
|
|
10272
10388
|
z.object({
|
|
10273
10389
|
kind: z.literal("pageBlock"),
|
|
10274
10390
|
...blockLocationFields,
|
|
@@ -10368,7 +10484,7 @@ const sdkMediaDeleteApplyResponseSchema = z.discriminatedUnion("kind", [
|
|
|
10368
10484
|
*
|
|
10369
10485
|
* Goal: a single source-of-truth for link shapes used across dashboard + SDK.
|
|
10370
10486
|
* - Types live in `packages/api/src/navigation/types.ts`
|
|
10371
|
-
* - Schemas live here and are tied to types via `satisfies z.ZodType<...>`
|
|
10487
|
+
* - Schemas live here and are tied to types via `satisfies z.core.$ZodType<...>`
|
|
10372
10488
|
*/
|
|
10373
10489
|
const portalLinkPayloadSchema = z.object({
|
|
10374
10490
|
kind: z.literal("portal"),
|
|
@@ -10607,18 +10723,7 @@ const sdkPullVenueSchema = z.object({
|
|
|
10607
10723
|
description: z.string().nullable(),
|
|
10608
10724
|
timezone: z.string().nullable(),
|
|
10609
10725
|
capacity: z.number().int().nonnegative().nullable(),
|
|
10610
|
-
|
|
10611
|
-
line1: z.string().optional(),
|
|
10612
|
-
line2: z.string().optional(),
|
|
10613
|
-
city: z.string().optional(),
|
|
10614
|
-
region: z.string().optional(),
|
|
10615
|
-
postcode: z.string().optional(),
|
|
10616
|
-
country: z.string().optional()
|
|
10617
|
-
}),
|
|
10618
|
-
location: z.object({
|
|
10619
|
-
lat: z.number(),
|
|
10620
|
-
lng: z.number()
|
|
10621
|
-
}).partial().nullable(),
|
|
10726
|
+
addressVisibility: z.enum(["everyone", "booked"]).optional(),
|
|
10622
10727
|
locationValue: locationValueSchema.nullable().optional(),
|
|
10623
10728
|
mapEmbed: z.string().nullable(),
|
|
10624
10729
|
updatedAt: sdkTimestampSchema$2,
|
|
@@ -10673,7 +10778,6 @@ const sdkPullEventRunCommonSchema = z.object({
|
|
|
10673
10778
|
capacity: z.number().int().nonnegative().nullable(),
|
|
10674
10779
|
bookingOffer: eventBookingOfferIntentSchema,
|
|
10675
10780
|
venueRef: z.string().nullable(),
|
|
10676
|
-
formRef: z.string().nullable(),
|
|
10677
10781
|
bookingQuestions: bookingQuestionAttachmentSchema.nullable().optional(),
|
|
10678
10782
|
settings: sdkJsonObjectSchema$1,
|
|
10679
10783
|
presentation: z.unknown().optional(),
|
|
@@ -10861,7 +10965,6 @@ const BOOKING_SETTINGS_SECTION_KEYS = {
|
|
|
10861
10965
|
],
|
|
10862
10966
|
appointments: [
|
|
10863
10967
|
"appointmentReminderOverrides",
|
|
10864
|
-
"appointmentRescheduleHours",
|
|
10865
10968
|
"appointmentDefaultMinLeadMinutes",
|
|
10866
10969
|
"appointmentDefaultMaxBookingNoticeMinutes",
|
|
10867
10970
|
"appointmentBookingPolicies"
|
|
@@ -10884,6 +10987,125 @@ const BOOKING_SETTINGS_EMAIL_SEND_KEYS = [
|
|
|
10884
10987
|
"appointmentStaffReminder"
|
|
10885
10988
|
];
|
|
10886
10989
|
|
|
10990
|
+
//#endregion
|
|
10991
|
+
//#region ../api/src/event-listing-destination.ts
|
|
10992
|
+
const EVENT_LISTING_DESTINATION_SETTINGS_KEY = "listingDestination";
|
|
10993
|
+
const DEFAULT_EXTERNAL_EVENT_CTA_LABEL = "View details";
|
|
10994
|
+
function isRecord$10(value) {
|
|
10995
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
10996
|
+
}
|
|
10997
|
+
function normalizeOptionalLabel(value) {
|
|
10998
|
+
return typeof value === "string" && value.trim() ? value.trim() : null;
|
|
10999
|
+
}
|
|
11000
|
+
function normalizeOptionalUrl(value) {
|
|
11001
|
+
return typeof value === "string" ? value.trim() : "";
|
|
11002
|
+
}
|
|
11003
|
+
function normalizeEventListingDestinationInput(value) {
|
|
11004
|
+
if (!value || value.kind === "native") {
|
|
11005
|
+
return { kind: "native" };
|
|
11006
|
+
}
|
|
11007
|
+
return {
|
|
11008
|
+
kind: "external",
|
|
11009
|
+
url: normalizeOptionalUrl(value.url),
|
|
11010
|
+
label: normalizeOptionalLabel(value.label)
|
|
11011
|
+
};
|
|
11012
|
+
}
|
|
11013
|
+
function readEventListingDestinationFromSettings(settingsJson) {
|
|
11014
|
+
if (!isRecord$10(settingsJson)) {
|
|
11015
|
+
return { kind: "native" };
|
|
11016
|
+
}
|
|
11017
|
+
const rawDestination = settingsJson[EVENT_LISTING_DESTINATION_SETTINGS_KEY];
|
|
11018
|
+
if (!isRecord$10(rawDestination) || rawDestination.kind !== "external") {
|
|
11019
|
+
return { kind: "native" };
|
|
11020
|
+
}
|
|
11021
|
+
return {
|
|
11022
|
+
kind: "external",
|
|
11023
|
+
url: normalizeOptionalUrl(rawDestination.url),
|
|
11024
|
+
label: normalizeOptionalLabel(rawDestination.label)
|
|
11025
|
+
};
|
|
11026
|
+
}
|
|
11027
|
+
function serializeEventListingDestination(destination) {
|
|
11028
|
+
switch (destination.kind) {
|
|
11029
|
+
case "native": return { kind: "native" };
|
|
11030
|
+
case "external": return {
|
|
11031
|
+
kind: "external",
|
|
11032
|
+
url: destination.url,
|
|
11033
|
+
label: destination.label
|
|
11034
|
+
};
|
|
11035
|
+
}
|
|
11036
|
+
}
|
|
11037
|
+
function buildEventListingDestinationSettingsPatch(destination) {
|
|
11038
|
+
return { [EVENT_LISTING_DESTINATION_SETTINGS_KEY]: serializeEventListingDestination(destination) };
|
|
11039
|
+
}
|
|
11040
|
+
function parseExternalEventUrl(value) {
|
|
11041
|
+
const raw = normalizeOptionalUrl(value);
|
|
11042
|
+
if (!raw) {
|
|
11043
|
+
return {
|
|
11044
|
+
ok: false,
|
|
11045
|
+
error: {
|
|
11046
|
+
code: "missing_url",
|
|
11047
|
+
message: "External event URL is required."
|
|
11048
|
+
}
|
|
11049
|
+
};
|
|
11050
|
+
}
|
|
11051
|
+
try {
|
|
11052
|
+
const url = assertAllowedPublicUrl(raw, {
|
|
11053
|
+
allowedProtocols: ["https:"],
|
|
11054
|
+
portPolicy: "standard-only"
|
|
11055
|
+
});
|
|
11056
|
+
return {
|
|
11057
|
+
ok: true,
|
|
11058
|
+
url: url.href
|
|
11059
|
+
};
|
|
11060
|
+
} catch (error) {
|
|
11061
|
+
return {
|
|
11062
|
+
ok: false,
|
|
11063
|
+
error: {
|
|
11064
|
+
code: "invalid_url",
|
|
11065
|
+
message: error instanceof Error ? error.message : "External event URL must be a valid public HTTPS URL."
|
|
11066
|
+
}
|
|
11067
|
+
};
|
|
11068
|
+
}
|
|
11069
|
+
}
|
|
11070
|
+
function getExternalEventCtaLabel(destination) {
|
|
11071
|
+
return destination.label ?? DEFAULT_EXTERNAL_EVENT_CTA_LABEL;
|
|
11072
|
+
}
|
|
11073
|
+
function getEventListingDestinationPublicationIssue(destination) {
|
|
11074
|
+
if (destination.kind === "native") {
|
|
11075
|
+
return null;
|
|
11076
|
+
}
|
|
11077
|
+
const validation = parseExternalEventUrl(destination.url);
|
|
11078
|
+
if (validation.ok) {
|
|
11079
|
+
return null;
|
|
11080
|
+
}
|
|
11081
|
+
return {
|
|
11082
|
+
field: "listingDestination.url",
|
|
11083
|
+
code: validation.error.code,
|
|
11084
|
+
message: validation.error.message
|
|
11085
|
+
};
|
|
11086
|
+
}
|
|
11087
|
+
function buildPublicEventDestination(input) {
|
|
11088
|
+
if (input.listingDestination.kind === "native") {
|
|
11089
|
+
if (!input.nativePath.trim()) {
|
|
11090
|
+
return null;
|
|
11091
|
+
}
|
|
11092
|
+
return {
|
|
11093
|
+
kind: "native",
|
|
11094
|
+
mode: input.nativeMode,
|
|
11095
|
+
path: input.nativePath
|
|
11096
|
+
};
|
|
11097
|
+
}
|
|
11098
|
+
const validation = parseExternalEventUrl(input.listingDestination.url);
|
|
11099
|
+
if (!validation.ok) {
|
|
11100
|
+
return null;
|
|
11101
|
+
}
|
|
11102
|
+
return {
|
|
11103
|
+
kind: "external",
|
|
11104
|
+
url: validation.url,
|
|
11105
|
+
label: getExternalEventCtaLabel(input.listingDestination)
|
|
11106
|
+
};
|
|
11107
|
+
}
|
|
11108
|
+
|
|
10887
11109
|
//#endregion
|
|
10888
11110
|
//#region ../api/src/event-destination-policy.ts
|
|
10889
11111
|
const eventDestinationModeValues = ["cms-managed", "system-managed"];
|
|
@@ -10915,6 +11137,245 @@ function macadamiaDefaultEventDestinationPolicy(selectedPresets) {
|
|
|
10915
11137
|
};
|
|
10916
11138
|
}
|
|
10917
11139
|
|
|
11140
|
+
//#endregion
|
|
11141
|
+
//#region ../api/src/appointmentServiceVisibility.ts
|
|
11142
|
+
const APPOINTMENT_SERVICE_VISIBILITY_VALUES = ["public", "unlisted"];
|
|
11143
|
+
function isAppointmentServiceVisibility(value) {
|
|
11144
|
+
return value === "public" || value === "unlisted";
|
|
11145
|
+
}
|
|
11146
|
+
|
|
11147
|
+
//#endregion
|
|
11148
|
+
//#region ../api/src/request-error.ts
|
|
11149
|
+
var ApiRequestError = class extends Error {
|
|
11150
|
+
constructor(message, options) {
|
|
11151
|
+
super(message);
|
|
11152
|
+
this.name = "ApiRequestError";
|
|
11153
|
+
this.endpoint = options.endpoint;
|
|
11154
|
+
this.status = options.status;
|
|
11155
|
+
this.method = options.method;
|
|
11156
|
+
this.auth = options.auth;
|
|
11157
|
+
this.requestId = options.requestId;
|
|
11158
|
+
this.body = options.body;
|
|
11159
|
+
this.cause = options.cause;
|
|
11160
|
+
this.errorCode = options.errorCode;
|
|
11161
|
+
this.tag = options.tag;
|
|
11162
|
+
this.retryAfterMs = options.retryAfterMs;
|
|
11163
|
+
}
|
|
11164
|
+
};
|
|
11165
|
+
|
|
11166
|
+
//#endregion
|
|
11167
|
+
//#region ../api/src/utils/isRecord.ts
|
|
11168
|
+
function isRecord$5(value) {
|
|
11169
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
11170
|
+
}
|
|
11171
|
+
|
|
11172
|
+
//#endregion
|
|
11173
|
+
//#region ../api/src/utils/error-propagation.ts
|
|
11174
|
+
/**
|
|
11175
|
+
* Extracts an error message from an API error response body.
|
|
11176
|
+
* Checks for our standard API error format: { error: { message: "..." } }
|
|
11177
|
+
* Also handles legacy formats with `error` or `message` as strings.
|
|
11178
|
+
*/
|
|
11179
|
+
function extractApiErrorMessage(body) {
|
|
11180
|
+
if (!isRecord$5(body)) {
|
|
11181
|
+
return null;
|
|
11182
|
+
}
|
|
11183
|
+
if (isRecord$5(body.error)) {
|
|
11184
|
+
if (typeof body.error.message === "string" && body.error.message.trim().length > 0) {
|
|
11185
|
+
return body.error.message;
|
|
11186
|
+
}
|
|
11187
|
+
}
|
|
11188
|
+
if (typeof body.error === "string" && body.error.trim().length > 0) {
|
|
11189
|
+
return body.error;
|
|
11190
|
+
}
|
|
11191
|
+
if (typeof body.message === "string" && body.message.trim().length > 0) {
|
|
11192
|
+
return body.message;
|
|
11193
|
+
}
|
|
11194
|
+
return null;
|
|
11195
|
+
}
|
|
11196
|
+
/**
|
|
11197
|
+
* Normalizes various error types and throws with an appropriate message.
|
|
11198
|
+
*
|
|
11199
|
+
* - For ApiRequestError: extracts message from body.error or body.message
|
|
11200
|
+
* - For Error instances: re-throws as-is
|
|
11201
|
+
* - For unknown errors: wraps with fallback message
|
|
11202
|
+
*
|
|
11203
|
+
* @param error - The error to normalize
|
|
11204
|
+
* @param fallback - Fallback message if no specific message can be extracted
|
|
11205
|
+
* @throws {Error} Always throws - this function never returns
|
|
11206
|
+
*/
|
|
11207
|
+
function normalizeApiError(error, fallback) {
|
|
11208
|
+
if (error instanceof ApiRequestError) {
|
|
11209
|
+
const message = extractApiErrorMessage(error.body);
|
|
11210
|
+
throw new ApiRequestError(message ?? fallback, {
|
|
11211
|
+
endpoint: error.endpoint,
|
|
11212
|
+
status: error.status,
|
|
11213
|
+
method: error.method,
|
|
11214
|
+
auth: error.auth,
|
|
11215
|
+
requestId: error.requestId,
|
|
11216
|
+
body: error.body,
|
|
11217
|
+
cause: error,
|
|
11218
|
+
errorCode: error.errorCode,
|
|
11219
|
+
tag: error.tag,
|
|
11220
|
+
retryAfterMs: error.retryAfterMs
|
|
11221
|
+
});
|
|
11222
|
+
}
|
|
11223
|
+
if (error instanceof Error) {
|
|
11224
|
+
throw error;
|
|
11225
|
+
}
|
|
11226
|
+
throw new Error(fallback);
|
|
11227
|
+
}
|
|
11228
|
+
|
|
11229
|
+
//#endregion
|
|
11230
|
+
//#region ../api/src/appointmentSetup.ts
|
|
11231
|
+
const APPOINTMENT_SETUP_MAX_MINUTES = 1440;
|
|
11232
|
+
const appointmentSetupRefSchema = z.discriminatedUnion("kind", [z.object({
|
|
11233
|
+
kind: z.literal("existing"),
|
|
11234
|
+
id: z.string().min(1)
|
|
11235
|
+
}), z.object({
|
|
11236
|
+
kind: z.literal("new"),
|
|
11237
|
+
draftId: z.string().min(1)
|
|
11238
|
+
})]);
|
|
11239
|
+
const appointmentSetupVenueChoiceSchema = z.discriminatedUnion("kind", [
|
|
11240
|
+
z.object({ kind: z.literal("none") }),
|
|
11241
|
+
z.object({
|
|
11242
|
+
kind: z.literal("existing"),
|
|
11243
|
+
venueId: z.string().min(1)
|
|
11244
|
+
}),
|
|
11245
|
+
z.object({
|
|
11246
|
+
kind: z.literal("create"),
|
|
11247
|
+
draftId: z.string().min(1),
|
|
11248
|
+
title: z.string().trim().min(1),
|
|
11249
|
+
description: z.string().nullable(),
|
|
11250
|
+
timezone: z.string().trim().min(1).nullable(),
|
|
11251
|
+
addressVisibility: z.enum(["everyone", "booked"]).optional(),
|
|
11252
|
+
locationValue: locationValueSchema.nullable().optional()
|
|
11253
|
+
})
|
|
11254
|
+
]);
|
|
11255
|
+
const appointmentSetupPaymentDraftSchema = z.discriminatedUnion("kind", [
|
|
11256
|
+
z.object({ kind: z.literal("free") }),
|
|
11257
|
+
z.object({ kind: z.literal("paid_online") }),
|
|
11258
|
+
z.object({ kind: z.literal("paid_offline") })
|
|
11259
|
+
]);
|
|
11260
|
+
const appointmentSetupServiceAssignmentDraftSchema = z.discriminatedUnion("kind", [z.object({ kind: z.literal("all_practitioners") }), z.object({
|
|
11261
|
+
kind: z.literal("selected_practitioners"),
|
|
11262
|
+
resourceRefs: z.array(appointmentSetupRefSchema)
|
|
11263
|
+
})]);
|
|
11264
|
+
const appointmentSetupServiceDraftSchema = z.object({
|
|
11265
|
+
ref: appointmentSetupRefSchema,
|
|
11266
|
+
title: z.string().trim().min(1),
|
|
11267
|
+
description: z.string().nullable(),
|
|
11268
|
+
durationMinutes: z.number().int().positive().max(APPOINTMENT_SETUP_MAX_MINUTES),
|
|
11269
|
+
prepMinutes: z.number().int().nonnegative().max(APPOINTMENT_SETUP_MAX_MINUTES).default(0),
|
|
11270
|
+
cleanupMinutes: z.number().int().nonnegative().max(APPOINTMENT_SETUP_MAX_MINUTES).default(0),
|
|
11271
|
+
priceAmount: z.number().int().nonnegative().nullable(),
|
|
11272
|
+
isActive: z.boolean(),
|
|
11273
|
+
visibility: z.enum(APPOINTMENT_SERVICE_VISIBILITY_VALUES).default("public"),
|
|
11274
|
+
locationType: z.union([
|
|
11275
|
+
z.literal("virtual"),
|
|
11276
|
+
z.literal("in_person"),
|
|
11277
|
+
z.literal("hybrid")
|
|
11278
|
+
]),
|
|
11279
|
+
venue: appointmentSetupVenueChoiceSchema,
|
|
11280
|
+
timezone: z.string().trim().min(1).nullable(),
|
|
11281
|
+
payment: appointmentSetupPaymentDraftSchema,
|
|
11282
|
+
assignment: appointmentSetupServiceAssignmentDraftSchema,
|
|
11283
|
+
categoryId: z.string().uuid().nullable().optional()
|
|
11284
|
+
}).superRefine((service, ctx) => {
|
|
11285
|
+
if (service.payment.kind !== "free" && (service.priceAmount === null || service.priceAmount <= 0)) {
|
|
11286
|
+
ctx.addIssue({
|
|
11287
|
+
code: "custom",
|
|
11288
|
+
path: ["priceAmount"],
|
|
11289
|
+
message: "Paid appointment services need a positive price."
|
|
11290
|
+
});
|
|
11291
|
+
}
|
|
11292
|
+
if (service.payment.kind === "free" && service.priceAmount !== null && service.priceAmount > 0) {
|
|
11293
|
+
ctx.addIssue({
|
|
11294
|
+
code: "custom",
|
|
11295
|
+
path: ["priceAmount"],
|
|
11296
|
+
message: "Free appointment services cannot carry a positive price."
|
|
11297
|
+
});
|
|
11298
|
+
}
|
|
11299
|
+
});
|
|
11300
|
+
const appointmentSetupPractitionerDraftSchema = z.object({
|
|
11301
|
+
ref: appointmentSetupRefSchema,
|
|
11302
|
+
displayName: z.string().trim().min(1),
|
|
11303
|
+
bio: z.string().nullable(),
|
|
11304
|
+
timezone: z.string().trim().min(1),
|
|
11305
|
+
colorHex: z.string().nullable()
|
|
11306
|
+
});
|
|
11307
|
+
const appointmentSetupWeeklyWindowDraftSchema = z.object({
|
|
11308
|
+
weekday: z.number().int().min(0).max(6),
|
|
11309
|
+
startTime: z.string(),
|
|
11310
|
+
endTime: z.string()
|
|
11311
|
+
});
|
|
11312
|
+
const appointmentSetupStartTimePolicyDraftSchema = z.discriminatedUnion("kind", [z.object({ kind: z.literal("automatic_spacing") }), z.object({
|
|
11313
|
+
kind: z.literal("regular_interval"),
|
|
11314
|
+
intervalMinutes: z.union([
|
|
11315
|
+
z.literal(10),
|
|
11316
|
+
z.literal(15),
|
|
11317
|
+
z.literal(20),
|
|
11318
|
+
z.literal(30),
|
|
11319
|
+
z.literal(45),
|
|
11320
|
+
z.literal(60)
|
|
11321
|
+
])
|
|
11322
|
+
})]);
|
|
11323
|
+
const appointmentSetupAvailabilityDraftSchema = z.object({
|
|
11324
|
+
kind: z.literal("weekly_windows"),
|
|
11325
|
+
resourceRef: appointmentSetupRefSchema,
|
|
11326
|
+
serviceRef: appointmentSetupRefSchema.nullable(),
|
|
11327
|
+
windows: z.array(appointmentSetupWeeklyWindowDraftSchema),
|
|
11328
|
+
startTimePolicy: appointmentSetupStartTimePolicyDraftSchema.optional()
|
|
11329
|
+
});
|
|
11330
|
+
/**
|
|
11331
|
+
* Upper bounds on the booking-notice fields. Site-wide defaults — these caps
|
|
11332
|
+
* keep the slot engine arithmetic (`now + minutes * 60_000`) well inside safe
|
|
11333
|
+
* integer range and surface UI typos rather than silently storing nonsense.
|
|
11334
|
+
*
|
|
11335
|
+
* - `MIN_LEAD` capped at 28 days. A "must book at least N minutes ahead"
|
|
11336
|
+
* value beyond that reads as misconfiguration.
|
|
11337
|
+
* - `MAX_NOTICE` capped at 10 years. A reasonable ceiling for "how far in
|
|
11338
|
+
* the future bookings may be made"; safe against overflow.
|
|
11339
|
+
*
|
|
11340
|
+
* Mirror these in `bookingSettingsSchema` (storage layer) and the wizard
|
|
11341
|
+
* draft schemas — single source of truth for the contract.
|
|
11342
|
+
*/
|
|
11343
|
+
const APPOINTMENT_DEFAULT_MIN_LEAD_MINUTES_MAX = 28 * 24 * 60;
|
|
11344
|
+
const APPOINTMENT_DEFAULT_MAX_BOOKING_NOTICE_MINUTES_MAX = 10 * 365 * 24 * 60;
|
|
11345
|
+
const appointmentSetupBookingNoticeSchema = z.object({
|
|
11346
|
+
minLeadMinutes: z.number().int().nonnegative().max(APPOINTMENT_DEFAULT_MIN_LEAD_MINUTES_MAX),
|
|
11347
|
+
maxBookingNoticeMinutes: z.number().int().nonnegative().max(APPOINTMENT_DEFAULT_MAX_BOOKING_NOTICE_MINUTES_MAX)
|
|
11348
|
+
});
|
|
11349
|
+
const applyAppointmentSetupRequestSchema = z.object({
|
|
11350
|
+
services: z.array(appointmentSetupServiceDraftSchema),
|
|
11351
|
+
practitioners: z.array(appointmentSetupPractitionerDraftSchema),
|
|
11352
|
+
availability: z.array(appointmentSetupAvailabilityDraftSchema),
|
|
11353
|
+
bookingNotice: appointmentSetupBookingNoticeSchema
|
|
11354
|
+
});
|
|
11355
|
+
async function getAppointmentSetupRequest(apiClient, siteId, options) {
|
|
11356
|
+
try {
|
|
11357
|
+
return unwrapResponse(await apiClient({
|
|
11358
|
+
endpoint: "getAppointmentSetup",
|
|
11359
|
+
params: { siteId },
|
|
11360
|
+
options
|
|
11361
|
+
}));
|
|
11362
|
+
} catch (error) {
|
|
11363
|
+
normalizeApiError(error, "Failed to load appointment setup");
|
|
11364
|
+
}
|
|
11365
|
+
}
|
|
11366
|
+
async function applyAppointmentSetupRequest(apiClient, siteId, payload, options) {
|
|
11367
|
+
try {
|
|
11368
|
+
return unwrapResponse(await apiClient({
|
|
11369
|
+
endpoint: "applyAppointmentSetup",
|
|
11370
|
+
params: { siteId },
|
|
11371
|
+
body: payload,
|
|
11372
|
+
options
|
|
11373
|
+
}));
|
|
11374
|
+
} catch (error) {
|
|
11375
|
+
normalizeApiError(error, "Failed to save appointment setup");
|
|
11376
|
+
}
|
|
11377
|
+
}
|
|
11378
|
+
|
|
10918
11379
|
//#endregion
|
|
10919
11380
|
//#region ../api/src/bookingConfig.ts
|
|
10920
11381
|
/** Every option kind; the checkout routes validate the requested kind against it. */
|
|
@@ -11117,7 +11578,10 @@ const sdkPatchPageBlockResponseSchema = z.object({
|
|
|
11117
11578
|
const sdkUpsertNavigationMenuResponseSchema = sdkCommandApplicationResultSchema(sdkManagementNavigationMenuSchema);
|
|
11118
11579
|
const sdkUpsertSettingsResponseSchema = sdkCommandApplicationResultSchema(sdkPullSettingsSchema);
|
|
11119
11580
|
const sdkUpsertFormResponseSchema = sdkCommandApplicationResultSchema(sdkPullFormSchemaWithId);
|
|
11120
|
-
const sdkUpsertVenueResponseSchema = sdkCommandApplicationResultSchema(sdkPullVenueSchema.extend({ id: sdkIdentifierSchema }))
|
|
11581
|
+
const sdkUpsertVenueResponseSchema = sdkCommandApplicationResultSchema(sdkPullVenueSchema.extend({ id: sdkIdentifierSchema })).extend({ warning: z.object({
|
|
11582
|
+
code: z.literal("venue_public_refresh_delayed"),
|
|
11583
|
+
message: z.string()
|
|
11584
|
+
}).optional() });
|
|
11121
11585
|
const sdkUpsertEventCategoryResponseSchema = sdkCommandApplicationResultSchema(sdkPullEventCategorySchema.extend({ id: sdkIdentifierSchema }));
|
|
11122
11586
|
const sdkManagementEventSchema = sdkPullEventSchema.extend({
|
|
11123
11587
|
id: sdkIdentifierSchema.transform(unsafeAsEventOfferingId),
|
|
@@ -11909,37 +12373,2289 @@ function createPageOperations(http) {
|
|
|
11909
12373
|
}
|
|
11910
12374
|
|
|
11911
12375
|
//#endregion
|
|
11912
|
-
//#region ../
|
|
11913
|
-
const
|
|
11914
|
-
|
|
11915
|
-
|
|
11916
|
-
|
|
11917
|
-
|
|
11918
|
-
|
|
11919
|
-
|
|
12376
|
+
//#region ../theme-core/src/footer/mode.ts
|
|
12377
|
+
const footerModeSchema = z.enum([
|
|
12378
|
+
"default",
|
|
12379
|
+
"blocks",
|
|
12380
|
+
"default+blocks",
|
|
12381
|
+
"none"
|
|
12382
|
+
]);
|
|
12383
|
+
/** Reads only SDK footer intent; unrelated config and theme fields are not authorities. */
|
|
12384
|
+
function resolveSdkFooterMode(config) {
|
|
12385
|
+
const parsed = z.object({ layout: z.object({ footerMode: footerModeSchema.optional() }).optional() }).safeParse(config);
|
|
12386
|
+
return parsed.success ? parsed.data.layout?.footerMode ?? "default" : "default";
|
|
12387
|
+
}
|
|
12388
|
+
const legacyFooterBrandSchema = z.object({
|
|
12389
|
+
variant: z.enum([
|
|
12390
|
+
"simple",
|
|
12391
|
+
"columns",
|
|
12392
|
+
"split",
|
|
12393
|
+
"centred"
|
|
12394
|
+
]),
|
|
12395
|
+
showLogoText: z.boolean().optional(),
|
|
12396
|
+
logo: z.object({
|
|
12397
|
+
showLogo: z.boolean().optional(),
|
|
12398
|
+
showLogoText: z.boolean().optional()
|
|
12399
|
+
}).optional()
|
|
12400
|
+
});
|
|
12401
|
+
/** Offline composition backfill only. Never read appearance visibility at runtime. */
|
|
12402
|
+
function readLegacyFooterBrandVisibility(value) {
|
|
12403
|
+
const parsed = legacyFooterBrandSchema.safeParse(value);
|
|
12404
|
+
if (!parsed.success) return null;
|
|
12405
|
+
const footer = parsed.data;
|
|
12406
|
+
return {
|
|
12407
|
+
showLogo: footer.variant === "simple" ? footer.logo?.showLogo === true : footer.logo?.showLogo !== false,
|
|
12408
|
+
showTitle: footer.logo?.showLogoText ?? footer.showLogoText === true
|
|
12409
|
+
};
|
|
11920
12410
|
}
|
|
11921
|
-
const SDK_PATCH_STALE_CONTENT_FIELD = "baseContentHash";
|
|
11922
|
-
const SDK_PATCH_STALE_CONTENT_CODE = "STALE_CONTENT";
|
|
11923
|
-
|
|
11924
|
-
//#endregion
|
|
11925
|
-
//#region ../management-client/src/blocks.ts
|
|
11926
12411
|
/**
|
|
11927
|
-
*
|
|
11928
|
-
|
|
11929
|
-
|
|
11930
|
-
*
|
|
11931
|
-
* changed since the caller's read. `currentHash` is the server's current
|
|
11932
|
-
* draft content hash — retry the patch with it as `baseContentHash`.
|
|
11933
|
-
* Extracted from the typed `SdkPatchPageBlockStaleContentFieldError` wire
|
|
11934
|
-
* contract, so callers never parse prose.
|
|
12412
|
+
* Resolve what to render for the footer given the current mode and whether the
|
|
12413
|
+
* site is SDK-managed.
|
|
12414
|
+
*
|
|
12415
|
+
* Backwards compatible: missing/undefined mode is treated as 'default'.
|
|
11935
12416
|
*/
|
|
11936
|
-
|
|
11937
|
-
|
|
11938
|
-
|
|
11939
|
-
|
|
11940
|
-
|
|
11941
|
-
|
|
11942
|
-
|
|
12417
|
+
function resolveFooterRenderPlan(params) {
|
|
12418
|
+
if (!params.isSdkSite) {
|
|
12419
|
+
return {
|
|
12420
|
+
mode: "default",
|
|
12421
|
+
renderDefaultFooter: true,
|
|
12422
|
+
renderFooterBlocks: false
|
|
12423
|
+
};
|
|
12424
|
+
}
|
|
12425
|
+
const mode = params.mode ?? "default";
|
|
12426
|
+
switch (mode) {
|
|
12427
|
+
case "blocks": return {
|
|
12428
|
+
mode: "blocks",
|
|
12429
|
+
renderDefaultFooter: false,
|
|
12430
|
+
renderFooterBlocks: true
|
|
12431
|
+
};
|
|
12432
|
+
case "default+blocks": return {
|
|
12433
|
+
mode: "default+blocks",
|
|
12434
|
+
renderDefaultFooter: true,
|
|
12435
|
+
renderFooterBlocks: true
|
|
12436
|
+
};
|
|
12437
|
+
case "none": return {
|
|
12438
|
+
mode: "none",
|
|
12439
|
+
renderDefaultFooter: false,
|
|
12440
|
+
renderFooterBlocks: false
|
|
12441
|
+
};
|
|
12442
|
+
case "default":
|
|
12443
|
+
default: return {
|
|
12444
|
+
mode: "default",
|
|
12445
|
+
renderDefaultFooter: true,
|
|
12446
|
+
renderFooterBlocks: false
|
|
12447
|
+
};
|
|
12448
|
+
}
|
|
12449
|
+
}
|
|
12450
|
+
|
|
12451
|
+
//#endregion
|
|
12452
|
+
//#region ../blocks/src/system/node/schema.ts
|
|
12453
|
+
const transformStepSchema = z.object({
|
|
12454
|
+
id: z.string().min(1, "Transform requires an identifier"),
|
|
12455
|
+
options: z.record(z.string(), z.unknown()).optional()
|
|
12456
|
+
});
|
|
12457
|
+
const bindingPathSchema = z.string().min(1, "Binding path is required").regex(/[A-Za-z0-9_\.$\[\]/-]+/, "Binding path contains invalid characters");
|
|
12458
|
+
const bindingSchema = z.object({
|
|
12459
|
+
from: bindingPathSchema,
|
|
12460
|
+
fallback: z.unknown().optional(),
|
|
12461
|
+
transforms: transformStepSchema.array().default([]),
|
|
12462
|
+
pick: z.enum([
|
|
12463
|
+
"value",
|
|
12464
|
+
"collection",
|
|
12465
|
+
"context"
|
|
12466
|
+
]).default("value"),
|
|
12467
|
+
coerce: z.enum([
|
|
12468
|
+
"number",
|
|
12469
|
+
"boolean",
|
|
12470
|
+
"string"
|
|
12471
|
+
]).optional()
|
|
12472
|
+
});
|
|
12473
|
+
const repeatSchema = z.object({
|
|
12474
|
+
collection: bindingSchema,
|
|
12475
|
+
itemName: z.string().min(1).default("item"),
|
|
12476
|
+
indexName: z.string().min(1).default("index"),
|
|
12477
|
+
limit: z.number().int().positive().optional(),
|
|
12478
|
+
sortBy: z.object({
|
|
12479
|
+
path: bindingPathSchema,
|
|
12480
|
+
direction: z.enum(["asc", "desc"]).default("asc")
|
|
12481
|
+
}).optional()
|
|
12482
|
+
});
|
|
12483
|
+
const conditionalAuthoringPresenceSchema = z.discriminatedUnion("kind", [z.object({ kind: z.literal("visitor-only") }), z.object({
|
|
12484
|
+
kind: z.literal("recover-empty-when-active"),
|
|
12485
|
+
placeholder: z.string().min(1).optional()
|
|
12486
|
+
})]);
|
|
12487
|
+
const conditionSchema = z.object({
|
|
12488
|
+
when: bindingSchema,
|
|
12489
|
+
equals: z.unknown().optional(),
|
|
12490
|
+
not: z.boolean().default(false),
|
|
12491
|
+
authoringPresence: conditionalAuthoringPresenceSchema.default({ kind: "visitor-only" })
|
|
12492
|
+
});
|
|
12493
|
+
const dataScopeSchema = z.object({
|
|
12494
|
+
name: z.string().min(1),
|
|
12495
|
+
from: bindingSchema
|
|
12496
|
+
});
|
|
12497
|
+
const nodePropsSchema = z.record(z.string(), z.unknown()).default({});
|
|
12498
|
+
const nodeSchema = z.object({
|
|
12499
|
+
type: z.string().min(1, "Node type is required"),
|
|
12500
|
+
key: z.string().optional(),
|
|
12501
|
+
props: nodePropsSchema.optional(),
|
|
12502
|
+
children: z.lazy(() => nodeSchema.array().default([])).optional(),
|
|
12503
|
+
$bind: bindingSchema.optional(),
|
|
12504
|
+
$repeat: repeatSchema.optional(),
|
|
12505
|
+
$when: conditionSchema.optional(),
|
|
12506
|
+
$scopes: dataScopeSchema.array().optional()
|
|
12507
|
+
});
|
|
12508
|
+
const nodeTreeSchema = nodeSchema.array().or(nodeSchema);
|
|
12509
|
+
const NodeSchema = nodeSchema;
|
|
12510
|
+
const NodeTreeSchema = nodeTreeSchema;
|
|
12511
|
+
|
|
12512
|
+
//#endregion
|
|
12513
|
+
//#region ../blocks/src/system/data/loaderEndpoints.ts
|
|
12514
|
+
const SUPPORTED_LOADER_ENDPOINTS = [
|
|
12515
|
+
"listPublishedEntries",
|
|
12516
|
+
"getPublishedEntryPreview",
|
|
12517
|
+
"listPublicEvents",
|
|
12518
|
+
"listPublicEventCalendarWindow",
|
|
12519
|
+
"listPublicRecurringSchedule",
|
|
12520
|
+
"getPublicEventDetails",
|
|
12521
|
+
"listPublicRouteCandidates",
|
|
12522
|
+
"listPublicTeamMembers",
|
|
12523
|
+
"listPublicProducts",
|
|
12524
|
+
"listPublicPasses",
|
|
12525
|
+
"listPublicMemberships",
|
|
12526
|
+
"getPublicFormById",
|
|
12527
|
+
"getPublicBookingServices",
|
|
12528
|
+
"getPublicWeeklySlotAvailability"
|
|
12529
|
+
];
|
|
12530
|
+
function isSupportedLoaderEndpoint(endpoint) {
|
|
12531
|
+
return SUPPORTED_LOADER_ENDPOINTS.includes(endpoint);
|
|
12532
|
+
}
|
|
12533
|
+
|
|
12534
|
+
//#endregion
|
|
12535
|
+
//#region ../blocks/src/system/constants/blockKinds.ts
|
|
12536
|
+
const EVENT_DISPLAY_BLOCK_KINDS = [
|
|
12537
|
+
"block.event-details",
|
|
12538
|
+
"block.event-spotlight",
|
|
12539
|
+
"block.event-listing",
|
|
12540
|
+
"block.event-calendar",
|
|
12541
|
+
"block.recurring-schedule",
|
|
12542
|
+
"block.offerings-preview"
|
|
12543
|
+
];
|
|
12544
|
+
const SYSTEM_BLOCK_KINDS = [
|
|
12545
|
+
"block.hero",
|
|
12546
|
+
"block.intro",
|
|
12547
|
+
"block.columns",
|
|
12548
|
+
"block.collection",
|
|
12549
|
+
"block.feature-cards",
|
|
12550
|
+
"block.media-text",
|
|
12551
|
+
"block.cta-full",
|
|
12552
|
+
"block.single-button",
|
|
12553
|
+
"block.body-text",
|
|
12554
|
+
"block.location-map",
|
|
12555
|
+
"block.faq",
|
|
12556
|
+
"block.testimonials",
|
|
12557
|
+
"block.team-members",
|
|
12558
|
+
"block.embed",
|
|
12559
|
+
"block.blog-post",
|
|
12560
|
+
"block.blog-listing",
|
|
12561
|
+
"block.site-header",
|
|
12562
|
+
"block.site-footer",
|
|
12563
|
+
"block.form",
|
|
12564
|
+
"block.contact-newsletter",
|
|
12565
|
+
"block.appointment-booking",
|
|
12566
|
+
"block.appointment-services",
|
|
12567
|
+
"block.weekly-slot-availability",
|
|
12568
|
+
"block.event-registration",
|
|
12569
|
+
...EVENT_DISPLAY_BLOCK_KINDS,
|
|
12570
|
+
"block.shop",
|
|
12571
|
+
"block.product-list",
|
|
12572
|
+
"block.product-detail",
|
|
12573
|
+
"block.cart",
|
|
12574
|
+
"block.checkout",
|
|
12575
|
+
"block.gifting",
|
|
12576
|
+
"block.newsletter-signup",
|
|
12577
|
+
"block.image-gallery",
|
|
12578
|
+
"block.video-grid",
|
|
12579
|
+
"block.audio-player",
|
|
12580
|
+
"block.file-download"
|
|
12581
|
+
];
|
|
12582
|
+
|
|
12583
|
+
//#endregion
|
|
12584
|
+
//#region ../blocks/src/system/node/typeBasedLayout.ts
|
|
12585
|
+
/**
|
|
12586
|
+
* Creates conditional layout branches based on item type.
|
|
12587
|
+
* Used for polymorphic repeaters where each item type has a different layout.
|
|
12588
|
+
*
|
|
12589
|
+
* @param itemTypesMap - Map of type IDs to their layout definitions
|
|
12590
|
+
* @param options - Configuration options
|
|
12591
|
+
* @returns Array of conditional node definitions
|
|
12592
|
+
*
|
|
12593
|
+
* @example
|
|
12594
|
+
* ```typescript
|
|
12595
|
+
* // In a repeater with items: [{_type: 'card', ...}, {_type: 'quote', ...}]
|
|
12596
|
+
* const children = typeBasedLayout({
|
|
12597
|
+
* card: stack({}, [
|
|
12598
|
+
* text({ as: 'h3' }, bind('item.title')),
|
|
12599
|
+
* richText({}, bind('item.body'))
|
|
12600
|
+
* ]),
|
|
12601
|
+
* quote: stack({}, [
|
|
12602
|
+
* richText({}, bind('item.text')),
|
|
12603
|
+
* text({ className: 'author' }, bind('item.author'))
|
|
12604
|
+
* ])
|
|
12605
|
+
* }, { itemName: 'item' });
|
|
12606
|
+
* ```
|
|
12607
|
+
*/
|
|
12608
|
+
function typeBasedLayout(itemTypesMap, options) {
|
|
12609
|
+
const itemName = options?.itemName ?? "item";
|
|
12610
|
+
const result = Object.entries(itemTypesMap).map(([typeId, layout]) => {
|
|
12611
|
+
const node = Array.isArray(layout) ? layout.length === 0 ? {
|
|
12612
|
+
type: "div",
|
|
12613
|
+
children: []
|
|
12614
|
+
} : layout.length === 1 ? layout[0] ?? {
|
|
12615
|
+
type: "div",
|
|
12616
|
+
children: []
|
|
12617
|
+
} : {
|
|
12618
|
+
type: "div",
|
|
12619
|
+
children: layout
|
|
12620
|
+
} : layout;
|
|
12621
|
+
return {
|
|
12622
|
+
...node,
|
|
12623
|
+
$when: {
|
|
12624
|
+
when: { from: `${itemName}._type` },
|
|
12625
|
+
equals: typeId
|
|
12626
|
+
}
|
|
12627
|
+
};
|
|
12628
|
+
});
|
|
12629
|
+
return result;
|
|
12630
|
+
}
|
|
12631
|
+
|
|
12632
|
+
//#endregion
|
|
12633
|
+
//#region ../blocks/src/system/node/validation-constants.ts
|
|
12634
|
+
/**
|
|
12635
|
+
* Constants for validating declarative node layouts.
|
|
12636
|
+
*
|
|
12637
|
+
* These whitelists define which node types and transform IDs
|
|
12638
|
+
* are allowed in custom block layouts.
|
|
12639
|
+
*/
|
|
12640
|
+
/**
|
|
12641
|
+
* Valid node types for declarative layouts.
|
|
12642
|
+
*
|
|
12643
|
+
* This whitelist defines which component types can be used in
|
|
12644
|
+
* SdkCustomBlock.layout definitions. Node types not in this list
|
|
12645
|
+
* will be rejected during validation.
|
|
12646
|
+
*/
|
|
12647
|
+
const ALLOWED_NODE_TYPES = [
|
|
12648
|
+
"div",
|
|
12649
|
+
"figure",
|
|
12650
|
+
"section",
|
|
12651
|
+
"headerSection",
|
|
12652
|
+
"stack",
|
|
12653
|
+
"inline",
|
|
12654
|
+
"grid",
|
|
12655
|
+
"text",
|
|
12656
|
+
"richText",
|
|
12657
|
+
"media",
|
|
12658
|
+
"videoEmbed",
|
|
12659
|
+
"icon",
|
|
12660
|
+
"feature-card-icon",
|
|
12661
|
+
"svg",
|
|
12662
|
+
"button",
|
|
12663
|
+
"link",
|
|
12664
|
+
"carousel",
|
|
12665
|
+
"accordion",
|
|
12666
|
+
"accordionItem",
|
|
12667
|
+
"form",
|
|
12668
|
+
"booking-form",
|
|
12669
|
+
"weekly-slot-availability",
|
|
12670
|
+
"event-registration",
|
|
12671
|
+
"event-spotlight",
|
|
12672
|
+
"event-listing",
|
|
12673
|
+
"event-calendar",
|
|
12674
|
+
"offerings-preview",
|
|
12675
|
+
"event-combined",
|
|
12676
|
+
"shop",
|
|
12677
|
+
"product-list",
|
|
12678
|
+
"product-detail",
|
|
12679
|
+
"cart",
|
|
12680
|
+
"checkout",
|
|
12681
|
+
"gifting"
|
|
12682
|
+
];
|
|
12683
|
+
/**
|
|
12684
|
+
* Valid transform IDs for declarative layouts.
|
|
12685
|
+
*
|
|
12686
|
+
* This whitelist defines which transforms can be referenced in
|
|
12687
|
+
* $bind.transforms within custom block layouts. Transform IDs
|
|
12688
|
+
* not in this list will be rejected during validation.
|
|
12689
|
+
*/
|
|
12690
|
+
const ALLOWED_TRANSFORM_IDS = [
|
|
12691
|
+
"string.uppercase",
|
|
12692
|
+
"value.fallback",
|
|
12693
|
+
"date.formatShort",
|
|
12694
|
+
"array.length",
|
|
12695
|
+
"value.jsonArray",
|
|
12696
|
+
"value.csv",
|
|
12697
|
+
"array.marqueeLoop",
|
|
12698
|
+
"value.mapToken",
|
|
12699
|
+
"layout.maxWidthClass",
|
|
12700
|
+
"layout.headerRootClass",
|
|
12701
|
+
"layout.footerRootClass",
|
|
12702
|
+
"layout.headerRootStyle",
|
|
12703
|
+
"layout.headerSectionBackground",
|
|
12704
|
+
"layout.footerRootStyle",
|
|
12705
|
+
"ui.headerNavLinkClass",
|
|
12706
|
+
"ui.headingClassFromAlignment",
|
|
12707
|
+
"ui.bodyClassFromAlignment",
|
|
12708
|
+
"ui.stackAlignFromAlignment",
|
|
12709
|
+
"ui.sectionStateTone",
|
|
12710
|
+
"ui.heroContentWrapperClass",
|
|
12711
|
+
"ui.heroCtaJustify",
|
|
12712
|
+
"ui.heroStackJustifyFromVerticalAlignment",
|
|
12713
|
+
"ui.heroGridClassFromVerticalAlignment",
|
|
12714
|
+
"ui.conditionalClass",
|
|
12715
|
+
"ui.twoColumnContainerClass",
|
|
12716
|
+
"ui.twoColumnFlexBasis",
|
|
12717
|
+
"ui.imageGalleryFilterClass",
|
|
12718
|
+
"ui.imageGalleryTileClass",
|
|
12719
|
+
"ui.imageGalleryTileStyle",
|
|
12720
|
+
"ui.imageGalleryMarqueeTileStyle",
|
|
12721
|
+
"ui.imageGalleryMarqueeDuration",
|
|
12722
|
+
"ui.imageGalleryMarqueeDirection",
|
|
12723
|
+
"ui.imageFragmentClass",
|
|
12724
|
+
"ui.imageFragmentSize",
|
|
12725
|
+
"media.fromUrl",
|
|
12726
|
+
"background.resolveStyle",
|
|
12727
|
+
"background.resolveColorsStyle",
|
|
12728
|
+
"background.resolveFrameClassName",
|
|
12729
|
+
"background.resolveImageStyle",
|
|
12730
|
+
"background.resolveImageClassName",
|
|
12731
|
+
"background.resolveOverlayStyle",
|
|
12732
|
+
"links.resolve",
|
|
12733
|
+
"links.label",
|
|
12734
|
+
"content.interpolateRichTextTokens",
|
|
12735
|
+
"boxStyles.spacing",
|
|
12736
|
+
"boxStyles.resolveRootClass",
|
|
12737
|
+
"boxStyles.resolveRootStyle",
|
|
12738
|
+
"boxStyles.semanticGap",
|
|
12739
|
+
"boxStyles.semanticGapValue",
|
|
12740
|
+
"containerStyles.resolveBoxClassName"
|
|
12741
|
+
];
|
|
12742
|
+
/**
|
|
12743
|
+
* Set for O(1) lookup of allowed node types.
|
|
12744
|
+
*/
|
|
12745
|
+
const ALLOWED_NODE_TYPES_SET = new Set(ALLOWED_NODE_TYPES);
|
|
12746
|
+
/**
|
|
12747
|
+
* Set for O(1) lookup of allowed transform IDs.
|
|
12748
|
+
*/
|
|
12749
|
+
const ALLOWED_TRANSFORM_IDS_SET = new Set(ALLOWED_TRANSFORM_IDS);
|
|
12750
|
+
/**
|
|
12751
|
+
* Check if a node type is allowed in layouts.
|
|
12752
|
+
*/
|
|
12753
|
+
function isAllowedNodeType(type) {
|
|
12754
|
+
return ALLOWED_NODE_TYPES_SET.has(type);
|
|
12755
|
+
}
|
|
12756
|
+
/**
|
|
12757
|
+
* Check if a transform ID is allowed in layouts.
|
|
12758
|
+
*/
|
|
12759
|
+
function isAllowedTransformId(id) {
|
|
12760
|
+
return ALLOWED_TRANSFORM_IDS_SET.has(id);
|
|
12761
|
+
}
|
|
12762
|
+
|
|
12763
|
+
//#endregion
|
|
12764
|
+
//#region ../blocks/src/system/layout/repeaterGridModel.ts
|
|
12765
|
+
const DEFAULT_REPEATER_GRID_SPAN_FIELD_ID = "gridSpan";
|
|
12766
|
+
const DEFAULT_REPEATER_GRID_ROW_FIELD_ID = "gridRow";
|
|
12767
|
+
const DEFAULT_REPEATER_GRID_COLUMN_FIELD_ID = "gridColumn";
|
|
12768
|
+
const MIN_COLUMNS = 1;
|
|
12769
|
+
const MAX_REPEATER_GRID_COLUMNS = 6;
|
|
12770
|
+
const MAX_COLUMNS = MAX_REPEATER_GRID_COLUMNS;
|
|
12771
|
+
function normalizeInteger(value) {
|
|
12772
|
+
if (typeof value === "number" && Number.isFinite(value)) {
|
|
12773
|
+
return Math.trunc(value);
|
|
12774
|
+
}
|
|
12775
|
+
if (typeof value === "string") {
|
|
12776
|
+
const trimmed = value.trim();
|
|
12777
|
+
if (!trimmed) return null;
|
|
12778
|
+
const parsed = Number(trimmed);
|
|
12779
|
+
if (!Number.isFinite(parsed)) return null;
|
|
12780
|
+
return Math.trunc(parsed);
|
|
12781
|
+
}
|
|
12782
|
+
return null;
|
|
12783
|
+
}
|
|
12784
|
+
function clamp(value, min, max) {
|
|
12785
|
+
return Math.max(min, Math.min(max, value));
|
|
12786
|
+
}
|
|
12787
|
+
function asNonEmptyString(value) {
|
|
12788
|
+
if (typeof value !== "string") return null;
|
|
12789
|
+
const trimmed = value.trim();
|
|
12790
|
+
return trimmed.length > 0 ? trimmed : null;
|
|
12791
|
+
}
|
|
12792
|
+
function resolveRepeaterGridSpanFieldId(options) {
|
|
12793
|
+
return asNonEmptyString(options?.id) ?? DEFAULT_REPEATER_GRID_SPAN_FIELD_ID;
|
|
12794
|
+
}
|
|
12795
|
+
function resolveRepeaterGridRowFieldId(options) {
|
|
12796
|
+
return asNonEmptyString(options?.id) ?? DEFAULT_REPEATER_GRID_ROW_FIELD_ID;
|
|
12797
|
+
}
|
|
12798
|
+
function resolveRepeaterGridColumnFieldId(options) {
|
|
12799
|
+
return asNonEmptyString(options?.id) ?? DEFAULT_REPEATER_GRID_COLUMN_FIELD_ID;
|
|
12800
|
+
}
|
|
12801
|
+
function getBreakpointFallbackOrder(breakpoint) {
|
|
12802
|
+
if (breakpoint === "xl") return [
|
|
12803
|
+
"xl",
|
|
12804
|
+
"lg",
|
|
12805
|
+
"md",
|
|
12806
|
+
"sm",
|
|
12807
|
+
"base"
|
|
12808
|
+
];
|
|
12809
|
+
if (breakpoint === "lg") return [
|
|
12810
|
+
"lg",
|
|
12811
|
+
"md",
|
|
12812
|
+
"sm",
|
|
12813
|
+
"base"
|
|
12814
|
+
];
|
|
12815
|
+
if (breakpoint === "md") return [
|
|
12816
|
+
"md",
|
|
12817
|
+
"sm",
|
|
12818
|
+
"base",
|
|
12819
|
+
"lg"
|
|
12820
|
+
];
|
|
12821
|
+
if (breakpoint === "sm") return [
|
|
12822
|
+
"sm",
|
|
12823
|
+
"base",
|
|
12824
|
+
"lg"
|
|
12825
|
+
];
|
|
12826
|
+
return ["base", "lg"];
|
|
12827
|
+
}
|
|
12828
|
+
function resolveRepeaterGridBreakpointForWidth(width) {
|
|
12829
|
+
if (!Number.isFinite(width)) {
|
|
12830
|
+
return "lg";
|
|
12831
|
+
}
|
|
12832
|
+
if (width >= BREAKPOINT_XL) return "xl";
|
|
12833
|
+
if (width >= BREAKPOINT_LG) return "lg";
|
|
12834
|
+
if (width >= BREAKPOINT_MD) return "md";
|
|
12835
|
+
if (width >= BREAKPOINT_SM) return "sm";
|
|
12836
|
+
return "base";
|
|
12837
|
+
}
|
|
12838
|
+
function createRepeaterGridSpanField(options) {
|
|
12839
|
+
const fieldId = resolveRepeaterGridSpanFieldId(options);
|
|
12840
|
+
const label = asNonEmptyString(options?.label) ?? "Grid span";
|
|
12841
|
+
const description = asNonEmptyString(options?.description) ?? "Columns occupied by this item in layout-grid mode.";
|
|
12842
|
+
const defaultSpan = clamp(normalizeRepeaterGridColumns(options?.defaultSpan ?? 1), 1, 4);
|
|
12843
|
+
return {
|
|
12844
|
+
id: fieldId,
|
|
12845
|
+
type: "select",
|
|
12846
|
+
label,
|
|
12847
|
+
description,
|
|
12848
|
+
options: [
|
|
12849
|
+
{
|
|
12850
|
+
value: "1",
|
|
12851
|
+
label: "1 column"
|
|
12852
|
+
},
|
|
12853
|
+
{
|
|
12854
|
+
value: "2",
|
|
12855
|
+
label: "2 columns"
|
|
12856
|
+
},
|
|
12857
|
+
{
|
|
12858
|
+
value: "3",
|
|
12859
|
+
label: "3 columns"
|
|
12860
|
+
},
|
|
12861
|
+
{
|
|
12862
|
+
value: "4",
|
|
12863
|
+
label: "4 columns"
|
|
12864
|
+
}
|
|
12865
|
+
],
|
|
12866
|
+
defaultValue: String(defaultSpan)
|
|
12867
|
+
};
|
|
12868
|
+
}
|
|
12869
|
+
function createRepeaterGridRowField(options) {
|
|
12870
|
+
const fieldId = resolveRepeaterGridRowFieldId(options);
|
|
12871
|
+
const label = asNonEmptyString(options?.label) ?? "Grid row";
|
|
12872
|
+
const description = asNonEmptyString(options?.description) ?? "Manual layout row for this item.";
|
|
12873
|
+
const defaultRow = normalizeRepeaterGridRow(options?.defaultRow ?? 1);
|
|
12874
|
+
return {
|
|
12875
|
+
id: fieldId,
|
|
12876
|
+
type: "number",
|
|
12877
|
+
label,
|
|
12878
|
+
description,
|
|
12879
|
+
defaultValue: defaultRow,
|
|
12880
|
+
ui: { hidden: true }
|
|
12881
|
+
};
|
|
12882
|
+
}
|
|
12883
|
+
function createRepeaterGridColumnField(options) {
|
|
12884
|
+
const fieldId = resolveRepeaterGridColumnFieldId(options);
|
|
12885
|
+
const label = asNonEmptyString(options?.label) ?? "Grid column";
|
|
12886
|
+
const description = asNonEmptyString(options?.description) ?? "Manual layout starting column for this item.";
|
|
12887
|
+
const defaultColumn = normalizeRepeaterGridColumns(options?.defaultColumn ?? 1);
|
|
12888
|
+
return {
|
|
12889
|
+
id: fieldId,
|
|
12890
|
+
type: "number",
|
|
12891
|
+
label,
|
|
12892
|
+
description,
|
|
12893
|
+
defaultValue: defaultColumn,
|
|
12894
|
+
ui: { hidden: true }
|
|
12895
|
+
};
|
|
12896
|
+
}
|
|
12897
|
+
function withRepeaterGridSpanField(fields, options) {
|
|
12898
|
+
const spanFieldId = resolveRepeaterGridSpanFieldId(options);
|
|
12899
|
+
if (fields.some((field) => field.id === spanFieldId)) {
|
|
12900
|
+
return [...fields];
|
|
12901
|
+
}
|
|
12902
|
+
return [createRepeaterGridSpanField(options), ...fields];
|
|
12903
|
+
}
|
|
12904
|
+
function withRepeaterGridPositionFields(fields, options) {
|
|
12905
|
+
const rowFieldId = resolveRepeaterGridRowFieldId(options?.row);
|
|
12906
|
+
const withRow = fields.some((field) => field.id === rowFieldId) ? [...fields] : [createRepeaterGridRowField(options?.row), ...fields];
|
|
12907
|
+
const columnFieldId = resolveRepeaterGridColumnFieldId(options?.column);
|
|
12908
|
+
return withRow.some((field) => field.id === columnFieldId) ? withRow : [createRepeaterGridColumnField(options?.column), ...withRow];
|
|
12909
|
+
}
|
|
12910
|
+
function withRepeaterGridSpanFieldOnItemTypes(itemTypes, options) {
|
|
12911
|
+
const entries = Object.entries(itemTypes).map(([typeKey, itemType]) => {
|
|
12912
|
+
const nextFields = withRepeaterGridSpanField(itemType.fields, options);
|
|
12913
|
+
return [typeKey, {
|
|
12914
|
+
...itemType,
|
|
12915
|
+
fields: nextFields
|
|
12916
|
+
}];
|
|
12917
|
+
});
|
|
12918
|
+
return Object.fromEntries(entries);
|
|
12919
|
+
}
|
|
12920
|
+
function withRepeaterGridPositionFieldsOnItemTypes(itemTypes, options) {
|
|
12921
|
+
const entries = Object.entries(itemTypes).map(([typeKey, itemType]) => {
|
|
12922
|
+
const nextFields = withRepeaterGridPositionFields(itemType.fields, options);
|
|
12923
|
+
return [typeKey, {
|
|
12924
|
+
...itemType,
|
|
12925
|
+
fields: nextFields
|
|
12926
|
+
}];
|
|
12927
|
+
});
|
|
12928
|
+
return Object.fromEntries(entries);
|
|
12929
|
+
}
|
|
12930
|
+
function normalizeRepeaterGridColumns(value) {
|
|
12931
|
+
const parsed = normalizeInteger(value);
|
|
12932
|
+
if (parsed == null) {
|
|
12933
|
+
return MIN_COLUMNS;
|
|
12934
|
+
}
|
|
12935
|
+
return clamp(parsed, MIN_COLUMNS, MAX_COLUMNS);
|
|
12936
|
+
}
|
|
12937
|
+
function normalizeRepeaterGridRow(value) {
|
|
12938
|
+
const parsed = normalizeInteger(value);
|
|
12939
|
+
if (parsed == null) {
|
|
12940
|
+
return 1;
|
|
12941
|
+
}
|
|
12942
|
+
return Math.max(1, parsed);
|
|
12943
|
+
}
|
|
12944
|
+
function resolveRepeaterGridColumnsForBreakpoint(values, bindings, breakpoint) {
|
|
12945
|
+
const order = getBreakpointFallbackOrder(breakpoint);
|
|
12946
|
+
for (const candidate of order) {
|
|
12947
|
+
const fieldId = asNonEmptyString(bindings[candidate]);
|
|
12948
|
+
if (!fieldId) continue;
|
|
12949
|
+
const resolved = normalizeInteger(values[fieldId]);
|
|
12950
|
+
if (resolved == null) continue;
|
|
12951
|
+
return clamp(resolved, MIN_COLUMNS, MAX_COLUMNS);
|
|
12952
|
+
}
|
|
12953
|
+
return MIN_COLUMNS;
|
|
12954
|
+
}
|
|
12955
|
+
/** Strict admission; rendering normalization never grants permission to persist. */
|
|
12956
|
+
function parseRepeaterGridInteger(value) {
|
|
12957
|
+
if (typeof value !== "number" && (typeof value !== "string" || !value.trim())) return null;
|
|
12958
|
+
const number = Number(value);
|
|
12959
|
+
return Number.isInteger(number) ? number : null;
|
|
12960
|
+
}
|
|
12961
|
+
function isAcceptedRepeaterGridSpan(value, columns, acceptedSpans) {
|
|
12962
|
+
const span = parseRepeaterGridInteger(value);
|
|
12963
|
+
return span != null && span >= 1 && span <= columns && (!acceptedSpans || acceptedSpans.includes(span));
|
|
12964
|
+
}
|
|
12965
|
+
function resolveRepeaterGridAcceptedSpans(fields, spanField) {
|
|
12966
|
+
const field = fields.find((candidate) => candidate.id === spanField);
|
|
12967
|
+
return field?.type === "select" ? (field.options ?? []).map((option) => Number(option.value)) : undefined;
|
|
12968
|
+
}
|
|
12969
|
+
function normalizeRepeaterGridSpan(value, activeColumns, acceptedSpans) {
|
|
12970
|
+
if (acceptedSpans) {
|
|
12971
|
+
const valid = acceptedSpans.filter((span) => span <= activeColumns);
|
|
12972
|
+
const parsed$1 = parseRepeaterGridInteger(value);
|
|
12973
|
+
return parsed$1 != null && valid.includes(parsed$1) ? parsed$1 : valid.length ? Math.min(...valid) : 1;
|
|
12974
|
+
}
|
|
12975
|
+
const parsed = normalizeInteger(value);
|
|
12976
|
+
if (parsed == null) {
|
|
12977
|
+
return 1;
|
|
12978
|
+
}
|
|
12979
|
+
return clamp(parsed, 1, Math.max(1, activeColumns));
|
|
12980
|
+
}
|
|
12981
|
+
function normalizeRepeaterGridColStart(value, activeColumns, span) {
|
|
12982
|
+
const parsed = normalizeInteger(value);
|
|
12983
|
+
const normalizedColumns = Math.max(1, activeColumns);
|
|
12984
|
+
const normalizedSpan = clamp(span, 1, normalizedColumns);
|
|
12985
|
+
const maxColStart = Math.max(1, normalizedColumns - normalizedSpan + 1);
|
|
12986
|
+
if (parsed == null) {
|
|
12987
|
+
return 1;
|
|
12988
|
+
}
|
|
12989
|
+
return clamp(parsed, 1, maxColStart);
|
|
12990
|
+
}
|
|
12991
|
+
function deriveRepeaterGridMaxRowsForBreakpoint(input) {
|
|
12992
|
+
const { desktopColumns, desktopMaxRows, activeColumns } = input;
|
|
12993
|
+
if (desktopMaxRows == null) {
|
|
12994
|
+
return undefined;
|
|
12995
|
+
}
|
|
12996
|
+
const normalizedDesktopColumns = Math.max(1, desktopColumns);
|
|
12997
|
+
const normalizedActiveColumns = Math.max(1, activeColumns);
|
|
12998
|
+
const capacityCells = normalizedDesktopColumns * desktopMaxRows;
|
|
12999
|
+
return Math.max(1, Math.ceil(capacityCells / normalizedActiveColumns));
|
|
13000
|
+
}
|
|
13001
|
+
function toRowCellKey(row, col, columns) {
|
|
13002
|
+
return (row - 1) * columns + col;
|
|
13003
|
+
}
|
|
13004
|
+
function buildPlacementCells(placement) {
|
|
13005
|
+
return Array.from({ length: placement.span }, (_, index) => placement.colStart + index);
|
|
13006
|
+
}
|
|
13007
|
+
function buildOccupancyByCell(placements, columns) {
|
|
13008
|
+
const entries = placements.flatMap((placement) => buildPlacementCells(placement).map((col) => [toRowCellKey(placement.row, col, columns), placement.id]));
|
|
13009
|
+
const occupancy = entries.reduce((cells, [cell, id]) => new Map(cells).set(cell, new Set([...cells.get(cell) ?? [], id])), new Map());
|
|
13010
|
+
return {
|
|
13011
|
+
occupancy,
|
|
13012
|
+
hasCollisions: Array.from(occupancy.values()).some((ids) => ids.size > 1)
|
|
13013
|
+
};
|
|
13014
|
+
}
|
|
13015
|
+
function getOverlappingItemIds(input) {
|
|
13016
|
+
return new Set(buildPlacementCells(input).flatMap((col) => Array.from(input.occupancy.get(toRowCellKey(input.row, col, input.columns)) ?? []).filter((id) => !input.excludedIds?.has(id))));
|
|
13017
|
+
}
|
|
13018
|
+
function normalizeInputItem(item, columns, sourceIndex) {
|
|
13019
|
+
const span = normalizeRepeaterGridSpan(item.span, columns, item.acceptedSpans);
|
|
13020
|
+
const row = normalizeRepeaterGridRow(item.row);
|
|
13021
|
+
const colStart = normalizeRepeaterGridColStart(item.colStart, columns, span);
|
|
13022
|
+
const colEnd = colStart + span - 1;
|
|
13023
|
+
return {
|
|
13024
|
+
id: item.id,
|
|
13025
|
+
startNewRow: item.startNewRow,
|
|
13026
|
+
acceptedSpans: item.acceptedSpans,
|
|
13027
|
+
sourceIndex,
|
|
13028
|
+
span,
|
|
13029
|
+
row,
|
|
13030
|
+
colStart,
|
|
13031
|
+
colEnd,
|
|
13032
|
+
startCell: toRowCellKey(row, colStart, columns)
|
|
13033
|
+
};
|
|
13034
|
+
}
|
|
13035
|
+
/** Ordered, non-dense placement. Rendering clamps widths; admission checks originals separately. */
|
|
13036
|
+
function flowRepeaterGridItems(items, columns) {
|
|
13037
|
+
const count = normalizeRepeaterGridColumns(columns);
|
|
13038
|
+
return items.reduce((placed, item) => {
|
|
13039
|
+
const span = normalizeRepeaterGridSpan(item.span, count);
|
|
13040
|
+
const previous = placed.at(-1);
|
|
13041
|
+
const nextColumn = previous ? previous.colStart + previous.span : 1;
|
|
13042
|
+
const wraps = previous != null && (item.startNewRow === true || nextColumn + span - 1 > count);
|
|
13043
|
+
return [...placed, {
|
|
13044
|
+
...item,
|
|
13045
|
+
span,
|
|
13046
|
+
row: (previous?.row ?? 1) + (wraps ? 1 : 0),
|
|
13047
|
+
colStart: wraps ? 1 : nextColumn
|
|
13048
|
+
}];
|
|
13049
|
+
}, []);
|
|
13050
|
+
}
|
|
13051
|
+
/** @internal */
|
|
13052
|
+
function orderedGridItems(layout) {
|
|
13053
|
+
return [...layout.placements].sort((a, b) => a.sourceIndex - b.sourceIndex);
|
|
13054
|
+
}
|
|
13055
|
+
function isRepeaterGridLayoutInvalid(layout) {
|
|
13056
|
+
return layout.hasInvalidItems || layout.hasCollisions || layout.overCapacity;
|
|
13057
|
+
}
|
|
13058
|
+
function hasInvalidGridItems(items, columns, policy) {
|
|
13059
|
+
const flow = policy === "flow" ? flowRepeaterGridItems(items, columns) : null;
|
|
13060
|
+
return items.some((item, index) => !isValidGridItemGeometry(item, columns) || flow != null && (item.row !== flow[index]?.row || item.colStart !== flow[index]?.colStart));
|
|
13061
|
+
}
|
|
13062
|
+
function isValidGridItemGeometry(item, columns) {
|
|
13063
|
+
return isAcceptedRepeaterGridSpan(item.span, columns, item.acceptedSpans) && parseRepeaterGridInteger(item.row) != null && item.row >= 1 && parseRepeaterGridInteger(item.colStart) != null && item.colStart >= 1 && item.colStart + item.span - 1 <= columns;
|
|
13064
|
+
}
|
|
13065
|
+
function findFlowAppendPlacement(layout, span, startNewRow) {
|
|
13066
|
+
const next = flowRepeaterGridItems([...orderedGridItems(layout), {
|
|
13067
|
+
id: "new",
|
|
13068
|
+
span,
|
|
13069
|
+
row: 1,
|
|
13070
|
+
colStart: 1,
|
|
13071
|
+
startNewRow
|
|
13072
|
+
}], layout.columns).at(-1);
|
|
13073
|
+
return !isRepeaterGridLayoutInvalid(layout) && next && (layout.maxRows == null || next.row <= layout.maxRows) ? {
|
|
13074
|
+
row: next.row,
|
|
13075
|
+
colStart: next.colStart
|
|
13076
|
+
} : null;
|
|
13077
|
+
}
|
|
13078
|
+
/** @internal */
|
|
13079
|
+
function planFlowItems(layout, items) {
|
|
13080
|
+
const placed = flowRepeaterGridItems(items, layout.columns);
|
|
13081
|
+
if (isRepeaterGridLayoutInvalid(layout) || placed.some((item) => !isAcceptedRepeaterGridSpan(item.span, layout.columns, item.acceptedSpans) || layout.maxRows != null && item.row > layout.maxRows)) return { kind: "blocked" };
|
|
13082
|
+
const [first, ...rest] = placed.map(({ id, row, colStart, span }) => ({
|
|
13083
|
+
id,
|
|
13084
|
+
row,
|
|
13085
|
+
colStart,
|
|
13086
|
+
span
|
|
13087
|
+
}));
|
|
13088
|
+
return first ? {
|
|
13089
|
+
kind: "move",
|
|
13090
|
+
updates: [first, ...rest]
|
|
13091
|
+
} : { kind: "unchanged" };
|
|
13092
|
+
}
|
|
13093
|
+
/** Insertion reorder, with a complete coordinate write for the resulting order. */
|
|
13094
|
+
function planRepeaterGridReorder(layout, id, to, interaction = "step") {
|
|
13095
|
+
if (layout.placementPolicy !== "flow") return { kind: "blocked" };
|
|
13096
|
+
const items = orderedGridItems(layout);
|
|
13097
|
+
const from = items.findIndex((item) => item.id === id);
|
|
13098
|
+
if (from < 0 || to < 0 || to >= items.length) return { kind: "blocked" };
|
|
13099
|
+
if (from === to) return { kind: "unchanged" };
|
|
13100
|
+
const active = items[from];
|
|
13101
|
+
if (!active) return { kind: "blocked" };
|
|
13102
|
+
return decideFlowReorder({
|
|
13103
|
+
layout,
|
|
13104
|
+
items,
|
|
13105
|
+
active,
|
|
13106
|
+
from,
|
|
13107
|
+
to,
|
|
13108
|
+
interaction
|
|
13109
|
+
});
|
|
13110
|
+
}
|
|
13111
|
+
function decideFlowReorder(input) {
|
|
13112
|
+
const { layout, items, active, from, to, interaction } = input;
|
|
13113
|
+
const id = active.id;
|
|
13114
|
+
const remaining = items.filter((item) => item.id !== id);
|
|
13115
|
+
const inserted = [
|
|
13116
|
+
...remaining.slice(0, to),
|
|
13117
|
+
active,
|
|
13118
|
+
...remaining.slice(to)
|
|
13119
|
+
];
|
|
13120
|
+
const clearRowBreak = interaction === "drag" && active.startNewRow === true && dragInsertsWithinRow(items, from, to, layout.columns);
|
|
13121
|
+
const nextItems = clearRowBreak ? inserted.map((item) => item.id === id ? {
|
|
13122
|
+
...item,
|
|
13123
|
+
startNewRow: false
|
|
13124
|
+
} : item) : inserted;
|
|
13125
|
+
const decision = planFlowItems(layout, nextItems);
|
|
13126
|
+
return decision.kind === "move" ? {
|
|
13127
|
+
...decision,
|
|
13128
|
+
reorder: {
|
|
13129
|
+
id,
|
|
13130
|
+
from,
|
|
13131
|
+
to
|
|
13132
|
+
},
|
|
13133
|
+
...clearRowBreak ? { startNewRowUpdate: {
|
|
13134
|
+
id,
|
|
13135
|
+
value: false
|
|
13136
|
+
} } : {}
|
|
13137
|
+
} : decision;
|
|
13138
|
+
}
|
|
13139
|
+
/** Read the insertion bar position, independently of the dragged width. */
|
|
13140
|
+
function dragInsertsWithinRow(items, from, to, columns) {
|
|
13141
|
+
const target = items[to];
|
|
13142
|
+
if (!target) return false;
|
|
13143
|
+
const column = from < to ? target.colEnd + 1 : target.colStart;
|
|
13144
|
+
return column > 1 && column <= columns;
|
|
13145
|
+
}
|
|
13146
|
+
function planRepeaterGridStartNewRow(layout, id, value) {
|
|
13147
|
+
const active = layout.byId[id];
|
|
13148
|
+
if (layout.placementPolicy !== "flow" || !active) return { kind: "blocked" };
|
|
13149
|
+
const decision = planFlowItems(layout, orderedGridItems(layout).map((item) => item.id === id ? {
|
|
13150
|
+
...item,
|
|
13151
|
+
startNewRow: value
|
|
13152
|
+
} : item));
|
|
13153
|
+
return decision.kind === "move" ? {
|
|
13154
|
+
...decision,
|
|
13155
|
+
startNewRowUpdate: {
|
|
13156
|
+
id,
|
|
13157
|
+
value
|
|
13158
|
+
}
|
|
13159
|
+
} : decision;
|
|
13160
|
+
}
|
|
13161
|
+
function buildRepeaterGridLayout(input) {
|
|
13162
|
+
const columns = normalizeRepeaterGridColumns(input.columns);
|
|
13163
|
+
const maxRows = input.maxRows != null ? Math.max(1, Math.trunc(input.maxRows)) : undefined;
|
|
13164
|
+
const capacityCells = maxRows != null ? maxRows * columns : undefined;
|
|
13165
|
+
const placements = input.items.map((item, sourceIndex) => normalizeInputItem(item, columns, sourceIndex)).sort((a, b) => {
|
|
13166
|
+
if (a.row !== b.row) return a.row - b.row;
|
|
13167
|
+
if (a.colStart !== b.colStart) return a.colStart - b.colStart;
|
|
13168
|
+
return a.sourceIndex - b.sourceIndex;
|
|
13169
|
+
});
|
|
13170
|
+
const byId = Object.fromEntries(placements.map((placement) => [placement.id, placement]));
|
|
13171
|
+
const maxRowUsed = placements.length > 0 ? Math.max(...placements.map((placement) => placement.row)) : 0;
|
|
13172
|
+
const { hasCollisions } = buildOccupancyByCell(placements, columns);
|
|
13173
|
+
const overCapacity = maxRows != null && maxRowUsed > maxRows;
|
|
13174
|
+
return {
|
|
13175
|
+
placementPolicy: input.placementPolicy,
|
|
13176
|
+
sourceItems: input.items.map((item) => ({ ...item })),
|
|
13177
|
+
columns,
|
|
13178
|
+
maxRows,
|
|
13179
|
+
capacityCells,
|
|
13180
|
+
maxRowUsed,
|
|
13181
|
+
overCapacity,
|
|
13182
|
+
hasCollisions,
|
|
13183
|
+
hasInvalidItems: hasInvalidGridItems(input.items, columns, input.placementPolicy),
|
|
13184
|
+
placements,
|
|
13185
|
+
byId
|
|
13186
|
+
};
|
|
13187
|
+
}
|
|
13188
|
+
function findFirstAvailableRepeaterGridPlacement(input) {
|
|
13189
|
+
const columns = normalizeRepeaterGridColumns(input.columns);
|
|
13190
|
+
const maxRows = input.maxRows != null ? Math.max(1, Math.trunc(input.maxRows)) : undefined;
|
|
13191
|
+
if (!isAcceptedRepeaterGridSpan(input.newItemSpan, columns, input.acceptedSpans)) return null;
|
|
13192
|
+
const span = normalizeRepeaterGridSpan(input.newItemSpan, columns, input.acceptedSpans);
|
|
13193
|
+
const layout = buildRepeaterGridLayout({
|
|
13194
|
+
items: input.items,
|
|
13195
|
+
columns,
|
|
13196
|
+
maxRows,
|
|
13197
|
+
placementPolicy: input.placementPolicy
|
|
13198
|
+
});
|
|
13199
|
+
if (input.placementPolicy === "flow") return findFlowAppendPlacement(layout, span, input.startNewRow);
|
|
13200
|
+
const { occupancy } = buildOccupancyByCell(layout.placements, columns);
|
|
13201
|
+
const rowLimit = maxRows ?? Math.max(1, layout.maxRowUsed + 1);
|
|
13202
|
+
if (isRepeaterGridLayoutInvalid(layout)) return null;
|
|
13203
|
+
const maxColStart = columns - span + 1;
|
|
13204
|
+
return Array.from({ length: rowLimit * maxColStart }, (_, index) => ({
|
|
13205
|
+
row: Math.floor(index / maxColStart) + 1,
|
|
13206
|
+
colStart: index % maxColStart + 1
|
|
13207
|
+
})).find(({ row, colStart }) => getOverlappingItemIds({
|
|
13208
|
+
occupancy,
|
|
13209
|
+
row,
|
|
13210
|
+
colStart,
|
|
13211
|
+
span,
|
|
13212
|
+
columns
|
|
13213
|
+
}).size === 0) ?? null;
|
|
13214
|
+
}
|
|
13215
|
+
function canAppendRepeaterGridItem(input) {
|
|
13216
|
+
return findFirstAvailableRepeaterGridPlacement({
|
|
13217
|
+
items: input.items,
|
|
13218
|
+
newItemSpan: input.newItemSpan,
|
|
13219
|
+
acceptedSpans: input.acceptedSpans,
|
|
13220
|
+
columns: input.columns,
|
|
13221
|
+
maxRows: input.maxRows
|
|
13222
|
+
}) != null;
|
|
13223
|
+
}
|
|
13224
|
+
|
|
13225
|
+
//#endregion
|
|
13226
|
+
//#region ../blocks/src/system/layout/repeaterGrid.ts
|
|
13227
|
+
function placementsOverlap(left, right) {
|
|
13228
|
+
return left.row === right.row && left.colStart < right.colStart + right.span && right.colStart < left.colStart + left.span;
|
|
13229
|
+
}
|
|
13230
|
+
function swapPositionUpdates(active, target) {
|
|
13231
|
+
const adjacent = active.row === target.row && (active.colEnd + 1 === target.colStart || target.colEnd + 1 === active.colStart);
|
|
13232
|
+
const activeCol = adjacent && active.colStart < target.colStart ? active.colStart + target.span : target.colStart;
|
|
13233
|
+
const targetCol = adjacent && target.colStart < active.colStart ? target.colStart + active.span : active.colStart;
|
|
13234
|
+
return [{
|
|
13235
|
+
id: active.id,
|
|
13236
|
+
row: target.row,
|
|
13237
|
+
colStart: activeCol,
|
|
13238
|
+
span: active.span
|
|
13239
|
+
}, {
|
|
13240
|
+
id: target.id,
|
|
13241
|
+
row: active.row,
|
|
13242
|
+
colStart: targetCol,
|
|
13243
|
+
span: target.span
|
|
13244
|
+
}];
|
|
13245
|
+
}
|
|
13246
|
+
/** Admission retains raw authoring geometry; display-only callers may supply placements alone. */
|
|
13247
|
+
function sourceGridItems(layout) {
|
|
13248
|
+
return layout.sourceItems ?? layout.placements;
|
|
13249
|
+
}
|
|
13250
|
+
/** Repair edits may remove existing defects, but cannot introduce another one. */
|
|
13251
|
+
function gridDefects(layout) {
|
|
13252
|
+
const geometry = sourceGridItems(layout).flatMap((item) => [
|
|
13253
|
+
...!isAcceptedRepeaterGridSpan(item.span, layout.columns, item.acceptedSpans) ? [`span:${item.id}`] : [],
|
|
13254
|
+
...!Number.isInteger(item.row) || item.row < 1 ? [`row:${item.id}`] : [],
|
|
13255
|
+
...!Number.isInteger(item.colStart) || item.colStart < 1 || item.colStart + item.span - 1 > layout.columns ? [`column:${item.id}`] : []
|
|
13256
|
+
]);
|
|
13257
|
+
const collisions = layout.placements.flatMap((item, index) => layout.placements.slice(index + 1).filter((other) => placementsOverlap(item, other)).map((other) => `collision:${[item.id, other.id].sort().join(":")}`));
|
|
13258
|
+
const capacity = layout.placements.filter((item) => layout.maxRows != null && item.row > layout.maxRows).map((item) => `capacity:${item.id}`);
|
|
13259
|
+
return new Set([
|
|
13260
|
+
...geometry,
|
|
13261
|
+
...collisions,
|
|
13262
|
+
...capacity,
|
|
13263
|
+
...(layout.invalidCoordinateIds ?? []).map((id) => `coordinate:${id}`)
|
|
13264
|
+
]);
|
|
13265
|
+
}
|
|
13266
|
+
function validateGridMove(layout, updates) {
|
|
13267
|
+
const items = sourceGridItems(layout).map((item) => ({
|
|
13268
|
+
...item,
|
|
13269
|
+
...updates.find((update) => update.id === item.id)
|
|
13270
|
+
}));
|
|
13271
|
+
const candidate = buildRepeaterGridLayout({
|
|
13272
|
+
items,
|
|
13273
|
+
columns: layout.columns,
|
|
13274
|
+
maxRows: layout.maxRows,
|
|
13275
|
+
placementPolicy: layout.placementPolicy
|
|
13276
|
+
});
|
|
13277
|
+
const unresolved = (layout.invalidCoordinateIds ?? []).filter((id) => !updates.some((update) => update.id === id));
|
|
13278
|
+
const before = gridDefects(layout);
|
|
13279
|
+
const after = gridDefects({
|
|
13280
|
+
...candidate,
|
|
13281
|
+
invalidCoordinateIds: unresolved
|
|
13282
|
+
});
|
|
13283
|
+
const valid = before.size === 0 ? after.size === 0 : after.size < before.size && [...after].every((defect) => before.has(defect));
|
|
13284
|
+
return valid ? {
|
|
13285
|
+
kind: "move",
|
|
13286
|
+
updates
|
|
13287
|
+
} : { kind: "blocked" };
|
|
13288
|
+
}
|
|
13289
|
+
function decideGridDrop(layout, active, destination) {
|
|
13290
|
+
const occupants = layout.placements.filter((placement) => placement.id !== active.id && placement.row === destination.row && destination.colStart >= placement.colStart && destination.colStart <= placement.colEnd);
|
|
13291
|
+
if (occupants.length > 1) return { kind: "blocked" };
|
|
13292
|
+
const target = occupants[0];
|
|
13293
|
+
if (target) return validateGridMove(layout, swapPositionUpdates(active, target));
|
|
13294
|
+
if (destination.row === active.row && destination.colStart === active.colStart && !isRepeaterGridLayoutInvalid(layout)) return { kind: "unchanged" };
|
|
13295
|
+
return validateGridMove(layout, [{
|
|
13296
|
+
id: active.id,
|
|
13297
|
+
row: destination.row,
|
|
13298
|
+
colStart: destination.colStart,
|
|
13299
|
+
span: active.span
|
|
13300
|
+
}]);
|
|
13301
|
+
}
|
|
13302
|
+
function buildRepeaterGridDropTargets(input) {
|
|
13303
|
+
const { layout, draggedId, rowsToRender } = input;
|
|
13304
|
+
const active = layout.byId[draggedId];
|
|
13305
|
+
if (!active || layout.placementPolicy === "flow" && isRepeaterGridLayoutInvalid(layout)) return [];
|
|
13306
|
+
if (layout.placementPolicy === "flow") return orderedGridItems(layout).map((item, index) => ({
|
|
13307
|
+
id: `repeater-grid-drop-${item.row}-${item.colStart}`,
|
|
13308
|
+
row: item.row,
|
|
13309
|
+
colStart: item.colStart,
|
|
13310
|
+
cell: item.startCell,
|
|
13311
|
+
decision: planRepeaterGridReorder(layout, draggedId, index, input.interaction ?? "drag")
|
|
13312
|
+
}));
|
|
13313
|
+
const rowCeiling = layout.maxRows ?? Math.max(1, rowsToRender ?? layout.maxRowUsed + 1, active.row + 1);
|
|
13314
|
+
const targets = Array.from({ length: rowCeiling * layout.columns }, (_, index) => {
|
|
13315
|
+
const row = Math.floor(index / layout.columns) + 1;
|
|
13316
|
+
const colStart = index % layout.columns + 1;
|
|
13317
|
+
return {
|
|
13318
|
+
id: `repeater-grid-drop-${row}-${colStart}`,
|
|
13319
|
+
row,
|
|
13320
|
+
colStart,
|
|
13321
|
+
cell: index + 1,
|
|
13322
|
+
decision: decideGridDrop(layout, active, {
|
|
13323
|
+
row,
|
|
13324
|
+
colStart
|
|
13325
|
+
})
|
|
13326
|
+
};
|
|
13327
|
+
});
|
|
13328
|
+
return targets.some((target) => target.row === active.row && target.colStart === active.colStart) ? targets : [...targets, {
|
|
13329
|
+
id: `repeater-grid-drop-${active.row}-${active.colStart}`,
|
|
13330
|
+
row: active.row,
|
|
13331
|
+
colStart: active.colStart,
|
|
13332
|
+
cell: active.startCell,
|
|
13333
|
+
decision: { kind: "unchanged" }
|
|
13334
|
+
}];
|
|
13335
|
+
}
|
|
13336
|
+
/** Width edits keep the item's start when it fits; otherwise relocate it to the first free cell.
|
|
13337
|
+
* Neighbours keep their positions. A full bounded grid blocks the change without partial writes.
|
|
13338
|
+
*/
|
|
13339
|
+
function planRepeaterGridSpanChange(input) {
|
|
13340
|
+
const { layout, id, span } = input;
|
|
13341
|
+
const active = layout.byId[id];
|
|
13342
|
+
if (layout.placementPolicy === "flow" && isRepeaterGridLayoutInvalid(layout)) return { kind: "blocked" };
|
|
13343
|
+
if (!active || !isAcceptedRepeaterGridSpan(span, layout.columns, active.acceptedSpans)) return { kind: "blocked" };
|
|
13344
|
+
const nextSpan = Number(span);
|
|
13345
|
+
if (!isRepeaterGridLayoutInvalid(layout) && sourceGridItems(layout).find((item) => item.id === id)?.span === nextSpan) return { kind: "unchanged" };
|
|
13346
|
+
if (layout.placementPolicy === "flow") return planFlowItems(layout, orderedGridItems(layout).map((item) => item.id === id ? {
|
|
13347
|
+
...item,
|
|
13348
|
+
span: nextSpan
|
|
13349
|
+
} : item));
|
|
13350
|
+
return planFreeGridSpanChange(layout, active, nextSpan);
|
|
13351
|
+
}
|
|
13352
|
+
/** Free width edits retain the current start when possible, then use the shared first-free planner. */
|
|
13353
|
+
function planFreeGridSpanChange(layout, active, nextSpan) {
|
|
13354
|
+
const id = active.id;
|
|
13355
|
+
const atCurrent = validateGridMove(layout, [{
|
|
13356
|
+
id,
|
|
13357
|
+
span: nextSpan,
|
|
13358
|
+
row: active.row,
|
|
13359
|
+
colStart: active.colStart
|
|
13360
|
+
}]);
|
|
13361
|
+
if (atCurrent.kind === "move") return atCurrent;
|
|
13362
|
+
const destination = findFirstAvailableRepeaterGridPlacement({
|
|
13363
|
+
items: layout.placements.filter((item) => item.id !== id),
|
|
13364
|
+
newItemSpan: nextSpan,
|
|
13365
|
+
acceptedSpans: active.acceptedSpans,
|
|
13366
|
+
columns: layout.columns,
|
|
13367
|
+
maxRows: layout.maxRows
|
|
13368
|
+
});
|
|
13369
|
+
return destination ? validateGridMove(layout, [{
|
|
13370
|
+
id,
|
|
13371
|
+
span: nextSpan,
|
|
13372
|
+
...destination
|
|
13373
|
+
}]) : { kind: "blocked" };
|
|
13374
|
+
}
|
|
13375
|
+
/** Narrow rendering is a projection only. Never feed these placements into an authoring write. */
|
|
13376
|
+
function projectRepeaterGridLayout(layout, columns) {
|
|
13377
|
+
if (isRepeaterGridLayoutInvalid(layout)) return projectRepeaterGridLayoutForRendering(layout, columns);
|
|
13378
|
+
if (columns === layout.columns) return layout;
|
|
13379
|
+
if (layout.placementPolicy === "flow") return buildProjectedRepeaterGridLayout(layout, flowRepeaterGridItems(orderedGridItems(layout), columns), columns);
|
|
13380
|
+
const items = layout.placements.reduce((placed, item) => {
|
|
13381
|
+
const span = normalizeRepeaterGridSpan(item.span, columns);
|
|
13382
|
+
const destination = findFirstAvailableRepeaterGridPlacement({
|
|
13383
|
+
items: placed,
|
|
13384
|
+
newItemSpan: span,
|
|
13385
|
+
columns
|
|
13386
|
+
});
|
|
13387
|
+
return [...placed, {
|
|
13388
|
+
id: item.id,
|
|
13389
|
+
span,
|
|
13390
|
+
row: destination?.row ?? placed.length + 1,
|
|
13391
|
+
colStart: destination?.colStart ?? 1
|
|
13392
|
+
}];
|
|
13393
|
+
}, []);
|
|
13394
|
+
return buildProjectedRepeaterGridLayout(layout, items, columns);
|
|
13395
|
+
}
|
|
13396
|
+
function buildProjectedRepeaterGridLayout(source, items, columns) {
|
|
13397
|
+
const projected = buildRepeaterGridLayout({
|
|
13398
|
+
items: items.map(({ id, span, row, colStart, startNewRow }) => ({
|
|
13399
|
+
id,
|
|
13400
|
+
span,
|
|
13401
|
+
row,
|
|
13402
|
+
colStart,
|
|
13403
|
+
startNewRow
|
|
13404
|
+
})),
|
|
13405
|
+
columns,
|
|
13406
|
+
placementPolicy: source.placementPolicy === "flow" || isRepeaterGridLayoutInvalid(source) ? "flow" : source.placementPolicy
|
|
13407
|
+
});
|
|
13408
|
+
const placements = projected.placements.map((item) => ({
|
|
13409
|
+
...item,
|
|
13410
|
+
sourceIndex: source.byId[item.id]?.sourceIndex ?? item.sourceIndex
|
|
13411
|
+
}));
|
|
13412
|
+
return {
|
|
13413
|
+
...projected,
|
|
13414
|
+
placements,
|
|
13415
|
+
byId: Object.fromEntries(placements.map((item) => [item.id, item]))
|
|
13416
|
+
};
|
|
13417
|
+
}
|
|
13418
|
+
/** Public fallback: preserve saved array order, use non-dense auto-placement, and ignore authoring capacity. */
|
|
13419
|
+
function projectRepeaterGridLayoutForRendering(layout, columns = layout.columns) {
|
|
13420
|
+
if (!isRepeaterGridLayoutInvalid(layout)) {
|
|
13421
|
+
return projectRepeaterGridLayout(layout, columns);
|
|
13422
|
+
}
|
|
13423
|
+
const count = normalizeRepeaterGridColumns(columns);
|
|
13424
|
+
return buildProjectedRepeaterGridLayout(layout, flowRepeaterGridItems(orderedGridItems(layout), count), count);
|
|
13425
|
+
}
|
|
13426
|
+
/** An explicit column reduction re-places free grids through the existing placement primitive. */
|
|
13427
|
+
function planRepeaterGridColumnChange(layout, columns) {
|
|
13428
|
+
if (layout.placementPolicy === "flow" || columns >= layout.columns) return { kind: "unchanged" };
|
|
13429
|
+
if (isRepeaterGridLayoutInvalid(layout)) return { kind: "blocked" };
|
|
13430
|
+
const placed = orderedGridItems(layout).reduce((items, item) => {
|
|
13431
|
+
if (!items) return null;
|
|
13432
|
+
const bounded = normalizeRepeaterGridSpan(item.span, columns);
|
|
13433
|
+
const span = item.acceptedSpans ? Math.max(0, ...item.acceptedSpans.filter((width) => width <= bounded)) : bounded;
|
|
13434
|
+
const destination = findFirstAvailableRepeaterGridPlacement({
|
|
13435
|
+
items,
|
|
13436
|
+
columns,
|
|
13437
|
+
maxRows: layout.maxRows,
|
|
13438
|
+
newItemSpan: span,
|
|
13439
|
+
acceptedSpans: item.acceptedSpans
|
|
13440
|
+
});
|
|
13441
|
+
return destination ? [...items, {
|
|
13442
|
+
...item,
|
|
13443
|
+
span,
|
|
13444
|
+
...destination
|
|
13445
|
+
}] : null;
|
|
13446
|
+
}, []);
|
|
13447
|
+
const [first, ...rest] = placed ?? [];
|
|
13448
|
+
return first ? {
|
|
13449
|
+
kind: "move",
|
|
13450
|
+
updates: [first, ...rest]
|
|
13451
|
+
} : placed ? { kind: "unchanged" } : { kind: "blocked" };
|
|
13452
|
+
}
|
|
13453
|
+
|
|
13454
|
+
//#endregion
|
|
13455
|
+
//#region ../blocks/src/system/manifest/layoutGridConfiguration.ts
|
|
13456
|
+
const itemFieldsSchema = z.object({ fields: z.array(z.object({
|
|
13457
|
+
id: z.string(),
|
|
13458
|
+
type: z.string(),
|
|
13459
|
+
defaultValue: z.unknown().optional(),
|
|
13460
|
+
options: z.array(z.object({ value: z.string() })).optional(),
|
|
13461
|
+
multiple: z.boolean().optional(),
|
|
13462
|
+
source: z.object({ kind: z.string() }).optional()
|
|
13463
|
+
})) });
|
|
13464
|
+
function validateSpanSelect(span, columns, ctx) {
|
|
13465
|
+
if (span.type !== "select") return;
|
|
13466
|
+
const options = span.options ?? [];
|
|
13467
|
+
const invalidOptions = options.some((option) => !isAcceptedRepeaterGridSpan(option.value, columns) || String(Number(option.value)) !== option.value);
|
|
13468
|
+
if (span.multiple || options.length === 0 || invalidOptions || span.source && span.source.kind !== "static") {
|
|
13469
|
+
ctx.addIssue({
|
|
13470
|
+
code: "custom",
|
|
13471
|
+
path: ["ui", "layoutGridSpanField"],
|
|
13472
|
+
message: `Layout-grid span select must have single, static integer options within 1..${columns}`
|
|
13473
|
+
});
|
|
13474
|
+
}
|
|
13475
|
+
if (span.defaultValue !== undefined && !options.some((option) => option.value === span.defaultValue)) {
|
|
13476
|
+
ctx.addIssue({
|
|
13477
|
+
code: "custom",
|
|
13478
|
+
path: ["ui", "layoutGridSpanField"],
|
|
13479
|
+
message: "Layout-grid default span must be one of the span select options"
|
|
13480
|
+
});
|
|
13481
|
+
}
|
|
13482
|
+
}
|
|
13483
|
+
/** Both monomorphic and polymorphic item schemas have the same layout contract. */
|
|
13484
|
+
function validateLayoutGridConfiguration(data, ctx) {
|
|
13485
|
+
const ui = data.ui;
|
|
13486
|
+
if (ui?.repeaterPreset !== "layoutGrid") return;
|
|
13487
|
+
if (ui.layoutGridColumns !== undefined === (ui.layoutGridColumnsFields !== undefined)) {
|
|
13488
|
+
ctx.addIssue({
|
|
13489
|
+
code: "custom",
|
|
13490
|
+
path: ["ui", "layoutGridColumns"],
|
|
13491
|
+
message: "Layout-grid requires exactly one of ui.layoutGridColumns or ui.layoutGridColumnsFields.lg; do not set both"
|
|
13492
|
+
});
|
|
13493
|
+
}
|
|
13494
|
+
const spanId = resolveRepeaterGridSpanFieldId({ id: ui.layoutGridSpanField });
|
|
13495
|
+
const requiredIds = [
|
|
13496
|
+
spanId,
|
|
13497
|
+
resolveRepeaterGridRowFieldId({ id: ui.layoutGridRowField }),
|
|
13498
|
+
resolveRepeaterGridColumnFieldId({ id: ui.layoutGridColumnField })
|
|
13499
|
+
];
|
|
13500
|
+
const schemas = data.polymorphic && data.itemTypes ? Object.values(data.itemTypes) : [data.schema];
|
|
13501
|
+
schemas.forEach((schema) => {
|
|
13502
|
+
const parsed = itemFieldsSchema.safeParse(schema);
|
|
13503
|
+
if (!parsed.success) return;
|
|
13504
|
+
if (!requiredIds.every((id) => parsed.data.fields.some((field) => field.id === id))) {
|
|
13505
|
+
ctx.addIssue({
|
|
13506
|
+
code: "custom",
|
|
13507
|
+
path: ["ui", "layoutGridRowField"],
|
|
13508
|
+
message: `layoutGrid repeaters require span/row/column fields (${requiredIds.join(", ")}) to exist in repeater item fields`
|
|
13509
|
+
});
|
|
13510
|
+
}
|
|
13511
|
+
if (ui.layoutGridStartNewRowField && !parsed.data.fields.some((field) => field.id === ui.layoutGridStartNewRowField && field.type === "boolean")) {
|
|
13512
|
+
ctx.addIssue({
|
|
13513
|
+
code: "custom",
|
|
13514
|
+
path: ["ui", "layoutGridStartNewRowField"],
|
|
13515
|
+
message: "Layout-grid Start a new row field must reference a boolean item field"
|
|
13516
|
+
});
|
|
13517
|
+
}
|
|
13518
|
+
if (ui.layoutGridStartNewRowField && ui.layoutGridColumns === undefined) {
|
|
13519
|
+
ctx.addIssue({
|
|
13520
|
+
code: "custom",
|
|
13521
|
+
path: ["ui", "layoutGridStartNewRowField"],
|
|
13522
|
+
message: "Layout-grid Start a new row requires fixed columns (flow placement)"
|
|
13523
|
+
});
|
|
13524
|
+
}
|
|
13525
|
+
parsed.data.fields.filter((field) => field.id === spanId).forEach((span) => validateSpanSelect(span, ui.layoutGridColumns ?? MAX_REPEATER_GRID_COLUMNS, ctx));
|
|
13526
|
+
});
|
|
13527
|
+
}
|
|
13528
|
+
|
|
13529
|
+
//#endregion
|
|
13530
|
+
//#region ../blocks/src/system/icons/presets.ts
|
|
13531
|
+
const ICON_PICKER_PRESETS = ["cta", "feature"];
|
|
13532
|
+
function isIconPickerPreset(value) {
|
|
13533
|
+
return typeof value === "string" && ICON_PICKER_PRESETS.includes(value);
|
|
13534
|
+
}
|
|
13535
|
+
function resolveIconPickerPreset(value) {
|
|
13536
|
+
return isIconPickerPreset(value) ? value : "cta";
|
|
13537
|
+
}
|
|
13538
|
+
|
|
13539
|
+
//#endregion
|
|
13540
|
+
//#region ../blocks/src/system/manifest/sdkBlockPresentationSchema.ts
|
|
13541
|
+
const sdkBlockPresentationShape = {
|
|
13542
|
+
variants: z.record(z.string().min(1), NodeSchema).optional(),
|
|
13543
|
+
defaultVariant: z.string().min(1).optional(),
|
|
13544
|
+
variantOptions: z.record(z.string().min(1), z.object({
|
|
13545
|
+
label: z.string().min(1).optional(),
|
|
13546
|
+
description: z.string().min(1).optional(),
|
|
13547
|
+
icon: z.string().min(1).optional()
|
|
13548
|
+
})).optional()
|
|
13549
|
+
};
|
|
13550
|
+
function validateSdkBlockDeclaration(data, ctx) {
|
|
13551
|
+
validateTitleSource(data, ctx);
|
|
13552
|
+
if (data.layout && data.variants) ctx.addIssue({
|
|
13553
|
+
code: z.ZodIssueCode.custom,
|
|
13554
|
+
message: "Custom block must define either `layout` or `variants`, not both",
|
|
13555
|
+
path: ["variants"]
|
|
13556
|
+
});
|
|
13557
|
+
if (data.variants) validateVariantKeys(data, ctx);
|
|
13558
|
+
}
|
|
13559
|
+
function validateTitleSource(data, ctx) {
|
|
13560
|
+
if (!data.titleSource) return;
|
|
13561
|
+
const sources = Array.isArray(data.titleSource) ? data.titleSource : [data.titleSource];
|
|
13562
|
+
sources.forEach((source, index) => {
|
|
13563
|
+
if (data.fields.some((field) => field !== null && typeof field === "object" && "id" in field && field.id === source)) return;
|
|
13564
|
+
ctx.addIssue({
|
|
13565
|
+
code: z.ZodIssueCode.custom,
|
|
13566
|
+
message: `titleSource must reference a valid field ID (got "${source}")`,
|
|
13567
|
+
path: Array.isArray(data.titleSource) ? ["titleSource", index] : ["titleSource"]
|
|
13568
|
+
});
|
|
13569
|
+
});
|
|
13570
|
+
}
|
|
13571
|
+
function validateVariantKeys(data, ctx) {
|
|
13572
|
+
const variants = data.variants ?? {};
|
|
13573
|
+
if (Object.keys(variants).length === 0) ctx.addIssue({
|
|
13574
|
+
code: z.ZodIssueCode.custom,
|
|
13575
|
+
message: "`variants` must contain at least one variant key",
|
|
13576
|
+
path: ["variants"]
|
|
13577
|
+
});
|
|
13578
|
+
if (data.defaultVariant && !Object.prototype.hasOwnProperty.call(variants, data.defaultVariant)) ctx.addIssue({
|
|
13579
|
+
code: z.ZodIssueCode.custom,
|
|
13580
|
+
message: "`defaultVariant` must be a key present in `variants`",
|
|
13581
|
+
path: ["defaultVariant"]
|
|
13582
|
+
});
|
|
13583
|
+
Object.keys(data.variantOptions ?? {}).filter((key) => !Object.prototype.hasOwnProperty.call(variants, key)).forEach((key) => ctx.addIssue({
|
|
13584
|
+
code: z.ZodIssueCode.custom,
|
|
13585
|
+
message: "`variantOptions` key must exist in `variants`",
|
|
13586
|
+
path: ["variantOptions", key]
|
|
13587
|
+
}));
|
|
13588
|
+
}
|
|
13589
|
+
|
|
13590
|
+
//#endregion
|
|
13591
|
+
//#region ../blocks/src/system/manifest/schema.ts
|
|
13592
|
+
const visibilityLevels = [
|
|
13593
|
+
"admin",
|
|
13594
|
+
"designer",
|
|
13595
|
+
"author"
|
|
13596
|
+
];
|
|
13597
|
+
const MEDIA_KINDS = [
|
|
13598
|
+
"image",
|
|
13599
|
+
"video",
|
|
13600
|
+
"audio",
|
|
13601
|
+
"document",
|
|
13602
|
+
"spreadsheet",
|
|
13603
|
+
"archive"
|
|
13604
|
+
];
|
|
13605
|
+
const DISPLAY_MEDIA_KINDS = [
|
|
13606
|
+
"image",
|
|
13607
|
+
"video",
|
|
13608
|
+
"audio"
|
|
13609
|
+
];
|
|
13610
|
+
const DEFAULT_MEDIA_FIELD_KINDS = ["image"];
|
|
13611
|
+
const PUBLIC_FILE_FIELD_KINDS = [
|
|
13612
|
+
"document",
|
|
13613
|
+
"spreadsheet",
|
|
13614
|
+
"archive",
|
|
13615
|
+
"audio"
|
|
13616
|
+
];
|
|
13617
|
+
function isDisplayMediaKind(kind) {
|
|
13618
|
+
return DISPLAY_MEDIA_KINDS.includes(kind);
|
|
13619
|
+
}
|
|
13620
|
+
const siteOptionSources = [
|
|
13621
|
+
"eventCategories",
|
|
13622
|
+
"eventSeries",
|
|
13623
|
+
"venues",
|
|
13624
|
+
"staffMembers",
|
|
13625
|
+
"appointmentServiceCategories"
|
|
13626
|
+
];
|
|
13627
|
+
const mediaDeliverySchema = z.object({
|
|
13628
|
+
sizes: z.string().optional(),
|
|
13629
|
+
widthCandidates: z.array(z.number().int().positive()).optional(),
|
|
13630
|
+
maxCssWidth: z.number().positive().optional(),
|
|
13631
|
+
maxCssHeight: z.number().positive().optional(),
|
|
13632
|
+
strategy: z.enum([
|
|
13633
|
+
"default",
|
|
13634
|
+
"columnsAuto",
|
|
13635
|
+
"imageGalleryVariant"
|
|
13636
|
+
]).optional(),
|
|
13637
|
+
strategyOptions: z.record(z.string(), z.unknown()).optional()
|
|
13638
|
+
});
|
|
13639
|
+
const selectOptionSchema = z.object({
|
|
13640
|
+
value: z.string(),
|
|
13641
|
+
label: z.string(),
|
|
13642
|
+
description: z.string().optional(),
|
|
13643
|
+
icon: z.string().optional()
|
|
13644
|
+
});
|
|
13645
|
+
const selectSourceSchema = z.discriminatedUnion("kind", [
|
|
13646
|
+
z.object({ kind: z.literal("static") }),
|
|
13647
|
+
z.object({
|
|
13648
|
+
kind: z.literal("site"),
|
|
13649
|
+
optionSource: z.enum(siteOptionSources)
|
|
13650
|
+
}),
|
|
13651
|
+
z.object({ kind: z.literal("sdk") })
|
|
13652
|
+
]);
|
|
13653
|
+
function assertNeverSelectSource(value) {
|
|
13654
|
+
throw new Error(`Unhandled select source: ${JSON.stringify(value)}`);
|
|
13655
|
+
}
|
|
13656
|
+
const visibleWhenPredicateSchema = z.object({
|
|
13657
|
+
field: z.string().min(1),
|
|
13658
|
+
equals: z.any().optional(),
|
|
13659
|
+
notEquals: z.any().optional(),
|
|
13660
|
+
oneOf: z.array(z.any()).optional(),
|
|
13661
|
+
notIn: z.array(z.any()).optional(),
|
|
13662
|
+
minItems: z.number().int().nonnegative().optional(),
|
|
13663
|
+
maxItems: z.number().int().nonnegative().optional()
|
|
13664
|
+
});
|
|
13665
|
+
const visibleWhenSchema = z.lazy(() => z.union([
|
|
13666
|
+
visibleWhenPredicateSchema,
|
|
13667
|
+
z.object({ allOf: z.array(visibleWhenSchema).min(1) }),
|
|
13668
|
+
z.object({ anyOf: z.array(visibleWhenSchema).min(1) })
|
|
13669
|
+
]));
|
|
13670
|
+
const uiSchema = z.object({
|
|
13671
|
+
widget: z.string().optional(),
|
|
13672
|
+
preset: z.enum(["disclosure"]).optional(),
|
|
13673
|
+
flattenInRepeater: z.boolean().optional(),
|
|
13674
|
+
hidden: z.boolean().optional(),
|
|
13675
|
+
hideLabel: z.boolean().optional(),
|
|
13676
|
+
hideDescription: z.boolean().optional(),
|
|
13677
|
+
showCharCount: z.union([z.number().int().positive(), z.object({ max: z.number().int().positive() })]).optional(),
|
|
13678
|
+
showSlugPreview: z.boolean().optional(),
|
|
13679
|
+
variant: z.enum([
|
|
13680
|
+
"full",
|
|
13681
|
+
"inline",
|
|
13682
|
+
"limited",
|
|
13683
|
+
"media",
|
|
13684
|
+
"notes"
|
|
13685
|
+
]).optional(),
|
|
13686
|
+
richTextVariant: z.enum([
|
|
13687
|
+
"full",
|
|
13688
|
+
"inline",
|
|
13689
|
+
"limited",
|
|
13690
|
+
"media",
|
|
13691
|
+
"notes"
|
|
13692
|
+
]).optional(),
|
|
13693
|
+
cropEditorMode: z.enum(["dialog", "inline"]).optional(),
|
|
13694
|
+
cropEditorAutoOpen: z.boolean().optional(),
|
|
13695
|
+
mediaPickerAutoOpen: z.boolean().optional(),
|
|
13696
|
+
showMediaPreview: z.boolean().optional(),
|
|
13697
|
+
showMediaOverlayActions: z.boolean().optional(),
|
|
13698
|
+
mediaPreviewMaxWidth: z.enum([
|
|
13699
|
+
"xs",
|
|
13700
|
+
"sm",
|
|
13701
|
+
"md",
|
|
13702
|
+
"none"
|
|
13703
|
+
]).optional(),
|
|
13704
|
+
preventMediaPreviewUpscale: z.boolean().optional(),
|
|
13705
|
+
mediaDelivery: mediaDeliverySchema.optional(),
|
|
13706
|
+
mediaDeliveryByVariant: z.record(z.string(), mediaDeliverySchema).optional(),
|
|
13707
|
+
inputType: z.enum([
|
|
13708
|
+
"text",
|
|
13709
|
+
"email",
|
|
13710
|
+
"tel",
|
|
13711
|
+
"number"
|
|
13712
|
+
]).optional(),
|
|
13713
|
+
min: z.number().optional(),
|
|
13714
|
+
max: z.number().optional(),
|
|
13715
|
+
step: z.number().optional(),
|
|
13716
|
+
showValue: z.boolean().optional(),
|
|
13717
|
+
valueUnit: z.enum([
|
|
13718
|
+
"percent",
|
|
13719
|
+
"pixels",
|
|
13720
|
+
"unitless"
|
|
13721
|
+
]).optional(),
|
|
13722
|
+
unsetValue: z.enum(["max", "field-default"]).optional(),
|
|
13723
|
+
allowDirectInput: z.boolean().optional(),
|
|
13724
|
+
pattern: z.string().optional(),
|
|
13725
|
+
placeholder: z.string().optional(),
|
|
13726
|
+
visibleWhen: visibleWhenSchema.optional(),
|
|
13727
|
+
modalConfig: z.object({
|
|
13728
|
+
buttonLabel: z.string().optional(),
|
|
13729
|
+
description: z.string().optional(),
|
|
13730
|
+
buttonVariant: z.enum(["default", "outline"]).optional(),
|
|
13731
|
+
showCustomizedIndicator: z.boolean().optional(),
|
|
13732
|
+
maxWidth: z.string().optional()
|
|
13733
|
+
}).optional(),
|
|
13734
|
+
allowedTypes: z.array(z.enum([
|
|
13735
|
+
"color",
|
|
13736
|
+
"gradient",
|
|
13737
|
+
"image"
|
|
13738
|
+
])).optional(),
|
|
13739
|
+
fullWidth: z.boolean().optional(),
|
|
13740
|
+
row: z.string().optional(),
|
|
13741
|
+
colSpan: z.number().int().min(1).max(4).optional(),
|
|
13742
|
+
layout: z.enum(["stack", "grid"]).optional(),
|
|
13743
|
+
columns: z.number().int().min(2).max(4).optional(),
|
|
13744
|
+
gridColsPreset: z.enum([
|
|
13745
|
+
"simple2",
|
|
13746
|
+
"simple3",
|
|
13747
|
+
"simple4",
|
|
13748
|
+
"buttonCtaRow"
|
|
13749
|
+
]).optional(),
|
|
13750
|
+
gridColumns: z.object({
|
|
13751
|
+
base: z.number().int().min(1).max(4).optional(),
|
|
13752
|
+
sm: z.number().int().min(1).max(4).optional(),
|
|
13753
|
+
md: z.number().int().min(1).max(4).optional(),
|
|
13754
|
+
lg: z.number().int().min(1).max(4).optional(),
|
|
13755
|
+
xl: z.number().int().min(1).max(4).optional()
|
|
13756
|
+
}).optional(),
|
|
13757
|
+
gridColsClassName: z.string().optional(),
|
|
13758
|
+
repeaterPreset: z.enum(["thumbnailGrid", "layoutGrid"]).optional(),
|
|
13759
|
+
thumbnailField: z.string().optional(),
|
|
13760
|
+
thumbnailAltField: z.string().optional(),
|
|
13761
|
+
thumbnailLabelField: z.string().optional(),
|
|
13762
|
+
thumbnailAspectRatio: z.string().optional(),
|
|
13763
|
+
thumbnailCardDensity: z.enum(["compact", "comfortable"]).optional(),
|
|
13764
|
+
layoutGridColumns: z.union([
|
|
13765
|
+
z.literal(1),
|
|
13766
|
+
z.literal(2),
|
|
13767
|
+
z.literal(3),
|
|
13768
|
+
z.literal(4),
|
|
13769
|
+
z.literal(5),
|
|
13770
|
+
z.literal(6)
|
|
13771
|
+
]).optional(),
|
|
13772
|
+
layoutGridColumnsFields: z.object({
|
|
13773
|
+
base: z.string().optional(),
|
|
13774
|
+
sm: z.string().optional(),
|
|
13775
|
+
md: z.string().optional(),
|
|
13776
|
+
lg: z.string().min(1),
|
|
13777
|
+
xl: z.string().optional()
|
|
13778
|
+
}).optional(),
|
|
13779
|
+
layoutGridSpanField: z.string().optional(),
|
|
13780
|
+
layoutGridStartNewRowField: z.string().optional(),
|
|
13781
|
+
layoutGridRowField: z.string().optional(),
|
|
13782
|
+
layoutGridColumnField: z.string().optional(),
|
|
13783
|
+
layoutGridMaxRows: z.number().int().positive().optional(),
|
|
13784
|
+
iconPickerPreset: z.enum(ICON_PICKER_PRESETS).optional(),
|
|
13785
|
+
contentTypeField: z.string().optional(),
|
|
13786
|
+
fixedContentType: z.string().optional(),
|
|
13787
|
+
valueFrom: z.enum([
|
|
13788
|
+
"id",
|
|
13789
|
+
"identifier",
|
|
13790
|
+
"slug"
|
|
13791
|
+
]).optional(),
|
|
13792
|
+
siteOptionSource: z.enum(siteOptionSources).optional(),
|
|
13793
|
+
searchThreshold: z.number().int().positive().optional(),
|
|
13794
|
+
renderInHeader: z.boolean().optional(),
|
|
13795
|
+
intent: z.enum([
|
|
13796
|
+
"content",
|
|
13797
|
+
"display",
|
|
13798
|
+
"behavior",
|
|
13799
|
+
"style"
|
|
13800
|
+
]).optional(),
|
|
13801
|
+
underLooks: z.literal("owner").optional(),
|
|
13802
|
+
valueContract: z.enum(["boxBackground", "location"]).optional()
|
|
13803
|
+
}).partial().superRefine((ui, ctx) => {
|
|
13804
|
+
if (ui.layoutGridColumns !== undefined && ui.layoutGridColumnsFields !== undefined) {
|
|
13805
|
+
ctx.addIssue({
|
|
13806
|
+
code: "custom",
|
|
13807
|
+
path: ["layoutGridColumns"],
|
|
13808
|
+
message: "Layout-grid requires exactly one of ui.layoutGridColumns or ui.layoutGridColumnsFields.lg; do not set both"
|
|
13809
|
+
});
|
|
13810
|
+
}
|
|
13811
|
+
}).transform((input) => {
|
|
13812
|
+
const { layoutGridColumns, layoutGridColumnsFields, ...ui } = input;
|
|
13813
|
+
if (layoutGridColumns !== undefined) return {
|
|
13814
|
+
...ui,
|
|
13815
|
+
layoutGridColumns
|
|
13816
|
+
};
|
|
13817
|
+
if (layoutGridColumnsFields !== undefined) return {
|
|
13818
|
+
...ui,
|
|
13819
|
+
layoutGridColumnsFields
|
|
13820
|
+
};
|
|
13821
|
+
return ui;
|
|
13822
|
+
});
|
|
13823
|
+
const baseFieldSchema = z.object({
|
|
13824
|
+
id: z.string().min(1, "Field id is required"),
|
|
13825
|
+
label: z.string().min(1, "Field label is required"),
|
|
13826
|
+
description: z.string().optional(),
|
|
13827
|
+
visibleRoles: z.array(z.enum(visibilityLevels)).optional(),
|
|
13828
|
+
defaultValue: z.any().optional(),
|
|
13829
|
+
required: z.boolean().default(false),
|
|
13830
|
+
ui: uiSchema.optional()
|
|
13831
|
+
});
|
|
13832
|
+
const textFieldSchema = baseFieldSchema.extend({
|
|
13833
|
+
type: z.literal("text"),
|
|
13834
|
+
multiline: z.boolean().default(false),
|
|
13835
|
+
maxLength: z.number().int().positive().optional()
|
|
13836
|
+
});
|
|
13837
|
+
const richTextFieldSchema = baseFieldSchema.extend({ type: z.literal("richText") });
|
|
13838
|
+
const inlineTextFieldSchema = baseFieldSchema.extend({
|
|
13839
|
+
type: z.literal("inlineText"),
|
|
13840
|
+
maxLength: z.number().int().positive().optional(),
|
|
13841
|
+
plainTextField: z.string().min(1).optional()
|
|
13842
|
+
});
|
|
13843
|
+
function hasMediaDefaultIdentifier(value) {
|
|
13844
|
+
return value !== null && typeof value === "object" && !Array.isArray(value) && typeof value.identifier === "string" && value.identifier.trim().length > 0;
|
|
13845
|
+
}
|
|
13846
|
+
function validateMediaFieldDefault(field, ctx) {
|
|
13847
|
+
if (field.defaultValue !== undefined && field.defaultValue !== null && !hasMediaDefaultIdentifier(field.defaultValue)) {
|
|
13848
|
+
ctx.addIssue({
|
|
13849
|
+
code: z.ZodIssueCode.custom,
|
|
13850
|
+
path: ["defaultValue"],
|
|
13851
|
+
message: `${field.type} defaultValue must be null or an object with a non-empty identifier`
|
|
13852
|
+
});
|
|
13853
|
+
}
|
|
13854
|
+
}
|
|
13855
|
+
const mediaFieldSchema = baseFieldSchema.extend({
|
|
13856
|
+
type: z.literal("media"),
|
|
13857
|
+
mediaKinds: z.array(z.enum(MEDIA_KINDS)).default([...DEFAULT_MEDIA_FIELD_KINDS]).transform((kinds) => {
|
|
13858
|
+
const displayKinds = kinds.filter(isDisplayMediaKind);
|
|
13859
|
+
return displayKinds.length > 0 ? displayKinds : [...DEFAULT_MEDIA_FIELD_KINDS];
|
|
13860
|
+
}),
|
|
13861
|
+
aspectRatio: z.string().optional()
|
|
13862
|
+
}).superRefine(validateMediaFieldDefault);
|
|
13863
|
+
const fileFieldSchema = baseFieldSchema.extend({
|
|
13864
|
+
type: z.literal("file"),
|
|
13865
|
+
fileKinds: z.array(z.enum(PUBLIC_FILE_FIELD_KINDS)).default([...PUBLIC_FILE_FIELD_KINDS])
|
|
13866
|
+
}).superRefine(validateMediaFieldDefault);
|
|
13867
|
+
const booleanFieldSchema = baseFieldSchema.extend({ type: z.literal("boolean") });
|
|
13868
|
+
const dateFieldSchema = baseFieldSchema.extend({ type: z.literal("date") });
|
|
13869
|
+
const timeFieldSchema = baseFieldSchema.extend({ type: z.literal("time") });
|
|
13870
|
+
const dateTimeFieldSchema = baseFieldSchema.extend({ type: z.literal("datetime") });
|
|
13871
|
+
const slugFieldSchema = baseFieldSchema.extend({
|
|
13872
|
+
type: z.literal("slug"),
|
|
13873
|
+
sourceFieldId: z.string().min(1).optional(),
|
|
13874
|
+
statusFieldId: z.string().min(1).optional(),
|
|
13875
|
+
maxLength: z.number().int().positive().optional()
|
|
13876
|
+
});
|
|
13877
|
+
const urlFieldSchema = baseFieldSchema.extend({
|
|
13878
|
+
type: z.literal("url"),
|
|
13879
|
+
allowRelative: z.boolean().default(false)
|
|
13880
|
+
});
|
|
13881
|
+
const linkFieldSchema = baseFieldSchema.extend({ type: z.literal("link") });
|
|
13882
|
+
const selectFieldSchema = baseFieldSchema.extend({
|
|
13883
|
+
type: z.literal("select"),
|
|
13884
|
+
options: z.array(selectOptionSchema),
|
|
13885
|
+
multiple: z.boolean().default(false),
|
|
13886
|
+
source: selectSourceSchema.optional()
|
|
13887
|
+
}).superRefine((field, ctx) => {
|
|
13888
|
+
const widget = field.ui?.widget;
|
|
13889
|
+
const siteOptionSource = field.ui?.siteOptionSource;
|
|
13890
|
+
if (!field.source) {
|
|
13891
|
+
if (siteOptionSource || widget === "sdkSelect") {
|
|
13892
|
+
ctx.addIssue({
|
|
13893
|
+
code: z.ZodIssueCode.custom,
|
|
13894
|
+
message: "Runtime-backed select fields must declare source.kind explicitly",
|
|
13895
|
+
path: ["source"]
|
|
13896
|
+
});
|
|
13897
|
+
return;
|
|
13898
|
+
}
|
|
13899
|
+
if (field.options.length === 0) {
|
|
13900
|
+
ctx.addIssue({
|
|
13901
|
+
code: z.ZodIssueCode.custom,
|
|
13902
|
+
message: "Select fields require at least one option unless a runtime source is configured",
|
|
13903
|
+
path: ["options"]
|
|
13904
|
+
});
|
|
13905
|
+
}
|
|
13906
|
+
return;
|
|
13907
|
+
}
|
|
13908
|
+
switch (field.source.kind) {
|
|
13909
|
+
case "static":
|
|
13910
|
+
if (siteOptionSource || widget === "sdkSelect") {
|
|
13911
|
+
ctx.addIssue({
|
|
13912
|
+
code: z.ZodIssueCode.custom,
|
|
13913
|
+
message: "Static select fields cannot declare runtime-backed UI metadata",
|
|
13914
|
+
path: ["ui"]
|
|
13915
|
+
});
|
|
13916
|
+
}
|
|
13917
|
+
if (field.options.length === 0) {
|
|
13918
|
+
ctx.addIssue({
|
|
13919
|
+
code: z.ZodIssueCode.custom,
|
|
13920
|
+
message: "Static select fields require at least one option",
|
|
13921
|
+
path: ["options"]
|
|
13922
|
+
});
|
|
13923
|
+
}
|
|
13924
|
+
return;
|
|
13925
|
+
case "site":
|
|
13926
|
+
if (field.options.length > 0) {
|
|
13927
|
+
ctx.addIssue({
|
|
13928
|
+
code: z.ZodIssueCode.custom,
|
|
13929
|
+
message: "Site-backed select fields must not define static options",
|
|
13930
|
+
path: ["options"]
|
|
13931
|
+
});
|
|
13932
|
+
}
|
|
13933
|
+
if (widget !== "siteOptionPicker") {
|
|
13934
|
+
ctx.addIssue({
|
|
13935
|
+
code: z.ZodIssueCode.custom,
|
|
13936
|
+
message: "Site-backed select fields must use the siteOptionPicker widget",
|
|
13937
|
+
path: ["ui", "widget"]
|
|
13938
|
+
});
|
|
13939
|
+
}
|
|
13940
|
+
if (siteOptionSource !== field.source.optionSource) {
|
|
13941
|
+
ctx.addIssue({
|
|
13942
|
+
code: z.ZodIssueCode.custom,
|
|
13943
|
+
message: "Site-backed select fields must keep ui.siteOptionSource in sync with source.optionSource",
|
|
13944
|
+
path: ["ui", "siteOptionSource"]
|
|
13945
|
+
});
|
|
13946
|
+
}
|
|
13947
|
+
return;
|
|
13948
|
+
case "sdk":
|
|
13949
|
+
if (widget !== "sdkSelect") {
|
|
13950
|
+
ctx.addIssue({
|
|
13951
|
+
code: z.ZodIssueCode.custom,
|
|
13952
|
+
message: "SDK-backed select fields must use the sdkSelect widget",
|
|
13953
|
+
path: ["ui", "widget"]
|
|
13954
|
+
});
|
|
13955
|
+
}
|
|
13956
|
+
if (siteOptionSource) {
|
|
13957
|
+
ctx.addIssue({
|
|
13958
|
+
code: z.ZodIssueCode.custom,
|
|
13959
|
+
message: "SDK-backed select fields cannot declare ui.siteOptionSource",
|
|
13960
|
+
path: ["ui", "siteOptionSource"]
|
|
13961
|
+
});
|
|
13962
|
+
}
|
|
13963
|
+
return;
|
|
13964
|
+
default: return assertNeverSelectSource(field.source);
|
|
13965
|
+
}
|
|
13966
|
+
});
|
|
13967
|
+
const referenceFieldSchema = baseFieldSchema.extend({
|
|
13968
|
+
type: z.literal("reference"),
|
|
13969
|
+
referenceKind: z.string().min(1),
|
|
13970
|
+
allowManualEntry: z.boolean().default(false)
|
|
13971
|
+
});
|
|
13972
|
+
/**
|
|
13973
|
+
* Item type schema for polymorphic repeaters.
|
|
13974
|
+
* Each type has a label, optional icon, and its own field schema.
|
|
13975
|
+
*/
|
|
13976
|
+
const itemTypeSchema = z.object({
|
|
13977
|
+
label: z.string().min(1),
|
|
13978
|
+
icon: z.string().optional(),
|
|
13979
|
+
fields: z.lazy(() => getFieldSchemaInternal().array().min(1, "Item type requires at least one field"))
|
|
13980
|
+
});
|
|
13981
|
+
/**
|
|
13982
|
+
* Repeater fields allow nesting of other fields within them.
|
|
13983
|
+
* This enables complex layouts and structures by repeating a set of fields.
|
|
13984
|
+
*
|
|
13985
|
+
* Supports two modes:
|
|
13986
|
+
* 1. Monomorphic (default): All items have the same schema
|
|
13987
|
+
* 2. Polymorphic: Items can be different types (e.g., card, quote, stat)
|
|
13988
|
+
*
|
|
13989
|
+
* Uses z.lazy() to allow mutual recursion.
|
|
13990
|
+
*/
|
|
13991
|
+
const repeaterFieldSchema = baseFieldSchema.extend({
|
|
13992
|
+
type: z.literal("repeater"),
|
|
13993
|
+
itemLabel: z.string().default("Item"),
|
|
13994
|
+
itemLabelSource: z.string().optional(),
|
|
13995
|
+
showSummary: z.boolean().optional(),
|
|
13996
|
+
summaryFields: z.string().array().optional(),
|
|
13997
|
+
summaryMaxChars: z.number().int().positive().optional(),
|
|
13998
|
+
minItems: z.number().int().min(0).default(0),
|
|
13999
|
+
maxItems: z.number().int().positive().optional(),
|
|
14000
|
+
schema: z.object({ fields: z.lazy(() => getFieldSchemaInternal().array().min(1, "Repeater requires at least one field")) }).optional(),
|
|
14001
|
+
polymorphic: z.boolean().optional(),
|
|
14002
|
+
itemTypes: z.record(z.string(), itemTypeSchema).optional(),
|
|
14003
|
+
allowConversion: z.boolean().default(true)
|
|
14004
|
+
}).refine((data) => {
|
|
14005
|
+
const hasSchema = data.schema !== undefined;
|
|
14006
|
+
const hasPolymorphic = data.polymorphic === true && data.itemTypes !== undefined;
|
|
14007
|
+
return hasSchema !== hasPolymorphic;
|
|
14008
|
+
}, { message: "Repeater must have either 'schema' (monomorphic) or 'polymorphic: true' with 'itemTypes' (polymorphic)" }).superRefine(validateLayoutGridConfiguration).refine((data) => {
|
|
14009
|
+
const thumbnailAspectRatio = data.ui?.thumbnailAspectRatio;
|
|
14010
|
+
if (!thumbnailAspectRatio) {
|
|
14011
|
+
return true;
|
|
14012
|
+
}
|
|
14013
|
+
return parseAspectRatioValue(thumbnailAspectRatio) != null;
|
|
14014
|
+
}, {
|
|
14015
|
+
message: "ui.thumbnailAspectRatio must be a valid ratio (e.g. '16/9', '4:3', or '1.5')",
|
|
14016
|
+
path: ["ui", "thumbnailAspectRatio"]
|
|
14017
|
+
}).refine((data) => {
|
|
14018
|
+
const labelField = data.ui?.thumbnailLabelField;
|
|
14019
|
+
if (!labelField) {
|
|
14020
|
+
return true;
|
|
14021
|
+
}
|
|
14022
|
+
const schemaFields = data.schema?.fields;
|
|
14023
|
+
if (!schemaFields) {
|
|
14024
|
+
return true;
|
|
14025
|
+
}
|
|
14026
|
+
const field = schemaFields.find((candidate) => candidate.id === labelField);
|
|
14027
|
+
return field?.type === "text";
|
|
14028
|
+
}, {
|
|
14029
|
+
message: "ui.thumbnailLabelField must reference a text field in the repeater schema",
|
|
14030
|
+
path: ["ui", "thumbnailLabelField"]
|
|
14031
|
+
});
|
|
14032
|
+
const groupFieldSchema = baseFieldSchema.extend({
|
|
14033
|
+
type: z.literal("group"),
|
|
14034
|
+
schema: z.object({ fields: z.lazy(() => getFieldSchemaInternal().array().min(1, "Group requires at least one field")) })
|
|
14035
|
+
});
|
|
14036
|
+
const modalFieldSchema = baseFieldSchema.extend({
|
|
14037
|
+
type: z.literal("modal"),
|
|
14038
|
+
schema: z.object({ fields: z.lazy(() => getFieldSchemaInternal().array().min(1, "Modal requires at least one field")) })
|
|
14039
|
+
});
|
|
14040
|
+
const numberFieldSchema = baseFieldSchema.extend({
|
|
14041
|
+
type: z.literal("number"),
|
|
14042
|
+
min: z.number().optional(),
|
|
14043
|
+
max: z.number().optional(),
|
|
14044
|
+
step: z.number().optional()
|
|
14045
|
+
});
|
|
14046
|
+
const tabDefinitionSchema = z.object({
|
|
14047
|
+
id: z.string().min(1),
|
|
14048
|
+
label: z.string().min(1),
|
|
14049
|
+
icon: z.string().optional(),
|
|
14050
|
+
description: z.string().optional(),
|
|
14051
|
+
fields: z.lazy(() => getFieldSchemaInternal().array()),
|
|
14052
|
+
sdkSectionOption: z.enum([
|
|
14053
|
+
"backgroundColor",
|
|
14054
|
+
"backgroundGradient",
|
|
14055
|
+
"backgroundImage"
|
|
14056
|
+
]).optional()
|
|
14057
|
+
});
|
|
14058
|
+
const tabGroupFieldSchema = baseFieldSchema.extend({
|
|
14059
|
+
type: z.literal("tabGroup"),
|
|
14060
|
+
tabs: z.array(tabDefinitionSchema).min(1, "TabGroup requires at least one tab"),
|
|
14061
|
+
activeTabField: z.string().optional()
|
|
14062
|
+
});
|
|
14063
|
+
const presetOptionSchema = z.object({
|
|
14064
|
+
value: z.string(),
|
|
14065
|
+
label: z.string()
|
|
14066
|
+
});
|
|
14067
|
+
const presetOrCustomFieldSchema = baseFieldSchema.extend({
|
|
14068
|
+
type: z.literal("presetOrCustom"),
|
|
14069
|
+
presets: z.array(presetOptionSchema).min(1, "PresetOrCustom requires at least one preset"),
|
|
14070
|
+
customInput: z.object({
|
|
14071
|
+
placeholder: z.string().optional(),
|
|
14072
|
+
pattern: z.string().optional(),
|
|
14073
|
+
helpText: z.string().optional()
|
|
14074
|
+
}).optional()
|
|
14075
|
+
});
|
|
14076
|
+
const contentTypeSelectFieldSchema = baseFieldSchema.extend({
|
|
14077
|
+
type: z.literal("contentTypeSelect"),
|
|
14078
|
+
filter: z.enum([
|
|
14079
|
+
"all",
|
|
14080
|
+
"routable",
|
|
14081
|
+
"nonRoutable"
|
|
14082
|
+
]).default("all")
|
|
14083
|
+
});
|
|
14084
|
+
const entryPickerFieldSchema = baseFieldSchema.extend({
|
|
14085
|
+
type: z.literal("entryPicker"),
|
|
14086
|
+
fixedContentType: z.string().optional(),
|
|
14087
|
+
contentTypeField: z.string().optional(),
|
|
14088
|
+
valueFrom: z.enum([
|
|
14089
|
+
"id",
|
|
14090
|
+
"identifier",
|
|
14091
|
+
"slug"
|
|
14092
|
+
]).optional()
|
|
14093
|
+
});
|
|
14094
|
+
let _fieldSchemaInternal = null;
|
|
14095
|
+
function getFieldSchemaInternal() {
|
|
14096
|
+
if (_fieldSchemaInternal) {
|
|
14097
|
+
return _fieldSchemaInternal;
|
|
14098
|
+
}
|
|
14099
|
+
_fieldSchemaInternal = z.discriminatedUnion("type", [
|
|
14100
|
+
textFieldSchema,
|
|
14101
|
+
richTextFieldSchema,
|
|
14102
|
+
inlineTextFieldSchema,
|
|
14103
|
+
mediaFieldSchema,
|
|
14104
|
+
fileFieldSchema,
|
|
14105
|
+
booleanFieldSchema,
|
|
14106
|
+
numberFieldSchema,
|
|
14107
|
+
dateFieldSchema,
|
|
14108
|
+
timeFieldSchema,
|
|
14109
|
+
dateTimeFieldSchema,
|
|
14110
|
+
slugFieldSchema,
|
|
14111
|
+
urlFieldSchema,
|
|
14112
|
+
linkFieldSchema,
|
|
14113
|
+
selectFieldSchema,
|
|
14114
|
+
referenceFieldSchema,
|
|
14115
|
+
repeaterFieldSchema,
|
|
14116
|
+
groupFieldSchema,
|
|
14117
|
+
modalFieldSchema,
|
|
14118
|
+
tabGroupFieldSchema,
|
|
14119
|
+
presetOrCustomFieldSchema,
|
|
14120
|
+
contentTypeSelectFieldSchema,
|
|
14121
|
+
entryPickerFieldSchema
|
|
14122
|
+
]);
|
|
14123
|
+
return _fieldSchemaInternal;
|
|
14124
|
+
}
|
|
14125
|
+
function getFieldSchema() {
|
|
14126
|
+
return getFieldSchemaInternal();
|
|
14127
|
+
}
|
|
14128
|
+
const fieldSchema = new Proxy({}, {
|
|
14129
|
+
get(_, prop) {
|
|
14130
|
+
const schema = getFieldSchemaInternal();
|
|
14131
|
+
const value = schema[prop];
|
|
14132
|
+
return typeof value === "function" ? value.bind(schema) : value;
|
|
14133
|
+
},
|
|
14134
|
+
has(_, prop) {
|
|
14135
|
+
return prop in getFieldSchemaInternal();
|
|
14136
|
+
}
|
|
14137
|
+
});
|
|
14138
|
+
function parseFieldDefinition(input) {
|
|
14139
|
+
return fieldSchema.parse(input);
|
|
14140
|
+
}
|
|
14141
|
+
function parseFieldDefinitions(input) {
|
|
14142
|
+
return fieldSchema.array().parse(input);
|
|
14143
|
+
}
|
|
14144
|
+
/**
|
|
14145
|
+
* Normalize legacy select fields into the closed source union before any consumer
|
|
14146
|
+
* branches on source semantics. Runtime-backed definitions must be explicit;
|
|
14147
|
+
* legacy static definitions normalize to `kind: "static"`.
|
|
14148
|
+
*/
|
|
14149
|
+
function normalizeSelectSource(field) {
|
|
14150
|
+
return field.source ?? { kind: "static" };
|
|
14151
|
+
}
|
|
14152
|
+
function expectParsedSelectField(field) {
|
|
14153
|
+
if (field.type !== "select") {
|
|
14154
|
+
throw new Error(`Expected select field, received ${field.type}`);
|
|
14155
|
+
}
|
|
14156
|
+
return field;
|
|
14157
|
+
}
|
|
14158
|
+
function createStaticSelectField(input) {
|
|
14159
|
+
return expectParsedSelectField(parseFieldDefinition({
|
|
14160
|
+
...input,
|
|
14161
|
+
type: "select",
|
|
14162
|
+
options: input.options,
|
|
14163
|
+
multiple: input.multiple ?? false,
|
|
14164
|
+
source: { kind: "static" }
|
|
14165
|
+
}));
|
|
14166
|
+
}
|
|
14167
|
+
function createSiteOptionSelectField(input) {
|
|
14168
|
+
return expectParsedSelectField(parseFieldDefinition({
|
|
14169
|
+
...input,
|
|
14170
|
+
type: "select",
|
|
14171
|
+
options: [],
|
|
14172
|
+
multiple: input.multiple ?? false,
|
|
14173
|
+
source: {
|
|
14174
|
+
kind: "site",
|
|
14175
|
+
optionSource: input.optionSource
|
|
14176
|
+
},
|
|
14177
|
+
ui: {
|
|
14178
|
+
...input.ui ?? {},
|
|
14179
|
+
widget: "siteOptionPicker",
|
|
14180
|
+
siteOptionSource: input.optionSource
|
|
14181
|
+
}
|
|
14182
|
+
}));
|
|
14183
|
+
}
|
|
14184
|
+
function createSdkSelectField(input) {
|
|
14185
|
+
return expectParsedSelectField(parseFieldDefinition({
|
|
14186
|
+
...input,
|
|
14187
|
+
type: "select",
|
|
14188
|
+
options: input.options,
|
|
14189
|
+
multiple: input.multiple ?? false,
|
|
14190
|
+
source: { kind: "sdk" },
|
|
14191
|
+
ui: {
|
|
14192
|
+
...input.ui ?? {},
|
|
14193
|
+
widget: "sdkSelect"
|
|
14194
|
+
}
|
|
14195
|
+
}));
|
|
14196
|
+
}
|
|
14197
|
+
const slotSchema = z.object({
|
|
14198
|
+
id: z.string().min(1),
|
|
14199
|
+
label: z.string().min(1),
|
|
14200
|
+
allowedKinds: z.array(z.string()).default([]),
|
|
14201
|
+
min: z.number().int().min(0).default(0),
|
|
14202
|
+
max: z.number().int().positive().optional(),
|
|
14203
|
+
help: z.string().optional(),
|
|
14204
|
+
visibleRoles: z.array(z.enum(visibilityLevels)).optional()
|
|
14205
|
+
});
|
|
14206
|
+
const typographyTokens = [
|
|
14207
|
+
"display",
|
|
14208
|
+
"heading",
|
|
14209
|
+
"subheading",
|
|
14210
|
+
"body",
|
|
14211
|
+
"caption"
|
|
14212
|
+
];
|
|
14213
|
+
const colorTokens = [
|
|
14214
|
+
"background",
|
|
14215
|
+
"surface",
|
|
14216
|
+
"foreground",
|
|
14217
|
+
"accent",
|
|
14218
|
+
"muted"
|
|
14219
|
+
];
|
|
14220
|
+
const spacingTokens = [
|
|
14221
|
+
"none",
|
|
14222
|
+
"xs",
|
|
14223
|
+
"sm",
|
|
14224
|
+
"md",
|
|
14225
|
+
"lg",
|
|
14226
|
+
"xl"
|
|
14227
|
+
];
|
|
14228
|
+
const radiusTokens = [
|
|
14229
|
+
"none",
|
|
14230
|
+
"sm",
|
|
14231
|
+
"md",
|
|
14232
|
+
"lg",
|
|
14233
|
+
"full"
|
|
14234
|
+
];
|
|
14235
|
+
const styleTokenSchema = z.object({
|
|
14236
|
+
background: z.enum(colorTokens).optional(),
|
|
14237
|
+
foreground: z.enum(colorTokens).optional(),
|
|
14238
|
+
border: z.enum(colorTokens).optional(),
|
|
14239
|
+
typography: z.enum(typographyTokens).optional(),
|
|
14240
|
+
spacing: z.enum(spacingTokens).optional(),
|
|
14241
|
+
radius: z.enum(radiusTokens).optional()
|
|
14242
|
+
});
|
|
14243
|
+
const behaviourSchema = z.object({
|
|
14244
|
+
supportsThemeSwitching: z.boolean().default(true),
|
|
14245
|
+
inlineEditing: z.boolean().default(false),
|
|
14246
|
+
animation: z.boolean().default(false),
|
|
14247
|
+
paletteHidden: z.boolean().default(false),
|
|
14248
|
+
managesOwnWrapper: z.boolean().optional()
|
|
14249
|
+
});
|
|
14250
|
+
const builtInBlockKindsSchema = z.array(z.enum(SYSTEM_BLOCK_KINDS)).readonly();
|
|
14251
|
+
const blockRequiredFeatureSchema = z.enum([
|
|
14252
|
+
"bookings.appointments",
|
|
14253
|
+
"bookings.events",
|
|
14254
|
+
"shop",
|
|
14255
|
+
"newsletter",
|
|
14256
|
+
"analytics"
|
|
14257
|
+
]);
|
|
14258
|
+
const blockCategoryEnum = z.enum([
|
|
14259
|
+
"text-and-images",
|
|
14260
|
+
"photos",
|
|
14261
|
+
"people",
|
|
14262
|
+
"events-and-bookings",
|
|
14263
|
+
"shop",
|
|
14264
|
+
"forms-and-sign-ups",
|
|
14265
|
+
"marketing",
|
|
14266
|
+
"content",
|
|
14267
|
+
"blog",
|
|
14268
|
+
"media",
|
|
14269
|
+
"layout",
|
|
14270
|
+
"interactive"
|
|
14271
|
+
]);
|
|
14272
|
+
/**
|
|
14273
|
+
* Explicit authoring classification of where a block's rendered content comes from.
|
|
14274
|
+
*
|
|
14275
|
+
* - `inline` — all content is authored inline in the block's own fields.
|
|
14276
|
+
* - `entries` — the block renders content-type entries; `contentType` names the backing type.
|
|
14277
|
+
* - `mixed` — the block has inline fields AND renders entries; `contentType` names the
|
|
14278
|
+
* backing type when a single one applies.
|
|
14279
|
+
*
|
|
14280
|
+
* This is EXPLICIT metadata declared on every block definition (not inferred from
|
|
14281
|
+
* dataLoaders/field types), so the catalog can classify entry-backed vs inline blocks
|
|
14282
|
+
* honestly. A registry-wide test asserts every registered block declares it.
|
|
14283
|
+
*/
|
|
14284
|
+
const blockContentSourceKinds = [
|
|
14285
|
+
"inline",
|
|
14286
|
+
"entries",
|
|
14287
|
+
"mixed"
|
|
14288
|
+
];
|
|
14289
|
+
const blockContentSourceSchema = z.object({
|
|
14290
|
+
kind: z.enum(blockContentSourceKinds),
|
|
14291
|
+
contentType: z.string().min(1).optional()
|
|
14292
|
+
}).superRefine((value, ctx) => {
|
|
14293
|
+
if (value.kind === "inline" && value.contentType) {
|
|
14294
|
+
ctx.addIssue({
|
|
14295
|
+
code: z.ZodIssueCode.custom,
|
|
14296
|
+
path: ["contentType"],
|
|
14297
|
+
message: "contentType is not allowed when contentSource.kind is \"inline\""
|
|
14298
|
+
});
|
|
14299
|
+
}
|
|
14300
|
+
});
|
|
14301
|
+
const manifestSchema = z.object({
|
|
14302
|
+
name: z.string().min(1),
|
|
14303
|
+
version: z.string().min(1),
|
|
14304
|
+
title: z.string().min(1),
|
|
14305
|
+
titleSource: z.union([z.string().min(1), z.array(z.string().min(1)).min(1)]).optional(),
|
|
14306
|
+
description: z.string().optional(),
|
|
14307
|
+
component: z.string().min(1),
|
|
14308
|
+
fields: fieldSchema.array().default([]),
|
|
14309
|
+
slots: slotSchema.array().default([]),
|
|
14310
|
+
styleTokens: styleTokenSchema.optional(),
|
|
14311
|
+
behaviours: behaviourSchema.optional(),
|
|
14312
|
+
layout: NodeTreeSchema.optional(),
|
|
14313
|
+
lookField: z.string().min(1).optional(),
|
|
14314
|
+
sourceLine: z.object({
|
|
14315
|
+
text: z.string(),
|
|
14316
|
+
target: z.enum([
|
|
14317
|
+
"testimonials",
|
|
14318
|
+
"team",
|
|
14319
|
+
"mailing-lists"
|
|
14320
|
+
]),
|
|
14321
|
+
when: z.object({
|
|
14322
|
+
field: z.string(),
|
|
14323
|
+
equals: z.string()
|
|
14324
|
+
}).optional()
|
|
14325
|
+
}).optional(),
|
|
14326
|
+
promotedFields: z.array(z.string().min(1)).optional(),
|
|
14327
|
+
variantMeaning: z.discriminatedUnion("kind", [z.object({
|
|
14328
|
+
kind: z.literal("arrangement"),
|
|
14329
|
+
lookOnlyKeys: z.array(z.string().min(1)).readonly().optional()
|
|
14330
|
+
}), z.object({
|
|
14331
|
+
kind: z.literal("mode"),
|
|
14332
|
+
modes: z.array(z.enum(arrangementModes)).min(1).readonly(),
|
|
14333
|
+
defaultMode: z.enum(arrangementModes),
|
|
14334
|
+
arrangementField: z.string().min(1),
|
|
14335
|
+
keys: z.array(z.string().min(1)).min(1).readonly(),
|
|
14336
|
+
defaultKey: z.string().min(1),
|
|
14337
|
+
lookOnlyKeys: z.array(z.string().min(1)).readonly().optional(),
|
|
14338
|
+
modeLabels: z.record(z.string(), z.string().min(1)).readonly().optional()
|
|
14339
|
+
})]),
|
|
14340
|
+
variants: z.record(z.string(), NodeTreeSchema).optional(),
|
|
14341
|
+
defaultVariant: z.string().optional(),
|
|
14342
|
+
variantOptions: z.record(z.string(), z.object({
|
|
14343
|
+
label: z.string().optional(),
|
|
14344
|
+
description: z.string().optional(),
|
|
14345
|
+
icon: z.string().optional()
|
|
14346
|
+
}).optional()).optional(),
|
|
14347
|
+
category: blockCategoryEnum.optional(),
|
|
14348
|
+
contentTypes: z.array(z.string()).optional(),
|
|
14349
|
+
requiredFeatures: z.array(blockRequiredFeatureSchema).optional(),
|
|
14350
|
+
contentSource: blockContentSourceSchema.optional(),
|
|
14351
|
+
tags: z.array(z.string()).optional(),
|
|
14352
|
+
icon: z.string().optional()
|
|
14353
|
+
});
|
|
14354
|
+
/** SDK Looks name a single top-level select owned by this definition. */
|
|
14355
|
+
function validateManifestLookField(data, ctx) {
|
|
14356
|
+
if (!data.lookField) return;
|
|
14357
|
+
const valid = data.fields.some((field) => field !== null && typeof field === "object" && "id" in field && field.id === data.lookField && "type" in field && field.type === "select" && (!("multiple" in field) || field.multiple !== true));
|
|
14358
|
+
if (!valid) ctx.addIssue({
|
|
14359
|
+
code: z.ZodIssueCode.custom,
|
|
14360
|
+
path: ["lookField"],
|
|
14361
|
+
message: "lookField must name an existing single select field"
|
|
14362
|
+
});
|
|
14363
|
+
}
|
|
14364
|
+
const blockSectionDeclarationShape = {
|
|
14365
|
+
promotedFields: z.array(z.string().min(1)).optional(),
|
|
14366
|
+
lookField: z.string().min(1).optional()
|
|
14367
|
+
};
|
|
14368
|
+
|
|
14369
|
+
//#endregion
|
|
14370
|
+
//#region ../api/src/sdkConfig/contracts.ts
|
|
14371
|
+
/**
|
|
14372
|
+
* Canonical SDK config Zod contracts.
|
|
14373
|
+
*
|
|
14374
|
+
* Goal: a single source-of-truth for request payload shapes used across dashboard + SDK.
|
|
14375
|
+
* - Endpoint request/response types live in `packages/api/src/types.ts`
|
|
14376
|
+
* - Schemas live here and are tied to those types via `satisfies z.core.$ZodType<...>`
|
|
14377
|
+
*
|
|
14378
|
+
* NOTE: These schemas represent the *wire/input* shape for API requests.
|
|
14379
|
+
* Service-layer code should parse/normalize again (defaults/refinements) into domain types.
|
|
14380
|
+
*/
|
|
14381
|
+
/**
|
|
14382
|
+
* UNSAFE: Retype an existing Zod schema to a different (compile-time) output type.
|
|
14383
|
+
*
|
|
14384
|
+
* Use only for cases where:
|
|
14385
|
+
* - the runtime schema is known to accept the desired *input/wire* shape, but
|
|
14386
|
+
* - Zod's inferred output type doesn't match the desired TS type due to transforms/defaults.
|
|
14387
|
+
*
|
|
14388
|
+
* If used incorrectly, this can lie to TypeScript and cause drift.
|
|
14389
|
+
*
|
|
14390
|
+
* @internal
|
|
14391
|
+
*/
|
|
14392
|
+
function unsafeRetypeZodSchema(schema) {
|
|
14393
|
+
return schema;
|
|
14394
|
+
}
|
|
14395
|
+
const navIdListSchema = z.preprocess((value) => {
|
|
14396
|
+
if (!Array.isArray(value)) return undefined;
|
|
14397
|
+
const filtered = value.filter((v) => typeof v === "string" && v.trim().length > 0);
|
|
14398
|
+
return filtered.length > 0 ? filtered : undefined;
|
|
14399
|
+
}, z.array(z.string()).max(200).optional());
|
|
14400
|
+
const dashboardNavVisibilityBaseSchema = z.object({
|
|
14401
|
+
mode: z.enum(["augment", "restrict"]).optional(),
|
|
14402
|
+
allowIds: navIdListSchema,
|
|
14403
|
+
denyIds: navIdListSchema
|
|
14404
|
+
}).strip();
|
|
14405
|
+
const dashboardNavVisibilityRolesSchema = z.object({
|
|
14406
|
+
administrator: dashboardNavVisibilityBaseSchema.optional(),
|
|
14407
|
+
editor: dashboardNavVisibilityBaseSchema.optional(),
|
|
14408
|
+
team_member: dashboardNavVisibilityBaseSchema.optional()
|
|
14409
|
+
}).strip().optional();
|
|
14410
|
+
const dashboardNavVisibilitySchema = dashboardNavVisibilityBaseSchema.extend({ roles: dashboardNavVisibilityRolesSchema }).strip();
|
|
14411
|
+
const dashboardNavigationConfigSchema = z.object({
|
|
14412
|
+
siteNav: dashboardNavVisibilitySchema.optional(),
|
|
14413
|
+
settingsNav: dashboardNavVisibilitySchema.optional()
|
|
14414
|
+
}).strip();
|
|
14415
|
+
const dashboardConfigSchema = z.object({
|
|
14416
|
+
previewEditingEnabled: z.boolean().optional(),
|
|
14417
|
+
navigation: dashboardNavigationConfigSchema.optional()
|
|
14418
|
+
}).strip();
|
|
14419
|
+
/**
|
|
14420
|
+
* Canonical field schema comes from @riverbankcms/blocks.
|
|
14421
|
+
*
|
|
14422
|
+
* It accepts FieldDefinitionInput (pre-defaults) and outputs FieldDefinition (post-defaults).
|
|
14423
|
+
* This `satisfies` assertion prevents drift in the *accepted wire shape* without forcing
|
|
14424
|
+
* the schema output type to match the input type.
|
|
14425
|
+
*/
|
|
14426
|
+
const fieldDefinitionInputSchema = unsafeRetypeZodSchema(fieldSchema);
|
|
14427
|
+
const nodeDefinitionSchema = unsafeRetypeZodSchema(NodeSchema);
|
|
14428
|
+
const sdkLoaderEndpointSchema = z.enum(SUPPORTED_LOADER_ENDPOINTS);
|
|
14429
|
+
const loaderFallbackScalarSchema = z.union([
|
|
14430
|
+
z.string(),
|
|
14431
|
+
z.number(),
|
|
14432
|
+
z.boolean(),
|
|
14433
|
+
z.null()
|
|
14434
|
+
]);
|
|
14435
|
+
const loaderParamBindingSchema = z.object({ $bind: z.object({
|
|
14436
|
+
from: z.string().min(1, "Binding path is required"),
|
|
14437
|
+
fallback: loaderFallbackScalarSchema.optional()
|
|
14438
|
+
}) });
|
|
14439
|
+
const loaderParamValueSchema = z.union([
|
|
14440
|
+
z.string(),
|
|
14441
|
+
z.number(),
|
|
14442
|
+
z.boolean(),
|
|
14443
|
+
loaderParamBindingSchema
|
|
14444
|
+
]);
|
|
14445
|
+
const sdkConfigLoaderSchema = z.object({
|
|
14446
|
+
endpoint: sdkLoaderEndpointSchema,
|
|
14447
|
+
params: z.record(z.string(), loaderParamValueSchema),
|
|
14448
|
+
requiredParams: z.array(z.string().min(1)).optional(),
|
|
14449
|
+
mode: z.enum(["server", "client"]).default("server")
|
|
14450
|
+
});
|
|
14451
|
+
const sdkDataLoadersSchema = z.record(z.string(), sdkConfigLoaderSchema).refine((loaders) => Object.keys(loaders).length <= 5, { message: "Maximum 5 data loaders per block" }).optional();
|
|
14452
|
+
const sdkCustomBlockIdSchema = z.string().min(8).regex(/^custom\.[a-z][a-z0-9-]*$/, { message: "Block ID must start with 'custom.' followed by lowercase letters, numbers, or hyphens" }).transform((value) => value);
|
|
14453
|
+
const sdkCustomBlockInputSchema = z.object({
|
|
14454
|
+
id: sdkCustomBlockIdSchema,
|
|
14455
|
+
title: z.string().min(1),
|
|
14456
|
+
titleSource: z.union([z.string().min(1), z.array(z.string().min(1)).min(1)]).optional(),
|
|
14457
|
+
description: z.string().optional(),
|
|
14458
|
+
category: blockCategoryEnum,
|
|
14459
|
+
icon: z.string().optional(),
|
|
14460
|
+
tags: z.array(z.string()).optional(),
|
|
14461
|
+
fields: fieldDefinitionInputSchema.array().min(1),
|
|
14462
|
+
dataLoaders: sdkDataLoadersSchema,
|
|
14463
|
+
layout: NodeSchema.optional(),
|
|
14464
|
+
...sdkBlockPresentationShape,
|
|
14465
|
+
...blockSectionDeclarationShape
|
|
14466
|
+
}).superRefine(validateManifestLookField).superRefine(validateSdkBlockDeclaration);
|
|
14467
|
+
const sdkBlockFieldExtensionInputSchema = z.object({
|
|
14468
|
+
fields: fieldDefinitionInputSchema.array().min(1),
|
|
14469
|
+
...blockSectionDeclarationShape
|
|
14470
|
+
}).superRefine(validateManifestLookField);
|
|
14471
|
+
const sdkBlockFieldExtensionsInputSchema = z.record(z.string().min(1), sdkBlockFieldExtensionInputSchema);
|
|
14472
|
+
const sdkThemePaletteSchema = z.record(z.string(), z.string());
|
|
14473
|
+
const sdkThemeConfigSchema = z.object({ palette: sdkThemePaletteSchema });
|
|
14474
|
+
const sdkSectionBackgroundSchema = z.object({
|
|
14475
|
+
id: z.string().min(1),
|
|
14476
|
+
label: z.string().min(1),
|
|
14477
|
+
token: z.string().min(1)
|
|
14478
|
+
});
|
|
14479
|
+
const sdkSectionOptionsConfigSchema = z.object({
|
|
14480
|
+
backgroundColor: z.boolean().optional(),
|
|
14481
|
+
backgroundImage: z.boolean().optional(),
|
|
14482
|
+
backgroundGradient: z.boolean().optional(),
|
|
14483
|
+
spacing: z.union([z.array(z.enum([
|
|
14484
|
+
"compact",
|
|
14485
|
+
"default",
|
|
14486
|
+
"spacious"
|
|
14487
|
+
])), z.boolean()]).optional(),
|
|
14488
|
+
textColor: z.boolean().optional()
|
|
14489
|
+
});
|
|
14490
|
+
const sdkContainerOptionsConfigSchema = z.object({
|
|
14491
|
+
maxWidth: z.union([z.array(z.enum([
|
|
14492
|
+
"narrow",
|
|
14493
|
+
"default",
|
|
14494
|
+
"wide",
|
|
14495
|
+
"full"
|
|
14496
|
+
])), z.boolean()]).optional(),
|
|
14497
|
+
alignment: z.union([z.array(z.enum([
|
|
14498
|
+
"left",
|
|
14499
|
+
"center",
|
|
14500
|
+
"right"
|
|
14501
|
+
])), z.boolean()]).optional()
|
|
14502
|
+
});
|
|
14503
|
+
const sdkSiteStyleConfigSchema = z.object({
|
|
14504
|
+
sectionBackgrounds: z.array(sdkSectionBackgroundSchema).optional(),
|
|
14505
|
+
sectionOptions: sdkSectionOptionsConfigSchema.optional(),
|
|
14506
|
+
containerOptions: sdkContainerOptionsConfigSchema.optional()
|
|
14507
|
+
});
|
|
14508
|
+
const sdkSiteLayoutConfigSchema = z.object({
|
|
14509
|
+
footerMode: footerModeSchema.optional(),
|
|
14510
|
+
footerBlocks: z.array(z.object({
|
|
14511
|
+
id: z.string().min(1).optional(),
|
|
14512
|
+
kind: z.string().min(1),
|
|
14513
|
+
content: z.record(z.string(), z.unknown()).optional()
|
|
14514
|
+
})).optional()
|
|
14515
|
+
});
|
|
14516
|
+
const sdkFieldSelectOptionSchema = z.object({
|
|
14517
|
+
value: z.string(),
|
|
14518
|
+
label: z.string()
|
|
14519
|
+
});
|
|
14520
|
+
const sdkBlockFieldConfigSchema = z.object({ options: z.array(sdkFieldSelectOptionSchema).optional() });
|
|
14521
|
+
const sdkBlockFieldOptionsMapSchema = z.record(z.string().regex(/^(block\.|custom\.)[a-z][a-z0-9-]*$/, { message: "Block ID must be 'block.*' or 'custom.*' format" }), z.record(z.string().min(1), sdkBlockFieldConfigSchema)).optional();
|
|
14522
|
+
const sdkContentTypeTemplateConfigSchema = z.object({ blocks: z.array(z.unknown()) }).passthrough();
|
|
14523
|
+
const sdkContentTypeOrderingConfigSchema = z.object({ mode: z.enum(["default", "manual"]).optional() }).optional();
|
|
14524
|
+
const sdkContentTypeConfigSchema = z.object({
|
|
14525
|
+
key: z.string().min(1),
|
|
14526
|
+
fields: z.array(z.unknown()),
|
|
14527
|
+
ordering: sdkContentTypeOrderingConfigSchema,
|
|
14528
|
+
template: sdkContentTypeTemplateConfigSchema.nullable().optional()
|
|
14529
|
+
}).passthrough();
|
|
14530
|
+
const sdkContentConfigSchema = z.object({
|
|
14531
|
+
contentTypes: z.array(sdkContentTypeConfigSchema).optional(),
|
|
14532
|
+
pages: z.array(z.unknown()).optional(),
|
|
14533
|
+
entries: z.array(z.unknown()).optional(),
|
|
14534
|
+
navigationMenus: z.array(z.unknown()).optional(),
|
|
14535
|
+
settings: z.unknown().optional()
|
|
14536
|
+
}).passthrough();
|
|
14537
|
+
const SDK_WORKFLOW_ID_MAX_LENGTH = 120;
|
|
14538
|
+
const SDK_WORKFLOW_TITLE_MAX_LENGTH = 120;
|
|
14539
|
+
const SDK_WORKFLOW_DESCRIPTION_MAX_LENGTH = 500;
|
|
14540
|
+
const SDK_WORKFLOW_CONTENT_TYPE_MAX_LENGTH = 120;
|
|
14541
|
+
const SDK_WORKFLOW_ENDPOINT_MAX_LENGTH = 500;
|
|
14542
|
+
const SDK_WORKFLOW_TRIGGER_MAX_COUNT = 5;
|
|
14543
|
+
const SDK_WORKFLOW_TRIGGER_FIELD_MAX_LENGTH = 120;
|
|
14544
|
+
const SDK_WORKFLOW_TRIGGER_CONDITION_MAX_COUNT = 10;
|
|
14545
|
+
const SDK_WORKFLOW_MAX_COUNT = 50;
|
|
14546
|
+
const SDK_WORKFLOW_CONFIRMATION_TITLE_MAX_LENGTH = 120;
|
|
14547
|
+
const SDK_WORKFLOW_CONFIRMATION_BODY_MAX_LENGTH = 500;
|
|
14548
|
+
const SDK_WORKFLOW_ID_PATTERN = /^[a-z][a-z0-9]*(?:[._-][a-z0-9]+)*$/;
|
|
14549
|
+
const SDK_WORKFLOW_ENDPOINT_PATTERN = /^\//;
|
|
14550
|
+
const sdkWorkflowIdSchema = z.string().min(1).max(SDK_WORKFLOW_ID_MAX_LENGTH).regex(SDK_WORKFLOW_ID_PATTERN, { message: "Workflow ID must start with a lowercase letter and use lowercase letters, numbers, dots, underscores, or hyphens" });
|
|
14551
|
+
const sdkWorkflowEndpointSchema = z.string().min(1).max(SDK_WORKFLOW_ENDPOINT_MAX_LENGTH).regex(SDK_WORKFLOW_ENDPOINT_PATTERN, { message: "Workflow endpoint must be a site-relative path starting with \"/\"" });
|
|
14552
|
+
const sdkWorkflowManualTriggerSchema = z.object({ type: z.literal("manual") }).strict();
|
|
14553
|
+
const sdkWorkflowTriggerFieldSchema = z.string().trim().min(1).max(SDK_WORKFLOW_TRIGGER_FIELD_MAX_LENGTH).refine((field) => !field.includes("."), { message: "Workflow trigger fields must be top-level content field IDs, not paths" });
|
|
14554
|
+
const sdkWorkflowFieldChangedConditionSchema = z.object({
|
|
14555
|
+
field: sdkWorkflowTriggerFieldSchema,
|
|
14556
|
+
changed: z.literal(true)
|
|
14557
|
+
}).strict();
|
|
14558
|
+
const sdkWorkflowFieldEmptyConditionSchema = z.object({
|
|
14559
|
+
field: sdkWorkflowTriggerFieldSchema,
|
|
14560
|
+
isEmpty: z.boolean()
|
|
14561
|
+
}).strict();
|
|
14562
|
+
const sdkWorkflowEntryDraftUpdatedTriggerSchema = z.object({
|
|
14563
|
+
type: z.literal("entry.draft.updated"),
|
|
14564
|
+
when: z.array(z.union([sdkWorkflowFieldChangedConditionSchema, sdkWorkflowFieldEmptyConditionSchema])).min(1).max(SDK_WORKFLOW_TRIGGER_CONDITION_MAX_COUNT)
|
|
14565
|
+
}).strict();
|
|
14566
|
+
const sdkWorkflowTriggerSchema = z.discriminatedUnion("type", [sdkWorkflowManualTriggerSchema, sdkWorkflowEntryDraftUpdatedTriggerSchema]);
|
|
14567
|
+
const sdkWorkflowConfirmationSchema = z.object({
|
|
14568
|
+
title: z.string().trim().min(1).max(SDK_WORKFLOW_CONFIRMATION_TITLE_MAX_LENGTH),
|
|
14569
|
+
body: z.string().trim().min(1).max(SDK_WORKFLOW_CONFIRMATION_BODY_MAX_LENGTH).optional()
|
|
14570
|
+
}).strict();
|
|
14571
|
+
const sdkWorkflowConfigSchema = z.object({
|
|
14572
|
+
id: sdkWorkflowIdSchema,
|
|
14573
|
+
title: z.string().trim().min(1).max(SDK_WORKFLOW_TITLE_MAX_LENGTH),
|
|
14574
|
+
description: z.string().trim().min(1).max(SDK_WORKFLOW_DESCRIPTION_MAX_LENGTH).optional(),
|
|
14575
|
+
contentType: z.string().trim().min(1).max(SDK_WORKFLOW_CONTENT_TYPE_MAX_LENGTH),
|
|
14576
|
+
endpoint: sdkWorkflowEndpointSchema,
|
|
14577
|
+
triggers: z.array(sdkWorkflowTriggerSchema).min(1).max(SDK_WORKFLOW_TRIGGER_MAX_COUNT),
|
|
14578
|
+
confirmation: sdkWorkflowConfirmationSchema.optional()
|
|
14579
|
+
}).strict();
|
|
14580
|
+
const sdkWorkflowsSchema = z.array(sdkWorkflowConfigSchema).max(SDK_WORKFLOW_MAX_COUNT, "Maximum 50 workflows per site").refine((workflows) => {
|
|
14581
|
+
const ids = workflows.map((workflow) => workflow.id);
|
|
14582
|
+
return ids.length === new Set(ids).size;
|
|
14583
|
+
}, { message: "Workflow IDs must be unique" }).optional();
|
|
14584
|
+
const riverbankSiteConfigInputSchema = z.object({
|
|
14585
|
+
siteId: z.string().uuid(),
|
|
14586
|
+
builtInBlocks: builtInBlockKindsSchema.optional(),
|
|
14587
|
+
previewUrl: z.string().url().optional(),
|
|
14588
|
+
liveUrl: z.string().url().optional(),
|
|
14589
|
+
theme: sdkThemeConfigSchema.optional(),
|
|
14590
|
+
styles: sdkSiteStyleConfigSchema.optional(),
|
|
14591
|
+
layout: sdkSiteLayoutConfigSchema.optional(),
|
|
14592
|
+
customBlocks: z.array(sdkCustomBlockInputSchema).max(100, "Maximum 100 custom blocks per site").refine((blocks) => {
|
|
14593
|
+
const ids = blocks.map((b) => b.id);
|
|
14594
|
+
return ids.length === new Set(ids).size;
|
|
14595
|
+
}, { message: "Block IDs must be unique" }).optional(),
|
|
14596
|
+
blockFieldOptions: sdkBlockFieldOptionsMapSchema,
|
|
14597
|
+
blockFieldExtensions: sdkBlockFieldExtensionsInputSchema.optional(),
|
|
14598
|
+
workflows: sdkWorkflowsSchema,
|
|
14599
|
+
content: sdkContentConfigSchema.optional(),
|
|
14600
|
+
contentDir: z.string().optional(),
|
|
14601
|
+
sync: z.object({ existingEntries: z.enum(["skip", "update"]).optional() }).optional(),
|
|
14602
|
+
deploy: z.object({
|
|
14603
|
+
verifyCommand: z.string().optional(),
|
|
14604
|
+
prebuildOutput: z.string().optional()
|
|
14605
|
+
}).optional(),
|
|
14606
|
+
themeSource: z.enum(["local", "cms"]).optional(),
|
|
14607
|
+
activeTheme: z.string().optional(),
|
|
14608
|
+
enablePortal: z.boolean().optional(),
|
|
14609
|
+
dashboard: dashboardConfigSchema.optional()
|
|
14610
|
+
}).strict();
|
|
14611
|
+
const pushSdkConfigBodySchema = z.object({
|
|
14612
|
+
config: riverbankSiteConfigInputSchema,
|
|
14613
|
+
forceContentTypes: z.boolean().optional(),
|
|
14614
|
+
mode: z.enum(["apply", "preview"]).optional(),
|
|
14615
|
+
expectedMailingListProviderScope: z.unknown().transform((value, ctx) => {
|
|
14616
|
+
if (value === undefined) return undefined;
|
|
14617
|
+
const parsed = parseMailingListProviderScope(value);
|
|
14618
|
+
if (parsed.ok) return parsed.value;
|
|
14619
|
+
ctx.addIssue({
|
|
14620
|
+
code: "custom",
|
|
14621
|
+
message: "Invalid mailing-list provider scope"
|
|
14622
|
+
});
|
|
14623
|
+
return z.NEVER;
|
|
14624
|
+
}).optional()
|
|
14625
|
+
}).strict();
|
|
14626
|
+
|
|
14627
|
+
//#endregion
|
|
14628
|
+
//#region ../api/src/sdkContracts.ts
|
|
14629
|
+
const ignoredDocumentSyncSectionWarnings = {
|
|
14630
|
+
pages: "content.pages is ignored by push-config; put pages in content/pages/*.json and use `riverbankcms push pages` (or `riverbankcms push`) to sync them.",
|
|
14631
|
+
entries: "content.entries is ignored by push-config; put entries in content/entries/<type>/*.json and use `riverbankcms push entries` (or `riverbankcms push`) to sync them.",
|
|
14632
|
+
navigationMenus: "content.navigationMenus is ignored by push-config; put navigation in content/navigation.json and use `riverbankcms push navigation` (or `riverbankcms push`) to sync it."
|
|
14633
|
+
};
|
|
14634
|
+
function formatIgnoredDocumentSyncSectionWarning(section) {
|
|
14635
|
+
return ignoredDocumentSyncSectionWarnings[section];
|
|
14636
|
+
}
|
|
14637
|
+
const SDK_PATCH_STALE_CONTENT_FIELD = "baseContentHash";
|
|
14638
|
+
const SDK_PATCH_STALE_CONTENT_CODE = "STALE_CONTENT";
|
|
14639
|
+
|
|
14640
|
+
//#endregion
|
|
14641
|
+
//#region ../management-client/src/blocks.ts
|
|
14642
|
+
/**
|
|
14643
|
+
* Block Operations for Management Client
|
|
14644
|
+
*/
|
|
14645
|
+
/**
|
|
14646
|
+
* Discriminated stale-conflict error for `blocks.patch`: the block's draft
|
|
14647
|
+
* changed since the caller's read. `currentHash` is the server's current
|
|
14648
|
+
* draft content hash — retry the patch with it as `baseContentHash`.
|
|
14649
|
+
* Extracted from the typed `SdkPatchPageBlockStaleContentFieldError` wire
|
|
14650
|
+
* contract, so callers never parse prose.
|
|
14651
|
+
*/
|
|
14652
|
+
var BlockPatchStaleContentError = class extends ManagementApiError {
|
|
14653
|
+
constructor(source, currentHash) {
|
|
14654
|
+
super(source.message, source.code, source.details, source.statusCode, source.request);
|
|
14655
|
+
this.kind = "stale-content";
|
|
14656
|
+
this.name = "BlockPatchStaleContentError";
|
|
14657
|
+
this.currentHash = currentHash;
|
|
14658
|
+
}
|
|
11943
14659
|
};
|
|
11944
14660
|
function isBlockPatchStaleContentError(error) {
|
|
11945
14661
|
return error instanceof BlockPatchStaleContentError;
|
|
@@ -14388,25 +17104,6 @@ function parsePublicProductCategorySelector(value) {
|
|
|
14388
17104
|
return undefined;
|
|
14389
17105
|
}
|
|
14390
17106
|
|
|
14391
|
-
//#endregion
|
|
14392
|
-
//#region ../api/src/request-error.ts
|
|
14393
|
-
var ApiRequestError = class extends Error {
|
|
14394
|
-
constructor(message, options) {
|
|
14395
|
-
super(message);
|
|
14396
|
-
this.name = "ApiRequestError";
|
|
14397
|
-
this.endpoint = options.endpoint;
|
|
14398
|
-
this.status = options.status;
|
|
14399
|
-
this.method = options.method;
|
|
14400
|
-
this.auth = options.auth;
|
|
14401
|
-
this.requestId = options.requestId;
|
|
14402
|
-
this.body = options.body;
|
|
14403
|
-
this.cause = options.cause;
|
|
14404
|
-
this.errorCode = options.errorCode;
|
|
14405
|
-
this.tag = options.tag;
|
|
14406
|
-
this.retryAfterMs = options.retryAfterMs;
|
|
14407
|
-
}
|
|
14408
|
-
};
|
|
14409
|
-
|
|
14410
17107
|
//#endregion
|
|
14411
17108
|
//#region ../api/src/token.ts
|
|
14412
17109
|
async function getAuthHeaders(supabase) {
|
|
@@ -14430,6 +17127,18 @@ async function getAuthHeaders(supabase) {
|
|
|
14430
17127
|
//#region ../surfaces/src/index.ts
|
|
14431
17128
|
const SURFACE_IDS = ["core", "macadamia"];
|
|
14432
17129
|
const DEFAULT_SURFACE_ID = "core";
|
|
17130
|
+
const SURFACE_ASSISTANT_MARKS = {
|
|
17131
|
+
core: {
|
|
17132
|
+
src: "/logo.svg",
|
|
17133
|
+
width: 22,
|
|
17134
|
+
height: 20
|
|
17135
|
+
},
|
|
17136
|
+
macadamia: {
|
|
17137
|
+
src: "/surfaces/macadamia/assistant-mark.svg",
|
|
17138
|
+
width: 26,
|
|
17139
|
+
height: 26
|
|
17140
|
+
}
|
|
17141
|
+
};
|
|
14433
17142
|
const SURFACE_DOCUMENT_DATA_ATTRIBUTE = "data-dashboard-surface";
|
|
14434
17143
|
function isSurfaceId(value) {
|
|
14435
17144
|
return typeof value === "string" && SURFACE_IDS.includes(value);
|
|
@@ -14895,7 +17604,6 @@ function getRevalidateTag() {
|
|
|
14895
17604
|
}
|
|
14896
17605
|
return revalidateTag;
|
|
14897
17606
|
}
|
|
14898
|
-
let sdkVersion;
|
|
14899
17607
|
/**
|
|
14900
17608
|
* Generate a UUID, with fallback for environments where crypto.randomUUID isn't available.
|
|
14901
17609
|
*/
|
|
@@ -14910,19 +17618,6 @@ function generateRequestId() {
|
|
|
14910
17618
|
});
|
|
14911
17619
|
}
|
|
14912
17620
|
/**
|
|
14913
|
-
* Set the SDK version to be included in API request headers
|
|
14914
|
-
* Called by the SDK during initialization
|
|
14915
|
-
*/
|
|
14916
|
-
function setSdkVersion(version) {
|
|
14917
|
-
sdkVersion = version;
|
|
14918
|
-
}
|
|
14919
|
-
/**
|
|
14920
|
-
* Get the currently set SDK version
|
|
14921
|
-
*/
|
|
14922
|
-
function getSdkVersion() {
|
|
14923
|
-
return sdkVersion;
|
|
14924
|
-
}
|
|
14925
|
-
/**
|
|
14926
17621
|
* Parse the Retry-After header value to milliseconds.
|
|
14927
17622
|
* The header can be either:
|
|
14928
17623
|
* - A number of seconds (e.g., "120")
|
|
@@ -15145,7 +17840,7 @@ async function createAuthenticatedApiClient(supabase) {
|
|
|
15145
17840
|
}
|
|
15146
17841
|
return createCMSClient(authHeaders);
|
|
15147
17842
|
}
|
|
15148
|
-
function createRawCMSClient(getHeaders = () => ({}), baseUrl) {
|
|
17843
|
+
function createRawCMSClient(getHeaders = () => ({}), baseUrl, { sdkVersion } = {}) {
|
|
15149
17844
|
return ({ endpoint, body, params, options = {} }) => {
|
|
15150
17845
|
const plan = buildApiRequestPlan({
|
|
15151
17846
|
endpoint,
|
|
@@ -15279,8 +17974,8 @@ function createParsedClient(rawClient) {
|
|
|
15279
17974
|
return parseSuccessResponse(endpoint, response, config);
|
|
15280
17975
|
};
|
|
15281
17976
|
}
|
|
15282
|
-
function createCMSClient(headers = {}, baseUrl) {
|
|
15283
|
-
return createParsedClient(createRawCMSClient(() => headers, baseUrl));
|
|
17977
|
+
function createCMSClient(headers = {}, baseUrl, options) {
|
|
17978
|
+
return createParsedClient(createRawCMSClient(() => headers, baseUrl, options));
|
|
15284
17979
|
}
|
|
15285
17980
|
const createServerAPIClient = (apiKey, baseUrl) => {
|
|
15286
17981
|
const normalizedApiKey = normalizeCmsApiKey(apiKey);
|
|
@@ -15290,11 +17985,11 @@ const createServerAPIClient = (apiKey, baseUrl) => {
|
|
|
15290
17985
|
const authHeaders = { Authorization: `Bearer ${normalizedApiKey}` };
|
|
15291
17986
|
return createCMSClient(authHeaders, baseUrl);
|
|
15292
17987
|
};
|
|
15293
|
-
function createInternalAPIClient(baseUrl) {
|
|
15294
|
-
return createCMSClient({}, baseUrl);
|
|
17988
|
+
function createInternalAPIClient(baseUrl, options) {
|
|
17989
|
+
return createCMSClient({}, baseUrl, options);
|
|
15295
17990
|
}
|
|
15296
|
-
function createBearerAPIClient(token, baseUrl) {
|
|
15297
|
-
return createParsedClient(createRawCMSClient(() => ({ Authorization: `Bearer ${typeof token === "function" ? token() : token}` }), baseUrl));
|
|
17991
|
+
function createBearerAPIClient(token, baseUrl, options) {
|
|
17992
|
+
return createParsedClient(createRawCMSClient(() => ({ Authorization: `Bearer ${typeof token === "function" ? token() : token}` }), baseUrl, options));
|
|
15298
17993
|
}
|
|
15299
17994
|
/** Positive receipt evidence is kept distinct from an HTTP or request failure. */
|
|
15300
17995
|
function requireUsableCommitProjection(response, parsed) {
|
|
@@ -15302,13 +17997,6 @@ function requireUsableCommitProjection(response, parsed) {
|
|
|
15302
17997
|
throw new ApiCommittedWriteNotice(pageCommitConfirmationEnvelopeSchema.parse(parsed).data);
|
|
15303
17998
|
}
|
|
15304
17999
|
|
|
15305
|
-
//#endregion
|
|
15306
|
-
//#region ../api/src/appointmentServiceVisibility.ts
|
|
15307
|
-
const APPOINTMENT_SERVICE_VISIBILITY_VALUES = ["public", "unlisted"];
|
|
15308
|
-
function isAppointmentServiceVisibility(value) {
|
|
15309
|
-
return value === "public" || value === "unlisted";
|
|
15310
|
-
}
|
|
15311
|
-
|
|
15312
18000
|
//#endregion
|
|
15313
18001
|
//#region ../api/src/appointmentServiceLocation.ts
|
|
15314
18002
|
const APPOINTMENT_SERVICE_LOCATION_TYPE_VALUES = [
|
|
@@ -15329,67 +18017,37 @@ const APPOINTMENT_RESERVATION_STATUS_VALUES = [
|
|
|
15329
18017
|
];
|
|
15330
18018
|
|
|
15331
18019
|
//#endregion
|
|
15332
|
-
//#region ../api/src/
|
|
15333
|
-
|
|
15334
|
-
|
|
18020
|
+
//#region ../api/src/wholeSeriesRuns.ts
|
|
18021
|
+
const DELETE_EVENT_OFFERING_CONFLICT_REASONS = [
|
|
18022
|
+
"has_registrations",
|
|
18023
|
+
"has_payments",
|
|
18024
|
+
"published_page",
|
|
18025
|
+
"inconsistent_ownership"
|
|
18026
|
+
];
|
|
18027
|
+
function isDeleteEventOfferingConflictReason(value) {
|
|
18028
|
+
return DELETE_EVENT_OFFERING_CONFLICT_REASONS.some((reason) => reason === value);
|
|
15335
18029
|
}
|
|
18030
|
+
const EVENT_SERIES_REGISTRATION_STATUSES = [
|
|
18031
|
+
"pending_payment",
|
|
18032
|
+
"enrolled",
|
|
18033
|
+
"waitlist",
|
|
18034
|
+
"cancelled",
|
|
18035
|
+
"completed"
|
|
18036
|
+
];
|
|
15336
18037
|
|
|
15337
18038
|
//#endregion
|
|
15338
|
-
//#region ../api/src/
|
|
15339
|
-
/**
|
|
15340
|
-
|
|
15341
|
-
|
|
15342
|
-
|
|
15343
|
-
|
|
15344
|
-
|
|
15345
|
-
|
|
15346
|
-
|
|
15347
|
-
|
|
15348
|
-
|
|
15349
|
-
|
|
15350
|
-
return body.error.message;
|
|
15351
|
-
}
|
|
15352
|
-
}
|
|
15353
|
-
if (typeof body.error === "string" && body.error.trim().length > 0) {
|
|
15354
|
-
return body.error;
|
|
15355
|
-
}
|
|
15356
|
-
if (typeof body.message === "string" && body.message.trim().length > 0) {
|
|
15357
|
-
return body.message;
|
|
15358
|
-
}
|
|
15359
|
-
return null;
|
|
15360
|
-
}
|
|
15361
|
-
/**
|
|
15362
|
-
* Normalizes various error types and throws with an appropriate message.
|
|
15363
|
-
*
|
|
15364
|
-
* - For ApiRequestError: extracts message from body.error or body.message
|
|
15365
|
-
* - For Error instances: re-throws as-is
|
|
15366
|
-
* - For unknown errors: wraps with fallback message
|
|
15367
|
-
*
|
|
15368
|
-
* @param error - The error to normalize
|
|
15369
|
-
* @param fallback - Fallback message if no specific message can be extracted
|
|
15370
|
-
* @throws {Error} Always throws - this function never returns
|
|
15371
|
-
*/
|
|
15372
|
-
function normalizeApiError(error, fallback) {
|
|
15373
|
-
if (error instanceof ApiRequestError) {
|
|
15374
|
-
const message = extractApiErrorMessage(error.body);
|
|
15375
|
-
throw new ApiRequestError(message ?? fallback, {
|
|
15376
|
-
endpoint: error.endpoint,
|
|
15377
|
-
status: error.status,
|
|
15378
|
-
method: error.method,
|
|
15379
|
-
auth: error.auth,
|
|
15380
|
-
requestId: error.requestId,
|
|
15381
|
-
body: error.body,
|
|
15382
|
-
cause: error,
|
|
15383
|
-
errorCode: error.errorCode,
|
|
15384
|
-
tag: error.tag,
|
|
15385
|
-
retryAfterMs: error.retryAfterMs
|
|
15386
|
-
});
|
|
15387
|
-
}
|
|
15388
|
-
if (error instanceof Error) {
|
|
15389
|
-
throw error;
|
|
15390
|
-
}
|
|
15391
|
-
throw new Error(fallback);
|
|
15392
|
-
}
|
|
18039
|
+
//#region ../api/src/recordedPayment.ts
|
|
18040
|
+
/** Facts shared by every staff-recorded payment. Future dates need the site's timezone. */
|
|
18041
|
+
const recordedPaymentFieldsSchema = z.object({
|
|
18042
|
+
method: z.enum(OFFLINE_PAYMENT_METHODS),
|
|
18043
|
+
paidOn: z.iso.date()
|
|
18044
|
+
});
|
|
18045
|
+
/** A newly added booking defaults its payment date to today in the site's timezone. */
|
|
18046
|
+
const handAddedPaidPaymentSchema = recordedPaymentFieldsSchema.extend({
|
|
18047
|
+
kind: z.literal("paid"),
|
|
18048
|
+
amountCents: z.number().int().positive(),
|
|
18049
|
+
paidOn: recordedPaymentFieldsSchema.shape.paidOn.optional()
|
|
18050
|
+
}).strict();
|
|
15393
18051
|
|
|
15394
18052
|
//#endregion
|
|
15395
18053
|
//#region ../api/src/bookingActivity.ts
|
|
@@ -15409,6 +18067,8 @@ const BOOKING_ACTIVITY_TYPE_VALUES = [
|
|
|
15409
18067
|
"event_booking_restored",
|
|
15410
18068
|
"waitlist_joined",
|
|
15411
18069
|
"balance_paid",
|
|
18070
|
+
"payment_recorded",
|
|
18071
|
+
"payment_waived",
|
|
15412
18072
|
"appointment_cancelled",
|
|
15413
18073
|
"booking_closure_created",
|
|
15414
18074
|
"booking_closure_removed",
|
|
@@ -15512,18 +18172,23 @@ const PERSON_DELETION_BLOCKER_CATEGORY_VALUES = [
|
|
|
15512
18172
|
"uploads",
|
|
15513
18173
|
"contact_corrections"
|
|
15514
18174
|
];
|
|
15515
|
-
|
|
15516
|
-
|
|
15517
|
-
|
|
15518
|
-
|
|
15519
|
-
"
|
|
15520
|
-
"
|
|
15521
|
-
"
|
|
15522
|
-
"
|
|
18175
|
+
const PERSON_REMOVAL_BLOCKER_CATEGORIES = [
|
|
18176
|
+
"upcoming_bookings",
|
|
18177
|
+
"memberships",
|
|
18178
|
+
"payment_plans",
|
|
18179
|
+
"flexible_balances",
|
|
18180
|
+
"unused_passes",
|
|
18181
|
+
"credit",
|
|
18182
|
+
"gifts",
|
|
18183
|
+
"purchases",
|
|
18184
|
+
"uploads"
|
|
18185
|
+
];
|
|
18186
|
+
const PERSON_REMOVAL_CLEANUP_KINDS = [
|
|
18187
|
+
"storage",
|
|
18188
|
+
"stripe",
|
|
18189
|
+
"google_calendar",
|
|
18190
|
+
"newsletter"
|
|
15523
18191
|
];
|
|
15524
|
-
function isDeleteEventOfferingConflictReason(value) {
|
|
15525
|
-
return DELETE_EVENT_OFFERING_CONFLICT_REASONS.some((reason) => reason === value);
|
|
15526
|
-
}
|
|
15527
18192
|
|
|
15528
18193
|
//#endregion
|
|
15529
18194
|
//#region ../api/src/eventAttendanceLabels.ts
|
|
@@ -16687,6 +19352,17 @@ async function adminChangePlan(apiClient, body, options) {
|
|
|
16687
19352
|
return normalizeApiError(error, "Failed to update billing plan");
|
|
16688
19353
|
}
|
|
16689
19354
|
}
|
|
19355
|
+
async function adminGrantTrial(apiClient, body, options) {
|
|
19356
|
+
try {
|
|
19357
|
+
return unwrapResponse(await apiClient({
|
|
19358
|
+
endpoint: "adminGrantTrial",
|
|
19359
|
+
body,
|
|
19360
|
+
options
|
|
19361
|
+
}));
|
|
19362
|
+
} catch (error) {
|
|
19363
|
+
return normalizeApiError(error, "Failed to give free time");
|
|
19364
|
+
}
|
|
19365
|
+
}
|
|
16690
19366
|
async function listAdminInvites(apiClient, params = {}, options) {
|
|
16691
19367
|
try {
|
|
16692
19368
|
return unwrapResponse(await apiClient({
|
|
@@ -16891,7 +19567,6 @@ async function adminClearSitePublicCache(apiClient, siteId, options) {
|
|
|
16891
19567
|
|
|
16892
19568
|
//#endregion
|
|
16893
19569
|
//#region ../api/src/accessAdmin.ts
|
|
16894
|
-
const STAFF_OPERATIONAL_CAPABILITIES = ["appointments", "event_ops"];
|
|
16895
19570
|
function getDefaultStaffCapabilities(surfaces) {
|
|
16896
19571
|
if (surfaces.events && !surfaces.appointments) return ["event_ops"];
|
|
16897
19572
|
return ["appointments"];
|
|
@@ -17056,6 +19731,7 @@ async function acceptSiteInvitation(apiClient, { body, options }) {
|
|
|
17056
19731
|
|
|
17057
19732
|
//#endregion
|
|
17058
19733
|
//#region ../api/src/staffAccess.ts
|
|
19734
|
+
const STAFF_OPERATIONAL_CAPABILITIES = ["appointments", "event_ops"];
|
|
17059
19735
|
function roleAtLeast$1(role, minimum) {
|
|
17060
19736
|
return SITE_ROLE_PERMISSION_LEVEL[role] >= SITE_ROLE_PERMISSION_LEVEL[minimum];
|
|
17061
19737
|
}
|
|
@@ -17223,6 +19899,21 @@ const FLEXIBLE_BALANCE_ADMIN_ACTION_SOURCES = [
|
|
|
17223
19899
|
"event_attendee_detail",
|
|
17224
19900
|
"event_series_registration_detail"
|
|
17225
19901
|
];
|
|
19902
|
+
const settlementAuditSchema = {
|
|
19903
|
+
operationId: z.string().uuid(),
|
|
19904
|
+
orderItemIds: z.array(z.string().uuid()).nonempty().optional(),
|
|
19905
|
+
amountCents: z.number().int().positive().optional().nullable(),
|
|
19906
|
+
externalReference: z.string().optional().nullable(),
|
|
19907
|
+
note: z.string().optional().nullable()
|
|
19908
|
+
};
|
|
19909
|
+
const settleShopOrderPaymentSchema = z.union([z.object({
|
|
19910
|
+
...settlementAuditSchema,
|
|
19911
|
+
action: z.enum(["mark_paid", "record_partial_payment"]),
|
|
19912
|
+
...recordedPaymentFieldsSchema.shape
|
|
19913
|
+
}), z.object({
|
|
19914
|
+
...settlementAuditSchema,
|
|
19915
|
+
action: z.literal("mark_waived")
|
|
19916
|
+
})]);
|
|
17226
19917
|
|
|
17227
19918
|
//#endregion
|
|
17228
19919
|
//#region ../api/src/navigation/visibility.ts
|
|
@@ -24898,4 +27589,4 @@ function optionalTitle(data) {
|
|
|
24898
27589
|
}
|
|
24899
27590
|
|
|
24900
27591
|
//#endregion
|
|
24901
|
-
export { normalizeMediaValueToIdentifierOnly as $,
|
|
27592
|
+
export { normalizeMediaValueToIdentifierOnly as $, normalizeApiError as $n, formatDateShort as $t, formatDiffDetail as A, createManagementClient as An, navigationIdentity as At, buildFieldChanges as B, planRepeaterGridColumnChange as Bn, preparePushConfigTarget as Bt, reportFromOutcomes as C, CliExit as Cn, fileExists$1 as Ct, normalizeRemoteHomepageReference as D, isLocalConfigured as Dn, writeJsonFileAtomic as Dt, createEmptySyncResult as E, getEnvironmentDebugInfo as En, writeJsonFile$1 as Et, calculatePullDiff as F, fieldSchema as Fn, navigationCommandItemToManagementInput as Ft, syncLocalMediaForPull as G, resolveRepeaterGridColumnsForBreakpoint as Gn, bindRequestTimeout as Gt, formatFieldPath as H, buildRepeaterGridLayout as Hn, createBearerAPIClient as Ht, buildJsonDiff as I, normalizeSelectSource as In, normalizeNavigationConfigItemForCommand as It, admitBinaryWriteDestination as J, typeBasedLayout as Jn, createCommandContext as Jt, applyLocalMediaPush as K, resolveRepeaterGridRowFieldId as Kn, confirmAction as Kt, hasPageOperations as L, parseFieldDefinition as Ln, applyPushConfigPlan as Lt, hasPendingChanges as M, PUBLIC_FILE_FIELD_KINDS as Mn, convertPulledFooterToLocal as Mt, findChangedFieldsWithValues as N, createSdkSelectField as Nn, convertPulledNavigationToLocal as Nt, calculateDiff as O, isRemoteConfigured as On, isMediaValue as Ot, formatHumanDiff as P, createSiteOptionSelectField as Pn, convertPulledPagesToLocal as Pt, normalizeMediaIdentifierInput as Q, extractApiErrorMessage as Qn, currentInvocationEnvelope as Qt, pageMetadataForUpsert as R, parseFieldDefinitions as Rn, createPushConfigCommand as Rt, planCliCommandBatchForBindings as S, transportSuggestion as Sn, ensureDir$1 as St, executeResidualSettingsSyncPlan as T, getEnvPrefix as Tn, readJsonFile as Tt, revalidateLocalMediaPushPlan as U, parseRepeaterGridInteger as Un, deriveCmsApiUrlFromDashboardUrl as Ut, formatFieldChange as V, projectRepeaterGridLayoutForRendering as Vn, revalidatePushConfigPlan as Vt, syncLocalMediaForPush as W, resolveRepeaterGridColumnFieldId as Wn, mapEnvironmentExecutionOutcomesJson as Wt, writeBinaryFileAtomic as X, bindingSchema as Xn, createListCommand as Xt, readStableBinaryFile as Y, NodeSchema as Yn, createGetCommand as Yt, sha256 as Z, ManagementApiError as Zn, createOutputForCommand as Zt, commandExecutionToReport as _, planEnvironmentWriteAuthorization as _n, metadataFilePath as _t, executeEntryCommandPlanAsReport as a, parseJsonArray as an, portablePageDesignSchema as ar, readEntries as at, executeCompiledCliCommandPlanAsReport as b, ErrorCodes as bn, writeMetadataFile as bt, titleFromEntryData as c, resolveCommandPath as cn, ApiEnvelopeError as cr, readEventsMeta as ct, unsafeAsCliEntryIdentifier as d, withConfirmation as dn, readNavigationMeta as dt, getOutputContext as en, isRecord$5 as er, walkLocalContentMedia as et, unsafeAsCliFormSlug as f, withErrorHandling as fn, readPagesMeta as ft, commandCompileFailureToReport as g, planEnvironmentExecution as gn, ensureMetadataDir as gt, mutationCounterForEffect as h, parseEnvironmentSelection as hn, writeMediaManifest as ht, entrySiteCommandTypes as i, mapEntryForOutput as in, linkPayloadSchema as ir, findLegacyEntryFiles as it, formatDiffSummary as j, DEFAULT_MEDIA_FIELD_KINDS as jn, navigationIdentityMap as jt, calculateFooterDiff as k, loadSdkEnvironment as kn, duplicateNavigationIdentityMessage as kt, unsafeAsCliBlockIdentifier as l, runWithInvocation as ln, unwrapResponse as lr, readFooterMeta as lt, commandReportToSyncResult as m, executeEnvironmentPlan as mn, readVenuesMeta as mt, entryCommandCompileErrorToReport as n, handleCommandError as nn, sdkEventScheduleSchema as nr, downloadMedia as nt, executeEntryCommandPlanAsSyncResult as o, parseJsonData as on, sdkConfiguredBookingVerticalConfigSchema as or, readAllMeta as ot, unsafeAsCliPageIdentifier as p, withOutputOnlyErrorHandling as pn, readSettingsMeta as pt, prepareLocalMediaForPush as q, resolveRepeaterGridSpanFieldId as qn, createClientForEnv as qt, entryCommandReportToSyncResult as r, managementClientConfigFor as rn, sdkSettingsUpdateSchema as rr, extractMediaIdentifiers as rt, syncResultForEntryCommandCompileError as s, parsePaginationOptions as sn, API_ENDPOINTS as sr, readEventCategoriesMeta as st, compileEntryPushCommands as t, getOutputOnlyContext as tn, ApiRequestError as tr, buildStorageUrl as tt, unsafeAsCliContentTypeKey as u, withCommandContext as un, readFormsMeta as ut, commandReportHasFailures as v, requireEnvironmentWriteAuthorization as vn, metadataTargetFromEnvironment as vt, mergeManySyncResults as w, createOutput as wn, listFiles as wt, formatManagementError as x, formatError as xn, writeMetadataRecordMap as xt, executeCliCommandPlan as y, withRiverbankEnv as yn, readMetadataFile as yt, calculateEntryDiffs as z, resolveIconPickerPreset as zn, loadPushConfigTarget as zt };
|