@moonbase.sh/storefront-api 2.4.0 → 3.0.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.
- package/README.md +21 -0
- package/dist/index.cjs +460 -24
- package/dist/index.d.cts +9140 -9191
- package/dist/index.d.ts +9140 -9191
- package/dist/index.js +453 -24
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -25,10 +25,12 @@ __export(index_exports, {
|
|
|
25
25
|
ActivationRequestStatus: () => ActivationRequestStatus,
|
|
26
26
|
ActivationStatus: () => ActivationStatus,
|
|
27
27
|
Architecture: () => Architecture,
|
|
28
|
+
CartUtils: () => CartUtils,
|
|
28
29
|
ConnectableAccountProvider: () => ConnectableAccountProvider,
|
|
29
30
|
ConsoleLogger: () => ConsoleLogger,
|
|
30
31
|
CycleLength: () => CycleLength,
|
|
31
32
|
DiscountUtils: () => DiscountUtils,
|
|
33
|
+
ImageUtils: () => ImageUtils,
|
|
32
34
|
InMemoryStore: () => InMemoryStore,
|
|
33
35
|
LicenseStatus: () => LicenseStatus,
|
|
34
36
|
LocalStorageStore: () => LocalStorageStore,
|
|
@@ -46,9 +48,14 @@ __export(index_exports, {
|
|
|
46
48
|
Platform: () => Platform,
|
|
47
49
|
SubscriptionStatus: () => SubscriptionStatus,
|
|
48
50
|
TokenStore: () => TokenStore,
|
|
51
|
+
imageWidths: () => imageWidths,
|
|
52
|
+
isPromotionDisplay: () => isPromotionDisplay,
|
|
53
|
+
isTransformableImageUrl: () => isTransformableImageUrl,
|
|
54
|
+
maxImageDimension: () => maxImageDimension,
|
|
49
55
|
objectToQuery: () => objectToQuery,
|
|
50
56
|
parseOffSiteReferrer: () => parseOffSiteReferrer,
|
|
51
57
|
problemDetailsSchema: () => problemDetailsSchema,
|
|
58
|
+
promotionDisplays: () => promotionDisplays,
|
|
52
59
|
resolveUtm: () => resolveUtm,
|
|
53
60
|
schemas: () => schemas_exports12,
|
|
54
61
|
utmToObject: () => utmToObject
|
|
@@ -69,10 +76,14 @@ var import_zod4 = require("zod");
|
|
|
69
76
|
var schemas_exports2 = {};
|
|
70
77
|
__export(schemas_exports2, {
|
|
71
78
|
offerConditionSchema: () => offerConditionSchema,
|
|
79
|
+
offerScopeSchema: () => offerScopeSchema,
|
|
72
80
|
storefrontBundleSchema: () => storefrontBundleSchema,
|
|
73
81
|
storefrontOfferSchema: () => storefrontOfferSchema,
|
|
74
82
|
storefrontOfferTargetSchema: () => storefrontOfferTargetSchema,
|
|
75
83
|
storefrontProductSchema: () => storefrontProductSchema,
|
|
84
|
+
storefrontPromotionCtaSchema: () => storefrontPromotionCtaSchema,
|
|
85
|
+
storefrontPromotionSchema: () => storefrontPromotionSchema,
|
|
86
|
+
storefrontPromotionTargetSchema: () => storefrontPromotionTargetSchema,
|
|
76
87
|
storefrontSchema: () => storefrontSchema,
|
|
77
88
|
taxEstimateSchema: () => taxEstimateSchema
|
|
78
89
|
});
|
|
@@ -112,6 +123,11 @@ function utmToObject(utm) {
|
|
|
112
123
|
|
|
113
124
|
// src/globalSchemas.ts
|
|
114
125
|
var priceCollectionSchema = import_zod.z.record(import_zod.z.number());
|
|
126
|
+
var discountPromotionFields = {
|
|
127
|
+
promotionId: import_zod.z.string().optional(),
|
|
128
|
+
validFrom: import_zod.z.string().optional(),
|
|
129
|
+
validUntil: import_zod.z.string().optional()
|
|
130
|
+
};
|
|
115
131
|
var percentageOffDiscountSchema = import_zod.z.object({
|
|
116
132
|
type: import_zod.z.literal("PercentageOffDiscount"),
|
|
117
133
|
name: import_zod.z.string(),
|
|
@@ -119,7 +135,8 @@ var percentageOffDiscountSchema = import_zod.z.object({
|
|
|
119
135
|
percentage: import_zod.z.number(),
|
|
120
136
|
total: priceCollectionSchema.optional(),
|
|
121
137
|
isExclusive: import_zod.z.boolean(),
|
|
122
|
-
recurringPaymentUseCount: import_zod.z.number().optional()
|
|
138
|
+
recurringPaymentUseCount: import_zod.z.number().optional(),
|
|
139
|
+
...discountPromotionFields
|
|
123
140
|
});
|
|
124
141
|
var flatAmountOffDiscountSchema = import_zod.z.object({
|
|
125
142
|
type: import_zod.z.literal("FlatAmountOffDiscount"),
|
|
@@ -127,7 +144,8 @@ var flatAmountOffDiscountSchema = import_zod.z.object({
|
|
|
127
144
|
description: import_zod.z.string().optional(),
|
|
128
145
|
total: priceCollectionSchema.optional(),
|
|
129
146
|
isExclusive: import_zod.z.boolean(),
|
|
130
|
-
recurringPaymentUseCount: import_zod.z.number().optional()
|
|
147
|
+
recurringPaymentUseCount: import_zod.z.number().optional(),
|
|
148
|
+
...discountPromotionFields
|
|
131
149
|
});
|
|
132
150
|
var moneySchema = import_zod.z.object({
|
|
133
151
|
currency: import_zod.z.string(),
|
|
@@ -161,6 +179,7 @@ var rawPropertyValueSchema = import_zod.z.lazy(
|
|
|
161
179
|
import_zod.z.string(),
|
|
162
180
|
import_zod.z.number(),
|
|
163
181
|
import_zod.z.boolean(),
|
|
182
|
+
import_zod.z.null(),
|
|
164
183
|
import_zod.z.record(rawPropertyValueSchema),
|
|
165
184
|
import_zod.z.array(rawPropertyValueSchema)
|
|
166
185
|
])
|
|
@@ -231,7 +250,7 @@ var downloadSchema = import_zod2.z.object({
|
|
|
231
250
|
var productSummarySchema = import_zod2.z.object({
|
|
232
251
|
id: import_zod2.z.string(),
|
|
233
252
|
name: import_zod2.z.string(),
|
|
234
|
-
tagline: import_zod2.z.string(),
|
|
253
|
+
tagline: import_zod2.z.string().nullish(),
|
|
235
254
|
description: import_zod2.z.string().nullish(),
|
|
236
255
|
website: import_zod2.z.string().optional(),
|
|
237
256
|
iconUrl: import_zod2.z.string().optional(),
|
|
@@ -254,8 +273,8 @@ var productSummarySchema = import_zod2.z.object({
|
|
|
254
273
|
var storefrontProductSchema = import_zod3.z.object({
|
|
255
274
|
id: import_zod3.z.string(),
|
|
256
275
|
name: import_zod3.z.string(),
|
|
257
|
-
tagline: import_zod3.z.string(),
|
|
258
|
-
description: import_zod3.z.string(),
|
|
276
|
+
tagline: import_zod3.z.string().nullish(),
|
|
277
|
+
description: import_zod3.z.string().nullish(),
|
|
259
278
|
iconUrl: import_zod3.z.string().nullable(),
|
|
260
279
|
website: import_zod3.z.string().nullish(),
|
|
261
280
|
owned: import_zod3.z.boolean(),
|
|
@@ -269,8 +288,8 @@ var storefrontProductSchema = import_zod3.z.object({
|
|
|
269
288
|
var storefrontBundleSchema = import_zod3.z.object({
|
|
270
289
|
id: import_zod3.z.string(),
|
|
271
290
|
name: import_zod3.z.string(),
|
|
272
|
-
tagline: import_zod3.z.string(),
|
|
273
|
-
description: import_zod3.z.string(),
|
|
291
|
+
tagline: import_zod3.z.string().nullish(),
|
|
292
|
+
description: import_zod3.z.string().nullish(),
|
|
274
293
|
iconUrl: import_zod3.z.string().nullable(),
|
|
275
294
|
owned: import_zod3.z.boolean(),
|
|
276
295
|
partial: import_zod3.z.boolean(),
|
|
@@ -287,7 +306,22 @@ var cartContainsItemsConditionSchema = import_zod3.z.object({
|
|
|
287
306
|
minimumItems: import_zod3.z.number(),
|
|
288
307
|
relevantItemVariations: import_zod3.z.record(import_zod3.z.string(), import_zod3.z.string().array())
|
|
289
308
|
});
|
|
290
|
-
var
|
|
309
|
+
var cartTotalConditionSchema = import_zod3.z.object({
|
|
310
|
+
type: import_zod3.z.literal("CartTotal"),
|
|
311
|
+
// Both bounds are optional, but never both absent. They serialize as an explicit `null` when
|
|
312
|
+
// unset, hence `.nullish()` rather than `.optional()`. The API converts them into every enabled
|
|
313
|
+
// currency up front, precisely so we can tell the buyer how much further they have to go without
|
|
314
|
+
// doing any currency conversion ourselves.
|
|
315
|
+
minimumTotal: priceCollectionSchema.nullish(),
|
|
316
|
+
maximumTotal: priceCollectionSchema.nullish()
|
|
317
|
+
});
|
|
318
|
+
var knownOfferConditionSchema = import_zod3.z.discriminatedUnion("type", [
|
|
319
|
+
cartContainsItemsConditionSchema,
|
|
320
|
+
cartTotalConditionSchema
|
|
321
|
+
]);
|
|
322
|
+
var unsupportedOfferConditionSchema = import_zod3.z.object({ type: import_zod3.z.string() }).passthrough().transform(({ type }) => ({ type: "Unsupported", unsupportedType: type }));
|
|
323
|
+
var offerConditionSchema = import_zod3.z.union([knownOfferConditionSchema, unsupportedOfferConditionSchema]);
|
|
324
|
+
var offerScopeSchema = import_zod3.z.enum(["Item", "Cart"]).catch("Item").optional().default("Item");
|
|
291
325
|
var offerTargetItemSchema = import_zod3.z.union([storefrontProductSchema, storefrontBundleSchema]);
|
|
292
326
|
var storefrontOfferTargetSchema = import_zod3.z.object({
|
|
293
327
|
// The product or bundle the offer applies to.
|
|
@@ -303,8 +337,10 @@ function legacyOfferTargets(target, targetVariations) {
|
|
|
303
337
|
}
|
|
304
338
|
var storefrontOfferSchema = import_zod3.z.object({
|
|
305
339
|
id: import_zod3.z.string(),
|
|
340
|
+
scope: offerScopeSchema,
|
|
306
341
|
// Each target pairs a product/bundle with the pricing variations the offer applies to (an empty
|
|
307
|
-
// list means all variations). This is the model to consume.
|
|
342
|
+
// list means all variations). This is the model to consume. A cart offer may have no targets at
|
|
343
|
+
// all, which means it discounts every line item.
|
|
308
344
|
targets: import_zod3.z.array(storefrontOfferTargetSchema).optional(),
|
|
309
345
|
// Legacy fields, kept optional only so old cached storefront responses still parse. The API now
|
|
310
346
|
// exposes the unified `targets` model; we derive `targets` from these when a response predates it.
|
|
@@ -318,6 +354,57 @@ var storefrontOfferSchema = import_zod3.z.object({
|
|
|
318
354
|
...rest,
|
|
319
355
|
targets: targets != null ? targets : legacyOfferTargets(target, targetVariations)
|
|
320
356
|
}));
|
|
357
|
+
var storefrontPromotionTargetSchema = import_zod3.z.object({
|
|
358
|
+
// A prefixed reference to the item the sale covers, `Product/<id>` or `Bundle/<id>`. The item
|
|
359
|
+
// itself is already serialized in `products`/`bundles`; embedding it again would duplicate the
|
|
360
|
+
// bulk of the payload for a sale spanning dozens of products.
|
|
361
|
+
referenceId: import_zod3.z.string(),
|
|
362
|
+
// The pricing variation IDs the sale is restricted to; an empty list means all variations.
|
|
363
|
+
variations: import_zod3.z.string().array()
|
|
364
|
+
});
|
|
365
|
+
var storefrontPromotionCtaSchema = import_zod3.z.object({
|
|
366
|
+
url: import_zod3.z.string(),
|
|
367
|
+
label: import_zod3.z.string()
|
|
368
|
+
});
|
|
369
|
+
var storefrontPromotionSchema = import_zod3.z.object({
|
|
370
|
+
id: import_zod3.z.string(),
|
|
371
|
+
name: import_zod3.z.string(),
|
|
372
|
+
description: import_zod3.z.string().nullish(),
|
|
373
|
+
imageUrl: import_zod3.z.string().nullish(),
|
|
374
|
+
// Absent when the merchant has not set one, in which case the storefront decides whether the
|
|
375
|
+
// sale leads anywhere.
|
|
376
|
+
cta: storefrontPromotionCtaSchema.nullish(),
|
|
377
|
+
// How the merchant wants the sale surfaced, e.g. `['Banner', 'Popup']`; an empty list means no
|
|
378
|
+
// preference. Kept as free-form strings and narrowed with `isPromotionDisplay`, so a display
|
|
379
|
+
// mode added server-side later is ignored rather than failing the whole storefront.
|
|
380
|
+
display: import_zod3.z.string().array(),
|
|
381
|
+
// The promotion's *intended* scope, not what is actually showing the sale price: each variation
|
|
382
|
+
// displays only its single best discount, so a targeted item may be showing a better one. The
|
|
383
|
+
// authoritative signal is `promotionId` on a variation's own discount.
|
|
384
|
+
targets: storefrontPromotionTargetSchema.array(),
|
|
385
|
+
// The promotion's own discount, for banner copy ("20% off"). Per-item prices are already
|
|
386
|
+
// discounted on each variation.
|
|
387
|
+
discount: discountSchema,
|
|
388
|
+
validFrom: import_zod3.z.string().nullish(),
|
|
389
|
+
validUntil: import_zod3.z.string().nullish(),
|
|
390
|
+
properties: import_zod3.z.record(rawPropertyValueSchema).nullish()
|
|
391
|
+
});
|
|
392
|
+
var storefrontOffersSchema = import_zod3.z.array(import_zod3.z.unknown()).transform((offers) => offers.flatMap((offer) => {
|
|
393
|
+
const parsed = storefrontOfferSchema.safeParse(offer);
|
|
394
|
+
if (!parsed.success) {
|
|
395
|
+
console.warn("Ignoring storefront offer that could not be parsed:", parsed.error.issues);
|
|
396
|
+
return [];
|
|
397
|
+
}
|
|
398
|
+
return [parsed.data];
|
|
399
|
+
}));
|
|
400
|
+
var storefrontPromotionsSchema = import_zod3.z.array(import_zod3.z.unknown()).transform((promotions) => promotions.flatMap((promotion) => {
|
|
401
|
+
const parsed = storefrontPromotionSchema.safeParse(promotion);
|
|
402
|
+
if (!parsed.success) {
|
|
403
|
+
console.warn("Ignoring storefront promotion that could not be parsed:", parsed.error.issues);
|
|
404
|
+
return [];
|
|
405
|
+
}
|
|
406
|
+
return [parsed.data];
|
|
407
|
+
}));
|
|
321
408
|
var taxEstimateSchema = import_zod3.z.object({
|
|
322
409
|
rate: import_zod3.z.number(),
|
|
323
410
|
mode: import_zod3.z.enum(["Exclusive", "Inclusive"]),
|
|
@@ -331,7 +418,9 @@ var storefrontSchema = import_zod3.z.object({
|
|
|
331
418
|
products: storefrontProductSchema.array(),
|
|
332
419
|
bundles: storefrontBundleSchema.array(),
|
|
333
420
|
// Offers need to be optional since we may still have old, cached representations in browsers
|
|
334
|
-
offers:
|
|
421
|
+
offers: storefrontOffersSchema.optional(),
|
|
422
|
+
// Promotions need to be optional since we may still have old, cached representations in browsers
|
|
423
|
+
promotions: storefrontPromotionsSchema.optional(),
|
|
335
424
|
// Depending on circumstances, we may have a tax estimate available
|
|
336
425
|
estimatedTax: taxEstimateSchema.optional()
|
|
337
426
|
});
|
|
@@ -1077,6 +1166,7 @@ var import_zod13 = require("zod");
|
|
|
1077
1166
|
var schemas_exports7 = {};
|
|
1078
1167
|
__export(schemas_exports7, {
|
|
1079
1168
|
completedOrderSchema: () => completedOrderSchema,
|
|
1169
|
+
lineItemTotalSchema: () => lineItemTotalSchema,
|
|
1080
1170
|
openBundleLineItem: () => openBundleLineItem,
|
|
1081
1171
|
openOrderLineItem: () => openOrderLineItem,
|
|
1082
1172
|
openOrderSchema: () => openOrderSchema,
|
|
@@ -1105,14 +1195,17 @@ var couponSchema = import_zod12.z.object({
|
|
|
1105
1195
|
var lineItemProductSchema = import_zod12.z.object({
|
|
1106
1196
|
id: import_zod12.z.string(),
|
|
1107
1197
|
name: import_zod12.z.string(),
|
|
1108
|
-
tagline: import_zod12.z.string(),
|
|
1198
|
+
tagline: import_zod12.z.string().nullish(),
|
|
1109
1199
|
iconUrl: import_zod12.z.string().nullable()
|
|
1110
1200
|
});
|
|
1111
1201
|
var lineItemTotalSchema = import_zod12.z.object({
|
|
1112
1202
|
original: moneySchema,
|
|
1113
1203
|
discount: moneySchema,
|
|
1114
1204
|
subtotal: moneySchema,
|
|
1115
|
-
due: moneySchema
|
|
1205
|
+
due: moneySchema,
|
|
1206
|
+
// This line's per-unit share of the order's cart offer, kept apart from the offer the buyer picked
|
|
1207
|
+
// for this item. Both are part of `discount`. Absent on orders priced before cart offers existed.
|
|
1208
|
+
cartOfferDiscount: moneySchema.optional()
|
|
1116
1209
|
});
|
|
1117
1210
|
var openProductLineItem = import_zod12.z.object({
|
|
1118
1211
|
id: import_zod12.z.string(),
|
|
@@ -1132,7 +1225,7 @@ var openProductLineItem = import_zod12.z.object({
|
|
|
1132
1225
|
var lineItemBundleSchema = import_zod12.z.object({
|
|
1133
1226
|
id: import_zod12.z.string(),
|
|
1134
1227
|
name: import_zod12.z.string(),
|
|
1135
|
-
tagline: import_zod12.z.string(),
|
|
1228
|
+
tagline: import_zod12.z.string().nullish(),
|
|
1136
1229
|
iconUrl: import_zod12.z.string().nullable(),
|
|
1137
1230
|
products: lineItemProductSchema.and(import_zod12.z.object({
|
|
1138
1231
|
included: import_zod12.z.boolean().optional()
|
|
@@ -1164,7 +1257,10 @@ var orderTotalSchema = import_zod12.z.object({
|
|
|
1164
1257
|
discount: moneySchema,
|
|
1165
1258
|
subtotal: moneySchema,
|
|
1166
1259
|
taxes: moneySchema,
|
|
1167
|
-
due: moneySchema
|
|
1260
|
+
due: moneySchema,
|
|
1261
|
+
// How much of `discount` came from the cart offer, so it can be shown as one order-wide saving
|
|
1262
|
+
// rather than a share on every line. Absent when no cart offer applied.
|
|
1263
|
+
cartOfferDiscount: moneySchema.optional()
|
|
1168
1264
|
});
|
|
1169
1265
|
var openOrderSchema = import_zod12.z.object({
|
|
1170
1266
|
id: import_zod12.z.string(),
|
|
@@ -1172,6 +1268,12 @@ var openOrderSchema = import_zod12.z.object({
|
|
|
1172
1268
|
total: orderTotalSchema.nullish(),
|
|
1173
1269
|
items: openOrderLineItem.array(),
|
|
1174
1270
|
couponsApplied: couponSchema.array(),
|
|
1271
|
+
// The cart offer on the order as a whole: the buyer's intent. It is part of the cart payload, so
|
|
1272
|
+
// a content push that leaves it out clears it. The API echoes it back even while the cart sits
|
|
1273
|
+
// below the offer's threshold.
|
|
1274
|
+
offerId: import_zod12.z.string().optional(),
|
|
1275
|
+
// The cart offer as actually applied: the result. Absent while the offer's condition doesn't hold.
|
|
1276
|
+
appliedOffer: discountSchema.optional(),
|
|
1175
1277
|
checkoutUrl: import_zod12.z.string().optional(),
|
|
1176
1278
|
hostedCheckoutUrl: import_zod12.z.string().optional(),
|
|
1177
1279
|
embeddedCheckoutUrl: import_zod12.z.string().optional()
|
|
@@ -1191,7 +1293,8 @@ var completedOrderSchema = import_zod12.z.object({
|
|
|
1191
1293
|
customer: customerSnapshotSchema,
|
|
1192
1294
|
total: orderTotalSchema,
|
|
1193
1295
|
items: openOrderLineItem.array(),
|
|
1194
|
-
couponsApplied: couponSchema.array()
|
|
1296
|
+
couponsApplied: couponSchema.array(),
|
|
1297
|
+
appliedOffer: discountSchema.optional()
|
|
1195
1298
|
});
|
|
1196
1299
|
var orderSchema = import_zod12.z.discriminatedUnion("status", [
|
|
1197
1300
|
openOrderSchema.extend({
|
|
@@ -1349,6 +1452,7 @@ var OrderEndpoints = class {
|
|
|
1349
1452
|
method: "PATCH",
|
|
1350
1453
|
body: {
|
|
1351
1454
|
currency: order.currency || void 0,
|
|
1455
|
+
offerId: order.offerId,
|
|
1352
1456
|
items: order.items
|
|
1353
1457
|
}
|
|
1354
1458
|
}
|
|
@@ -1363,6 +1467,12 @@ var OrderEndpoints = class {
|
|
|
1363
1467
|
const response = await this.api.fetch(`/api/customer/orders/${orderId}/billing-details`, openOrderSchema, { method: "DELETE" });
|
|
1364
1468
|
return response.data;
|
|
1365
1469
|
}
|
|
1470
|
+
// Takes the cart offer off the order without re-sending the cart. Offers picked for a single item
|
|
1471
|
+
// come off by pushing that item without its `offerId` instead.
|
|
1472
|
+
async removeOffer(orderId) {
|
|
1473
|
+
const response = await this.api.fetch(`/api/customer/orders/${orderId}/offer`, openOrderSchema, { method: "DELETE" });
|
|
1474
|
+
return response.data;
|
|
1475
|
+
}
|
|
1366
1476
|
async addCouponCode(orderId, code) {
|
|
1367
1477
|
const response = await this.api.fetch(`/api/customer/orders/${orderId}/coupon?code=${encodeURIComponent(code)}`, openOrderSchema, { method: "PATCH" });
|
|
1368
1478
|
return response.data;
|
|
@@ -1667,6 +1777,12 @@ __export(schemas_exports11, {
|
|
|
1667
1777
|
subscriptions: () => schemas_exports8
|
|
1668
1778
|
});
|
|
1669
1779
|
|
|
1780
|
+
// src/storefront/models.ts
|
|
1781
|
+
var promotionDisplays = ["Banner", "Popup"];
|
|
1782
|
+
function isPromotionDisplay(value) {
|
|
1783
|
+
return promotionDisplays.includes(value);
|
|
1784
|
+
}
|
|
1785
|
+
|
|
1670
1786
|
// src/utils/discount.ts
|
|
1671
1787
|
var DiscountUtils = class {
|
|
1672
1788
|
static apply(discount, price) {
|
|
@@ -1693,25 +1809,68 @@ var MoneyCollectionUtils = class {
|
|
|
1693
1809
|
};
|
|
1694
1810
|
|
|
1695
1811
|
// src/utils/offer.ts
|
|
1812
|
+
function evaluated(unit, currency, current, target, eligible) {
|
|
1813
|
+
return {
|
|
1814
|
+
eligible,
|
|
1815
|
+
progress: {
|
|
1816
|
+
unit,
|
|
1817
|
+
currency,
|
|
1818
|
+
current,
|
|
1819
|
+
target,
|
|
1820
|
+
remaining: Math.max(0, target - current),
|
|
1821
|
+
fraction: target <= 0 ? 1 : Math.min(1, Math.max(0, current / target)),
|
|
1822
|
+
eligible
|
|
1823
|
+
}
|
|
1824
|
+
};
|
|
1825
|
+
}
|
|
1696
1826
|
var OfferUtils = class _OfferUtils {
|
|
1697
|
-
|
|
1698
|
-
|
|
1827
|
+
// Callers that only have an order keep working and simply have no cart total, so a monetary
|
|
1828
|
+
// condition fails closed for them. Pass the full context to have `CartTotal` actually evaluate.
|
|
1829
|
+
static eligible(offer, context) {
|
|
1830
|
+
return _OfferUtils.evaluate(offer, context).eligible;
|
|
1831
|
+
}
|
|
1832
|
+
// How close the cart is to satisfying the offer, or `null` when there is nothing to show progress
|
|
1833
|
+
// toward: an unsupported condition, an unknown cart total, or a condition with only an upper
|
|
1834
|
+
// bound (which the buyer starts out satisfying rather than working toward).
|
|
1835
|
+
static progress(offer, context) {
|
|
1836
|
+
return _OfferUtils.evaluate(offer, context).progress;
|
|
1837
|
+
}
|
|
1838
|
+
// The single evaluator behind both faces, so the "spend €12 more" nudge and the gate that unlocks
|
|
1839
|
+
// the discount can never disagree about the same cart.
|
|
1840
|
+
static evaluate(offer, context) {
|
|
1841
|
+
var _a, _b;
|
|
1842
|
+
const { order, cartTotal } = "items" in context ? { order: context, cartTotal: void 0 } : context;
|
|
1843
|
+
const condition = offer.condition;
|
|
1844
|
+
switch (condition.type) {
|
|
1699
1845
|
case "CartContainsItems": {
|
|
1700
1846
|
const relevantItems = order.items.filter(
|
|
1701
|
-
(i) => i.type === "Product" &&
|
|
1847
|
+
(i) => i.type === "Product" && condition.relevantItemVariations[`Product/${i.productId}`] && condition.relevantItemVariations[`Product/${i.productId}`].includes(i.variationId) || i.type === "Bundle" && condition.relevantItemVariations[`Bundle/${i.bundleId}`] && condition.relevantItemVariations[`Bundle/${i.bundleId}`].includes(i.variationId)
|
|
1702
1848
|
);
|
|
1703
1849
|
const relevantReplacedItems = order.items.flatMap((i) => {
|
|
1704
|
-
var
|
|
1705
|
-
return offer.id === i.offerId && i.type === "Bundle" && _OfferUtils.targetContainsBundle(offer, i.id) ? (
|
|
1850
|
+
var _a2;
|
|
1851
|
+
return offer.id === i.offerId && i.type === "Bundle" && _OfferUtils.targetContainsBundle(offer, i.id) ? (_a2 = i.replaced) != null ? _a2 : [] : [];
|
|
1706
1852
|
}).filter((ref) => {
|
|
1707
1853
|
const [productId, variationId] = ref.split("/");
|
|
1708
|
-
return
|
|
1854
|
+
return condition.relevantItemVariations[`Product/${productId}`] && condition.relevantItemVariations[`Product/${productId}`].includes(variationId);
|
|
1709
1855
|
});
|
|
1710
|
-
|
|
1856
|
+
const current = relevantItems.length + relevantReplacedItems.length;
|
|
1857
|
+
return evaluated("Items", void 0, current, condition.minimumItems, current >= condition.minimumItems);
|
|
1858
|
+
}
|
|
1859
|
+
case "CartTotal": {
|
|
1860
|
+
if (!cartTotal)
|
|
1861
|
+
return { eligible: false, progress: null };
|
|
1862
|
+
const minimum = (_a = condition.minimumTotal) == null ? void 0 : _a[cartTotal.currency];
|
|
1863
|
+
const maximum = (_b = condition.maximumTotal) == null ? void 0 : _b[cartTotal.currency];
|
|
1864
|
+
if (minimum === void 0 && maximum === void 0)
|
|
1865
|
+
return { eligible: false, progress: null };
|
|
1866
|
+
const eligible = (minimum === void 0 || cartTotal.amount >= minimum) && (maximum === void 0 || cartTotal.amount <= maximum);
|
|
1867
|
+
if (minimum === void 0)
|
|
1868
|
+
return { eligible, progress: null };
|
|
1869
|
+
return evaluated("Money", cartTotal.currency, cartTotal.amount, minimum, eligible);
|
|
1711
1870
|
}
|
|
1712
1871
|
}
|
|
1713
|
-
console.warn("Unsupported offer condition found:",
|
|
1714
|
-
return false;
|
|
1872
|
+
console.warn("Unsupported offer condition found:", condition);
|
|
1873
|
+
return { eligible: false, progress: null };
|
|
1715
1874
|
}
|
|
1716
1875
|
// The relevant targets of an offer with more than a single target needs to:
|
|
1717
1876
|
// a. Not be owned, or all targets need to be owned
|
|
@@ -1752,6 +1911,276 @@ var OfferUtils = class _OfferUtils {
|
|
|
1752
1911
|
}
|
|
1753
1912
|
};
|
|
1754
1913
|
|
|
1914
|
+
// src/utils/cart.ts
|
|
1915
|
+
var CartUtils = class _CartUtils {
|
|
1916
|
+
// The pricing variation a line item points at, falling back to the item's default variation when
|
|
1917
|
+
// the variation id doesn't resolve, which is what the cart has always displayed.
|
|
1918
|
+
static resolveVariation(catalog, item) {
|
|
1919
|
+
var _a, _b;
|
|
1920
|
+
const source = item.type === "Product" ? catalog.products.find((p) => p.id === item.productId) : catalog.bundles.find((b) => b.id === item.bundleId);
|
|
1921
|
+
return (_b = (_a = source == null ? void 0 : source.variations) == null ? void 0 : _a.find((v) => v.id === item.variationId)) != null ? _b : source == null ? void 0 : source.defaultVariation;
|
|
1922
|
+
}
|
|
1923
|
+
// A line's unit price after its product discount and any pricing tier, and *before* any offer.
|
|
1924
|
+
// This is the basis the API measures cart total conditions on, which is why it reads the catalog
|
|
1925
|
+
// rather than the line item's own `price`: `enrichLineItems` rewrites that to the offer-discounted
|
|
1926
|
+
// price, and reading it here would take the offer off twice.
|
|
1927
|
+
static unitPrice(catalog, item, currency) {
|
|
1928
|
+
var _a;
|
|
1929
|
+
return (_a = _CartUtils.unitPricing(catalog, item, currency)) == null ? void 0 : _a.price;
|
|
1930
|
+
}
|
|
1931
|
+
// A line's list and post-product-discount unit prices, from whichever pricing tier applies. Both
|
|
1932
|
+
// come from the same tier so they can be subtracted from one another: taking the list price off one
|
|
1933
|
+
// tier and the paid price off another would invent a discount neither of them offers.
|
|
1934
|
+
static unitPricing(catalog, item, currency) {
|
|
1935
|
+
var _a, _b, _c, _d, _e, _f;
|
|
1936
|
+
const variation = _CartUtils.resolveVariation(catalog, item);
|
|
1937
|
+
const price = variation == null ? void 0 : variation.price[currency];
|
|
1938
|
+
if (price === void 0 || variation === void 0)
|
|
1939
|
+
return void 0;
|
|
1940
|
+
const eligiblePricingTiers = (_b = (_a = variation.pricingTiers) == null ? void 0 : _a.filter((tier) => item.quantity >= tier.minQuantity)) != null ? _b : [];
|
|
1941
|
+
const matchingPricingTier = eligiblePricingTiers.length > 0 ? eligiblePricingTiers[eligiblePricingTiers.length - 1] : null;
|
|
1942
|
+
const tierPrice = matchingPricingTier == null ? void 0 : matchingPricingTier.price[currency];
|
|
1943
|
+
if (tierPrice !== void 0 && tierPrice < price) {
|
|
1944
|
+
return {
|
|
1945
|
+
price: tierPrice,
|
|
1946
|
+
originalPrice: (_d = (_c = matchingPricingTier == null ? void 0 : matchingPricingTier.originalPrice) == null ? void 0 : _c[currency]) != null ? _d : tierPrice
|
|
1947
|
+
};
|
|
1948
|
+
}
|
|
1949
|
+
return { price, originalPrice: (_f = (_e = variation.originalPrice) == null ? void 0 : _e[currency]) != null ? _f : price };
|
|
1950
|
+
}
|
|
1951
|
+
// The number the API measures cart conditions against: each line's price after its product
|
|
1952
|
+
// discount, times quantity, before offers and coupons. `undefined` when any line's price can't be
|
|
1953
|
+
// resolved in `currency`. One unreadable line makes the whole measurement a guess, and a guess
|
|
1954
|
+
// must not unlock an offer.
|
|
1955
|
+
static measure(catalog, items, currency) {
|
|
1956
|
+
if (!currency)
|
|
1957
|
+
return void 0;
|
|
1958
|
+
let amount = 0;
|
|
1959
|
+
for (const item of items) {
|
|
1960
|
+
const unitPrice = _CartUtils.unitPrice(catalog, item, currency);
|
|
1961
|
+
if (unitPrice === void 0)
|
|
1962
|
+
return void 0;
|
|
1963
|
+
amount += unitPrice * item.quantity;
|
|
1964
|
+
}
|
|
1965
|
+
return { currency, amount };
|
|
1966
|
+
}
|
|
1967
|
+
static conditionContext(catalog, order, currency) {
|
|
1968
|
+
return { order, cartTotal: _CartUtils.measure(catalog, order.items, currency) };
|
|
1969
|
+
}
|
|
1970
|
+
// Which lines a cart offer discounts. No targets means the whole cart, a reading that only holds
|
|
1971
|
+
// for cart-scope offers, where an empty target list is the API's way of saying "every line".
|
|
1972
|
+
static cartOfferAppliesToLine(offer, item) {
|
|
1973
|
+
if (offer.targets.length === 0)
|
|
1974
|
+
return true;
|
|
1975
|
+
return offer.targets.some(({ item: target, variations }) => target.type === item.type && target.id === (item.type === "Product" ? item.productId : item.bundleId) && (variations.length === 0 || variations.includes(item.variationId)));
|
|
1976
|
+
}
|
|
1977
|
+
// The cart offer actually discounting this cart, as opposed to the one attached to the order. An
|
|
1978
|
+
// offer whose targets name nothing in the cart discounts nothing however healthy the total looks,
|
|
1979
|
+
// so it has not really applied.
|
|
1980
|
+
static appliedCartOffer(catalog, order, context) {
|
|
1981
|
+
var _a;
|
|
1982
|
+
if (!order.offerId)
|
|
1983
|
+
return void 0;
|
|
1984
|
+
const offer = (_a = catalog.offers) == null ? void 0 : _a.find((o) => o.id === order.offerId);
|
|
1985
|
+
if (!offer || offer.scope !== "Cart" || !OfferUtils.eligible(offer, context))
|
|
1986
|
+
return void 0;
|
|
1987
|
+
return order.items.some((item) => _CartUtils.cartOfferAppliesToLine(offer, item)) ? offer : void 0;
|
|
1988
|
+
}
|
|
1989
|
+
// What the cart is worth to the buyer, broken down so the cart can show the saving on its own row.
|
|
1990
|
+
// Unlike `measure`, an unresolvable price counts as zero here: this number fills a label, so a
|
|
1991
|
+
// half-loaded catalog should still render a total rather than nothing.
|
|
1992
|
+
static total(catalog, order, currency) {
|
|
1993
|
+
var _a, _b;
|
|
1994
|
+
const context = _CartUtils.conditionContext(catalog, order, currency);
|
|
1995
|
+
const cartOffer = _CartUtils.appliedCartOffer(catalog, order, context);
|
|
1996
|
+
let original = 0;
|
|
1997
|
+
let subtotal = 0;
|
|
1998
|
+
let cartOfferDiscount = 0;
|
|
1999
|
+
for (const item of order.items) {
|
|
2000
|
+
const unitPrice = _CartUtils.unitPriceAfterItemOffer(catalog, item, currency, context);
|
|
2001
|
+
original += ((_b = (_a = _CartUtils.unitPricing(catalog, item, currency)) == null ? void 0 : _a.originalPrice) != null ? _b : unitPrice) * item.quantity;
|
|
2002
|
+
subtotal += unitPrice * item.quantity;
|
|
2003
|
+
if (cartOffer && _CartUtils.cartOfferAppliesToLine(cartOffer, item))
|
|
2004
|
+
cartOfferDiscount += DiscountUtils.apply(cartOffer.discount, { [currency]: unitPrice })[currency] * item.quantity;
|
|
2005
|
+
}
|
|
2006
|
+
const amount = subtotal - cartOfferDiscount;
|
|
2007
|
+
return {
|
|
2008
|
+
currency,
|
|
2009
|
+
original,
|
|
2010
|
+
subtotal,
|
|
2011
|
+
cartOffer: cartOffer ? { offer: cartOffer, discount: cartOfferDiscount } : null,
|
|
2012
|
+
discount: original - amount,
|
|
2013
|
+
amount
|
|
2014
|
+
};
|
|
2015
|
+
}
|
|
2016
|
+
// The cart offer worth attaching to the order. Cart offers are always percentages, but 10% off
|
|
2017
|
+
// everything can still beat 50% off one cheap line, so they are ranked by what they would actually
|
|
2018
|
+
// take off this cart rather than by percentage.
|
|
2019
|
+
static bestCartOffer(catalog, order, currency) {
|
|
2020
|
+
var _a;
|
|
2021
|
+
const context = _CartUtils.conditionContext(catalog, order, currency);
|
|
2022
|
+
let best;
|
|
2023
|
+
for (const offer of (_a = catalog.offers) != null ? _a : []) {
|
|
2024
|
+
if (offer.scope !== "Cart" || !OfferUtils.eligible(offer, context))
|
|
2025
|
+
continue;
|
|
2026
|
+
const value = _CartUtils.cartOfferValue(catalog, order, offer, currency, context);
|
|
2027
|
+
if (value <= 0)
|
|
2028
|
+
continue;
|
|
2029
|
+
if (!best || value > best.value || value === best.value && offer.id === order.offerId)
|
|
2030
|
+
best = { offer, value };
|
|
2031
|
+
}
|
|
2032
|
+
return best == null ? void 0 : best.offer;
|
|
2033
|
+
}
|
|
2034
|
+
/**
|
|
2035
|
+
* What a cart offer would take off this cart, whether or not its condition is met yet.
|
|
2036
|
+
*
|
|
2037
|
+
* Computed on the same post-line-offer price `total` discounts, so "which offer do we attach" and
|
|
2038
|
+
* "what does the cart actually save" can never disagree. Ranking on the list price instead would
|
|
2039
|
+
* over-value an offer aimed at a line that already carries a steep offer of its own: 50% of a
|
|
2040
|
+
* line discounted to a tenth of its price is worth less than 10% of a full-price one.
|
|
2041
|
+
*/
|
|
2042
|
+
static cartOfferValue(catalog, order, offer, currency, context) {
|
|
2043
|
+
const conditionContext = context != null ? context : _CartUtils.conditionContext(catalog, order, currency);
|
|
2044
|
+
return order.items.reduce((aggregate, item) => {
|
|
2045
|
+
if (!_CartUtils.cartOfferAppliesToLine(offer, item))
|
|
2046
|
+
return aggregate;
|
|
2047
|
+
const unitPrice = _CartUtils.unitPriceAfterItemOffer(catalog, item, currency, conditionContext);
|
|
2048
|
+
return aggregate + DiscountUtils.apply(offer.discount, { [currency]: unitPrice })[currency] * item.quantity;
|
|
2049
|
+
}, 0);
|
|
2050
|
+
}
|
|
2051
|
+
// A line's unit price once its own offer has come off, which is what a cart offer is applied to:
|
|
2052
|
+
// the API discounts in the order product discount -> line offer -> cart offer -> coupons. An
|
|
2053
|
+
// unresolvable price counts as zero, matching `total`'s "still render something" stance rather
|
|
2054
|
+
// than `measure`'s "refuse to guess".
|
|
2055
|
+
static unitPriceAfterItemOffer(catalog, item, currency, context) {
|
|
2056
|
+
var _a, _b;
|
|
2057
|
+
const unitPrice = (_a = _CartUtils.unitPrice(catalog, item, currency)) != null ? _a : 0;
|
|
2058
|
+
const itemOffer = item.offerId ? (_b = catalog.offers) == null ? void 0 : _b.find((o) => o.id === item.offerId) : void 0;
|
|
2059
|
+
if (!itemOffer || !OfferUtils.eligible(itemOffer, context))
|
|
2060
|
+
return unitPrice;
|
|
2061
|
+
return unitPrice - DiscountUtils.apply(itemOffer.discount, { [currency]: unitPrice })[currency];
|
|
2062
|
+
}
|
|
2063
|
+
};
|
|
2064
|
+
|
|
2065
|
+
// src/utils/image.ts
|
|
2066
|
+
var imageWidths = [16, 32, 48, 64, 96, 128, 192, 256, 384, 512, 768, 1024, 1536, 2048];
|
|
2067
|
+
var maxImageDimension = imageWidths[imageWidths.length - 1];
|
|
2068
|
+
var transformPathSegment = "_i";
|
|
2069
|
+
var maxCoverAspectRatio = 4;
|
|
2070
|
+
function isAssetUrl(url) {
|
|
2071
|
+
let parsed;
|
|
2072
|
+
try {
|
|
2073
|
+
parsed = new URL(url);
|
|
2074
|
+
} catch (e) {
|
|
2075
|
+
return false;
|
|
2076
|
+
}
|
|
2077
|
+
return parsed.hostname === "assets.moonbase.sh" || parsed.hostname.startsWith("assets.") && parsed.hostname.endsWith(".moonbase.sh");
|
|
2078
|
+
}
|
|
2079
|
+
function isRendition(url) {
|
|
2080
|
+
try {
|
|
2081
|
+
return new URL(url).pathname.startsWith(`/${transformPathSegment}/`);
|
|
2082
|
+
} catch (e) {
|
|
2083
|
+
return false;
|
|
2084
|
+
}
|
|
2085
|
+
}
|
|
2086
|
+
function isTransformableImageUrl(url) {
|
|
2087
|
+
return isAssetUrl(url) && !isRendition(url);
|
|
2088
|
+
}
|
|
2089
|
+
var warned = /* @__PURE__ */ new Set();
|
|
2090
|
+
function warnOnce(reason, url, message) {
|
|
2091
|
+
const key = `${reason}:${url}`;
|
|
2092
|
+
if (warned.has(key))
|
|
2093
|
+
return;
|
|
2094
|
+
warned.add(key);
|
|
2095
|
+
console.warn(message, url);
|
|
2096
|
+
}
|
|
2097
|
+
function snap(value) {
|
|
2098
|
+
var _a;
|
|
2099
|
+
if (value === void 0 || !Number.isFinite(value) || value <= 0)
|
|
2100
|
+
return void 0;
|
|
2101
|
+
return (_a = imageWidths.find((width) => value <= width)) != null ? _a : maxImageDimension;
|
|
2102
|
+
}
|
|
2103
|
+
var ImageUtils = class _ImageUtils {
|
|
2104
|
+
/**
|
|
2105
|
+
* Rewrites an asset URL to ask the CDN for a rendition at the given size.
|
|
2106
|
+
*
|
|
2107
|
+
* Returns the URL untouched — never a broken one — when there is nothing to do: a URL that
|
|
2108
|
+
* isn't a Moonbase asset, one that is already a rendition, an empty transform, or a `cover`
|
|
2109
|
+
* crop too extreme for the CDN to serve. Callers can apply it unconditionally.
|
|
2110
|
+
*/
|
|
2111
|
+
static transform(url, transform) {
|
|
2112
|
+
if (!url)
|
|
2113
|
+
return void 0;
|
|
2114
|
+
if (!isAssetUrl(url)) {
|
|
2115
|
+
warnOnce(
|
|
2116
|
+
"host",
|
|
2117
|
+
url,
|
|
2118
|
+
"Only images hosted by Moonbase can be resized on the fly. Serving this one at its original size; host it in Moonbase, or size it yourself before uploading:"
|
|
2119
|
+
);
|
|
2120
|
+
return url;
|
|
2121
|
+
}
|
|
2122
|
+
if (isRendition(url))
|
|
2123
|
+
return url;
|
|
2124
|
+
const query = _ImageUtils.toQuery(transform);
|
|
2125
|
+
if (query === null) {
|
|
2126
|
+
warnOnce(
|
|
2127
|
+
"aspect-ratio",
|
|
2128
|
+
url,
|
|
2129
|
+
`A 'cover' crop more lopsided than ${maxCoverAspectRatio}:1 is not served. Serving this image at its original size instead:`
|
|
2130
|
+
);
|
|
2131
|
+
return url;
|
|
2132
|
+
}
|
|
2133
|
+
if (query.length === 0)
|
|
2134
|
+
return url;
|
|
2135
|
+
const parsed = new URL(url);
|
|
2136
|
+
const separator = parsed.search ? "&" : "?";
|
|
2137
|
+
return `${parsed.origin}/${transformPathSegment}${parsed.pathname}${parsed.search}${separator}${query.map(([key, value]) => `${key}=${value}`).join("&")}`;
|
|
2138
|
+
}
|
|
2139
|
+
/**
|
|
2140
|
+
* A `srcset` covering the given pixel densities, so a retina screen gets the sharper rendition
|
|
2141
|
+
* without every screen paying for it. Pair it with a `src` built from the same transform.
|
|
2142
|
+
*/
|
|
2143
|
+
static srcSet(url, transform, ...scales) {
|
|
2144
|
+
if (!url || scales.length === 0)
|
|
2145
|
+
return void 0;
|
|
2146
|
+
return scales.map((scale) => `${_ImageUtils.transform(url, _ImageUtils.atScale(transform, scale))} ${scale}x`).join(", ");
|
|
2147
|
+
}
|
|
2148
|
+
/** The same transform at a different pixel density. */
|
|
2149
|
+
static atScale(transform, scale) {
|
|
2150
|
+
return {
|
|
2151
|
+
...transform,
|
|
2152
|
+
width: transform.width === void 0 ? void 0 : transform.width * scale,
|
|
2153
|
+
height: transform.height === void 0 ? void 0 : transform.height * scale
|
|
2154
|
+
};
|
|
2155
|
+
}
|
|
2156
|
+
/**
|
|
2157
|
+
* The query parameters for a transform, in the CDN's own order, with defaults left out so two
|
|
2158
|
+
* equivalent transforms always produce the same URL — and therefore the same cache entry.
|
|
2159
|
+
* `null` when the CDN would refuse the transform outright.
|
|
2160
|
+
*/
|
|
2161
|
+
static toQuery(transform) {
|
|
2162
|
+
var _a, _b;
|
|
2163
|
+
const fit = (_a = transform.fit) != null ? _a : "contain";
|
|
2164
|
+
const width = snap(transform.width);
|
|
2165
|
+
const height = fit === "cover" ? (_b = snap(transform.height)) != null ? _b : width : snap(transform.height);
|
|
2166
|
+
if (fit === "cover" && width && height) {
|
|
2167
|
+
const ratio = width > height ? width / height : height / width;
|
|
2168
|
+
if (ratio > maxCoverAspectRatio)
|
|
2169
|
+
return null;
|
|
2170
|
+
}
|
|
2171
|
+
const query = [];
|
|
2172
|
+
if (width)
|
|
2173
|
+
query.push(["w", String(width)]);
|
|
2174
|
+
if (height && (fit === "cover" || transform.height !== void 0))
|
|
2175
|
+
query.push(["h", String(height)]);
|
|
2176
|
+
if (fit !== "contain")
|
|
2177
|
+
query.push(["fit", fit]);
|
|
2178
|
+
if (transform.format)
|
|
2179
|
+
query.push(["f", transform.format]);
|
|
2180
|
+
return query;
|
|
2181
|
+
}
|
|
2182
|
+
};
|
|
2183
|
+
|
|
1755
2184
|
// src/utils/utm.ts
|
|
1756
2185
|
function parseOffSiteReferrer(rawReferrer, currentOrigin) {
|
|
1757
2186
|
if (!rawReferrer)
|
|
@@ -1799,10 +2228,12 @@ var MoonbaseClient = class {
|
|
|
1799
2228
|
ActivationRequestStatus,
|
|
1800
2229
|
ActivationStatus,
|
|
1801
2230
|
Architecture,
|
|
2231
|
+
CartUtils,
|
|
1802
2232
|
ConnectableAccountProvider,
|
|
1803
2233
|
ConsoleLogger,
|
|
1804
2234
|
CycleLength,
|
|
1805
2235
|
DiscountUtils,
|
|
2236
|
+
ImageUtils,
|
|
1806
2237
|
InMemoryStore,
|
|
1807
2238
|
LicenseStatus,
|
|
1808
2239
|
LocalStorageStore,
|
|
@@ -1820,9 +2251,14 @@ var MoonbaseClient = class {
|
|
|
1820
2251
|
Platform,
|
|
1821
2252
|
SubscriptionStatus,
|
|
1822
2253
|
TokenStore,
|
|
2254
|
+
imageWidths,
|
|
2255
|
+
isPromotionDisplay,
|
|
2256
|
+
isTransformableImageUrl,
|
|
2257
|
+
maxImageDimension,
|
|
1823
2258
|
objectToQuery,
|
|
1824
2259
|
parseOffSiteReferrer,
|
|
1825
2260
|
problemDetailsSchema,
|
|
2261
|
+
promotionDisplays,
|
|
1826
2262
|
resolveUtm,
|
|
1827
2263
|
schemas,
|
|
1828
2264
|
utmToObject
|