riverbankcms 0.28.0 → 0.28.1

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.
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
- import { A as evaluateSiteCommandExposure, E as parseAndPlanCommandBatchForTypes, F as entryIdentifierForCommand, It as themeSchema, K as bookingQuestionAttachmentSchema, Mr as asPageFlowPresetId, Nr as asSiteStyleId, T as createDryRunReport, V as eventBookingOfferIntentSchema, X as createCommandRef, Xr as parseSurfaceTree, Z as createContentEntryRef, Zn as getDirectImageUrlForSiteScopedPath, _n as pageDesignIntentSchemaVersion, _r as normalizeAmbiguousStoragePathForSite, br as MAX_MANAGEMENT_MEDIA_UPLOAD_BYTES, cn as asHeaderLayoutId, ct as require_dist, dn as headerStyleIdValues, dr as slugifyMediaIdentifier, fn as buttonStyleSelectionSchema, gn as pageDesignBoundaryTreatmentSchema, gt as headerPreferencesSchema, hr as getMimeType, it as createSiteCommandSiteRef, j as getSiteCommandMetadata, k as evaluateSiteCommandBatchExposure, kr as asAppearancePresetId, ln as asHeaderStyleId, m as isInternalResolvedLinkValue, mr as getExtensionForMimeType, n as sdkCourseBookingSettingsSchema, on as asFooterLookId, r as sdkEventOccurrenceInputSchema, s as RESOLVED_INTERNAL_LINK_ENTITY_TYPES, sn as footerLookIdValues, sr as buildMediaIdentifierFromFilename, st as slugPart, un as headerLayoutIdValues, ur as resolveMediaIdentifier, vn as footerPreferencesSchema, w as stableStringify, x as resolvePageUpsertSeo, y as eventVisibilitySchema, yr as FILE_SIZE_LIMITS } from "./sdk-event-upsert-BrJvTQ-D.mjs";
3
- import { C as createSecret, Cn as parsePageId, Et as resolveBookingTimeZoneId, L as parseFooterCompositionV1, Ln as unsafeAsEventOfferingId, On as unsafeAsAppointmentResourceId, Qn as unsafeAsStaffMemberId, Rn as unsafeAsEventSeriesId, St as parseBookingLocalDate, T as parseManagementKey, Tn as parseSiteId, Vn as unsafeAsOccurrenceId, Z as locationValueSchema, _ as publicReadFreshnessSeconds, c as parseVerticalId, d as DEFAULT_EVENT_TYPE_PRESETS, f as EVENT_PRESET_VALUES, g as getPublicReadCachePolicy, ir as ok, l as parseVerticalPresetId, o as areConfiguredBookingVerticalConfigsEqual, ot as formatDate, pn as assertNever, r as resolveEventCatalogue, rr as err, s as canonicalizeConfiguredBookingVerticalConfig, t as committedWriteProofSchema, u as resolveSiteVerticalBookingConfig, w as exposeSecret } from "./src-D9-RAM-l.mjs";
4
- import { n as CliSdkPeerError, t as loadConfigFile } from "./load-config-9DBnYiBm.mjs";
2
+ import { A as evaluateSiteCommandExposure, E as parseAndPlanCommandBatchForTypes, F as entryIdentifierForCommand, Ht as BREAKPOINT_MD, It as themeSchema, Jn as parseAspectRatioValue, K as bookingQuestionAttachmentSchema, Mr as asPageFlowPresetId, Nr as asSiteStyleId, Or as arrangementModes, T as createDryRunReport, Ut as BREAKPOINT_SM, V as eventBookingOfferIntentSchema, Vt as BREAKPOINT_LG, Wt as BREAKPOINT_XL, X as createCommandRef, Xr as parseSurfaceTree, Z as createContentEntryRef, Zn as getDirectImageUrlForSiteScopedPath, _n as pageDesignIntentSchemaVersion, _r as normalizeAmbiguousStoragePathForSite, br as MAX_MANAGEMENT_MEDIA_UPLOAD_BYTES, cn as asHeaderLayoutId, ct as require_dist, dn as headerStyleIdValues, dr as slugifyMediaIdentifier, fn as buttonStyleSelectionSchema, gn as pageDesignBoundaryTreatmentSchema, hr as getMimeType, it as createSiteCommandSiteRef, j as getSiteCommandMetadata, k as evaluateSiteCommandBatchExposure, kr as asAppearancePresetId, ln as asHeaderStyleId, m as isInternalResolvedLinkValue, mr as getExtensionForMimeType, n as sdkCourseBookingSettingsSchema, on as asFooterLookId, r as sdkEventOccurrenceInputSchema, s as RESOLVED_INTERNAL_LINK_ENTITY_TYPES, sn as footerLookIdValues, sr as buildMediaIdentifierFromFilename, st as slugPart, un as headerLayoutIdValues, ur as resolveMediaIdentifier, vn as footerPreferencesSchema, vt as headerPreferencesSchema, w as stableStringify, x as resolvePageUpsertSeo, y as eventVisibilitySchema, yr as FILE_SIZE_LIMITS } from "./sdk-event-upsert-4Dn75Ui9.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-nzVzuMR1.mjs";
4
+ import { n as CliSdkPeerError, t as loadConfigFile } from "./load-config-DTbiJyCf.mjs";
5
5
  import { Command } from "commander";
6
6
  import { AsyncLocalStorage } from "node:async_hooks";
7
7
  import * as fs$1 from "fs/promises";
@@ -82,20 +82,20 @@ function isApiError(result) {
82
82
  function isApiSuccess(result) {
83
83
  return result.success === true;
84
84
  }
85
- function isRecord$10(value) {
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$10(value) && typeof value.field === "string" && typeof value.code === "string" && typeof value.message === "string" && (value.capacityOverride === undefined || isCapacityOverrideRefusal(value.capacityOverride));
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$10(body)) return null;
96
+ if (!isRecord$11(body)) return null;
97
97
  const error = body.error;
98
- if (!isRecord$10(error)) return null;
98
+ if (!isRecord$11(error)) return null;
99
99
  if ("success" in body && body.success !== false) {
100
100
  return null;
101
101
  }
@@ -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",
@@ -4605,20 +4636,22 @@ const ENDPOINT_DEFINITIONS = {
4605
4636
  auth: "user",
4606
4637
  responseKind: "json"
4607
4638
  },
4608
- aiPlaygroundProposeMulti: {
4609
- path: "/sites/{siteId}/ai/playground/propose-multi",
4639
+ assistantConversation: {
4640
+ path: "/assistant/conversation",
4610
4641
  method: "POST",
4611
4642
  auth: "user",
4612
- responseKind: "stream"
4643
+ responseKind: "json",
4644
+ errors: []
4613
4645
  },
4614
- aiPlaygroundProposeMultiGet: {
4615
- path: "/sites/{siteId}/ai/playground/propose-multi",
4616
- method: "GET",
4646
+ assistantNewConversation: {
4647
+ path: "/assistant/conversation/new",
4648
+ method: "POST",
4617
4649
  auth: "user",
4618
- responseKind: "stream"
4650
+ responseKind: "json",
4651
+ errors: []
4619
4652
  },
4620
- assistantSuggestions: {
4621
- path: "/assistant/suggestions",
4653
+ assistantOutcome: {
4654
+ path: "/assistant/outcome",
4622
4655
  method: "POST",
4623
4656
  auth: "user",
4624
4657
  responseKind: "json",
@@ -4645,42 +4678,6 @@ const ENDPOINT_DEFINITIONS = {
4645
4678
  responseKind: "stream",
4646
4679
  errors: []
4647
4680
  },
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
4681
  aiGenerateSite: {
4685
4682
  path: "/sites/{siteId}/ai/generate",
4686
4683
  method: "POST",
@@ -5463,6 +5460,32 @@ const ENDPOINT_DEFINITIONS = {
5463
5460
  auth: "user",
5464
5461
  responseKind: "json"
5465
5462
  },
5463
+ getRouteAddress: {
5464
+ path: "/sites/{siteId}/content-address/{targetKind}/{targetId}",
5465
+ method: "GET",
5466
+ tags: [TAG_TEMPLATES.site, TAG_TEMPLATES.siteRoutableContent],
5467
+ auth: "user",
5468
+ responseKind: "json",
5469
+ errors: ["resource:not_found", "resource:conflict"]
5470
+ },
5471
+ changeRouteAddress: {
5472
+ editLease: "managed",
5473
+ path: "/sites/{siteId}/content-address/{targetKind}/{targetId}",
5474
+ method: "PATCH",
5475
+ tags: [
5476
+ TAG_TEMPLATES.site,
5477
+ TAG_TEMPLATES.siteRoutableContent,
5478
+ TAG_TEMPLATES.siteRedirects
5479
+ ],
5480
+ auth: "user",
5481
+ responseKind: "json",
5482
+ errors: [
5483
+ "validation:invalid_input",
5484
+ "resource:not_found",
5485
+ "resource:conflict",
5486
+ "server:internal_error"
5487
+ ]
5488
+ },
5466
5489
  updateRouteMetadata: {
5467
5490
  editLease: "managed",
5468
5491
  path: "/sites/{siteId}/routes/{routeId}/metadata",
@@ -5653,6 +5676,30 @@ const ENDPOINT_DEFINITIONS = {
5653
5676
  auth: "user",
5654
5677
  responseKind: "json"
5655
5678
  },
5679
+ previewWebsiteOperation: {
5680
+ path: "/sites/{siteId}/website/preview",
5681
+ method: "POST",
5682
+ auth: "user",
5683
+ responseKind: "json",
5684
+ errors: [
5685
+ "validation:invalid_input",
5686
+ "auth:insufficient_permissions",
5687
+ "server:internal_error"
5688
+ ]
5689
+ },
5690
+ confirmWebsiteOperation: {
5691
+ editLease: "unleased-editor",
5692
+ path: "/sites/{siteId}/website/confirm",
5693
+ method: "POST",
5694
+ auth: "user",
5695
+ responseKind: "json",
5696
+ tags: [TAG_TEMPLATES.site, TAG_TEMPLATES.siteSettings],
5697
+ errors: [
5698
+ "validation:invalid_input",
5699
+ "auth:insufficient_permissions",
5700
+ "server:internal_error"
5701
+ ]
5702
+ },
5656
5703
  previewSiteBrandingOperation: {
5657
5704
  path: "/sites/{siteId}/settings/general/preview",
5658
5705
  method: "POST",
@@ -8799,14 +8846,6 @@ const ENDPOINT_DEFINITIONS = {
8799
8846
  auth: "user",
8800
8847
  responseKind: "json"
8801
8848
  },
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
8849
  billingStatus: {
8811
8850
  path: "/billing/status",
8812
8851
  method: "GET",
@@ -9759,6 +9798,7 @@ const ENDPOINT_DEFINITIONS = {
9759
9798
  responseKind: "json",
9760
9799
  errors: [
9761
9800
  "resource:not_found",
9801
+ "resource:conflict",
9762
9802
  "validation:invalid_input",
9763
9803
  "server:internal_error"
9764
9804
  ]
@@ -10250,17 +10290,6 @@ const blockLocationFields = {
10250
10290
  blockIdentifier: z.string().nullable(),
10251
10291
  stage: z.enum(["draft", "published"])
10252
10292
  };
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
10293
  const mediaUsageLocationSchema = z.discriminatedUnion("kind", [
10265
10294
  z.object({
10266
10295
  kind: z.literal("appointmentService"),
@@ -10268,7 +10297,12 @@ const mediaUsageLocationSchema = z.discriminatedUnion("kind", [
10268
10297
  serviceId: identifierSchema,
10269
10298
  serviceTitle: z.string()
10270
10299
  }),
10271
- ...newsletterUsageLocationSchemas,
10300
+ z.object({
10301
+ kind: z.literal("newsletterCampaign"),
10302
+ siteId: identifierSchema,
10303
+ campaignId: identifierSchema,
10304
+ campaignName: z.string()
10305
+ }),
10272
10306
  z.object({
10273
10307
  kind: z.literal("pageBlock"),
10274
10308
  ...blockLocationFields,
@@ -10861,7 +10895,6 @@ const BOOKING_SETTINGS_SECTION_KEYS = {
10861
10895
  ],
10862
10896
  appointments: [
10863
10897
  "appointmentReminderOverrides",
10864
- "appointmentRescheduleHours",
10865
10898
  "appointmentDefaultMinLeadMinutes",
10866
10899
  "appointmentDefaultMaxBookingNoticeMinutes",
10867
10900
  "appointmentBookingPolicies"
@@ -10885,61 +10918,423 @@ const BOOKING_SETTINGS_EMAIL_SEND_KEYS = [
10885
10918
  ];
10886
10919
 
10887
10920
  //#endregion
10888
- //#region ../api/src/event-destination-policy.ts
10889
- const eventDestinationModeValues = ["cms-managed", "system-managed"];
10890
- const eventDestinationPresetValues = EVENT_PRESET_VALUES;
10891
- const DEFAULT_EVENT_DESTINATION_POLICY = {
10892
- defaultMode: "cms-managed",
10893
- presetOverrides: {}
10894
- };
10895
- function isEventDestinationMode(value) {
10896
- return eventDestinationModeValues.some((mode) => mode === value);
10921
+ //#region ../api/src/event-listing-destination.ts
10922
+ const EVENT_LISTING_DESTINATION_SETTINGS_KEY = "listingDestination";
10923
+ const DEFAULT_EXTERNAL_EVENT_CTA_LABEL = "View details";
10924
+ function isRecord$10(value) {
10925
+ return value !== null && typeof value === "object" && !Array.isArray(value);
10897
10926
  }
10898
- function resolveEventDestinationMode(input) {
10899
- const policy = input.policy ?? DEFAULT_EVENT_DESTINATION_POLICY;
10900
- if (input.preset) {
10901
- const presetOverride = policy.presetOverrides[input.preset];
10902
- if (presetOverride) return presetOverride;
10927
+ function normalizeOptionalLabel(value) {
10928
+ return typeof value === "string" && value.trim() ? value.trim() : null;
10929
+ }
10930
+ function normalizeOptionalUrl(value) {
10931
+ return typeof value === "string" ? value.trim() : "";
10932
+ }
10933
+ function normalizeEventListingDestinationInput(value) {
10934
+ if (!value || value.kind === "native") {
10935
+ return { kind: "native" };
10903
10936
  }
10904
- if (input.publicExperience === "hosted_booking") return "system-managed";
10905
- return policy.defaultMode;
10937
+ return {
10938
+ kind: "external",
10939
+ url: normalizeOptionalUrl(value.url),
10940
+ label: normalizeOptionalLabel(value.label)
10941
+ };
10906
10942
  }
10907
- function macadamiaDefaultEventDestinationPolicy(selectedPresets) {
10908
- const presetOverrides = {};
10909
- for (const preset of selectedPresets) {
10910
- presetOverrides[preset] = preset === "class" ? "system-managed" : "cms-managed";
10943
+ function readEventListingDestinationFromSettings(settingsJson) {
10944
+ if (!isRecord$10(settingsJson)) {
10945
+ return { kind: "native" };
10946
+ }
10947
+ const rawDestination = settingsJson[EVENT_LISTING_DESTINATION_SETTINGS_KEY];
10948
+ if (!isRecord$10(rawDestination) || rawDestination.kind !== "external") {
10949
+ return { kind: "native" };
10911
10950
  }
10912
10951
  return {
10913
- defaultMode: "cms-managed",
10914
- presetOverrides
10952
+ kind: "external",
10953
+ url: normalizeOptionalUrl(rawDestination.url),
10954
+ label: normalizeOptionalLabel(rawDestination.label)
10915
10955
  };
10916
10956
  }
10917
-
10918
- //#endregion
10919
- //#region ../api/src/bookingConfig.ts
10920
- /** Every option kind; the checkout routes validate the requested kind against it. */
10921
- const DIRECT_CHECKOUT_COLLECTION_OPTION_KINDS = [
10922
- "upfront",
10923
- "deferred_manual",
10924
- "instalment_plan",
10925
- "flexible_balance"
10926
- ];
10927
- const eventOccurrenceRestorationSummarySchema = z.object({
10928
- restoredOccurrenceIds: z.array(z.string().uuid()),
10929
- excludedBookings: z.array(z.object({
10930
- attendeeId: z.string().uuid(),
10931
- occurrenceId: z.string().uuid(),
10932
- customerId: z.string().uuid(),
10933
- customerName: z.string().nullable(),
10934
- customerEmail: z.string().nullable()
10935
- }))
10936
- });
10937
- const DEFAULT_BOOKING_NOTIFICATION_SEND_RULE = {
10938
- kind: "send",
10939
- sendToAssignedStaff: true,
10940
- extraEmail: null,
10941
- sendToSiteOwnerIfNoStaff: true
10942
- };
10957
+ function serializeEventListingDestination(destination) {
10958
+ switch (destination.kind) {
10959
+ case "native": return { kind: "native" };
10960
+ case "external": return {
10961
+ kind: "external",
10962
+ url: destination.url,
10963
+ label: destination.label
10964
+ };
10965
+ }
10966
+ }
10967
+ function buildEventListingDestinationSettingsPatch(destination) {
10968
+ return { [EVENT_LISTING_DESTINATION_SETTINGS_KEY]: serializeEventListingDestination(destination) };
10969
+ }
10970
+ function parseExternalEventUrl(value) {
10971
+ const raw = normalizeOptionalUrl(value);
10972
+ if (!raw) {
10973
+ return {
10974
+ ok: false,
10975
+ error: {
10976
+ code: "missing_url",
10977
+ message: "External event URL is required."
10978
+ }
10979
+ };
10980
+ }
10981
+ try {
10982
+ const url = assertAllowedPublicUrl(raw, {
10983
+ allowedProtocols: ["https:"],
10984
+ portPolicy: "standard-only"
10985
+ });
10986
+ return {
10987
+ ok: true,
10988
+ url: url.href
10989
+ };
10990
+ } catch (error) {
10991
+ return {
10992
+ ok: false,
10993
+ error: {
10994
+ code: "invalid_url",
10995
+ message: error instanceof Error ? error.message : "External event URL must be a valid public HTTPS URL."
10996
+ }
10997
+ };
10998
+ }
10999
+ }
11000
+ function getExternalEventCtaLabel(destination) {
11001
+ return destination.label ?? DEFAULT_EXTERNAL_EVENT_CTA_LABEL;
11002
+ }
11003
+ function getEventListingDestinationPublicationIssue(destination) {
11004
+ if (destination.kind === "native") {
11005
+ return null;
11006
+ }
11007
+ const validation = parseExternalEventUrl(destination.url);
11008
+ if (validation.ok) {
11009
+ return null;
11010
+ }
11011
+ return {
11012
+ field: "listingDestination.url",
11013
+ code: validation.error.code,
11014
+ message: validation.error.message
11015
+ };
11016
+ }
11017
+ function buildPublicEventDestination(input) {
11018
+ if (input.listingDestination.kind === "native") {
11019
+ if (!input.nativePath.trim()) {
11020
+ return null;
11021
+ }
11022
+ return {
11023
+ kind: "native",
11024
+ mode: input.nativeMode,
11025
+ path: input.nativePath
11026
+ };
11027
+ }
11028
+ const validation = parseExternalEventUrl(input.listingDestination.url);
11029
+ if (!validation.ok) {
11030
+ return null;
11031
+ }
11032
+ return {
11033
+ kind: "external",
11034
+ url: validation.url,
11035
+ label: getExternalEventCtaLabel(input.listingDestination)
11036
+ };
11037
+ }
11038
+
11039
+ //#endregion
11040
+ //#region ../api/src/event-destination-policy.ts
11041
+ const eventDestinationModeValues = ["cms-managed", "system-managed"];
11042
+ const eventDestinationPresetValues = EVENT_PRESET_VALUES;
11043
+ const DEFAULT_EVENT_DESTINATION_POLICY = {
11044
+ defaultMode: "cms-managed",
11045
+ presetOverrides: {}
11046
+ };
11047
+ function isEventDestinationMode(value) {
11048
+ return eventDestinationModeValues.some((mode) => mode === value);
11049
+ }
11050
+ function resolveEventDestinationMode(input) {
11051
+ const policy = input.policy ?? DEFAULT_EVENT_DESTINATION_POLICY;
11052
+ if (input.preset) {
11053
+ const presetOverride = policy.presetOverrides[input.preset];
11054
+ if (presetOverride) return presetOverride;
11055
+ }
11056
+ if (input.publicExperience === "hosted_booking") return "system-managed";
11057
+ return policy.defaultMode;
11058
+ }
11059
+ function macadamiaDefaultEventDestinationPolicy(selectedPresets) {
11060
+ const presetOverrides = {};
11061
+ for (const preset of selectedPresets) {
11062
+ presetOverrides[preset] = preset === "class" ? "system-managed" : "cms-managed";
11063
+ }
11064
+ return {
11065
+ defaultMode: "cms-managed",
11066
+ presetOverrides
11067
+ };
11068
+ }
11069
+
11070
+ //#endregion
11071
+ //#region ../api/src/appointmentServiceVisibility.ts
11072
+ const APPOINTMENT_SERVICE_VISIBILITY_VALUES = ["public", "unlisted"];
11073
+ function isAppointmentServiceVisibility(value) {
11074
+ return value === "public" || value === "unlisted";
11075
+ }
11076
+
11077
+ //#endregion
11078
+ //#region ../api/src/request-error.ts
11079
+ var ApiRequestError = class extends Error {
11080
+ constructor(message, options) {
11081
+ super(message);
11082
+ this.name = "ApiRequestError";
11083
+ this.endpoint = options.endpoint;
11084
+ this.status = options.status;
11085
+ this.method = options.method;
11086
+ this.auth = options.auth;
11087
+ this.requestId = options.requestId;
11088
+ this.body = options.body;
11089
+ this.cause = options.cause;
11090
+ this.errorCode = options.errorCode;
11091
+ this.tag = options.tag;
11092
+ this.retryAfterMs = options.retryAfterMs;
11093
+ }
11094
+ };
11095
+
11096
+ //#endregion
11097
+ //#region ../api/src/utils/isRecord.ts
11098
+ function isRecord$5(value) {
11099
+ return typeof value === "object" && value !== null && !Array.isArray(value);
11100
+ }
11101
+
11102
+ //#endregion
11103
+ //#region ../api/src/utils/error-propagation.ts
11104
+ /**
11105
+ * Extracts an error message from an API error response body.
11106
+ * Checks for our standard API error format: { error: { message: "..." } }
11107
+ * Also handles legacy formats with `error` or `message` as strings.
11108
+ */
11109
+ function extractApiErrorMessage(body) {
11110
+ if (!isRecord$5(body)) {
11111
+ return null;
11112
+ }
11113
+ if (isRecord$5(body.error)) {
11114
+ if (typeof body.error.message === "string" && body.error.message.trim().length > 0) {
11115
+ return body.error.message;
11116
+ }
11117
+ }
11118
+ if (typeof body.error === "string" && body.error.trim().length > 0) {
11119
+ return body.error;
11120
+ }
11121
+ if (typeof body.message === "string" && body.message.trim().length > 0) {
11122
+ return body.message;
11123
+ }
11124
+ return null;
11125
+ }
11126
+ /**
11127
+ * Normalizes various error types and throws with an appropriate message.
11128
+ *
11129
+ * - For ApiRequestError: extracts message from body.error or body.message
11130
+ * - For Error instances: re-throws as-is
11131
+ * - For unknown errors: wraps with fallback message
11132
+ *
11133
+ * @param error - The error to normalize
11134
+ * @param fallback - Fallback message if no specific message can be extracted
11135
+ * @throws {Error} Always throws - this function never returns
11136
+ */
11137
+ function normalizeApiError(error, fallback) {
11138
+ if (error instanceof ApiRequestError) {
11139
+ const message = extractApiErrorMessage(error.body);
11140
+ throw new ApiRequestError(message ?? fallback, {
11141
+ endpoint: error.endpoint,
11142
+ status: error.status,
11143
+ method: error.method,
11144
+ auth: error.auth,
11145
+ requestId: error.requestId,
11146
+ body: error.body,
11147
+ cause: error,
11148
+ errorCode: error.errorCode,
11149
+ tag: error.tag,
11150
+ retryAfterMs: error.retryAfterMs
11151
+ });
11152
+ }
11153
+ if (error instanceof Error) {
11154
+ throw error;
11155
+ }
11156
+ throw new Error(fallback);
11157
+ }
11158
+
11159
+ //#endregion
11160
+ //#region ../api/src/appointmentSetup.ts
11161
+ const APPOINTMENT_SETUP_MAX_MINUTES = 1440;
11162
+ const appointmentSetupRefSchema = z.discriminatedUnion("kind", [z.object({
11163
+ kind: z.literal("existing"),
11164
+ id: z.string().min(1)
11165
+ }), z.object({
11166
+ kind: z.literal("new"),
11167
+ draftId: z.string().min(1)
11168
+ })]);
11169
+ const appointmentSetupVenueChoiceSchema = z.discriminatedUnion("kind", [
11170
+ z.object({ kind: z.literal("none") }),
11171
+ z.object({
11172
+ kind: z.literal("existing"),
11173
+ venueId: z.string().min(1)
11174
+ }),
11175
+ z.object({
11176
+ kind: z.literal("create"),
11177
+ draftId: z.string().min(1),
11178
+ title: z.string().trim().min(1),
11179
+ description: z.string().nullable(),
11180
+ timezone: z.string().trim().min(1).nullable(),
11181
+ locationValue: locationValueSchema.nullable().optional(),
11182
+ addressJson: z.record(z.string(), z.unknown()).optional(),
11183
+ locationJson: z.object({
11184
+ lat: z.number(),
11185
+ lng: z.number()
11186
+ }).nullable().optional()
11187
+ })
11188
+ ]);
11189
+ const appointmentSetupPaymentDraftSchema = z.discriminatedUnion("kind", [
11190
+ z.object({ kind: z.literal("free") }),
11191
+ z.object({ kind: z.literal("paid_online") }),
11192
+ z.object({ kind: z.literal("paid_offline") })
11193
+ ]);
11194
+ const appointmentSetupServiceAssignmentDraftSchema = z.discriminatedUnion("kind", [z.object({ kind: z.literal("all_practitioners") }), z.object({
11195
+ kind: z.literal("selected_practitioners"),
11196
+ resourceRefs: z.array(appointmentSetupRefSchema)
11197
+ })]);
11198
+ const appointmentSetupServiceDraftSchema = z.object({
11199
+ ref: appointmentSetupRefSchema,
11200
+ title: z.string().trim().min(1),
11201
+ description: z.string().nullable(),
11202
+ durationMinutes: z.number().int().positive().max(APPOINTMENT_SETUP_MAX_MINUTES),
11203
+ prepMinutes: z.number().int().nonnegative().max(APPOINTMENT_SETUP_MAX_MINUTES).default(0),
11204
+ cleanupMinutes: z.number().int().nonnegative().max(APPOINTMENT_SETUP_MAX_MINUTES).default(0),
11205
+ priceAmount: z.number().int().nonnegative().nullable(),
11206
+ isActive: z.boolean(),
11207
+ visibility: z.enum(APPOINTMENT_SERVICE_VISIBILITY_VALUES).default("public"),
11208
+ locationType: z.union([
11209
+ z.literal("virtual"),
11210
+ z.literal("in_person"),
11211
+ z.literal("hybrid")
11212
+ ]),
11213
+ venue: appointmentSetupVenueChoiceSchema,
11214
+ timezone: z.string().trim().min(1).nullable(),
11215
+ payment: appointmentSetupPaymentDraftSchema,
11216
+ assignment: appointmentSetupServiceAssignmentDraftSchema,
11217
+ categoryId: z.string().uuid().nullable().optional()
11218
+ }).superRefine((service, ctx) => {
11219
+ if (service.payment.kind !== "free" && (service.priceAmount === null || service.priceAmount <= 0)) {
11220
+ ctx.addIssue({
11221
+ code: "custom",
11222
+ path: ["priceAmount"],
11223
+ message: "Paid appointment services need a positive price."
11224
+ });
11225
+ }
11226
+ if (service.payment.kind === "free" && service.priceAmount !== null && service.priceAmount > 0) {
11227
+ ctx.addIssue({
11228
+ code: "custom",
11229
+ path: ["priceAmount"],
11230
+ message: "Free appointment services cannot carry a positive price."
11231
+ });
11232
+ }
11233
+ });
11234
+ const appointmentSetupPractitionerDraftSchema = z.object({
11235
+ ref: appointmentSetupRefSchema,
11236
+ displayName: z.string().trim().min(1),
11237
+ bio: z.string().nullable(),
11238
+ timezone: z.string().trim().min(1),
11239
+ colorHex: z.string().nullable()
11240
+ });
11241
+ const appointmentSetupWeeklyWindowDraftSchema = z.object({
11242
+ weekday: z.number().int().min(0).max(6),
11243
+ startTime: z.string(),
11244
+ endTime: z.string()
11245
+ });
11246
+ const appointmentSetupStartTimePolicyDraftSchema = z.discriminatedUnion("kind", [z.object({ kind: z.literal("automatic_spacing") }), z.object({
11247
+ kind: z.literal("regular_interval"),
11248
+ intervalMinutes: z.union([
11249
+ z.literal(10),
11250
+ z.literal(15),
11251
+ z.literal(20),
11252
+ z.literal(30),
11253
+ z.literal(45),
11254
+ z.literal(60)
11255
+ ])
11256
+ })]);
11257
+ const appointmentSetupAvailabilityDraftSchema = z.object({
11258
+ kind: z.literal("weekly_windows"),
11259
+ resourceRef: appointmentSetupRefSchema,
11260
+ serviceRef: appointmentSetupRefSchema.nullable(),
11261
+ windows: z.array(appointmentSetupWeeklyWindowDraftSchema),
11262
+ startTimePolicy: appointmentSetupStartTimePolicyDraftSchema.optional()
11263
+ });
11264
+ /**
11265
+ * Upper bounds on the booking-notice fields. Site-wide defaults — these caps
11266
+ * keep the slot engine arithmetic (`now + minutes * 60_000`) well inside safe
11267
+ * integer range and surface UI typos rather than silently storing nonsense.
11268
+ *
11269
+ * - `MIN_LEAD` capped at 28 days. A "must book at least N minutes ahead"
11270
+ * value beyond that reads as misconfiguration.
11271
+ * - `MAX_NOTICE` capped at 10 years. A reasonable ceiling for "how far in
11272
+ * the future bookings may be made"; safe against overflow.
11273
+ *
11274
+ * Mirror these in `bookingSettingsSchema` (storage layer) and the wizard
11275
+ * draft schemas — single source of truth for the contract.
11276
+ */
11277
+ const APPOINTMENT_DEFAULT_MIN_LEAD_MINUTES_MAX = 28 * 24 * 60;
11278
+ const APPOINTMENT_DEFAULT_MAX_BOOKING_NOTICE_MINUTES_MAX = 10 * 365 * 24 * 60;
11279
+ const appointmentSetupBookingNoticeSchema = z.object({
11280
+ minLeadMinutes: z.number().int().nonnegative().max(APPOINTMENT_DEFAULT_MIN_LEAD_MINUTES_MAX),
11281
+ maxBookingNoticeMinutes: z.number().int().nonnegative().max(APPOINTMENT_DEFAULT_MAX_BOOKING_NOTICE_MINUTES_MAX)
11282
+ });
11283
+ const applyAppointmentSetupRequestSchema = z.object({
11284
+ services: z.array(appointmentSetupServiceDraftSchema),
11285
+ practitioners: z.array(appointmentSetupPractitionerDraftSchema),
11286
+ availability: z.array(appointmentSetupAvailabilityDraftSchema),
11287
+ bookingNotice: appointmentSetupBookingNoticeSchema
11288
+ });
11289
+ async function getAppointmentSetupRequest(apiClient, siteId, options) {
11290
+ try {
11291
+ return unwrapResponse(await apiClient({
11292
+ endpoint: "getAppointmentSetup",
11293
+ params: { siteId },
11294
+ options
11295
+ }));
11296
+ } catch (error) {
11297
+ normalizeApiError(error, "Failed to load appointment setup");
11298
+ }
11299
+ }
11300
+ async function applyAppointmentSetupRequest(apiClient, siteId, payload, options) {
11301
+ try {
11302
+ return unwrapResponse(await apiClient({
11303
+ endpoint: "applyAppointmentSetup",
11304
+ params: { siteId },
11305
+ body: payload,
11306
+ options
11307
+ }));
11308
+ } catch (error) {
11309
+ normalizeApiError(error, "Failed to save appointment setup");
11310
+ }
11311
+ }
11312
+
11313
+ //#endregion
11314
+ //#region ../api/src/bookingConfig.ts
11315
+ /** Every option kind; the checkout routes validate the requested kind against it. */
11316
+ const DIRECT_CHECKOUT_COLLECTION_OPTION_KINDS = [
11317
+ "upfront",
11318
+ "deferred_manual",
11319
+ "instalment_plan",
11320
+ "flexible_balance"
11321
+ ];
11322
+ const eventOccurrenceRestorationSummarySchema = z.object({
11323
+ restoredOccurrenceIds: z.array(z.string().uuid()),
11324
+ excludedBookings: z.array(z.object({
11325
+ attendeeId: z.string().uuid(),
11326
+ occurrenceId: z.string().uuid(),
11327
+ customerId: z.string().uuid(),
11328
+ customerName: z.string().nullable(),
11329
+ customerEmail: z.string().nullable()
11330
+ }))
11331
+ });
11332
+ const DEFAULT_BOOKING_NOTIFICATION_SEND_RULE = {
11333
+ kind: "send",
11334
+ sendToAssignedStaff: true,
11335
+ extraEmail: null,
11336
+ sendToSiteOwnerIfNoStaff: true
11337
+ };
10943
11338
  const DEFAULT_BOOKING_NOTIFICATION_SETTINGS = {
10944
11339
  state: "configured",
10945
11340
  defaultRule: DEFAULT_BOOKING_NOTIFICATION_SEND_RULE,
@@ -11854,59 +12249,2311 @@ function paginateItems(items, options) {
11854
12249
  }
11855
12250
  };
11856
12251
  }
11857
- function mapPulledPage(page) {
11858
- return {
11859
- id: page.id,
11860
- identifier: page.identifier,
11861
- title: page.title,
11862
- ...page.seoTitle !== undefined ? { seoTitle: page.seoTitle } : {},
11863
- ...page.seoDescription !== undefined ? { seoDescription: page.seoDescription } : {},
11864
- path: page.path,
11865
- status: page.status,
11866
- hasUnpublishedChanges: page.hasUnpublishedChanges,
11867
- createdAt: page.createdAt,
11868
- updatedAt: page.updatedAt,
11869
- publishedAt: page.publishedAt
11870
- };
12252
+ function mapPulledPage(page) {
12253
+ return {
12254
+ id: page.id,
12255
+ identifier: page.identifier,
12256
+ title: page.title,
12257
+ ...page.seoTitle !== undefined ? { seoTitle: page.seoTitle } : {},
12258
+ ...page.seoDescription !== undefined ? { seoDescription: page.seoDescription } : {},
12259
+ path: page.path,
12260
+ status: page.status,
12261
+ hasUnpublishedChanges: page.hasUnpublishedChanges,
12262
+ createdAt: page.createdAt,
12263
+ updatedAt: page.updatedAt,
12264
+ publishedAt: page.publishedAt
12265
+ };
12266
+ }
12267
+ function createPageOperations(http) {
12268
+ async function upsert(input) {
12269
+ if (input.publication === "publish") {
12270
+ return sdkUpsertPagePublishResponseSchema.parse(await http.post("/pages", input, "write"));
12271
+ }
12272
+ return sdkUpsertPageDraftResponseSchema.parse(await http.post("/pages", input, "write"));
12273
+ }
12274
+ return {
12275
+ async list(options) {
12276
+ const result = await http.get("/pull/pages");
12277
+ return paginateItems(result.pages.map(mapPulledPage), options);
12278
+ },
12279
+ async get(identifier) {
12280
+ const result = await http.get("/pull/pages");
12281
+ const page = result.pages.find((entry) => entry.identifier === identifier);
12282
+ return page ? mapPulledPage(page) : null;
12283
+ },
12284
+ upsert,
12285
+ async publish(identifier) {
12286
+ return http.post(`/pages/${encodeURIComponent(identifier)}/publish`, undefined, "write");
12287
+ },
12288
+ async unpublish(identifier) {
12289
+ return http.post(`/pages/${encodeURIComponent(identifier)}/unpublish`, undefined, "write");
12290
+ },
12291
+ async delete(identifier) {
12292
+ return http.delete(`/pages/${encodeURIComponent(identifier)}`);
12293
+ },
12294
+ async getDesign(identifier) {
12295
+ return http.getValidated(designPath(identifier), sdkGetPageDesignResponseSchema);
12296
+ },
12297
+ async saveDesign(identifier, design, expectedRevision) {
12298
+ return http.postValidated(designPath(identifier), sdkSavePageDesignResponseSchema, {
12299
+ design,
12300
+ expectedRevision
12301
+ }, "write");
12302
+ }
12303
+ };
12304
+ }
12305
+
12306
+ //#endregion
12307
+ //#region ../theme-core/src/footer/mode.ts
12308
+ const footerModeSchema = z.enum([
12309
+ "default",
12310
+ "blocks",
12311
+ "default+blocks",
12312
+ "none"
12313
+ ]);
12314
+ /** Reads only SDK footer intent; unrelated config and theme fields are not authorities. */
12315
+ function resolveSdkFooterMode(config) {
12316
+ const parsed = z.object({ layout: z.object({ footerMode: footerModeSchema.optional() }).optional() }).safeParse(config);
12317
+ return parsed.success ? parsed.data.layout?.footerMode ?? "default" : "default";
12318
+ }
12319
+ const legacyFooterBrandSchema = z.object({
12320
+ variant: z.enum([
12321
+ "simple",
12322
+ "columns",
12323
+ "split",
12324
+ "centred"
12325
+ ]),
12326
+ showLogoText: z.boolean().optional(),
12327
+ logo: z.object({
12328
+ showLogo: z.boolean().optional(),
12329
+ showLogoText: z.boolean().optional()
12330
+ }).optional()
12331
+ });
12332
+ /** Offline composition backfill only. Never read appearance visibility at runtime. */
12333
+ function readLegacyFooterBrandVisibility(value) {
12334
+ const parsed = legacyFooterBrandSchema.safeParse(value);
12335
+ if (!parsed.success) return null;
12336
+ const footer = parsed.data;
12337
+ return {
12338
+ showLogo: footer.variant === "simple" ? footer.logo?.showLogo === true : footer.logo?.showLogo !== false,
12339
+ showTitle: footer.logo?.showLogoText ?? footer.showLogoText === true
12340
+ };
12341
+ }
12342
+ /**
12343
+ * Resolve what to render for the footer given the current mode and whether the
12344
+ * site is SDK-managed.
12345
+ *
12346
+ * Backwards compatible: missing/undefined mode is treated as 'default'.
12347
+ */
12348
+ function resolveFooterRenderPlan(params) {
12349
+ if (!params.isSdkSite) {
12350
+ return {
12351
+ mode: "default",
12352
+ renderDefaultFooter: true,
12353
+ renderFooterBlocks: false
12354
+ };
12355
+ }
12356
+ const mode = params.mode ?? "default";
12357
+ switch (mode) {
12358
+ case "blocks": return {
12359
+ mode: "blocks",
12360
+ renderDefaultFooter: false,
12361
+ renderFooterBlocks: true
12362
+ };
12363
+ case "default+blocks": return {
12364
+ mode: "default+blocks",
12365
+ renderDefaultFooter: true,
12366
+ renderFooterBlocks: true
12367
+ };
12368
+ case "none": return {
12369
+ mode: "none",
12370
+ renderDefaultFooter: false,
12371
+ renderFooterBlocks: false
12372
+ };
12373
+ case "default":
12374
+ default: return {
12375
+ mode: "default",
12376
+ renderDefaultFooter: true,
12377
+ renderFooterBlocks: false
12378
+ };
12379
+ }
12380
+ }
12381
+
12382
+ //#endregion
12383
+ //#region ../blocks/src/system/node/schema.ts
12384
+ const transformStepSchema = z.object({
12385
+ id: z.string().min(1, "Transform requires an identifier"),
12386
+ options: z.record(z.string(), z.unknown()).optional()
12387
+ });
12388
+ const bindingPathSchema = z.string().min(1, "Binding path is required").regex(/[A-Za-z0-9_\.$\[\]/-]+/, "Binding path contains invalid characters");
12389
+ const bindingSchema = z.object({
12390
+ from: bindingPathSchema,
12391
+ fallback: z.unknown().optional(),
12392
+ transforms: transformStepSchema.array().default([]),
12393
+ pick: z.enum([
12394
+ "value",
12395
+ "collection",
12396
+ "context"
12397
+ ]).default("value"),
12398
+ coerce: z.enum([
12399
+ "number",
12400
+ "boolean",
12401
+ "string"
12402
+ ]).optional()
12403
+ });
12404
+ const repeatSchema = z.object({
12405
+ collection: bindingSchema,
12406
+ itemName: z.string().min(1).default("item"),
12407
+ indexName: z.string().min(1).default("index"),
12408
+ limit: z.number().int().positive().optional(),
12409
+ sortBy: z.object({
12410
+ path: bindingPathSchema,
12411
+ direction: z.enum(["asc", "desc"]).default("asc")
12412
+ }).optional()
12413
+ });
12414
+ const conditionalAuthoringPresenceSchema = z.discriminatedUnion("kind", [z.object({ kind: z.literal("visitor-only") }), z.object({
12415
+ kind: z.literal("recover-empty-when-active"),
12416
+ placeholder: z.string().min(1).optional()
12417
+ })]);
12418
+ const conditionSchema = z.object({
12419
+ when: bindingSchema,
12420
+ equals: z.unknown().optional(),
12421
+ not: z.boolean().default(false),
12422
+ authoringPresence: conditionalAuthoringPresenceSchema.default({ kind: "visitor-only" })
12423
+ });
12424
+ const dataScopeSchema = z.object({
12425
+ name: z.string().min(1),
12426
+ from: bindingSchema
12427
+ });
12428
+ const nodePropsSchema = z.record(z.string(), z.unknown()).default({});
12429
+ const nodeSchema = z.object({
12430
+ type: z.string().min(1, "Node type is required"),
12431
+ key: z.string().optional(),
12432
+ props: nodePropsSchema.optional(),
12433
+ children: z.lazy(() => nodeSchema.array().default([])).optional(),
12434
+ $bind: bindingSchema.optional(),
12435
+ $repeat: repeatSchema.optional(),
12436
+ $when: conditionSchema.optional(),
12437
+ $scopes: dataScopeSchema.array().optional()
12438
+ });
12439
+ const nodeTreeSchema = nodeSchema.array().or(nodeSchema);
12440
+ const NodeSchema = nodeSchema;
12441
+ const NodeTreeSchema = nodeTreeSchema;
12442
+
12443
+ //#endregion
12444
+ //#region ../blocks/src/system/data/loaderEndpoints.ts
12445
+ const SUPPORTED_LOADER_ENDPOINTS = [
12446
+ "listPublishedEntries",
12447
+ "getPublishedEntryPreview",
12448
+ "listPublicEvents",
12449
+ "listPublicEventCalendarWindow",
12450
+ "listPublicRecurringSchedule",
12451
+ "getPublicEventDetails",
12452
+ "listPublicRouteCandidates",
12453
+ "listPublicTeamMembers",
12454
+ "listPublicProducts",
12455
+ "listPublicPasses",
12456
+ "listPublicMemberships",
12457
+ "getPublicFormById",
12458
+ "getPublicBookingServices",
12459
+ "getPublicWeeklySlotAvailability"
12460
+ ];
12461
+ function isSupportedLoaderEndpoint(endpoint) {
12462
+ return SUPPORTED_LOADER_ENDPOINTS.includes(endpoint);
12463
+ }
12464
+
12465
+ //#endregion
12466
+ //#region ../blocks/src/system/constants/blockKinds.ts
12467
+ const EVENT_DISPLAY_BLOCK_KINDS = [
12468
+ "block.event-details",
12469
+ "block.event-spotlight",
12470
+ "block.event-listing",
12471
+ "block.event-calendar",
12472
+ "block.recurring-schedule",
12473
+ "block.offerings-preview"
12474
+ ];
12475
+ const SYSTEM_BLOCK_KINDS = [
12476
+ "block.hero",
12477
+ "block.intro",
12478
+ "block.columns",
12479
+ "block.collection",
12480
+ "block.feature-cards",
12481
+ "block.media-text",
12482
+ "block.cta-full",
12483
+ "block.single-button",
12484
+ "block.body-text",
12485
+ "block.location-map",
12486
+ "block.faq",
12487
+ "block.testimonials",
12488
+ "block.team-members",
12489
+ "block.embed",
12490
+ "block.blog-post",
12491
+ "block.blog-listing",
12492
+ "block.site-header",
12493
+ "block.site-footer",
12494
+ "block.form",
12495
+ "block.contact-newsletter",
12496
+ "block.appointment-booking",
12497
+ "block.appointment-services",
12498
+ "block.weekly-slot-availability",
12499
+ "block.event-registration",
12500
+ ...EVENT_DISPLAY_BLOCK_KINDS,
12501
+ "block.shop",
12502
+ "block.product-list",
12503
+ "block.product-detail",
12504
+ "block.cart",
12505
+ "block.checkout",
12506
+ "block.gifting",
12507
+ "block.newsletter-signup",
12508
+ "block.image-gallery",
12509
+ "block.video-grid",
12510
+ "block.audio-player",
12511
+ "block.file-download"
12512
+ ];
12513
+
12514
+ //#endregion
12515
+ //#region ../blocks/src/system/node/typeBasedLayout.ts
12516
+ /**
12517
+ * Creates conditional layout branches based on item type.
12518
+ * Used for polymorphic repeaters where each item type has a different layout.
12519
+ *
12520
+ * @param itemTypesMap - Map of type IDs to their layout definitions
12521
+ * @param options - Configuration options
12522
+ * @returns Array of conditional node definitions
12523
+ *
12524
+ * @example
12525
+ * ```typescript
12526
+ * // In a repeater with items: [{_type: 'card', ...}, {_type: 'quote', ...}]
12527
+ * const children = typeBasedLayout({
12528
+ * card: stack({}, [
12529
+ * text({ as: 'h3' }, bind('item.title')),
12530
+ * richText({}, bind('item.body'))
12531
+ * ]),
12532
+ * quote: stack({}, [
12533
+ * richText({}, bind('item.text')),
12534
+ * text({ className: 'author' }, bind('item.author'))
12535
+ * ])
12536
+ * }, { itemName: 'item' });
12537
+ * ```
12538
+ */
12539
+ function typeBasedLayout(itemTypesMap, options) {
12540
+ const itemName = options?.itemName ?? "item";
12541
+ const result = Object.entries(itemTypesMap).map(([typeId, layout]) => {
12542
+ const node = Array.isArray(layout) ? layout.length === 0 ? {
12543
+ type: "div",
12544
+ children: []
12545
+ } : layout.length === 1 ? layout[0] ?? {
12546
+ type: "div",
12547
+ children: []
12548
+ } : {
12549
+ type: "div",
12550
+ children: layout
12551
+ } : layout;
12552
+ return {
12553
+ ...node,
12554
+ $when: {
12555
+ when: { from: `${itemName}._type` },
12556
+ equals: typeId
12557
+ }
12558
+ };
12559
+ });
12560
+ return result;
12561
+ }
12562
+
12563
+ //#endregion
12564
+ //#region ../blocks/src/system/node/validation-constants.ts
12565
+ /**
12566
+ * Constants for validating declarative node layouts.
12567
+ *
12568
+ * These whitelists define which node types and transform IDs
12569
+ * are allowed in custom block layouts.
12570
+ */
12571
+ /**
12572
+ * Valid node types for declarative layouts.
12573
+ *
12574
+ * This whitelist defines which component types can be used in
12575
+ * SdkCustomBlock.layout definitions. Node types not in this list
12576
+ * will be rejected during validation.
12577
+ */
12578
+ const ALLOWED_NODE_TYPES = [
12579
+ "div",
12580
+ "figure",
12581
+ "section",
12582
+ "headerSection",
12583
+ "stack",
12584
+ "inline",
12585
+ "grid",
12586
+ "text",
12587
+ "richText",
12588
+ "media",
12589
+ "videoEmbed",
12590
+ "icon",
12591
+ "feature-card-icon",
12592
+ "svg",
12593
+ "button",
12594
+ "link",
12595
+ "carousel",
12596
+ "accordion",
12597
+ "accordionItem",
12598
+ "form",
12599
+ "booking-form",
12600
+ "weekly-slot-availability",
12601
+ "event-registration",
12602
+ "event-spotlight",
12603
+ "event-listing",
12604
+ "event-calendar",
12605
+ "offerings-preview",
12606
+ "event-combined",
12607
+ "shop",
12608
+ "product-list",
12609
+ "product-detail",
12610
+ "cart",
12611
+ "checkout",
12612
+ "gifting"
12613
+ ];
12614
+ /**
12615
+ * Valid transform IDs for declarative layouts.
12616
+ *
12617
+ * This whitelist defines which transforms can be referenced in
12618
+ * $bind.transforms within custom block layouts. Transform IDs
12619
+ * not in this list will be rejected during validation.
12620
+ */
12621
+ const ALLOWED_TRANSFORM_IDS = [
12622
+ "string.uppercase",
12623
+ "value.fallback",
12624
+ "date.formatShort",
12625
+ "array.length",
12626
+ "value.jsonArray",
12627
+ "value.csv",
12628
+ "array.marqueeLoop",
12629
+ "value.mapToken",
12630
+ "layout.maxWidthClass",
12631
+ "layout.headerRootClass",
12632
+ "layout.footerRootClass",
12633
+ "layout.headerRootStyle",
12634
+ "layout.headerSectionBackground",
12635
+ "layout.footerRootStyle",
12636
+ "ui.headerNavLinkClass",
12637
+ "ui.headingClassFromAlignment",
12638
+ "ui.bodyClassFromAlignment",
12639
+ "ui.stackAlignFromAlignment",
12640
+ "ui.sectionStateTone",
12641
+ "ui.heroContentWrapperClass",
12642
+ "ui.heroCtaJustify",
12643
+ "ui.heroStackJustifyFromVerticalAlignment",
12644
+ "ui.heroGridClassFromVerticalAlignment",
12645
+ "ui.conditionalClass",
12646
+ "ui.twoColumnContainerClass",
12647
+ "ui.twoColumnFlexBasis",
12648
+ "ui.imageGalleryFilterClass",
12649
+ "ui.imageGalleryTileClass",
12650
+ "ui.imageGalleryTileStyle",
12651
+ "ui.imageGalleryMarqueeTileStyle",
12652
+ "ui.imageGalleryMarqueeDuration",
12653
+ "ui.imageGalleryMarqueeDirection",
12654
+ "ui.imageFragmentClass",
12655
+ "ui.imageFragmentSize",
12656
+ "media.fromUrl",
12657
+ "background.resolveStyle",
12658
+ "background.resolveColorsStyle",
12659
+ "background.resolveFrameClassName",
12660
+ "background.resolveImageStyle",
12661
+ "background.resolveImageClassName",
12662
+ "background.resolveOverlayStyle",
12663
+ "links.resolve",
12664
+ "links.label",
12665
+ "content.interpolateRichTextTokens",
12666
+ "boxStyles.spacing",
12667
+ "boxStyles.resolveRootClass",
12668
+ "boxStyles.resolveRootStyle",
12669
+ "boxStyles.semanticGap",
12670
+ "boxStyles.semanticGapValue",
12671
+ "containerStyles.resolveBoxClassName"
12672
+ ];
12673
+ /**
12674
+ * Set for O(1) lookup of allowed node types.
12675
+ */
12676
+ const ALLOWED_NODE_TYPES_SET = new Set(ALLOWED_NODE_TYPES);
12677
+ /**
12678
+ * Set for O(1) lookup of allowed transform IDs.
12679
+ */
12680
+ const ALLOWED_TRANSFORM_IDS_SET = new Set(ALLOWED_TRANSFORM_IDS);
12681
+ /**
12682
+ * Check if a node type is allowed in layouts.
12683
+ */
12684
+ function isAllowedNodeType(type) {
12685
+ return ALLOWED_NODE_TYPES_SET.has(type);
12686
+ }
12687
+ /**
12688
+ * Check if a transform ID is allowed in layouts.
12689
+ */
12690
+ function isAllowedTransformId(id) {
12691
+ return ALLOWED_TRANSFORM_IDS_SET.has(id);
12692
+ }
12693
+
12694
+ //#endregion
12695
+ //#region ../blocks/src/system/layout/repeaterGridModel.ts
12696
+ const DEFAULT_REPEATER_GRID_SPAN_FIELD_ID = "gridSpan";
12697
+ const DEFAULT_REPEATER_GRID_ROW_FIELD_ID = "gridRow";
12698
+ const DEFAULT_REPEATER_GRID_COLUMN_FIELD_ID = "gridColumn";
12699
+ const MIN_COLUMNS = 1;
12700
+ const MAX_REPEATER_GRID_COLUMNS = 6;
12701
+ const MAX_COLUMNS = MAX_REPEATER_GRID_COLUMNS;
12702
+ function normalizeInteger(value) {
12703
+ if (typeof value === "number" && Number.isFinite(value)) {
12704
+ return Math.trunc(value);
12705
+ }
12706
+ if (typeof value === "string") {
12707
+ const trimmed = value.trim();
12708
+ if (!trimmed) return null;
12709
+ const parsed = Number(trimmed);
12710
+ if (!Number.isFinite(parsed)) return null;
12711
+ return Math.trunc(parsed);
12712
+ }
12713
+ return null;
12714
+ }
12715
+ function clamp(value, min, max) {
12716
+ return Math.max(min, Math.min(max, value));
12717
+ }
12718
+ function asNonEmptyString(value) {
12719
+ if (typeof value !== "string") return null;
12720
+ const trimmed = value.trim();
12721
+ return trimmed.length > 0 ? trimmed : null;
12722
+ }
12723
+ function resolveRepeaterGridSpanFieldId(options) {
12724
+ return asNonEmptyString(options?.id) ?? DEFAULT_REPEATER_GRID_SPAN_FIELD_ID;
12725
+ }
12726
+ function resolveRepeaterGridRowFieldId(options) {
12727
+ return asNonEmptyString(options?.id) ?? DEFAULT_REPEATER_GRID_ROW_FIELD_ID;
12728
+ }
12729
+ function resolveRepeaterGridColumnFieldId(options) {
12730
+ return asNonEmptyString(options?.id) ?? DEFAULT_REPEATER_GRID_COLUMN_FIELD_ID;
12731
+ }
12732
+ function getBreakpointFallbackOrder(breakpoint) {
12733
+ if (breakpoint === "xl") return [
12734
+ "xl",
12735
+ "lg",
12736
+ "md",
12737
+ "sm",
12738
+ "base"
12739
+ ];
12740
+ if (breakpoint === "lg") return [
12741
+ "lg",
12742
+ "md",
12743
+ "sm",
12744
+ "base"
12745
+ ];
12746
+ if (breakpoint === "md") return [
12747
+ "md",
12748
+ "sm",
12749
+ "base",
12750
+ "lg"
12751
+ ];
12752
+ if (breakpoint === "sm") return [
12753
+ "sm",
12754
+ "base",
12755
+ "lg"
12756
+ ];
12757
+ return ["base", "lg"];
12758
+ }
12759
+ function resolveRepeaterGridBreakpointForWidth(width) {
12760
+ if (!Number.isFinite(width)) {
12761
+ return "lg";
12762
+ }
12763
+ if (width >= BREAKPOINT_XL) return "xl";
12764
+ if (width >= BREAKPOINT_LG) return "lg";
12765
+ if (width >= BREAKPOINT_MD) return "md";
12766
+ if (width >= BREAKPOINT_SM) return "sm";
12767
+ return "base";
12768
+ }
12769
+ function createRepeaterGridSpanField(options) {
12770
+ const fieldId = resolveRepeaterGridSpanFieldId(options);
12771
+ const label = asNonEmptyString(options?.label) ?? "Grid span";
12772
+ const description = asNonEmptyString(options?.description) ?? "Columns occupied by this item in layout-grid mode.";
12773
+ const defaultSpan = clamp(normalizeRepeaterGridColumns(options?.defaultSpan ?? 1), 1, 4);
12774
+ return {
12775
+ id: fieldId,
12776
+ type: "select",
12777
+ label,
12778
+ description,
12779
+ options: [
12780
+ {
12781
+ value: "1",
12782
+ label: "1 column"
12783
+ },
12784
+ {
12785
+ value: "2",
12786
+ label: "2 columns"
12787
+ },
12788
+ {
12789
+ value: "3",
12790
+ label: "3 columns"
12791
+ },
12792
+ {
12793
+ value: "4",
12794
+ label: "4 columns"
12795
+ }
12796
+ ],
12797
+ defaultValue: String(defaultSpan)
12798
+ };
12799
+ }
12800
+ function createRepeaterGridRowField(options) {
12801
+ const fieldId = resolveRepeaterGridRowFieldId(options);
12802
+ const label = asNonEmptyString(options?.label) ?? "Grid row";
12803
+ const description = asNonEmptyString(options?.description) ?? "Manual layout row for this item.";
12804
+ const defaultRow = normalizeRepeaterGridRow(options?.defaultRow ?? 1);
12805
+ return {
12806
+ id: fieldId,
12807
+ type: "number",
12808
+ label,
12809
+ description,
12810
+ defaultValue: defaultRow,
12811
+ ui: { hidden: true }
12812
+ };
12813
+ }
12814
+ function createRepeaterGridColumnField(options) {
12815
+ const fieldId = resolveRepeaterGridColumnFieldId(options);
12816
+ const label = asNonEmptyString(options?.label) ?? "Grid column";
12817
+ const description = asNonEmptyString(options?.description) ?? "Manual layout starting column for this item.";
12818
+ const defaultColumn = normalizeRepeaterGridColumns(options?.defaultColumn ?? 1);
12819
+ return {
12820
+ id: fieldId,
12821
+ type: "number",
12822
+ label,
12823
+ description,
12824
+ defaultValue: defaultColumn,
12825
+ ui: { hidden: true }
12826
+ };
12827
+ }
12828
+ function withRepeaterGridSpanField(fields, options) {
12829
+ const spanFieldId = resolveRepeaterGridSpanFieldId(options);
12830
+ if (fields.some((field) => field.id === spanFieldId)) {
12831
+ return [...fields];
12832
+ }
12833
+ return [createRepeaterGridSpanField(options), ...fields];
12834
+ }
12835
+ function withRepeaterGridPositionFields(fields, options) {
12836
+ const rowFieldId = resolveRepeaterGridRowFieldId(options?.row);
12837
+ const withRow = fields.some((field) => field.id === rowFieldId) ? [...fields] : [createRepeaterGridRowField(options?.row), ...fields];
12838
+ const columnFieldId = resolveRepeaterGridColumnFieldId(options?.column);
12839
+ return withRow.some((field) => field.id === columnFieldId) ? withRow : [createRepeaterGridColumnField(options?.column), ...withRow];
12840
+ }
12841
+ function withRepeaterGridSpanFieldOnItemTypes(itemTypes, options) {
12842
+ const entries = Object.entries(itemTypes).map(([typeKey, itemType]) => {
12843
+ const nextFields = withRepeaterGridSpanField(itemType.fields, options);
12844
+ return [typeKey, {
12845
+ ...itemType,
12846
+ fields: nextFields
12847
+ }];
12848
+ });
12849
+ return Object.fromEntries(entries);
12850
+ }
12851
+ function withRepeaterGridPositionFieldsOnItemTypes(itemTypes, options) {
12852
+ const entries = Object.entries(itemTypes).map(([typeKey, itemType]) => {
12853
+ const nextFields = withRepeaterGridPositionFields(itemType.fields, options);
12854
+ return [typeKey, {
12855
+ ...itemType,
12856
+ fields: nextFields
12857
+ }];
12858
+ });
12859
+ return Object.fromEntries(entries);
12860
+ }
12861
+ function normalizeRepeaterGridColumns(value) {
12862
+ const parsed = normalizeInteger(value);
12863
+ if (parsed == null) {
12864
+ return MIN_COLUMNS;
12865
+ }
12866
+ return clamp(parsed, MIN_COLUMNS, MAX_COLUMNS);
12867
+ }
12868
+ function normalizeRepeaterGridRow(value) {
12869
+ const parsed = normalizeInteger(value);
12870
+ if (parsed == null) {
12871
+ return 1;
12872
+ }
12873
+ return Math.max(1, parsed);
12874
+ }
12875
+ function resolveRepeaterGridColumnsForBreakpoint(values, bindings, breakpoint) {
12876
+ const order = getBreakpointFallbackOrder(breakpoint);
12877
+ for (const candidate of order) {
12878
+ const fieldId = asNonEmptyString(bindings[candidate]);
12879
+ if (!fieldId) continue;
12880
+ const resolved = normalizeInteger(values[fieldId]);
12881
+ if (resolved == null) continue;
12882
+ return clamp(resolved, MIN_COLUMNS, MAX_COLUMNS);
12883
+ }
12884
+ return MIN_COLUMNS;
12885
+ }
12886
+ /** Strict admission; rendering normalization never grants permission to persist. */
12887
+ function parseRepeaterGridInteger(value) {
12888
+ if (typeof value !== "number" && (typeof value !== "string" || !value.trim())) return null;
12889
+ const number = Number(value);
12890
+ return Number.isInteger(number) ? number : null;
12891
+ }
12892
+ function isAcceptedRepeaterGridSpan(value, columns, acceptedSpans) {
12893
+ const span = parseRepeaterGridInteger(value);
12894
+ return span != null && span >= 1 && span <= columns && (!acceptedSpans || acceptedSpans.includes(span));
12895
+ }
12896
+ function resolveRepeaterGridAcceptedSpans(fields, spanField) {
12897
+ const field = fields.find((candidate) => candidate.id === spanField);
12898
+ return field?.type === "select" ? (field.options ?? []).map((option) => Number(option.value)) : undefined;
12899
+ }
12900
+ function normalizeRepeaterGridSpan(value, activeColumns, acceptedSpans) {
12901
+ if (acceptedSpans) {
12902
+ const valid = acceptedSpans.filter((span) => span <= activeColumns);
12903
+ const parsed$1 = parseRepeaterGridInteger(value);
12904
+ return parsed$1 != null && valid.includes(parsed$1) ? parsed$1 : valid.length ? Math.min(...valid) : 1;
12905
+ }
12906
+ const parsed = normalizeInteger(value);
12907
+ if (parsed == null) {
12908
+ return 1;
12909
+ }
12910
+ return clamp(parsed, 1, Math.max(1, activeColumns));
12911
+ }
12912
+ function normalizeRepeaterGridColStart(value, activeColumns, span) {
12913
+ const parsed = normalizeInteger(value);
12914
+ const normalizedColumns = Math.max(1, activeColumns);
12915
+ const normalizedSpan = clamp(span, 1, normalizedColumns);
12916
+ const maxColStart = Math.max(1, normalizedColumns - normalizedSpan + 1);
12917
+ if (parsed == null) {
12918
+ return 1;
12919
+ }
12920
+ return clamp(parsed, 1, maxColStart);
12921
+ }
12922
+ function deriveRepeaterGridMaxRowsForBreakpoint(input) {
12923
+ const { desktopColumns, desktopMaxRows, activeColumns } = input;
12924
+ if (desktopMaxRows == null) {
12925
+ return undefined;
12926
+ }
12927
+ const normalizedDesktopColumns = Math.max(1, desktopColumns);
12928
+ const normalizedActiveColumns = Math.max(1, activeColumns);
12929
+ const capacityCells = normalizedDesktopColumns * desktopMaxRows;
12930
+ return Math.max(1, Math.ceil(capacityCells / normalizedActiveColumns));
12931
+ }
12932
+ function toRowCellKey(row, col, columns) {
12933
+ return (row - 1) * columns + col;
12934
+ }
12935
+ function buildPlacementCells(placement) {
12936
+ return Array.from({ length: placement.span }, (_, index) => placement.colStart + index);
12937
+ }
12938
+ function buildOccupancyByCell(placements, columns) {
12939
+ const entries = placements.flatMap((placement) => buildPlacementCells(placement).map((col) => [toRowCellKey(placement.row, col, columns), placement.id]));
12940
+ const occupancy = entries.reduce((cells, [cell, id]) => new Map(cells).set(cell, new Set([...cells.get(cell) ?? [], id])), new Map());
12941
+ return {
12942
+ occupancy,
12943
+ hasCollisions: Array.from(occupancy.values()).some((ids) => ids.size > 1)
12944
+ };
12945
+ }
12946
+ function getOverlappingItemIds(input) {
12947
+ return new Set(buildPlacementCells(input).flatMap((col) => Array.from(input.occupancy.get(toRowCellKey(input.row, col, input.columns)) ?? []).filter((id) => !input.excludedIds?.has(id))));
12948
+ }
12949
+ function normalizeInputItem(item, columns, sourceIndex) {
12950
+ const span = normalizeRepeaterGridSpan(item.span, columns, item.acceptedSpans);
12951
+ const row = normalizeRepeaterGridRow(item.row);
12952
+ const colStart = normalizeRepeaterGridColStart(item.colStart, columns, span);
12953
+ const colEnd = colStart + span - 1;
12954
+ return {
12955
+ id: item.id,
12956
+ startNewRow: item.startNewRow,
12957
+ acceptedSpans: item.acceptedSpans,
12958
+ sourceIndex,
12959
+ span,
12960
+ row,
12961
+ colStart,
12962
+ colEnd,
12963
+ startCell: toRowCellKey(row, colStart, columns)
12964
+ };
12965
+ }
12966
+ /** Ordered, non-dense placement. Rendering clamps widths; admission checks originals separately. */
12967
+ function flowRepeaterGridItems(items, columns) {
12968
+ const count = normalizeRepeaterGridColumns(columns);
12969
+ return items.reduce((placed, item) => {
12970
+ const span = normalizeRepeaterGridSpan(item.span, count);
12971
+ const previous = placed.at(-1);
12972
+ const nextColumn = previous ? previous.colStart + previous.span : 1;
12973
+ const wraps = previous != null && (item.startNewRow === true || nextColumn + span - 1 > count);
12974
+ return [...placed, {
12975
+ ...item,
12976
+ span,
12977
+ row: (previous?.row ?? 1) + (wraps ? 1 : 0),
12978
+ colStart: wraps ? 1 : nextColumn
12979
+ }];
12980
+ }, []);
12981
+ }
12982
+ /** @internal */
12983
+ function orderedGridItems(layout) {
12984
+ return [...layout.placements].sort((a, b) => a.sourceIndex - b.sourceIndex);
12985
+ }
12986
+ function isRepeaterGridLayoutInvalid(layout) {
12987
+ return layout.hasInvalidItems || layout.hasCollisions || layout.overCapacity;
12988
+ }
12989
+ function hasInvalidGridItems(items, columns, policy) {
12990
+ const flow = policy === "flow" ? flowRepeaterGridItems(items, columns) : null;
12991
+ return items.some((item, index) => !isValidGridItemGeometry(item, columns) || flow != null && (item.row !== flow[index]?.row || item.colStart !== flow[index]?.colStart));
12992
+ }
12993
+ function isValidGridItemGeometry(item, columns) {
12994
+ 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;
12995
+ }
12996
+ function findFlowAppendPlacement(layout, span, startNewRow) {
12997
+ const next = flowRepeaterGridItems([...orderedGridItems(layout), {
12998
+ id: "new",
12999
+ span,
13000
+ row: 1,
13001
+ colStart: 1,
13002
+ startNewRow
13003
+ }], layout.columns).at(-1);
13004
+ return !isRepeaterGridLayoutInvalid(layout) && next && (layout.maxRows == null || next.row <= layout.maxRows) ? {
13005
+ row: next.row,
13006
+ colStart: next.colStart
13007
+ } : null;
13008
+ }
13009
+ /** @internal */
13010
+ function planFlowItems(layout, items) {
13011
+ const placed = flowRepeaterGridItems(items, layout.columns);
13012
+ if (isRepeaterGridLayoutInvalid(layout) || placed.some((item) => !isAcceptedRepeaterGridSpan(item.span, layout.columns, item.acceptedSpans) || layout.maxRows != null && item.row > layout.maxRows)) return { kind: "blocked" };
13013
+ const [first, ...rest] = placed.map(({ id, row, colStart, span }) => ({
13014
+ id,
13015
+ row,
13016
+ colStart,
13017
+ span
13018
+ }));
13019
+ return first ? {
13020
+ kind: "move",
13021
+ updates: [first, ...rest]
13022
+ } : { kind: "unchanged" };
13023
+ }
13024
+ /** Insertion reorder, with a complete coordinate write for the resulting order. */
13025
+ function planRepeaterGridReorder(layout, id, to, interaction = "step") {
13026
+ if (layout.placementPolicy !== "flow") return { kind: "blocked" };
13027
+ const items = orderedGridItems(layout);
13028
+ const from = items.findIndex((item) => item.id === id);
13029
+ if (from < 0 || to < 0 || to >= items.length) return { kind: "blocked" };
13030
+ if (from === to) return { kind: "unchanged" };
13031
+ const active = items[from];
13032
+ if (!active) return { kind: "blocked" };
13033
+ return decideFlowReorder({
13034
+ layout,
13035
+ items,
13036
+ active,
13037
+ from,
13038
+ to,
13039
+ interaction
13040
+ });
13041
+ }
13042
+ function decideFlowReorder(input) {
13043
+ const { layout, items, active, from, to, interaction } = input;
13044
+ const id = active.id;
13045
+ const remaining = items.filter((item) => item.id !== id);
13046
+ const inserted = [
13047
+ ...remaining.slice(0, to),
13048
+ active,
13049
+ ...remaining.slice(to)
13050
+ ];
13051
+ const clearRowBreak = interaction === "drag" && active.startNewRow === true && dragInsertsWithinRow(items, from, to, layout.columns);
13052
+ const nextItems = clearRowBreak ? inserted.map((item) => item.id === id ? {
13053
+ ...item,
13054
+ startNewRow: false
13055
+ } : item) : inserted;
13056
+ const decision = planFlowItems(layout, nextItems);
13057
+ return decision.kind === "move" ? {
13058
+ ...decision,
13059
+ reorder: {
13060
+ id,
13061
+ from,
13062
+ to
13063
+ },
13064
+ ...clearRowBreak ? { startNewRowUpdate: {
13065
+ id,
13066
+ value: false
13067
+ } } : {}
13068
+ } : decision;
13069
+ }
13070
+ /** Read the insertion bar position, independently of the dragged width. */
13071
+ function dragInsertsWithinRow(items, from, to, columns) {
13072
+ const target = items[to];
13073
+ if (!target) return false;
13074
+ const column = from < to ? target.colEnd + 1 : target.colStart;
13075
+ return column > 1 && column <= columns;
13076
+ }
13077
+ function planRepeaterGridStartNewRow(layout, id, value) {
13078
+ const active = layout.byId[id];
13079
+ if (layout.placementPolicy !== "flow" || !active) return { kind: "blocked" };
13080
+ const decision = planFlowItems(layout, orderedGridItems(layout).map((item) => item.id === id ? {
13081
+ ...item,
13082
+ startNewRow: value
13083
+ } : item));
13084
+ return decision.kind === "move" ? {
13085
+ ...decision,
13086
+ startNewRowUpdate: {
13087
+ id,
13088
+ value
13089
+ }
13090
+ } : decision;
13091
+ }
13092
+ function buildRepeaterGridLayout(input) {
13093
+ const columns = normalizeRepeaterGridColumns(input.columns);
13094
+ const maxRows = input.maxRows != null ? Math.max(1, Math.trunc(input.maxRows)) : undefined;
13095
+ const capacityCells = maxRows != null ? maxRows * columns : undefined;
13096
+ const placements = input.items.map((item, sourceIndex) => normalizeInputItem(item, columns, sourceIndex)).sort((a, b) => {
13097
+ if (a.row !== b.row) return a.row - b.row;
13098
+ if (a.colStart !== b.colStart) return a.colStart - b.colStart;
13099
+ return a.sourceIndex - b.sourceIndex;
13100
+ });
13101
+ const byId = Object.fromEntries(placements.map((placement) => [placement.id, placement]));
13102
+ const maxRowUsed = placements.length > 0 ? Math.max(...placements.map((placement) => placement.row)) : 0;
13103
+ const { hasCollisions } = buildOccupancyByCell(placements, columns);
13104
+ const overCapacity = maxRows != null && maxRowUsed > maxRows;
13105
+ return {
13106
+ placementPolicy: input.placementPolicy,
13107
+ sourceItems: input.items.map((item) => ({ ...item })),
13108
+ columns,
13109
+ maxRows,
13110
+ capacityCells,
13111
+ maxRowUsed,
13112
+ overCapacity,
13113
+ hasCollisions,
13114
+ hasInvalidItems: hasInvalidGridItems(input.items, columns, input.placementPolicy),
13115
+ placements,
13116
+ byId
13117
+ };
13118
+ }
13119
+ function findFirstAvailableRepeaterGridPlacement(input) {
13120
+ const columns = normalizeRepeaterGridColumns(input.columns);
13121
+ const maxRows = input.maxRows != null ? Math.max(1, Math.trunc(input.maxRows)) : undefined;
13122
+ if (!isAcceptedRepeaterGridSpan(input.newItemSpan, columns, input.acceptedSpans)) return null;
13123
+ const span = normalizeRepeaterGridSpan(input.newItemSpan, columns, input.acceptedSpans);
13124
+ const layout = buildRepeaterGridLayout({
13125
+ items: input.items,
13126
+ columns,
13127
+ maxRows,
13128
+ placementPolicy: input.placementPolicy
13129
+ });
13130
+ if (input.placementPolicy === "flow") return findFlowAppendPlacement(layout, span, input.startNewRow);
13131
+ const { occupancy } = buildOccupancyByCell(layout.placements, columns);
13132
+ const rowLimit = maxRows ?? Math.max(1, layout.maxRowUsed + 1);
13133
+ if (isRepeaterGridLayoutInvalid(layout)) return null;
13134
+ const maxColStart = columns - span + 1;
13135
+ return Array.from({ length: rowLimit * maxColStart }, (_, index) => ({
13136
+ row: Math.floor(index / maxColStart) + 1,
13137
+ colStart: index % maxColStart + 1
13138
+ })).find(({ row, colStart }) => getOverlappingItemIds({
13139
+ occupancy,
13140
+ row,
13141
+ colStart,
13142
+ span,
13143
+ columns
13144
+ }).size === 0) ?? null;
13145
+ }
13146
+ function canAppendRepeaterGridItem(input) {
13147
+ return findFirstAvailableRepeaterGridPlacement({
13148
+ items: input.items,
13149
+ newItemSpan: input.newItemSpan,
13150
+ acceptedSpans: input.acceptedSpans,
13151
+ columns: input.columns,
13152
+ maxRows: input.maxRows
13153
+ }) != null;
13154
+ }
13155
+
13156
+ //#endregion
13157
+ //#region ../blocks/src/system/layout/repeaterGrid.ts
13158
+ function placementsOverlap(left, right) {
13159
+ return left.row === right.row && left.colStart < right.colStart + right.span && right.colStart < left.colStart + left.span;
13160
+ }
13161
+ function swapPositionUpdates(active, target) {
13162
+ const adjacent = active.row === target.row && (active.colEnd + 1 === target.colStart || target.colEnd + 1 === active.colStart);
13163
+ const activeCol = adjacent && active.colStart < target.colStart ? active.colStart + target.span : target.colStart;
13164
+ const targetCol = adjacent && target.colStart < active.colStart ? target.colStart + active.span : active.colStart;
13165
+ return [{
13166
+ id: active.id,
13167
+ row: target.row,
13168
+ colStart: activeCol,
13169
+ span: active.span
13170
+ }, {
13171
+ id: target.id,
13172
+ row: active.row,
13173
+ colStart: targetCol,
13174
+ span: target.span
13175
+ }];
13176
+ }
13177
+ /** Admission retains raw authoring geometry; display-only callers may supply placements alone. */
13178
+ function sourceGridItems(layout) {
13179
+ return layout.sourceItems ?? layout.placements;
13180
+ }
13181
+ /** Repair edits may remove existing defects, but cannot introduce another one. */
13182
+ function gridDefects(layout) {
13183
+ const geometry = sourceGridItems(layout).flatMap((item) => [
13184
+ ...!isAcceptedRepeaterGridSpan(item.span, layout.columns, item.acceptedSpans) ? [`span:${item.id}`] : [],
13185
+ ...!Number.isInteger(item.row) || item.row < 1 ? [`row:${item.id}`] : [],
13186
+ ...!Number.isInteger(item.colStart) || item.colStart < 1 || item.colStart + item.span - 1 > layout.columns ? [`column:${item.id}`] : []
13187
+ ]);
13188
+ 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(":")}`));
13189
+ const capacity = layout.placements.filter((item) => layout.maxRows != null && item.row > layout.maxRows).map((item) => `capacity:${item.id}`);
13190
+ return new Set([
13191
+ ...geometry,
13192
+ ...collisions,
13193
+ ...capacity,
13194
+ ...(layout.invalidCoordinateIds ?? []).map((id) => `coordinate:${id}`)
13195
+ ]);
13196
+ }
13197
+ function validateGridMove(layout, updates) {
13198
+ const items = sourceGridItems(layout).map((item) => ({
13199
+ ...item,
13200
+ ...updates.find((update) => update.id === item.id)
13201
+ }));
13202
+ const candidate = buildRepeaterGridLayout({
13203
+ items,
13204
+ columns: layout.columns,
13205
+ maxRows: layout.maxRows,
13206
+ placementPolicy: layout.placementPolicy
13207
+ });
13208
+ const unresolved = (layout.invalidCoordinateIds ?? []).filter((id) => !updates.some((update) => update.id === id));
13209
+ const before = gridDefects(layout);
13210
+ const after = gridDefects({
13211
+ ...candidate,
13212
+ invalidCoordinateIds: unresolved
13213
+ });
13214
+ const valid = before.size === 0 ? after.size === 0 : after.size < before.size && [...after].every((defect) => before.has(defect));
13215
+ return valid ? {
13216
+ kind: "move",
13217
+ updates
13218
+ } : { kind: "blocked" };
13219
+ }
13220
+ function decideGridDrop(layout, active, destination) {
13221
+ const occupants = layout.placements.filter((placement) => placement.id !== active.id && placement.row === destination.row && destination.colStart >= placement.colStart && destination.colStart <= placement.colEnd);
13222
+ if (occupants.length > 1) return { kind: "blocked" };
13223
+ const target = occupants[0];
13224
+ if (target) return validateGridMove(layout, swapPositionUpdates(active, target));
13225
+ if (destination.row === active.row && destination.colStart === active.colStart && !isRepeaterGridLayoutInvalid(layout)) return { kind: "unchanged" };
13226
+ return validateGridMove(layout, [{
13227
+ id: active.id,
13228
+ row: destination.row,
13229
+ colStart: destination.colStart,
13230
+ span: active.span
13231
+ }]);
13232
+ }
13233
+ function buildRepeaterGridDropTargets(input) {
13234
+ const { layout, draggedId, rowsToRender } = input;
13235
+ const active = layout.byId[draggedId];
13236
+ if (!active || layout.placementPolicy === "flow" && isRepeaterGridLayoutInvalid(layout)) return [];
13237
+ if (layout.placementPolicy === "flow") return orderedGridItems(layout).map((item, index) => ({
13238
+ id: `repeater-grid-drop-${item.row}-${item.colStart}`,
13239
+ row: item.row,
13240
+ colStart: item.colStart,
13241
+ cell: item.startCell,
13242
+ decision: planRepeaterGridReorder(layout, draggedId, index, input.interaction ?? "drag")
13243
+ }));
13244
+ const rowCeiling = layout.maxRows ?? Math.max(1, rowsToRender ?? layout.maxRowUsed + 1, active.row + 1);
13245
+ const targets = Array.from({ length: rowCeiling * layout.columns }, (_, index) => {
13246
+ const row = Math.floor(index / layout.columns) + 1;
13247
+ const colStart = index % layout.columns + 1;
13248
+ return {
13249
+ id: `repeater-grid-drop-${row}-${colStart}`,
13250
+ row,
13251
+ colStart,
13252
+ cell: index + 1,
13253
+ decision: decideGridDrop(layout, active, {
13254
+ row,
13255
+ colStart
13256
+ })
13257
+ };
13258
+ });
13259
+ return targets.some((target) => target.row === active.row && target.colStart === active.colStart) ? targets : [...targets, {
13260
+ id: `repeater-grid-drop-${active.row}-${active.colStart}`,
13261
+ row: active.row,
13262
+ colStart: active.colStart,
13263
+ cell: active.startCell,
13264
+ decision: { kind: "unchanged" }
13265
+ }];
13266
+ }
13267
+ /** Width edits keep the item's start when it fits; otherwise relocate it to the first free cell.
13268
+ * Neighbours keep their positions. A full bounded grid blocks the change without partial writes.
13269
+ */
13270
+ function planRepeaterGridSpanChange(input) {
13271
+ const { layout, id, span } = input;
13272
+ const active = layout.byId[id];
13273
+ if (layout.placementPolicy === "flow" && isRepeaterGridLayoutInvalid(layout)) return { kind: "blocked" };
13274
+ if (!active || !isAcceptedRepeaterGridSpan(span, layout.columns, active.acceptedSpans)) return { kind: "blocked" };
13275
+ const nextSpan = Number(span);
13276
+ if (!isRepeaterGridLayoutInvalid(layout) && sourceGridItems(layout).find((item) => item.id === id)?.span === nextSpan) return { kind: "unchanged" };
13277
+ if (layout.placementPolicy === "flow") return planFlowItems(layout, orderedGridItems(layout).map((item) => item.id === id ? {
13278
+ ...item,
13279
+ span: nextSpan
13280
+ } : item));
13281
+ return planFreeGridSpanChange(layout, active, nextSpan);
13282
+ }
13283
+ /** Free width edits retain the current start when possible, then use the shared first-free planner. */
13284
+ function planFreeGridSpanChange(layout, active, nextSpan) {
13285
+ const id = active.id;
13286
+ const atCurrent = validateGridMove(layout, [{
13287
+ id,
13288
+ span: nextSpan,
13289
+ row: active.row,
13290
+ colStart: active.colStart
13291
+ }]);
13292
+ if (atCurrent.kind === "move") return atCurrent;
13293
+ const destination = findFirstAvailableRepeaterGridPlacement({
13294
+ items: layout.placements.filter((item) => item.id !== id),
13295
+ newItemSpan: nextSpan,
13296
+ acceptedSpans: active.acceptedSpans,
13297
+ columns: layout.columns,
13298
+ maxRows: layout.maxRows
13299
+ });
13300
+ return destination ? validateGridMove(layout, [{
13301
+ id,
13302
+ span: nextSpan,
13303
+ ...destination
13304
+ }]) : { kind: "blocked" };
13305
+ }
13306
+ /** Narrow rendering is a projection only. Never feed these placements into an authoring write. */
13307
+ function projectRepeaterGridLayout(layout, columns) {
13308
+ if (isRepeaterGridLayoutInvalid(layout)) return projectRepeaterGridLayoutForRendering(layout, columns);
13309
+ if (columns === layout.columns) return layout;
13310
+ if (layout.placementPolicy === "flow") return buildProjectedRepeaterGridLayout(layout, flowRepeaterGridItems(orderedGridItems(layout), columns), columns);
13311
+ const items = layout.placements.reduce((placed, item) => {
13312
+ const span = normalizeRepeaterGridSpan(item.span, columns);
13313
+ const destination = findFirstAvailableRepeaterGridPlacement({
13314
+ items: placed,
13315
+ newItemSpan: span,
13316
+ columns
13317
+ });
13318
+ return [...placed, {
13319
+ id: item.id,
13320
+ span,
13321
+ row: destination?.row ?? placed.length + 1,
13322
+ colStart: destination?.colStart ?? 1
13323
+ }];
13324
+ }, []);
13325
+ return buildProjectedRepeaterGridLayout(layout, items, columns);
13326
+ }
13327
+ function buildProjectedRepeaterGridLayout(source, items, columns) {
13328
+ const projected = buildRepeaterGridLayout({
13329
+ items: items.map(({ id, span, row, colStart, startNewRow }) => ({
13330
+ id,
13331
+ span,
13332
+ row,
13333
+ colStart,
13334
+ startNewRow
13335
+ })),
13336
+ columns,
13337
+ placementPolicy: source.placementPolicy === "flow" || isRepeaterGridLayoutInvalid(source) ? "flow" : source.placementPolicy
13338
+ });
13339
+ const placements = projected.placements.map((item) => ({
13340
+ ...item,
13341
+ sourceIndex: source.byId[item.id]?.sourceIndex ?? item.sourceIndex
13342
+ }));
13343
+ return {
13344
+ ...projected,
13345
+ placements,
13346
+ byId: Object.fromEntries(placements.map((item) => [item.id, item]))
13347
+ };
13348
+ }
13349
+ /** Public fallback: preserve saved array order, use non-dense auto-placement, and ignore authoring capacity. */
13350
+ function projectRepeaterGridLayoutForRendering(layout, columns = layout.columns) {
13351
+ if (!isRepeaterGridLayoutInvalid(layout)) {
13352
+ return projectRepeaterGridLayout(layout, columns);
13353
+ }
13354
+ const count = normalizeRepeaterGridColumns(columns);
13355
+ return buildProjectedRepeaterGridLayout(layout, flowRepeaterGridItems(orderedGridItems(layout), count), count);
13356
+ }
13357
+ /** An explicit column reduction re-places free grids through the existing placement primitive. */
13358
+ function planRepeaterGridColumnChange(layout, columns) {
13359
+ if (layout.placementPolicy === "flow" || columns >= layout.columns) return { kind: "unchanged" };
13360
+ if (isRepeaterGridLayoutInvalid(layout)) return { kind: "blocked" };
13361
+ const placed = orderedGridItems(layout).reduce((items, item) => {
13362
+ if (!items) return null;
13363
+ const bounded = normalizeRepeaterGridSpan(item.span, columns);
13364
+ const span = item.acceptedSpans ? Math.max(0, ...item.acceptedSpans.filter((width) => width <= bounded)) : bounded;
13365
+ const destination = findFirstAvailableRepeaterGridPlacement({
13366
+ items,
13367
+ columns,
13368
+ maxRows: layout.maxRows,
13369
+ newItemSpan: span,
13370
+ acceptedSpans: item.acceptedSpans
13371
+ });
13372
+ return destination ? [...items, {
13373
+ ...item,
13374
+ span,
13375
+ ...destination
13376
+ }] : null;
13377
+ }, []);
13378
+ const [first, ...rest] = placed ?? [];
13379
+ return first ? {
13380
+ kind: "move",
13381
+ updates: [first, ...rest]
13382
+ } : placed ? { kind: "unchanged" } : { kind: "blocked" };
13383
+ }
13384
+
13385
+ //#endregion
13386
+ //#region ../blocks/src/system/manifest/layoutGridConfiguration.ts
13387
+ const itemFieldsSchema = z.object({ fields: z.array(z.object({
13388
+ id: z.string(),
13389
+ type: z.string(),
13390
+ defaultValue: z.unknown().optional(),
13391
+ options: z.array(z.object({ value: z.string() })).optional(),
13392
+ multiple: z.boolean().optional(),
13393
+ source: z.object({ kind: z.string() }).optional()
13394
+ })) });
13395
+ function validateSpanSelect(span, columns, ctx) {
13396
+ if (span.type !== "select") return;
13397
+ const options = span.options ?? [];
13398
+ const invalidOptions = options.some((option) => !isAcceptedRepeaterGridSpan(option.value, columns) || String(Number(option.value)) !== option.value);
13399
+ if (span.multiple || options.length === 0 || invalidOptions || span.source && span.source.kind !== "static") {
13400
+ ctx.addIssue({
13401
+ code: "custom",
13402
+ path: ["ui", "layoutGridSpanField"],
13403
+ message: `Layout-grid span select must have single, static integer options within 1..${columns}`
13404
+ });
13405
+ }
13406
+ if (span.defaultValue !== undefined && !options.some((option) => option.value === span.defaultValue)) {
13407
+ ctx.addIssue({
13408
+ code: "custom",
13409
+ path: ["ui", "layoutGridSpanField"],
13410
+ message: "Layout-grid default span must be one of the span select options"
13411
+ });
13412
+ }
13413
+ }
13414
+ /** Both monomorphic and polymorphic item schemas have the same layout contract. */
13415
+ function validateLayoutGridConfiguration(data, ctx) {
13416
+ const ui = data.ui;
13417
+ if (ui?.repeaterPreset !== "layoutGrid") return;
13418
+ if (ui.layoutGridColumns !== undefined === (ui.layoutGridColumnsFields !== undefined)) {
13419
+ ctx.addIssue({
13420
+ code: "custom",
13421
+ path: ["ui", "layoutGridColumns"],
13422
+ message: "Layout-grid requires exactly one of ui.layoutGridColumns or ui.layoutGridColumnsFields.lg; do not set both"
13423
+ });
13424
+ }
13425
+ const spanId = resolveRepeaterGridSpanFieldId({ id: ui.layoutGridSpanField });
13426
+ const requiredIds = [
13427
+ spanId,
13428
+ resolveRepeaterGridRowFieldId({ id: ui.layoutGridRowField }),
13429
+ resolveRepeaterGridColumnFieldId({ id: ui.layoutGridColumnField })
13430
+ ];
13431
+ const schemas = data.polymorphic && data.itemTypes ? Object.values(data.itemTypes) : [data.schema];
13432
+ schemas.forEach((schema) => {
13433
+ const parsed = itemFieldsSchema.safeParse(schema);
13434
+ if (!parsed.success) return;
13435
+ if (!requiredIds.every((id) => parsed.data.fields.some((field) => field.id === id))) {
13436
+ ctx.addIssue({
13437
+ code: "custom",
13438
+ path: ["ui", "layoutGridRowField"],
13439
+ message: `layoutGrid repeaters require span/row/column fields (${requiredIds.join(", ")}) to exist in repeater item fields`
13440
+ });
13441
+ }
13442
+ if (ui.layoutGridStartNewRowField && !parsed.data.fields.some((field) => field.id === ui.layoutGridStartNewRowField && field.type === "boolean")) {
13443
+ ctx.addIssue({
13444
+ code: "custom",
13445
+ path: ["ui", "layoutGridStartNewRowField"],
13446
+ message: "Layout-grid Start a new row field must reference a boolean item field"
13447
+ });
13448
+ }
13449
+ if (ui.layoutGridStartNewRowField && ui.layoutGridColumns === undefined) {
13450
+ ctx.addIssue({
13451
+ code: "custom",
13452
+ path: ["ui", "layoutGridStartNewRowField"],
13453
+ message: "Layout-grid Start a new row requires fixed columns (flow placement)"
13454
+ });
13455
+ }
13456
+ parsed.data.fields.filter((field) => field.id === spanId).forEach((span) => validateSpanSelect(span, ui.layoutGridColumns ?? MAX_REPEATER_GRID_COLUMNS, ctx));
13457
+ });
13458
+ }
13459
+
13460
+ //#endregion
13461
+ //#region ../blocks/src/system/icons/presets.ts
13462
+ const ICON_PICKER_PRESETS = ["cta", "feature"];
13463
+ function isIconPickerPreset(value) {
13464
+ return typeof value === "string" && ICON_PICKER_PRESETS.includes(value);
13465
+ }
13466
+ function resolveIconPickerPreset(value) {
13467
+ return isIconPickerPreset(value) ? value : "cta";
13468
+ }
13469
+
13470
+ //#endregion
13471
+ //#region ../blocks/src/system/manifest/sdkBlockPresentationSchema.ts
13472
+ const sdkBlockPresentationShape = {
13473
+ variants: z.record(z.string().min(1), NodeSchema).optional(),
13474
+ defaultVariant: z.string().min(1).optional(),
13475
+ variantOptions: z.record(z.string().min(1), z.object({
13476
+ label: z.string().min(1).optional(),
13477
+ description: z.string().min(1).optional(),
13478
+ icon: z.string().min(1).optional()
13479
+ })).optional()
13480
+ };
13481
+ function validateSdkBlockDeclaration(data, ctx) {
13482
+ validateTitleSource(data, ctx);
13483
+ if (data.layout && data.variants) ctx.addIssue({
13484
+ code: z.ZodIssueCode.custom,
13485
+ message: "Custom block must define either `layout` or `variants`, not both",
13486
+ path: ["variants"]
13487
+ });
13488
+ if (data.variants) validateVariantKeys(data, ctx);
13489
+ }
13490
+ function validateTitleSource(data, ctx) {
13491
+ if (!data.titleSource) return;
13492
+ const sources = Array.isArray(data.titleSource) ? data.titleSource : [data.titleSource];
13493
+ sources.forEach((source, index) => {
13494
+ if (data.fields.some((field) => field !== null && typeof field === "object" && "id" in field && field.id === source)) return;
13495
+ ctx.addIssue({
13496
+ code: z.ZodIssueCode.custom,
13497
+ message: `titleSource must reference a valid field ID (got "${source}")`,
13498
+ path: Array.isArray(data.titleSource) ? ["titleSource", index] : ["titleSource"]
13499
+ });
13500
+ });
13501
+ }
13502
+ function validateVariantKeys(data, ctx) {
13503
+ const variants = data.variants ?? {};
13504
+ if (Object.keys(variants).length === 0) ctx.addIssue({
13505
+ code: z.ZodIssueCode.custom,
13506
+ message: "`variants` must contain at least one variant key",
13507
+ path: ["variants"]
13508
+ });
13509
+ if (data.defaultVariant && !Object.prototype.hasOwnProperty.call(variants, data.defaultVariant)) ctx.addIssue({
13510
+ code: z.ZodIssueCode.custom,
13511
+ message: "`defaultVariant` must be a key present in `variants`",
13512
+ path: ["defaultVariant"]
13513
+ });
13514
+ Object.keys(data.variantOptions ?? {}).filter((key) => !Object.prototype.hasOwnProperty.call(variants, key)).forEach((key) => ctx.addIssue({
13515
+ code: z.ZodIssueCode.custom,
13516
+ message: "`variantOptions` key must exist in `variants`",
13517
+ path: ["variantOptions", key]
13518
+ }));
13519
+ }
13520
+
13521
+ //#endregion
13522
+ //#region ../blocks/src/system/manifest/schema.ts
13523
+ const visibilityLevels = [
13524
+ "admin",
13525
+ "designer",
13526
+ "author"
13527
+ ];
13528
+ const MEDIA_KINDS = [
13529
+ "image",
13530
+ "video",
13531
+ "audio",
13532
+ "document",
13533
+ "spreadsheet",
13534
+ "archive"
13535
+ ];
13536
+ const DISPLAY_MEDIA_KINDS = [
13537
+ "image",
13538
+ "video",
13539
+ "audio"
13540
+ ];
13541
+ const DEFAULT_MEDIA_FIELD_KINDS = ["image"];
13542
+ const PUBLIC_FILE_FIELD_KINDS = [
13543
+ "document",
13544
+ "spreadsheet",
13545
+ "archive",
13546
+ "audio"
13547
+ ];
13548
+ function isDisplayMediaKind(kind) {
13549
+ return DISPLAY_MEDIA_KINDS.includes(kind);
13550
+ }
13551
+ const siteOptionSources = [
13552
+ "eventCategories",
13553
+ "eventSeries",
13554
+ "venues",
13555
+ "staffMembers",
13556
+ "appointmentServiceCategories"
13557
+ ];
13558
+ const mediaDeliverySchema = z.object({
13559
+ sizes: z.string().optional(),
13560
+ widthCandidates: z.array(z.number().int().positive()).optional(),
13561
+ maxCssWidth: z.number().positive().optional(),
13562
+ maxCssHeight: z.number().positive().optional(),
13563
+ strategy: z.enum([
13564
+ "default",
13565
+ "columnsAuto",
13566
+ "imageGalleryVariant"
13567
+ ]).optional(),
13568
+ strategyOptions: z.record(z.string(), z.unknown()).optional()
13569
+ });
13570
+ const selectOptionSchema = z.object({
13571
+ value: z.string(),
13572
+ label: z.string(),
13573
+ description: z.string().optional(),
13574
+ icon: z.string().optional()
13575
+ });
13576
+ const selectSourceSchema = z.discriminatedUnion("kind", [
13577
+ z.object({ kind: z.literal("static") }),
13578
+ z.object({
13579
+ kind: z.literal("site"),
13580
+ optionSource: z.enum(siteOptionSources)
13581
+ }),
13582
+ z.object({ kind: z.literal("sdk") })
13583
+ ]);
13584
+ function assertNeverSelectSource(value) {
13585
+ throw new Error(`Unhandled select source: ${JSON.stringify(value)}`);
13586
+ }
13587
+ const visibleWhenPredicateSchema = z.object({
13588
+ field: z.string().min(1),
13589
+ equals: z.any().optional(),
13590
+ notEquals: z.any().optional(),
13591
+ oneOf: z.array(z.any()).optional(),
13592
+ notIn: z.array(z.any()).optional(),
13593
+ minItems: z.number().int().nonnegative().optional(),
13594
+ maxItems: z.number().int().nonnegative().optional()
13595
+ });
13596
+ const visibleWhenSchema = z.lazy(() => z.union([
13597
+ visibleWhenPredicateSchema,
13598
+ z.object({ allOf: z.array(visibleWhenSchema).min(1) }),
13599
+ z.object({ anyOf: z.array(visibleWhenSchema).min(1) })
13600
+ ]));
13601
+ const uiSchema = z.object({
13602
+ widget: z.string().optional(),
13603
+ preset: z.enum(["disclosure"]).optional(),
13604
+ flattenInRepeater: z.boolean().optional(),
13605
+ hidden: z.boolean().optional(),
13606
+ hideLabel: z.boolean().optional(),
13607
+ hideDescription: z.boolean().optional(),
13608
+ showCharCount: z.union([z.number().int().positive(), z.object({ max: z.number().int().positive() })]).optional(),
13609
+ showSlugPreview: z.boolean().optional(),
13610
+ variant: z.enum([
13611
+ "full",
13612
+ "inline",
13613
+ "limited",
13614
+ "media",
13615
+ "notes"
13616
+ ]).optional(),
13617
+ richTextVariant: z.enum([
13618
+ "full",
13619
+ "inline",
13620
+ "limited",
13621
+ "media",
13622
+ "notes"
13623
+ ]).optional(),
13624
+ cropEditorMode: z.enum(["dialog", "inline"]).optional(),
13625
+ cropEditorAutoOpen: z.boolean().optional(),
13626
+ mediaPickerAutoOpen: z.boolean().optional(),
13627
+ showMediaPreview: z.boolean().optional(),
13628
+ showMediaOverlayActions: z.boolean().optional(),
13629
+ mediaPreviewMaxWidth: z.enum([
13630
+ "xs",
13631
+ "sm",
13632
+ "md",
13633
+ "none"
13634
+ ]).optional(),
13635
+ preventMediaPreviewUpscale: z.boolean().optional(),
13636
+ mediaDelivery: mediaDeliverySchema.optional(),
13637
+ mediaDeliveryByVariant: z.record(z.string(), mediaDeliverySchema).optional(),
13638
+ inputType: z.enum([
13639
+ "text",
13640
+ "email",
13641
+ "tel",
13642
+ "number"
13643
+ ]).optional(),
13644
+ min: z.number().optional(),
13645
+ max: z.number().optional(),
13646
+ step: z.number().optional(),
13647
+ showValue: z.boolean().optional(),
13648
+ valueUnit: z.enum([
13649
+ "percent",
13650
+ "pixels",
13651
+ "unitless"
13652
+ ]).optional(),
13653
+ unsetValue: z.enum(["max", "field-default"]).optional(),
13654
+ allowDirectInput: z.boolean().optional(),
13655
+ pattern: z.string().optional(),
13656
+ placeholder: z.string().optional(),
13657
+ visibleWhen: visibleWhenSchema.optional(),
13658
+ modalConfig: z.object({
13659
+ buttonLabel: z.string().optional(),
13660
+ description: z.string().optional(),
13661
+ buttonVariant: z.enum(["default", "outline"]).optional(),
13662
+ showCustomizedIndicator: z.boolean().optional(),
13663
+ maxWidth: z.string().optional()
13664
+ }).optional(),
13665
+ allowedTypes: z.array(z.enum([
13666
+ "color",
13667
+ "gradient",
13668
+ "image"
13669
+ ])).optional(),
13670
+ fullWidth: z.boolean().optional(),
13671
+ row: z.string().optional(),
13672
+ colSpan: z.number().int().min(1).max(4).optional(),
13673
+ layout: z.enum(["stack", "grid"]).optional(),
13674
+ columns: z.number().int().min(2).max(4).optional(),
13675
+ gridColsPreset: z.enum([
13676
+ "simple2",
13677
+ "simple3",
13678
+ "simple4",
13679
+ "buttonCtaRow"
13680
+ ]).optional(),
13681
+ gridColumns: z.object({
13682
+ base: z.number().int().min(1).max(4).optional(),
13683
+ sm: z.number().int().min(1).max(4).optional(),
13684
+ md: z.number().int().min(1).max(4).optional(),
13685
+ lg: z.number().int().min(1).max(4).optional(),
13686
+ xl: z.number().int().min(1).max(4).optional()
13687
+ }).optional(),
13688
+ gridColsClassName: z.string().optional(),
13689
+ repeaterPreset: z.enum(["thumbnailGrid", "layoutGrid"]).optional(),
13690
+ thumbnailField: z.string().optional(),
13691
+ thumbnailAltField: z.string().optional(),
13692
+ thumbnailLabelField: z.string().optional(),
13693
+ thumbnailAspectRatio: z.string().optional(),
13694
+ thumbnailCardDensity: z.enum(["compact", "comfortable"]).optional(),
13695
+ layoutGridColumns: z.union([
13696
+ z.literal(1),
13697
+ z.literal(2),
13698
+ z.literal(3),
13699
+ z.literal(4),
13700
+ z.literal(5),
13701
+ z.literal(6)
13702
+ ]).optional(),
13703
+ layoutGridColumnsFields: z.object({
13704
+ base: z.string().optional(),
13705
+ sm: z.string().optional(),
13706
+ md: z.string().optional(),
13707
+ lg: z.string().min(1),
13708
+ xl: z.string().optional()
13709
+ }).optional(),
13710
+ layoutGridSpanField: z.string().optional(),
13711
+ layoutGridStartNewRowField: z.string().optional(),
13712
+ layoutGridRowField: z.string().optional(),
13713
+ layoutGridColumnField: z.string().optional(),
13714
+ layoutGridMaxRows: z.number().int().positive().optional(),
13715
+ iconPickerPreset: z.enum(ICON_PICKER_PRESETS).optional(),
13716
+ contentTypeField: z.string().optional(),
13717
+ fixedContentType: z.string().optional(),
13718
+ valueFrom: z.enum([
13719
+ "id",
13720
+ "identifier",
13721
+ "slug"
13722
+ ]).optional(),
13723
+ siteOptionSource: z.enum(siteOptionSources).optional(),
13724
+ searchThreshold: z.number().int().positive().optional(),
13725
+ renderInHeader: z.boolean().optional(),
13726
+ intent: z.enum([
13727
+ "content",
13728
+ "display",
13729
+ "behavior",
13730
+ "style"
13731
+ ]).optional(),
13732
+ underLooks: z.literal("owner").optional(),
13733
+ valueContract: z.enum(["boxBackground", "location"]).optional()
13734
+ }).partial().superRefine((ui, ctx) => {
13735
+ if (ui.layoutGridColumns !== undefined && ui.layoutGridColumnsFields !== undefined) {
13736
+ ctx.addIssue({
13737
+ code: "custom",
13738
+ path: ["layoutGridColumns"],
13739
+ message: "Layout-grid requires exactly one of ui.layoutGridColumns or ui.layoutGridColumnsFields.lg; do not set both"
13740
+ });
13741
+ }
13742
+ }).transform((input) => {
13743
+ const { layoutGridColumns, layoutGridColumnsFields, ...ui } = input;
13744
+ if (layoutGridColumns !== undefined) return {
13745
+ ...ui,
13746
+ layoutGridColumns
13747
+ };
13748
+ if (layoutGridColumnsFields !== undefined) return {
13749
+ ...ui,
13750
+ layoutGridColumnsFields
13751
+ };
13752
+ return ui;
13753
+ });
13754
+ const baseFieldSchema = z.object({
13755
+ id: z.string().min(1, "Field id is required"),
13756
+ label: z.string().min(1, "Field label is required"),
13757
+ description: z.string().optional(),
13758
+ visibleRoles: z.array(z.enum(visibilityLevels)).optional(),
13759
+ defaultValue: z.any().optional(),
13760
+ required: z.boolean().default(false),
13761
+ ui: uiSchema.optional()
13762
+ });
13763
+ const textFieldSchema = baseFieldSchema.extend({
13764
+ type: z.literal("text"),
13765
+ multiline: z.boolean().default(false),
13766
+ maxLength: z.number().int().positive().optional()
13767
+ });
13768
+ const richTextFieldSchema = baseFieldSchema.extend({ type: z.literal("richText") });
13769
+ const inlineTextFieldSchema = baseFieldSchema.extend({
13770
+ type: z.literal("inlineText"),
13771
+ maxLength: z.number().int().positive().optional(),
13772
+ plainTextField: z.string().min(1).optional()
13773
+ });
13774
+ function hasMediaDefaultIdentifier(value) {
13775
+ return value !== null && typeof value === "object" && !Array.isArray(value) && typeof value.identifier === "string" && value.identifier.trim().length > 0;
13776
+ }
13777
+ function validateMediaFieldDefault(field, ctx) {
13778
+ if (field.defaultValue !== undefined && field.defaultValue !== null && !hasMediaDefaultIdentifier(field.defaultValue)) {
13779
+ ctx.addIssue({
13780
+ code: z.ZodIssueCode.custom,
13781
+ path: ["defaultValue"],
13782
+ message: `${field.type} defaultValue must be null or an object with a non-empty identifier`
13783
+ });
13784
+ }
13785
+ }
13786
+ const mediaFieldSchema = baseFieldSchema.extend({
13787
+ type: z.literal("media"),
13788
+ mediaKinds: z.array(z.enum(MEDIA_KINDS)).default([...DEFAULT_MEDIA_FIELD_KINDS]).transform((kinds) => {
13789
+ const displayKinds = kinds.filter(isDisplayMediaKind);
13790
+ return displayKinds.length > 0 ? displayKinds : [...DEFAULT_MEDIA_FIELD_KINDS];
13791
+ }),
13792
+ aspectRatio: z.string().optional()
13793
+ }).superRefine(validateMediaFieldDefault);
13794
+ const fileFieldSchema = baseFieldSchema.extend({
13795
+ type: z.literal("file"),
13796
+ fileKinds: z.array(z.enum(PUBLIC_FILE_FIELD_KINDS)).default([...PUBLIC_FILE_FIELD_KINDS])
13797
+ }).superRefine(validateMediaFieldDefault);
13798
+ const booleanFieldSchema = baseFieldSchema.extend({ type: z.literal("boolean") });
13799
+ const dateFieldSchema = baseFieldSchema.extend({ type: z.literal("date") });
13800
+ const timeFieldSchema = baseFieldSchema.extend({ type: z.literal("time") });
13801
+ const dateTimeFieldSchema = baseFieldSchema.extend({ type: z.literal("datetime") });
13802
+ const slugFieldSchema = baseFieldSchema.extend({
13803
+ type: z.literal("slug"),
13804
+ sourceFieldId: z.string().min(1).optional(),
13805
+ statusFieldId: z.string().min(1).optional(),
13806
+ maxLength: z.number().int().positive().optional()
13807
+ });
13808
+ const urlFieldSchema = baseFieldSchema.extend({
13809
+ type: z.literal("url"),
13810
+ allowRelative: z.boolean().default(false)
13811
+ });
13812
+ const linkFieldSchema = baseFieldSchema.extend({ type: z.literal("link") });
13813
+ const selectFieldSchema = baseFieldSchema.extend({
13814
+ type: z.literal("select"),
13815
+ options: z.array(selectOptionSchema),
13816
+ multiple: z.boolean().default(false),
13817
+ source: selectSourceSchema.optional()
13818
+ }).superRefine((field, ctx) => {
13819
+ const widget = field.ui?.widget;
13820
+ const siteOptionSource = field.ui?.siteOptionSource;
13821
+ if (!field.source) {
13822
+ if (siteOptionSource || widget === "sdkSelect") {
13823
+ ctx.addIssue({
13824
+ code: z.ZodIssueCode.custom,
13825
+ message: "Runtime-backed select fields must declare source.kind explicitly",
13826
+ path: ["source"]
13827
+ });
13828
+ return;
13829
+ }
13830
+ if (field.options.length === 0) {
13831
+ ctx.addIssue({
13832
+ code: z.ZodIssueCode.custom,
13833
+ message: "Select fields require at least one option unless a runtime source is configured",
13834
+ path: ["options"]
13835
+ });
13836
+ }
13837
+ return;
13838
+ }
13839
+ switch (field.source.kind) {
13840
+ case "static":
13841
+ if (siteOptionSource || widget === "sdkSelect") {
13842
+ ctx.addIssue({
13843
+ code: z.ZodIssueCode.custom,
13844
+ message: "Static select fields cannot declare runtime-backed UI metadata",
13845
+ path: ["ui"]
13846
+ });
13847
+ }
13848
+ if (field.options.length === 0) {
13849
+ ctx.addIssue({
13850
+ code: z.ZodIssueCode.custom,
13851
+ message: "Static select fields require at least one option",
13852
+ path: ["options"]
13853
+ });
13854
+ }
13855
+ return;
13856
+ case "site":
13857
+ if (field.options.length > 0) {
13858
+ ctx.addIssue({
13859
+ code: z.ZodIssueCode.custom,
13860
+ message: "Site-backed select fields must not define static options",
13861
+ path: ["options"]
13862
+ });
13863
+ }
13864
+ if (widget !== "siteOptionPicker") {
13865
+ ctx.addIssue({
13866
+ code: z.ZodIssueCode.custom,
13867
+ message: "Site-backed select fields must use the siteOptionPicker widget",
13868
+ path: ["ui", "widget"]
13869
+ });
13870
+ }
13871
+ if (siteOptionSource !== field.source.optionSource) {
13872
+ ctx.addIssue({
13873
+ code: z.ZodIssueCode.custom,
13874
+ message: "Site-backed select fields must keep ui.siteOptionSource in sync with source.optionSource",
13875
+ path: ["ui", "siteOptionSource"]
13876
+ });
13877
+ }
13878
+ return;
13879
+ case "sdk":
13880
+ if (widget !== "sdkSelect") {
13881
+ ctx.addIssue({
13882
+ code: z.ZodIssueCode.custom,
13883
+ message: "SDK-backed select fields must use the sdkSelect widget",
13884
+ path: ["ui", "widget"]
13885
+ });
13886
+ }
13887
+ if (siteOptionSource) {
13888
+ ctx.addIssue({
13889
+ code: z.ZodIssueCode.custom,
13890
+ message: "SDK-backed select fields cannot declare ui.siteOptionSource",
13891
+ path: ["ui", "siteOptionSource"]
13892
+ });
13893
+ }
13894
+ return;
13895
+ default: return assertNeverSelectSource(field.source);
13896
+ }
13897
+ });
13898
+ const referenceFieldSchema = baseFieldSchema.extend({
13899
+ type: z.literal("reference"),
13900
+ referenceKind: z.string().min(1),
13901
+ allowManualEntry: z.boolean().default(false)
13902
+ });
13903
+ /**
13904
+ * Item type schema for polymorphic repeaters.
13905
+ * Each type has a label, optional icon, and its own field schema.
13906
+ */
13907
+ const itemTypeSchema = z.object({
13908
+ label: z.string().min(1),
13909
+ icon: z.string().optional(),
13910
+ fields: z.lazy(() => getFieldSchemaInternal().array().min(1, "Item type requires at least one field"))
13911
+ });
13912
+ /**
13913
+ * Repeater fields allow nesting of other fields within them.
13914
+ * This enables complex layouts and structures by repeating a set of fields.
13915
+ *
13916
+ * Supports two modes:
13917
+ * 1. Monomorphic (default): All items have the same schema
13918
+ * 2. Polymorphic: Items can be different types (e.g., card, quote, stat)
13919
+ *
13920
+ * Uses z.lazy() to allow mutual recursion.
13921
+ */
13922
+ const repeaterFieldSchema = baseFieldSchema.extend({
13923
+ type: z.literal("repeater"),
13924
+ itemLabel: z.string().default("Item"),
13925
+ itemLabelSource: z.string().optional(),
13926
+ showSummary: z.boolean().optional(),
13927
+ summaryFields: z.string().array().optional(),
13928
+ summaryMaxChars: z.number().int().positive().optional(),
13929
+ minItems: z.number().int().min(0).default(0),
13930
+ maxItems: z.number().int().positive().optional(),
13931
+ schema: z.object({ fields: z.lazy(() => getFieldSchemaInternal().array().min(1, "Repeater requires at least one field")) }).optional(),
13932
+ polymorphic: z.boolean().optional(),
13933
+ itemTypes: z.record(z.string(), itemTypeSchema).optional(),
13934
+ allowConversion: z.boolean().default(true)
13935
+ }).refine((data) => {
13936
+ const hasSchema = data.schema !== undefined;
13937
+ const hasPolymorphic = data.polymorphic === true && data.itemTypes !== undefined;
13938
+ return hasSchema !== hasPolymorphic;
13939
+ }, { message: "Repeater must have either 'schema' (monomorphic) or 'polymorphic: true' with 'itemTypes' (polymorphic)" }).superRefine(validateLayoutGridConfiguration).refine((data) => {
13940
+ const thumbnailAspectRatio = data.ui?.thumbnailAspectRatio;
13941
+ if (!thumbnailAspectRatio) {
13942
+ return true;
13943
+ }
13944
+ return parseAspectRatioValue(thumbnailAspectRatio) != null;
13945
+ }, {
13946
+ message: "ui.thumbnailAspectRatio must be a valid ratio (e.g. '16/9', '4:3', or '1.5')",
13947
+ path: ["ui", "thumbnailAspectRatio"]
13948
+ }).refine((data) => {
13949
+ const labelField = data.ui?.thumbnailLabelField;
13950
+ if (!labelField) {
13951
+ return true;
13952
+ }
13953
+ const schemaFields = data.schema?.fields;
13954
+ if (!schemaFields) {
13955
+ return true;
13956
+ }
13957
+ const field = schemaFields.find((candidate) => candidate.id === labelField);
13958
+ return field?.type === "text";
13959
+ }, {
13960
+ message: "ui.thumbnailLabelField must reference a text field in the repeater schema",
13961
+ path: ["ui", "thumbnailLabelField"]
13962
+ });
13963
+ const groupFieldSchema = baseFieldSchema.extend({
13964
+ type: z.literal("group"),
13965
+ schema: z.object({ fields: z.lazy(() => getFieldSchemaInternal().array().min(1, "Group requires at least one field")) })
13966
+ });
13967
+ const modalFieldSchema = baseFieldSchema.extend({
13968
+ type: z.literal("modal"),
13969
+ schema: z.object({ fields: z.lazy(() => getFieldSchemaInternal().array().min(1, "Modal requires at least one field")) })
13970
+ });
13971
+ const numberFieldSchema = baseFieldSchema.extend({
13972
+ type: z.literal("number"),
13973
+ min: z.number().optional(),
13974
+ max: z.number().optional(),
13975
+ step: z.number().optional()
13976
+ });
13977
+ const tabDefinitionSchema = z.object({
13978
+ id: z.string().min(1),
13979
+ label: z.string().min(1),
13980
+ icon: z.string().optional(),
13981
+ description: z.string().optional(),
13982
+ fields: z.lazy(() => getFieldSchemaInternal().array()),
13983
+ sdkSectionOption: z.enum([
13984
+ "backgroundColor",
13985
+ "backgroundGradient",
13986
+ "backgroundImage"
13987
+ ]).optional()
13988
+ });
13989
+ const tabGroupFieldSchema = baseFieldSchema.extend({
13990
+ type: z.literal("tabGroup"),
13991
+ tabs: z.array(tabDefinitionSchema).min(1, "TabGroup requires at least one tab"),
13992
+ activeTabField: z.string().optional()
13993
+ });
13994
+ const presetOptionSchema = z.object({
13995
+ value: z.string(),
13996
+ label: z.string()
13997
+ });
13998
+ const presetOrCustomFieldSchema = baseFieldSchema.extend({
13999
+ type: z.literal("presetOrCustom"),
14000
+ presets: z.array(presetOptionSchema).min(1, "PresetOrCustom requires at least one preset"),
14001
+ customInput: z.object({
14002
+ placeholder: z.string().optional(),
14003
+ pattern: z.string().optional(),
14004
+ helpText: z.string().optional()
14005
+ }).optional()
14006
+ });
14007
+ const contentTypeSelectFieldSchema = baseFieldSchema.extend({
14008
+ type: z.literal("contentTypeSelect"),
14009
+ filter: z.enum([
14010
+ "all",
14011
+ "routable",
14012
+ "nonRoutable"
14013
+ ]).default("all")
14014
+ });
14015
+ const entryPickerFieldSchema = baseFieldSchema.extend({
14016
+ type: z.literal("entryPicker"),
14017
+ fixedContentType: z.string().optional(),
14018
+ contentTypeField: z.string().optional(),
14019
+ valueFrom: z.enum([
14020
+ "id",
14021
+ "identifier",
14022
+ "slug"
14023
+ ]).optional()
14024
+ });
14025
+ let _fieldSchemaInternal = null;
14026
+ function getFieldSchemaInternal() {
14027
+ if (_fieldSchemaInternal) {
14028
+ return _fieldSchemaInternal;
14029
+ }
14030
+ _fieldSchemaInternal = z.discriminatedUnion("type", [
14031
+ textFieldSchema,
14032
+ richTextFieldSchema,
14033
+ inlineTextFieldSchema,
14034
+ mediaFieldSchema,
14035
+ fileFieldSchema,
14036
+ booleanFieldSchema,
14037
+ numberFieldSchema,
14038
+ dateFieldSchema,
14039
+ timeFieldSchema,
14040
+ dateTimeFieldSchema,
14041
+ slugFieldSchema,
14042
+ urlFieldSchema,
14043
+ linkFieldSchema,
14044
+ selectFieldSchema,
14045
+ referenceFieldSchema,
14046
+ repeaterFieldSchema,
14047
+ groupFieldSchema,
14048
+ modalFieldSchema,
14049
+ tabGroupFieldSchema,
14050
+ presetOrCustomFieldSchema,
14051
+ contentTypeSelectFieldSchema,
14052
+ entryPickerFieldSchema
14053
+ ]);
14054
+ return _fieldSchemaInternal;
14055
+ }
14056
+ function getFieldSchema() {
14057
+ return getFieldSchemaInternal();
14058
+ }
14059
+ const fieldSchema = new Proxy({}, {
14060
+ get(_, prop) {
14061
+ const schema = getFieldSchemaInternal();
14062
+ const value = schema[prop];
14063
+ return typeof value === "function" ? value.bind(schema) : value;
14064
+ },
14065
+ has(_, prop) {
14066
+ return prop in getFieldSchemaInternal();
14067
+ }
14068
+ });
14069
+ function parseFieldDefinition(input) {
14070
+ return fieldSchema.parse(input);
14071
+ }
14072
+ function parseFieldDefinitions(input) {
14073
+ return fieldSchema.array().parse(input);
11871
14074
  }
11872
- function createPageOperations(http) {
11873
- async function upsert(input) {
11874
- if (input.publication === "publish") {
11875
- return sdkUpsertPagePublishResponseSchema.parse(await http.post("/pages", input, "write"));
11876
- }
11877
- return sdkUpsertPageDraftResponseSchema.parse(await http.post("/pages", input, "write"));
14075
+ /**
14076
+ * Normalize legacy select fields into the closed source union before any consumer
14077
+ * branches on source semantics. Runtime-backed definitions must be explicit;
14078
+ * legacy static definitions normalize to `kind: "static"`.
14079
+ */
14080
+ function normalizeSelectSource(field) {
14081
+ return field.source ?? { kind: "static" };
14082
+ }
14083
+ function expectParsedSelectField(field) {
14084
+ if (field.type !== "select") {
14085
+ throw new Error(`Expected select field, received ${field.type}`);
11878
14086
  }
11879
- return {
11880
- async list(options) {
11881
- const result = await http.get("/pull/pages");
11882
- return paginateItems(result.pages.map(mapPulledPage), options);
11883
- },
11884
- async get(identifier) {
11885
- const result = await http.get("/pull/pages");
11886
- const page = result.pages.find((entry) => entry.identifier === identifier);
11887
- return page ? mapPulledPage(page) : null;
11888
- },
11889
- upsert,
11890
- async publish(identifier) {
11891
- return http.post(`/pages/${encodeURIComponent(identifier)}/publish`, undefined, "write");
11892
- },
11893
- async unpublish(identifier) {
11894
- return http.post(`/pages/${encodeURIComponent(identifier)}/unpublish`, undefined, "write");
11895
- },
11896
- async delete(identifier) {
11897
- return http.delete(`/pages/${encodeURIComponent(identifier)}`);
11898
- },
11899
- async getDesign(identifier) {
11900
- return http.getValidated(designPath(identifier), sdkGetPageDesignResponseSchema);
14087
+ return field;
14088
+ }
14089
+ function createStaticSelectField(input) {
14090
+ return expectParsedSelectField(parseFieldDefinition({
14091
+ ...input,
14092
+ type: "select",
14093
+ options: input.options,
14094
+ multiple: input.multiple ?? false,
14095
+ source: { kind: "static" }
14096
+ }));
14097
+ }
14098
+ function createSiteOptionSelectField(input) {
14099
+ return expectParsedSelectField(parseFieldDefinition({
14100
+ ...input,
14101
+ type: "select",
14102
+ options: [],
14103
+ multiple: input.multiple ?? false,
14104
+ source: {
14105
+ kind: "site",
14106
+ optionSource: input.optionSource
11901
14107
  },
11902
- async saveDesign(identifier, design, expectedRevision) {
11903
- return http.postValidated(designPath(identifier), sdkSavePageDesignResponseSchema, {
11904
- design,
11905
- expectedRevision
11906
- }, "write");
14108
+ ui: {
14109
+ ...input.ui ?? {},
14110
+ widget: "siteOptionPicker",
14111
+ siteOptionSource: input.optionSource
11907
14112
  }
11908
- };
14113
+ }));
14114
+ }
14115
+ function createSdkSelectField(input) {
14116
+ return expectParsedSelectField(parseFieldDefinition({
14117
+ ...input,
14118
+ type: "select",
14119
+ options: input.options,
14120
+ multiple: input.multiple ?? false,
14121
+ source: { kind: "sdk" },
14122
+ ui: {
14123
+ ...input.ui ?? {},
14124
+ widget: "sdkSelect"
14125
+ }
14126
+ }));
14127
+ }
14128
+ const slotSchema = z.object({
14129
+ id: z.string().min(1),
14130
+ label: z.string().min(1),
14131
+ allowedKinds: z.array(z.string()).default([]),
14132
+ min: z.number().int().min(0).default(0),
14133
+ max: z.number().int().positive().optional(),
14134
+ help: z.string().optional(),
14135
+ visibleRoles: z.array(z.enum(visibilityLevels)).optional()
14136
+ });
14137
+ const typographyTokens = [
14138
+ "display",
14139
+ "heading",
14140
+ "subheading",
14141
+ "body",
14142
+ "caption"
14143
+ ];
14144
+ const colorTokens = [
14145
+ "background",
14146
+ "surface",
14147
+ "foreground",
14148
+ "accent",
14149
+ "muted"
14150
+ ];
14151
+ const spacingTokens = [
14152
+ "none",
14153
+ "xs",
14154
+ "sm",
14155
+ "md",
14156
+ "lg",
14157
+ "xl"
14158
+ ];
14159
+ const radiusTokens = [
14160
+ "none",
14161
+ "sm",
14162
+ "md",
14163
+ "lg",
14164
+ "full"
14165
+ ];
14166
+ const styleTokenSchema = z.object({
14167
+ background: z.enum(colorTokens).optional(),
14168
+ foreground: z.enum(colorTokens).optional(),
14169
+ border: z.enum(colorTokens).optional(),
14170
+ typography: z.enum(typographyTokens).optional(),
14171
+ spacing: z.enum(spacingTokens).optional(),
14172
+ radius: z.enum(radiusTokens).optional()
14173
+ });
14174
+ const behaviourSchema = z.object({
14175
+ supportsThemeSwitching: z.boolean().default(true),
14176
+ inlineEditing: z.boolean().default(false),
14177
+ animation: z.boolean().default(false),
14178
+ paletteHidden: z.boolean().default(false),
14179
+ managesOwnWrapper: z.boolean().optional()
14180
+ });
14181
+ const builtInBlockKindsSchema = z.array(z.enum(SYSTEM_BLOCK_KINDS)).readonly();
14182
+ const blockRequiredFeatureSchema = z.enum([
14183
+ "bookings.appointments",
14184
+ "bookings.events",
14185
+ "shop",
14186
+ "newsletter",
14187
+ "analytics"
14188
+ ]);
14189
+ const blockCategoryEnum = z.enum([
14190
+ "text-and-images",
14191
+ "photos",
14192
+ "people",
14193
+ "events-and-bookings",
14194
+ "shop",
14195
+ "forms-and-sign-ups",
14196
+ "marketing",
14197
+ "content",
14198
+ "blog",
14199
+ "media",
14200
+ "layout",
14201
+ "interactive"
14202
+ ]);
14203
+ /**
14204
+ * Explicit authoring classification of where a block's rendered content comes from.
14205
+ *
14206
+ * - `inline` — all content is authored inline in the block's own fields.
14207
+ * - `entries` — the block renders content-type entries; `contentType` names the backing type.
14208
+ * - `mixed` — the block has inline fields AND renders entries; `contentType` names the
14209
+ * backing type when a single one applies.
14210
+ *
14211
+ * This is EXPLICIT metadata declared on every block definition (not inferred from
14212
+ * dataLoaders/field types), so the catalog can classify entry-backed vs inline blocks
14213
+ * honestly. A registry-wide test asserts every registered block declares it.
14214
+ */
14215
+ const blockContentSourceKinds = [
14216
+ "inline",
14217
+ "entries",
14218
+ "mixed"
14219
+ ];
14220
+ const blockContentSourceSchema = z.object({
14221
+ kind: z.enum(blockContentSourceKinds),
14222
+ contentType: z.string().min(1).optional()
14223
+ }).superRefine((value, ctx) => {
14224
+ if (value.kind === "inline" && value.contentType) {
14225
+ ctx.addIssue({
14226
+ code: z.ZodIssueCode.custom,
14227
+ path: ["contentType"],
14228
+ message: "contentType is not allowed when contentSource.kind is \"inline\""
14229
+ });
14230
+ }
14231
+ });
14232
+ const manifestSchema = z.object({
14233
+ name: z.string().min(1),
14234
+ version: z.string().min(1),
14235
+ title: z.string().min(1),
14236
+ titleSource: z.union([z.string().min(1), z.array(z.string().min(1)).min(1)]).optional(),
14237
+ description: z.string().optional(),
14238
+ component: z.string().min(1),
14239
+ fields: fieldSchema.array().default([]),
14240
+ slots: slotSchema.array().default([]),
14241
+ styleTokens: styleTokenSchema.optional(),
14242
+ behaviours: behaviourSchema.optional(),
14243
+ layout: NodeTreeSchema.optional(),
14244
+ lookField: z.string().min(1).optional(),
14245
+ sourceLine: z.object({
14246
+ text: z.string(),
14247
+ target: z.enum([
14248
+ "testimonials",
14249
+ "team",
14250
+ "mailing-lists"
14251
+ ]),
14252
+ when: z.object({
14253
+ field: z.string(),
14254
+ equals: z.string()
14255
+ }).optional()
14256
+ }).optional(),
14257
+ promotedFields: z.array(z.string().min(1)).optional(),
14258
+ variantMeaning: z.discriminatedUnion("kind", [z.object({
14259
+ kind: z.literal("arrangement"),
14260
+ lookOnlyKeys: z.array(z.string().min(1)).readonly().optional()
14261
+ }), z.object({
14262
+ kind: z.literal("mode"),
14263
+ modes: z.array(z.enum(arrangementModes)).min(1).readonly(),
14264
+ defaultMode: z.enum(arrangementModes),
14265
+ arrangementField: z.string().min(1),
14266
+ keys: z.array(z.string().min(1)).min(1).readonly(),
14267
+ defaultKey: z.string().min(1),
14268
+ lookOnlyKeys: z.array(z.string().min(1)).readonly().optional(),
14269
+ modeLabels: z.record(z.string(), z.string().min(1)).readonly().optional()
14270
+ })]),
14271
+ variants: z.record(z.string(), NodeTreeSchema).optional(),
14272
+ defaultVariant: z.string().optional(),
14273
+ variantOptions: z.record(z.string(), z.object({
14274
+ label: z.string().optional(),
14275
+ description: z.string().optional(),
14276
+ icon: z.string().optional()
14277
+ }).optional()).optional(),
14278
+ category: blockCategoryEnum.optional(),
14279
+ contentTypes: z.array(z.string()).optional(),
14280
+ requiredFeatures: z.array(blockRequiredFeatureSchema).optional(),
14281
+ contentSource: blockContentSourceSchema.optional(),
14282
+ tags: z.array(z.string()).optional(),
14283
+ icon: z.string().optional()
14284
+ });
14285
+ /** SDK Looks name a single top-level select owned by this definition. */
14286
+ function validateManifestLookField(data, ctx) {
14287
+ if (!data.lookField) return;
14288
+ 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));
14289
+ if (!valid) ctx.addIssue({
14290
+ code: z.ZodIssueCode.custom,
14291
+ path: ["lookField"],
14292
+ message: "lookField must name an existing single select field"
14293
+ });
11909
14294
  }
14295
+ const blockSectionDeclarationShape = {
14296
+ promotedFields: z.array(z.string().min(1)).optional(),
14297
+ lookField: z.string().min(1).optional()
14298
+ };
14299
+
14300
+ //#endregion
14301
+ //#region ../api/src/sdkConfig/contracts.ts
14302
+ /**
14303
+ * Canonical SDK config Zod contracts.
14304
+ *
14305
+ * Goal: a single source-of-truth for request payload shapes used across dashboard + SDK.
14306
+ * - Endpoint request/response types live in `packages/api/src/types.ts`
14307
+ * - Schemas live here and are tied to those types via `satisfies z.ZodType<...>`
14308
+ *
14309
+ * NOTE: These schemas represent the *wire/input* shape for API requests.
14310
+ * Service-layer code should parse/normalize again (defaults/refinements) into domain types.
14311
+ */
14312
+ /**
14313
+ * UNSAFE: Retype an existing Zod schema to a different (compile-time) output type.
14314
+ *
14315
+ * Use only for cases where:
14316
+ * - the runtime schema is known to accept the desired *input/wire* shape, but
14317
+ * - Zod's inferred output type doesn't match the desired TS type due to transforms/defaults.
14318
+ *
14319
+ * If used incorrectly, this can lie to TypeScript and cause drift.
14320
+ *
14321
+ * @internal
14322
+ */
14323
+ function unsafeRetypeZodSchema(schema) {
14324
+ return schema;
14325
+ }
14326
+ const navIdListSchema = z.preprocess((value) => {
14327
+ if (!Array.isArray(value)) return undefined;
14328
+ const filtered = value.filter((v) => typeof v === "string" && v.trim().length > 0);
14329
+ return filtered.length > 0 ? filtered : undefined;
14330
+ }, z.array(z.string()).max(200).optional());
14331
+ const dashboardNavVisibilityBaseSchema = z.object({
14332
+ mode: z.enum(["augment", "restrict"]).optional(),
14333
+ allowIds: navIdListSchema,
14334
+ denyIds: navIdListSchema
14335
+ }).strip();
14336
+ const dashboardNavVisibilityRolesSchema = z.object({
14337
+ administrator: dashboardNavVisibilityBaseSchema.optional(),
14338
+ editor: dashboardNavVisibilityBaseSchema.optional(),
14339
+ team_member: dashboardNavVisibilityBaseSchema.optional()
14340
+ }).strip().optional();
14341
+ const dashboardNavVisibilitySchema = dashboardNavVisibilityBaseSchema.extend({ roles: dashboardNavVisibilityRolesSchema }).strip();
14342
+ const dashboardNavigationConfigSchema = z.object({
14343
+ siteNav: dashboardNavVisibilitySchema.optional(),
14344
+ settingsNav: dashboardNavVisibilitySchema.optional()
14345
+ }).strip();
14346
+ const dashboardConfigSchema = z.object({
14347
+ previewEditingEnabled: z.boolean().optional(),
14348
+ navigation: dashboardNavigationConfigSchema.optional()
14349
+ }).strip();
14350
+ /**
14351
+ * Canonical field schema comes from @riverbankcms/blocks.
14352
+ *
14353
+ * It accepts FieldDefinitionInput (pre-defaults) and outputs FieldDefinition (post-defaults).
14354
+ * This `satisfies` assertion prevents drift in the *accepted wire shape* without forcing
14355
+ * the schema output type to match the input type.
14356
+ */
14357
+ const fieldDefinitionInputSchema = unsafeRetypeZodSchema(fieldSchema);
14358
+ const nodeDefinitionSchema = unsafeRetypeZodSchema(NodeSchema);
14359
+ const sdkLoaderEndpointSchema = z.enum(SUPPORTED_LOADER_ENDPOINTS);
14360
+ const loaderFallbackScalarSchema = z.union([
14361
+ z.string(),
14362
+ z.number(),
14363
+ z.boolean(),
14364
+ z.null()
14365
+ ]);
14366
+ const loaderParamBindingSchema = z.object({ $bind: z.object({
14367
+ from: z.string().min(1, "Binding path is required"),
14368
+ fallback: loaderFallbackScalarSchema.optional()
14369
+ }) });
14370
+ const loaderParamValueSchema = z.union([
14371
+ z.string(),
14372
+ z.number(),
14373
+ z.boolean(),
14374
+ loaderParamBindingSchema
14375
+ ]);
14376
+ const sdkConfigLoaderSchema = z.object({
14377
+ endpoint: sdkLoaderEndpointSchema,
14378
+ params: z.record(z.string(), loaderParamValueSchema),
14379
+ requiredParams: z.array(z.string().min(1)).optional(),
14380
+ mode: z.enum(["server", "client"]).default("server")
14381
+ });
14382
+ const sdkDataLoadersSchema = z.record(z.string(), sdkConfigLoaderSchema).refine((loaders) => Object.keys(loaders).length <= 5, { message: "Maximum 5 data loaders per block" }).optional();
14383
+ 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);
14384
+ const sdkCustomBlockInputSchema = z.object({
14385
+ id: sdkCustomBlockIdSchema,
14386
+ title: z.string().min(1),
14387
+ titleSource: z.union([z.string().min(1), z.array(z.string().min(1)).min(1)]).optional(),
14388
+ description: z.string().optional(),
14389
+ category: blockCategoryEnum,
14390
+ icon: z.string().optional(),
14391
+ tags: z.array(z.string()).optional(),
14392
+ fields: fieldDefinitionInputSchema.array().min(1),
14393
+ dataLoaders: sdkDataLoadersSchema,
14394
+ layout: NodeSchema.optional(),
14395
+ ...sdkBlockPresentationShape,
14396
+ ...blockSectionDeclarationShape
14397
+ }).superRefine(validateManifestLookField).superRefine(validateSdkBlockDeclaration);
14398
+ const sdkBlockFieldExtensionInputSchema = z.object({
14399
+ fields: fieldDefinitionInputSchema.array().min(1),
14400
+ ...blockSectionDeclarationShape
14401
+ }).superRefine(validateManifestLookField);
14402
+ const sdkBlockFieldExtensionsInputSchema = z.record(z.string().min(1), sdkBlockFieldExtensionInputSchema);
14403
+ const sdkThemePaletteSchema = z.record(z.string(), z.string());
14404
+ const sdkThemeConfigSchema = z.object({ palette: sdkThemePaletteSchema });
14405
+ const sdkSectionBackgroundSchema = z.object({
14406
+ id: z.string().min(1),
14407
+ label: z.string().min(1),
14408
+ token: z.string().min(1)
14409
+ });
14410
+ const sdkSectionOptionsConfigSchema = z.object({
14411
+ backgroundColor: z.boolean().optional(),
14412
+ backgroundImage: z.boolean().optional(),
14413
+ backgroundGradient: z.boolean().optional(),
14414
+ spacing: z.union([z.array(z.enum([
14415
+ "compact",
14416
+ "default",
14417
+ "spacious"
14418
+ ])), z.boolean()]).optional(),
14419
+ textColor: z.boolean().optional()
14420
+ });
14421
+ const sdkContainerOptionsConfigSchema = z.object({
14422
+ maxWidth: z.union([z.array(z.enum([
14423
+ "narrow",
14424
+ "default",
14425
+ "wide",
14426
+ "full"
14427
+ ])), z.boolean()]).optional(),
14428
+ alignment: z.union([z.array(z.enum([
14429
+ "left",
14430
+ "center",
14431
+ "right"
14432
+ ])), z.boolean()]).optional()
14433
+ });
14434
+ const sdkSiteStyleConfigSchema = z.object({
14435
+ sectionBackgrounds: z.array(sdkSectionBackgroundSchema).optional(),
14436
+ sectionOptions: sdkSectionOptionsConfigSchema.optional(),
14437
+ containerOptions: sdkContainerOptionsConfigSchema.optional()
14438
+ });
14439
+ const sdkSiteLayoutConfigSchema = z.object({
14440
+ footerMode: footerModeSchema.optional(),
14441
+ footerBlocks: z.array(z.object({
14442
+ id: z.string().min(1).optional(),
14443
+ kind: z.string().min(1),
14444
+ content: z.record(z.string(), z.unknown()).optional()
14445
+ })).optional()
14446
+ });
14447
+ const sdkFieldSelectOptionSchema = z.object({
14448
+ value: z.string(),
14449
+ label: z.string()
14450
+ });
14451
+ const sdkBlockFieldConfigSchema = z.object({ options: z.array(sdkFieldSelectOptionSchema).optional() });
14452
+ 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();
14453
+ const sdkContentTypeTemplateConfigSchema = z.object({ blocks: z.array(z.unknown()) }).passthrough();
14454
+ const sdkContentTypeOrderingConfigSchema = z.object({ mode: z.enum(["default", "manual"]).optional() }).optional();
14455
+ const sdkContentTypeConfigSchema = z.object({
14456
+ key: z.string().min(1),
14457
+ fields: z.array(z.unknown()),
14458
+ ordering: sdkContentTypeOrderingConfigSchema,
14459
+ template: sdkContentTypeTemplateConfigSchema.nullable().optional()
14460
+ }).passthrough();
14461
+ const sdkContentConfigSchema = z.object({
14462
+ contentTypes: z.array(sdkContentTypeConfigSchema).optional(),
14463
+ pages: z.array(z.unknown()).optional(),
14464
+ entries: z.array(z.unknown()).optional(),
14465
+ navigationMenus: z.array(z.unknown()).optional(),
14466
+ settings: z.unknown().optional()
14467
+ }).passthrough();
14468
+ const SDK_WORKFLOW_ID_MAX_LENGTH = 120;
14469
+ const SDK_WORKFLOW_TITLE_MAX_LENGTH = 120;
14470
+ const SDK_WORKFLOW_DESCRIPTION_MAX_LENGTH = 500;
14471
+ const SDK_WORKFLOW_CONTENT_TYPE_MAX_LENGTH = 120;
14472
+ const SDK_WORKFLOW_ENDPOINT_MAX_LENGTH = 500;
14473
+ const SDK_WORKFLOW_TRIGGER_MAX_COUNT = 5;
14474
+ const SDK_WORKFLOW_TRIGGER_FIELD_MAX_LENGTH = 120;
14475
+ const SDK_WORKFLOW_TRIGGER_CONDITION_MAX_COUNT = 10;
14476
+ const SDK_WORKFLOW_MAX_COUNT = 50;
14477
+ const SDK_WORKFLOW_CONFIRMATION_TITLE_MAX_LENGTH = 120;
14478
+ const SDK_WORKFLOW_CONFIRMATION_BODY_MAX_LENGTH = 500;
14479
+ const SDK_WORKFLOW_ID_PATTERN = /^[a-z][a-z0-9]*(?:[._-][a-z0-9]+)*$/;
14480
+ const SDK_WORKFLOW_ENDPOINT_PATTERN = /^\//;
14481
+ 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" });
14482
+ 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 \"/\"" });
14483
+ const sdkWorkflowManualTriggerSchema = z.object({ type: z.literal("manual") }).strict();
14484
+ 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" });
14485
+ const sdkWorkflowFieldChangedConditionSchema = z.object({
14486
+ field: sdkWorkflowTriggerFieldSchema,
14487
+ changed: z.literal(true)
14488
+ }).strict();
14489
+ const sdkWorkflowFieldEmptyConditionSchema = z.object({
14490
+ field: sdkWorkflowTriggerFieldSchema,
14491
+ isEmpty: z.boolean()
14492
+ }).strict();
14493
+ const sdkWorkflowEntryDraftUpdatedTriggerSchema = z.object({
14494
+ type: z.literal("entry.draft.updated"),
14495
+ when: z.array(z.union([sdkWorkflowFieldChangedConditionSchema, sdkWorkflowFieldEmptyConditionSchema])).min(1).max(SDK_WORKFLOW_TRIGGER_CONDITION_MAX_COUNT)
14496
+ }).strict();
14497
+ const sdkWorkflowTriggerSchema = z.discriminatedUnion("type", [sdkWorkflowManualTriggerSchema, sdkWorkflowEntryDraftUpdatedTriggerSchema]);
14498
+ const sdkWorkflowConfirmationSchema = z.object({
14499
+ title: z.string().trim().min(1).max(SDK_WORKFLOW_CONFIRMATION_TITLE_MAX_LENGTH),
14500
+ body: z.string().trim().min(1).max(SDK_WORKFLOW_CONFIRMATION_BODY_MAX_LENGTH).optional()
14501
+ }).strict();
14502
+ const sdkWorkflowConfigSchema = z.object({
14503
+ id: sdkWorkflowIdSchema,
14504
+ title: z.string().trim().min(1).max(SDK_WORKFLOW_TITLE_MAX_LENGTH),
14505
+ description: z.string().trim().min(1).max(SDK_WORKFLOW_DESCRIPTION_MAX_LENGTH).optional(),
14506
+ contentType: z.string().trim().min(1).max(SDK_WORKFLOW_CONTENT_TYPE_MAX_LENGTH),
14507
+ endpoint: sdkWorkflowEndpointSchema,
14508
+ triggers: z.array(sdkWorkflowTriggerSchema).min(1).max(SDK_WORKFLOW_TRIGGER_MAX_COUNT),
14509
+ confirmation: sdkWorkflowConfirmationSchema.optional()
14510
+ }).strict();
14511
+ const sdkWorkflowsSchema = z.array(sdkWorkflowConfigSchema).max(SDK_WORKFLOW_MAX_COUNT, "Maximum 50 workflows per site").refine((workflows) => {
14512
+ const ids = workflows.map((workflow) => workflow.id);
14513
+ return ids.length === new Set(ids).size;
14514
+ }, { message: "Workflow IDs must be unique" }).optional();
14515
+ const riverbankSiteConfigInputSchema = z.object({
14516
+ siteId: z.string().uuid(),
14517
+ builtInBlocks: builtInBlockKindsSchema.optional(),
14518
+ previewUrl: z.string().url().optional(),
14519
+ liveUrl: z.string().url().optional(),
14520
+ theme: sdkThemeConfigSchema.optional(),
14521
+ styles: sdkSiteStyleConfigSchema.optional(),
14522
+ layout: sdkSiteLayoutConfigSchema.optional(),
14523
+ customBlocks: z.array(sdkCustomBlockInputSchema).max(100, "Maximum 100 custom blocks per site").refine((blocks) => {
14524
+ const ids = blocks.map((b) => b.id);
14525
+ return ids.length === new Set(ids).size;
14526
+ }, { message: "Block IDs must be unique" }).optional(),
14527
+ blockFieldOptions: sdkBlockFieldOptionsMapSchema,
14528
+ blockFieldExtensions: sdkBlockFieldExtensionsInputSchema.optional(),
14529
+ workflows: sdkWorkflowsSchema,
14530
+ content: sdkContentConfigSchema.optional(),
14531
+ contentDir: z.string().optional(),
14532
+ sync: z.object({ existingEntries: z.enum(["skip", "update"]).optional() }).optional(),
14533
+ deploy: z.object({
14534
+ verifyCommand: z.string().optional(),
14535
+ prebuildOutput: z.string().optional()
14536
+ }).optional(),
14537
+ themeSource: z.enum(["local", "cms"]).optional(),
14538
+ activeTheme: z.string().optional(),
14539
+ enablePortal: z.boolean().optional(),
14540
+ dashboard: dashboardConfigSchema.optional()
14541
+ }).strict();
14542
+ const pushSdkConfigBodySchema = z.object({
14543
+ config: riverbankSiteConfigInputSchema,
14544
+ forceContentTypes: z.boolean().optional(),
14545
+ mode: z.enum(["apply", "preview"]).optional(),
14546
+ expectedMailingListProviderScope: z.unknown().transform((value, ctx) => {
14547
+ if (value === undefined) return undefined;
14548
+ const parsed = parseMailingListProviderScope(value);
14549
+ if (parsed.ok) return parsed.value;
14550
+ ctx.addIssue({
14551
+ code: "custom",
14552
+ message: "Invalid mailing-list provider scope"
14553
+ });
14554
+ return z.NEVER;
14555
+ }).optional()
14556
+ }).strict();
11910
14557
 
11911
14558
  //#endregion
11912
14559
  //#region ../api/src/sdkContracts.ts
@@ -14388,25 +17035,6 @@ function parsePublicProductCategorySelector(value) {
14388
17035
  return undefined;
14389
17036
  }
14390
17037
 
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
17038
  //#endregion
14411
17039
  //#region ../api/src/token.ts
14412
17040
  async function getAuthHeaders(supabase) {
@@ -14430,6 +17058,18 @@ async function getAuthHeaders(supabase) {
14430
17058
  //#region ../surfaces/src/index.ts
14431
17059
  const SURFACE_IDS = ["core", "macadamia"];
14432
17060
  const DEFAULT_SURFACE_ID = "core";
17061
+ const SURFACE_ASSISTANT_MARKS = {
17062
+ core: {
17063
+ src: "/logo.svg",
17064
+ width: 22,
17065
+ height: 20
17066
+ },
17067
+ macadamia: {
17068
+ src: "/surfaces/macadamia/assistant-mark.svg",
17069
+ width: 26,
17070
+ height: 26
17071
+ }
17072
+ };
14433
17073
  const SURFACE_DOCUMENT_DATA_ATTRIBUTE = "data-dashboard-surface";
14434
17074
  function isSurfaceId(value) {
14435
17075
  return typeof value === "string" && SURFACE_IDS.includes(value);
@@ -14895,7 +17535,6 @@ function getRevalidateTag() {
14895
17535
  }
14896
17536
  return revalidateTag;
14897
17537
  }
14898
- let sdkVersion;
14899
17538
  /**
14900
17539
  * Generate a UUID, with fallback for environments where crypto.randomUUID isn't available.
14901
17540
  */
@@ -14910,19 +17549,6 @@ function generateRequestId() {
14910
17549
  });
14911
17550
  }
14912
17551
  /**
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
17552
  * Parse the Retry-After header value to milliseconds.
14927
17553
  * The header can be either:
14928
17554
  * - A number of seconds (e.g., "120")
@@ -15145,7 +17771,7 @@ async function createAuthenticatedApiClient(supabase) {
15145
17771
  }
15146
17772
  return createCMSClient(authHeaders);
15147
17773
  }
15148
- function createRawCMSClient(getHeaders = () => ({}), baseUrl) {
17774
+ function createRawCMSClient(getHeaders = () => ({}), baseUrl, { sdkVersion } = {}) {
15149
17775
  return ({ endpoint, body, params, options = {} }) => {
15150
17776
  const plan = buildApiRequestPlan({
15151
17777
  endpoint,
@@ -15279,8 +17905,8 @@ function createParsedClient(rawClient) {
15279
17905
  return parseSuccessResponse(endpoint, response, config);
15280
17906
  };
15281
17907
  }
15282
- function createCMSClient(headers = {}, baseUrl) {
15283
- return createParsedClient(createRawCMSClient(() => headers, baseUrl));
17908
+ function createCMSClient(headers = {}, baseUrl, options) {
17909
+ return createParsedClient(createRawCMSClient(() => headers, baseUrl, options));
15284
17910
  }
15285
17911
  const createServerAPIClient = (apiKey, baseUrl) => {
15286
17912
  const normalizedApiKey = normalizeCmsApiKey(apiKey);
@@ -15290,11 +17916,11 @@ const createServerAPIClient = (apiKey, baseUrl) => {
15290
17916
  const authHeaders = { Authorization: `Bearer ${normalizedApiKey}` };
15291
17917
  return createCMSClient(authHeaders, baseUrl);
15292
17918
  };
15293
- function createInternalAPIClient(baseUrl) {
15294
- return createCMSClient({}, baseUrl);
17919
+ function createInternalAPIClient(baseUrl, options) {
17920
+ return createCMSClient({}, baseUrl, options);
15295
17921
  }
15296
- function createBearerAPIClient(token, baseUrl) {
15297
- return createParsedClient(createRawCMSClient(() => ({ Authorization: `Bearer ${typeof token === "function" ? token() : token}` }), baseUrl));
17922
+ function createBearerAPIClient(token, baseUrl, options) {
17923
+ return createParsedClient(createRawCMSClient(() => ({ Authorization: `Bearer ${typeof token === "function" ? token() : token}` }), baseUrl, options));
15298
17924
  }
15299
17925
  /** Positive receipt evidence is kept distinct from an HTTP or request failure. */
15300
17926
  function requireUsableCommitProjection(response, parsed) {
@@ -15302,13 +17928,6 @@ function requireUsableCommitProjection(response, parsed) {
15302
17928
  throw new ApiCommittedWriteNotice(pageCommitConfirmationEnvelopeSchema.parse(parsed).data);
15303
17929
  }
15304
17930
 
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
17931
  //#endregion
15313
17932
  //#region ../api/src/appointmentServiceLocation.ts
15314
17933
  const APPOINTMENT_SERVICE_LOCATION_TYPE_VALUES = [
@@ -15329,67 +17948,23 @@ const APPOINTMENT_RESERVATION_STATUS_VALUES = [
15329
17948
  ];
15330
17949
 
15331
17950
  //#endregion
15332
- //#region ../api/src/utils/isRecord.ts
15333
- function isRecord$5(value) {
15334
- return typeof value === "object" && value !== null && !Array.isArray(value);
15335
- }
15336
-
15337
- //#endregion
15338
- //#region ../api/src/utils/error-propagation.ts
15339
- /**
15340
- * Extracts an error message from an API error response body.
15341
- * Checks for our standard API error format: { error: { message: "..." } }
15342
- * Also handles legacy formats with `error` or `message` as strings.
15343
- */
15344
- function extractApiErrorMessage(body) {
15345
- if (!isRecord$5(body)) {
15346
- return null;
15347
- }
15348
- if (isRecord$5(body.error)) {
15349
- if (typeof body.error.message === "string" && body.error.message.trim().length > 0) {
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);
17951
+ //#region ../api/src/wholeSeriesRuns.ts
17952
+ const DELETE_EVENT_OFFERING_CONFLICT_REASONS = [
17953
+ "has_registrations",
17954
+ "has_payments",
17955
+ "published_page",
17956
+ "inconsistent_ownership"
17957
+ ];
17958
+ function isDeleteEventOfferingConflictReason(value) {
17959
+ return DELETE_EVENT_OFFERING_CONFLICT_REASONS.some((reason) => reason === value);
15392
17960
  }
17961
+ const EVENT_SERIES_REGISTRATION_STATUSES = [
17962
+ "pending_payment",
17963
+ "enrolled",
17964
+ "waitlist",
17965
+ "cancelled",
17966
+ "completed"
17967
+ ];
15393
17968
 
15394
17969
  //#endregion
15395
17970
  //#region ../api/src/bookingActivity.ts
@@ -15409,6 +17984,8 @@ const BOOKING_ACTIVITY_TYPE_VALUES = [
15409
17984
  "event_booking_restored",
15410
17985
  "waitlist_joined",
15411
17986
  "balance_paid",
17987
+ "payment_recorded",
17988
+ "payment_waived",
15412
17989
  "appointment_cancelled",
15413
17990
  "booking_closure_created",
15414
17991
  "booking_closure_removed",
@@ -15513,18 +18090,6 @@ const PERSON_DELETION_BLOCKER_CATEGORY_VALUES = [
15513
18090
  "contact_corrections"
15514
18091
  ];
15515
18092
 
15516
- //#endregion
15517
- //#region ../api/src/wholeSeriesRuns.ts
15518
- const DELETE_EVENT_OFFERING_CONFLICT_REASONS = [
15519
- "has_registrations",
15520
- "has_payments",
15521
- "published_page",
15522
- "inconsistent_ownership"
15523
- ];
15524
- function isDeleteEventOfferingConflictReason(value) {
15525
- return DELETE_EVENT_OFFERING_CONFLICT_REASONS.some((reason) => reason === value);
15526
- }
15527
-
15528
18093
  //#endregion
15529
18094
  //#region ../api/src/eventAttendanceLabels.ts
15530
18095
  function getEventAttendanceModeLabel(mode) {
@@ -16891,7 +19456,6 @@ async function adminClearSitePublicCache(apiClient, siteId, options) {
16891
19456
 
16892
19457
  //#endregion
16893
19458
  //#region ../api/src/accessAdmin.ts
16894
- const STAFF_OPERATIONAL_CAPABILITIES = ["appointments", "event_ops"];
16895
19459
  function getDefaultStaffCapabilities(surfaces) {
16896
19460
  if (surfaces.events && !surfaces.appointments) return ["event_ops"];
16897
19461
  return ["appointments"];
@@ -17056,6 +19620,7 @@ async function acceptSiteInvitation(apiClient, { body, options }) {
17056
19620
 
17057
19621
  //#endregion
17058
19622
  //#region ../api/src/staffAccess.ts
19623
+ const STAFF_OPERATIONAL_CAPABILITIES = ["appointments", "event_ops"];
17059
19624
  function roleAtLeast$1(role, minimum) {
17060
19625
  return SITE_ROLE_PERMISSION_LEVEL[role] >= SITE_ROLE_PERMISSION_LEVEL[minimum];
17061
19626
  }
@@ -17223,6 +19788,22 @@ const FLEXIBLE_BALANCE_ADMIN_ACTION_SOURCES = [
17223
19788
  "event_attendee_detail",
17224
19789
  "event_series_registration_detail"
17225
19790
  ];
19791
+ const settlementAuditSchema = {
19792
+ operationId: z.string().uuid(),
19793
+ orderItemIds: z.array(z.string().uuid()).nonempty().optional(),
19794
+ amountCents: z.number().int().positive().optional().nullable(),
19795
+ externalReference: z.string().optional().nullable(),
19796
+ note: z.string().optional().nullable()
19797
+ };
19798
+ const settleShopOrderPaymentSchema = z.union([z.object({
19799
+ ...settlementAuditSchema,
19800
+ action: z.enum(["mark_paid", "record_partial_payment"]),
19801
+ method: z.enum(OFFLINE_PAYMENT_METHODS),
19802
+ paidOn: z.iso.date()
19803
+ }), z.object({
19804
+ ...settlementAuditSchema,
19805
+ action: z.literal("mark_waived")
19806
+ })]);
17226
19807
 
17227
19808
  //#endregion
17228
19809
  //#region ../api/src/navigation/visibility.ts
@@ -24898,4 +27479,4 @@ function optionalTitle(data) {
24898
27479
  }
24899
27480
 
24900
27481
  //#endregion
24901
- export { normalizeMediaValueToIdentifierOnly as $, createCommandContext as $t, formatDiffDetail as A, getEnvPrefix as An, navigationIdentity as At, buildFieldChanges as B, portablePageDesignSchema as Bn, preparePushConfigTarget as Bt, reportFromOutcomes as C, requireEnvironmentWriteAuthorization as Cn, fileExists$1 as Ct, normalizeRemoteHomepageReference as D, transportSuggestion as Dn, writeJsonFileAtomic as Dt, createEmptySyncResult as E, formatError as En, writeJsonFile$1 as Et, calculatePullDiff as F, createManagementClient as Fn, navigationCommandItemToManagementInput as Ft, syncLocalMediaForPull as G, APPOINTMENT_SERVICE_VISIBILITY_VALUES as Gt, formatFieldPath as H, API_ENDPOINTS as Hn, extractApiErrorMessage as Ht, buildJsonDiff as I, ManagementApiError as In, normalizeNavigationConfigItemForCommand as It, admitBinaryWriteDestination as J, ApiRequestError as Jt, applyLocalMediaPush as K, createBearerAPIClient as Kt, hasPageOperations as L, sdkEventScheduleSchema as Ln, applyPushConfigPlan as Lt, hasPendingChanges as M, isLocalConfigured as Mn, convertPulledFooterToLocal as Mt, findChangedFieldsWithValues as N, isRemoteConfigured as Nn, convertPulledNavigationToLocal as Nt, calculateDiff as O, CliExit as On, isMediaValue as Ot, formatHumanDiff as P, loadSdkEnvironment as Pn, convertPulledPagesToLocal as Pt, normalizeMediaIdentifierInput as Q, createClientForEnv as Qt, pageMetadataForUpsert as R, sdkSettingsUpdateSchema as Rn, createPushConfigCommand as Rt, planCliCommandBatchForBindings as S, planEnvironmentWriteAuthorization as Sn, ensureDir$1 as St, executeResidualSettingsSyncPlan as T, ErrorCodes as Tn, readJsonFile as Tt, revalidateLocalMediaPushPlan as U, ApiEnvelopeError as Un, normalizeApiError as Ut, formatFieldChange as V, sdkConfiguredBookingVerticalConfigSchema as Vn, revalidatePushConfigPlan as Vt, syncLocalMediaForPush as W, unwrapResponse as Wn, isRecord$5 as Wt, writeBinaryFileAtomic as X, bindRequestTimeout as Xt, readStableBinaryFile as Y, mapEnvironmentExecutionOutcomesJson as Yt, sha256 as Z, confirmAction as Zt, commandExecutionToReport as _, withErrorHandling as _n, metadataFilePath as _t, executeEntryCommandPlanAsReport as a, getOutputContext as an, readEntries as at, executeCompiledCliCommandPlanAsReport as b, parseEnvironmentSelection as bn, writeMetadataFile as bt, titleFromEntryData as c, managementClientConfigFor as cn, readEventsMeta as ct, unsafeAsCliEntryIdentifier as d, parseJsonData as dn, readNavigationMeta as dt, createGetCommand as en, walkLocalContentMedia as et, unsafeAsCliFormSlug as f, parsePaginationOptions as fn, readPagesMeta as ft, commandCompileFailureToReport as g, withConfirmation as gn, ensureMetadataDir as gt, mutationCounterForEffect as h, withCommandContext as hn, writeMediaManifest as ht, entrySiteCommandTypes as i, formatDateShort as in, findLegacyEntryFiles as it, formatDiffSummary as j, getEnvironmentDebugInfo as jn, navigationIdentityMap as jt, calculateFooterDiff as k, createOutput as kn, duplicateNavigationIdentityMessage as kt, unsafeAsCliBlockIdentifier as l, mapEntryForOutput as ln, readFooterMeta as lt, commandReportToSyncResult as m, runWithInvocation as mn, readVenuesMeta as mt, entryCommandCompileErrorToReport as n, createOutputForCommand as nn, downloadMedia as nt, executeEntryCommandPlanAsSyncResult as o, getOutputOnlyContext as on, readAllMeta as ot, unsafeAsCliPageIdentifier as p, resolveCommandPath as pn, readSettingsMeta as pt, prepareLocalMediaForPush as q, deriveCmsApiUrlFromDashboardUrl as qt, entryCommandReportToSyncResult as r, currentInvocationEnvelope as rn, extractMediaIdentifiers as rt, syncResultForEntryCommandCompileError as s, handleCommandError as sn, readEventCategoriesMeta as st, compileEntryPushCommands as t, createListCommand as tn, buildStorageUrl as tt, unsafeAsCliContentTypeKey as u, parseJsonArray as un, readFormsMeta as ut, commandReportHasFailures as v, withOutputOnlyErrorHandling as vn, metadataTargetFromEnvironment as vt, mergeManySyncResults as w, withRiverbankEnv as wn, listFiles as wt, formatManagementError as x, planEnvironmentExecution as xn, writeMetadataRecordMap as xt, executeCliCommandPlan as y, executeEnvironmentPlan as yn, readMetadataFile as yt, calculateEntryDiffs as z, linkPayloadSchema as zn, loadPushConfigTarget as zt };
27482
+ 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 };