@rebuy/rebuy 3.28.0 → 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.
@@ -1408,7 +1408,14 @@ var CABReviewsSection = import_zod17.z.object({
1408
1408
  sectionId: import_zod17.z.uuid().default(() => (0, import_uuid10.v7)()),
1409
1409
  sectionType: import_zod17.z.literal(SectionType.reviews).default(SectionType.reviews),
1410
1410
  size: import_zod17.z.enum(reviewsSizes).default(ReviewsSize.default),
1411
- strokeColor: import_zod17.z.string().default("#fadb14"),
1411
+ /**
1412
+ * Star outline color, interpolated into a style by the renderer, so it is hex-constrained like
1413
+ * `CABButtonSection.color`. The legacy source (`PostPurchaseSettings.styles.starStrokeColor`) is a free
1414
+ * string: its converter must validate and fall back to this default rather than let one odd value drop
1415
+ * the whole reviews section.
1416
+ */
1417
+ strokeColor: import_zod17.z.string().regex(HEX_COLOR_REGEX).default("#fadb14"),
1418
+ /** Star outline width in px; `0` draws no outline. The legacy `styles.strokeWidth` is a STRING ('1') — its converter coerces. */
1412
1419
  strokeWidth: import_zod17.z.number().nonnegative().default(0)
1413
1420
  });
1414
1421
 
@@ -3588,7 +3595,7 @@ var convertOfferToV2 = ({ id, name, settings }) => {
3588
3595
  var parseShielded = (schema, raw, transform = (input) => input) => schema.parse(transform(convertNumericObjects(deepCamelCase(raw))));
3589
3596
 
3590
3597
  // src/transforms/postPurchase/convertMessagePreview.ts
3591
- var import_zod27 = require("zod");
3598
+ var import_zod30 = require("zod");
3592
3599
 
3593
3600
  // src/server/shared.ts
3594
3601
  var NotFoundError = class extends Error {
@@ -3819,6 +3826,150 @@ var convertCalloutBlock = (raw, report) => {
3819
3826
  return sections.length ? [CABLayoutSection.parse({ alignment: { horizontal }, frame: "callout", sections })] : [];
3820
3827
  };
3821
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
+
3822
3973
  // src/transforms/postPurchase/convertPayButtonBlock.ts
3823
3974
  var convertPayButtonBlock = (raw, report) => {
3824
3975
  const omit = () => {
@@ -3847,6 +3998,60 @@ var convertPayButtonBlock = (raw, report) => {
3847
3998
  ];
3848
3999
  };
3849
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
+
3850
4055
  // src/transforms/postPurchase/convertSeparatorBlock.ts
3851
4056
  var convertSeparatorBlock = (raw, report) => {
3852
4057
  if (!SeparatorBlock.safeParse(raw).success) {
@@ -3857,34 +4062,36 @@ var convertSeparatorBlock = (raw, report) => {
3857
4062
  };
3858
4063
 
3859
4064
  // src/transforms/postPurchase/convertTextBlock.ts
3860
- var import_zod26 = require("zod");
3861
- var fields2 = ["superTitle", "title", "description"];
4065
+ var import_zod29 = require("zod");
4066
+ var fields3 = ["superTitle", "title", "description"];
3862
4067
  var styles = {
3863
4068
  description: [{ attrs: { color: "base", fontSize: "small" }, type: "textStyle" }],
3864
4069
  superTitle: [{ attrs: { color: "subdued", fontSize: "small" }, type: "textStyle" }],
3865
4070
  title: [{ type: "bold" }, { attrs: { color: "base", fontSize: "large" }, type: "textStyle" }]
3866
4071
  };
3867
4072
  var settingsSchema = TextBlock.shape.settings;
3868
- var Shell2 = import_zod26.z.object({
4073
+ var Shell3 = import_zod29.z.object({
3869
4074
  settings: settingsSchema.pick({ horizontalAlignment: true }).extend({
3870
- description: import_zod26.z.unknown().optional(),
3871
- superTitle: import_zod26.z.unknown().optional(),
3872
- 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()
3873
4078
  }),
3874
- type: import_zod26.z.literal("text")
4079
+ type: import_zod29.z.literal("text")
3875
4080
  });
3876
4081
  var convertTextBlock = (raw, report) => {
3877
- const shell = Shell2.safeParse(raw);
4082
+ const shell = Shell3.safeParse(raw);
3878
4083
  if (!shell.success) {
3879
4084
  report?.({ detail: "Unreadable post-purchase text block", kind: "section" });
3880
4085
  return [];
3881
4086
  }
3882
- const values = fields2.map((field) => ({
4087
+ const values = fields3.map((field) => ({
3883
4088
  field,
3884
4089
  result: settingsSchema.shape[field].safeParse(shell.data.settings[field])
3885
4090
  }));
3886
4091
  const horizontal = { center: "center", leading: "start", trailing: "end" }[shell.data.settings.horizontalAlignment];
3887
- const styled = values.every(({ result }) => result.success && typeof result.data === "string");
4092
+ const styled = values.every(
4093
+ ({ result }) => result.success && (result.data === null || typeof result.data === "string")
4094
+ );
3888
4095
  const omit = (field) => {
3889
4096
  report?.({ detail: `Unreadable post-purchase text field: ${field}`, kind: "section" });
3890
4097
  return [];
@@ -3897,20 +4104,18 @@ var convertTextBlock = (raw, report) => {
3897
4104
  const section = CABTextSection.safeParse({ content: { en: content } });
3898
4105
  return section.success ? [section.data] : omit(field);
3899
4106
  });
3900
- return [
3901
- CABLayoutSection.parse({
3902
- alignment: {
3903
- horizontal
3904
- },
3905
- sections
3906
- })
3907
- ];
4107
+ return sections.length ? [CABLayoutSection.parse({ alignment: { horizontal }, sections })] : [];
3908
4108
  };
3909
4109
 
3910
4110
  // src/transforms/postPurchase/convertTimerBlock.ts
3911
4111
  var Presentation = TimerBlock.pick({ styles: true }).extend({
3912
4112
  settings: TimerBlock.shape.settings.pick({ title: true })
3913
4113
  });
4114
+ var withCountdown = (doc, run, textAlign) => {
4115
+ const paragraphs = doc.content.length ? doc.content : [TiptapParagraph.parse({ attrs: { textAlign } })];
4116
+ const last = paragraphs[paragraphs.length - 1];
4117
+ return { ...doc, content: [...paragraphs.slice(0, -1), { ...last, content: [...last.content, run] }] };
4118
+ };
3914
4119
  var convertTimerBlock = (raw, report) => {
3915
4120
  const omit = () => {
3916
4121
  report?.({ detail: "Unreadable post-purchase timer display", kind: "section" });
@@ -3921,17 +4126,15 @@ var convertTimerBlock = (raw, report) => {
3921
4126
  const { title } = parsed.data.settings;
3922
4127
  const styles2 = parsed.data.styles;
3923
4128
  const horizontal = { center: "center", leading: "start", trailing: "end" }[styles2.textAlignment];
4129
+ const countdown = styledTextRun(" {{timeRemaining}}", styles2.timerEmphasized, styles2.timerSize);
3924
4130
  const content = typeof title === "string" ? {
3925
4131
  content: [
3926
4132
  {
3927
4133
  attrs: { textAlign: horizontal },
3928
- content: [
3929
- styledTextRun(title, styles2.titleEmphasized, styles2.titleSize),
3930
- styledTextRun(" {{timeRemaining}}", styles2.timerEmphasized, styles2.timerSize)
3931
- ]
4134
+ content: [styledTextRun(title, styles2.titleEmphasized, styles2.titleSize), countdown]
3932
4135
  }
3933
4136
  ]
3934
- } : title;
4137
+ } : withCountdown(title, countdown, horizontal);
3935
4138
  const text = CABTextSection.safeParse({ content: { en: content } });
3936
4139
  if (!text.success) return omit();
3937
4140
  return [
@@ -3945,14 +4148,17 @@ var convertTimerBlock = (raw, report) => {
3945
4148
  };
3946
4149
 
3947
4150
  // src/transforms/postPurchase/convertMessagePreview.ts
3948
- var MessageWidget = import_zod27.z.object({
3949
- blocks: import_zod27.z.array(import_zod27.z.unknown()).min(1),
3950
- type: import_zod27.z.literal("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)
3951
4154
  });
3952
- var BlockType2 = import_zod27.z.object({ type: import_zod27.z.string() });
4155
+ var BlockType2 = import_zod30.z.object({ type: import_zod30.z.string() });
3953
4156
  var converters = {
3954
4157
  callout_banner: convertCalloutBlock,
4158
+ image: convertImageBlock,
4159
+ image_content: convertImageContentBlock,
3955
4160
  pay_button: convertPayButtonBlock,
4161
+ product_information: convertProductInformation,
3956
4162
  separator: convertSeparatorBlock,
3957
4163
  text: convertTextBlock,
3958
4164
  timer: convertTimerBlock
@@ -3966,7 +4172,7 @@ var convertMessagePreview = (id, settings, report) => {
3966
4172
  sections: parsed.data.blocks.flatMap((block) => {
3967
4173
  const type = BlockType2.safeParse(block);
3968
4174
  const kind = type.success ? type.data.type : void 0;
3969
- const convert2 = kind ? converters[kind] : void 0;
4175
+ const convert2 = kind && Object.hasOwn(converters, kind) ? converters[kind] : void 0;
3970
4176
  if (convert2) return convert2(block, report);
3971
4177
  report?.({
3972
4178
  detail: kind ? `Unsupported post-purchase block type: ${kind}` : "Unreadable post-purchase block: missing type",