@final-commerce/common 2.5.1-preprod.4 → 2.6.0-staging.2

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.js CHANGED
@@ -20,11 +20,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
20
20
  var index_exports = {};
21
21
  __export(index_exports, {
22
22
  ADYEN_ROLLOUT_COUNTRY_CODES: () => ADYEN_ROLLOUT_COUNTRY_CODES,
23
- AFFIRM_COUNTRY_CODES: () => AFFIRM_COUNTRY_CODES,
24
- AFFIRM_PRICE_PLANS: () => AFFIRM_PRICE_PLANS,
25
23
  AttributeType: () => AttributeType,
26
- BNPL_METHOD_STATUS: () => BNPL_METHOD_STATUS,
27
- BNPL_SKIP_REASON: () => BNPL_SKIP_REASON,
28
24
  BookingRatePeriod: () => BookingRatePeriod,
29
25
  BookingResourceKind: () => BookingResourceKind,
30
26
  BookingResourceStatus: () => BookingResourceStatus,
@@ -102,6 +98,7 @@ __export(index_exports, {
102
98
  UserTypes: () => UserTypes,
103
99
  addAmounts: () => addAmounts,
104
100
  amountToString: () => amountToString,
101
+ ancestorChain: () => ancestorChain,
105
102
  assertUnitSane: () => assertUnitSane,
106
103
  availableIn: () => availableIn,
107
104
  baseFor: () => baseFor,
@@ -115,6 +112,14 @@ __export(index_exports, {
115
112
  buildResolvedConfig: () => buildResolvedConfig,
116
113
  calculatePercentage: () => calculatePercentage,
117
114
  canTransition: () => canTransition,
115
+ cheapestCompositeItem: () => cheapestCompositeItem,
116
+ cheapestCompositePrice: () => cheapestCompositePrice,
117
+ compositeItemCost: () => compositeItemCost,
118
+ compositeItemUnavailable: () => compositeItemUnavailable,
119
+ compositeMakeable: () => compositeMakeable,
120
+ compositeModifierMismatches: () => compositeModifierMismatches,
121
+ compositeReservationMismatches: () => compositeReservationMismatches,
122
+ compositeVariantInStock: () => compositeVariantInStock,
118
123
  conditionSummary: () => conditionSummary,
119
124
  convertFixedFeeToLocalCurrency: () => convertFixedFeeToLocalCurrency,
120
125
  createAmount: () => createAmount,
@@ -149,15 +154,13 @@ __export(index_exports, {
149
154
  getPricingRegionFromCountry: () => getPricingRegionFromCountry,
150
155
  getSettlementTotal: () => getSettlementTotal,
151
156
  getTotalCollected: () => getTotalCollected,
152
- hasInCartFlag: () => hasInCartFlag,
157
+ hasSellableChoice: () => hasSellableChoice,
153
158
  holdExpiresAt: () => holdExpiresAt,
154
159
  inferStatesFromLegacyStatus: () => inferStatesFromLegacyStatus,
155
160
  isAdyenRolloutCountry: () => isAdyenRolloutCountry,
156
- isAffirmCountry: () => isAffirmCountry,
157
- isAffirmPricePlan: () => isAffirmPricePlan,
161
+ isChoiceAvailableAtOutlet: () => isChoiceAvailableAtOutlet,
158
162
  isConditionOperator: () => isConditionOperator,
159
163
  isFulfillmentState: () => isFulfillmentState,
160
- isInCart: () => isInCart,
161
164
  isPaymentState: () => isPaymentState,
162
165
  isSupportedCurrency: () => isSupportedCurrency,
163
166
  isSystemFamilyCode: () => isSystemFamilyCode,
@@ -166,6 +169,7 @@ __export(index_exports, {
166
169
  mapToLegacyStatus: () => mapToLegacyStatus,
167
170
  maxPrecisionFor: () => maxPrecisionFor,
168
171
  mergeFragment: () => mergeFragment,
172
+ modifiersSellable: () => modifiersSellable,
169
173
  multiplyAmount: () => multiplyAmount,
170
174
  normalizeMongoIdString: () => normalizeMongoIdString,
171
175
  occupancyIn: () => occupancyIn,
@@ -173,9 +177,14 @@ __export(index_exports, {
173
177
  parseCurrencyCode: () => parseCurrencyCode,
174
178
  parseStripeCurrencyCode: () => parseStripeCurrencyCode,
175
179
  planWindow: () => planWindow,
180
+ poolDemand: () => poolDemand,
181
+ poolOf: () => poolOf,
182
+ poolShortfall: () => poolShortfall,
183
+ poolStockAt: () => poolStockAt,
176
184
  presentAt: () => presentAt,
177
185
  removeFragment: () => removeFragment,
178
186
  resolveFamily: () => resolveFamily,
187
+ resolveModifiersForProduct: () => resolveModifiersForProduct,
179
188
  resolveTemplate: () => resolveTemplate,
180
189
  resolveUnit: () => resolveUnit,
181
190
  roundToPrecision: () => roundToPrecision,
@@ -191,7 +200,6 @@ __export(index_exports, {
191
200
  toMinorUnits: () => toMinorUnits,
192
201
  validateConfig: () => validateConfig,
193
202
  validateFragmentCompatibility: () => validateFragmentCompatibility,
194
- validateOrderStatuses: () => validateOrderStatuses,
195
203
  violatesFinancialInvariant: () => violatesFinancialInvariant,
196
204
  windowEndsAt: () => windowEndsAt,
197
205
  withinPresence: () => withinPresence
@@ -437,7 +445,6 @@ var InventorySpecificActionType = /* @__PURE__ */ ((InventorySpecificActionType2
437
445
  InventorySpecificActionType2["PO_RECEIVED"] = "PO_RECEIVED";
438
446
  InventorySpecificActionType2["EXTERNAL_SALE"] = "EXTERNAL_SALE";
439
447
  InventorySpecificActionType2["EXTERNAL_RESTOCK"] = "EXTERNAL_RESTOCK";
440
- InventorySpecificActionType2["CUSTOM"] = "CUSTOM";
441
448
  return InventorySpecificActionType2;
442
449
  })(InventorySpecificActionType || {});
443
450
  var ExtensionCategory = /* @__PURE__ */ ((ExtensionCategory2) => {
@@ -624,6 +631,7 @@ var ProductType = /* @__PURE__ */ ((ProductType2) => {
624
631
  ProductType2["SIMPLE"] = "simple";
625
632
  ProductType2["VARIABLE"] = "variable";
626
633
  ProductType2["BOOKING"] = "booking";
634
+ ProductType2["COMPOSITE"] = "composite";
627
635
  return ProductType2;
628
636
  })(ProductType || {});
629
637
  var ProductStatus = /* @__PURE__ */ ((ProductStatus2) => {
@@ -1554,6 +1562,312 @@ function normalizeMongoIdString(value) {
1554
1562
  return null;
1555
1563
  }
1556
1564
 
1565
+ // utils/composite-price.util.ts
1566
+ var compositeItemCost = (item) => item.extraCharge * item.quantity;
1567
+ function cheapestCompositeItem(items) {
1568
+ let cheapest;
1569
+ items.forEach((item, index) => {
1570
+ if (!item.available) return;
1571
+ if (cheapest === void 0 || compositeItemCost(item) < compositeItemCost(items[cheapest])) cheapest = index;
1572
+ });
1573
+ return cheapest;
1574
+ }
1575
+ function cheapestCompositePrice(parts) {
1576
+ let total = 0;
1577
+ for (const part of parts) {
1578
+ if (part.min === 0) continue;
1579
+ const index = cheapestCompositeItem(part.items) ?? cheapestCompositeItem(part.items.map((item) => ({ ...item, available: true })));
1580
+ if (index === void 0) return void 0;
1581
+ total += part.min * compositeItemCost(part.items[index]);
1582
+ }
1583
+ return total;
1584
+ }
1585
+
1586
+ // utils/modifier-resolution.util.ts
1587
+ function ancestorChain(categoryId, categoriesById) {
1588
+ const chain = [];
1589
+ const seen = /* @__PURE__ */ new Set([categoryId]);
1590
+ let current = categoriesById.get(categoryId)?.parentId ?? void 0;
1591
+ while (current && !seen.has(current)) {
1592
+ chain.push(current);
1593
+ seen.add(current);
1594
+ current = categoriesById.get(current)?.parentId ?? void 0;
1595
+ }
1596
+ return chain;
1597
+ }
1598
+ function clampRule(rule) {
1599
+ if (rule.min != null && rule.max != null && rule.min > rule.max) {
1600
+ return { ...rule, min: rule.max };
1601
+ }
1602
+ return rule;
1603
+ }
1604
+ function mergeRules(a, b) {
1605
+ const required = a.required || b.required;
1606
+ const min = a.min == null ? b.min : b.min == null ? a.min : Math.max(a.min, b.min);
1607
+ const max = a.max == null ? b.max : b.max == null ? a.max : Math.min(a.max, b.max);
1608
+ return clampRule({ required, min, max, sortOrder: Math.min(a.sortOrder, b.sortOrder) });
1609
+ }
1610
+ function toRule(row) {
1611
+ const required = row.required ?? false;
1612
+ return clampRule({
1613
+ required,
1614
+ // `required` is the gate: a stale min left on a placement that went optional is
1615
+ // ignored. A cleared min/max stays the explicit null it arrived as — nothing
1616
+ // downstream distinguishes null from absent.
1617
+ min: required ? row.min : void 0,
1618
+ max: row.max,
1619
+ sortOrder: row.sortOrder ?? 0
1620
+ });
1621
+ }
1622
+ function toResolvedChoices(modifier) {
1623
+ return (modifier.choices ?? []).map((choice, index) => ({
1624
+ _id: String(choice._id),
1625
+ name: choice.name,
1626
+ price: choice.price ?? 0,
1627
+ isDefault: choice.isDefault,
1628
+ defaultQuantity: choice.defaultQuantity,
1629
+ unavailableOutletIds: choice.unavailableOutletIds?.map(String),
1630
+ sortOrder: choice.sortOrder ?? index
1631
+ })).sort((a, b) => a.sortOrder - b.sortOrder);
1632
+ }
1633
+ function resolveModifiersForProduct(inputs) {
1634
+ const categoriesById = new Map(
1635
+ inputs.categories.filter((category) => !category.isDeleted).map((category) => [String(category._id), category])
1636
+ );
1637
+ const modifiersById = new Map(
1638
+ inputs.modifiers.filter((modifier) => !modifier.isDeleted).map((modifier) => [String(modifier._id), modifier])
1639
+ );
1640
+ const directCategories = new Set(inputs.categoryIds.map(String));
1641
+ const inheritedCategories = /* @__PURE__ */ new Set();
1642
+ for (const categoryId of directCategories) {
1643
+ for (const ancestor of ancestorChain(categoryId, categoriesById)) {
1644
+ if (!directCategories.has(ancestor)) inheritedCategories.add(ancestor);
1645
+ }
1646
+ }
1647
+ const rules = /* @__PURE__ */ new Map();
1648
+ for (const placement of inputs.categoryModifiers) {
1649
+ if (placement.isDeleted) continue;
1650
+ const categoryId = String(placement.categoryId);
1651
+ const precedence = directCategories.has(categoryId) ? 0 : inheritedCategories.has(categoryId) ? 1 : -1;
1652
+ if (precedence === -1) continue;
1653
+ const modifierId = String(placement.modifierId);
1654
+ if (!modifiersById.has(modifierId)) continue;
1655
+ const incoming = toRule(placement);
1656
+ const existing = rules.get(modifierId);
1657
+ if (!existing || precedence < existing.precedence) {
1658
+ rules.set(modifierId, { precedence, rule: incoming });
1659
+ } else if (precedence === existing.precedence) {
1660
+ rules.set(modifierId, { precedence, rule: mergeRules(existing.rule, incoming) });
1661
+ }
1662
+ }
1663
+ const resolved = [];
1664
+ for (const [modifierId, { rule }] of rules) {
1665
+ const modifier = modifiersById.get(modifierId);
1666
+ if (!modifier) continue;
1667
+ const required = rule.required;
1668
+ resolved.push({
1669
+ _id: modifierId,
1670
+ name: modifier.name,
1671
+ selectionType: modifier.selectionType,
1672
+ allowQuantity: modifier.allowQuantity,
1673
+ required,
1674
+ // single-selection is implicitly capped at 1 — min/max are never carried.
1675
+ min: modifier.selectionType === "single" ? void 0 : rule.min,
1676
+ max: modifier.selectionType === "single" ? void 0 : rule.max,
1677
+ sortOrder: rule.sortOrder,
1678
+ choices: toResolvedChoices(modifier)
1679
+ });
1680
+ }
1681
+ return resolved.sort((a, b) => a.sortOrder - b.sortOrder || a.name.localeCompare(b.name));
1682
+ }
1683
+ function isChoiceAvailableAtOutlet(choice, outletId) {
1684
+ return !(choice.unavailableOutletIds ?? []).includes(outletId);
1685
+ }
1686
+ function hasSellableChoice(modifier, outletId) {
1687
+ return modifier.choices.some((choice) => isChoiceAvailableAtOutlet(choice, outletId));
1688
+ }
1689
+ var modifiersSellable = (modifiers, outletId) => modifiers.every((modifier) => !modifier.required || hasSellableChoice(modifier, outletId));
1690
+
1691
+ // utils/composite-availability.util.ts
1692
+ function poolStockAt(rows, outletId) {
1693
+ if (!rows.some((row) => row.manageStock === true)) return null;
1694
+ return Math.max(0, rows.find((row) => String(row.outletId) === outletId)?.quantity ?? 0);
1695
+ }
1696
+ function compositeVariantInStock(variantId, quantity, outlet) {
1697
+ const shelf = outlet.shelves.get(variantId);
1698
+ if (!shelf || shelf.unresolved) return false;
1699
+ const onHand = outlet.poolStock.get(shelf.pool);
1700
+ if (onHand === void 0) return false;
1701
+ if (onHand === null || shelf.backorder) return true;
1702
+ return onHand >= (shelf.unit ? toBase(quantity, shelf.unit) : quantity);
1703
+ }
1704
+ function compositeItemUnavailable(item, rows) {
1705
+ const { outlet } = rows;
1706
+ const sellableHere = (productId2) => !outlet || !outlet.hiddenProductIds.has(productId2) && !outlet.unsellableProductIds.has(productId2);
1707
+ if (item.categoryId) {
1708
+ const variants = rows.categoryVariants.get(String(item.categoryId));
1709
+ if (!variants) return "deleted";
1710
+ const offered = variants.filter((variantId2) => sellableHere(rows.variantProducts.get(variantId2) ?? ""));
1711
+ if (!offered.length) return "empty";
1712
+ if (!outlet) return null;
1713
+ const unmarked = offered.filter((variantId2) => !outlet.markedVariantIds.has(variantId2));
1714
+ if (!unmarked.length) return "marked_sold_out";
1715
+ return unmarked.some((variantId2) => compositeVariantInStock(variantId2, item.quantity, outlet)) ? null : "out_of_stock";
1716
+ }
1717
+ const variantId = String(item.variantId);
1718
+ const productId = rows.variantProducts.get(variantId);
1719
+ if (!productId || !rows.productStatuses.has(productId)) return "deleted";
1720
+ if (rows.productStatuses.get(productId) !== "active" /* ACTIVE */) return "inactive";
1721
+ if (!outlet) return null;
1722
+ if (outlet.hiddenProductIds.has(productId)) return "hidden";
1723
+ if (outlet.unsellableProductIds.has(productId)) return "empty";
1724
+ if (outlet.markedVariantIds.has(variantId)) return "marked_sold_out";
1725
+ if (rows.bookableProductIds.has(productId)) {
1726
+ if (!outlet.bookableSeats) return "by_date";
1727
+ return (outlet.bookableSeats.get(productId) ?? 0) >= item.quantity ? null : "fully_booked";
1728
+ }
1729
+ return compositeVariantInStock(variantId, item.quantity, outlet) ? null : "out_of_stock";
1730
+ }
1731
+ function compositeMakeable(parts, rows) {
1732
+ const { outlet } = rows;
1733
+ if (!outlet) throw new Error("compositeMakeable counts at one outlet: rows.outlet is required");
1734
+ const required = parts.map((part, partIndex) => ({ ...part, partIndex })).filter((part) => part.min > 0);
1735
+ const isBookable = (variantId) => rows.bookableProductIds.has(rows.variantProducts.get(String(variantId)) ?? "");
1736
+ if (required.some((part) => part.items.some((item) => item.variantId && isBookable(item.variantId))))
1737
+ return { byDate: true };
1738
+ const optionsOf = (item) => (item.categoryId ? rows.categoryVariants.get(String(item.categoryId)) ?? [] : [String(item.variantId)]).flatMap(
1739
+ (variantId) => {
1740
+ const reason = compositeItemUnavailable({ variantId, quantity: item.quantity }, rows);
1741
+ const shelf = outlet.shelves.get(variantId);
1742
+ if (reason && reason !== "out_of_stock" || !shelf) return [];
1743
+ const stock = outlet.poolStock.get(shelf.pool);
1744
+ const onHand = shelf.unresolved || stock === void 0 ? 0 : stock === null || shelf.backorder ? null : stock;
1745
+ const perPick = shelf.unit ? toBase(item.quantity, shelf.unit) : item.quantity;
1746
+ return [{ itemId: item.id, pool: shelf.pool, perPick, onHand }];
1747
+ }
1748
+ );
1749
+ const counted = required.map((part) => ({ ...part, options: part.items.flatMap(optionsOf) }));
1750
+ const order = [...counted].sort((a, b) => Number(a.options.length !== 1) - Number(b.options.length !== 1));
1751
+ const picksLeft = (option, reserved) => option.onHand === null ? Infinity : Math.max(0, Math.floor((option.onHand - (reserved.get(option.pool) ?? 0)) / option.perPick));
1752
+ const shortAt = (n) => {
1753
+ const reserved = /* @__PURE__ */ new Map();
1754
+ for (const part of order) {
1755
+ let wanted = part.min * n;
1756
+ const ranked = [...part.options].sort((a, b) => picksLeft(b, reserved) - picksLeft(a, reserved));
1757
+ for (const option of ranked) {
1758
+ if (wanted <= 0) break;
1759
+ const taken = Math.min(wanted, picksLeft(option, reserved));
1760
+ if (option.onHand !== null)
1761
+ reserved.set(option.pool, (reserved.get(option.pool) ?? 0) + taken * option.perPick);
1762
+ wanted -= taken;
1763
+ }
1764
+ if (wanted > 0) return part.partIndex;
1765
+ }
1766
+ return void 0;
1767
+ };
1768
+ const whyRows = [];
1769
+ const rowPools = /* @__PURE__ */ new Map();
1770
+ const makes = (has, uses) => has === null ? null : Math.floor(has / uses);
1771
+ for (const part of counted) {
1772
+ const [option] = part.options;
1773
+ if (part.options.length === 1) {
1774
+ const uses = option.perPick * part.min;
1775
+ const row = whyRows.find((candidate) => rowPools.get(candidate) === option.pool);
1776
+ if (!row) {
1777
+ const created = { itemIds: [option.itemId], uses, has: option.onHand, makes: makes(option.onHand, uses) };
1778
+ rowPools.set(created, option.pool);
1779
+ whyRows.push(created);
1780
+ continue;
1781
+ }
1782
+ row.itemIds.push(option.itemId);
1783
+ row.uses += uses;
1784
+ if (option.onHand === null) row.has = null;
1785
+ row.makes = makes(row.has, row.uses);
1786
+ continue;
1787
+ }
1788
+ const perPool = /* @__PURE__ */ new Map();
1789
+ for (const candidate of part.options)
1790
+ perPool.set(candidate.pool, Math.max(perPool.get(candidate.pool) ?? 0, picksLeft(candidate, /* @__PURE__ */ new Map())));
1791
+ const total = [...perPool.values()].reduce((sum, picks) => sum + picks, 0);
1792
+ const has = total === Infinity ? null : total;
1793
+ whyRows.push({
1794
+ itemIds: part.items.map((item) => item.id),
1795
+ partIndex: part.partIndex,
1796
+ uses: part.min,
1797
+ has,
1798
+ makes: makes(has, part.min)
1799
+ });
1800
+ }
1801
+ if (counted.every((part) => part.options.some((option) => option.onHand === null)))
1802
+ return { count: null, rows: whyRows };
1803
+ const ceiling = Math.min(
1804
+ ...counted.filter((part) => !part.options.some((option) => option.onHand === null)).map(
1805
+ (part) => Math.floor(part.options.reduce((sum, option) => sum + picksLeft(option, /* @__PURE__ */ new Map()), 0) / part.min)
1806
+ )
1807
+ );
1808
+ let [low, high] = [0, ceiling];
1809
+ while (low < high) {
1810
+ const mid = Math.ceil((low + high) / 2);
1811
+ if (shortAt(mid) === void 0) low = mid;
1812
+ else high = mid - 1;
1813
+ }
1814
+ const short = shortAt(low + 1);
1815
+ const shortPart = counted.find((part) => part.partIndex === short);
1816
+ const limitedBy = whyRows.findIndex(
1817
+ (row) => shortPart?.options.length === 1 ? row.partIndex === void 0 && row.itemIds.includes(shortPart.options[0].itemId) : row.partIndex === short
1818
+ );
1819
+ return { count: low, rows: whyRows, ...limitedBy >= 0 ? { limitedBy } : {} };
1820
+ }
1821
+ var poolOf = (line) => line.stockVariantId || line.variantId;
1822
+ function poolDemand(lines) {
1823
+ const demand = /* @__PURE__ */ new Map();
1824
+ const add = (line, times) => demand.set(
1825
+ poolOf(line),
1826
+ (demand.get(poolOf(line)) ?? 0) + (line.unit ? toBase(line.quantity * times, line.unit) : line.quantity * times)
1827
+ );
1828
+ for (const line of lines) {
1829
+ if (line.components) for (const component of line.components) add(component, line.quantity);
1830
+ else add(line, 1);
1831
+ }
1832
+ return demand;
1833
+ }
1834
+ function poolShortfall(demand, poolStock, backordered = /* @__PURE__ */ new Set()) {
1835
+ for (const [poolId, wanted] of demand) {
1836
+ const available = poolStock.get(poolId);
1837
+ if (available === null || available === void 0 || backordered.has(poolId)) continue;
1838
+ if (wanted > available) return { poolId, available, wanted };
1839
+ }
1840
+ return null;
1841
+ }
1842
+
1843
+ // utils/composite-reservation.util.ts
1844
+ function compositeModifierMismatches(lineItems) {
1845
+ return lineItems.flatMap(
1846
+ (line) => line.components?.length ? (line.modifiers ?? []).flatMap(
1847
+ (modifier) => modifier.componentIndex !== void 0 && line.components[modifier.componentIndex] !== void 0 ? [] : [
1848
+ `Modifier ${modifier.modifierName ?? "?"} on composite line ${line.internalId ?? "?"} names no component (componentIndex ${modifier.componentIndex}) \u2014 a composite has no modifiers of its own`
1849
+ ]
1850
+ ) : []
1851
+ );
1852
+ }
1853
+ function compositeReservationMismatches(order) {
1854
+ return (order.reservations ?? []).flatMap((reservation) => {
1855
+ if (reservation.lineItemInternalId === void 0) return [];
1856
+ const name = `Booking ${String(reservation.bookingId ?? "?")}`;
1857
+ const line = order.lineItems?.find((candidate) => candidate.internalId === reservation.lineItemInternalId);
1858
+ if (!line) return [`${name} names line ${reservation.lineItemInternalId}, which is not on the order`];
1859
+ const component = reservation.componentIndex === void 0 ? void 0 : line.components?.[reservation.componentIndex];
1860
+ if (!component) return [`${name} names no component ${reservation.componentIndex} of line ${line.internalId}`];
1861
+ if (String(component.productId) !== String(reservation.productId))
1862
+ return [
1863
+ `${name} is for ${String(reservation.productId)}, but that component sold ${String(component.productId)}`
1864
+ ];
1865
+ if (reservation.price !== 0 || reservation.total !== 0)
1866
+ return [`${name} carries money (${reservation.price}/${reservation.total}); its component already does`];
1867
+ return [];
1868
+ });
1869
+ }
1870
+
1557
1871
  // units/family-code.ts
1558
1872
  var MAX_SLUG_LENGTH = 32;
1559
1873
  var FALLBACK_SLUG = "family";
@@ -2692,51 +3006,6 @@ function inferStatesFromLegacyStatus(order) {
2692
3006
  return { payment: "unpaid", fulfillment: "pending" };
2693
3007
  }
2694
3008
 
2695
- // order-state-machine/in-cart.ts
2696
- function hasInCartFlag(order) {
2697
- return order.inCart != null;
2698
- }
2699
- function isInCart(order) {
2700
- if (order.inCart != null) return order.inCart.active === true;
2701
- if (order.fulfillmentState) return order.fulfillmentState === "draft";
2702
- return (order.status ?? "").trim().toLowerCase() === "in-cart";
2703
- }
2704
-
2705
- // order-state-machine/order-status.ts
2706
- function validateOrderStatuses(statuses) {
2707
- if (!Array.isArray(statuses)) {
2708
- return [{ index: -1, field: "orderStatuses", message: "must be an array" }];
2709
- }
2710
- const errors = [];
2711
- const seen = /* @__PURE__ */ new Set();
2712
- statuses.forEach((raw, index) => {
2713
- const s = raw ?? {};
2714
- const add = (field, message) => errors.push({ index, field, message });
2715
- if (typeof s.id !== "string" || !s.id.trim()) add("id", "must be a non-empty string");
2716
- else if (seen.has(s.id)) add("id", `duplicate id "${s.id}"`);
2717
- else seen.add(s.id);
2718
- if (typeof s.label !== "string" || !s.label.trim()) add("label", "must be a non-empty string");
2719
- for (const field of ["color", "icon"]) {
2720
- if (s[field] !== void 0 && typeof s[field] !== "string") add(field, "must be a string");
2721
- }
2722
- if (s.fulfillmentState !== void 0 && !(typeof s.fulfillmentState === "string" && isFulfillmentState(s.fulfillmentState))) {
2723
- add("fulfillmentState", `"${String(s.fulfillmentState)}" is not a fulfillment state`);
2724
- }
2725
- if (s.requiresPaymentState !== void 0) {
2726
- if (!Array.isArray(s.requiresPaymentState) || s.requiresPaymentState.length === 0) {
2727
- add("requiresPaymentState", "must be a non-empty array of payment states");
2728
- } else {
2729
- for (const p of s.requiresPaymentState) {
2730
- if (!(typeof p === "string" && isPaymentState(p))) {
2731
- add("requiresPaymentState", `"${String(p)}" is not a payment state`);
2732
- }
2733
- }
2734
- }
2735
- }
2736
- });
2737
- return errors;
2738
- }
2739
-
2740
3009
  // order-state-machine/write-strategy.ts
2741
3010
  var LegacyWriteStrategy = class {
2742
3011
  applyState(_order) {
@@ -3248,7 +3517,9 @@ function buildGrid(rule, timeZone, from, to, now, busy = []) {
3248
3517
  const start = startAt.getTime();
3249
3518
  if (start < askedFrom || start >= askedTo) return false;
3250
3519
  if (start < notBefore || start > notAfter) return false;
3251
- return !blocked.some(({ from: blockFrom, to: blockTo }) => start < blockTo && bufferEndAt.getTime() > blockFrom);
3520
+ return !blocked.some(
3521
+ ({ from: blockFrom, to: blockTo }) => start < blockTo && bufferEndAt.getTime() > blockFrom
3522
+ );
3252
3523
  }).sort((first, second) => +first.startAt - +second.startAt);
3253
3524
  }
3254
3525
  function slotsForWindow(grid, startAt, endAt) {
@@ -3367,7 +3638,14 @@ function buildAvailability({
3367
3638
  for (const slot of slots2) {
3368
3639
  const { taken, foreignProductId } = occupancyIn(mine, productId, slot.startAt, slot.bufferEndAt, shared);
3369
3640
  const free = foreignProductId ? 0 : Math.max(capacity - taken, 0);
3370
- const entry = byInstant.get(+slot.startAt) ?? { ...slot, capacity: 0, booked: 0, free: 0, resources: [], dayKey: shopDayKey(slot.startAt, resource.timeZone) };
3641
+ const entry = byInstant.get(+slot.startAt) ?? {
3642
+ ...slot,
3643
+ capacity: 0,
3644
+ booked: 0,
3645
+ free: 0,
3646
+ resources: [],
3647
+ dayKey: shopDayKey(slot.startAt, resource.timeZone)
3648
+ };
3371
3649
  entry.capacity += capacity;
3372
3650
  entry.booked += foreignProductId ? capacity : taken;
3373
3651
  entry.free += free;
@@ -3445,34 +3723,10 @@ var ADYEN_ROLLOUT_COUNTRY_CODES = /* @__PURE__ */ new Set(["CA"]);
3445
3723
  function isAdyenRolloutCountry(alpha2) {
3446
3724
  return ADYEN_ROLLOUT_COUNTRY_CODES.has(alpha2);
3447
3725
  }
3448
- var AFFIRM_PRICE_PLANS = ["core", "standard", "signature"];
3449
- function isAffirmPricePlan(value) {
3450
- return typeof value === "string" && AFFIRM_PRICE_PLANS.includes(value);
3451
- }
3452
- var AFFIRM_COUNTRY_CODES = /* @__PURE__ */ new Set(["CA"]);
3453
- function isAffirmCountry(alpha2) {
3454
- return !!alpha2 && AFFIRM_COUNTRY_CODES.has(alpha2.toUpperCase());
3455
- }
3456
- var BNPL_METHOD_STATUS = {
3457
- ACTIVE: "active",
3458
- PENDING: "pending",
3459
- UNAVAILABLE: "unavailable",
3460
- UNKNOWN: "unknown"
3461
- };
3462
- var BNPL_SKIP_REASON = {
3463
- PENDING: "pending",
3464
- NO_SUPPORT_EMAIL: "no_support_email",
3465
- NO_CURRENCY: "no_currency",
3466
- NO_BUSINESS_LINE: "no_business_line"
3467
- };
3468
3726
  // Annotate the CommonJS export names for ESM import in node:
3469
3727
  0 && (module.exports = {
3470
3728
  ADYEN_ROLLOUT_COUNTRY_CODES,
3471
- AFFIRM_COUNTRY_CODES,
3472
- AFFIRM_PRICE_PLANS,
3473
3729
  AttributeType,
3474
- BNPL_METHOD_STATUS,
3475
- BNPL_SKIP_REASON,
3476
3730
  BookingRatePeriod,
3477
3731
  BookingResourceKind,
3478
3732
  BookingResourceStatus,
@@ -3550,6 +3804,7 @@ var BNPL_SKIP_REASON = {
3550
3804
  UserTypes,
3551
3805
  addAmounts,
3552
3806
  amountToString,
3807
+ ancestorChain,
3553
3808
  assertUnitSane,
3554
3809
  availableIn,
3555
3810
  baseFor,
@@ -3563,6 +3818,14 @@ var BNPL_SKIP_REASON = {
3563
3818
  buildResolvedConfig,
3564
3819
  calculatePercentage,
3565
3820
  canTransition,
3821
+ cheapestCompositeItem,
3822
+ cheapestCompositePrice,
3823
+ compositeItemCost,
3824
+ compositeItemUnavailable,
3825
+ compositeMakeable,
3826
+ compositeModifierMismatches,
3827
+ compositeReservationMismatches,
3828
+ compositeVariantInStock,
3566
3829
  conditionSummary,
3567
3830
  convertFixedFeeToLocalCurrency,
3568
3831
  createAmount,
@@ -3597,15 +3860,13 @@ var BNPL_SKIP_REASON = {
3597
3860
  getPricingRegionFromCountry,
3598
3861
  getSettlementTotal,
3599
3862
  getTotalCollected,
3600
- hasInCartFlag,
3863
+ hasSellableChoice,
3601
3864
  holdExpiresAt,
3602
3865
  inferStatesFromLegacyStatus,
3603
3866
  isAdyenRolloutCountry,
3604
- isAffirmCountry,
3605
- isAffirmPricePlan,
3867
+ isChoiceAvailableAtOutlet,
3606
3868
  isConditionOperator,
3607
3869
  isFulfillmentState,
3608
- isInCart,
3609
3870
  isPaymentState,
3610
3871
  isSupportedCurrency,
3611
3872
  isSystemFamilyCode,
@@ -3614,6 +3875,7 @@ var BNPL_SKIP_REASON = {
3614
3875
  mapToLegacyStatus,
3615
3876
  maxPrecisionFor,
3616
3877
  mergeFragment,
3878
+ modifiersSellable,
3617
3879
  multiplyAmount,
3618
3880
  normalizeMongoIdString,
3619
3881
  occupancyIn,
@@ -3621,9 +3883,14 @@ var BNPL_SKIP_REASON = {
3621
3883
  parseCurrencyCode,
3622
3884
  parseStripeCurrencyCode,
3623
3885
  planWindow,
3886
+ poolDemand,
3887
+ poolOf,
3888
+ poolShortfall,
3889
+ poolStockAt,
3624
3890
  presentAt,
3625
3891
  removeFragment,
3626
3892
  resolveFamily,
3893
+ resolveModifiersForProduct,
3627
3894
  resolveTemplate,
3628
3895
  resolveUnit,
3629
3896
  roundToPrecision,
@@ -3639,7 +3906,6 @@ var BNPL_SKIP_REASON = {
3639
3906
  toMinorUnits,
3640
3907
  validateConfig,
3641
3908
  validateFragmentCompatibility,
3642
- validateOrderStatuses,
3643
3909
  violatesFinancialInvariant,
3644
3910
  windowEndsAt,
3645
3911
  withinPresence