@moonbase.sh/storefront-api 2.3.1 → 2.4.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/dist/index.cjs CHANGED
@@ -71,6 +71,7 @@ __export(schemas_exports2, {
71
71
  offerConditionSchema: () => offerConditionSchema,
72
72
  storefrontBundleSchema: () => storefrontBundleSchema,
73
73
  storefrontOfferSchema: () => storefrontOfferSchema,
74
+ storefrontOfferTargetSchema: () => storefrontOfferTargetSchema,
74
75
  storefrontProductSchema: () => storefrontProductSchema,
75
76
  storefrontSchema: () => storefrontSchema,
76
77
  taxEstimateSchema: () => taxEstimateSchema
@@ -287,13 +288,36 @@ var cartContainsItemsConditionSchema = import_zod3.z.object({
287
288
  relevantItemVariations: import_zod3.z.record(import_zod3.z.string(), import_zod3.z.string().array())
288
289
  });
289
290
  var offerConditionSchema = import_zod3.z.discriminatedUnion("type", [cartContainsItemsConditionSchema]);
291
+ var offerTargetItemSchema = import_zod3.z.union([storefrontProductSchema, storefrontBundleSchema]);
292
+ var storefrontOfferTargetSchema = import_zod3.z.object({
293
+ // The product or bundle the offer applies to.
294
+ item: offerTargetItemSchema,
295
+ // The pricing variation IDs the offer is restricted to; an empty list means all variations.
296
+ variations: import_zod3.z.string().array()
297
+ });
298
+ function legacyOfferTargets(target, targetVariations) {
299
+ if (!target)
300
+ return [];
301
+ const items = Array.isArray(target) ? target : [target];
302
+ return items.map((item) => ({ item, variations: items.length === 1 ? targetVariations != null ? targetVariations : [] : [] }));
303
+ }
290
304
  var storefrontOfferSchema = import_zod3.z.object({
291
305
  id: import_zod3.z.string(),
292
- target: singleOrMultiple(import_zod3.z.union([storefrontProductSchema, storefrontBundleSchema])),
293
- targetVariations: import_zod3.z.string().array(),
306
+ // 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.
308
+ targets: import_zod3.z.array(storefrontOfferTargetSchema).optional(),
309
+ // Legacy fields, kept optional only so old cached storefront responses still parse. The API now
310
+ // exposes the unified `targets` model; we derive `targets` from these when a response predates it.
311
+ // `.catch` guards against the deprecated shape drifting: a malformed legacy field is ignored
312
+ // rather than failing the whole response (newer responses carry `targets` anyway).
313
+ target: singleOrMultiple(offerTargetItemSchema).optional().catch(void 0),
314
+ targetVariations: import_zod3.z.string().array().optional().catch(void 0),
294
315
  condition: offerConditionSchema,
295
316
  discount: discountSchema
296
- });
317
+ }).transform(({ target, targetVariations, targets, ...rest }) => ({
318
+ ...rest,
319
+ targets: targets != null ? targets : legacyOfferTargets(target, targetVariations)
320
+ }));
297
321
  var taxEstimateSchema = import_zod3.z.object({
298
322
  rate: import_zod3.z.number(),
299
323
  mode: import_zod3.z.enum(["Exclusive", "Inclusive"]),
@@ -1693,11 +1717,27 @@ var OfferUtils = class _OfferUtils {
1693
1717
  // a. Not be owned, or all targets need to be owned
1694
1718
  // b. Not already in the cart
1695
1719
  static relevantTargets(offer, cartItems) {
1696
- if (Array.isArray(offer.target)) {
1697
- const allOwned = offer.target.every((i) => i.owned);
1698
- return offer.target.filter((i) => (allOwned || !i.owned) && !cartItems.some((l) => l.type === i.type && i.id === (l.type === "Product" ? l.productId : l.bundleId)));
1720
+ if (offer.targets.length > 1) {
1721
+ const allOwned = offer.targets.every((t) => t.item.owned);
1722
+ return offer.targets.filter((t) => (allOwned || !t.item.owned) && !cartItems.some((l) => l.type === t.item.type && t.item.id === (l.type === "Product" ? l.productId : l.bundleId)));
1699
1723
  }
1700
- return [offer.target];
1724
+ return offer.targets;
1725
+ }
1726
+ // The pricing variations a target restricts the offer to. An empty (or missing) variation list
1727
+ // means the offer is not narrowed to specific variations, in which case we keep the established
1728
+ // single default-variation upsell.
1729
+ static targetedVariations(target) {
1730
+ var _a;
1731
+ const { item, variations } = target;
1732
+ if (variations.length === 0)
1733
+ return item.defaultVariation ? [item.defaultVariation] : [];
1734
+ const itemVariations = (_a = item.variations) != null ? _a : item.defaultVariation ? [item.defaultVariation] : [];
1735
+ return itemVariations.filter((v) => variations.includes(v.id));
1736
+ }
1737
+ // Expands the relevant targets into one `{ target, variation }` entry per targeted variation, so
1738
+ // an offer targeting several variations (or several products) surfaces each as its own upsell row.
1739
+ static relevantTargetVariations(offer, cartItems) {
1740
+ return _OfferUtils.relevantTargets(offer, cartItems).flatMap((target) => _OfferUtils.targetedVariations(target).map((variation) => ({ target: target.item, variation })));
1701
1741
  }
1702
1742
  static applyToVariation(offer, variation) {
1703
1743
  const discount = DiscountUtils.apply(offer.discount, variation.price);
@@ -1708,8 +1748,7 @@ var OfferUtils = class _OfferUtils {
1708
1748
  };
1709
1749
  }
1710
1750
  static targetContainsBundle(offer, bundleId) {
1711
- if (Array.isArray(offer.target))
1712
- return offer.target.some((i) => i.type === "Bundle" && i.id === bundleId);
1751
+ return offer.targets.some((t) => t.item.type === "Bundle" && t.item.id === bundleId);
1713
1752
  }
1714
1753
  };
1715
1754