@riverbankcms/sdk 0.91.0 → 0.93.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (118) hide show
  1. package/dist/{PreviewEditorSidebar-3TT47WCF.mjs → PreviewEditorSidebar-D3PAF7NW.mjs} +3 -3
  2. package/dist/{PreviewEditorUI-ORCLLUNZ.mjs → PreviewEditorUI-G5JVJEIJ.mjs} +3 -3
  3. package/dist/SdkPreviewModeRuntime-NHTLRNQD.mjs +8 -0
  4. package/dist/_dts/api/src/bookingConfig.d.ts +13 -1
  5. package/dist/_dts/api/src/bookingOperations.d.ts +1 -0
  6. package/dist/_dts/api/src/bookingRefundOutcomes.d.ts +6 -6
  7. package/dist/_dts/api/src/common/envelope.d.ts +10 -0
  8. package/dist/_dts/api/src/customerMembershipOutcomes.d.ts +4 -4
  9. package/dist/_dts/api/src/index.d.ts +2 -2
  10. package/dist/_dts/api/src/publicExperienceEndpoints.d.ts +6 -0
  11. package/dist/_dts/api/src/request.d.ts +1 -1
  12. package/dist/_dts/blocks/src/contracts/proof-block-islands.d.ts +32 -0
  13. package/dist/_dts/blocks/src/system/blocks/courses/shared/pricing.d.ts +12 -1
  14. package/dist/_dts/blocks/src/system/blocks/courses/shared/schemas.d.ts +12 -0
  15. package/dist/_dts/blocks/src/system/blocks/courses/shared/types.d.ts +4 -1
  16. package/dist/_dts/blocks/src/system/blocks/events/shared/schemas.d.ts +44 -0
  17. package/dist/_dts/blocks/src/system/manifest/fieldValidation/index.d.ts +8 -0
  18. package/dist/_dts/blocks/src/system/runtime/api/types.d.ts +8 -1
  19. package/dist/_dts/blocks/src/system/runtime/hooks/useHeadlessBookingFlow.d.ts +8 -1
  20. package/dist/_dts/blocks/src/system/runtime/hooks/useHeadlessBookingFlow.state.d.ts +1 -0
  21. package/dist/_dts/blocks/src/system/runtime/nodes/booking-form.headless.state.d.ts +4 -0
  22. package/dist/_dts/blocks/src/system/runtime/nodes/booking-form.interactive.d.ts +40 -0
  23. package/dist/_dts/blocks/src/system/runtime/nodes/booking-form.reserved-fields.d.ts +1 -1
  24. package/dist/_dts/blocks/src/system/runtime/nodes/booking-form.state.d.ts +29 -0
  25. package/dist/_dts/blocks/src/system/runtime/nodes/booking-runtime-sources.d.ts +1 -0
  26. package/dist/_dts/blocks/src/system/runtime/nodes/course-registration/components.d.ts +6 -0
  27. package/dist/_dts/blocks/src/system/runtime/nodes/course-registration/useCourseRegistrationWizard.d.ts +8 -0
  28. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/TicketSelectionStep.d.ts +8 -0
  29. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/hooks.d.ts +1 -0
  30. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/types.d.ts +1 -1
  31. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/useEventRegistrationSubmit.d.ts +1 -0
  32. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/useEventRegistrationWizard.d.ts +9 -0
  33. package/dist/_dts/content-editor/src/controller/EntryControllerProvider.d.ts +14 -14
  34. package/dist/_dts/content-editor/src/controller/useEntrySyncHelpers.d.ts +1 -1
  35. package/dist/_dts/content-editor/src/hooks/useEntryActions.d.ts +5 -15
  36. package/dist/_dts/content-editor/src/ui/editorSnapshot.d.ts +46 -0
  37. package/dist/_dts/content-editor/src/ui/editorUiStore.d.ts +43 -36
  38. package/dist/_dts/content-model/src/entry-slots/types.d.ts +110 -2
  39. package/dist/_dts/core/src/branded.d.ts +3 -0
  40. package/dist/_dts/core/src/event-pricing.d.ts +18 -1
  41. package/dist/_dts/core/src/money-input.d.ts +34 -4
  42. package/dist/_dts/db/src/generated/supabase/database.types.d.ts +70 -0
  43. package/dist/_dts/media-core/src/fileAssetCompatibility.d.ts +1 -1
  44. package/dist/_dts/media-core/src/filePersistence.d.ts +5 -1
  45. package/dist/_dts/media-core/src/fileStorage.d.ts +1 -0
  46. package/dist/_dts/media-core/src/fileUploadFinalization.d.ts +29 -1
  47. package/dist/_dts/media-core/src/index.d.ts +1 -0
  48. package/dist/_dts/media-core/src/legacyMediaValue.d.ts +11 -0
  49. package/dist/_dts/media-core/src/videoPrep.d.ts +15 -6
  50. package/dist/_dts/media-core/src/videoSlotPolicy.d.ts +5 -5
  51. package/dist/_dts/media-react/src/hooks/useApiMediaUploader.d.ts +2 -1
  52. package/dist/_dts/sdk/src/cli/commands/push/execute/diff.d.ts +2 -0
  53. package/dist/_dts/sdk/src/cli/commands/push/execute/localMediaReporting.d.ts +23 -0
  54. package/dist/_dts/sdk/src/cli/commands/push/scopes/commandBackedDatedOfferingScope.d.ts +54 -0
  55. package/dist/_dts/sdk/src/cli/commands/push/scopes/mediaPrep.d.ts +7 -2
  56. package/dist/_dts/sdk/src/cli/commands/push/scopes/types.d.ts +6 -0
  57. package/dist/_dts/sdk/src/cli/media/local-sync.d.ts +35 -0
  58. package/dist/_dts/sdk/src/rendering/islands/types.d.ts +23 -1
  59. package/dist/_dts/sdk/src/version.d.ts +1 -1
  60. package/dist/{chunk-GDT2QGIL.mjs → chunk-A63UXN3P.mjs} +21 -19
  61. package/dist/{chunk-U5YM4AWE.mjs → chunk-AJZGT6YG.mjs} +1 -1
  62. package/dist/{chunk-AFPMCLS7.mjs → chunk-AQ6RT6LZ.mjs} +2 -2
  63. package/dist/{chunk-ZA2S7H34.mjs → chunk-NT76QGIJ.mjs} +98 -1
  64. package/dist/{chunk-RQUZYSES.mjs → chunk-PCNCETBI.mjs} +1196 -426
  65. package/dist/{chunk-S3YHJWEU.mjs → chunk-XIK2HYI3.mjs} +7 -7
  66. package/dist/cli/index.mjs +749 -166
  67. package/dist/client/{accordion-AFX37652.mjs → accordion-RNQIPG55.mjs} +2 -2
  68. package/dist/client/{bookingForm-32V5QCOW.mjs → bookingForm-HYEEVRLP.mjs} +89 -14
  69. package/dist/client/bookings.mjs +243 -17
  70. package/dist/client/{carousel-TJGQJ7ER.mjs → carousel-W7DQ3YPU.mjs} +3 -3
  71. package/dist/client/{cart-JUU2JEW4.mjs → cart-PXJ3SXXO.mjs} +7 -7
  72. package/dist/client/{checkout-RD6BTFX4.mjs → checkout-X3JYSOXI.mjs} +7 -7
  73. package/dist/client/{chunk-MLBNVN4H.mjs → chunk-6SYGVVWA.mjs} +67 -4
  74. package/dist/client/{chunk-E6VIWZUG.mjs → chunk-DUOHLWLF.mjs} +3 -3
  75. package/dist/client/{chunk-6QZIBNGV.mjs → chunk-IXDSXF2D.mjs} +154 -16
  76. package/dist/client/{chunk-CKXEUWWA.mjs → chunk-J6TMMMXW.mjs} +3 -3
  77. package/dist/client/{chunk-PMNAV6LO.mjs → chunk-JNE6UI6U.mjs} +7 -6
  78. package/dist/client/{chunk-ISAUXU5R.mjs → chunk-OLAOAZEO.mjs} +1 -1
  79. package/dist/client/{chunk-RWYFG625.mjs → chunk-PCHACQEB.mjs} +30 -2
  80. package/dist/client/{chunk-FMZZHAIR.mjs → chunk-PICR3NTQ.mjs} +52 -5
  81. package/dist/client/{chunk-6RN62XBV.mjs → chunk-Q47XGAGS.mjs} +1 -1
  82. package/dist/client/{chunk-WHZQDPBZ.mjs → chunk-TNGL2WJF.mjs} +1 -0
  83. package/dist/client/{chunk-C4LP6BPG.mjs → chunk-WERRJXNJ.mjs} +1 -1
  84. package/dist/client/{chunk-RMOFBBNA.mjs → chunk-YYVGSYBO.mjs} +6 -6
  85. package/dist/client/client.mjs +641 -54
  86. package/dist/client/{courseRegistration-X63QAIVV.mjs → courseRegistration-QZ67PVHO.mjs} +111 -11
  87. package/dist/client/{eventCalendar-5T24GCZL.mjs → eventCalendar-V3BDHQNT.mjs} +9 -9
  88. package/dist/client/{eventCombined-AZVTF22X.mjs → eventCombined-XYFRN2RG.mjs} +9 -9
  89. package/dist/client/{eventDetails-7GPFNLGF.mjs → eventDetails-TFE5TWKV.mjs} +9 -9
  90. package/dist/client/{eventListing-QEUMTMUM.mjs → eventListing-IQ6AGWZ6.mjs} +9 -9
  91. package/dist/client/{eventRegistration-P2EDKCQZ.mjs → eventRegistration-WGJVFKQJ.mjs} +9 -9
  92. package/dist/client/{form-NUJ7H4IN.mjs → form-6UZY6RUA.mjs} +3 -3
  93. package/dist/client/{headerSection-JB7LECPL.mjs → headerSection-RSFXY6V3.mjs} +3 -3
  94. package/dist/client/hooks.mjs +57 -5
  95. package/dist/client/{locationMap-UEYWUCSW.mjs → locationMap-24FH67I4.mjs} +3 -3
  96. package/dist/client/{newsletterForm-S2YZQ2X7.mjs → newsletterForm-QVL3MLTN.mjs} +3 -3
  97. package/dist/client/{productDetail-H5MADGG7.mjs → productDetail-ZDYN3G4T.mjs} +7 -7
  98. package/dist/client/{productList-DK7YON7H.mjs → productList-4BMJLQHZ.mjs} +7 -7
  99. package/dist/client/rendering/client.mjs +503 -36
  100. package/dist/client/rendering/islands.mjs +19 -19
  101. package/dist/client/rendering.mjs +583 -39
  102. package/dist/client/{shop-6PA7RDRJ.mjs → shop-U5C634FF.mjs} +7 -7
  103. package/dist/preview-next/client/runtime.mjs +5 -5
  104. package/dist/{sdk-runtime-JP5347OB.mjs → sdk-runtime-VGV46WVB.mjs} +6 -6
  105. package/dist/server/components.mjs +287 -68
  106. package/dist/server/config-validation.mjs +58 -5
  107. package/dist/server/config.mjs +58 -5
  108. package/dist/server/data.mjs +57 -5
  109. package/dist/server/index.mjs +104 -29
  110. package/dist/server/next.mjs +347 -85
  111. package/dist/server/page-converter.mjs +22 -5
  112. package/dist/server/prebuild.mjs +1 -1
  113. package/dist/server/rendering/server.mjs +287 -68
  114. package/dist/server/rendering.mjs +287 -68
  115. package/dist/server/routing.mjs +131 -67
  116. package/dist/server/server.mjs +189 -94
  117. package/package.json +1 -1
  118. package/dist/SdkPreviewModeRuntime-GMCDQPZZ.mjs +0 -8
@@ -56,13 +56,14 @@ function parseUuidBrand(value, unsafeConstructor) {
56
56
  }
57
57
  return ok(unsafeConstructor(normalized));
58
58
  }
59
- var UUID_PATTERN, unsafeAsSiteId, unsafeAsStorageBucketName, unsafeAsPositiveByteCount, unsafeAsPositiveSeconds, parseSiteId;
59
+ var UUID_PATTERN, unsafeAsSiteId, unsafeAsStorageBucketName, unsafeAsStoragePath, unsafeAsPositiveByteCount, unsafeAsPositiveSeconds, parseSiteId;
60
60
  var init_branded = __esm({
61
61
  "../core/src/branded.ts"() {
62
62
  init_result();
63
63
  UUID_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
64
64
  unsafeAsSiteId = (id) => id;
65
65
  unsafeAsStorageBucketName = (value) => value;
66
+ unsafeAsStoragePath = (value) => value;
66
67
  unsafeAsPositiveByteCount = (value) => value;
67
68
  unsafeAsPositiveSeconds = (value) => value;
68
69
  parseSiteId = (value) => parseUuidBrand(value, unsafeAsSiteId);
@@ -168,6 +169,7 @@ function publicEventPricingType(pricing) {
168
169
  return "free";
169
170
  case "single_paid":
170
171
  case "ticket_types":
172
+ case "sliding_scale":
171
173
  case "course":
172
174
  return isFreePublicEventPriceDisplay(pricing.display) ? "free" : "paid";
173
175
  case "external":
@@ -650,8 +652,19 @@ var init_types = __esm({
650
652
  });
651
653
 
652
654
  // ../media-core/src/fileSafety.ts
655
+ function containsUnsafePathSegmentCharacter(value) {
656
+ return value.includes("/") || value.includes("\\") || CONTROL_CHARACTER_PATTERN.test(value);
657
+ }
658
+ function isReservedPathSegment(value) {
659
+ return value === "." || value === "..";
660
+ }
661
+ function isUnsafePathSegment(segment) {
662
+ return segment.trim().length === 0 || segment !== segment.trim() || isReservedPathSegment(segment) || containsUnsafePathSegmentCharacter(segment);
663
+ }
664
+ var CONTROL_CHARACTER_PATTERN;
653
665
  var init_fileSafety = __esm({
654
666
  "../media-core/src/fileSafety.ts"() {
667
+ CONTROL_CHARACTER_PATTERN = /[\u0000-\u001f\u007f]/u;
655
668
  }
656
669
  });
657
670
 
@@ -690,10 +703,20 @@ var init_fileDomain = __esm({
690
703
  });
691
704
 
692
705
  // ../media-core/src/fileStorage.ts
706
+ function parseStoragePath(value) {
707
+ return joinStoragePathSegments(value.split("/"));
708
+ }
709
+ function joinStoragePathSegments(segments) {
710
+ if (segments.some(isUnsafePathSegment)) {
711
+ return err("unsafe-path-segment");
712
+ }
713
+ return ok(unsafeAsStoragePath(segments.join("/")));
714
+ }
693
715
  var FILE_STORAGE_BUCKETS, PRIVATE_SITE_FILE_BUCKET_MIME_TYPES;
694
716
  var init_fileStorage = __esm({
695
717
  "../media-core/src/fileStorage.ts"() {
696
718
  init_src();
719
+ init_fileSafety();
697
720
  FILE_STORAGE_BUCKETS = {
698
721
  publicSite: unsafeAsStorageBucketName("media"),
699
722
  privateSite: unsafeAsStorageBucketName("site-files-private"),
@@ -845,8 +868,20 @@ var init_uploadVerificationContract = __esm({
845
868
  new Set(PRODUCT_FILE_EXTENSION_TAGS);
846
869
  }
847
870
  });
871
+
872
+ // ../media-core/src/videoPrep.ts
873
+ function unsafeAsPositiveVideoDurationSeconds(value) {
874
+ return value;
875
+ }
848
876
  var init_videoPrep = __esm({
849
877
  "../media-core/src/videoPrep.ts"() {
878
+ init_src();
879
+ ({
880
+ maxRawByteCount: unsafeAsPositiveByteCount(500 * 1024 * 1024),
881
+ inlineContentMaxDurationSeconds: unsafeAsPositiveVideoDurationSeconds(60),
882
+ backgroundVideoMaxDurationSeconds: unsafeAsPositiveVideoDurationSeconds(30),
883
+ targetMaxDimensionPixels: 1080
884
+ });
850
885
  }
851
886
  });
852
887
 
@@ -928,10 +963,10 @@ var init_identifiers = __esm({
928
963
 
929
964
  // ../media-core/src/url.ts
930
965
  function isRelativeDirectImageStoragePath(storagePath) {
931
- return RELATIVE_STORAGE_PATH_PREFIXES.some((prefix) => storagePath.startsWith(prefix));
966
+ return RELATIVE_STORAGE_PATH_PREFIXES.some((prefix) => storagePath.startsWith(prefix)) && parseStoragePath(storagePath).ok;
932
967
  }
933
968
  function isSiteScopedDirectImageStoragePath(storagePath) {
934
- return storagePath.startsWith("sites/");
969
+ return storagePath.startsWith("sites/") && parseStoragePath(storagePath).ok;
935
970
  }
936
971
  function encodeStoragePath(storagePath) {
937
972
  return storagePath.split("/").map((segment) => encodeURIComponent(segment)).join("/");
@@ -997,6 +1032,7 @@ function getDirectImageUrl(supabaseUrl, storagePath, storageBucket = "media", si
997
1032
  var RELATIVE_STORAGE_PATH_PREFIXES;
998
1033
  var init_url = __esm({
999
1034
  "../media-core/src/url.ts"() {
1035
+ init_fileStorage();
1000
1036
  RELATIVE_STORAGE_PATH_PREFIXES = [
1001
1037
  "originals/",
1002
1038
  "variants/",
@@ -1142,6 +1178,27 @@ var init_renderableMediaAsset = __esm({
1142
1178
  }
1143
1179
  });
1144
1180
 
1181
+ // ../media-core/src/legacyMediaValue.ts
1182
+ function legacyStringMediaTypeForField(field) {
1183
+ if (field.type === "file") {
1184
+ return field.fileKinds?.[0] ?? "document";
1185
+ }
1186
+ return field.mediaKinds?.[0] ?? "image";
1187
+ }
1188
+ function normalizeLegacyStringMediaValueForField(value, field) {
1189
+ if (typeof value !== "string") return value;
1190
+ const src = value.trim();
1191
+ if (!src) return value;
1192
+ return {
1193
+ type: legacyStringMediaTypeForField(field),
1194
+ src
1195
+ };
1196
+ }
1197
+ var init_legacyMediaValue = __esm({
1198
+ "../media-core/src/legacyMediaValue.ts"() {
1199
+ }
1200
+ });
1201
+
1145
1202
  // ../media-core/src/index.ts
1146
1203
  var init_src2 = __esm({
1147
1204
  "../media-core/src/index.ts"() {
@@ -1181,6 +1238,7 @@ var init_src2 = __esm({
1181
1238
  init_mediaUsageProjection();
1182
1239
  init_portableMediaReferences();
1183
1240
  init_renderableMediaAsset();
1241
+ init_legacyMediaValue();
1184
1242
  }
1185
1243
  });
1186
1244
  var transformStepSchema, bindingPathSchema, bindingSchema, repeatSchema, conditionSchema, dataScopeSchema, nodePropsSchema, nodeSchema, nodeTreeSchema, NodeSchema, NodeTreeSchema;
@@ -18801,9 +18859,12 @@ var init_schemas = __esm({
18801
18859
  });
18802
18860
  coursePricingSchema = z.object({
18803
18861
  bookingRequirement: z.enum(["none", "required"]),
18804
- pricingStrategy: z.enum(["free", "single", "tiered"]),
18862
+ pricingStrategy: z.enum(["free", "single", "tiered", "sliding_scale"]),
18805
18863
  currency: z.string(),
18806
18864
  singlePriceAmount: z.number().nullable(),
18865
+ slidingMinAmount: z.number().nullable().optional(),
18866
+ slidingMaxAmount: z.number().nullable().optional(),
18867
+ slidingSuggestedAmount: z.number().nullable().optional(),
18807
18868
  paymentTerms: blockBookingPaymentTermsSchema,
18808
18869
  options: z.array(coursePricingOptionSchema)
18809
18870
  });
@@ -18964,6 +19025,19 @@ var init_schemas2 = __esm({
18964
19025
  ticketTypes: z.array(publicEventTicketTypeSchema),
18965
19026
  display: publicEventTicketTypePriceDisplaySchema
18966
19027
  }),
19028
+ z.object({
19029
+ kind: z.literal("sliding_scale"),
19030
+ minAmount: z.number(),
19031
+ maxAmount: z.number(),
19032
+ suggestedAmount: z.number().nullable(),
19033
+ currency: z.string(),
19034
+ display: z.object({
19035
+ kind: z.literal("range"),
19036
+ minAmount: z.number(),
19037
+ maxAmount: z.number(),
19038
+ currency: z.string()
19039
+ })
19040
+ }),
18967
19041
  z.object({
18968
19042
  kind: z.literal("course"),
18969
19043
  display: z.object({ kind: z.literal("course"), display: publicCoursePriceDisplaySchema })
@@ -19418,7 +19492,7 @@ function isRecord5(value) {
19418
19492
  }
19419
19493
  function isPublicEventPricing(value) {
19420
19494
  if (!isRecord5(value) || typeof value.kind !== "string") return false;
19421
- return value.kind === "drop_in" || value.kind === "free_registration" || value.kind === "single_paid" || value.kind === "ticket_types" || value.kind === "course" || value.kind === "external";
19495
+ return value.kind === "drop_in" || value.kind === "free_registration" || value.kind === "single_paid" || value.kind === "sliding_scale" || value.kind === "ticket_types" || value.kind === "course" || value.kind === "external";
19422
19496
  }
19423
19497
  function isPublicEventTicketType(value) {
19424
19498
  return isRecord5(value) && typeof value.id === "string";
@@ -21846,6 +21920,7 @@ var init_booking_form_reserved_fields = __esm({
21846
21920
  "selectedDate",
21847
21921
  "selectedSlot",
21848
21922
  "discountCode",
21923
+ "selectedSlidingAmountInput",
21849
21924
  "selectedCollectionOptionKind",
21850
21925
  "selectedAppointmentPackageId"
21851
21926
  ];
@@ -28587,6 +28662,9 @@ function internalNavigationLinkToLocalLink(item) {
28587
28662
  }
28588
28663
  return void 0;
28589
28664
  }
28665
+
28666
+ // ../blocks/src/system/manifest/fieldValidation/index.ts
28667
+ init_src2();
28590
28668
  var nullableString = z.union([z.string().min(1), z.null()]);
28591
28669
  var resolvedInternalLinkKeys = ["entityId", "entityType", "href", "title", "typeLabel"];
28592
28670
  var internalRouteOnlyLinkSchema = z.object({
@@ -29161,7 +29239,10 @@ function deriveFieldValidationPlan(field, path28 = [field.id]) {
29161
29239
  return makePlan("richText", "object", field, path28, required, commonConstraints(required, { kind: "richTextDoc" }));
29162
29240
  case "media":
29163
29241
  case "file":
29164
- return makePlan("media", "object", field, path28, required, commonConstraints(required, { kind: "mediaObject" }));
29242
+ return {
29243
+ ...makePlan("media", "object", field, path28, required, commonConstraints(required, { kind: "mediaObject" })),
29244
+ legacyStringType: legacyStringMediaTypeForField(field)
29245
+ };
29165
29246
  case "link":
29166
29247
  return makePlan("link", "object", field, path28, required, commonConstraints(required, { kind: "linkObject" }));
29167
29248
  case "boolean":
@@ -29286,15 +29367,23 @@ function normalizeFieldValue(plan, value, ctx) {
29286
29367
  return normalizeRepeaterItems(plan, value, ctx);
29287
29368
  case "boolean":
29288
29369
  case "richText":
29289
- case "media":
29290
29370
  case "link":
29291
29371
  case "select":
29292
29372
  case "passthrough":
29293
29373
  return value;
29374
+ case "media":
29375
+ return normalizeMediaInput(plan, value);
29294
29376
  default:
29295
29377
  return assertNever2(plan);
29296
29378
  }
29297
29379
  }
29380
+ function normalizeMediaInput(plan, value) {
29381
+ return normalizeLegacyStringMediaValueForField(value, {
29382
+ type: plan.fieldType,
29383
+ mediaKinds: plan.fieldType === "media" ? [plan.legacyStringType] : void 0,
29384
+ fileKinds: plan.fieldType === "file" ? [plan.legacyStringType] : void 0
29385
+ });
29386
+ }
29298
29387
  function normalizeObjectChildren(children, value, ctx) {
29299
29388
  if (!value || typeof value !== "object" || Array.isArray(value)) return value;
29300
29389
  const normalized = {
@@ -42583,12 +42672,85 @@ function resolveApiBaseUrl() {
42583
42672
  }
42584
42673
 
42585
42674
  // ../api/src/common/envelope.ts
42675
+ var API_ERROR_CODES = [
42676
+ "auth:unauthenticated",
42677
+ "auth:token_expired",
42678
+ "auth:token_invalid",
42679
+ "auth:forbidden",
42680
+ "auth:mfa_required",
42681
+ "auth:sudo_required",
42682
+ "auth:insufficient_permissions",
42683
+ "auth:impersonation_restricted",
42684
+ "validation:invalid_input",
42685
+ "validation:missing_field",
42686
+ "validation:invalid_format",
42687
+ "validation:payload_too_large",
42688
+ "resource:not_found",
42689
+ "resource:already_exists",
42690
+ "resource:conflict",
42691
+ "resource:gone",
42692
+ "rate_limit:exceeded",
42693
+ "billing:payment_required",
42694
+ "billing:plan_limit_exceeded",
42695
+ "billing:pass_redemption_failed",
42696
+ "server:internal_error",
42697
+ "server:unavailable",
42698
+ "external:service_error",
42699
+ "external:registrar_orphaned_contact_profile",
42700
+ "network:connection_error",
42701
+ "network:timeout",
42702
+ "network:dns_error"
42703
+ ];
42704
+ var API_ERROR_CODE_SET = new Set(API_ERROR_CODES);
42705
+ function isApiErrorCode(value) {
42706
+ return typeof value === "string" && API_ERROR_CODE_SET.has(value);
42707
+ }
42586
42708
  function isApiError(result) {
42587
42709
  return result.success === false;
42588
42710
  }
42589
42711
  function isApiSuccess(result) {
42590
42712
  return result.success === true;
42591
42713
  }
42714
+ function isRecord9(value) {
42715
+ return typeof value === "object" && value !== null && !Array.isArray(value);
42716
+ }
42717
+ function isFieldError(value) {
42718
+ return isRecord9(value) && typeof value.field === "string" && typeof value.code === "string" && typeof value.message === "string";
42719
+ }
42720
+ function fieldErrorsFromUnknown(value) {
42721
+ if (!Array.isArray(value)) return null;
42722
+ return value.every(isFieldError) ? value : null;
42723
+ }
42724
+ function extractEnvelopeErrorObject(body) {
42725
+ if (!isRecord9(body)) return null;
42726
+ const error = body.error;
42727
+ if (!isRecord9(error)) return null;
42728
+ if ("success" in body && body.success !== false) {
42729
+ return null;
42730
+ }
42731
+ return error;
42732
+ }
42733
+ function extractApiEnvelopeErrorDetails(body) {
42734
+ const error = extractEnvelopeErrorObject(body);
42735
+ if (!error) {
42736
+ return {
42737
+ rawCode: null,
42738
+ code: null,
42739
+ message: null,
42740
+ status: null,
42741
+ fieldErrors: null
42742
+ };
42743
+ }
42744
+ const rawCode = typeof error.code === "string" ? error.code : null;
42745
+ const message = typeof error.message === "string" && error.message.trim().length > 0 ? error.message : null;
42746
+ return {
42747
+ rawCode,
42748
+ code: isApiErrorCode(rawCode) ? rawCode : null,
42749
+ message,
42750
+ status: typeof error.status === "number" && Number.isFinite(error.status) ? error.status : null,
42751
+ fieldErrors: fieldErrorsFromUnknown(error.fieldErrors)
42752
+ };
42753
+ }
42592
42754
  var ApiEnvelopeError = class extends Error {
42593
42755
  constructor(error) {
42594
42756
  super(error.message);
@@ -42985,6 +43147,7 @@ function createParsedClient(rawClient) {
42985
43147
  const auth = config2.auth ?? "user";
42986
43148
  if (!response.ok) {
42987
43149
  const body = await parseErrorBody(response);
43150
+ const envelopeError = extractApiEnvelopeErrorDetails(body);
42988
43151
  const requestId = response.headers.get("x-request-id") ?? void 0;
42989
43152
  const retryAfterMs = parseRetryAfterHeader(response.headers.get("retry-after"));
42990
43153
  throw new ApiRequestError(
@@ -42996,6 +43159,7 @@ function createParsedClient(rawClient) {
42996
43159
  auth,
42997
43160
  requestId,
42998
43161
  body,
43162
+ errorCode: envelopeError.code ?? void 0,
42999
43163
  retryAfterMs
43000
43164
  }
43001
43165
  );
@@ -43233,7 +43397,7 @@ function preset(id, verticalId, canonicalBehaviour, label, pluralLabel, descript
43233
43397
  }
43234
43398
 
43235
43399
  // ../api/src/utils/isRecord.ts
43236
- function isRecord9(value) {
43400
+ function isRecord10(value) {
43237
43401
  return typeof value === "object" && value !== null && !Array.isArray(value);
43238
43402
  }
43239
43403
  var APPOINTMENT_SETUP_MAX_MINUTES = 1440;
@@ -43506,7 +43670,7 @@ function sanitizeMarks(marks) {
43506
43670
  if (mark.type === "bold" || mark.type === "strong" || mark.type === "italic" || mark.type === "em") {
43507
43671
  return [{ type: mark.type }];
43508
43672
  }
43509
- if (mark.type === "link" && isRecord9(mark.attrs) && typeof mark.attrs.href === "string") {
43673
+ if (mark.type === "link" && isRecord10(mark.attrs) && typeof mark.attrs.href === "string") {
43510
43674
  const attrs = { href: mark.attrs.href };
43511
43675
  if (typeof mark.attrs.target === "string" && mark.attrs.target.trim().length > 0) {
43512
43676
  attrs.target = mark.attrs.target;
@@ -43521,10 +43685,10 @@ function sanitizeMarks(marks) {
43521
43685
  return sanitized.length > 0 ? sanitized : void 0;
43522
43686
  }
43523
43687
  function unwrapRichTextValue(value) {
43524
- if (isRecord9(value) && isRecord9(value.doc)) {
43688
+ if (isRecord10(value) && isRecord10(value.doc)) {
43525
43689
  return unwrapRichTextValue(value.doc);
43526
43690
  }
43527
- if (isRecord9(value) && value.type === "doc") {
43691
+ if (isRecord10(value) && value.type === "doc") {
43528
43692
  return {
43529
43693
  type: "doc",
43530
43694
  content: Array.isArray(value.content) ? value.content.filter(isTipTapNodeLike).map(coerceTipTapNode) : []
@@ -43533,7 +43697,7 @@ function unwrapRichTextValue(value) {
43533
43697
  return EMPTY_SITE_BANNER_BODY;
43534
43698
  }
43535
43699
  function isTipTapNodeLike(value) {
43536
- return isRecord9(value) && typeof value.type === "string";
43700
+ return isRecord10(value) && typeof value.type === "string";
43537
43701
  }
43538
43702
  function coerceTipTapNode(value) {
43539
43703
  return {
@@ -43541,9 +43705,9 @@ function coerceTipTapNode(value) {
43541
43705
  ...Array.isArray(value.content) ? { content: value.content.filter(isTipTapNodeLike).map(coerceTipTapNode) } : {},
43542
43706
  ...typeof value.text === "string" ? { text: value.text } : {},
43543
43707
  ...Array.isArray(value.marks) ? {
43544
- marks: value.marks.filter((mark) => isRecord9(mark) && typeof mark.type === "string").map((mark) => ({
43708
+ marks: value.marks.filter((mark) => isRecord10(mark) && typeof mark.type === "string").map((mark) => ({
43545
43709
  type: mark.type,
43546
- ...isRecord9(mark.attrs) ? { attrs: mark.attrs } : {}
43710
+ ...isRecord10(mark.attrs) ? { attrs: mark.attrs } : {}
43547
43711
  }))
43548
43712
  } : {}
43549
43713
  };
@@ -43659,7 +43823,7 @@ function collectCartLinkStats(items, depth = 0) {
43659
43823
  return result;
43660
43824
  }
43661
43825
  function isNavigationMenuValidationItem(value) {
43662
- if (!isRecord9(value)) return false;
43826
+ if (!isRecord10(value)) return false;
43663
43827
  return typeof value.label === "string";
43664
43828
  }
43665
43829
 
@@ -44912,24 +45076,24 @@ var ManagementApiError = class extends Error {
44912
45076
  function is404Error(error) {
44913
45077
  return error instanceof ManagementApiError && error.statusCode === 404;
44914
45078
  }
44915
- function isRecord10(value) {
45079
+ function isRecord11(value) {
44916
45080
  return Boolean(value && typeof value === "object" && !Array.isArray(value));
44917
45081
  }
44918
- function isFieldError(value) {
44919
- return isRecord10(value) && typeof value.field === "string" && typeof value.code === "string" && typeof value.message === "string";
45082
+ function isFieldError2(value) {
45083
+ return isRecord11(value) && typeof value.field === "string" && typeof value.code === "string" && typeof value.message === "string";
44920
45084
  }
44921
45085
  function parseApiResponse(value) {
44922
- if (!isRecord10(value)) return null;
45086
+ if (!isRecord11(value)) return null;
44923
45087
  if (value.success === true) {
44924
45088
  return { success: true, data: value.data };
44925
45089
  }
44926
45090
  if (value.success === false) {
44927
45091
  const error = value.error;
44928
- if (!isRecord10(error) || typeof error.code !== "string" || typeof error.message !== "string") {
45092
+ if (!isRecord11(error) || typeof error.code !== "string" || typeof error.message !== "string") {
44929
45093
  return { success: false, error: { code: "sdk:http-error", message: "Request failed", details: error } };
44930
45094
  }
44931
45095
  const fieldErrorsRaw = error.fieldErrors;
44932
- const fieldErrors = Array.isArray(fieldErrorsRaw) ? fieldErrorsRaw.filter(isFieldError) : void 0;
45096
+ const fieldErrors = Array.isArray(fieldErrorsRaw) ? fieldErrorsRaw.filter(isFieldError2) : void 0;
44933
45097
  return {
44934
45098
  success: false,
44935
45099
  error: {
@@ -46007,6 +46171,11 @@ function createOutput(options) {
46007
46171
  }
46008
46172
  };
46009
46173
  }
46174
+ function formatSize(bytes) {
46175
+ if (bytes < 1024) return `${bytes} B`;
46176
+ if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
46177
+ return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
46178
+ }
46010
46179
 
46011
46180
  // src/cli/errors.ts
46012
46181
  var ErrorCodes = {
@@ -46454,7 +46623,7 @@ function createListCommand(config2) {
46454
46623
 
46455
46624
  // src/cli/push-config.ts
46456
46625
  var DEFAULT_CONFIG_PATH = "./riverbank.config.ts";
46457
- function isRecord11(value) {
46626
+ function isRecord12(value) {
46458
46627
  return typeof value === "object" && value !== null;
46459
46628
  }
46460
46629
  function parseErrorDetails(value) {
@@ -46462,10 +46631,10 @@ function parseErrorDetails(value) {
46462
46631
  if (typeof value === "string") {
46463
46632
  return { message: value };
46464
46633
  }
46465
- if (!isRecord11(value)) return {};
46634
+ if (!isRecord12(value)) return {};
46466
46635
  const message = typeof value.message === "string" ? value.message : void 0;
46467
46636
  const fieldErrorsRaw = value.fieldErrors;
46468
- const fieldErrors = Array.isArray(fieldErrorsRaw) ? fieldErrorsRaw.filter(isRecord11).map((d) => ({
46637
+ const fieldErrors = Array.isArray(fieldErrorsRaw) ? fieldErrorsRaw.filter(isRecord12).map((d) => ({
46469
46638
  field: typeof d.field === "string" ? d.field : "",
46470
46639
  message: typeof d.message === "string" ? d.message : ""
46471
46640
  })).filter((d) => d.field.length > 0 && d.message.length > 0) : void 0;
@@ -46477,12 +46646,12 @@ function formatFieldErrors(fieldErrors) {
46477
46646
  }
46478
46647
  function getDashboardErrorMessage(responseJson, fallback) {
46479
46648
  if (!responseJson) return fallback;
46480
- if (isRecord11(responseJson) && responseJson.success === false && "error" in responseJson) {
46649
+ if (isRecord12(responseJson) && responseJson.success === false && "error" in responseJson) {
46481
46650
  const parsed = parseErrorDetails(responseJson.error);
46482
46651
  const msg = parsed.message ?? fallback;
46483
46652
  return msg + formatFieldErrors(parsed.fieldErrors);
46484
46653
  }
46485
- if (isRecord11(responseJson) && "error" in responseJson) {
46654
+ if (isRecord12(responseJson) && "error" in responseJson) {
46486
46655
  const parsed = parseErrorDetails(responseJson.error);
46487
46656
  const msg = parsed.message ?? fallback;
46488
46657
  return msg + formatFieldErrors(parsed.fieldErrors);
@@ -46807,7 +46976,7 @@ function duplicateNavigationIdentityMessage(error) {
46807
46976
 
46808
46977
  // src/cli/media/portable.ts
46809
46978
  var ENV_SPECIFIC_MEDIA_KEYS = ["assetId", "storagePath", "storageBucket"];
46810
- var isRecord12 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
46979
+ var isRecord13 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
46811
46980
  var isMediaObject = (value) => value.type === "image" || value.type === "video";
46812
46981
  function stripEnvSpecificMediaFieldsInPlace(value) {
46813
46982
  if (value === null || value === void 0) return;
@@ -46817,7 +46986,7 @@ function stripEnvSpecificMediaFieldsInPlace(value) {
46817
46986
  }
46818
46987
  return;
46819
46988
  }
46820
- if (!isRecord12(value)) return;
46989
+ if (!isRecord13(value)) return;
46821
46990
  if (isMediaObject(value)) {
46822
46991
  const hasCmsRef = typeof value.identifier === "string" || typeof value.assetId === "string" || typeof value.storagePath === "string" || typeof value.storageBucket === "string";
46823
46992
  for (const key of ENV_SPECIFIC_MEDIA_KEYS) {
@@ -46828,7 +46997,7 @@ function stripEnvSpecificMediaFieldsInPlace(value) {
46828
46997
  }
46829
46998
  delete value.mimeType;
46830
46999
  }
46831
- if (value.type === "image" && isRecord12(value.attrs)) {
47000
+ if (value.type === "image" && isRecord13(value.attrs)) {
46832
47001
  const attrs = value.attrs;
46833
47002
  const hasIdentifier = typeof attrs.identifier === "string" && attrs.identifier.trim().length > 0;
46834
47003
  if (hasIdentifier) {
@@ -47000,11 +47169,11 @@ function entryFilePath(input) {
47000
47169
  }
47001
47170
 
47002
47171
  // src/cli/content/writer.ts
47003
- function isRecord13(value) {
47172
+ function isRecord14(value) {
47004
47173
  return typeof value === "object" && value !== null && !Array.isArray(value);
47005
47174
  }
47006
47175
  function parseThemeMeta(value) {
47007
- if (!isRecord13(value) || typeof value.pulledAt !== "string" || !isRecord13(value.themes)) {
47176
+ if (!isRecord14(value) || typeof value.pulledAt !== "string" || !isRecord14(value.themes)) {
47008
47177
  return null;
47009
47178
  }
47010
47179
  return { pulledAt: value.pulledAt, themes: value.themes };
@@ -48439,9 +48608,9 @@ async function localMediaFileExists(filePath) {
48439
48608
  }
48440
48609
 
48441
48610
  // src/cli/media/identifiers.ts
48442
- var isRecord14 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
48611
+ var isRecord15 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
48443
48612
  var isMediaValue = (value) => {
48444
- if (!isRecord14(value)) return false;
48613
+ if (!isRecord15(value)) return false;
48445
48614
  const type = value.type;
48446
48615
  if (type !== "image" && type !== "video" && type !== "audio" && type !== "document" && type !== "spreadsheet" && type !== "archive") return false;
48447
48616
  return typeof value.identifier === "string" || typeof value.assetId === "string" || typeof value.storagePath === "string" || typeof value.src === "string";
@@ -48456,7 +48625,7 @@ var walkMediaValues = (value, location2, handler) => {
48456
48625
  });
48457
48626
  return;
48458
48627
  }
48459
- if (isRecord14(value)) {
48628
+ if (isRecord15(value)) {
48460
48629
  Object.entries(value).forEach(([key, entry]) => {
48461
48630
  walkMediaValues(entry, location2 ? `${location2}.${key}` : key, handler);
48462
48631
  });
@@ -48582,7 +48751,7 @@ var normalizeMediaValueToIdentifierOnly = (value, identifier, asset) => {
48582
48751
  };
48583
48752
 
48584
48753
  // src/cli/media/rich-text.ts
48585
- var isRecord15 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
48754
+ var isRecord16 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
48586
48755
  function collectRichTextImageAttrs(value, out = [], location2 = "") {
48587
48756
  if (Array.isArray(value)) {
48588
48757
  value.forEach(
@@ -48590,8 +48759,8 @@ function collectRichTextImageAttrs(value, out = [], location2 = "") {
48590
48759
  );
48591
48760
  return out;
48592
48761
  }
48593
- if (!isRecord15(value)) return out;
48594
- if (value.type === "image" && isRecord15(value.attrs)) {
48762
+ if (!isRecord16(value)) return out;
48763
+ if (value.type === "image" && isRecord16(value.attrs)) {
48595
48764
  out.push({ attrs: value.attrs, location: location2 ? `${location2}.attrs` : "attrs" });
48596
48765
  }
48597
48766
  for (const [key, child] of Object.entries(value)) {
@@ -48819,6 +48988,81 @@ var buildLocationNote = (locationsByIdentifier, identifier) => {
48819
48988
  const locations = locationsByIdentifier.get(identifier);
48820
48989
  return locations?.length ? ` Referenced at: ${locations.join(", ")}.` : "";
48821
48990
  };
48991
+ var locationsForIdentifier = (locationsByIdentifier, identifier) => {
48992
+ const locations = locationsByIdentifier.get(identifier);
48993
+ return locations?.length ? [...locations] : void 0;
48994
+ };
48995
+ var fileReportFields = (file) => {
48996
+ if (!file) return {};
48997
+ return {
48998
+ filename: file.filename,
48999
+ sizeBytes: file.data.length,
49000
+ mimeType: file.mimeType
49001
+ };
49002
+ };
49003
+ function classifyManagementApiError(error) {
49004
+ const code = error.code.toLowerCase();
49005
+ const message = error.message.toLowerCase();
49006
+ if (error.statusCode === 413 || code.includes("too_large") || message.includes("too large")) {
49007
+ return "max_object_size";
49008
+ }
49009
+ if (error.statusCode === 415 || code.includes("mime") || code.includes("content-type") || message.includes("mime") || message.includes("content-type")) {
49010
+ return "unsupported_mime";
49011
+ }
49012
+ if (error.statusCode === 402 || error.statusCode === 507 || code.includes("quota") || code.includes("storage") || message.includes("quota") || message.includes("storage limit")) {
49013
+ return "quota_or_storage_limit";
49014
+ }
49015
+ if (error.statusCode === 401 || error.statusCode === 403 || code.includes("auth") || code.includes("permission")) {
49016
+ return "auth_or_permission";
49017
+ }
49018
+ if (code === "sdk:network-error" || code === "sdk:timeout") {
49019
+ return "network";
49020
+ }
49021
+ if (code.startsWith("sdk:") || typeof error.statusCode === "number") {
49022
+ return "cms_api";
49023
+ }
49024
+ return "unknown";
49025
+ }
49026
+ function classifyErrorMessage(error) {
49027
+ const message = error.message.toLowerCase();
49028
+ if (message.includes("network") || message.includes("timeout") || message.includes("timed out")) {
49029
+ return "network";
49030
+ }
49031
+ if (message.includes("too large") || message.includes("payload too large") || message.includes("413")) {
49032
+ return "max_object_size";
49033
+ }
49034
+ if (message.includes("mime") || message.includes("content-type") || message.includes("unsupported media")) {
49035
+ return "unsupported_mime";
49036
+ }
49037
+ if (message.includes("quota") || message.includes("storage limit")) {
49038
+ return "quota_or_storage_limit";
49039
+ }
49040
+ if (message.includes("unauthorized") || message.includes("forbidden") || message.includes("permission")) {
49041
+ return "auth_or_permission";
49042
+ }
49043
+ return "unknown";
49044
+ }
49045
+ function classifyLocalMediaUploadFailure(error) {
49046
+ if (error instanceof ManagementApiError) {
49047
+ return classifyManagementApiError(error);
49048
+ }
49049
+ if (error instanceof Error) {
49050
+ return classifyErrorMessage(error);
49051
+ }
49052
+ return "unknown";
49053
+ }
49054
+ var rawErrorMessage = (error) => error instanceof Error ? error.message : String(error);
49055
+ var makeReport = (params) => {
49056
+ const failedIdentifiers = params.items.filter((item) => item.status === "failed").map((item) => item.identifier);
49057
+ const missingIdentifiers = params.items.filter((item) => item.status === "missing_local_file").map((item) => item.identifier);
49058
+ return {
49059
+ targetEnv: params.targetEnv,
49060
+ summary: { ...params.summary },
49061
+ items: params.items,
49062
+ failedIdentifiers,
49063
+ missingIdentifiers
49064
+ };
49065
+ };
48822
49066
  var loadLocalMediaFiles = async (contentDir, identifiers, manifest, errors) => {
48823
49067
  const localFiles = /* @__PURE__ */ new Map();
48824
49068
  for (const identifier of identifiers) {
@@ -48852,10 +49096,12 @@ var handleChecksumConflicts = async (options) => {
48852
49096
  assetsByIdentifier,
48853
49097
  missingIdentifiers,
48854
49098
  summary,
49099
+ reportItems,
48855
49100
  warnings,
48856
49101
  errors,
48857
49102
  locationsByIdentifier,
48858
49103
  dryRun,
49104
+ targetEnv,
48859
49105
  forceUpdateAsset
48860
49106
  } = options;
48861
49107
  const identifiersToReplace = /* @__PURE__ */ new Set();
@@ -48865,6 +49111,13 @@ var handleChecksumConflicts = async (options) => {
48865
49111
  summary.skipped += 1;
48866
49112
  const locationNote = buildLocationNote(locationsByIdentifier, identifier);
48867
49113
  warnings.push(`Missing local file for "${identifier}". Using CMS asset instead.${locationNote}`);
49114
+ reportItems.push({
49115
+ identifier,
49116
+ status: "skipped",
49117
+ targetEnv,
49118
+ locations: locationsForIdentifier(locationsByIdentifier, identifier),
49119
+ message: "Using existing CMS asset because no local file was found."
49120
+ });
48868
49121
  continue;
48869
49122
  }
48870
49123
  if (asset.checksumSha256 && asset.checksumSha256 !== localFile.checksum) {
@@ -48876,6 +49129,14 @@ var handleChecksumConflicts = async (options) => {
48876
49129
  } else {
48877
49130
  identifiersToReplace.add(identifier);
48878
49131
  }
49132
+ } else {
49133
+ reportItems.push({
49134
+ identifier,
49135
+ status: "resolved",
49136
+ targetEnv,
49137
+ ...fileReportFields(localFile),
49138
+ locations: locationsForIdentifier(locationsByIdentifier, identifier)
49139
+ });
48879
49140
  }
48880
49141
  }
48881
49142
  if (identifiersToReplace.size === 0) {
@@ -48898,10 +49159,12 @@ var resolveMissingByChecksum = async (options) => {
48898
49159
  localFiles,
48899
49160
  assetsByIdentifier,
48900
49161
  summary,
49162
+ reportItems,
48901
49163
  errors,
48902
49164
  warnings,
48903
49165
  locationsByIdentifier,
48904
49166
  dryRun,
49167
+ targetEnv,
48905
49168
  forceUpdateAsset
48906
49169
  } = options;
48907
49170
  const checksumCandidates = /* @__PURE__ */ new Map();
@@ -48912,6 +49175,13 @@ var resolveMissingByChecksum = async (options) => {
48912
49175
  errors.push(
48913
49176
  `Identifier "${identifier}" does not exist on the CMS and no local file was found. Add content/media/${identifier}.* or pull with --local-media first.${locationNote}`
48914
49177
  );
49178
+ reportItems.push({
49179
+ identifier,
49180
+ status: "missing_local_file",
49181
+ targetEnv,
49182
+ locations: locationsForIdentifier(locationsByIdentifier, identifier),
49183
+ message: `Add content/media/${identifier}.* or pull with --local-media first.`
49184
+ });
48915
49185
  continue;
48916
49186
  }
48917
49187
  checksumCandidates.set(identifier, localFile);
@@ -48932,6 +49202,15 @@ var resolveMissingByChecksum = async (options) => {
48932
49202
  const matches = assetsByChecksum.get(localFile.checksum) ?? [];
48933
49203
  if (matches.length === 0) {
48934
49204
  uploadPlans.push({ identifier, file: localFile });
49205
+ if (dryRun) {
49206
+ reportItems.push({
49207
+ identifier,
49208
+ status: "pending_upload",
49209
+ targetEnv,
49210
+ ...fileReportFields(localFile),
49211
+ locations: locationsForIdentifier(locationsByIdentifier, identifier)
49212
+ });
49213
+ }
48935
49214
  continue;
48936
49215
  }
48937
49216
  const exact = matches.find((asset) => asset.identifier === identifier);
@@ -48939,6 +49218,14 @@ var resolveMissingByChecksum = async (options) => {
48939
49218
  mapIdentifierToAsset(assetsByIdentifier, identifier, exact);
48940
49219
  missingIdentifiers.delete(identifier);
48941
49220
  summary.reused += 1;
49221
+ reportItems.push({
49222
+ identifier,
49223
+ status: "reused",
49224
+ targetEnv,
49225
+ ...fileReportFields(localFile),
49226
+ locations: locationsForIdentifier(locationsByIdentifier, identifier),
49227
+ message: "Reused existing CMS asset with matching identifier and checksum."
49228
+ });
48942
49229
  continue;
48943
49230
  }
48944
49231
  const unassigned = matches.filter((asset) => !asset.identifier);
@@ -48959,9 +49246,25 @@ var resolveMissingByChecksum = async (options) => {
48959
49246
  });
48960
49247
  summary.assigned += 1;
48961
49248
  mapIdentifierToAsset(assetsByIdentifier, identifier, assigned);
49249
+ reportItems.push({
49250
+ identifier,
49251
+ status: "assigned",
49252
+ targetEnv,
49253
+ ...fileReportFields(localFile),
49254
+ locations: locationsForIdentifier(locationsByIdentifier, identifier),
49255
+ message: "Assigned identifier to an existing matching CMS asset."
49256
+ });
48962
49257
  } else {
48963
49258
  summary.pendingAssignments += 1;
48964
49259
  mapIdentifierToAsset(assetsByIdentifier, identifier, { ...candidate, identifier });
49260
+ reportItems.push({
49261
+ identifier,
49262
+ status: "assigned",
49263
+ targetEnv,
49264
+ ...fileReportFields(localFile),
49265
+ locations: locationsForIdentifier(locationsByIdentifier, identifier),
49266
+ message: "Would assign identifier to an existing matching CMS asset."
49267
+ });
48965
49268
  }
48966
49269
  missingIdentifiers.delete(identifier);
48967
49270
  summary.reused += 1;
@@ -48980,9 +49283,25 @@ var resolveMissingByChecksum = async (options) => {
48980
49283
  });
48981
49284
  summary.assigned += 1;
48982
49285
  mapIdentifierToAsset(assetsByIdentifier, identifier, reassigned);
49286
+ reportItems.push({
49287
+ identifier,
49288
+ status: "assigned",
49289
+ targetEnv,
49290
+ ...fileReportFields(localFile),
49291
+ locations: locationsForIdentifier(locationsByIdentifier, identifier),
49292
+ message: "Assigned identifier to an existing matching CMS asset."
49293
+ });
48983
49294
  } else {
48984
49295
  summary.pendingAssignments += 1;
48985
49296
  mapIdentifierToAsset(assetsByIdentifier, identifier, { ...candidate, identifier });
49297
+ reportItems.push({
49298
+ identifier,
49299
+ status: "assigned",
49300
+ targetEnv,
49301
+ ...fileReportFields(localFile),
49302
+ locations: locationsForIdentifier(locationsByIdentifier, identifier),
49303
+ message: "Would assign identifier to an existing matching CMS asset."
49304
+ });
48986
49305
  }
48987
49306
  missingIdentifiers.delete(identifier);
48988
49307
  summary.reused += 1;
@@ -48996,7 +49315,7 @@ var resolveMissingByChecksum = async (options) => {
48996
49315
  return uploadPlans;
48997
49316
  };
48998
49317
  var uploadMissingAssets = async (options) => {
48999
- const { client, uploadPlans, assetsByIdentifier, summary, errors, dryRun } = options;
49318
+ const { client, uploadPlans, assetsByIdentifier, summary, reportItems, errors, dryRun, targetEnv, onEvent } = options;
49000
49319
  if (uploadPlans.length === 0) {
49001
49320
  return;
49002
49321
  }
@@ -49006,6 +49325,13 @@ var uploadMissingAssets = async (options) => {
49006
49325
  }
49007
49326
  const uploadedIdentifiers = [];
49008
49327
  for (const plan of uploadPlans) {
49328
+ const startedItem = {
49329
+ identifier: plan.identifier,
49330
+ status: "pending_upload",
49331
+ targetEnv,
49332
+ ...fileReportFields(plan.file)
49333
+ };
49334
+ onEvent?.({ type: "upload_started", item: startedItem });
49009
49335
  try {
49010
49336
  const result = await client.media.upload({
49011
49337
  data: plan.file.data,
@@ -49017,9 +49343,30 @@ var uploadMissingAssets = async (options) => {
49017
49343
  if (result.status === "created") {
49018
49344
  summary.uploaded += 1;
49019
49345
  }
49346
+ const uploadedItem = {
49347
+ identifier: plan.identifier,
49348
+ status: "uploaded",
49349
+ targetEnv,
49350
+ ...fileReportFields(plan.file),
49351
+ message: result.status === "created" ? "Uploaded local media file." : "CMS reported media already exists."
49352
+ };
49353
+ reportItems.push(uploadedItem);
49354
+ onEvent?.({ type: "upload_succeeded", item: uploadedItem });
49020
49355
  } catch (error) {
49021
49356
  const message = error instanceof Error ? error.message : String(error);
49022
- errors.push(`Failed to upload "${plan.identifier}": ${message}`);
49357
+ const failureKind = classifyLocalMediaUploadFailure(error);
49358
+ const failedItem = {
49359
+ identifier: plan.identifier,
49360
+ status: "failed",
49361
+ targetEnv,
49362
+ ...fileReportFields(plan.file),
49363
+ message: `Failed to upload "${plan.identifier}": ${message}`,
49364
+ failureKind,
49365
+ rawError: rawErrorMessage(error)
49366
+ };
49367
+ reportItems.push(failedItem);
49368
+ onEvent?.({ type: "upload_failed", item: failedItem });
49369
+ errors.push(`Failed to upload "${plan.identifier}" (${plan.file.filename}, ${plan.file.mimeType}): ${message}`);
49023
49370
  }
49024
49371
  }
49025
49372
  if (uploadedIdentifiers.length === 0) {
@@ -49036,10 +49383,11 @@ var uploadMissingAssets = async (options) => {
49036
49383
  }
49037
49384
  };
49038
49385
  async function syncLocalMediaForPush(options) {
49039
- const { contentDir, content, client, dryRun, forceUpdateAsset } = options;
49386
+ const { contentDir, content, client, dryRun, forceUpdateAsset, targetEnv, onEvent } = options;
49040
49387
  const manifest = await readMediaManifest(contentDir);
49041
49388
  const warnings = [];
49042
49389
  const errors = [];
49390
+ const reportItems = [];
49043
49391
  const existingIdentifiers = /* @__PURE__ */ new Set();
49044
49392
  walkLocalContentMedia(content, (value) => {
49045
49393
  if (typeof value.identifier === "string" && value.identifier.trim().length > 0) {
@@ -49105,6 +49453,7 @@ async function syncLocalMediaForPush(options) {
49105
49453
  content,
49106
49454
  manifest,
49107
49455
  summary,
49456
+ report: makeReport({ targetEnv, summary, items: reportItems }),
49108
49457
  warnings,
49109
49458
  errors,
49110
49459
  hasChanges: false
@@ -49121,10 +49470,12 @@ async function syncLocalMediaForPush(options) {
49121
49470
  assetsByIdentifier,
49122
49471
  missingIdentifiers,
49123
49472
  summary,
49473
+ reportItems,
49124
49474
  warnings,
49125
49475
  errors,
49126
49476
  locationsByIdentifier,
49127
49477
  dryRun,
49478
+ targetEnv,
49128
49479
  forceUpdateAsset
49129
49480
  });
49130
49481
  const uploadPlans = await resolveMissingByChecksum({
@@ -49133,10 +49484,12 @@ async function syncLocalMediaForPush(options) {
49133
49484
  localFiles,
49134
49485
  assetsByIdentifier,
49135
49486
  summary,
49487
+ reportItems,
49136
49488
  errors,
49137
49489
  warnings,
49138
49490
  locationsByIdentifier,
49139
49491
  dryRun,
49492
+ targetEnv,
49140
49493
  forceUpdateAsset
49141
49494
  });
49142
49495
  await uploadMissingAssets({
@@ -49144,8 +49497,11 @@ async function syncLocalMediaForPush(options) {
49144
49497
  uploadPlans,
49145
49498
  assetsByIdentifier,
49146
49499
  summary,
49500
+ reportItems,
49147
49501
  errors,
49148
- dryRun
49502
+ dryRun,
49503
+ targetEnv,
49504
+ onEvent
49149
49505
  });
49150
49506
  summary.resolved = assetsByIdentifier.size;
49151
49507
  for (const reference of mediaReferences) {
@@ -49168,6 +49524,7 @@ async function syncLocalMediaForPush(options) {
49168
49524
  content,
49169
49525
  manifest,
49170
49526
  summary,
49527
+ report: makeReport({ targetEnv, summary, items: reportItems }),
49171
49528
  warnings,
49172
49529
  errors,
49173
49530
  hasChanges
@@ -52729,7 +53086,7 @@ var isoDateTimeRegex = /^\d{4}-\d{2}-\d{2}T.+/;
52729
53086
  function issue2(path28, message) {
52730
53087
  return { path: path28, message };
52731
53088
  }
52732
- function isRecord16(value) {
53089
+ function isRecord17(value) {
52733
53090
  return typeof value === "object" && value !== null && !Array.isArray(value);
52734
53091
  }
52735
53092
  function isCurrency(value) {
@@ -52745,7 +53102,7 @@ function isWholeMinuteIsoDateTime(value) {
52745
53102
  }
52746
53103
  function validateTicketType(raw, index, currency) {
52747
53104
  const path28 = ["ticketTypes", String(index)];
52748
- if (!isRecord16(raw)) return [issue2(path28, "Ticket type must be an object")];
53105
+ if (!isRecord17(raw)) return [issue2(path28, "Ticket type must be an object")];
52749
53106
  const issues = [];
52750
53107
  const identifier = raw.identifier;
52751
53108
  if (typeof identifier !== "string" || !identifierRegex.test(identifier)) {
@@ -52868,7 +53225,7 @@ function validateTicketTypes(rawTicketTypes, currency) {
52868
53225
  const ticketTypes = [];
52869
53226
  const seenIdentifiers = /* @__PURE__ */ new Set();
52870
53227
  rawTicketTypes.forEach((rawTicketType, index) => {
52871
- const rawIdentifier = isRecord16(rawTicketType) ? rawTicketType.identifier : void 0;
53228
+ const rawIdentifier = isRecord17(rawTicketType) ? rawTicketType.identifier : void 0;
52872
53229
  const hasValidIdentifier = typeof rawIdentifier === "string" && identifierRegex.test(rawIdentifier);
52873
53230
  const isDuplicateIdentifier = hasValidIdentifier && seenIdentifiers.has(rawIdentifier);
52874
53231
  if (isDuplicateIdentifier) {
@@ -52912,7 +53269,7 @@ function classifySdkEventPricing(rawInput) {
52912
53269
  if (rawInput === void 0 || rawInput === null) {
52913
53270
  return { ok: true, pricing: { kind: "free" } };
52914
53271
  }
52915
- if (!isRecord16(rawInput)) {
53272
+ if (!isRecord17(rawInput)) {
52916
53273
  return { ok: false, issues: [issue2([], "Pricing must be an object")] };
52917
53274
  }
52918
53275
  const record = rawInput;
@@ -53022,7 +53379,7 @@ var supportedKinds = /* @__PURE__ */ new Set([
53022
53379
  function issue3(path28, message) {
53023
53380
  return { path: path28, message };
53024
53381
  }
53025
- function isRecord17(value) {
53382
+ function isRecord18(value) {
53026
53383
  return typeof value === "object" && value !== null && !Array.isArray(value);
53027
53384
  }
53028
53385
  function isWholeNumber2(value) {
@@ -53038,7 +53395,7 @@ function isSupportedOptionKind(value) {
53038
53395
  return typeof value === "string" && supportedKinds.has(value);
53039
53396
  }
53040
53397
  function validateFlexibleBalanceDueDateRule(raw, path28) {
53041
- if (!isRecord17(raw)) {
53398
+ if (!isRecord18(raw)) {
53042
53399
  return [issue3(path28, "Flexible balance dueDateRule must be an object")];
53043
53400
  }
53044
53401
  if (raw.kind === "relative_to_start_date") {
@@ -53077,7 +53434,7 @@ function validateFlexibleBalanceDueDateRule(raw, path28) {
53077
53434
  ];
53078
53435
  }
53079
53436
  function validateFlexibleBalanceConfig(raw, path28) {
53080
- if (!isRecord17(raw)) {
53437
+ if (!isRecord18(raw)) {
53081
53438
  return [issue3(path28, "Flexible balance config must be an object")];
53082
53439
  }
53083
53440
  const issues = [];
@@ -53123,7 +53480,7 @@ function validateFlexibleBalanceConfig(raw, path28) {
53123
53480
  }
53124
53481
  function validateCollectionOption(raw, index) {
53125
53482
  const path28 = ["allowedOptions", String(index)];
53126
- if (!isRecord17(raw)) {
53483
+ if (!isRecord18(raw)) {
53127
53484
  return [issue3(path28, "Payment option must be an object")];
53128
53485
  }
53129
53486
  if (raw.kind === "instalment_plan") {
@@ -53194,7 +53551,7 @@ function classifySdkEventPaymentTerms(rawInput) {
53194
53551
  if (rawInput === void 0 || rawInput === null) {
53195
53552
  return { ok: true, paymentTerms: void 0 };
53196
53553
  }
53197
- if (!isRecord17(rawInput)) {
53554
+ if (!isRecord18(rawInput)) {
53198
53555
  return {
53199
53556
  ok: false,
53200
53557
  issues: [issue3([], "Event paymentTerms must be an object")]
@@ -53245,7 +53602,7 @@ function classifySdkEventPaymentTerms(rawInput) {
53245
53602
  function nullishToNull(value) {
53246
53603
  return value == null ? null : value;
53247
53604
  }
53248
- function isRecord18(value) {
53605
+ function isRecord19(value) {
53249
53606
  return typeof value === "object" && value !== null && !Array.isArray(value);
53250
53607
  }
53251
53608
  function stringValue(value, fallback) {
@@ -53266,7 +53623,7 @@ function normalizeEventPaymentTermsForCompare(paymentTerms) {
53266
53623
  return void 0;
53267
53624
  }
53268
53625
  function normalizeVenueAddress(value) {
53269
- const obj = isRecord18(value) ? value : {};
53626
+ const obj = isRecord19(value) ? value : {};
53270
53627
  return {
53271
53628
  line1: nullishToNull(typeof obj.line1 === "string" ? obj.line1 : void 0),
53272
53629
  line2: nullishToNull(typeof obj.line2 === "string" ? obj.line2 : void 0),
@@ -53283,7 +53640,7 @@ function normalizeVenueAddress(value) {
53283
53640
  };
53284
53641
  }
53285
53642
  function normalizeVenueLocation(value) {
53286
- if (!isRecord18(value)) return null;
53643
+ if (!isRecord19(value)) return null;
53287
53644
  const obj = value;
53288
53645
  const lat = typeof obj.lat === "number" ? obj.lat : null;
53289
53646
  const lng = typeof obj.lng === "number" ? obj.lng : null;
@@ -53291,7 +53648,7 @@ function normalizeVenueLocation(value) {
53291
53648
  return { lat, lng };
53292
53649
  }
53293
53650
  function normalizeVenueForCompare(venue) {
53294
- const obj = isRecord18(venue) ? venue : {};
53651
+ const obj = isRecord19(venue) ? venue : {};
53295
53652
  return {
53296
53653
  slug: String(obj.slug),
53297
53654
  title: String(obj.title),
@@ -53312,7 +53669,7 @@ function normalizeVenueForCompare(venue) {
53312
53669
  };
53313
53670
  }
53314
53671
  function normalizeEventCategoryForCompare(category) {
53315
- const obj = isRecord18(category) ? category : {};
53672
+ const obj = isRecord19(category) ? category : {};
53316
53673
  return {
53317
53674
  identifier: String(obj.identifier),
53318
53675
  name: String(obj.name),
@@ -53324,8 +53681,8 @@ function normalizeEventCategoryForCompare(category) {
53324
53681
  };
53325
53682
  }
53326
53683
  function normalizeRemoteEventForCompare(event) {
53327
- const obj = isRecord18(event) ? event : {};
53328
- const schedule = isRecord18(obj.schedule) ? obj.schedule : {};
53684
+ const obj = isRecord19(event) ? event : {};
53685
+ const schedule = isRecord19(obj.schedule) ? obj.schedule : {};
53329
53686
  return {
53330
53687
  identifier: String(obj.identifier),
53331
53688
  title: String(obj.title),
@@ -53354,14 +53711,14 @@ function normalizeRemoteEventForCompare(event) {
53354
53711
  formRef: nullishToNull(
53355
53712
  typeof obj.formRef === "string" ? obj.formRef : void 0
53356
53713
  ),
53357
- content: isRecord18(obj.content) ? obj.content : {},
53358
- settings: isRecord18(obj.settings) ? obj.settings : {},
53359
- presentation: isRecord18(obj.presentation) ? obj.presentation : null,
53714
+ content: isRecord19(obj.content) ? obj.content : {},
53715
+ settings: isRecord19(obj.settings) ? obj.settings : {},
53716
+ presentation: isRecord19(obj.presentation) ? obj.presentation : null,
53360
53717
  tags: Array.isArray(obj.tags) ? obj.tags : []
53361
53718
  };
53362
53719
  }
53363
53720
  function normalizeLocalEventAgainstRemoteForCompare(localEvent, remoteEvent) {
53364
- const local = isRecord18(localEvent) ? localEvent : {};
53721
+ const local = isRecord19(localEvent) ? localEvent : {};
53365
53722
  const base = remoteEvent ? normalizeRemoteEventForCompare(remoteEvent) : normalizeRemoteEventForCompare({
53366
53723
  identifier: local.identifier,
53367
53724
  title: local.title,
@@ -53380,7 +53737,7 @@ function normalizeLocalEventAgainstRemoteForCompare(localEvent, remoteEvent) {
53380
53737
  presentation: local.presentation ?? null,
53381
53738
  tags: local.tags ?? []
53382
53739
  });
53383
- const schedule = isRecord18(local.schedule) ? local.schedule : {};
53740
+ const schedule = isRecord19(local.schedule) ? local.schedule : {};
53384
53741
  return {
53385
53742
  ...base,
53386
53743
  identifier: stringValue(local.identifier, base.identifier),
@@ -53411,9 +53768,9 @@ function normalizeLocalEventAgainstRemoteForCompare(localEvent, remoteEvent) {
53411
53768
  formRef: local.formRef === void 0 ? base.formRef : nullishToNull(
53412
53769
  typeof local.formRef === "string" ? local.formRef : void 0
53413
53770
  ),
53414
- content: local.content === void 0 ? base.content : isRecord18(local.content) ? local.content : {},
53415
- settings: local.settings === void 0 ? base.settings : isRecord18(local.settings) ? local.settings : {},
53416
- presentation: local.presentation === void 0 ? base.presentation : isRecord18(local.presentation) ? local.presentation : null,
53771
+ content: local.content === void 0 ? base.content : isRecord19(local.content) ? local.content : {},
53772
+ settings: local.settings === void 0 ? base.settings : isRecord19(local.settings) ? local.settings : {},
53773
+ presentation: local.presentation === void 0 ? base.presentation : isRecord19(local.presentation) ? local.presentation : null,
53417
53774
  tags: local.tags === void 0 ? base.tags : Array.isArray(local.tags) ? local.tags : []
53418
53775
  };
53419
53776
  }
@@ -53425,7 +53782,7 @@ function toIsoStringOrOriginal(value) {
53425
53782
  function normalizeCourseSessions(value) {
53426
53783
  if (!Array.isArray(value)) return [];
53427
53784
  return value.map((session) => {
53428
- const obj = isRecord18(session) ? session : {};
53785
+ const obj = isRecord19(session) ? session : {};
53429
53786
  return {
53430
53787
  startsAt: toIsoStringOrOriginal(obj.startsAt),
53431
53788
  endsAt: toIsoStringOrOriginal(obj.endsAt)
@@ -53437,18 +53794,18 @@ function normalizeCourseSessions(value) {
53437
53794
  }
53438
53795
  function normalizeCourseProgram(value) {
53439
53796
  if (value == null) return null;
53440
- if (!isRecord18(value)) {
53797
+ if (!isRecord19(value)) {
53441
53798
  return { identifier: null, data: {} };
53442
53799
  }
53443
53800
  return {
53444
53801
  identifier: nullishToNull(
53445
53802
  typeof value.identifier === "string" ? value.identifier : void 0
53446
53803
  ),
53447
- data: isRecord18(value.data) ? value.data : {}
53804
+ data: isRecord19(value.data) ? value.data : {}
53448
53805
  };
53449
53806
  }
53450
53807
  function normalizeRemoteCourseForCompare(course) {
53451
- const obj = isRecord18(course) ? course : {};
53808
+ const obj = isRecord19(course) ? course : {};
53452
53809
  return {
53453
53810
  identifier: String(obj.identifier),
53454
53811
  title: String(obj.title),
@@ -53483,7 +53840,7 @@ function normalizeRemoteCourseForCompare(course) {
53483
53840
  };
53484
53841
  }
53485
53842
  function normalizeLocalCourseAgainstRemoteForCompare(localCourse, remoteCourse) {
53486
- const local = isRecord18(localCourse) ? localCourse : {};
53843
+ const local = isRecord19(localCourse) ? localCourse : {};
53487
53844
  const base = remoteCourse ? normalizeRemoteCourseForCompare(remoteCourse) : normalizeRemoteCourseForCompare({
53488
53845
  identifier: local.identifier,
53489
53846
  title: local.title,
@@ -53504,11 +53861,11 @@ function normalizeLocalCourseAgainstRemoteForCompare(localCourse, remoteCourse)
53504
53861
  });
53505
53862
  const status = local.status === void 0 ? base.status : local.status === "active" || local.status === "cancelled" || local.status === "completed" ? local.status : "draft";
53506
53863
  const visibility = local.visibility === void 0 ? base.visibility : local.visibility === "public" || local.visibility === "private" || local.visibility === "unlisted" ? local.visibility : "public";
53507
- const program2 = local.program === void 0 ? base.program : local.program === null ? null : isRecord18(local.program) ? {
53864
+ const program2 = local.program === void 0 ? base.program : local.program === null ? null : isRecord19(local.program) ? {
53508
53865
  identifier: local.program.identifier === void 0 ? base.program?.identifier ?? null : nullishToNull(
53509
53866
  typeof local.program.identifier === "string" ? local.program.identifier : void 0
53510
53867
  ),
53511
- data: isRecord18(local.program.data) ? local.program.data : base.program?.data ?? {}
53868
+ data: isRecord19(local.program.data) ? local.program.data : base.program?.data ?? {}
53512
53869
  } : base.program;
53513
53870
  return {
53514
53871
  ...base,
@@ -53934,18 +54291,22 @@ async function readJsonSafe(filePath) {
53934
54291
 
53935
54292
  // src/cli/commands/push/execute/diff.ts
53936
54293
  function displayDiff(params) {
53937
- const { output, diff, config: config2, dryRun, isRemote, jsonOutput, jsonDiffMode, localContent, remoteContent, humanDiff } = params;
54294
+ const { output, diff, config: config2, dryRun, isRemote, jsonOutput, jsonDiffMode, localContent, remoteContent, humanDiff, mediaReport } = params;
53938
54295
  const envLabel = isRemote ? "REMOTE" : "LOCAL";
53939
54296
  if (jsonDiffMode) {
53940
54297
  const jsonDiff = buildJsonDiff(diff, localContent, remoteContent, jsonDiffMode);
53941
- output.json(jsonDiff);
54298
+ output.json({
54299
+ ...jsonDiff,
54300
+ ...mediaReport ? { media: mediaReport } : {}
54301
+ });
53942
54302
  return;
53943
54303
  }
53944
54304
  if (jsonOutput) {
53945
54305
  output.json({
53946
54306
  summary: diff.summary,
53947
54307
  syncConfig: config2.sync,
53948
- dryRun
54308
+ dryRun,
54309
+ ...mediaReport ? { media: mediaReport } : {}
53949
54310
  });
53950
54311
  return;
53951
54312
  }
@@ -55762,7 +56123,7 @@ function getSiteCommandLifecycleIntent(command) {
55762
56123
  init_src();
55763
56124
 
55764
56125
  // ../site-commands/src/guards.ts
55765
- function isRecord19(value) {
56126
+ function isRecord20(value) {
55766
56127
  return typeof value === "object" && value !== null;
55767
56128
  }
55768
56129
 
@@ -56027,7 +56388,7 @@ function collectVenueRefsFromIntervals(intervals, path28) {
56027
56388
  }
56028
56389
  function collectNavigationRefs(items, path28) {
56029
56390
  return items.flatMap((item, index) => {
56030
- if (!isRecord19(item)) return [];
56391
+ if (!isRecord20(item)) return [];
56031
56392
  const itemPath = `${path28}[${index}]`;
56032
56393
  const refs = [];
56033
56394
  if (typeof item.pageRef === "string") {
@@ -56044,7 +56405,7 @@ function collectNavigationRefs(items, path28) {
56044
56405
  }
56045
56406
  function collectBlockRefs(blocks, path28) {
56046
56407
  return blocks.flatMap((block, index) => {
56047
- if (!isRecord19(block)) return [];
56408
+ if (!isRecord20(block)) return [];
56048
56409
  const blockPath = `${path28}[${index}]`;
56049
56410
  return [
56050
56411
  ...collectNestedRefs(block.props, `${blockPath}.props`),
@@ -56060,7 +56421,7 @@ function collectNestedRefs(value, path28) {
56060
56421
  if (Array.isArray(value)) {
56061
56422
  return value.flatMap((item, index) => collectNestedRefs(item, `${path28}[${index}]`));
56062
56423
  }
56063
- if (!isRecord19(value)) return [];
56424
+ if (!isRecord20(value)) return [];
56064
56425
  return Object.entries(value).flatMap(([key, child]) => collectNestedRefs(child, `${path28}.${key}`));
56065
56426
  }
56066
56427
 
@@ -56316,9 +56677,9 @@ function deriveIdempotencyKey(input) {
56316
56677
  ].join("|"));
56317
56678
  }
56318
56679
  function findUnknownCommandTypes(input) {
56319
- if (!isRecord19(input) || !Array.isArray(input.commands)) return [];
56680
+ if (!isRecord20(input) || !Array.isArray(input.commands)) return [];
56320
56681
  return input.commands.flatMap((command, commandIndex) => {
56321
- if (!isRecord19(command) || typeof command.type !== "string") return [];
56682
+ if (!isRecord20(command) || typeof command.type !== "string") return [];
56322
56683
  if (commandTypeSet.has(command.type)) return [];
56323
56684
  return [{
56324
56685
  code: "unknown_command_type",
@@ -56344,9 +56705,9 @@ function commandIndexForIssuePath(path28) {
56344
56705
  return path28[0] === "commands" && typeof path28[1] === "number" ? path28[1] : -1;
56345
56706
  }
56346
56707
  function commandTypeForIssue(input, commandIndex) {
56347
- if (!isRecord19(input) || !isUnknownArray3(input.commands)) return null;
56708
+ if (!isRecord20(input) || !isUnknownArray3(input.commands)) return null;
56348
56709
  const command = input.commands[commandIndex];
56349
- return isRecord19(command) && typeof command.type === "string" ? command.type : null;
56710
+ return isRecord20(command) && typeof command.type === "string" ? command.type : null;
56350
56711
  }
56351
56712
  function isUnknownArray3(value) {
56352
56713
  return Array.isArray(value);
@@ -57661,8 +58022,8 @@ function eventContentInput(content) {
57661
58022
  if (!content) return void 0;
57662
58023
  return {
57663
58024
  ...content.summary ? { summary: content.summary } : {},
57664
- ...typeof content.body === "string" || isRecord20(content.body) ? { body: content.body } : {},
57665
- ...typeof content.image === "string" || isRecord20(content.image) ? { image: content.image } : {}
58025
+ ...typeof content.body === "string" || isRecord21(content.body) ? { body: content.body } : {},
58026
+ ...typeof content.image === "string" || isRecord21(content.image) ? { image: content.image } : {}
57666
58027
  };
57667
58028
  }
57668
58029
  function courseUpsertInput(command) {
@@ -57843,7 +58204,7 @@ function toUtcIso(date, time) {
57843
58204
  const normalizedTime = time.length === 5 ? `${time}:00` : time;
57844
58205
  return (/* @__PURE__ */ new Date(`${date}T${normalizedTime}Z`)).toISOString();
57845
58206
  }
57846
- function isRecord20(value) {
58207
+ function isRecord21(value) {
57847
58208
  return Boolean(value && typeof value === "object" && !Array.isArray(value));
57848
58209
  }
57849
58210
  function unsupported(identifier, message) {
@@ -60047,6 +60408,113 @@ async function syncMediaFilesForPush(params) {
60047
60408
  });
60048
60409
  }
60049
60410
 
60411
+ // src/cli/commands/push/execute/localMediaReporting.ts
60412
+ function targetSuffix(item) {
60413
+ return item.targetEnv ? ` to ${item.targetEnv}` : "";
60414
+ }
60415
+ function fileDetails(item) {
60416
+ const parts = [
60417
+ item.filename,
60418
+ typeof item.sizeBytes === "number" ? formatSize(item.sizeBytes) : void 0,
60419
+ item.mimeType
60420
+ ].filter((part) => Boolean(part));
60421
+ return parts.length ? ` (${parts.join(", ")})` : "";
60422
+ }
60423
+ function reportLocalMediaPushEvent(output, event) {
60424
+ switch (event.type) {
60425
+ case "upload_started":
60426
+ output.info(
60427
+ `Uploading media "${event.item.identifier}"${fileDetails(event.item)}${targetSuffix(event.item)}...`
60428
+ );
60429
+ return;
60430
+ case "upload_succeeded":
60431
+ output.info(
60432
+ `Uploaded media "${event.item.identifier}"${fileDetails(event.item)}${targetSuffix(event.item)}`
60433
+ );
60434
+ return;
60435
+ case "upload_failed":
60436
+ output.warn(
60437
+ `Failed to upload media "${event.item.identifier}"${fileDetails(event.item)}${targetSuffix(event.item)}: ${event.item.rawError ?? event.item.message ?? "Unknown error"}`
60438
+ );
60439
+ return;
60440
+ }
60441
+ }
60442
+ function reportLocalMediaPushSummary(params) {
60443
+ const { output, report, dryRun, includeResolved = false } = params;
60444
+ const { summary } = report;
60445
+ const pendingLabel = dryRun ? `${summary.pendingUploads} uploads, ${summary.pendingAssignments} identifier updates pending` : `${summary.uploaded} uploaded, ${summary.assigned} identifier updates`;
60446
+ output.info(
60447
+ `Local media${report.targetEnv ? ` (${report.targetEnv})` : ""}: ${summary.resolved} resolved, ${pendingLabel}`
60448
+ );
60449
+ for (const item of report.items) {
60450
+ if (!includeResolved && (item.status === "resolved" || item.status === "reused")) {
60451
+ continue;
60452
+ }
60453
+ switch (item.status) {
60454
+ case "pending_upload":
60455
+ output.info(
60456
+ ` Would upload "${item.identifier}"${fileDetails(item)}${targetSuffix(item)}`
60457
+ );
60458
+ break;
60459
+ case "assigned":
60460
+ output.info(` ${dryRun ? "Would assign" : "Assigned"} identifier "${item.identifier}"`);
60461
+ break;
60462
+ case "uploaded":
60463
+ output.info(` Uploaded "${item.identifier}"${fileDetails(item)}${targetSuffix(item)}`);
60464
+ break;
60465
+ case "missing_local_file":
60466
+ output.warn(` Missing local file for "${item.identifier}"${item.message ? `: ${item.message}` : ""}`);
60467
+ break;
60468
+ case "failed":
60469
+ output.warn(
60470
+ ` Failed "${item.identifier}"${fileDetails(item)}${item.failureKind ? ` [${item.failureKind}]` : ""}: ${item.rawError ?? item.message ?? "Unknown error"}`
60471
+ );
60472
+ break;
60473
+ case "skipped":
60474
+ output.info(` Skipped "${item.identifier}"${item.message ? `: ${item.message}` : ""}`);
60475
+ break;
60476
+ case "resolved":
60477
+ case "reused":
60478
+ output.info(` ${item.status === "reused" ? "Reused" : "Resolved"} "${item.identifier}"`);
60479
+ break;
60480
+ }
60481
+ }
60482
+ }
60483
+ async function syncLocalMediaForPushWithReporting(params) {
60484
+ const {
60485
+ output,
60486
+ contentDir,
60487
+ content,
60488
+ client,
60489
+ dryRun,
60490
+ targetEnv,
60491
+ isStructuredOutput,
60492
+ forceUpdateAsset,
60493
+ printSummary = true
60494
+ } = params;
60495
+ if (!isStructuredOutput) {
60496
+ output.info("Resolving local media identifiers...");
60497
+ }
60498
+ const result = await syncLocalMediaForPush({
60499
+ contentDir,
60500
+ content,
60501
+ client,
60502
+ dryRun,
60503
+ forceUpdateAsset,
60504
+ targetEnv,
60505
+ onEvent: isStructuredOutput ? void 0 : (event) => reportLocalMediaPushEvent(output, event)
60506
+ });
60507
+ result.warnings.forEach((warning) => output.warn(warning));
60508
+ if (!isStructuredOutput && printSummary && (result.report.items.length > 0 || result.hasChanges)) {
60509
+ reportLocalMediaPushSummary({
60510
+ output,
60511
+ report: result.report,
60512
+ dryRun
60513
+ });
60514
+ }
60515
+ return result;
60516
+ }
60517
+
60050
60518
  // src/cli/commands/push/execute/metadata.ts
60051
60519
  function mapIfUpdating(shouldUpdate, items, mapper) {
60052
60520
  return shouldUpdate ? items.map(mapper) : [];
@@ -60150,6 +60618,100 @@ ${errors}`
60150
60618
  output.error(message);
60151
60619
  }
60152
60620
  }
60621
+
60622
+ // src/cli/commands/push/scopes/commandBackedDatedOfferingScope.ts
60623
+ var VOLATILE_DATED_OFFERING_FIELDS = /* @__PURE__ */ new Set([
60624
+ "id",
60625
+ "siteId",
60626
+ "createdAt",
60627
+ "updatedAt",
60628
+ "contentUpdatedAt"
60629
+ ]);
60630
+ function normalizeDatedOfferingForComparison(value) {
60631
+ const normalized = JSON.parse(JSON.stringify(value));
60632
+ for (const field of VOLATILE_DATED_OFFERING_FIELDS) {
60633
+ delete normalized[field];
60634
+ }
60635
+ return normalized;
60636
+ }
60637
+ function createEmptyDryRunSummary() {
60638
+ return { created: 0, updated: 0, unchanged: 0, deleted: 0 };
60639
+ }
60640
+ function incrementDryRunSummary(summary, status) {
60641
+ switch (status) {
60642
+ case "created":
60643
+ return { ...summary, created: summary.created + 1 };
60644
+ case "updated":
60645
+ return { ...summary, updated: summary.updated + 1 };
60646
+ case "unchanged":
60647
+ return { ...summary, unchanged: summary.unchanged + 1 };
60648
+ case "deleted":
60649
+ return { ...summary, deleted: summary.deleted + 1 };
60650
+ }
60651
+ }
60652
+ function buildDatedOfferingDryRunPlan(input) {
60653
+ let summary = createEmptyDryRunSummary();
60654
+ const items = [];
60655
+ for (const item of input.localItems) {
60656
+ const identifier = input.config.getIdentifier(item);
60657
+ const remoteItem = input.config.findRemote(input.remoteItems, identifier);
60658
+ if (!remoteItem) {
60659
+ summary = incrementDryRunSummary(summary, "created");
60660
+ items.push({
60661
+ identifier,
60662
+ label: input.config.getDisplayLabel(item),
60663
+ status: "created",
60664
+ changes: []
60665
+ });
60666
+ continue;
60667
+ }
60668
+ const localObject = normalizeDatedOfferingForComparison(item);
60669
+ const remoteObject = normalizeDatedOfferingForComparison(remoteItem);
60670
+ const changes = findChangedFieldsWithValues(localObject, remoteObject);
60671
+ const status = changes.length > 0 ? "updated" : "unchanged";
60672
+ summary = incrementDryRunSummary(summary, status);
60673
+ items.push({
60674
+ identifier,
60675
+ label: input.config.getDisplayLabel(item),
60676
+ status,
60677
+ changes
60678
+ });
60679
+ }
60680
+ for (const identifier of input.orphanIdentifiers) {
60681
+ summary = incrementDryRunSummary(summary, "deleted");
60682
+ items.push({
60683
+ identifier,
60684
+ label: identifier,
60685
+ status: "deleted",
60686
+ changes: []
60687
+ });
60688
+ }
60689
+ return { summary, items };
60690
+ }
60691
+ function jsonDryRunReport(params) {
60692
+ const mode = params.jsonDiffMode ?? "summary";
60693
+ const changes = params.plan.items.filter((item) => item.status !== "unchanged").map((item) => ({
60694
+ type: params.scope,
60695
+ identifier: item.identifier,
60696
+ operation: item.status === "deleted" ? "delete" : item.status === "created" ? "create" : "update",
60697
+ ...mode === "full" ? { changes: item.changes } : {}
60698
+ }));
60699
+ return {
60700
+ mode,
60701
+ direction: "push",
60702
+ scope: params.scope,
60703
+ targetEnv: params.targetEnv,
60704
+ dryRun: true,
60705
+ summary: {
60706
+ creates: params.plan.summary.created,
60707
+ updates: params.plan.summary.updated,
60708
+ deletes: params.plan.summary.deleted,
60709
+ unchanged: params.plan.summary.unchanged
60710
+ },
60711
+ changes,
60712
+ ...params.media ? { media: params.media } : {}
60713
+ };
60714
+ }
60153
60715
  async function pushCommandBackedDatedOfferingScope(ctx, config2) {
60154
60716
  const noopResult = {
60155
60717
  status: "noop",
@@ -60229,58 +60791,84 @@ Run "riverbankcms pull ${config2.scopeCommand}" first to get latest, or use --fo
60229
60791
  const allLocalIdentifierSet = new Set(allLocalItems.map(config2.getIdentifier));
60230
60792
  const orphanIdentifiers = ctx.deleteOrphaned && !config2.shouldPushItem && remoteItems ? remoteItems.map(config2.getRemoteIdentifier).filter((identifier) => !allLocalIdentifierSet.has(identifier)) : [];
60231
60793
  if (ctx.dryRun) {
60232
- if (localItems.length > 0) {
60233
- ctx.output.info(`[DRY RUN] Would push ${localItems.length} ${config2.scopeName}`);
60794
+ const dryRunPlan = buildDatedOfferingDryRunPlan({
60795
+ localItems,
60796
+ remoteItems: remoteItems ?? [],
60797
+ orphanIdentifiers,
60798
+ config: config2
60799
+ });
60800
+ const plannedPushes = dryRunPlan.summary.created + dryRunPlan.summary.updated;
60801
+ if (ctx.jsonDiffMode) {
60802
+ ctx.output.json(jsonDryRunReport({
60803
+ scope: config2.scopeCommand,
60804
+ targetEnv: ctx.targetEnv,
60805
+ jsonDiffMode: ctx.jsonDiffMode,
60806
+ plan: dryRunPlan,
60807
+ media: config2.dryRunReportExtras?.media
60808
+ }));
60809
+ return {
60810
+ status: "dry-run",
60811
+ scopeCommand: config2.scopeCommand,
60812
+ scopeName: config2.scopeName,
60813
+ created: dryRunPlan.summary.created,
60814
+ updated: dryRunPlan.summary.updated,
60815
+ unchanged: dryRunPlan.summary.unchanged,
60816
+ deleted: dryRunPlan.summary.deleted,
60817
+ failed: 0,
60818
+ errors: [],
60819
+ media: config2.dryRunReportExtras?.media
60820
+ };
60821
+ }
60822
+ if (plannedPushes > 0) {
60823
+ ctx.output.info(
60824
+ `[DRY RUN] Would push ${plannedPushes} ${config2.scopeName} (${dryRunPlan.summary.created} create, ${dryRunPlan.summary.updated} update, ${dryRunPlan.summary.unchanged} unchanged)`
60825
+ );
60234
60826
  if (skippedUnchanged > 0) {
60235
- ctx.output.info(` (skipping ${skippedUnchanged} unchanged)`);
60236
- }
60237
- if (ctx.humanDiff && remoteItems) {
60238
- for (const item of localItems) {
60239
- const identifier = config2.getIdentifier(item);
60240
- const remoteItem = config2.findRemote(remoteItems, identifier);
60241
- if (!remoteItem) {
60242
- ctx.output.info(` + ${identifier}: ${config2.getDisplayLabel(item)} (new)`);
60243
- continue;
60244
- }
60245
- const localObject = JSON.parse(JSON.stringify(item));
60246
- const remoteObject = JSON.parse(JSON.stringify(remoteItem));
60247
- delete remoteObject.createdAt;
60248
- delete remoteObject.updatedAt;
60249
- delete remoteObject.contentUpdatedAt;
60250
- if (equal(localObject, remoteObject)) {
60251
- ctx.output.info(` = ${identifier}: ${config2.getDisplayLabel(item)} (unchanged)`);
60252
- } else {
60253
- ctx.output.info(` ~ ${identifier}: ${config2.getDisplayLabel(item)}`);
60254
- const changes = findChangedFieldsWithValues(localObject, remoteObject);
60255
- for (const change of changes) {
60827
+ ctx.output.info(` (skipping ${skippedUnchanged} filtered out)`);
60828
+ }
60829
+ } else if (dryRunPlan.summary.deleted === 0) {
60830
+ ctx.output.info(
60831
+ `[DRY RUN] No ${config2.scopeName} changes to push (${dryRunPlan.summary.unchanged} unchanged)`
60832
+ );
60833
+ }
60834
+ for (const item of dryRunPlan.items) {
60835
+ switch (item.status) {
60836
+ case "created":
60837
+ ctx.output.info(` + ${item.identifier}: ${item.label} (new)`);
60838
+ break;
60839
+ case "updated":
60840
+ ctx.output.info(` ~ ${item.identifier}: ${item.label}`);
60841
+ if (ctx.humanDiff) {
60842
+ for (const change of item.changes) {
60256
60843
  ctx.output.info(` ${change.path}: ${formatValue(change.before)} -> ${formatValue(change.after)}`);
60257
60844
  }
60258
60845
  }
60259
- }
60260
- } else {
60261
- for (const item of localItems) {
60262
- ctx.output.info(` - ${config2.getIdentifier(item)}: ${config2.getDisplayLabel(item)}`);
60263
- }
60846
+ break;
60847
+ case "unchanged":
60848
+ ctx.output.info(` = ${item.identifier}: ${item.label} (unchanged)`);
60849
+ break;
60264
60850
  }
60265
60851
  }
60266
- if (orphanIdentifiers.length > 0) {
60267
- ctx.output.info(`[DRY RUN] Would delete ${orphanIdentifiers.length} orphaned ${config2.scopeName}:`);
60268
- for (const identifier of orphanIdentifiers) {
60269
- ctx.output.info(` - ${identifier}`);
60852
+ if (dryRunPlan.summary.deleted > 0) {
60853
+ ctx.output.info(`[DRY RUN] Would delete ${dryRunPlan.summary.deleted} orphaned ${config2.scopeName}:`);
60854
+ for (const item of dryRunPlan.items.filter((candidate) => candidate.status === "deleted")) {
60855
+ ctx.output.info(` - ${item.identifier}`);
60270
60856
  }
60271
60857
  }
60272
- if (localItems.length > 0 || orphanIdentifiers.length > 0) {
60858
+ if (plannedPushes > 0 || dryRunPlan.summary.deleted > 0) {
60273
60859
  ctx.output.info("\nUse --yes (for remote) or remove --dry-run (for local) to apply changes.");
60274
60860
  }
60275
60861
  return {
60276
60862
  status: "dry-run",
60277
60863
  scopeCommand: config2.scopeCommand,
60278
60864
  scopeName: config2.scopeName,
60279
- created: 0,
60280
- updated: 0,
60281
- deleted: orphanIdentifiers.length,
60865
+ created: dryRunPlan.summary.created,
60866
+ updated: dryRunPlan.summary.updated,
60867
+ unchanged: dryRunPlan.summary.unchanged,
60868
+ deleted: dryRunPlan.summary.deleted,
60282
60869
  failed: 0,
60283
- errors: []
60870
+ errors: [],
60871
+ media: config2.dryRunReportExtras?.media
60284
60872
  };
60285
60873
  }
60286
60874
  const upserts = localItems.map((item) => ({
@@ -60405,22 +60993,23 @@ function createEmptyLocalContent() {
60405
60993
  };
60406
60994
  }
60407
60995
  async function syncLocalMediaForScopePush(ctx, content) {
60408
- ctx.output.info("Resolving local media identifiers...");
60409
- const result = await syncLocalMediaForPush({
60996
+ const result = await syncLocalMediaForPushWithReporting({
60997
+ output: ctx.output,
60410
60998
  contentDir: ctx.contentDir,
60411
60999
  content,
60412
61000
  client: ctx.client,
60413
61001
  dryRun: ctx.dryRun,
60414
- forceUpdateAsset: ctx.forceUpdateAsset
61002
+ forceUpdateAsset: ctx.forceUpdateAsset,
61003
+ targetEnv: ctx.targetEnv,
61004
+ isStructuredOutput: ctx.isStructuredOutput ?? ctx.isJsonOutput
60415
61005
  });
60416
- result.warnings.forEach((warning) => ctx.output.warn(warning));
60417
61006
  if (result.errors.length > 0) {
60418
- return { ok: false, errors: result.errors };
61007
+ return { ok: false, errors: result.errors, mediaReport: result.report };
60419
61008
  }
60420
61009
  if (!ctx.dryRun && !ctx.skipMeta) {
60421
61010
  await writeMediaManifest(ctx.contentDir, result.manifest);
60422
61011
  }
60423
- return { ok: true };
61012
+ return { ok: true, mediaReport: result.report };
60424
61013
  }
60425
61014
 
60426
61015
  // src/cli/commands/push/scopes/courses.ts
@@ -60642,7 +61231,8 @@ ${duplicate.filePaths.map((filePath) => ` - ${formatDuplicateEventFilePath(ct
60642
61231
  updated: 0,
60643
61232
  deleted: 0,
60644
61233
  failed: 0,
60645
- errors: []
61234
+ errors: [],
61235
+ media: mediaResult.mediaReport
60646
61236
  };
60647
61237
  }
60648
61238
  return pushCommandBackedDatedOfferingScope(ctx, {
@@ -60676,6 +61266,9 @@ ${duplicate.filePaths.map((filePath) => ` - ${formatDuplicateEventFilePath(ct
60676
61266
  await writeEvents(dir, pulledEvents, opts);
60677
61267
  },
60678
61268
  readOnlyStatusWarning: (event) => event.status === "completed" ? `Event ${event.identifier}: status "completed" is read-only; omitting status on push.` : null,
61269
+ dryRunReportExtras: {
61270
+ media: mediaResult.mediaReport
61271
+ },
60679
61272
  buildCompileInput: ({ source, siteId, targetEnv, upserts, deletes }) => ({
60680
61273
  source,
60681
61274
  siteId,
@@ -61536,6 +62129,8 @@ async function executeScopedPush(output, isRemote, options, globalOpts, scopeLab
61536
62129
  dryRun: options.dryRun,
61537
62130
  force: options.force ?? false,
61538
62131
  isJsonOutput: globalOpts.json ?? false,
62132
+ isStructuredOutput: Boolean(globalOpts.json || options.jsonDiffMode),
62133
+ jsonDiffMode: options.jsonDiffMode,
61539
62134
  forceUpdateAsset: options.forceUpdateAsset,
61540
62135
  deleteOrphaned: shouldDeleteOrphans,
61541
62136
  autoPullStale: options.autoPullStale ?? false,
@@ -62037,17 +62632,16 @@ ${errors}`
62037
62632
  }
62038
62633
  }
62039
62634
  }
62040
- output.info("Resolving local media identifiers...");
62041
- const localMediaResult = await syncLocalMediaForPush({
62635
+ const localMediaResult = await syncLocalMediaForPushWithReporting({
62636
+ output,
62042
62637
  contentDir,
62043
62638
  content: filteredLocal,
62044
62639
  client,
62045
62640
  dryRun,
62046
- forceUpdateAsset: options.forceUpdateAsset
62641
+ forceUpdateAsset: options.forceUpdateAsset,
62642
+ targetEnv: target,
62643
+ isStructuredOutput: jsonOutput || Boolean(jsonDiffMode)
62047
62644
  });
62048
- if (localMediaResult.warnings.length > 0) {
62049
- localMediaResult.warnings.forEach((warning) => output.warn(warning));
62050
- }
62051
62645
  if (localMediaResult.errors.length > 0) {
62052
62646
  output.error("Local media sync failed", {
62053
62647
  details: localMediaResult.errors.map((message) => ` - ${message}`).join("\n")
@@ -62085,15 +62679,12 @@ ${errors}`
62085
62679
  jsonDiffMode: jsonDiffMode ?? void 0,
62086
62680
  localContent: filteredLocal,
62087
62681
  remoteContent,
62088
- humanDiff
62682
+ humanDiff,
62683
+ mediaReport: localMediaResult.report
62089
62684
  });
62090
62685
  return { success: true };
62091
62686
  }
62092
62687
  if (localMediaResult && !dryRun) {
62093
- const mediaSummary = localMediaResult.summary;
62094
- output.info(
62095
- `Local media: ${mediaSummary.resolved} resolved, ${mediaSummary.uploaded} uploaded, ${mediaSummary.assigned} identifier updates`
62096
- );
62097
62688
  if (!skipMeta) {
62098
62689
  await writeMediaManifest(contentDir, localMediaResult.manifest);
62099
62690
  }
@@ -62126,7 +62717,8 @@ ${errors}`
62126
62717
  jsonDiffMode: jsonDiffMode ?? void 0,
62127
62718
  localContent: filteredLocal,
62128
62719
  remoteContent,
62129
- humanDiff
62720
+ humanDiff,
62721
+ mediaReport: localMediaResult.report
62130
62722
  });
62131
62723
  if (dryRun) {
62132
62724
  const preflight = preflightContentPushPlanAsCommandReport({
@@ -62197,13 +62789,6 @@ ${errors}`
62197
62789
  [DRY RUN] Would also push theme: ${themeName}`);
62198
62790
  }
62199
62791
  }
62200
- if (localMediaResult) {
62201
- const mediaSummary = localMediaResult.summary;
62202
- output.info(
62203
- `
62204
- [DRY RUN] Local media: ${mediaSummary.resolved} resolved, ${mediaSummary.pendingUploads} uploads, ${mediaSummary.pendingAssignments} identifier updates`
62205
- );
62206
- }
62207
62792
  if (options.syncMedia) {
62208
62793
  const extractable = localContentToExtractable(filteredLocal);
62209
62794
  const identifiers = extractMediaIdentifiers(extractable);
@@ -62242,10 +62827,6 @@ ${errors}`
62242
62827
  let hasFailures = syncResult.errors.length > 0;
62243
62828
  reportSyncResults({ output, result: syncResult, hasErrors: syncResult.errors.length > 0 });
62244
62829
  if (localMediaResult && !dryRun) {
62245
- const mediaSummary = localMediaResult.summary;
62246
- output.info(
62247
- `Local media: ${mediaSummary.resolved} resolved, ${mediaSummary.uploaded} uploaded, ${mediaSummary.assigned} identifier updates`
62248
- );
62249
62830
  if (!skipMeta) {
62250
62831
  await writeMediaManifest(contentDir, localMediaResult.manifest);
62251
62832
  }
@@ -62309,6 +62890,8 @@ ${errors}`
62309
62890
  dryRun: false,
62310
62891
  force: options.force ?? false,
62311
62892
  isJsonOutput,
62893
+ isStructuredOutput: isJsonOutput,
62894
+ jsonDiffMode: null,
62312
62895
  deleteOrphaned: false,
62313
62896
  autoPullStale: options.autoPullStale ?? false,
62314
62897
  skipMeta,
@@ -64033,7 +64616,7 @@ var THEME_FIELD_CATEGORY_MAP = {
64033
64616
  };
64034
64617
 
64035
64618
  // src/cli/init-docs/zod-to-markdown.ts
64036
- function isRecord21(value) {
64619
+ function isRecord22(value) {
64037
64620
  return typeof value === "object" && value !== null && !Array.isArray(value);
64038
64621
  }
64039
64622
  function isZodTypeAny(value) {
@@ -64161,7 +64744,7 @@ function extractLiteralValue(schema) {
64161
64744
  const typeName = getTypeName(schema);
64162
64745
  if (typeName !== ZodTypeName.ZodLiteral) return void 0;
64163
64746
  const def = schema._def;
64164
- if (!isRecord21(def)) return void 0;
64747
+ if (!isRecord22(def)) return void 0;
64165
64748
  return String(def.value);
64166
64749
  }
64167
64750
  function extractSchemaInfo(schema, skipFields = []) {
@@ -64231,7 +64814,7 @@ function extractFieldInfo(name, schema) {
64231
64814
  }
64232
64815
  if (typeName === ZodTypeName.ZodRecord) {
64233
64816
  const def = inner._def;
64234
- const valueSchema = isRecord21(def) ? def.valueType : void 0;
64817
+ const valueSchema = isRecord22(def) ? def.valueType : void 0;
64235
64818
  if (!isZodTypeAny(valueSchema)) {
64236
64819
  info.recordValueType = "unknown";
64237
64820
  return info;
@@ -64246,7 +64829,7 @@ function extractFieldInfo(name, schema) {
64246
64829
  }
64247
64830
  } else if (getTypeName(valueInner) === ZodTypeName.ZodRecord) {
64248
64831
  const nestedDef = valueInner._def;
64249
- const nestedValueSchema = isRecord21(nestedDef) ? nestedDef.valueType : void 0;
64832
+ const nestedValueSchema = isRecord22(nestedDef) ? nestedDef.valueType : void 0;
64250
64833
  if (!isZodTypeAny(nestedValueSchema)) {
64251
64834
  info.recordValueType = "record<string, unknown>";
64252
64835
  return info;
@@ -64257,7 +64840,7 @@ function extractFieldInfo(name, schema) {
64257
64840
  }
64258
64841
  if (typeName === ZodTypeName.ZodUnion || typeName === ZodTypeName.ZodDiscriminatedUnion) {
64259
64842
  const def = inner._def;
64260
- const optionsValue = isRecord21(def) ? def.options : void 0;
64843
+ const optionsValue = isRecord22(def) ? def.options : void 0;
64261
64844
  const options = Array.isArray(optionsValue) ? optionsValue.filter(isZodTypeAny) : [];
64262
64845
  const optionTypes = options.map((opt) => {
64263
64846
  const { inner: optInner } = unwrapSchema(opt);
@@ -65434,7 +66017,7 @@ var SimpleCache = class {
65434
66017
  };
65435
66018
 
65436
66019
  // src/version.ts
65437
- var SDK_VERSION = "0.91.0";
66020
+ var SDK_VERSION = "0.93.0";
65438
66021
 
65439
66022
  // src/client/error.ts
65440
66023
  var RiverbankApiError = class _RiverbankApiError extends Error {
@@ -73145,20 +73728,20 @@ var LEGACY_DATA_FIELDS = /* @__PURE__ */ new Set([
73145
73728
  "cardTitleOverride",
73146
73729
  "presentation"
73147
73730
  ]);
73148
- function isRecord22(value) {
73731
+ function isRecord23(value) {
73149
73732
  return typeof value === "object" && value !== null && !Array.isArray(value);
73150
73733
  }
73151
73734
  function hasMeaningfulValue(value) {
73152
73735
  if (value === void 0 || value === null) return false;
73153
73736
  if (typeof value === "string") return value.trim().length > 0;
73154
73737
  if (Array.isArray(value)) return value.length > 0;
73155
- if (isRecord22(value)) return Object.keys(value).length > 0;
73738
+ if (isRecord23(value)) return Object.keys(value).length > 0;
73156
73739
  return true;
73157
73740
  }
73158
73741
  function normalizeComparable(value) {
73159
73742
  if (value === void 0 || value === null) return null;
73160
73743
  if (Array.isArray(value)) return value.map(normalizeComparable);
73161
- if (!isRecord22(value)) return value;
73744
+ if (!isRecord23(value)) return value;
73162
73745
  const normalized = {};
73163
73746
  for (const key of Object.keys(value).sort()) {
73164
73747
  const normalizedValue = normalizeComparable(value[key]);
@@ -73178,7 +73761,7 @@ function slugFromPath(value) {
73178
73761
  }
73179
73762
  function legacySlugCandidates(legacy) {
73180
73763
  const candidates = /* @__PURE__ */ new Set();
73181
- const data = isRecord22(legacy.entry.data) ? legacy.entry.data : {};
73764
+ const data = isRecord23(legacy.entry.data) ? legacy.entry.data : {};
73182
73765
  for (const value of [
73183
73766
  legacy.entry.slug,
73184
73767
  data.slug,
@@ -73199,7 +73782,7 @@ async function readLegacyEventEntries(contentDir) {
73199
73782
  const entries = [];
73200
73783
  for (const filePath of files) {
73201
73784
  const raw = await readJsonFile(filePath);
73202
- if (!isRecord22(raw)) {
73785
+ if (!isRecord23(raw)) {
73203
73786
  entries.push({
73204
73787
  tag: "invalid",
73205
73788
  filePath,
@@ -73207,7 +73790,7 @@ async function readLegacyEventEntries(contentDir) {
73207
73790
  });
73208
73791
  continue;
73209
73792
  }
73210
- if (!isRecord22(raw.data)) {
73793
+ if (!isRecord23(raw.data)) {
73211
73794
  entries.push({
73212
73795
  tag: "invalid",
73213
73796
  filePath,
@@ -73345,7 +73928,7 @@ function mergeLegacyEventIntoCanonical(legacy, event) {
73345
73928
  conflicts.push(...blocksResult.fields);
73346
73929
  }
73347
73930
  if (hasMeaningfulValue(data.presentation)) {
73348
- if (!isRecord22(data.presentation)) {
73931
+ if (!isRecord23(data.presentation)) {
73349
73932
  return { tag: "unmappable", fields: ["data.presentation"] };
73350
73933
  }
73351
73934
  for (const [key, value] of Object.entries(data.presentation)) {