@rebuy/rebuy 3.28.1 → 3.29.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -3595,7 +3595,7 @@ var convertOfferToV2 = ({ id, name, settings }) => {
3595
3595
  var parseShielded = (schema, raw, transform = (input) => input) => schema.parse(transform(convertNumericObjects(deepCamelCase(raw))));
3596
3596
 
3597
3597
  // src/transforms/postPurchase/convertMessagePreview.ts
3598
- var import_zod27 = require("zod");
3598
+ var import_zod30 = require("zod");
3599
3599
 
3600
3600
  // src/server/shared.ts
3601
3601
  var NotFoundError = class extends Error {
@@ -3826,6 +3826,150 @@ var convertCalloutBlock = (raw, report) => {
3826
3826
  return sections.length ? [CABLayoutSection.parse({ alignment: { horizontal }, frame: "callout", sections })] : [];
3827
3827
  };
3828
3828
 
3829
+ // src/transforms/postPurchase/convertImageFrame.ts
3830
+ var import_zod26 = require("zod");
3831
+
3832
+ // src/transforms/postPurchase/sizeImageSource.ts
3833
+ var sizeImageSource = (source, size) => {
3834
+ const extension = source.split(".").pop();
3835
+ for (const previous of ["small", "medium", "large", "original", "master"]) {
3836
+ source = source.replace(`_${previous}.${extension}`, `.${extension}`);
3837
+ }
3838
+ return source.replace(`.${extension}`, `${size === void 0 ? "" : `_${size}`}.${extension}`);
3839
+ };
3840
+
3841
+ // src/transforms/postPurchase/convertImageFrame.ts
3842
+ var Source = import_zod26.z.url().default("https://cdn.shopify.com/s/files/1/0605/0555/5177/files/placeholder.jpg?v=1657762972");
3843
+ var DEFAULT_IMAGE_SIZE = 20;
3844
+ var convertImageFrame = (raw) => {
3845
+ const parsed = ImageBlock.shape.settings.options[0].safeParse(raw);
3846
+ if (!parsed.success) return null;
3847
+ const settings = parsed.data;
3848
+ const source = sizeImageSource(Source.parse(settings.image || void 0));
3849
+ const withAlt = CABImageSection.safeParse({ altText: settings.alt, source });
3850
+ const image = withAlt.success ? withAlt.data : CABImageSection.parse({ source });
3851
+ const size = settings.imageSize > 0 ? settings.imageSize : DEFAULT_IMAGE_SIZE;
3852
+ return CABLayoutSection.parse({
3853
+ alignment: { horizontal: "center" },
3854
+ direction: "grid",
3855
+ grid: { columns: size <= 1 ? ["100%"] : [size] },
3856
+ sections: [image],
3857
+ width: size <= 1 ? size * 100 : 100
3858
+ });
3859
+ };
3860
+
3861
+ // src/transforms/postPurchase/convertImageBlock.ts
3862
+ var dropMalformedAlt = (raw) => {
3863
+ if (typeof raw !== "object" || raw === null) return raw;
3864
+ const { settings, ...rest } = raw;
3865
+ if (typeof settings !== "object" || settings === null || Array.isArray(settings)) return raw;
3866
+ const { alt, ...restSettings } = settings;
3867
+ if (typeof alt === "string" || alt === void 0) return raw;
3868
+ return { ...rest, settings: restSettings };
3869
+ };
3870
+ var convertImageBlock = (raw, report) => {
3871
+ const block = ImageBlock.safeParse(dropMalformedAlt(raw));
3872
+ const image = block.success ? convertImageFrame(block.data.settings) : null;
3873
+ if (!image) {
3874
+ report?.({ detail: "Unreadable post-purchase image", kind: "section" });
3875
+ return [];
3876
+ }
3877
+ return [
3878
+ CABLayoutSection.parse({
3879
+ alignment: { horizontal: "center" },
3880
+ frame: "offer-content",
3881
+ sections: [image],
3882
+ spacing: "none"
3883
+ })
3884
+ ];
3885
+ };
3886
+
3887
+ // src/transforms/postPurchase/convertImageContentBlock.ts
3888
+ var import_zod28 = require("zod");
3889
+
3890
+ // src/transforms/postPurchase/convertImageContentText.ts
3891
+ var fields2 = ["superTitle", "title", "description"];
3892
+ var convertImageContentText = (settings, report) => {
3893
+ const horizontal = { center: "center", leading: "start", trailing: "end" }[settings.horizontalAlignment];
3894
+ const sections = fields2.flatMap((field) => {
3895
+ const omit = () => {
3896
+ report?.({ detail: `Unreadable post-purchase image-content field: ${field}`, kind: "section" });
3897
+ return [];
3898
+ };
3899
+ const value = ImageContentBlock.shape.settings.shape[field].safeParse(settings[field]);
3900
+ if (!value.success) return omit();
3901
+ const text = value.data;
3902
+ if (text === null || text === "") return [];
3903
+ const content = typeof text === "string" ? {
3904
+ content: [
3905
+ {
3906
+ attrs: { textAlign: horizontal },
3907
+ content: [styledTextRun(text, field !== "description", settings[`${field}Size`])]
3908
+ }
3909
+ ]
3910
+ } : text;
3911
+ const section = CABTextSection.safeParse({ content: { en: content } });
3912
+ return section.success ? [section.data] : omit();
3913
+ });
3914
+ return CABLayoutSection.parse({ alignment: { horizontal }, sections });
3915
+ };
3916
+
3917
+ // src/transforms/postPurchase/imageContentSettings.ts
3918
+ var import_zod27 = require("zod");
3919
+ var ImageContentSettings = ImageContentBlock.shape.settings.pick({
3920
+ descriptionSize: true,
3921
+ horizontalAlignment: true,
3922
+ superTitleSize: true,
3923
+ swapContent: true,
3924
+ titleSize: true
3925
+ }).extend({
3926
+ alt: import_zod27.z.unknown().optional(),
3927
+ description: import_zod27.z.unknown().optional(),
3928
+ image: import_zod27.z.unknown().optional(),
3929
+ imagePlacement: ImageContentBlock.shape.settings.shape.imagePlacement.unwrap().nullable(),
3930
+ imageSize: ImageContentBlock.shape.settings.shape.imageSize.pipe(import_zod27.z.number().positive()),
3931
+ superTitle: import_zod27.z.unknown().optional(),
3932
+ title: import_zod27.z.unknown().optional()
3933
+ });
3934
+
3935
+ // src/transforms/postPurchase/convertImageContentBlock.ts
3936
+ var Shell2 = import_zod28.z.object({ settings: ImageContentSettings, type: import_zod28.z.literal("image_content") });
3937
+ var PROPORTIONAL_BREAK_AT = 500;
3938
+ var convertImageContentBlock = (raw, report) => {
3939
+ const shell = Shell2.safeParse(raw);
3940
+ if (!shell.success) {
3941
+ report?.({ detail: "Unreadable post-purchase image-content block", kind: "section" });
3942
+ return [];
3943
+ }
3944
+ const settings = shell.data.settings;
3945
+ const alt = typeof settings.alt === "string" ? settings.alt : void 0;
3946
+ if (settings.alt !== void 0 && alt === void 0) {
3947
+ report?.({ detail: "Unreadable post-purchase image-content alt text", kind: "section" });
3948
+ }
3949
+ const image = convertImageFrame({ alt, image: settings.image, imageSize: settings.imageSize });
3950
+ if (!image) report?.({ detail: "Unreadable post-purchase image-content image", kind: "section" });
3951
+ const text = convertImageContentText(settings, report);
3952
+ const textFrame = CABLayoutSection.parse({ direction: "grid", grid: { columns: ["100%"] }, sections: [text] });
3953
+ const placement = settings.imagePlacement ?? (settings.swapContent ? "right" : "left");
3954
+ const imageFirst = placement === "left" || placement === "top";
3955
+ const sections = imageFirst ? [image, textFrame] : [textFrame, image];
3956
+ const ordered = sections.filter((section) => section !== null);
3957
+ const inner = placement === "left" || placement === "right" ? CABLayoutSection.parse({
3958
+ sections: ordered,
3959
+ spacing: "loose",
3960
+ wrapping: {
3961
+ breakAt: settings.imageSize <= 1 ? PROPORTIONAL_BREAK_AT : settings.imageSize,
3962
+ maxPerLine: 2
3963
+ }
3964
+ }) : CABLayoutSection.parse({
3965
+ direction: "grid",
3966
+ grid: { columns: ["100%"] },
3967
+ sections: [CABLayoutSection.parse({ sections: ordered })],
3968
+ width: 95
3969
+ });
3970
+ return [CABLayoutSection.parse({ frame: "offer-content", sections: [inner], spacing: "none" })];
3971
+ };
3972
+
3829
3973
  // src/transforms/postPurchase/convertPayButtonBlock.ts
3830
3974
  var convertPayButtonBlock = (raw, report) => {
3831
3975
  const omit = () => {
@@ -3854,6 +3998,60 @@ var convertPayButtonBlock = (raw, report) => {
3854
3998
  ];
3855
3999
  };
3856
4000
 
4001
+ // src/transforms/postPurchase/convertProductInformation.ts
4002
+ var convertProductInformation = (raw, report) => {
4003
+ const block = ProductInformationBlock.safeParse(raw);
4004
+ if (!block.success) {
4005
+ report?.({ detail: "Unreadable post-purchase product information", kind: "section" });
4006
+ return [];
4007
+ }
4008
+ return [
4009
+ CABOffersSection.parse({
4010
+ direction: "grid",
4011
+ sections: [
4012
+ CABLayoutSection.parse({
4013
+ alignment: { horizontal: "center" },
4014
+ sections: [
4015
+ CABLayoutSection.parse({
4016
+ alignment: { horizontal: "center" },
4017
+ direction: "grid",
4018
+ grid: { columns: [250] },
4019
+ sections: [
4020
+ CABImageSection.parse({ altText: "{{productTitle}}", source: "{{productImage}}" })
4021
+ ],
4022
+ width: 90
4023
+ })
4024
+ ]
4025
+ }),
4026
+ CABLayoutSection.parse({
4027
+ sections: [
4028
+ CABTextSection.parse({
4029
+ content: {
4030
+ en: {
4031
+ content: [
4032
+ {
4033
+ content: [
4034
+ {
4035
+ marks: [{ attrs: { fontSize: "heading" }, type: "textStyle" }],
4036
+ text: "{{productTitle}}"
4037
+ }
4038
+ ]
4039
+ }
4040
+ ]
4041
+ }
4042
+ }
4043
+ }),
4044
+ CABReviewsSection.parse({}),
4045
+ CABTextSection.parse({ content: { en: "{{productDescription}}" } })
4046
+ ]
4047
+ })
4048
+ ],
4049
+ spacing: "extraLoose",
4050
+ wrapping: { breakAt: 500, maxPerLine: block.data.hideProductImage ? 1 : 2 }
4051
+ })
4052
+ ];
4053
+ };
4054
+
3857
4055
  // src/transforms/postPurchase/convertSeparatorBlock.ts
3858
4056
  var convertSeparatorBlock = (raw, report) => {
3859
4057
  if (!SeparatorBlock.safeParse(raw).success) {
@@ -3864,29 +4062,29 @@ var convertSeparatorBlock = (raw, report) => {
3864
4062
  };
3865
4063
 
3866
4064
  // src/transforms/postPurchase/convertTextBlock.ts
3867
- var import_zod26 = require("zod");
3868
- var fields2 = ["superTitle", "title", "description"];
4065
+ var import_zod29 = require("zod");
4066
+ var fields3 = ["superTitle", "title", "description"];
3869
4067
  var styles = {
3870
4068
  description: [{ attrs: { color: "base", fontSize: "small" }, type: "textStyle" }],
3871
4069
  superTitle: [{ attrs: { color: "subdued", fontSize: "small" }, type: "textStyle" }],
3872
4070
  title: [{ type: "bold" }, { attrs: { color: "base", fontSize: "large" }, type: "textStyle" }]
3873
4071
  };
3874
4072
  var settingsSchema = TextBlock.shape.settings;
3875
- var Shell2 = import_zod26.z.object({
4073
+ var Shell3 = import_zod29.z.object({
3876
4074
  settings: settingsSchema.pick({ horizontalAlignment: true }).extend({
3877
- description: import_zod26.z.unknown().optional(),
3878
- superTitle: import_zod26.z.unknown().optional(),
3879
- title: import_zod26.z.unknown().optional()
4075
+ description: import_zod29.z.unknown().optional(),
4076
+ superTitle: import_zod29.z.unknown().optional(),
4077
+ title: import_zod29.z.unknown().optional()
3880
4078
  }),
3881
- type: import_zod26.z.literal("text")
4079
+ type: import_zod29.z.literal("text")
3882
4080
  });
3883
4081
  var convertTextBlock = (raw, report) => {
3884
- const shell = Shell2.safeParse(raw);
4082
+ const shell = Shell3.safeParse(raw);
3885
4083
  if (!shell.success) {
3886
4084
  report?.({ detail: "Unreadable post-purchase text block", kind: "section" });
3887
4085
  return [];
3888
4086
  }
3889
- const values = fields2.map((field) => ({
4087
+ const values = fields3.map((field) => ({
3890
4088
  field,
3891
4089
  result: settingsSchema.shape[field].safeParse(shell.data.settings[field])
3892
4090
  }));
@@ -3950,14 +4148,17 @@ var convertTimerBlock = (raw, report) => {
3950
4148
  };
3951
4149
 
3952
4150
  // src/transforms/postPurchase/convertMessagePreview.ts
3953
- var MessageWidget = import_zod27.z.object({
3954
- blocks: import_zod27.z.array(import_zod27.z.unknown()).min(1),
3955
- type: import_zod27.z.literal(WidgetType.shopify_post_purchase_extension)
4151
+ var MessageWidget = import_zod30.z.object({
4152
+ blocks: import_zod30.z.array(import_zod30.z.unknown()).min(1),
4153
+ type: import_zod30.z.literal(WidgetType.shopify_post_purchase_extension)
3956
4154
  });
3957
- var BlockType2 = import_zod27.z.object({ type: import_zod27.z.string() });
4155
+ var BlockType2 = import_zod30.z.object({ type: import_zod30.z.string() });
3958
4156
  var converters = {
3959
4157
  callout_banner: convertCalloutBlock,
4158
+ image: convertImageBlock,
4159
+ image_content: convertImageContentBlock,
3960
4160
  pay_button: convertPayButtonBlock,
4161
+ product_information: convertProductInformation,
3961
4162
  separator: convertSeparatorBlock,
3962
4163
  text: convertTextBlock,
3963
4164
  timer: convertTimerBlock
@@ -3534,7 +3534,7 @@ var convertOfferToV2 = ({ id, name, settings }) => {
3534
3534
  var parseShielded = (schema, raw, transform = (input) => input) => schema.parse(transform(convertNumericObjects(deepCamelCase(raw))));
3535
3535
 
3536
3536
  // src/transforms/postPurchase/convertMessagePreview.ts
3537
- import { z as z27 } from "zod";
3537
+ import { z as z30 } from "zod";
3538
3538
 
3539
3539
  // src/server/shared.ts
3540
3540
  var NotFoundError = class extends Error {
@@ -3765,6 +3765,150 @@ var convertCalloutBlock = (raw, report) => {
3765
3765
  return sections.length ? [CABLayoutSection.parse({ alignment: { horizontal }, frame: "callout", sections })] : [];
3766
3766
  };
3767
3767
 
3768
+ // src/transforms/postPurchase/convertImageFrame.ts
3769
+ import { z as z26 } from "zod";
3770
+
3771
+ // src/transforms/postPurchase/sizeImageSource.ts
3772
+ var sizeImageSource = (source, size) => {
3773
+ const extension = source.split(".").pop();
3774
+ for (const previous of ["small", "medium", "large", "original", "master"]) {
3775
+ source = source.replace(`_${previous}.${extension}`, `.${extension}`);
3776
+ }
3777
+ return source.replace(`.${extension}`, `${size === void 0 ? "" : `_${size}`}.${extension}`);
3778
+ };
3779
+
3780
+ // src/transforms/postPurchase/convertImageFrame.ts
3781
+ var Source = z26.url().default("https://cdn.shopify.com/s/files/1/0605/0555/5177/files/placeholder.jpg?v=1657762972");
3782
+ var DEFAULT_IMAGE_SIZE = 20;
3783
+ var convertImageFrame = (raw) => {
3784
+ const parsed = ImageBlock.shape.settings.options[0].safeParse(raw);
3785
+ if (!parsed.success) return null;
3786
+ const settings = parsed.data;
3787
+ const source = sizeImageSource(Source.parse(settings.image || void 0));
3788
+ const withAlt = CABImageSection.safeParse({ altText: settings.alt, source });
3789
+ const image = withAlt.success ? withAlt.data : CABImageSection.parse({ source });
3790
+ const size = settings.imageSize > 0 ? settings.imageSize : DEFAULT_IMAGE_SIZE;
3791
+ return CABLayoutSection.parse({
3792
+ alignment: { horizontal: "center" },
3793
+ direction: "grid",
3794
+ grid: { columns: size <= 1 ? ["100%"] : [size] },
3795
+ sections: [image],
3796
+ width: size <= 1 ? size * 100 : 100
3797
+ });
3798
+ };
3799
+
3800
+ // src/transforms/postPurchase/convertImageBlock.ts
3801
+ var dropMalformedAlt = (raw) => {
3802
+ if (typeof raw !== "object" || raw === null) return raw;
3803
+ const { settings, ...rest } = raw;
3804
+ if (typeof settings !== "object" || settings === null || Array.isArray(settings)) return raw;
3805
+ const { alt, ...restSettings } = settings;
3806
+ if (typeof alt === "string" || alt === void 0) return raw;
3807
+ return { ...rest, settings: restSettings };
3808
+ };
3809
+ var convertImageBlock = (raw, report) => {
3810
+ const block = ImageBlock.safeParse(dropMalformedAlt(raw));
3811
+ const image = block.success ? convertImageFrame(block.data.settings) : null;
3812
+ if (!image) {
3813
+ report?.({ detail: "Unreadable post-purchase image", kind: "section" });
3814
+ return [];
3815
+ }
3816
+ return [
3817
+ CABLayoutSection.parse({
3818
+ alignment: { horizontal: "center" },
3819
+ frame: "offer-content",
3820
+ sections: [image],
3821
+ spacing: "none"
3822
+ })
3823
+ ];
3824
+ };
3825
+
3826
+ // src/transforms/postPurchase/convertImageContentBlock.ts
3827
+ import { z as z28 } from "zod";
3828
+
3829
+ // src/transforms/postPurchase/convertImageContentText.ts
3830
+ var fields2 = ["superTitle", "title", "description"];
3831
+ var convertImageContentText = (settings, report) => {
3832
+ const horizontal = { center: "center", leading: "start", trailing: "end" }[settings.horizontalAlignment];
3833
+ const sections = fields2.flatMap((field) => {
3834
+ const omit = () => {
3835
+ report?.({ detail: `Unreadable post-purchase image-content field: ${field}`, kind: "section" });
3836
+ return [];
3837
+ };
3838
+ const value = ImageContentBlock.shape.settings.shape[field].safeParse(settings[field]);
3839
+ if (!value.success) return omit();
3840
+ const text = value.data;
3841
+ if (text === null || text === "") return [];
3842
+ const content = typeof text === "string" ? {
3843
+ content: [
3844
+ {
3845
+ attrs: { textAlign: horizontal },
3846
+ content: [styledTextRun(text, field !== "description", settings[`${field}Size`])]
3847
+ }
3848
+ ]
3849
+ } : text;
3850
+ const section = CABTextSection.safeParse({ content: { en: content } });
3851
+ return section.success ? [section.data] : omit();
3852
+ });
3853
+ return CABLayoutSection.parse({ alignment: { horizontal }, sections });
3854
+ };
3855
+
3856
+ // src/transforms/postPurchase/imageContentSettings.ts
3857
+ import { z as z27 } from "zod";
3858
+ var ImageContentSettings = ImageContentBlock.shape.settings.pick({
3859
+ descriptionSize: true,
3860
+ horizontalAlignment: true,
3861
+ superTitleSize: true,
3862
+ swapContent: true,
3863
+ titleSize: true
3864
+ }).extend({
3865
+ alt: z27.unknown().optional(),
3866
+ description: z27.unknown().optional(),
3867
+ image: z27.unknown().optional(),
3868
+ imagePlacement: ImageContentBlock.shape.settings.shape.imagePlacement.unwrap().nullable(),
3869
+ imageSize: ImageContentBlock.shape.settings.shape.imageSize.pipe(z27.number().positive()),
3870
+ superTitle: z27.unknown().optional(),
3871
+ title: z27.unknown().optional()
3872
+ });
3873
+
3874
+ // src/transforms/postPurchase/convertImageContentBlock.ts
3875
+ var Shell2 = z28.object({ settings: ImageContentSettings, type: z28.literal("image_content") });
3876
+ var PROPORTIONAL_BREAK_AT = 500;
3877
+ var convertImageContentBlock = (raw, report) => {
3878
+ const shell = Shell2.safeParse(raw);
3879
+ if (!shell.success) {
3880
+ report?.({ detail: "Unreadable post-purchase image-content block", kind: "section" });
3881
+ return [];
3882
+ }
3883
+ const settings = shell.data.settings;
3884
+ const alt = typeof settings.alt === "string" ? settings.alt : void 0;
3885
+ if (settings.alt !== void 0 && alt === void 0) {
3886
+ report?.({ detail: "Unreadable post-purchase image-content alt text", kind: "section" });
3887
+ }
3888
+ const image = convertImageFrame({ alt, image: settings.image, imageSize: settings.imageSize });
3889
+ if (!image) report?.({ detail: "Unreadable post-purchase image-content image", kind: "section" });
3890
+ const text = convertImageContentText(settings, report);
3891
+ const textFrame = CABLayoutSection.parse({ direction: "grid", grid: { columns: ["100%"] }, sections: [text] });
3892
+ const placement = settings.imagePlacement ?? (settings.swapContent ? "right" : "left");
3893
+ const imageFirst = placement === "left" || placement === "top";
3894
+ const sections = imageFirst ? [image, textFrame] : [textFrame, image];
3895
+ const ordered = sections.filter((section) => section !== null);
3896
+ const inner = placement === "left" || placement === "right" ? CABLayoutSection.parse({
3897
+ sections: ordered,
3898
+ spacing: "loose",
3899
+ wrapping: {
3900
+ breakAt: settings.imageSize <= 1 ? PROPORTIONAL_BREAK_AT : settings.imageSize,
3901
+ maxPerLine: 2
3902
+ }
3903
+ }) : CABLayoutSection.parse({
3904
+ direction: "grid",
3905
+ grid: { columns: ["100%"] },
3906
+ sections: [CABLayoutSection.parse({ sections: ordered })],
3907
+ width: 95
3908
+ });
3909
+ return [CABLayoutSection.parse({ frame: "offer-content", sections: [inner], spacing: "none" })];
3910
+ };
3911
+
3768
3912
  // src/transforms/postPurchase/convertPayButtonBlock.ts
3769
3913
  var convertPayButtonBlock = (raw, report) => {
3770
3914
  const omit = () => {
@@ -3793,6 +3937,60 @@ var convertPayButtonBlock = (raw, report) => {
3793
3937
  ];
3794
3938
  };
3795
3939
 
3940
+ // src/transforms/postPurchase/convertProductInformation.ts
3941
+ var convertProductInformation = (raw, report) => {
3942
+ const block = ProductInformationBlock.safeParse(raw);
3943
+ if (!block.success) {
3944
+ report?.({ detail: "Unreadable post-purchase product information", kind: "section" });
3945
+ return [];
3946
+ }
3947
+ return [
3948
+ CABOffersSection.parse({
3949
+ direction: "grid",
3950
+ sections: [
3951
+ CABLayoutSection.parse({
3952
+ alignment: { horizontal: "center" },
3953
+ sections: [
3954
+ CABLayoutSection.parse({
3955
+ alignment: { horizontal: "center" },
3956
+ direction: "grid",
3957
+ grid: { columns: [250] },
3958
+ sections: [
3959
+ CABImageSection.parse({ altText: "{{productTitle}}", source: "{{productImage}}" })
3960
+ ],
3961
+ width: 90
3962
+ })
3963
+ ]
3964
+ }),
3965
+ CABLayoutSection.parse({
3966
+ sections: [
3967
+ CABTextSection.parse({
3968
+ content: {
3969
+ en: {
3970
+ content: [
3971
+ {
3972
+ content: [
3973
+ {
3974
+ marks: [{ attrs: { fontSize: "heading" }, type: "textStyle" }],
3975
+ text: "{{productTitle}}"
3976
+ }
3977
+ ]
3978
+ }
3979
+ ]
3980
+ }
3981
+ }
3982
+ }),
3983
+ CABReviewsSection.parse({}),
3984
+ CABTextSection.parse({ content: { en: "{{productDescription}}" } })
3985
+ ]
3986
+ })
3987
+ ],
3988
+ spacing: "extraLoose",
3989
+ wrapping: { breakAt: 500, maxPerLine: block.data.hideProductImage ? 1 : 2 }
3990
+ })
3991
+ ];
3992
+ };
3993
+
3796
3994
  // src/transforms/postPurchase/convertSeparatorBlock.ts
3797
3995
  var convertSeparatorBlock = (raw, report) => {
3798
3996
  if (!SeparatorBlock.safeParse(raw).success) {
@@ -3803,29 +4001,29 @@ var convertSeparatorBlock = (raw, report) => {
3803
4001
  };
3804
4002
 
3805
4003
  // src/transforms/postPurchase/convertTextBlock.ts
3806
- import { z as z26 } from "zod";
3807
- var fields2 = ["superTitle", "title", "description"];
4004
+ import { z as z29 } from "zod";
4005
+ var fields3 = ["superTitle", "title", "description"];
3808
4006
  var styles = {
3809
4007
  description: [{ attrs: { color: "base", fontSize: "small" }, type: "textStyle" }],
3810
4008
  superTitle: [{ attrs: { color: "subdued", fontSize: "small" }, type: "textStyle" }],
3811
4009
  title: [{ type: "bold" }, { attrs: { color: "base", fontSize: "large" }, type: "textStyle" }]
3812
4010
  };
3813
4011
  var settingsSchema = TextBlock.shape.settings;
3814
- var Shell2 = z26.object({
4012
+ var Shell3 = z29.object({
3815
4013
  settings: settingsSchema.pick({ horizontalAlignment: true }).extend({
3816
- description: z26.unknown().optional(),
3817
- superTitle: z26.unknown().optional(),
3818
- title: z26.unknown().optional()
4014
+ description: z29.unknown().optional(),
4015
+ superTitle: z29.unknown().optional(),
4016
+ title: z29.unknown().optional()
3819
4017
  }),
3820
- type: z26.literal("text")
4018
+ type: z29.literal("text")
3821
4019
  });
3822
4020
  var convertTextBlock = (raw, report) => {
3823
- const shell = Shell2.safeParse(raw);
4021
+ const shell = Shell3.safeParse(raw);
3824
4022
  if (!shell.success) {
3825
4023
  report?.({ detail: "Unreadable post-purchase text block", kind: "section" });
3826
4024
  return [];
3827
4025
  }
3828
- const values = fields2.map((field) => ({
4026
+ const values = fields3.map((field) => ({
3829
4027
  field,
3830
4028
  result: settingsSchema.shape[field].safeParse(shell.data.settings[field])
3831
4029
  }));
@@ -3889,14 +4087,17 @@ var convertTimerBlock = (raw, report) => {
3889
4087
  };
3890
4088
 
3891
4089
  // src/transforms/postPurchase/convertMessagePreview.ts
3892
- var MessageWidget = z27.object({
3893
- blocks: z27.array(z27.unknown()).min(1),
3894
- type: z27.literal(WidgetType.shopify_post_purchase_extension)
4090
+ var MessageWidget = z30.object({
4091
+ blocks: z30.array(z30.unknown()).min(1),
4092
+ type: z30.literal(WidgetType.shopify_post_purchase_extension)
3895
4093
  });
3896
- var BlockType2 = z27.object({ type: z27.string() });
4094
+ var BlockType2 = z30.object({ type: z30.string() });
3897
4095
  var converters = {
3898
4096
  callout_banner: convertCalloutBlock,
4097
+ image: convertImageBlock,
4098
+ image_content: convertImageContentBlock,
3899
4099
  pay_button: convertPayButtonBlock,
4100
+ product_information: convertProductInformation,
3900
4101
  separator: convertSeparatorBlock,
3901
4102
  text: convertTextBlock,
3902
4103
  timer: convertTimerBlock
@@ -0,0 +1,4 @@
1
+ import { CABLayoutSection } from '../../schema/widgets/checkout-and-beyond/layout';
2
+ import { type Reporter } from '../../server/shared';
3
+ /** A standalone image block, framed as the post-purchase offer's own content card. */
4
+ export declare const convertImageBlock: (raw: unknown, report?: Reporter) => CABLayoutSection[];
@@ -0,0 +1,7 @@
1
+ import { CABLayoutSection } from '../../schema/widgets/checkout-and-beyond/layout';
2
+ import { type Reporter } from '../../server/shared';
3
+ /**
4
+ * Image with text, placed left/right/top/bottom of it. Alt text is read from THIS block's own settings
5
+ * only — the plain `image` block's `alt` is never rendered by legacy (plan decision 15).
6
+ */
7
+ export declare const convertImageContentBlock: (raw: unknown, report?: Reporter) => CABLayoutSection[];
@@ -0,0 +1,5 @@
1
+ import { CABLayoutSection } from '../../schema/widgets/checkout-and-beyond/layout';
2
+ import { type Reporter } from '../../server/shared';
3
+ import { type ImageContentSettings } from '../../transforms/postPurchase/imageContentSettings';
4
+ /** Style readable strings independently; documents retain their authored marks and alignment. */
5
+ export declare const convertImageContentText: (settings: ImageContentSettings, report?: Reporter) => CABLayoutSection;
@@ -0,0 +1,9 @@
1
+ import { CABLayoutSection } from '../../schema/widgets/checkout-and-beyond/layout';
2
+ /**
3
+ * Shared legacy image policy, preserving numeric pixel and proportional frame units. A malformed `alt`
4
+ * (e.g. stray HTML) never drops the image itself — legacy never rejected the whole block for it, so this
5
+ * retries without alt text rather than returning `null`. A non-positive `imageSize` (corrupted saved data;
6
+ * the schema only defaults on a missing value, not on `0` or a negative one) falls back to the schema's own
7
+ * default instead of producing a zero/negative-width column.
8
+ */
9
+ export declare const convertImageFrame: (raw: unknown) => CABLayoutSection | null;
@@ -1,8 +1,8 @@
1
1
  import { CABRootSection } from '../../schema/widgets/checkout-and-beyond/root';
2
2
  import { type Reporter } from '../../server/shared';
3
3
  /**
4
- * Converts a saved post-purchase widget's blocks into a CAB preview tree. Only the five converters this
5
- * slice ships handle their block; every other block type — future (image/image_content/product_information)
6
- * or truly unknown — reports and drops only itself, and an unreadable block (no `type`) does the same.
4
+ * Converts a saved post-purchase widget's blocks into a CAB preview tree. Only the eight converters above
5
+ * handle their block; a truly unknown block type reports and drops only itself, and an unreadable block
6
+ * (no `type`) does the same.
7
7
  */
8
8
  export declare const convertMessagePreview: (id: number, settings: unknown, report?: Reporter) => CABRootSection;
@@ -0,0 +1,4 @@
1
+ import { CABOffersSection } from '../../schema/widgets/checkout-and-beyond/offers';
2
+ import type { Reporter } from '../../server/shared';
3
+ /** Structure only: the card provider binds the separately prepared fixed product. */
4
+ export declare const convertProductInformation: (raw: unknown, report?: Reporter) => CABOffersSection[];
@@ -0,0 +1,50 @@
1
+ import { z } from 'zod';
2
+ /** Keep raw fields for local recovery and do not inject the full schema's placement default. */
3
+ export declare const ImageContentSettings: z.ZodObject<{
4
+ superTitleSize: z.ZodDefault<z.ZodUnion<readonly [z.ZodEnum<{
5
+ small: "small";
6
+ default: "default";
7
+ base: "base";
8
+ large: "large";
9
+ medium: "medium";
10
+ xsmall: "xsmall";
11
+ xlarge: "xlarge";
12
+ }>, z.ZodPipe<z.ZodUnknown, z.ZodTransform<string, unknown>>]>>;
13
+ titleSize: z.ZodDefault<z.ZodUnion<readonly [z.ZodEnum<{
14
+ small: "small";
15
+ default: "default";
16
+ base: "base";
17
+ large: "large";
18
+ medium: "medium";
19
+ xsmall: "xsmall";
20
+ xlarge: "xlarge";
21
+ }>, z.ZodPipe<z.ZodUnknown, z.ZodTransform<string, unknown>>]>>;
22
+ descriptionSize: z.ZodDefault<z.ZodUnion<readonly [z.ZodEnum<{
23
+ small: "small";
24
+ default: "default";
25
+ base: "base";
26
+ large: "large";
27
+ medium: "medium";
28
+ xsmall: "xsmall";
29
+ xlarge: "xlarge";
30
+ }>, z.ZodPipe<z.ZodUnknown, z.ZodTransform<string, unknown>>]>>;
31
+ horizontalAlignment: z.ZodDefault<z.ZodUnion<readonly [z.ZodEnum<{
32
+ center: "center";
33
+ leading: "leading";
34
+ trailing: "trailing";
35
+ }>, z.ZodPipe<z.ZodUnknown, z.ZodTransform<string, unknown>>]>>;
36
+ swapContent: z.ZodDefault<z.ZodOptional<z.ZodBoolean>>;
37
+ alt: z.ZodOptional<z.ZodUnknown>;
38
+ description: z.ZodOptional<z.ZodUnknown>;
39
+ image: z.ZodOptional<z.ZodUnknown>;
40
+ imagePlacement: z.ZodNullable<z.ZodOptional<z.ZodEnum<{
41
+ top: "top";
42
+ bottom: "bottom";
43
+ right: "right";
44
+ left: "left";
45
+ }>>>;
46
+ imageSize: z.ZodPipe<z.ZodDefault<z.ZodNumber>, z.ZodNumber>;
47
+ superTitle: z.ZodOptional<z.ZodUnknown>;
48
+ title: z.ZodOptional<z.ZodUnknown>;
49
+ }, z.core.$strip>;
50
+ export type ImageContentSettings = z.infer<typeof ImageContentSettings>;
@@ -0,0 +1,2 @@
1
+ /** Preserve legacy named-size removal independently of each image's fallback and requested size. */
2
+ export declare const sizeImageSource: (source: string, size?: string) => string;