@riverbankcms/sdk 0.91.0 → 0.92.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 (104) hide show
  1. package/dist/{PreviewEditorSidebar-3TT47WCF.mjs → PreviewEditorSidebar-3CI5MKV3.mjs} +2 -2
  2. package/dist/{PreviewEditorUI-ORCLLUNZ.mjs → PreviewEditorUI-DKEP636I.mjs} +2 -2
  3. package/dist/SdkPreviewModeRuntime-BRGRDQFH.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/customerMembershipOutcomes.d.ts +4 -4
  8. package/dist/_dts/api/src/publicExperienceEndpoints.d.ts +6 -0
  9. package/dist/_dts/blocks/src/contracts/proof-block-islands.d.ts +32 -0
  10. package/dist/_dts/blocks/src/system/blocks/courses/shared/pricing.d.ts +12 -1
  11. package/dist/_dts/blocks/src/system/blocks/courses/shared/schemas.d.ts +12 -0
  12. package/dist/_dts/blocks/src/system/blocks/courses/shared/types.d.ts +4 -1
  13. package/dist/_dts/blocks/src/system/blocks/events/shared/schemas.d.ts +44 -0
  14. package/dist/_dts/blocks/src/system/manifest/fieldValidation/index.d.ts +7 -0
  15. package/dist/_dts/blocks/src/system/runtime/api/types.d.ts +8 -1
  16. package/dist/_dts/blocks/src/system/runtime/hooks/useHeadlessBookingFlow.d.ts +8 -1
  17. package/dist/_dts/blocks/src/system/runtime/hooks/useHeadlessBookingFlow.state.d.ts +1 -0
  18. package/dist/_dts/blocks/src/system/runtime/nodes/booking-form.headless.state.d.ts +4 -0
  19. package/dist/_dts/blocks/src/system/runtime/nodes/booking-form.interactive.d.ts +40 -0
  20. package/dist/_dts/blocks/src/system/runtime/nodes/booking-form.reserved-fields.d.ts +1 -1
  21. package/dist/_dts/blocks/src/system/runtime/nodes/booking-form.state.d.ts +29 -0
  22. package/dist/_dts/blocks/src/system/runtime/nodes/booking-runtime-sources.d.ts +1 -0
  23. package/dist/_dts/blocks/src/system/runtime/nodes/course-registration/components.d.ts +6 -0
  24. package/dist/_dts/blocks/src/system/runtime/nodes/course-registration/useCourseRegistrationWizard.d.ts +8 -0
  25. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/TicketSelectionStep.d.ts +8 -0
  26. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/hooks.d.ts +1 -0
  27. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/types.d.ts +1 -1
  28. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/useEventRegistrationSubmit.d.ts +1 -0
  29. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/useEventRegistrationWizard.d.ts +9 -0
  30. package/dist/_dts/content-model/src/entry-slots/types.d.ts +110 -2
  31. package/dist/_dts/core/src/branded.d.ts +3 -0
  32. package/dist/_dts/core/src/event-pricing.d.ts +18 -1
  33. package/dist/_dts/core/src/money-input.d.ts +34 -4
  34. package/dist/_dts/db/src/generated/supabase/database.types.d.ts +70 -0
  35. package/dist/_dts/media-core/src/fileAssetCompatibility.d.ts +1 -1
  36. package/dist/_dts/media-core/src/filePersistence.d.ts +5 -1
  37. package/dist/_dts/media-core/src/fileStorage.d.ts +1 -0
  38. package/dist/_dts/media-core/src/videoPrep.d.ts +15 -6
  39. package/dist/_dts/media-core/src/videoSlotPolicy.d.ts +5 -5
  40. package/dist/_dts/sdk/src/cli/commands/push/execute/diff.d.ts +2 -0
  41. package/dist/_dts/sdk/src/cli/commands/push/execute/localMediaReporting.d.ts +23 -0
  42. package/dist/_dts/sdk/src/cli/commands/push/scopes/commandBackedDatedOfferingScope.d.ts +54 -0
  43. package/dist/_dts/sdk/src/cli/commands/push/scopes/mediaPrep.d.ts +7 -2
  44. package/dist/_dts/sdk/src/cli/commands/push/scopes/types.d.ts +6 -0
  45. package/dist/_dts/sdk/src/cli/media/local-sync.d.ts +35 -0
  46. package/dist/_dts/sdk/src/rendering/islands/types.d.ts +23 -1
  47. package/dist/_dts/sdk/src/version.d.ts +1 -1
  48. package/dist/{chunk-AFPMCLS7.mjs → chunk-6D7PLF4M.mjs} +1 -1
  49. package/dist/{chunk-RQUZYSES.mjs → chunk-I6EOKV6B.mjs} +520 -40
  50. package/dist/{chunk-S3YHJWEU.mjs → chunk-LERKJM3E.mjs} +5 -5
  51. package/dist/{chunk-GDT2QGIL.mjs → chunk-UOMTCHZD.mjs} +1 -1
  52. package/dist/cli/index.mjs +552 -80
  53. package/dist/client/{accordion-AFX37652.mjs → accordion-BUDH2G3S.mjs} +2 -2
  54. package/dist/client/{bookingForm-32V5QCOW.mjs → bookingForm-NQMCELVB.mjs} +89 -14
  55. package/dist/client/bookings.mjs +243 -17
  56. package/dist/client/{carousel-TJGQJ7ER.mjs → carousel-ONCEW6IJ.mjs} +3 -3
  57. package/dist/client/{cart-JUU2JEW4.mjs → cart-V2TCK2GI.mjs} +7 -7
  58. package/dist/client/{checkout-RD6BTFX4.mjs → checkout-ZI7SWNCS.mjs} +7 -7
  59. package/dist/client/{chunk-FMZZHAIR.mjs → chunk-AUMRQ242.mjs} +52 -5
  60. package/dist/client/{chunk-PMNAV6LO.mjs → chunk-B5UC2D6X.mjs} +7 -6
  61. package/dist/client/{chunk-6RN62XBV.mjs → chunk-ERQDHUS2.mjs} +1 -1
  62. package/dist/client/{chunk-RWYFG625.mjs → chunk-GH6IIOOE.mjs} +30 -2
  63. package/dist/client/{chunk-C4LP6BPG.mjs → chunk-HQWIJLYN.mjs} +1 -1
  64. package/dist/client/{chunk-RMOFBBNA.mjs → chunk-JXY6NHG6.mjs} +6 -6
  65. package/dist/client/{chunk-6QZIBNGV.mjs → chunk-KXYEKEV6.mjs} +154 -16
  66. package/dist/client/{chunk-E6VIWZUG.mjs → chunk-N2WJPTF7.mjs} +3 -3
  67. package/dist/client/{chunk-ISAUXU5R.mjs → chunk-QQNVQ62W.mjs} +1 -1
  68. package/dist/client/{chunk-MLBNVN4H.mjs → chunk-R3CZQLXF.mjs} +67 -4
  69. package/dist/client/{chunk-WHZQDPBZ.mjs → chunk-TNGL2WJF.mjs} +1 -0
  70. package/dist/client/{chunk-CKXEUWWA.mjs → chunk-ZJDBOLX7.mjs} +3 -3
  71. package/dist/client/client.mjs +515 -38
  72. package/dist/client/{courseRegistration-X63QAIVV.mjs → courseRegistration-JPWAQ6KM.mjs} +111 -11
  73. package/dist/client/{eventCalendar-5T24GCZL.mjs → eventCalendar-QDOCHBBV.mjs} +9 -9
  74. package/dist/client/{eventCombined-AZVTF22X.mjs → eventCombined-ZS2CXSIE.mjs} +9 -9
  75. package/dist/client/{eventDetails-7GPFNLGF.mjs → eventDetails-5LOBMPLN.mjs} +9 -9
  76. package/dist/client/{eventListing-QEUMTMUM.mjs → eventListing-SBGY4I3L.mjs} +9 -9
  77. package/dist/client/{eventRegistration-P2EDKCQZ.mjs → eventRegistration-XGPIU3Z2.mjs} +9 -9
  78. package/dist/client/{form-NUJ7H4IN.mjs → form-R7D4LZWW.mjs} +3 -3
  79. package/dist/client/{headerSection-JB7LECPL.mjs → headerSection-RRLYUCGC.mjs} +3 -3
  80. package/dist/client/hooks.mjs +22 -2
  81. package/dist/client/{locationMap-UEYWUCSW.mjs → locationMap-35ZJMRBZ.mjs} +3 -3
  82. package/dist/client/{newsletterForm-S2YZQ2X7.mjs → newsletterForm-MJ2VCFUT.mjs} +3 -3
  83. package/dist/client/{productDetail-H5MADGG7.mjs → productDetail-2O7YQNBX.mjs} +7 -7
  84. package/dist/client/{productList-DK7YON7H.mjs → productList-FDP5BH7W.mjs} +7 -7
  85. package/dist/client/rendering/client.mjs +495 -36
  86. package/dist/client/rendering/islands.mjs +19 -19
  87. package/dist/client/rendering.mjs +514 -37
  88. package/dist/client/{shop-6PA7RDRJ.mjs → shop-BGYJ3LDT.mjs} +7 -7
  89. package/dist/preview-next/client/runtime.mjs +3 -3
  90. package/dist/{sdk-runtime-JP5347OB.mjs → sdk-runtime-B3TFXRXZ.mjs} +4 -4
  91. package/dist/server/components.mjs +166 -7
  92. package/dist/server/config-validation.mjs +23 -2
  93. package/dist/server/config.mjs +23 -2
  94. package/dist/server/data.mjs +22 -2
  95. package/dist/server/index.mjs +1 -1
  96. package/dist/server/next.mjs +167 -8
  97. package/dist/server/page-converter.mjs +19 -2
  98. package/dist/server/prebuild.mjs +1 -1
  99. package/dist/server/rendering/server.mjs +166 -7
  100. package/dist/server/rendering.mjs +166 -7
  101. package/dist/server/routing.mjs +29 -5
  102. package/dist/server/server.mjs +23 -3
  103. package/package.json +1 -1
  104. 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/",
@@ -18801,9 +18837,12 @@ var init_schemas = __esm({
18801
18837
  });
18802
18838
  coursePricingSchema = z.object({
18803
18839
  bookingRequirement: z.enum(["none", "required"]),
18804
- pricingStrategy: z.enum(["free", "single", "tiered"]),
18840
+ pricingStrategy: z.enum(["free", "single", "tiered", "sliding_scale"]),
18805
18841
  currency: z.string(),
18806
18842
  singlePriceAmount: z.number().nullable(),
18843
+ slidingMinAmount: z.number().nullable().optional(),
18844
+ slidingMaxAmount: z.number().nullable().optional(),
18845
+ slidingSuggestedAmount: z.number().nullable().optional(),
18807
18846
  paymentTerms: blockBookingPaymentTermsSchema,
18808
18847
  options: z.array(coursePricingOptionSchema)
18809
18848
  });
@@ -18964,6 +19003,19 @@ var init_schemas2 = __esm({
18964
19003
  ticketTypes: z.array(publicEventTicketTypeSchema),
18965
19004
  display: publicEventTicketTypePriceDisplaySchema
18966
19005
  }),
19006
+ z.object({
19007
+ kind: z.literal("sliding_scale"),
19008
+ minAmount: z.number(),
19009
+ maxAmount: z.number(),
19010
+ suggestedAmount: z.number().nullable(),
19011
+ currency: z.string(),
19012
+ display: z.object({
19013
+ kind: z.literal("range"),
19014
+ minAmount: z.number(),
19015
+ maxAmount: z.number(),
19016
+ currency: z.string()
19017
+ })
19018
+ }),
18967
19019
  z.object({
18968
19020
  kind: z.literal("course"),
18969
19021
  display: z.object({ kind: z.literal("course"), display: publicCoursePriceDisplaySchema })
@@ -19418,7 +19470,7 @@ function isRecord5(value) {
19418
19470
  }
19419
19471
  function isPublicEventPricing(value) {
19420
19472
  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";
19473
+ 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
19474
  }
19423
19475
  function isPublicEventTicketType(value) {
19424
19476
  return isRecord5(value) && typeof value.id === "string";
@@ -21846,6 +21898,7 @@ var init_booking_form_reserved_fields = __esm({
21846
21898
  "selectedDate",
21847
21899
  "selectedSlot",
21848
21900
  "discountCode",
21901
+ "selectedSlidingAmountInput",
21849
21902
  "selectedCollectionOptionKind",
21850
21903
  "selectedAppointmentPackageId"
21851
21904
  ];
@@ -46007,6 +46060,11 @@ function createOutput(options) {
46007
46060
  }
46008
46061
  };
46009
46062
  }
46063
+ function formatSize(bytes) {
46064
+ if (bytes < 1024) return `${bytes} B`;
46065
+ if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
46066
+ return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
46067
+ }
46010
46068
 
46011
46069
  // src/cli/errors.ts
46012
46070
  var ErrorCodes = {
@@ -48819,6 +48877,81 @@ var buildLocationNote = (locationsByIdentifier, identifier) => {
48819
48877
  const locations = locationsByIdentifier.get(identifier);
48820
48878
  return locations?.length ? ` Referenced at: ${locations.join(", ")}.` : "";
48821
48879
  };
48880
+ var locationsForIdentifier = (locationsByIdentifier, identifier) => {
48881
+ const locations = locationsByIdentifier.get(identifier);
48882
+ return locations?.length ? [...locations] : void 0;
48883
+ };
48884
+ var fileReportFields = (file) => {
48885
+ if (!file) return {};
48886
+ return {
48887
+ filename: file.filename,
48888
+ sizeBytes: file.data.length,
48889
+ mimeType: file.mimeType
48890
+ };
48891
+ };
48892
+ function classifyManagementApiError(error) {
48893
+ const code = error.code.toLowerCase();
48894
+ const message = error.message.toLowerCase();
48895
+ if (error.statusCode === 413 || code.includes("too_large") || message.includes("too large")) {
48896
+ return "max_object_size";
48897
+ }
48898
+ if (error.statusCode === 415 || code.includes("mime") || code.includes("content-type") || message.includes("mime") || message.includes("content-type")) {
48899
+ return "unsupported_mime";
48900
+ }
48901
+ if (error.statusCode === 402 || error.statusCode === 507 || code.includes("quota") || code.includes("storage") || message.includes("quota") || message.includes("storage limit")) {
48902
+ return "quota_or_storage_limit";
48903
+ }
48904
+ if (error.statusCode === 401 || error.statusCode === 403 || code.includes("auth") || code.includes("permission")) {
48905
+ return "auth_or_permission";
48906
+ }
48907
+ if (code === "sdk:network-error" || code === "sdk:timeout") {
48908
+ return "network";
48909
+ }
48910
+ if (code.startsWith("sdk:") || typeof error.statusCode === "number") {
48911
+ return "cms_api";
48912
+ }
48913
+ return "unknown";
48914
+ }
48915
+ function classifyErrorMessage(error) {
48916
+ const message = error.message.toLowerCase();
48917
+ if (message.includes("network") || message.includes("timeout") || message.includes("timed out")) {
48918
+ return "network";
48919
+ }
48920
+ if (message.includes("too large") || message.includes("payload too large") || message.includes("413")) {
48921
+ return "max_object_size";
48922
+ }
48923
+ if (message.includes("mime") || message.includes("content-type") || message.includes("unsupported media")) {
48924
+ return "unsupported_mime";
48925
+ }
48926
+ if (message.includes("quota") || message.includes("storage limit")) {
48927
+ return "quota_or_storage_limit";
48928
+ }
48929
+ if (message.includes("unauthorized") || message.includes("forbidden") || message.includes("permission")) {
48930
+ return "auth_or_permission";
48931
+ }
48932
+ return "unknown";
48933
+ }
48934
+ function classifyLocalMediaUploadFailure(error) {
48935
+ if (error instanceof ManagementApiError) {
48936
+ return classifyManagementApiError(error);
48937
+ }
48938
+ if (error instanceof Error) {
48939
+ return classifyErrorMessage(error);
48940
+ }
48941
+ return "unknown";
48942
+ }
48943
+ var rawErrorMessage = (error) => error instanceof Error ? error.message : String(error);
48944
+ var makeReport = (params) => {
48945
+ const failedIdentifiers = params.items.filter((item) => item.status === "failed").map((item) => item.identifier);
48946
+ const missingIdentifiers = params.items.filter((item) => item.status === "missing_local_file").map((item) => item.identifier);
48947
+ return {
48948
+ targetEnv: params.targetEnv,
48949
+ summary: { ...params.summary },
48950
+ items: params.items,
48951
+ failedIdentifiers,
48952
+ missingIdentifiers
48953
+ };
48954
+ };
48822
48955
  var loadLocalMediaFiles = async (contentDir, identifiers, manifest, errors) => {
48823
48956
  const localFiles = /* @__PURE__ */ new Map();
48824
48957
  for (const identifier of identifiers) {
@@ -48852,10 +48985,12 @@ var handleChecksumConflicts = async (options) => {
48852
48985
  assetsByIdentifier,
48853
48986
  missingIdentifiers,
48854
48987
  summary,
48988
+ reportItems,
48855
48989
  warnings,
48856
48990
  errors,
48857
48991
  locationsByIdentifier,
48858
48992
  dryRun,
48993
+ targetEnv,
48859
48994
  forceUpdateAsset
48860
48995
  } = options;
48861
48996
  const identifiersToReplace = /* @__PURE__ */ new Set();
@@ -48865,6 +49000,13 @@ var handleChecksumConflicts = async (options) => {
48865
49000
  summary.skipped += 1;
48866
49001
  const locationNote = buildLocationNote(locationsByIdentifier, identifier);
48867
49002
  warnings.push(`Missing local file for "${identifier}". Using CMS asset instead.${locationNote}`);
49003
+ reportItems.push({
49004
+ identifier,
49005
+ status: "skipped",
49006
+ targetEnv,
49007
+ locations: locationsForIdentifier(locationsByIdentifier, identifier),
49008
+ message: "Using existing CMS asset because no local file was found."
49009
+ });
48868
49010
  continue;
48869
49011
  }
48870
49012
  if (asset.checksumSha256 && asset.checksumSha256 !== localFile.checksum) {
@@ -48876,6 +49018,14 @@ var handleChecksumConflicts = async (options) => {
48876
49018
  } else {
48877
49019
  identifiersToReplace.add(identifier);
48878
49020
  }
49021
+ } else {
49022
+ reportItems.push({
49023
+ identifier,
49024
+ status: "resolved",
49025
+ targetEnv,
49026
+ ...fileReportFields(localFile),
49027
+ locations: locationsForIdentifier(locationsByIdentifier, identifier)
49028
+ });
48879
49029
  }
48880
49030
  }
48881
49031
  if (identifiersToReplace.size === 0) {
@@ -48898,10 +49048,12 @@ var resolveMissingByChecksum = async (options) => {
48898
49048
  localFiles,
48899
49049
  assetsByIdentifier,
48900
49050
  summary,
49051
+ reportItems,
48901
49052
  errors,
48902
49053
  warnings,
48903
49054
  locationsByIdentifier,
48904
49055
  dryRun,
49056
+ targetEnv,
48905
49057
  forceUpdateAsset
48906
49058
  } = options;
48907
49059
  const checksumCandidates = /* @__PURE__ */ new Map();
@@ -48912,6 +49064,13 @@ var resolveMissingByChecksum = async (options) => {
48912
49064
  errors.push(
48913
49065
  `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
49066
  );
49067
+ reportItems.push({
49068
+ identifier,
49069
+ status: "missing_local_file",
49070
+ targetEnv,
49071
+ locations: locationsForIdentifier(locationsByIdentifier, identifier),
49072
+ message: `Add content/media/${identifier}.* or pull with --local-media first.`
49073
+ });
48915
49074
  continue;
48916
49075
  }
48917
49076
  checksumCandidates.set(identifier, localFile);
@@ -48932,6 +49091,15 @@ var resolveMissingByChecksum = async (options) => {
48932
49091
  const matches = assetsByChecksum.get(localFile.checksum) ?? [];
48933
49092
  if (matches.length === 0) {
48934
49093
  uploadPlans.push({ identifier, file: localFile });
49094
+ if (dryRun) {
49095
+ reportItems.push({
49096
+ identifier,
49097
+ status: "pending_upload",
49098
+ targetEnv,
49099
+ ...fileReportFields(localFile),
49100
+ locations: locationsForIdentifier(locationsByIdentifier, identifier)
49101
+ });
49102
+ }
48935
49103
  continue;
48936
49104
  }
48937
49105
  const exact = matches.find((asset) => asset.identifier === identifier);
@@ -48939,6 +49107,14 @@ var resolveMissingByChecksum = async (options) => {
48939
49107
  mapIdentifierToAsset(assetsByIdentifier, identifier, exact);
48940
49108
  missingIdentifiers.delete(identifier);
48941
49109
  summary.reused += 1;
49110
+ reportItems.push({
49111
+ identifier,
49112
+ status: "reused",
49113
+ targetEnv,
49114
+ ...fileReportFields(localFile),
49115
+ locations: locationsForIdentifier(locationsByIdentifier, identifier),
49116
+ message: "Reused existing CMS asset with matching identifier and checksum."
49117
+ });
48942
49118
  continue;
48943
49119
  }
48944
49120
  const unassigned = matches.filter((asset) => !asset.identifier);
@@ -48959,9 +49135,25 @@ var resolveMissingByChecksum = async (options) => {
48959
49135
  });
48960
49136
  summary.assigned += 1;
48961
49137
  mapIdentifierToAsset(assetsByIdentifier, identifier, assigned);
49138
+ reportItems.push({
49139
+ identifier,
49140
+ status: "assigned",
49141
+ targetEnv,
49142
+ ...fileReportFields(localFile),
49143
+ locations: locationsForIdentifier(locationsByIdentifier, identifier),
49144
+ message: "Assigned identifier to an existing matching CMS asset."
49145
+ });
48962
49146
  } else {
48963
49147
  summary.pendingAssignments += 1;
48964
49148
  mapIdentifierToAsset(assetsByIdentifier, identifier, { ...candidate, identifier });
49149
+ reportItems.push({
49150
+ identifier,
49151
+ status: "assigned",
49152
+ targetEnv,
49153
+ ...fileReportFields(localFile),
49154
+ locations: locationsForIdentifier(locationsByIdentifier, identifier),
49155
+ message: "Would assign identifier to an existing matching CMS asset."
49156
+ });
48965
49157
  }
48966
49158
  missingIdentifiers.delete(identifier);
48967
49159
  summary.reused += 1;
@@ -48980,9 +49172,25 @@ var resolveMissingByChecksum = async (options) => {
48980
49172
  });
48981
49173
  summary.assigned += 1;
48982
49174
  mapIdentifierToAsset(assetsByIdentifier, identifier, reassigned);
49175
+ reportItems.push({
49176
+ identifier,
49177
+ status: "assigned",
49178
+ targetEnv,
49179
+ ...fileReportFields(localFile),
49180
+ locations: locationsForIdentifier(locationsByIdentifier, identifier),
49181
+ message: "Assigned identifier to an existing matching CMS asset."
49182
+ });
48983
49183
  } else {
48984
49184
  summary.pendingAssignments += 1;
48985
49185
  mapIdentifierToAsset(assetsByIdentifier, identifier, { ...candidate, identifier });
49186
+ reportItems.push({
49187
+ identifier,
49188
+ status: "assigned",
49189
+ targetEnv,
49190
+ ...fileReportFields(localFile),
49191
+ locations: locationsForIdentifier(locationsByIdentifier, identifier),
49192
+ message: "Would assign identifier to an existing matching CMS asset."
49193
+ });
48986
49194
  }
48987
49195
  missingIdentifiers.delete(identifier);
48988
49196
  summary.reused += 1;
@@ -48996,7 +49204,7 @@ var resolveMissingByChecksum = async (options) => {
48996
49204
  return uploadPlans;
48997
49205
  };
48998
49206
  var uploadMissingAssets = async (options) => {
48999
- const { client, uploadPlans, assetsByIdentifier, summary, errors, dryRun } = options;
49207
+ const { client, uploadPlans, assetsByIdentifier, summary, reportItems, errors, dryRun, targetEnv, onEvent } = options;
49000
49208
  if (uploadPlans.length === 0) {
49001
49209
  return;
49002
49210
  }
@@ -49006,6 +49214,13 @@ var uploadMissingAssets = async (options) => {
49006
49214
  }
49007
49215
  const uploadedIdentifiers = [];
49008
49216
  for (const plan of uploadPlans) {
49217
+ const startedItem = {
49218
+ identifier: plan.identifier,
49219
+ status: "pending_upload",
49220
+ targetEnv,
49221
+ ...fileReportFields(plan.file)
49222
+ };
49223
+ onEvent?.({ type: "upload_started", item: startedItem });
49009
49224
  try {
49010
49225
  const result = await client.media.upload({
49011
49226
  data: plan.file.data,
@@ -49017,9 +49232,30 @@ var uploadMissingAssets = async (options) => {
49017
49232
  if (result.status === "created") {
49018
49233
  summary.uploaded += 1;
49019
49234
  }
49235
+ const uploadedItem = {
49236
+ identifier: plan.identifier,
49237
+ status: "uploaded",
49238
+ targetEnv,
49239
+ ...fileReportFields(plan.file),
49240
+ message: result.status === "created" ? "Uploaded local media file." : "CMS reported media already exists."
49241
+ };
49242
+ reportItems.push(uploadedItem);
49243
+ onEvent?.({ type: "upload_succeeded", item: uploadedItem });
49020
49244
  } catch (error) {
49021
49245
  const message = error instanceof Error ? error.message : String(error);
49022
- errors.push(`Failed to upload "${plan.identifier}": ${message}`);
49246
+ const failureKind = classifyLocalMediaUploadFailure(error);
49247
+ const failedItem = {
49248
+ identifier: plan.identifier,
49249
+ status: "failed",
49250
+ targetEnv,
49251
+ ...fileReportFields(plan.file),
49252
+ message: `Failed to upload "${plan.identifier}": ${message}`,
49253
+ failureKind,
49254
+ rawError: rawErrorMessage(error)
49255
+ };
49256
+ reportItems.push(failedItem);
49257
+ onEvent?.({ type: "upload_failed", item: failedItem });
49258
+ errors.push(`Failed to upload "${plan.identifier}" (${plan.file.filename}, ${plan.file.mimeType}): ${message}`);
49023
49259
  }
49024
49260
  }
49025
49261
  if (uploadedIdentifiers.length === 0) {
@@ -49036,10 +49272,11 @@ var uploadMissingAssets = async (options) => {
49036
49272
  }
49037
49273
  };
49038
49274
  async function syncLocalMediaForPush(options) {
49039
- const { contentDir, content, client, dryRun, forceUpdateAsset } = options;
49275
+ const { contentDir, content, client, dryRun, forceUpdateAsset, targetEnv, onEvent } = options;
49040
49276
  const manifest = await readMediaManifest(contentDir);
49041
49277
  const warnings = [];
49042
49278
  const errors = [];
49279
+ const reportItems = [];
49043
49280
  const existingIdentifiers = /* @__PURE__ */ new Set();
49044
49281
  walkLocalContentMedia(content, (value) => {
49045
49282
  if (typeof value.identifier === "string" && value.identifier.trim().length > 0) {
@@ -49105,6 +49342,7 @@ async function syncLocalMediaForPush(options) {
49105
49342
  content,
49106
49343
  manifest,
49107
49344
  summary,
49345
+ report: makeReport({ targetEnv, summary, items: reportItems }),
49108
49346
  warnings,
49109
49347
  errors,
49110
49348
  hasChanges: false
@@ -49121,10 +49359,12 @@ async function syncLocalMediaForPush(options) {
49121
49359
  assetsByIdentifier,
49122
49360
  missingIdentifiers,
49123
49361
  summary,
49362
+ reportItems,
49124
49363
  warnings,
49125
49364
  errors,
49126
49365
  locationsByIdentifier,
49127
49366
  dryRun,
49367
+ targetEnv,
49128
49368
  forceUpdateAsset
49129
49369
  });
49130
49370
  const uploadPlans = await resolveMissingByChecksum({
@@ -49133,10 +49373,12 @@ async function syncLocalMediaForPush(options) {
49133
49373
  localFiles,
49134
49374
  assetsByIdentifier,
49135
49375
  summary,
49376
+ reportItems,
49136
49377
  errors,
49137
49378
  warnings,
49138
49379
  locationsByIdentifier,
49139
49380
  dryRun,
49381
+ targetEnv,
49140
49382
  forceUpdateAsset
49141
49383
  });
49142
49384
  await uploadMissingAssets({
@@ -49144,8 +49386,11 @@ async function syncLocalMediaForPush(options) {
49144
49386
  uploadPlans,
49145
49387
  assetsByIdentifier,
49146
49388
  summary,
49389
+ reportItems,
49147
49390
  errors,
49148
- dryRun
49391
+ dryRun,
49392
+ targetEnv,
49393
+ onEvent
49149
49394
  });
49150
49395
  summary.resolved = assetsByIdentifier.size;
49151
49396
  for (const reference of mediaReferences) {
@@ -49168,6 +49413,7 @@ async function syncLocalMediaForPush(options) {
49168
49413
  content,
49169
49414
  manifest,
49170
49415
  summary,
49416
+ report: makeReport({ targetEnv, summary, items: reportItems }),
49171
49417
  warnings,
49172
49418
  errors,
49173
49419
  hasChanges
@@ -53934,18 +54180,22 @@ async function readJsonSafe(filePath) {
53934
54180
 
53935
54181
  // src/cli/commands/push/execute/diff.ts
53936
54182
  function displayDiff(params) {
53937
- const { output, diff, config: config2, dryRun, isRemote, jsonOutput, jsonDiffMode, localContent, remoteContent, humanDiff } = params;
54183
+ const { output, diff, config: config2, dryRun, isRemote, jsonOutput, jsonDiffMode, localContent, remoteContent, humanDiff, mediaReport } = params;
53938
54184
  const envLabel = isRemote ? "REMOTE" : "LOCAL";
53939
54185
  if (jsonDiffMode) {
53940
54186
  const jsonDiff = buildJsonDiff(diff, localContent, remoteContent, jsonDiffMode);
53941
- output.json(jsonDiff);
54187
+ output.json({
54188
+ ...jsonDiff,
54189
+ ...mediaReport ? { media: mediaReport } : {}
54190
+ });
53942
54191
  return;
53943
54192
  }
53944
54193
  if (jsonOutput) {
53945
54194
  output.json({
53946
54195
  summary: diff.summary,
53947
54196
  syncConfig: config2.sync,
53948
- dryRun
54197
+ dryRun,
54198
+ ...mediaReport ? { media: mediaReport } : {}
53949
54199
  });
53950
54200
  return;
53951
54201
  }
@@ -60047,6 +60297,113 @@ async function syncMediaFilesForPush(params) {
60047
60297
  });
60048
60298
  }
60049
60299
 
60300
+ // src/cli/commands/push/execute/localMediaReporting.ts
60301
+ function targetSuffix(item) {
60302
+ return item.targetEnv ? ` to ${item.targetEnv}` : "";
60303
+ }
60304
+ function fileDetails(item) {
60305
+ const parts = [
60306
+ item.filename,
60307
+ typeof item.sizeBytes === "number" ? formatSize(item.sizeBytes) : void 0,
60308
+ item.mimeType
60309
+ ].filter((part) => Boolean(part));
60310
+ return parts.length ? ` (${parts.join(", ")})` : "";
60311
+ }
60312
+ function reportLocalMediaPushEvent(output, event) {
60313
+ switch (event.type) {
60314
+ case "upload_started":
60315
+ output.info(
60316
+ `Uploading media "${event.item.identifier}"${fileDetails(event.item)}${targetSuffix(event.item)}...`
60317
+ );
60318
+ return;
60319
+ case "upload_succeeded":
60320
+ output.info(
60321
+ `Uploaded media "${event.item.identifier}"${fileDetails(event.item)}${targetSuffix(event.item)}`
60322
+ );
60323
+ return;
60324
+ case "upload_failed":
60325
+ output.warn(
60326
+ `Failed to upload media "${event.item.identifier}"${fileDetails(event.item)}${targetSuffix(event.item)}: ${event.item.rawError ?? event.item.message ?? "Unknown error"}`
60327
+ );
60328
+ return;
60329
+ }
60330
+ }
60331
+ function reportLocalMediaPushSummary(params) {
60332
+ const { output, report, dryRun, includeResolved = false } = params;
60333
+ const { summary } = report;
60334
+ const pendingLabel = dryRun ? `${summary.pendingUploads} uploads, ${summary.pendingAssignments} identifier updates pending` : `${summary.uploaded} uploaded, ${summary.assigned} identifier updates`;
60335
+ output.info(
60336
+ `Local media${report.targetEnv ? ` (${report.targetEnv})` : ""}: ${summary.resolved} resolved, ${pendingLabel}`
60337
+ );
60338
+ for (const item of report.items) {
60339
+ if (!includeResolved && (item.status === "resolved" || item.status === "reused")) {
60340
+ continue;
60341
+ }
60342
+ switch (item.status) {
60343
+ case "pending_upload":
60344
+ output.info(
60345
+ ` Would upload "${item.identifier}"${fileDetails(item)}${targetSuffix(item)}`
60346
+ );
60347
+ break;
60348
+ case "assigned":
60349
+ output.info(` ${dryRun ? "Would assign" : "Assigned"} identifier "${item.identifier}"`);
60350
+ break;
60351
+ case "uploaded":
60352
+ output.info(` Uploaded "${item.identifier}"${fileDetails(item)}${targetSuffix(item)}`);
60353
+ break;
60354
+ case "missing_local_file":
60355
+ output.warn(` Missing local file for "${item.identifier}"${item.message ? `: ${item.message}` : ""}`);
60356
+ break;
60357
+ case "failed":
60358
+ output.warn(
60359
+ ` Failed "${item.identifier}"${fileDetails(item)}${item.failureKind ? ` [${item.failureKind}]` : ""}: ${item.rawError ?? item.message ?? "Unknown error"}`
60360
+ );
60361
+ break;
60362
+ case "skipped":
60363
+ output.info(` Skipped "${item.identifier}"${item.message ? `: ${item.message}` : ""}`);
60364
+ break;
60365
+ case "resolved":
60366
+ case "reused":
60367
+ output.info(` ${item.status === "reused" ? "Reused" : "Resolved"} "${item.identifier}"`);
60368
+ break;
60369
+ }
60370
+ }
60371
+ }
60372
+ async function syncLocalMediaForPushWithReporting(params) {
60373
+ const {
60374
+ output,
60375
+ contentDir,
60376
+ content,
60377
+ client,
60378
+ dryRun,
60379
+ targetEnv,
60380
+ isStructuredOutput,
60381
+ forceUpdateAsset,
60382
+ printSummary = true
60383
+ } = params;
60384
+ if (!isStructuredOutput) {
60385
+ output.info("Resolving local media identifiers...");
60386
+ }
60387
+ const result = await syncLocalMediaForPush({
60388
+ contentDir,
60389
+ content,
60390
+ client,
60391
+ dryRun,
60392
+ forceUpdateAsset,
60393
+ targetEnv,
60394
+ onEvent: isStructuredOutput ? void 0 : (event) => reportLocalMediaPushEvent(output, event)
60395
+ });
60396
+ result.warnings.forEach((warning) => output.warn(warning));
60397
+ if (!isStructuredOutput && printSummary && (result.report.items.length > 0 || result.hasChanges)) {
60398
+ reportLocalMediaPushSummary({
60399
+ output,
60400
+ report: result.report,
60401
+ dryRun
60402
+ });
60403
+ }
60404
+ return result;
60405
+ }
60406
+
60050
60407
  // src/cli/commands/push/execute/metadata.ts
60051
60408
  function mapIfUpdating(shouldUpdate, items, mapper) {
60052
60409
  return shouldUpdate ? items.map(mapper) : [];
@@ -60150,6 +60507,100 @@ ${errors}`
60150
60507
  output.error(message);
60151
60508
  }
60152
60509
  }
60510
+
60511
+ // src/cli/commands/push/scopes/commandBackedDatedOfferingScope.ts
60512
+ var VOLATILE_DATED_OFFERING_FIELDS = /* @__PURE__ */ new Set([
60513
+ "id",
60514
+ "siteId",
60515
+ "createdAt",
60516
+ "updatedAt",
60517
+ "contentUpdatedAt"
60518
+ ]);
60519
+ function normalizeDatedOfferingForComparison(value) {
60520
+ const normalized = JSON.parse(JSON.stringify(value));
60521
+ for (const field of VOLATILE_DATED_OFFERING_FIELDS) {
60522
+ delete normalized[field];
60523
+ }
60524
+ return normalized;
60525
+ }
60526
+ function createEmptyDryRunSummary() {
60527
+ return { created: 0, updated: 0, unchanged: 0, deleted: 0 };
60528
+ }
60529
+ function incrementDryRunSummary(summary, status) {
60530
+ switch (status) {
60531
+ case "created":
60532
+ return { ...summary, created: summary.created + 1 };
60533
+ case "updated":
60534
+ return { ...summary, updated: summary.updated + 1 };
60535
+ case "unchanged":
60536
+ return { ...summary, unchanged: summary.unchanged + 1 };
60537
+ case "deleted":
60538
+ return { ...summary, deleted: summary.deleted + 1 };
60539
+ }
60540
+ }
60541
+ function buildDatedOfferingDryRunPlan(input) {
60542
+ let summary = createEmptyDryRunSummary();
60543
+ const items = [];
60544
+ for (const item of input.localItems) {
60545
+ const identifier = input.config.getIdentifier(item);
60546
+ const remoteItem = input.config.findRemote(input.remoteItems, identifier);
60547
+ if (!remoteItem) {
60548
+ summary = incrementDryRunSummary(summary, "created");
60549
+ items.push({
60550
+ identifier,
60551
+ label: input.config.getDisplayLabel(item),
60552
+ status: "created",
60553
+ changes: []
60554
+ });
60555
+ continue;
60556
+ }
60557
+ const localObject = normalizeDatedOfferingForComparison(item);
60558
+ const remoteObject = normalizeDatedOfferingForComparison(remoteItem);
60559
+ const changes = findChangedFieldsWithValues(localObject, remoteObject);
60560
+ const status = changes.length > 0 ? "updated" : "unchanged";
60561
+ summary = incrementDryRunSummary(summary, status);
60562
+ items.push({
60563
+ identifier,
60564
+ label: input.config.getDisplayLabel(item),
60565
+ status,
60566
+ changes
60567
+ });
60568
+ }
60569
+ for (const identifier of input.orphanIdentifiers) {
60570
+ summary = incrementDryRunSummary(summary, "deleted");
60571
+ items.push({
60572
+ identifier,
60573
+ label: identifier,
60574
+ status: "deleted",
60575
+ changes: []
60576
+ });
60577
+ }
60578
+ return { summary, items };
60579
+ }
60580
+ function jsonDryRunReport(params) {
60581
+ const mode = params.jsonDiffMode ?? "summary";
60582
+ const changes = params.plan.items.filter((item) => item.status !== "unchanged").map((item) => ({
60583
+ type: params.scope,
60584
+ identifier: item.identifier,
60585
+ operation: item.status === "deleted" ? "delete" : item.status === "created" ? "create" : "update",
60586
+ ...mode === "full" ? { changes: item.changes } : {}
60587
+ }));
60588
+ return {
60589
+ mode,
60590
+ direction: "push",
60591
+ scope: params.scope,
60592
+ targetEnv: params.targetEnv,
60593
+ dryRun: true,
60594
+ summary: {
60595
+ creates: params.plan.summary.created,
60596
+ updates: params.plan.summary.updated,
60597
+ deletes: params.plan.summary.deleted,
60598
+ unchanged: params.plan.summary.unchanged
60599
+ },
60600
+ changes,
60601
+ ...params.media ? { media: params.media } : {}
60602
+ };
60603
+ }
60153
60604
  async function pushCommandBackedDatedOfferingScope(ctx, config2) {
60154
60605
  const noopResult = {
60155
60606
  status: "noop",
@@ -60229,58 +60680,84 @@ Run "riverbankcms pull ${config2.scopeCommand}" first to get latest, or use --fo
60229
60680
  const allLocalIdentifierSet = new Set(allLocalItems.map(config2.getIdentifier));
60230
60681
  const orphanIdentifiers = ctx.deleteOrphaned && !config2.shouldPushItem && remoteItems ? remoteItems.map(config2.getRemoteIdentifier).filter((identifier) => !allLocalIdentifierSet.has(identifier)) : [];
60231
60682
  if (ctx.dryRun) {
60232
- if (localItems.length > 0) {
60233
- ctx.output.info(`[DRY RUN] Would push ${localItems.length} ${config2.scopeName}`);
60683
+ const dryRunPlan = buildDatedOfferingDryRunPlan({
60684
+ localItems,
60685
+ remoteItems: remoteItems ?? [],
60686
+ orphanIdentifiers,
60687
+ config: config2
60688
+ });
60689
+ const plannedPushes = dryRunPlan.summary.created + dryRunPlan.summary.updated;
60690
+ if (ctx.jsonDiffMode) {
60691
+ ctx.output.json(jsonDryRunReport({
60692
+ scope: config2.scopeCommand,
60693
+ targetEnv: ctx.targetEnv,
60694
+ jsonDiffMode: ctx.jsonDiffMode,
60695
+ plan: dryRunPlan,
60696
+ media: config2.dryRunReportExtras?.media
60697
+ }));
60698
+ return {
60699
+ status: "dry-run",
60700
+ scopeCommand: config2.scopeCommand,
60701
+ scopeName: config2.scopeName,
60702
+ created: dryRunPlan.summary.created,
60703
+ updated: dryRunPlan.summary.updated,
60704
+ unchanged: dryRunPlan.summary.unchanged,
60705
+ deleted: dryRunPlan.summary.deleted,
60706
+ failed: 0,
60707
+ errors: [],
60708
+ media: config2.dryRunReportExtras?.media
60709
+ };
60710
+ }
60711
+ if (plannedPushes > 0) {
60712
+ ctx.output.info(
60713
+ `[DRY RUN] Would push ${plannedPushes} ${config2.scopeName} (${dryRunPlan.summary.created} create, ${dryRunPlan.summary.updated} update, ${dryRunPlan.summary.unchanged} unchanged)`
60714
+ );
60234
60715
  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) {
60716
+ ctx.output.info(` (skipping ${skippedUnchanged} filtered out)`);
60717
+ }
60718
+ } else if (dryRunPlan.summary.deleted === 0) {
60719
+ ctx.output.info(
60720
+ `[DRY RUN] No ${config2.scopeName} changes to push (${dryRunPlan.summary.unchanged} unchanged)`
60721
+ );
60722
+ }
60723
+ for (const item of dryRunPlan.items) {
60724
+ switch (item.status) {
60725
+ case "created":
60726
+ ctx.output.info(` + ${item.identifier}: ${item.label} (new)`);
60727
+ break;
60728
+ case "updated":
60729
+ ctx.output.info(` ~ ${item.identifier}: ${item.label}`);
60730
+ if (ctx.humanDiff) {
60731
+ for (const change of item.changes) {
60256
60732
  ctx.output.info(` ${change.path}: ${formatValue(change.before)} -> ${formatValue(change.after)}`);
60257
60733
  }
60258
60734
  }
60259
- }
60260
- } else {
60261
- for (const item of localItems) {
60262
- ctx.output.info(` - ${config2.getIdentifier(item)}: ${config2.getDisplayLabel(item)}`);
60263
- }
60735
+ break;
60736
+ case "unchanged":
60737
+ ctx.output.info(` = ${item.identifier}: ${item.label} (unchanged)`);
60738
+ break;
60264
60739
  }
60265
60740
  }
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}`);
60741
+ if (dryRunPlan.summary.deleted > 0) {
60742
+ ctx.output.info(`[DRY RUN] Would delete ${dryRunPlan.summary.deleted} orphaned ${config2.scopeName}:`);
60743
+ for (const item of dryRunPlan.items.filter((candidate) => candidate.status === "deleted")) {
60744
+ ctx.output.info(` - ${item.identifier}`);
60270
60745
  }
60271
60746
  }
60272
- if (localItems.length > 0 || orphanIdentifiers.length > 0) {
60747
+ if (plannedPushes > 0 || dryRunPlan.summary.deleted > 0) {
60273
60748
  ctx.output.info("\nUse --yes (for remote) or remove --dry-run (for local) to apply changes.");
60274
60749
  }
60275
60750
  return {
60276
60751
  status: "dry-run",
60277
60752
  scopeCommand: config2.scopeCommand,
60278
60753
  scopeName: config2.scopeName,
60279
- created: 0,
60280
- updated: 0,
60281
- deleted: orphanIdentifiers.length,
60754
+ created: dryRunPlan.summary.created,
60755
+ updated: dryRunPlan.summary.updated,
60756
+ unchanged: dryRunPlan.summary.unchanged,
60757
+ deleted: dryRunPlan.summary.deleted,
60282
60758
  failed: 0,
60283
- errors: []
60759
+ errors: [],
60760
+ media: config2.dryRunReportExtras?.media
60284
60761
  };
60285
60762
  }
60286
60763
  const upserts = localItems.map((item) => ({
@@ -60405,22 +60882,23 @@ function createEmptyLocalContent() {
60405
60882
  };
60406
60883
  }
60407
60884
  async function syncLocalMediaForScopePush(ctx, content) {
60408
- ctx.output.info("Resolving local media identifiers...");
60409
- const result = await syncLocalMediaForPush({
60885
+ const result = await syncLocalMediaForPushWithReporting({
60886
+ output: ctx.output,
60410
60887
  contentDir: ctx.contentDir,
60411
60888
  content,
60412
60889
  client: ctx.client,
60413
60890
  dryRun: ctx.dryRun,
60414
- forceUpdateAsset: ctx.forceUpdateAsset
60891
+ forceUpdateAsset: ctx.forceUpdateAsset,
60892
+ targetEnv: ctx.targetEnv,
60893
+ isStructuredOutput: ctx.isStructuredOutput ?? ctx.isJsonOutput
60415
60894
  });
60416
- result.warnings.forEach((warning) => ctx.output.warn(warning));
60417
60895
  if (result.errors.length > 0) {
60418
- return { ok: false, errors: result.errors };
60896
+ return { ok: false, errors: result.errors, mediaReport: result.report };
60419
60897
  }
60420
60898
  if (!ctx.dryRun && !ctx.skipMeta) {
60421
60899
  await writeMediaManifest(ctx.contentDir, result.manifest);
60422
60900
  }
60423
- return { ok: true };
60901
+ return { ok: true, mediaReport: result.report };
60424
60902
  }
60425
60903
 
60426
60904
  // src/cli/commands/push/scopes/courses.ts
@@ -60642,7 +61120,8 @@ ${duplicate.filePaths.map((filePath) => ` - ${formatDuplicateEventFilePath(ct
60642
61120
  updated: 0,
60643
61121
  deleted: 0,
60644
61122
  failed: 0,
60645
- errors: []
61123
+ errors: [],
61124
+ media: mediaResult.mediaReport
60646
61125
  };
60647
61126
  }
60648
61127
  return pushCommandBackedDatedOfferingScope(ctx, {
@@ -60676,6 +61155,9 @@ ${duplicate.filePaths.map((filePath) => ` - ${formatDuplicateEventFilePath(ct
60676
61155
  await writeEvents(dir, pulledEvents, opts);
60677
61156
  },
60678
61157
  readOnlyStatusWarning: (event) => event.status === "completed" ? `Event ${event.identifier}: status "completed" is read-only; omitting status on push.` : null,
61158
+ dryRunReportExtras: {
61159
+ media: mediaResult.mediaReport
61160
+ },
60679
61161
  buildCompileInput: ({ source, siteId, targetEnv, upserts, deletes }) => ({
60680
61162
  source,
60681
61163
  siteId,
@@ -61536,6 +62018,8 @@ async function executeScopedPush(output, isRemote, options, globalOpts, scopeLab
61536
62018
  dryRun: options.dryRun,
61537
62019
  force: options.force ?? false,
61538
62020
  isJsonOutput: globalOpts.json ?? false,
62021
+ isStructuredOutput: Boolean(globalOpts.json || options.jsonDiffMode),
62022
+ jsonDiffMode: options.jsonDiffMode,
61539
62023
  forceUpdateAsset: options.forceUpdateAsset,
61540
62024
  deleteOrphaned: shouldDeleteOrphans,
61541
62025
  autoPullStale: options.autoPullStale ?? false,
@@ -62037,17 +62521,16 @@ ${errors}`
62037
62521
  }
62038
62522
  }
62039
62523
  }
62040
- output.info("Resolving local media identifiers...");
62041
- const localMediaResult = await syncLocalMediaForPush({
62524
+ const localMediaResult = await syncLocalMediaForPushWithReporting({
62525
+ output,
62042
62526
  contentDir,
62043
62527
  content: filteredLocal,
62044
62528
  client,
62045
62529
  dryRun,
62046
- forceUpdateAsset: options.forceUpdateAsset
62530
+ forceUpdateAsset: options.forceUpdateAsset,
62531
+ targetEnv: target,
62532
+ isStructuredOutput: jsonOutput || Boolean(jsonDiffMode)
62047
62533
  });
62048
- if (localMediaResult.warnings.length > 0) {
62049
- localMediaResult.warnings.forEach((warning) => output.warn(warning));
62050
- }
62051
62534
  if (localMediaResult.errors.length > 0) {
62052
62535
  output.error("Local media sync failed", {
62053
62536
  details: localMediaResult.errors.map((message) => ` - ${message}`).join("\n")
@@ -62085,15 +62568,12 @@ ${errors}`
62085
62568
  jsonDiffMode: jsonDiffMode ?? void 0,
62086
62569
  localContent: filteredLocal,
62087
62570
  remoteContent,
62088
- humanDiff
62571
+ humanDiff,
62572
+ mediaReport: localMediaResult.report
62089
62573
  });
62090
62574
  return { success: true };
62091
62575
  }
62092
62576
  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
62577
  if (!skipMeta) {
62098
62578
  await writeMediaManifest(contentDir, localMediaResult.manifest);
62099
62579
  }
@@ -62126,7 +62606,8 @@ ${errors}`
62126
62606
  jsonDiffMode: jsonDiffMode ?? void 0,
62127
62607
  localContent: filteredLocal,
62128
62608
  remoteContent,
62129
- humanDiff
62609
+ humanDiff,
62610
+ mediaReport: localMediaResult.report
62130
62611
  });
62131
62612
  if (dryRun) {
62132
62613
  const preflight = preflightContentPushPlanAsCommandReport({
@@ -62197,13 +62678,6 @@ ${errors}`
62197
62678
  [DRY RUN] Would also push theme: ${themeName}`);
62198
62679
  }
62199
62680
  }
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
62681
  if (options.syncMedia) {
62208
62682
  const extractable = localContentToExtractable(filteredLocal);
62209
62683
  const identifiers = extractMediaIdentifiers(extractable);
@@ -62242,10 +62716,6 @@ ${errors}`
62242
62716
  let hasFailures = syncResult.errors.length > 0;
62243
62717
  reportSyncResults({ output, result: syncResult, hasErrors: syncResult.errors.length > 0 });
62244
62718
  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
62719
  if (!skipMeta) {
62250
62720
  await writeMediaManifest(contentDir, localMediaResult.manifest);
62251
62721
  }
@@ -62309,6 +62779,8 @@ ${errors}`
62309
62779
  dryRun: false,
62310
62780
  force: options.force ?? false,
62311
62781
  isJsonOutput,
62782
+ isStructuredOutput: isJsonOutput,
62783
+ jsonDiffMode: null,
62312
62784
  deleteOrphaned: false,
62313
62785
  autoPullStale: options.autoPullStale ?? false,
62314
62786
  skipMeta,
@@ -65434,7 +65906,7 @@ var SimpleCache = class {
65434
65906
  };
65435
65907
 
65436
65908
  // src/version.ts
65437
- var SDK_VERSION = "0.91.0";
65909
+ var SDK_VERSION = "0.92.0";
65438
65910
 
65439
65911
  // src/client/error.ts
65440
65912
  var RiverbankApiError = class _RiverbankApiError extends Error {