@final-commerce/common 2.5.1-preprod.3 → 2.6.0-staging.1

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.mjs CHANGED
@@ -237,7 +237,6 @@ var InventorySpecificActionType = /* @__PURE__ */ ((InventorySpecificActionType2
237
237
  InventorySpecificActionType2["PO_RECEIVED"] = "PO_RECEIVED";
238
238
  InventorySpecificActionType2["EXTERNAL_SALE"] = "EXTERNAL_SALE";
239
239
  InventorySpecificActionType2["EXTERNAL_RESTOCK"] = "EXTERNAL_RESTOCK";
240
- InventorySpecificActionType2["CUSTOM"] = "CUSTOM";
241
240
  return InventorySpecificActionType2;
242
241
  })(InventorySpecificActionType || {});
243
242
  var ExtensionCategory = /* @__PURE__ */ ((ExtensionCategory2) => {
@@ -424,6 +423,7 @@ var ProductType = /* @__PURE__ */ ((ProductType2) => {
424
423
  ProductType2["SIMPLE"] = "simple";
425
424
  ProductType2["VARIABLE"] = "variable";
426
425
  ProductType2["BOOKING"] = "booking";
426
+ ProductType2["COMPOSITE"] = "composite";
427
427
  return ProductType2;
428
428
  })(ProductType || {});
429
429
  var ProductStatus = /* @__PURE__ */ ((ProductStatus2) => {
@@ -1354,6 +1354,312 @@ function normalizeMongoIdString(value) {
1354
1354
  return null;
1355
1355
  }
1356
1356
 
1357
+ // utils/composite-price.util.ts
1358
+ var compositeItemCost = (item) => item.extraCharge * item.quantity;
1359
+ function cheapestCompositeItem(items) {
1360
+ let cheapest;
1361
+ items.forEach((item, index) => {
1362
+ if (!item.available) return;
1363
+ if (cheapest === void 0 || compositeItemCost(item) < compositeItemCost(items[cheapest])) cheapest = index;
1364
+ });
1365
+ return cheapest;
1366
+ }
1367
+ function cheapestCompositePrice(parts) {
1368
+ let total = 0;
1369
+ for (const part of parts) {
1370
+ if (part.min === 0) continue;
1371
+ const index = cheapestCompositeItem(part.items) ?? cheapestCompositeItem(part.items.map((item) => ({ ...item, available: true })));
1372
+ if (index === void 0) return void 0;
1373
+ total += part.min * compositeItemCost(part.items[index]);
1374
+ }
1375
+ return total;
1376
+ }
1377
+
1378
+ // utils/modifier-resolution.util.ts
1379
+ function ancestorChain(categoryId, categoriesById) {
1380
+ const chain = [];
1381
+ const seen = /* @__PURE__ */ new Set([categoryId]);
1382
+ let current = categoriesById.get(categoryId)?.parentId ?? void 0;
1383
+ while (current && !seen.has(current)) {
1384
+ chain.push(current);
1385
+ seen.add(current);
1386
+ current = categoriesById.get(current)?.parentId ?? void 0;
1387
+ }
1388
+ return chain;
1389
+ }
1390
+ function clampRule(rule) {
1391
+ if (rule.min != null && rule.max != null && rule.min > rule.max) {
1392
+ return { ...rule, min: rule.max };
1393
+ }
1394
+ return rule;
1395
+ }
1396
+ function mergeRules(a, b) {
1397
+ const required = a.required || b.required;
1398
+ const min = a.min == null ? b.min : b.min == null ? a.min : Math.max(a.min, b.min);
1399
+ const max = a.max == null ? b.max : b.max == null ? a.max : Math.min(a.max, b.max);
1400
+ return clampRule({ required, min, max, sortOrder: Math.min(a.sortOrder, b.sortOrder) });
1401
+ }
1402
+ function toRule(row) {
1403
+ const required = row.required ?? false;
1404
+ return clampRule({
1405
+ required,
1406
+ // `required` is the gate: a stale min left on a placement that went optional is
1407
+ // ignored. A cleared min/max stays the explicit null it arrived as — nothing
1408
+ // downstream distinguishes null from absent.
1409
+ min: required ? row.min : void 0,
1410
+ max: row.max,
1411
+ sortOrder: row.sortOrder ?? 0
1412
+ });
1413
+ }
1414
+ function toResolvedChoices(modifier) {
1415
+ return (modifier.choices ?? []).map((choice, index) => ({
1416
+ _id: String(choice._id),
1417
+ name: choice.name,
1418
+ price: choice.price ?? 0,
1419
+ isDefault: choice.isDefault,
1420
+ defaultQuantity: choice.defaultQuantity,
1421
+ unavailableOutletIds: choice.unavailableOutletIds?.map(String),
1422
+ sortOrder: choice.sortOrder ?? index
1423
+ })).sort((a, b) => a.sortOrder - b.sortOrder);
1424
+ }
1425
+ function resolveModifiersForProduct(inputs) {
1426
+ const categoriesById = new Map(
1427
+ inputs.categories.filter((category) => !category.isDeleted).map((category) => [String(category._id), category])
1428
+ );
1429
+ const modifiersById = new Map(
1430
+ inputs.modifiers.filter((modifier) => !modifier.isDeleted).map((modifier) => [String(modifier._id), modifier])
1431
+ );
1432
+ const directCategories = new Set(inputs.categoryIds.map(String));
1433
+ const inheritedCategories = /* @__PURE__ */ new Set();
1434
+ for (const categoryId of directCategories) {
1435
+ for (const ancestor of ancestorChain(categoryId, categoriesById)) {
1436
+ if (!directCategories.has(ancestor)) inheritedCategories.add(ancestor);
1437
+ }
1438
+ }
1439
+ const rules = /* @__PURE__ */ new Map();
1440
+ for (const placement of inputs.categoryModifiers) {
1441
+ if (placement.isDeleted) continue;
1442
+ const categoryId = String(placement.categoryId);
1443
+ const precedence = directCategories.has(categoryId) ? 0 : inheritedCategories.has(categoryId) ? 1 : -1;
1444
+ if (precedence === -1) continue;
1445
+ const modifierId = String(placement.modifierId);
1446
+ if (!modifiersById.has(modifierId)) continue;
1447
+ const incoming = toRule(placement);
1448
+ const existing = rules.get(modifierId);
1449
+ if (!existing || precedence < existing.precedence) {
1450
+ rules.set(modifierId, { precedence, rule: incoming });
1451
+ } else if (precedence === existing.precedence) {
1452
+ rules.set(modifierId, { precedence, rule: mergeRules(existing.rule, incoming) });
1453
+ }
1454
+ }
1455
+ const resolved = [];
1456
+ for (const [modifierId, { rule }] of rules) {
1457
+ const modifier = modifiersById.get(modifierId);
1458
+ if (!modifier) continue;
1459
+ const required = rule.required;
1460
+ resolved.push({
1461
+ _id: modifierId,
1462
+ name: modifier.name,
1463
+ selectionType: modifier.selectionType,
1464
+ allowQuantity: modifier.allowQuantity,
1465
+ required,
1466
+ // single-selection is implicitly capped at 1 — min/max are never carried.
1467
+ min: modifier.selectionType === "single" ? void 0 : rule.min,
1468
+ max: modifier.selectionType === "single" ? void 0 : rule.max,
1469
+ sortOrder: rule.sortOrder,
1470
+ choices: toResolvedChoices(modifier)
1471
+ });
1472
+ }
1473
+ return resolved.sort((a, b) => a.sortOrder - b.sortOrder || a.name.localeCompare(b.name));
1474
+ }
1475
+ function isChoiceAvailableAtOutlet(choice, outletId) {
1476
+ return !(choice.unavailableOutletIds ?? []).includes(outletId);
1477
+ }
1478
+ function hasSellableChoice(modifier, outletId) {
1479
+ return modifier.choices.some((choice) => isChoiceAvailableAtOutlet(choice, outletId));
1480
+ }
1481
+ var modifiersSellable = (modifiers, outletId) => modifiers.every((modifier) => !modifier.required || hasSellableChoice(modifier, outletId));
1482
+
1483
+ // utils/composite-availability.util.ts
1484
+ function poolStockAt(rows, outletId) {
1485
+ if (!rows.some((row) => row.manageStock === true)) return null;
1486
+ return Math.max(0, rows.find((row) => String(row.outletId) === outletId)?.quantity ?? 0);
1487
+ }
1488
+ function compositeVariantInStock(variantId, quantity, outlet) {
1489
+ const shelf = outlet.shelves.get(variantId);
1490
+ if (!shelf || shelf.unresolved) return false;
1491
+ const onHand = outlet.poolStock.get(shelf.pool);
1492
+ if (onHand === void 0) return false;
1493
+ if (onHand === null || shelf.backorder) return true;
1494
+ return onHand >= (shelf.unit ? toBase(quantity, shelf.unit) : quantity);
1495
+ }
1496
+ function compositeItemUnavailable(item, rows) {
1497
+ const { outlet } = rows;
1498
+ const sellableHere = (productId2) => !outlet || !outlet.hiddenProductIds.has(productId2) && !outlet.unsellableProductIds.has(productId2);
1499
+ if (item.categoryId) {
1500
+ const variants = rows.categoryVariants.get(String(item.categoryId));
1501
+ if (!variants) return "deleted";
1502
+ const offered = variants.filter((variantId2) => sellableHere(rows.variantProducts.get(variantId2) ?? ""));
1503
+ if (!offered.length) return "empty";
1504
+ if (!outlet) return null;
1505
+ const unmarked = offered.filter((variantId2) => !outlet.markedVariantIds.has(variantId2));
1506
+ if (!unmarked.length) return "marked_sold_out";
1507
+ return unmarked.some((variantId2) => compositeVariantInStock(variantId2, item.quantity, outlet)) ? null : "out_of_stock";
1508
+ }
1509
+ const variantId = String(item.variantId);
1510
+ const productId = rows.variantProducts.get(variantId);
1511
+ if (!productId || !rows.productStatuses.has(productId)) return "deleted";
1512
+ if (rows.productStatuses.get(productId) !== "active" /* ACTIVE */) return "inactive";
1513
+ if (!outlet) return null;
1514
+ if (outlet.hiddenProductIds.has(productId)) return "hidden";
1515
+ if (outlet.unsellableProductIds.has(productId)) return "empty";
1516
+ if (outlet.markedVariantIds.has(variantId)) return "marked_sold_out";
1517
+ if (rows.bookableProductIds.has(productId)) {
1518
+ if (!outlet.bookableSeats) return "by_date";
1519
+ return (outlet.bookableSeats.get(productId) ?? 0) >= item.quantity ? null : "fully_booked";
1520
+ }
1521
+ return compositeVariantInStock(variantId, item.quantity, outlet) ? null : "out_of_stock";
1522
+ }
1523
+ function compositeMakeable(parts, rows) {
1524
+ const { outlet } = rows;
1525
+ if (!outlet) throw new Error("compositeMakeable counts at one outlet: rows.outlet is required");
1526
+ const required = parts.map((part, partIndex) => ({ ...part, partIndex })).filter((part) => part.min > 0);
1527
+ const isBookable = (variantId) => rows.bookableProductIds.has(rows.variantProducts.get(String(variantId)) ?? "");
1528
+ if (required.some((part) => part.items.some((item) => item.variantId && isBookable(item.variantId))))
1529
+ return { byDate: true };
1530
+ const optionsOf = (item) => (item.categoryId ? rows.categoryVariants.get(String(item.categoryId)) ?? [] : [String(item.variantId)]).flatMap(
1531
+ (variantId) => {
1532
+ const reason = compositeItemUnavailable({ variantId, quantity: item.quantity }, rows);
1533
+ const shelf = outlet.shelves.get(variantId);
1534
+ if (reason && reason !== "out_of_stock" || !shelf) return [];
1535
+ const stock = outlet.poolStock.get(shelf.pool);
1536
+ const onHand = shelf.unresolved || stock === void 0 ? 0 : stock === null || shelf.backorder ? null : stock;
1537
+ const perPick = shelf.unit ? toBase(item.quantity, shelf.unit) : item.quantity;
1538
+ return [{ itemId: item.id, pool: shelf.pool, perPick, onHand }];
1539
+ }
1540
+ );
1541
+ const counted = required.map((part) => ({ ...part, options: part.items.flatMap(optionsOf) }));
1542
+ const order = [...counted].sort((a, b) => Number(a.options.length !== 1) - Number(b.options.length !== 1));
1543
+ const picksLeft = (option, reserved) => option.onHand === null ? Infinity : Math.max(0, Math.floor((option.onHand - (reserved.get(option.pool) ?? 0)) / option.perPick));
1544
+ const shortAt = (n) => {
1545
+ const reserved = /* @__PURE__ */ new Map();
1546
+ for (const part of order) {
1547
+ let wanted = part.min * n;
1548
+ const ranked = [...part.options].sort((a, b) => picksLeft(b, reserved) - picksLeft(a, reserved));
1549
+ for (const option of ranked) {
1550
+ if (wanted <= 0) break;
1551
+ const taken = Math.min(wanted, picksLeft(option, reserved));
1552
+ if (option.onHand !== null)
1553
+ reserved.set(option.pool, (reserved.get(option.pool) ?? 0) + taken * option.perPick);
1554
+ wanted -= taken;
1555
+ }
1556
+ if (wanted > 0) return part.partIndex;
1557
+ }
1558
+ return void 0;
1559
+ };
1560
+ const whyRows = [];
1561
+ const rowPools = /* @__PURE__ */ new Map();
1562
+ const makes = (has, uses) => has === null ? null : Math.floor(has / uses);
1563
+ for (const part of counted) {
1564
+ const [option] = part.options;
1565
+ if (part.options.length === 1) {
1566
+ const uses = option.perPick * part.min;
1567
+ const row = whyRows.find((candidate) => rowPools.get(candidate) === option.pool);
1568
+ if (!row) {
1569
+ const created = { itemIds: [option.itemId], uses, has: option.onHand, makes: makes(option.onHand, uses) };
1570
+ rowPools.set(created, option.pool);
1571
+ whyRows.push(created);
1572
+ continue;
1573
+ }
1574
+ row.itemIds.push(option.itemId);
1575
+ row.uses += uses;
1576
+ if (option.onHand === null) row.has = null;
1577
+ row.makes = makes(row.has, row.uses);
1578
+ continue;
1579
+ }
1580
+ const perPool = /* @__PURE__ */ new Map();
1581
+ for (const candidate of part.options)
1582
+ perPool.set(candidate.pool, Math.max(perPool.get(candidate.pool) ?? 0, picksLeft(candidate, /* @__PURE__ */ new Map())));
1583
+ const total = [...perPool.values()].reduce((sum, picks) => sum + picks, 0);
1584
+ const has = total === Infinity ? null : total;
1585
+ whyRows.push({
1586
+ itemIds: part.items.map((item) => item.id),
1587
+ partIndex: part.partIndex,
1588
+ uses: part.min,
1589
+ has,
1590
+ makes: makes(has, part.min)
1591
+ });
1592
+ }
1593
+ if (counted.every((part) => part.options.some((option) => option.onHand === null)))
1594
+ return { count: null, rows: whyRows };
1595
+ const ceiling = Math.min(
1596
+ ...counted.filter((part) => !part.options.some((option) => option.onHand === null)).map(
1597
+ (part) => Math.floor(part.options.reduce((sum, option) => sum + picksLeft(option, /* @__PURE__ */ new Map()), 0) / part.min)
1598
+ )
1599
+ );
1600
+ let [low, high] = [0, ceiling];
1601
+ while (low < high) {
1602
+ const mid = Math.ceil((low + high) / 2);
1603
+ if (shortAt(mid) === void 0) low = mid;
1604
+ else high = mid - 1;
1605
+ }
1606
+ const short = shortAt(low + 1);
1607
+ const shortPart = counted.find((part) => part.partIndex === short);
1608
+ const limitedBy = whyRows.findIndex(
1609
+ (row) => shortPart?.options.length === 1 ? row.partIndex === void 0 && row.itemIds.includes(shortPart.options[0].itemId) : row.partIndex === short
1610
+ );
1611
+ return { count: low, rows: whyRows, ...limitedBy >= 0 ? { limitedBy } : {} };
1612
+ }
1613
+ var poolOf = (line) => line.stockVariantId || line.variantId;
1614
+ function poolDemand(lines) {
1615
+ const demand = /* @__PURE__ */ new Map();
1616
+ const add = (line, times) => demand.set(
1617
+ poolOf(line),
1618
+ (demand.get(poolOf(line)) ?? 0) + (line.unit ? toBase(line.quantity * times, line.unit) : line.quantity * times)
1619
+ );
1620
+ for (const line of lines) {
1621
+ if (line.components) for (const component of line.components) add(component, line.quantity);
1622
+ else add(line, 1);
1623
+ }
1624
+ return demand;
1625
+ }
1626
+ function poolShortfall(demand, poolStock, backordered = /* @__PURE__ */ new Set()) {
1627
+ for (const [poolId, wanted] of demand) {
1628
+ const available = poolStock.get(poolId);
1629
+ if (available === null || available === void 0 || backordered.has(poolId)) continue;
1630
+ if (wanted > available) return { poolId, available, wanted };
1631
+ }
1632
+ return null;
1633
+ }
1634
+
1635
+ // utils/composite-reservation.util.ts
1636
+ function compositeModifierMismatches(lineItems) {
1637
+ return lineItems.flatMap(
1638
+ (line) => line.components?.length ? (line.modifiers ?? []).flatMap(
1639
+ (modifier) => modifier.componentIndex !== void 0 && line.components[modifier.componentIndex] !== void 0 ? [] : [
1640
+ `Modifier ${modifier.modifierName ?? "?"} on composite line ${line.internalId ?? "?"} names no component (componentIndex ${modifier.componentIndex}) \u2014 a composite has no modifiers of its own`
1641
+ ]
1642
+ ) : []
1643
+ );
1644
+ }
1645
+ function compositeReservationMismatches(order) {
1646
+ return (order.reservations ?? []).flatMap((reservation) => {
1647
+ if (reservation.lineItemInternalId === void 0) return [];
1648
+ const name = `Booking ${String(reservation.bookingId ?? "?")}`;
1649
+ const line = order.lineItems?.find((candidate) => candidate.internalId === reservation.lineItemInternalId);
1650
+ if (!line) return [`${name} names line ${reservation.lineItemInternalId}, which is not on the order`];
1651
+ const component = reservation.componentIndex === void 0 ? void 0 : line.components?.[reservation.componentIndex];
1652
+ if (!component) return [`${name} names no component ${reservation.componentIndex} of line ${line.internalId}`];
1653
+ if (String(component.productId) !== String(reservation.productId))
1654
+ return [
1655
+ `${name} is for ${String(reservation.productId)}, but that component sold ${String(component.productId)}`
1656
+ ];
1657
+ if (reservation.price !== 0 || reservation.total !== 0)
1658
+ return [`${name} carries money (${reservation.price}/${reservation.total}); its component already does`];
1659
+ return [];
1660
+ });
1661
+ }
1662
+
1357
1663
  // units/family-code.ts
1358
1664
  var MAX_SLUG_LENGTH = 32;
1359
1665
  var FALLBACK_SLUG = "family";
@@ -2492,51 +2798,6 @@ function inferStatesFromLegacyStatus(order) {
2492
2798
  return { payment: "unpaid", fulfillment: "pending" };
2493
2799
  }
2494
2800
 
2495
- // order-state-machine/in-cart.ts
2496
- function hasInCartFlag(order) {
2497
- return order.inCart != null;
2498
- }
2499
- function isInCart(order) {
2500
- if (order.inCart != null) return order.inCart.active === true;
2501
- if (order.fulfillmentState) return order.fulfillmentState === "draft";
2502
- return (order.status ?? "").trim().toLowerCase() === "in-cart";
2503
- }
2504
-
2505
- // order-state-machine/order-status.ts
2506
- function validateOrderStatuses(statuses) {
2507
- if (!Array.isArray(statuses)) {
2508
- return [{ index: -1, field: "orderStatuses", message: "must be an array" }];
2509
- }
2510
- const errors = [];
2511
- const seen = /* @__PURE__ */ new Set();
2512
- statuses.forEach((raw, index) => {
2513
- const s = raw ?? {};
2514
- const add = (field, message) => errors.push({ index, field, message });
2515
- if (typeof s.id !== "string" || !s.id.trim()) add("id", "must be a non-empty string");
2516
- else if (seen.has(s.id)) add("id", `duplicate id "${s.id}"`);
2517
- else seen.add(s.id);
2518
- if (typeof s.label !== "string" || !s.label.trim()) add("label", "must be a non-empty string");
2519
- for (const field of ["color", "icon"]) {
2520
- if (s[field] !== void 0 && typeof s[field] !== "string") add(field, "must be a string");
2521
- }
2522
- if (s.fulfillmentState !== void 0 && !(typeof s.fulfillmentState === "string" && isFulfillmentState(s.fulfillmentState))) {
2523
- add("fulfillmentState", `"${String(s.fulfillmentState)}" is not a fulfillment state`);
2524
- }
2525
- if (s.requiresPaymentState !== void 0) {
2526
- if (!Array.isArray(s.requiresPaymentState) || s.requiresPaymentState.length === 0) {
2527
- add("requiresPaymentState", "must be a non-empty array of payment states");
2528
- } else {
2529
- for (const p of s.requiresPaymentState) {
2530
- if (!(typeof p === "string" && isPaymentState(p))) {
2531
- add("requiresPaymentState", `"${String(p)}" is not a payment state`);
2532
- }
2533
- }
2534
- }
2535
- }
2536
- });
2537
- return errors;
2538
- }
2539
-
2540
2801
  // order-state-machine/write-strategy.ts
2541
2802
  var LegacyWriteStrategy = class {
2542
2803
  applyState(_order) {
@@ -3048,7 +3309,9 @@ function buildGrid(rule, timeZone, from, to, now, busy = []) {
3048
3309
  const start = startAt.getTime();
3049
3310
  if (start < askedFrom || start >= askedTo) return false;
3050
3311
  if (start < notBefore || start > notAfter) return false;
3051
- return !blocked.some(({ from: blockFrom, to: blockTo }) => start < blockTo && bufferEndAt.getTime() > blockFrom);
3312
+ return !blocked.some(
3313
+ ({ from: blockFrom, to: blockTo }) => start < blockTo && bufferEndAt.getTime() > blockFrom
3314
+ );
3052
3315
  }).sort((first, second) => +first.startAt - +second.startAt);
3053
3316
  }
3054
3317
  function slotsForWindow(grid, startAt, endAt) {
@@ -3167,7 +3430,14 @@ function buildAvailability({
3167
3430
  for (const slot of slots2) {
3168
3431
  const { taken, foreignProductId } = occupancyIn(mine, productId, slot.startAt, slot.bufferEndAt, shared);
3169
3432
  const free = foreignProductId ? 0 : Math.max(capacity - taken, 0);
3170
- const entry = byInstant.get(+slot.startAt) ?? { ...slot, capacity: 0, booked: 0, free: 0, resources: [], dayKey: shopDayKey(slot.startAt, resource.timeZone) };
3433
+ const entry = byInstant.get(+slot.startAt) ?? {
3434
+ ...slot,
3435
+ capacity: 0,
3436
+ booked: 0,
3437
+ free: 0,
3438
+ resources: [],
3439
+ dayKey: shopDayKey(slot.startAt, resource.timeZone)
3440
+ };
3171
3441
  entry.capacity += capacity;
3172
3442
  entry.booked += foreignProductId ? capacity : taken;
3173
3443
  entry.free += free;
@@ -3245,13 +3515,8 @@ var ADYEN_ROLLOUT_COUNTRY_CODES = /* @__PURE__ */ new Set(["CA"]);
3245
3515
  function isAdyenRolloutCountry(alpha2) {
3246
3516
  return ADYEN_ROLLOUT_COUNTRY_CODES.has(alpha2);
3247
3517
  }
3248
- var AFFIRM_PRICE_PLANS = ["core", "standard", "signature"];
3249
- function isAffirmPricePlan(value) {
3250
- return typeof value === "string" && AFFIRM_PRICE_PLANS.includes(value);
3251
- }
3252
3518
  export {
3253
3519
  ADYEN_ROLLOUT_COUNTRY_CODES,
3254
- AFFIRM_PRICE_PLANS,
3255
3520
  AttributeType,
3256
3521
  BookingRatePeriod,
3257
3522
  BookingResourceKind,
@@ -3330,6 +3595,7 @@ export {
3330
3595
  UserTypes,
3331
3596
  addAmounts,
3332
3597
  amountToString,
3598
+ ancestorChain,
3333
3599
  assertUnitSane,
3334
3600
  availableIn,
3335
3601
  baseFor,
@@ -3343,6 +3609,14 @@ export {
3343
3609
  buildResolvedConfig,
3344
3610
  calculatePercentage,
3345
3611
  canTransition,
3612
+ cheapestCompositeItem,
3613
+ cheapestCompositePrice,
3614
+ compositeItemCost,
3615
+ compositeItemUnavailable,
3616
+ compositeMakeable,
3617
+ compositeModifierMismatches,
3618
+ compositeReservationMismatches,
3619
+ compositeVariantInStock,
3346
3620
  conditionSummary,
3347
3621
  convertFixedFeeToLocalCurrency,
3348
3622
  createAmount,
@@ -3377,14 +3651,13 @@ export {
3377
3651
  getPricingRegionFromCountry,
3378
3652
  getSettlementTotal,
3379
3653
  getTotalCollected,
3380
- hasInCartFlag,
3654
+ hasSellableChoice,
3381
3655
  holdExpiresAt,
3382
3656
  inferStatesFromLegacyStatus,
3383
3657
  isAdyenRolloutCountry,
3384
- isAffirmPricePlan,
3658
+ isChoiceAvailableAtOutlet,
3385
3659
  isConditionOperator,
3386
3660
  isFulfillmentState,
3387
- isInCart,
3388
3661
  isPaymentState,
3389
3662
  isSupportedCurrency,
3390
3663
  isSystemFamilyCode,
@@ -3393,6 +3666,7 @@ export {
3393
3666
  mapToLegacyStatus,
3394
3667
  maxPrecisionFor,
3395
3668
  mergeFragment,
3669
+ modifiersSellable,
3396
3670
  multiplyAmount,
3397
3671
  normalizeMongoIdString,
3398
3672
  occupancyIn,
@@ -3400,9 +3674,14 @@ export {
3400
3674
  parseCurrencyCode,
3401
3675
  parseStripeCurrencyCode,
3402
3676
  planWindow,
3677
+ poolDemand,
3678
+ poolOf,
3679
+ poolShortfall,
3680
+ poolStockAt,
3403
3681
  presentAt,
3404
3682
  removeFragment,
3405
3683
  resolveFamily,
3684
+ resolveModifiersForProduct,
3406
3685
  resolveTemplate,
3407
3686
  resolveUnit,
3408
3687
  roundToPrecision,
@@ -3418,7 +3697,6 @@ export {
3418
3697
  toMinorUnits,
3419
3698
  validateConfig,
3420
3699
  validateFragmentCompatibility,
3421
- validateOrderStatuses,
3422
3700
  violatesFinancialInvariant,
3423
3701
  windowEndsAt,
3424
3702
  withinPresence