@rebuy/rebuy 3.21.0 → 3.22.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.
|
@@ -6,13 +6,17 @@ import { type MonetizeInput, type ServerContext } from '../server/shared';
|
|
|
6
6
|
* built (impression/decline/click) — the composer wires them onto the card verbatim.
|
|
7
7
|
*/
|
|
8
8
|
export declare const AdsEngineOffer: z.ZodObject<{
|
|
9
|
+
callout: z.ZodCatch<z.ZodString>;
|
|
9
10
|
click_url: z.ZodString;
|
|
10
11
|
cta: z.ZodString;
|
|
11
12
|
decline_cta: z.ZodCatch<z.ZodString>;
|
|
12
13
|
decline_url: z.ZodString;
|
|
13
14
|
description: z.ZodString;
|
|
15
|
+
disclaimer: z.ZodCatch<z.ZodString>;
|
|
16
|
+
extended_disclaimer: z.ZodCatch<z.ZodString>;
|
|
14
17
|
headline: z.ZodString;
|
|
15
18
|
id: z.ZodString;
|
|
19
|
+
image_alt: z.ZodCatch<z.ZodString>;
|
|
16
20
|
image_url: z.ZodOptional<z.ZodString>;
|
|
17
21
|
view_url: z.ZodString;
|
|
18
22
|
}, z.core.$strip>;
|
package/dist/server/index.cjs
CHANGED
|
@@ -4184,14 +4184,22 @@ var transactionItems = (input) => (input.transactionItems?.map(({ id, priceCents
|
|
|
4184
4184
|
type
|
|
4185
4185
|
})) ?? input.cart.items.map(({ productId, quantity }) => ({ id: productId, quantity }))).slice(0, MAX_TRANSACTION_ITEMS);
|
|
4186
4186
|
var AdsEngineOffer = import_zod28.z.object({
|
|
4187
|
+
/** Pill-style value prop ("Get $45 Cashback"). Carried through the parse; the card does not render it yet (REB-24429 — pill design pending). */
|
|
4188
|
+
callout: import_zod28.z.string().catch(""),
|
|
4187
4189
|
click_url: import_zod28.z.string(),
|
|
4188
4190
|
cta: import_zod28.z.string(),
|
|
4189
4191
|
/** `omitempty` on the ad server — a bid without it must not fail the parse and drop the whole batch (composeOffer falls back to 'No Thanks'). */
|
|
4190
4192
|
decline_cta: import_zod28.z.string().catch(""),
|
|
4191
4193
|
decline_url: import_zod28.z.string(),
|
|
4192
4194
|
description: import_zod28.z.string(),
|
|
4195
|
+
/** Short legal disclosure (FDIC / auto-renewal / FTC-affiliation lines). Rendered subdued under the CTAs. */
|
|
4196
|
+
disclaimer: import_zod28.z.string().catch(""),
|
|
4197
|
+
/** Long legal text behind the disclosure toggle in the composed card (REB-24429). */
|
|
4198
|
+
extended_disclaimer: import_zod28.z.string().catch(""),
|
|
4193
4199
|
headline: import_zod28.z.string(),
|
|
4194
4200
|
id: import_zod28.z.string(),
|
|
4201
|
+
/** Advertiser-supplied image alt; the composed card falls back to the stripped headline when empty. */
|
|
4202
|
+
image_alt: import_zod28.z.string().catch(""),
|
|
4195
4203
|
image_url: import_zod28.z.string().optional(),
|
|
4196
4204
|
view_url: import_zod28.z.string()
|
|
4197
4205
|
});
|
|
@@ -4208,6 +4216,15 @@ var isTestingRequest = (input, isProduction) => !isProduction || !!input.testing
|
|
|
4208
4216
|
var buildAdsParams = (input, publisherKey, supplyKey, testing, userAgent, ipAddress) => {
|
|
4209
4217
|
const items = transactionItems(input);
|
|
4210
4218
|
return {
|
|
4219
|
+
/**
|
|
4220
|
+
* The engine converts text fields to the requested format before responding (Fluent's source
|
|
4221
|
+
* format is html). `html` makes that conversion a no-op, so the canonical HTML→Tiptap pipeline
|
|
4222
|
+
* receives the DSP's original markup — the default (markdown) would hand it `**bold**` and
|
|
4223
|
+
* `[link](url)` syntax the pipeline renders literally. Contract verified against ad-server
|
|
4224
|
+
* PR #349: the engine lowercases/trims the value, `html` is the accepted spelling, and an
|
|
4225
|
+
* unrecognized value falls through unconverted rather than erroring.
|
|
4226
|
+
*/
|
|
4227
|
+
format: "html",
|
|
4211
4228
|
limit: String(OFFER_LIMIT),
|
|
4212
4229
|
placement: input.placement,
|
|
4213
4230
|
publisher_key: publisherKey,
|
|
@@ -4255,6 +4272,14 @@ var fetchAdsEngineOffers = async (input, ctx, prefetched) => {
|
|
|
4255
4272
|
return { offers: AdsEngineResponse.parse(upstream).offers, storeName };
|
|
4256
4273
|
};
|
|
4257
4274
|
|
|
4275
|
+
// src/transforms/htmlToPlainText.ts
|
|
4276
|
+
var paragraphText = (paragraph) => paragraph.content.map((run) => run.text).join("");
|
|
4277
|
+
var FORCE_PARSER = { defaultTextStyle: {} };
|
|
4278
|
+
var withoutTagShapes = (text2) => isHTML(text2) ? text2.replace(/[<>]/g, "") : text2;
|
|
4279
|
+
var htmlToPlainText = (html) => withoutTagShapes(
|
|
4280
|
+
normalizeTextValue(html, FORCE_PARSER).content.map((paragraph) => paragraphText(paragraph).trim()).filter((line) => line !== "").join("\n")
|
|
4281
|
+
);
|
|
4282
|
+
|
|
4258
4283
|
// src/server/composeOffer.ts
|
|
4259
4284
|
var ACCEPT_FALLBACK = "Accept";
|
|
4260
4285
|
var DECLINE_FALLBACK = "No Thanks";
|
|
@@ -4264,8 +4289,19 @@ var label = (text2) => ({
|
|
|
4264
4289
|
sectionType: SectionType.text
|
|
4265
4290
|
});
|
|
4266
4291
|
var text = (content) => ({ content: { en: content }, sectionType: SectionType.text });
|
|
4292
|
+
var AS_DOC = { defaultTextStyle: {} };
|
|
4293
|
+
var safeTextValue = (raw, options) => {
|
|
4294
|
+
const converted = normalizeTextValue(raw, options);
|
|
4295
|
+
return checkForHTML(converted) ? converted : normalizeTextValue(htmlToPlainText(raw), options);
|
|
4296
|
+
};
|
|
4297
|
+
var baseText = (raw) => ({ content: { en: safeTextValue(raw, AS_DOC) }, sectionType: SectionType.text });
|
|
4298
|
+
var dspLabel = (raw, fallback) => ({
|
|
4299
|
+
buttonField: ButtonField.buttonLabel,
|
|
4300
|
+
content: { en: htmlToPlainText(raw) === "" ? fallback : safeTextValue(raw, AS_DOC) },
|
|
4301
|
+
sectionType: SectionType.text
|
|
4302
|
+
});
|
|
4267
4303
|
var smallText = (raw) => ({
|
|
4268
|
-
content: { en:
|
|
4304
|
+
content: { en: safeTextValue(raw, { defaultTextStyle: { fontSize: "small" } }) },
|
|
4269
4305
|
sectionType: SectionType.text
|
|
4270
4306
|
});
|
|
4271
4307
|
var styled = (value, { align, bold, color, fontSize, href }) => ({
|
|
@@ -4308,9 +4344,43 @@ var headerSections = (input, storeName) => [
|
|
|
4308
4344
|
},
|
|
4309
4345
|
text(input.orderName ? `Order ${input.orderName} Confirmed` : "Order Confirmed")
|
|
4310
4346
|
];
|
|
4347
|
+
var SHOW_EXTENDED = "showExtendedDisclaimer";
|
|
4348
|
+
var LEGAL_STYLE = { defaultTextStyle: { color: TextColorName.subdued, fontSize: "extraSmall" } };
|
|
4349
|
+
var legalCopy = (raw, visibleWhen) => ({
|
|
4350
|
+
content: { en: safeTextValue(raw, LEGAL_STYLE) },
|
|
4351
|
+
sectionType: SectionType.text,
|
|
4352
|
+
...visibleWhen !== void 0 && { visibility: { variable: SHOW_EXTENDED, visibleWhen } }
|
|
4353
|
+
});
|
|
4354
|
+
var disclosureToggle = (text2, visibleWhen) => ({
|
|
4355
|
+
buttonStyle: ButtonStyle.plain,
|
|
4356
|
+
sections: [label(text2)],
|
|
4357
|
+
sectionType: SectionType.button,
|
|
4358
|
+
variableEffects: [{ effect: "toggle", variable: SHOW_EXTENDED }],
|
|
4359
|
+
visibility: { variable: SHOW_EXTENDED, visibleWhen }
|
|
4360
|
+
});
|
|
4361
|
+
var disclaimerSections = (offer) => {
|
|
4362
|
+
const hasShort = htmlToPlainText(offer.disclaimer) !== "";
|
|
4363
|
+
const hasExtended = htmlToPlainText(offer.extended_disclaimer) !== "";
|
|
4364
|
+
if (!hasExtended) return hasShort ? [legalCopy(offer.disclaimer)] : [];
|
|
4365
|
+
return [
|
|
4366
|
+
{
|
|
4367
|
+
direction: Direction.rows,
|
|
4368
|
+
sections: [
|
|
4369
|
+
...hasShort ? [legalCopy(offer.disclaimer)] : [],
|
|
4370
|
+
legalCopy(offer.extended_disclaimer, true),
|
|
4371
|
+
/** With no short line above it, "See more..." has no referent — the toggle names the disclaimer itself. */
|
|
4372
|
+
disclosureToggle(hasShort ? "See more for details..." : "See disclaimer", false),
|
|
4373
|
+
disclosureToggle(hasShort ? "See less" : "Hide disclaimer", true)
|
|
4374
|
+
],
|
|
4375
|
+
sectionType: SectionType.layout,
|
|
4376
|
+
spacing: Spacing.none,
|
|
4377
|
+
variables: [{ defaultValue: false, name: SHOW_EXTENDED }]
|
|
4378
|
+
}
|
|
4379
|
+
];
|
|
4380
|
+
};
|
|
4311
4381
|
var composeOfferCard = (offer, input, storeName) => {
|
|
4312
4382
|
const copy = {
|
|
4313
|
-
sections: [
|
|
4383
|
+
sections: [baseText(offer.headline), smallText(offer.description)],
|
|
4314
4384
|
sectionType: SectionType.layout,
|
|
4315
4385
|
spacing: Spacing.tight
|
|
4316
4386
|
};
|
|
@@ -4327,8 +4397,8 @@ var composeOfferCard = (offer, input, storeName) => {
|
|
|
4327
4397
|
sections: [
|
|
4328
4398
|
copy,
|
|
4329
4399
|
{
|
|
4330
|
-
/** Image alt must be PLAIN — a DSP headline like `Get <b>50%</b> off` would trip the image NO_HTML refine and bleed fill; the
|
|
4331
|
-
altText: offer.headline.replace(
|
|
4400
|
+
/** Image alt must be PLAIN — a DSP headline like `Get <b>50%</b> off` would trip the image NO_HTML refine and bleed fill; the advertiser's own `image_alt` wins over the headline fallback, both single-lined through {@link htmlToPlainText}. */
|
|
4401
|
+
altText: htmlToPlainText(offer.image_alt).replace(/\n+/g, " ") || htmlToPlainText(offer.headline).replace(/\n+/g, " "),
|
|
4332
4402
|
sectionType: SectionType.image,
|
|
4333
4403
|
source: offer.image_url
|
|
4334
4404
|
}
|
|
@@ -4344,7 +4414,7 @@ var composeOfferCard = (offer, input, storeName) => {
|
|
|
4344
4414
|
action: ButtonAction.acceptOffer,
|
|
4345
4415
|
buttonStyle: ButtonStyle.primary,
|
|
4346
4416
|
sections: [
|
|
4347
|
-
|
|
4417
|
+
dspLabel(offer.cta, ACCEPT_FALLBACK),
|
|
4348
4418
|
{
|
|
4349
4419
|
buttonField: ButtonField.destinationUrl,
|
|
4350
4420
|
content: { en: offer.click_url },
|
|
@@ -4356,13 +4426,14 @@ var composeOfferCard = (offer, input, storeName) => {
|
|
|
4356
4426
|
{
|
|
4357
4427
|
action: ButtonAction.declineOffer,
|
|
4358
4428
|
buttonStyle: ButtonStyle.secondary,
|
|
4359
|
-
sections: [
|
|
4429
|
+
sections: [dspLabel(offer.decline_cta, DECLINE_FALLBACK)],
|
|
4360
4430
|
sectionType: SectionType.button,
|
|
4361
4431
|
trackingUrl: offer.decline_url
|
|
4362
4432
|
}
|
|
4363
4433
|
],
|
|
4364
4434
|
sectionType: SectionType.layout
|
|
4365
4435
|
},
|
|
4436
|
+
...disclaimerSections(offer),
|
|
4366
4437
|
/**
|
|
4367
4438
|
* Ad-unit Privacy Policy footer (compliance). Its own end-aligned layout packs a fit-content
|
|
4368
4439
|
* paragraph right; the paragraph's own `textAlign: end` covers the full-width case — together
|
package/dist/server/index.mjs
CHANGED
|
@@ -4133,14 +4133,22 @@ var transactionItems = (input) => (input.transactionItems?.map(({ id, priceCents
|
|
|
4133
4133
|
type
|
|
4134
4134
|
})) ?? input.cart.items.map(({ productId, quantity }) => ({ id: productId, quantity }))).slice(0, MAX_TRANSACTION_ITEMS);
|
|
4135
4135
|
var AdsEngineOffer = z28.object({
|
|
4136
|
+
/** Pill-style value prop ("Get $45 Cashback"). Carried through the parse; the card does not render it yet (REB-24429 — pill design pending). */
|
|
4137
|
+
callout: z28.string().catch(""),
|
|
4136
4138
|
click_url: z28.string(),
|
|
4137
4139
|
cta: z28.string(),
|
|
4138
4140
|
/** `omitempty` on the ad server — a bid without it must not fail the parse and drop the whole batch (composeOffer falls back to 'No Thanks'). */
|
|
4139
4141
|
decline_cta: z28.string().catch(""),
|
|
4140
4142
|
decline_url: z28.string(),
|
|
4141
4143
|
description: z28.string(),
|
|
4144
|
+
/** Short legal disclosure (FDIC / auto-renewal / FTC-affiliation lines). Rendered subdued under the CTAs. */
|
|
4145
|
+
disclaimer: z28.string().catch(""),
|
|
4146
|
+
/** Long legal text behind the disclosure toggle in the composed card (REB-24429). */
|
|
4147
|
+
extended_disclaimer: z28.string().catch(""),
|
|
4142
4148
|
headline: z28.string(),
|
|
4143
4149
|
id: z28.string(),
|
|
4150
|
+
/** Advertiser-supplied image alt; the composed card falls back to the stripped headline when empty. */
|
|
4151
|
+
image_alt: z28.string().catch(""),
|
|
4144
4152
|
image_url: z28.string().optional(),
|
|
4145
4153
|
view_url: z28.string()
|
|
4146
4154
|
});
|
|
@@ -4157,6 +4165,15 @@ var isTestingRequest = (input, isProduction) => !isProduction || !!input.testing
|
|
|
4157
4165
|
var buildAdsParams = (input, publisherKey, supplyKey, testing, userAgent, ipAddress) => {
|
|
4158
4166
|
const items = transactionItems(input);
|
|
4159
4167
|
return {
|
|
4168
|
+
/**
|
|
4169
|
+
* The engine converts text fields to the requested format before responding (Fluent's source
|
|
4170
|
+
* format is html). `html` makes that conversion a no-op, so the canonical HTML→Tiptap pipeline
|
|
4171
|
+
* receives the DSP's original markup — the default (markdown) would hand it `**bold**` and
|
|
4172
|
+
* `[link](url)` syntax the pipeline renders literally. Contract verified against ad-server
|
|
4173
|
+
* PR #349: the engine lowercases/trims the value, `html` is the accepted spelling, and an
|
|
4174
|
+
* unrecognized value falls through unconverted rather than erroring.
|
|
4175
|
+
*/
|
|
4176
|
+
format: "html",
|
|
4160
4177
|
limit: String(OFFER_LIMIT),
|
|
4161
4178
|
placement: input.placement,
|
|
4162
4179
|
publisher_key: publisherKey,
|
|
@@ -4204,6 +4221,14 @@ var fetchAdsEngineOffers = async (input, ctx, prefetched) => {
|
|
|
4204
4221
|
return { offers: AdsEngineResponse.parse(upstream).offers, storeName };
|
|
4205
4222
|
};
|
|
4206
4223
|
|
|
4224
|
+
// src/transforms/htmlToPlainText.ts
|
|
4225
|
+
var paragraphText = (paragraph) => paragraph.content.map((run) => run.text).join("");
|
|
4226
|
+
var FORCE_PARSER = { defaultTextStyle: {} };
|
|
4227
|
+
var withoutTagShapes = (text2) => isHTML(text2) ? text2.replace(/[<>]/g, "") : text2;
|
|
4228
|
+
var htmlToPlainText = (html) => withoutTagShapes(
|
|
4229
|
+
normalizeTextValue(html, FORCE_PARSER).content.map((paragraph) => paragraphText(paragraph).trim()).filter((line) => line !== "").join("\n")
|
|
4230
|
+
);
|
|
4231
|
+
|
|
4207
4232
|
// src/server/composeOffer.ts
|
|
4208
4233
|
var ACCEPT_FALLBACK = "Accept";
|
|
4209
4234
|
var DECLINE_FALLBACK = "No Thanks";
|
|
@@ -4213,8 +4238,19 @@ var label = (text2) => ({
|
|
|
4213
4238
|
sectionType: SectionType.text
|
|
4214
4239
|
});
|
|
4215
4240
|
var text = (content) => ({ content: { en: content }, sectionType: SectionType.text });
|
|
4241
|
+
var AS_DOC = { defaultTextStyle: {} };
|
|
4242
|
+
var safeTextValue = (raw, options) => {
|
|
4243
|
+
const converted = normalizeTextValue(raw, options);
|
|
4244
|
+
return checkForHTML(converted) ? converted : normalizeTextValue(htmlToPlainText(raw), options);
|
|
4245
|
+
};
|
|
4246
|
+
var baseText = (raw) => ({ content: { en: safeTextValue(raw, AS_DOC) }, sectionType: SectionType.text });
|
|
4247
|
+
var dspLabel = (raw, fallback) => ({
|
|
4248
|
+
buttonField: ButtonField.buttonLabel,
|
|
4249
|
+
content: { en: htmlToPlainText(raw) === "" ? fallback : safeTextValue(raw, AS_DOC) },
|
|
4250
|
+
sectionType: SectionType.text
|
|
4251
|
+
});
|
|
4216
4252
|
var smallText = (raw) => ({
|
|
4217
|
-
content: { en:
|
|
4253
|
+
content: { en: safeTextValue(raw, { defaultTextStyle: { fontSize: "small" } }) },
|
|
4218
4254
|
sectionType: SectionType.text
|
|
4219
4255
|
});
|
|
4220
4256
|
var styled = (value, { align, bold, color, fontSize, href }) => ({
|
|
@@ -4257,9 +4293,43 @@ var headerSections = (input, storeName) => [
|
|
|
4257
4293
|
},
|
|
4258
4294
|
text(input.orderName ? `Order ${input.orderName} Confirmed` : "Order Confirmed")
|
|
4259
4295
|
];
|
|
4296
|
+
var SHOW_EXTENDED = "showExtendedDisclaimer";
|
|
4297
|
+
var LEGAL_STYLE = { defaultTextStyle: { color: TextColorName.subdued, fontSize: "extraSmall" } };
|
|
4298
|
+
var legalCopy = (raw, visibleWhen) => ({
|
|
4299
|
+
content: { en: safeTextValue(raw, LEGAL_STYLE) },
|
|
4300
|
+
sectionType: SectionType.text,
|
|
4301
|
+
...visibleWhen !== void 0 && { visibility: { variable: SHOW_EXTENDED, visibleWhen } }
|
|
4302
|
+
});
|
|
4303
|
+
var disclosureToggle = (text2, visibleWhen) => ({
|
|
4304
|
+
buttonStyle: ButtonStyle.plain,
|
|
4305
|
+
sections: [label(text2)],
|
|
4306
|
+
sectionType: SectionType.button,
|
|
4307
|
+
variableEffects: [{ effect: "toggle", variable: SHOW_EXTENDED }],
|
|
4308
|
+
visibility: { variable: SHOW_EXTENDED, visibleWhen }
|
|
4309
|
+
});
|
|
4310
|
+
var disclaimerSections = (offer) => {
|
|
4311
|
+
const hasShort = htmlToPlainText(offer.disclaimer) !== "";
|
|
4312
|
+
const hasExtended = htmlToPlainText(offer.extended_disclaimer) !== "";
|
|
4313
|
+
if (!hasExtended) return hasShort ? [legalCopy(offer.disclaimer)] : [];
|
|
4314
|
+
return [
|
|
4315
|
+
{
|
|
4316
|
+
direction: Direction.rows,
|
|
4317
|
+
sections: [
|
|
4318
|
+
...hasShort ? [legalCopy(offer.disclaimer)] : [],
|
|
4319
|
+
legalCopy(offer.extended_disclaimer, true),
|
|
4320
|
+
/** With no short line above it, "See more..." has no referent — the toggle names the disclaimer itself. */
|
|
4321
|
+
disclosureToggle(hasShort ? "See more for details..." : "See disclaimer", false),
|
|
4322
|
+
disclosureToggle(hasShort ? "See less" : "Hide disclaimer", true)
|
|
4323
|
+
],
|
|
4324
|
+
sectionType: SectionType.layout,
|
|
4325
|
+
spacing: Spacing.none,
|
|
4326
|
+
variables: [{ defaultValue: false, name: SHOW_EXTENDED }]
|
|
4327
|
+
}
|
|
4328
|
+
];
|
|
4329
|
+
};
|
|
4260
4330
|
var composeOfferCard = (offer, input, storeName) => {
|
|
4261
4331
|
const copy = {
|
|
4262
|
-
sections: [
|
|
4332
|
+
sections: [baseText(offer.headline), smallText(offer.description)],
|
|
4263
4333
|
sectionType: SectionType.layout,
|
|
4264
4334
|
spacing: Spacing.tight
|
|
4265
4335
|
};
|
|
@@ -4276,8 +4346,8 @@ var composeOfferCard = (offer, input, storeName) => {
|
|
|
4276
4346
|
sections: [
|
|
4277
4347
|
copy,
|
|
4278
4348
|
{
|
|
4279
|
-
/** Image alt must be PLAIN — a DSP headline like `Get <b>50%</b> off` would trip the image NO_HTML refine and bleed fill; the
|
|
4280
|
-
altText: offer.headline.replace(
|
|
4349
|
+
/** Image alt must be PLAIN — a DSP headline like `Get <b>50%</b> off` would trip the image NO_HTML refine and bleed fill; the advertiser's own `image_alt` wins over the headline fallback, both single-lined through {@link htmlToPlainText}. */
|
|
4350
|
+
altText: htmlToPlainText(offer.image_alt).replace(/\n+/g, " ") || htmlToPlainText(offer.headline).replace(/\n+/g, " "),
|
|
4281
4351
|
sectionType: SectionType.image,
|
|
4282
4352
|
source: offer.image_url
|
|
4283
4353
|
}
|
|
@@ -4293,7 +4363,7 @@ var composeOfferCard = (offer, input, storeName) => {
|
|
|
4293
4363
|
action: ButtonAction.acceptOffer,
|
|
4294
4364
|
buttonStyle: ButtonStyle.primary,
|
|
4295
4365
|
sections: [
|
|
4296
|
-
|
|
4366
|
+
dspLabel(offer.cta, ACCEPT_FALLBACK),
|
|
4297
4367
|
{
|
|
4298
4368
|
buttonField: ButtonField.destinationUrl,
|
|
4299
4369
|
content: { en: offer.click_url },
|
|
@@ -4305,13 +4375,14 @@ var composeOfferCard = (offer, input, storeName) => {
|
|
|
4305
4375
|
{
|
|
4306
4376
|
action: ButtonAction.declineOffer,
|
|
4307
4377
|
buttonStyle: ButtonStyle.secondary,
|
|
4308
|
-
sections: [
|
|
4378
|
+
sections: [dspLabel(offer.decline_cta, DECLINE_FALLBACK)],
|
|
4309
4379
|
sectionType: SectionType.button,
|
|
4310
4380
|
trackingUrl: offer.decline_url
|
|
4311
4381
|
}
|
|
4312
4382
|
],
|
|
4313
4383
|
sectionType: SectionType.layout
|
|
4314
4384
|
},
|
|
4385
|
+
...disclaimerSections(offer),
|
|
4315
4386
|
/**
|
|
4316
4387
|
* Ad-unit Privacy Policy footer (compliance). Its own end-aligned layout packs a fit-content
|
|
4317
4388
|
* paragraph right; the paragraph's own `textAlign: end` covers the full-width case — together
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* HTML → display plain text for ad copy that must ship as a bare string (image alt, the compose
|
|
3
|
+
* gates on legal copy). Derived FROM the canonical pipeline rather than a parallel strip:
|
|
4
|
+
* {@link normalizeTextValue} converts exactly as the in-card text sections do (entities decoded
|
|
5
|
+
* NO_HTML-safe, comments and script/style bodies dropped, block tags and `<br>`/`<hr>` flushing
|
|
6
|
+
* paragraphs), and the doc's paragraphs flatten to newline-joined lines. By construction this
|
|
7
|
+
* cannot drift from what the card renders, and the single-pass parser has none of the
|
|
8
|
+
* malformed-nesting or adversarial-input hazards a regex strip must defend against.
|
|
9
|
+
*/
|
|
10
|
+
export declare const htmlToPlainText: (html: string) => string;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rebuy/rebuy",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.22.0",
|
|
4
4
|
"description": "Shared zod schemas, legacy-to-CAB widget transforms, and server orchestrators for Rebuy's Shopify consumers (rebuy-api, admin-nextjs, rebuy-shopify-extensions)",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Rebuy, Inc.",
|