@final-commerce/common 2.4.1-staging2.1 → 2.5.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
@@ -422,6 +422,8 @@ var StripeCurrencyCode = /* @__PURE__ */ ((StripeCurrencyCode2) => {
422
422
  var ProductType = /* @__PURE__ */ ((ProductType2) => {
423
423
  ProductType2["SIMPLE"] = "simple";
424
424
  ProductType2["VARIABLE"] = "variable";
425
+ ProductType2["BOOKING"] = "booking";
426
+ ProductType2["COMPOSITE"] = "composite";
425
427
  return ProductType2;
426
428
  })(ProductType || {});
427
429
  var ProductStatus = /* @__PURE__ */ ((ProductStatus2) => {
@@ -439,6 +441,50 @@ var PaymentProcessor = /* @__PURE__ */ ((PaymentProcessor2) => {
439
441
  return PaymentProcessor2;
440
442
  })(PaymentProcessor || {});
441
443
 
444
+ // enums/booking.enum.ts
445
+ var BookingType = /* @__PURE__ */ ((BookingType3) => {
446
+ BookingType3["RENTAL"] = "rental";
447
+ BookingType3["APPOINTMENT"] = "appointment";
448
+ BookingType3["OVERNIGHT"] = "overnight";
449
+ BookingType3["GROUP"] = "group";
450
+ return BookingType3;
451
+ })(BookingType || {});
452
+ var BookingRatePeriod = /* @__PURE__ */ ((BookingRatePeriod2) => {
453
+ BookingRatePeriod2["SLOT"] = "slot";
454
+ BookingRatePeriod2["DAY"] = "day";
455
+ BookingRatePeriod2["NIGHT"] = "night";
456
+ BookingRatePeriod2["WEEK"] = "week";
457
+ BookingRatePeriod2["MONTH"] = "month";
458
+ return BookingRatePeriod2;
459
+ })(BookingRatePeriod || {});
460
+ var BookingResourceKind = /* @__PURE__ */ ((BookingResourceKind2) => {
461
+ BookingResourceKind2["ASSET"] = "asset";
462
+ BookingResourceKind2["ROOM"] = "room";
463
+ BookingResourceKind2["PROPERTY"] = "property";
464
+ BookingResourceKind2["SPACE"] = "space";
465
+ BookingResourceKind2["STAFF"] = "staff";
466
+ BookingResourceKind2["TABLE"] = "table";
467
+ return BookingResourceKind2;
468
+ })(BookingResourceKind || {});
469
+ var ReservationStatus = /* @__PURE__ */ ((ReservationStatus2) => {
470
+ ReservationStatus2["HELD"] = "held";
471
+ ReservationStatus2["CONFIRMED"] = "confirmed";
472
+ ReservationStatus2["CANCELLED"] = "cancelled";
473
+ ReservationStatus2["EXPIRED"] = "expired";
474
+ return ReservationStatus2;
475
+ })(ReservationStatus || {});
476
+ var BookingResourceStatus = /* @__PURE__ */ ((BookingResourceStatus2) => {
477
+ BookingResourceStatus2["ACTIVE"] = "active";
478
+ BookingResourceStatus2["INACTIVE"] = "inactive";
479
+ return BookingResourceStatus2;
480
+ })(BookingResourceStatus || {});
481
+ var ResourceAvailabilityState = /* @__PURE__ */ ((ResourceAvailabilityState2) => {
482
+ ResourceAvailabilityState2["AVAILABLE"] = "available";
483
+ ResourceAvailabilityState2["FULLY_BOOKED"] = "fullyBooked";
484
+ ResourceAvailabilityState2["NO_HOURS_IN_RANGE"] = "noHoursInRange";
485
+ return ResourceAvailabilityState2;
486
+ })(ResourceAvailabilityState || {});
487
+
442
488
  // utils/currency.util.ts
443
489
  var CURRENCY_CONFIG = {
444
490
  // Major currencies with 2 decimal places
@@ -1308,6 +1354,309 @@ function normalizeMongoIdString(value) {
1308
1354
  return null;
1309
1355
  }
1310
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
+ return offered.some((variantId2) => compositeVariantInStock(variantId2, item.quantity, outlet)) ? null : "out_of_stock";
1506
+ }
1507
+ const variantId = String(item.variantId);
1508
+ const productId = rows.variantProducts.get(variantId);
1509
+ if (!productId || !rows.productStatuses.has(productId)) return "deleted";
1510
+ if (rows.productStatuses.get(productId) !== "active" /* ACTIVE */) return "inactive";
1511
+ if (!outlet) return null;
1512
+ if (outlet.hiddenProductIds.has(productId)) return "hidden";
1513
+ if (outlet.unsellableProductIds.has(productId)) return "empty";
1514
+ if (rows.bookableProductIds.has(productId)) {
1515
+ if (!outlet.bookableSeats) return "by_date";
1516
+ return (outlet.bookableSeats.get(productId) ?? 0) >= item.quantity ? null : "fully_booked";
1517
+ }
1518
+ return compositeVariantInStock(variantId, item.quantity, outlet) ? null : "out_of_stock";
1519
+ }
1520
+ function compositeMakeable(parts, rows) {
1521
+ const { outlet } = rows;
1522
+ if (!outlet) throw new Error("compositeMakeable counts at one outlet: rows.outlet is required");
1523
+ const required = parts.map((part, partIndex) => ({ ...part, partIndex })).filter((part) => part.min > 0);
1524
+ const isBookable = (variantId) => rows.bookableProductIds.has(rows.variantProducts.get(String(variantId)) ?? "");
1525
+ if (required.some((part) => part.items.some((item) => item.variantId && isBookable(item.variantId))))
1526
+ return { byDate: true };
1527
+ const optionsOf = (item) => (item.categoryId ? rows.categoryVariants.get(String(item.categoryId)) ?? [] : [String(item.variantId)]).flatMap(
1528
+ (variantId) => {
1529
+ const reason = compositeItemUnavailable({ variantId, quantity: item.quantity }, rows);
1530
+ const shelf = outlet.shelves.get(variantId);
1531
+ if (reason && reason !== "out_of_stock" || !shelf) return [];
1532
+ const stock = outlet.poolStock.get(shelf.pool);
1533
+ const onHand = shelf.unresolved || stock === void 0 ? 0 : stock === null || shelf.backorder ? null : stock;
1534
+ const perPick = shelf.unit ? toBase(item.quantity, shelf.unit) : item.quantity;
1535
+ return [{ itemId: item.id, pool: shelf.pool, perPick, onHand }];
1536
+ }
1537
+ );
1538
+ const counted = required.map((part) => ({ ...part, options: part.items.flatMap(optionsOf) }));
1539
+ const order = [...counted].sort((a, b) => Number(a.options.length !== 1) - Number(b.options.length !== 1));
1540
+ const picksLeft = (option, reserved) => option.onHand === null ? Infinity : Math.max(0, Math.floor((option.onHand - (reserved.get(option.pool) ?? 0)) / option.perPick));
1541
+ const shortAt = (n) => {
1542
+ const reserved = /* @__PURE__ */ new Map();
1543
+ for (const part of order) {
1544
+ let wanted = part.min * n;
1545
+ const ranked = [...part.options].sort((a, b) => picksLeft(b, reserved) - picksLeft(a, reserved));
1546
+ for (const option of ranked) {
1547
+ if (wanted <= 0) break;
1548
+ const taken = Math.min(wanted, picksLeft(option, reserved));
1549
+ if (option.onHand !== null)
1550
+ reserved.set(option.pool, (reserved.get(option.pool) ?? 0) + taken * option.perPick);
1551
+ wanted -= taken;
1552
+ }
1553
+ if (wanted > 0) return part.partIndex;
1554
+ }
1555
+ return void 0;
1556
+ };
1557
+ const whyRows = [];
1558
+ const rowPools = /* @__PURE__ */ new Map();
1559
+ const makes = (has, uses) => has === null ? null : Math.floor(has / uses);
1560
+ for (const part of counted) {
1561
+ const [option] = part.options;
1562
+ if (part.options.length === 1) {
1563
+ const uses = option.perPick * part.min;
1564
+ const row = whyRows.find((candidate) => rowPools.get(candidate) === option.pool);
1565
+ if (!row) {
1566
+ const created = { itemIds: [option.itemId], uses, has: option.onHand, makes: makes(option.onHand, uses) };
1567
+ rowPools.set(created, option.pool);
1568
+ whyRows.push(created);
1569
+ continue;
1570
+ }
1571
+ row.itemIds.push(option.itemId);
1572
+ row.uses += uses;
1573
+ if (option.onHand === null) row.has = null;
1574
+ row.makes = makes(row.has, row.uses);
1575
+ continue;
1576
+ }
1577
+ const perPool = /* @__PURE__ */ new Map();
1578
+ for (const candidate of part.options)
1579
+ perPool.set(candidate.pool, Math.max(perPool.get(candidate.pool) ?? 0, picksLeft(candidate, /* @__PURE__ */ new Map())));
1580
+ const total = [...perPool.values()].reduce((sum, picks) => sum + picks, 0);
1581
+ const has = total === Infinity ? null : total;
1582
+ whyRows.push({
1583
+ itemIds: part.items.map((item) => item.id),
1584
+ partIndex: part.partIndex,
1585
+ uses: part.min,
1586
+ has,
1587
+ makes: makes(has, part.min)
1588
+ });
1589
+ }
1590
+ if (counted.every((part) => part.options.some((option) => option.onHand === null)))
1591
+ return { count: null, rows: whyRows };
1592
+ const ceiling = Math.min(
1593
+ ...counted.filter((part) => !part.options.some((option) => option.onHand === null)).map(
1594
+ (part) => Math.floor(part.options.reduce((sum, option) => sum + picksLeft(option, /* @__PURE__ */ new Map()), 0) / part.min)
1595
+ )
1596
+ );
1597
+ let [low, high] = [0, ceiling];
1598
+ while (low < high) {
1599
+ const mid = Math.ceil((low + high) / 2);
1600
+ if (shortAt(mid) === void 0) low = mid;
1601
+ else high = mid - 1;
1602
+ }
1603
+ const short = shortAt(low + 1);
1604
+ const shortPart = counted.find((part) => part.partIndex === short);
1605
+ const limitedBy = whyRows.findIndex(
1606
+ (row) => shortPart?.options.length === 1 ? row.partIndex === void 0 && row.itemIds.includes(shortPart.options[0].itemId) : row.partIndex === short
1607
+ );
1608
+ return { count: low, rows: whyRows, ...limitedBy >= 0 ? { limitedBy } : {} };
1609
+ }
1610
+ var poolOf = (line) => line.stockVariantId || line.variantId;
1611
+ function poolDemand(lines) {
1612
+ const demand = /* @__PURE__ */ new Map();
1613
+ const add = (line, times) => demand.set(
1614
+ poolOf(line),
1615
+ (demand.get(poolOf(line)) ?? 0) + (line.unit ? toBase(line.quantity * times, line.unit) : line.quantity * times)
1616
+ );
1617
+ for (const line of lines) {
1618
+ if (line.components) for (const component of line.components) add(component, line.quantity);
1619
+ else add(line, 1);
1620
+ }
1621
+ return demand;
1622
+ }
1623
+ function poolShortfall(demand, poolStock, backordered = /* @__PURE__ */ new Set()) {
1624
+ for (const [poolId, wanted] of demand) {
1625
+ const available = poolStock.get(poolId);
1626
+ if (available === null || available === void 0 || backordered.has(poolId)) continue;
1627
+ if (wanted > available) return { poolId, available, wanted };
1628
+ }
1629
+ return null;
1630
+ }
1631
+
1632
+ // utils/composite-reservation.util.ts
1633
+ function compositeModifierMismatches(lineItems) {
1634
+ return lineItems.flatMap(
1635
+ (line) => line.components?.length ? (line.modifiers ?? []).flatMap(
1636
+ (modifier) => modifier.componentIndex !== void 0 && line.components[modifier.componentIndex] !== void 0 ? [] : [
1637
+ `Modifier ${modifier.modifierName ?? "?"} on composite line ${line.internalId ?? "?"} names no component (componentIndex ${modifier.componentIndex}) \u2014 a composite has no modifiers of its own`
1638
+ ]
1639
+ ) : []
1640
+ );
1641
+ }
1642
+ function compositeReservationMismatches(order) {
1643
+ return (order.reservations ?? []).flatMap((reservation) => {
1644
+ if (reservation.lineItemInternalId === void 0) return [];
1645
+ const name = `Booking ${String(reservation.bookingId ?? "?")}`;
1646
+ const line = order.lineItems?.find((candidate) => candidate.internalId === reservation.lineItemInternalId);
1647
+ if (!line) return [`${name} names line ${reservation.lineItemInternalId}, which is not on the order`];
1648
+ const component = reservation.componentIndex === void 0 ? void 0 : line.components?.[reservation.componentIndex];
1649
+ if (!component) return [`${name} names no component ${reservation.componentIndex} of line ${line.internalId}`];
1650
+ if (String(component.productId) !== String(reservation.productId))
1651
+ return [
1652
+ `${name} is for ${String(reservation.productId)}, but that component sold ${String(component.productId)}`
1653
+ ];
1654
+ if (reservation.price !== 0 || reservation.total !== 0)
1655
+ return [`${name} carries money (${reservation.price}/${reservation.total}); its component already does`];
1656
+ return [];
1657
+ });
1658
+ }
1659
+
1311
1660
  // units/family-code.ts
1312
1661
  var MAX_SLUG_LENGTH = 32;
1313
1662
  var FALLBACK_SLUG = "family";
@@ -1650,6 +1999,15 @@ function sumRefundQuantities(refunds) {
1650
1999
  }
1651
2000
  return t;
1652
2001
  }
2002
+ var asCount = (value) => typeof value === "number" && Number.isFinite(value) ? Math.max(0, value) : 0;
2003
+ function countsOf(row) {
2004
+ const quantity = asCount(row.quantity);
2005
+ return {
2006
+ quantity,
2007
+ fulfilled: Math.min(asCount(row.fulfilledQuantity), quantity),
2008
+ returned: Math.min(asCount(row.returnedQuantity), quantity)
2009
+ };
2010
+ }
1653
2011
  function buildOrderContext(order, extras) {
1654
2012
  const o = order ?? {};
1655
2013
  const total = typeof o.total === "number" && Number.isFinite(o.total) ? o.total : 0;
@@ -1657,6 +2015,7 @@ function buildOrderContext(order, extras) {
1657
2015
  const paymentMethods = o.paymentMethods ?? [];
1658
2016
  const refunds = o.refunds ?? o.refund ?? [];
1659
2017
  const lineItems = o.lineItems ?? [];
2018
+ const reservations = o.reservations ?? [];
1660
2019
  const totalPaid = sumNumbers(paymentMethods);
1661
2020
  const totalRefunded = sumRefundQuantities(refunds);
1662
2021
  const isFullyPaid = total > 0 ? balance <= 0 : totalPaid > 0 && balance <= 0;
@@ -1664,13 +2023,11 @@ function buildOrderContext(order, extras) {
1664
2023
  let itemCount = 0;
1665
2024
  let fulfilledItemCount = 0;
1666
2025
  let returnedItemCount = 0;
1667
- for (const li of lineItems) {
1668
- const q = typeof li.quantity === "number" && Number.isFinite(li.quantity) ? Math.max(0, li.quantity) : 0;
1669
- itemCount += q;
1670
- const fq = typeof li.fulfilledQuantity === "number" && Number.isFinite(li.fulfilledQuantity) ? Math.max(0, li.fulfilledQuantity) : 0;
1671
- fulfilledItemCount += Math.min(fq, q);
1672
- const rq = typeof li.returnedQuantity === "number" && Number.isFinite(li.returnedQuantity) ? Math.max(0, li.returnedQuantity) : 0;
1673
- returnedItemCount += Math.min(rq, q);
2026
+ for (const row of [...lineItems, ...reservations]) {
2027
+ const { quantity, fulfilled, returned } = countsOf(row);
2028
+ itemCount += quantity;
2029
+ fulfilledItemCount += fulfilled;
2030
+ returnedItemCount += returned;
1674
2031
  }
1675
2032
  const isFullyReturned = itemCount > 0 ? returnedItemCount >= itemCount : returnedItemCount > 0;
1676
2033
  const hasPartialReturns = returnedItemCount > 0 && !isFullyReturned;
@@ -1680,7 +2037,8 @@ function buildOrderContext(order, extras) {
1680
2037
  balance,
1681
2038
  paymentMethods,
1682
2039
  refunds,
1683
- lineItems
2040
+ lineItems,
2041
+ reservations
1684
2042
  },
1685
2043
  computed: {
1686
2044
  balance,
@@ -2760,43 +3118,408 @@ var ORDER_STATE_EVENTS = {
2760
3118
  configUpdated: "config-updated"
2761
3119
  };
2762
3120
 
2763
- // constants/adyen.constants.ts
2764
- var ADYEN_ROLLOUT_COUNTRY_CODES = /* @__PURE__ */ new Set(["CA"]);
2765
- function isAdyenRolloutCountry(alpha2) {
2766
- return ADYEN_ROLLOUT_COUNTRY_CODES.has(alpha2);
3121
+ // booking/types.ts
3122
+ var BookingRulesError = class extends Error {
3123
+ constructor(message) {
3124
+ super(message);
3125
+ this.name = "BookingRulesError";
3126
+ }
3127
+ };
3128
+
3129
+ // booking/zoned-time.ts
3130
+ var MINUTE = 6e4;
3131
+ var formatters = /* @__PURE__ */ new Map();
3132
+ var formatterFor = (timeZone) => {
3133
+ const existing = formatters.get(timeZone);
3134
+ if (existing) return existing;
3135
+ let created;
3136
+ try {
3137
+ created = new Intl.DateTimeFormat("en-US", {
3138
+ timeZone,
3139
+ hour12: false,
3140
+ year: "numeric",
3141
+ month: "2-digit",
3142
+ day: "2-digit",
3143
+ hour: "2-digit",
3144
+ minute: "2-digit",
3145
+ second: "2-digit",
3146
+ weekday: "short"
3147
+ });
3148
+ } catch {
3149
+ throw new BookingRulesError(`Booking rules: "${timeZone}" is not a known time zone`);
3150
+ }
3151
+ formatters.set(timeZone, created);
3152
+ return created;
3153
+ };
3154
+ var WEEKDAYS = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
3155
+ function zonedParts(instant, timeZone) {
3156
+ const parts = {};
3157
+ for (const { type, value } of formatterFor(timeZone).formatToParts(instant)) parts[type] = value;
3158
+ const hour = Number(parts.hour) % 24;
3159
+ return {
3160
+ year: Number(parts.year),
3161
+ month: Number(parts.month),
3162
+ day: Number(parts.day),
3163
+ hour,
3164
+ minute: Number(parts.minute),
3165
+ second: Number(parts.second),
3166
+ weekday: WEEKDAYS.indexOf(parts.weekday)
3167
+ };
3168
+ }
3169
+ function offsetAt(instant, timeZone) {
3170
+ const { year, month, day, hour, minute, second } = zonedParts(instant, timeZone);
3171
+ return Date.UTC(year, month - 1, day, hour, minute, second) - Math.floor(instant.getTime() / 1e3) * 1e3;
3172
+ }
3173
+ function instantFromLocal(year, month, day, minutes, timeZone) {
3174
+ const naive = Date.UTC(year, month - 1, day) + minutes * MINUTE;
3175
+ const firstGuess = naive - offsetAt(new Date(naive), timeZone);
3176
+ return new Date(naive - offsetAt(new Date(firstGuess), timeZone));
3177
+ }
3178
+ function startOfZonedDay(instant, timeZone) {
3179
+ const { year, month, day } = zonedParts(instant, timeZone);
3180
+ return instantFromLocal(year, month, day, 0, timeZone);
3181
+ }
3182
+ function nextZonedDay(dayStart, timeZone) {
3183
+ const { year, month, day } = zonedParts(dayStart, timeZone);
3184
+ return instantFromLocal(year, month, day + 1, 0, timeZone);
2767
3185
  }
2768
- var ADYEN_STORE_STATE_PROVINCE_COUNTRIES = /* @__PURE__ */ new Set(["AU", "BR", "CA", "IN", "MX", "NZ", "US"]);
2769
- function adyenStoreRequiresStateOrProvince(alpha2) {
2770
- return !!alpha2 && ADYEN_STORE_STATE_PROVINCE_COUNTRIES.has(alpha2);
3186
+ function atZonedMinute(dayStart, minutes, timeZone) {
3187
+ const { year, month, day } = zonedParts(dayStart, timeZone);
3188
+ return instantFromLocal(year, month, day, minutes, timeZone);
3189
+ }
3190
+
3191
+ // booking/grid.ts
3192
+ var MINUTE2 = 6e4;
3193
+ var MAX_RANGE_DAYS = 366;
3194
+ var asTime = (value, field) => {
3195
+ if (value == null) return void 0;
3196
+ const time = new Date(value).getTime();
3197
+ if (Number.isNaN(time)) throw new BookingRulesError(`Booking rules: ${field} is not a date, got "${String(value)}"`);
3198
+ return time;
3199
+ };
3200
+ function parseHhMm(value, field) {
3201
+ const [hours, minutes] = value.split(":").map(Number);
3202
+ if (!Number.isInteger(hours) || !Number.isInteger(minutes)) {
3203
+ throw new BookingRulesError(`Booking rules: ${field} must be "HH:mm", got "${value}"`);
3204
+ }
3205
+ return hours * 60 + minutes;
3206
+ }
3207
+ function windowEndsAt(rule, now) {
3208
+ const { maxAdvanceDays, bookableTo } = rule.window;
3209
+ const ends = Math.min(
3210
+ maxAdvanceDays ? now.getTime() + maxAdvanceDays * 1440 * MINUTE2 : Number.POSITIVE_INFINITY,
3211
+ asTime(bookableTo, "window.bookableTo") ?? Number.POSITIVE_INFINITY
3212
+ );
3213
+ return Number.isFinite(ends) ? new Date(ends) : null;
3214
+ }
3215
+ function periodSlot(dayStart, timeZone, rule, openMinute, closeMinute) {
3216
+ const { ratePeriod, slot, checkInTime, checkOutTime } = rule;
3217
+ const at = (minutes) => atZonedMinute(dayStart, minutes, timeZone);
3218
+ if (ratePeriod === "night" /* NIGHT */) {
3219
+ if (!checkInTime || !checkOutTime) {
3220
+ throw new BookingRulesError("Overnight booking rules need both checkInTime and checkOutTime");
3221
+ }
3222
+ const checkIn = parseHhMm(checkInTime, "checkInTime");
3223
+ const checkOut = parseHhMm(checkOutTime, "checkOutTime");
3224
+ return {
3225
+ startAt: at(checkIn),
3226
+ endAt: at(1440 + checkOut),
3227
+ // Housekeeping sits between check-out and the next check-in: it costs no night.
3228
+ bufferEndAt: at(1440 + checkIn),
3229
+ canStart: true
3230
+ };
3231
+ }
3232
+ const from = ratePeriod === "day" /* DAY */ ? openMinute ?? 0 : 0;
3233
+ const to = ratePeriod === "day" /* DAY */ ? closeMinute ?? 1440 : 1440;
3234
+ return {
3235
+ startAt: at(from),
3236
+ endAt: at(to),
3237
+ bufferEndAt: at(to + (slot.bufferMinutes ?? 0)),
3238
+ canStart: true
3239
+ };
2771
3240
  }
2772
- var ADYEN_LEGAL_ENTITY_STATE_PROVINCE_COUNTRIES = /* @__PURE__ */ new Set(["AU", "BR", "CA", "IN", "MX", "NZ", "US"]);
2773
- function adyenLegalEntityRequiresStateOrProvince(alpha2) {
2774
- return !!alpha2 && ADYEN_LEGAL_ENTITY_STATE_PROVINCE_COUNTRIES.has(alpha2);
3241
+ function freeGaps(openAt, closeAt, busy) {
3242
+ const inside = busy.map(({ startAt, bufferEndAt }) => ({ from: +startAt, to: +bufferEndAt })).filter(({ from, to }) => to > openAt && from < closeAt).sort((first, second) => first.from - second.from);
3243
+ const gaps = [];
3244
+ let cursor = openAt;
3245
+ for (const { from, to } of inside) {
3246
+ if (from > cursor) gaps.push({ from: cursor, to: from, endsAtBooking: true });
3247
+ cursor = Math.max(cursor, to);
3248
+ if (cursor >= closeAt) break;
3249
+ }
3250
+ if (cursor < closeAt) gaps.push({ from: cursor, to: closeAt, endsAtBooking: false });
3251
+ return gaps;
3252
+ }
3253
+ function buildGrid(rule, timeZone, from, to, now, busy = []) {
3254
+ if (to.getTime() - from.getTime() > MAX_RANGE_DAYS * 1440 * MINUTE2) {
3255
+ throw new BookingRulesError(`A booking range may span at most ${MAX_RANGE_DAYS} days`);
3256
+ }
3257
+ const { availability, slot, window, ratePeriod } = rule;
3258
+ const periodMode = ratePeriod !== "slot" /* SLOT */;
3259
+ const weeklyHours = availability.weeklyHours ?? [];
3260
+ const notBefore = Math.max(
3261
+ now.getTime() + (window.minNoticeMinutes ?? 0) * MINUTE2,
3262
+ asTime(window.bookableFrom, "window.bookableFrom") ?? 0,
3263
+ asTime(availability.availableFrom, "availability.availableFrom") ?? 0
3264
+ );
3265
+ const notAfter = windowEndsAt(rule, now)?.getTime() ?? Number.POSITIVE_INFINITY;
3266
+ const blocked = (availability.offMarket ?? []).map(({ from: blockFrom, to: blockTo }) => ({
3267
+ from: asTime(blockFrom, "availability.offMarket.from"),
3268
+ to: asTime(blockTo, "availability.offMarket.to")
3269
+ }));
3270
+ const arrivalDays = availability.arrivalDays;
3271
+ const step = slot.lengthMinutes + (slot.bufferMinutes ?? 0);
3272
+ const slots = [];
3273
+ const lastDay = startOfZonedDay(to, timeZone).getTime();
3274
+ let dayStart = startOfZonedDay(from, timeZone);
3275
+ while (dayStart.getTime() <= lastDay) {
3276
+ const weekday = zonedParts(dayStart, timeZone).weekday;
3277
+ const rows = weeklyHours.filter((entry) => entry.weekday === weekday);
3278
+ if (periodMode) {
3279
+ const openToday = weeklyHours.length === 0 || rows.length > 0;
3280
+ if (openToday) {
3281
+ const built = periodSlot(dayStart, timeZone, rule, rows[0]?.openMinute, rows[0]?.closeMinute);
3282
+ slots.push({ ...built, canStart: !arrivalDays?.length || arrivalDays.includes(weekday) });
3283
+ }
3284
+ } else {
3285
+ for (const row of rows) {
3286
+ const openAt = +atZonedMinute(dayStart, row.openMinute, timeZone);
3287
+ const closeAt = +atZonedMinute(dayStart, row.closeMinute, timeZone);
3288
+ for (const gap of freeGaps(openAt, closeAt, busy)) {
3289
+ const mustFit = (gap.endsAtBooking ? step : slot.lengthMinutes) * MINUTE2;
3290
+ for (let start = gap.from; start + mustFit <= gap.to; start += step * MINUTE2) {
3291
+ slots.push({
3292
+ startAt: new Date(start),
3293
+ endAt: new Date(start + slot.lengthMinutes * MINUTE2),
3294
+ bufferEndAt: new Date(start + step * MINUTE2),
3295
+ canStart: true
3296
+ });
3297
+ }
3298
+ }
3299
+ }
3300
+ }
3301
+ dayStart = nextZonedDay(dayStart, timeZone);
3302
+ }
3303
+ const askedFrom = from.getTime();
3304
+ const askedTo = to.getTime();
3305
+ return slots.filter(({ startAt, bufferEndAt }) => {
3306
+ const start = startAt.getTime();
3307
+ if (start < askedFrom || start >= askedTo) return false;
3308
+ if (start < notBefore || start > notAfter) return false;
3309
+ return !blocked.some(
3310
+ ({ from: blockFrom, to: blockTo }) => start < blockTo && bufferEndAt.getTime() > blockFrom
3311
+ );
3312
+ }).sort((first, second) => +first.startAt - +second.startAt);
3313
+ }
3314
+ function slotsForWindow(grid, startAt, endAt) {
3315
+ const startIndex = grid.findIndex((entry) => entry.startAt.getTime() === startAt.getTime());
3316
+ if (startIndex < 0) {
3317
+ throw new BookingRulesError(`${startAt.toISOString()} is not a bookable slot start for these rules`);
3318
+ }
3319
+ if (!grid[startIndex].canStart) {
3320
+ throw new BookingRulesError(`${startAt.toISOString()} is not an arrival day for these rules`);
3321
+ }
3322
+ const covered = [];
3323
+ for (let index = startIndex; index < grid.length; index++) {
3324
+ const entry = grid[index];
3325
+ const previous = covered[covered.length - 1];
3326
+ if (previous && entry.startAt.getTime() !== previous.bufferEndAt.getTime()) break;
3327
+ covered.push(entry);
3328
+ if (entry.endAt.getTime() === endAt.getTime()) return covered;
3329
+ if (entry.endAt.getTime() > endAt.getTime()) break;
3330
+ }
3331
+ throw new BookingRulesError(
3332
+ `${startAt.toISOString()} \u2192 ${endAt.toISOString()} does not line up with the bookable grid`
3333
+ );
2775
3334
  }
2776
- var ADYEN_NO_TAX_NUMBER_ABSENT_COUNTRIES = /* @__PURE__ */ new Set(["CA"]);
2777
- function adyenSupportsTaxNumberAbsent(alpha2) {
2778
- return !!alpha2 && !ADYEN_NO_TAX_NUMBER_ABSENT_COUNTRIES.has(alpha2);
3335
+
3336
+ // booking/availability.ts
3337
+ var withinPresence = (slot, timeZone, presence) => {
3338
+ if (!presence?.length) return true;
3339
+ const dayStart = startOfZonedDay(slot.startAt, timeZone);
3340
+ const weekday = zonedParts(dayStart, timeZone).weekday;
3341
+ return presence.some(
3342
+ (shift) => shift.weekday === weekday && slot.startAt.getTime() >= atZonedMinute(dayStart, shift.openMinute, timeZone).getTime() && slot.endAt.getTime() <= atZonedMinute(dayStart, shift.closeMinute, timeZone).getTime()
3343
+ );
3344
+ };
3345
+ var overlaps = (entry, startAt, bufferEndAt) => entry.startAt.getTime() < bufferEndAt.getTime() && entry.bufferEndAt.getTime() > startAt.getTime();
3346
+ function occupancyIn(occupied, productId, startAt, bufferEndAt, shared) {
3347
+ const clashing = occupied.filter((entry) => overlaps(entry, startAt, bufferEndAt));
3348
+ const foreign = shared ? void 0 : clashing.find((entry) => entry.productId !== productId);
3349
+ return {
3350
+ taken: clashing.filter((entry) => shared || entry.productId === productId).reduce((sum, entry) => sum + entry.qty, 0),
3351
+ foreignProductId: foreign?.productId
3352
+ };
2779
3353
  }
2780
- var ADYEN_TAX_NUMBER_FORMATS = {
2781
- CA: { pattern: /^\d{9}$/, length: 9 }
3354
+ var stateFor = (hasShift, offerable, free) => {
3355
+ if (offerable === 0) {
3356
+ return hasShift() ? "fullyBooked" /* FULLY_BOOKED */ : "noHoursInRange" /* NO_HOURS_IN_RANGE */;
3357
+ }
3358
+ return free === 0 ? "fullyBooked" /* FULLY_BOOKED */ : "available" /* AVAILABLE */;
2782
3359
  };
2783
- function adyenTaxNumberFormat(alpha2) {
2784
- return alpha2 ? ADYEN_TAX_NUMBER_FORMATS[alpha2] : void 0;
3360
+ function shopDayKey(instant, timeZone) {
3361
+ const { year, month, day } = zonedParts(instant, timeZone);
3362
+ const pad = (value) => String(value).padStart(2, "0");
3363
+ return `${year}-${pad(month)}-${pad(day)}`;
3364
+ }
3365
+ function shopDayRange(fromDayKey, count, timeZone) {
3366
+ const match = /^(\d{4})-(\d{2})-(\d{2})$/.exec(fromDayKey ?? "");
3367
+ if (!match) {
3368
+ throw new BookingRulesError(`Booking rules: "${fromDayKey}" is not a shop day (YYYY-MM-DD)`);
3369
+ }
3370
+ if (!Number.isInteger(count) || count < 1) {
3371
+ throw new BookingRulesError(`Booking rules: "${count}" is not a number of days`);
3372
+ }
3373
+ const [, year, month, day] = match.map(Number);
3374
+ const from = instantFromLocal(year, month, day, 0, timeZone);
3375
+ if (shopDayKey(from, timeZone) !== fromDayKey) {
3376
+ throw new BookingRulesError(`Booking rules: "${fromDayKey}" is not a real day`);
3377
+ }
3378
+ return { from, to: instantFromLocal(year, month, day + count, 0, timeZone) };
3379
+ }
3380
+ function buildAvailability({
3381
+ productId,
3382
+ bookingRulesId,
3383
+ bookingType,
3384
+ rule,
3385
+ resources,
3386
+ occupied,
3387
+ from,
3388
+ to,
3389
+ now
3390
+ }) {
3391
+ const shared = rule.slot.sharedAcrossProducts ?? false;
3392
+ const bookableUntil = windowEndsAt(rule, now);
3393
+ const header = {
3394
+ productId,
3395
+ bookingRulesId,
3396
+ bookingType,
3397
+ ratePeriod: rule.ratePeriod,
3398
+ // Passed through for the UI to label: a group slot under minimumToRun may not run.
3399
+ minimumToRun: rule.slot.minimumToRun,
3400
+ minStay: rule.slot.minStay,
3401
+ maxStay: rule.slot.maxStay,
3402
+ // Why a day can come back empty. Past this instant the rule refuses everything, so the UI
3403
+ // can say "beyond the booking window" where it would otherwise claim "nothing free".
3404
+ bookableUntil,
3405
+ // The comparison itself, not just the boundary: deciding whether a day lies past the
3406
+ // window is a rule, and a UI that re-derives it owns a copy of that rule — which is how
3407
+ // the same empty grid ends up explained one way on the till and another in a flow.
3408
+ beyondWindow: !!bookableUntil && from.getTime() > bookableUntil.getTime()
3409
+ };
3410
+ const byResource = /* @__PURE__ */ new Map();
3411
+ for (const entry of occupied) {
3412
+ byResource.set(entry.resourceId, [...byResource.get(entry.resourceId) ?? [], entry]);
3413
+ }
3414
+ const grids = resources.map((resource) => {
3415
+ const mine = byResource.get(resource.id) ?? [];
3416
+ const busy = (resource.seats ?? 1) === 1 ? mine : [];
3417
+ const slots2 = buildGrid(rule, resource.timeZone, from, to, now, busy).filter(
3418
+ (slot) => withinPresence(slot, resource.timeZone, resource.presence)
3419
+ );
3420
+ return { resource, slots: slots2 };
3421
+ });
3422
+ const byInstant = /* @__PURE__ */ new Map();
3423
+ const freeSlotsPerResource = /* @__PURE__ */ new Map();
3424
+ for (const { resource, slots: slots2 } of grids) {
3425
+ const capacity = resource.seats ?? 1;
3426
+ const mine = byResource.get(resource.id) ?? [];
3427
+ for (const slot of slots2) {
3428
+ const { taken, foreignProductId } = occupancyIn(mine, productId, slot.startAt, slot.bufferEndAt, shared);
3429
+ const free = foreignProductId ? 0 : Math.max(capacity - taken, 0);
3430
+ const entry = byInstant.get(+slot.startAt) ?? {
3431
+ ...slot,
3432
+ capacity: 0,
3433
+ booked: 0,
3434
+ free: 0,
3435
+ resources: [],
3436
+ dayKey: shopDayKey(slot.startAt, resource.timeZone)
3437
+ };
3438
+ entry.capacity += capacity;
3439
+ entry.booked += foreignProductId ? capacity : taken;
3440
+ entry.free += free;
3441
+ entry.canStart = entry.canStart || slot.canStart;
3442
+ entry.resources.push({ resourceId: resource.id, name: resource.name, capacity, booked: taken, free });
3443
+ if (free > 0) freeSlotsPerResource.set(resource.id, (freeSlotsPerResource.get(resource.id) ?? 0) + 1);
3444
+ byInstant.set(+slot.startAt, entry);
3445
+ }
3446
+ }
3447
+ const inWindow = grids.map(({ resource, slots: slots2 }) => {
3448
+ const free = freeSlotsPerResource.get(resource.id) ?? 0;
3449
+ return {
3450
+ resourceId: resource.id,
3451
+ name: resource.name,
3452
+ slots: slots2.length,
3453
+ free,
3454
+ // Lazily: the shift grid is a second walk, and only an empty answer needs it.
3455
+ state: stateFor(
3456
+ () => buildGrid(rule, resource.timeZone, from, to, now).filter(
3457
+ (slot) => withinPresence(slot, resource.timeZone, resource.presence)
3458
+ ).length > 0,
3459
+ slots2.length,
3460
+ free
3461
+ )
3462
+ };
3463
+ });
3464
+ const slots = [...byInstant.values()].sort((first, second) => +first.startAt - +second.startAt);
3465
+ return {
3466
+ ...header,
3467
+ resources: inWindow,
3468
+ slots,
3469
+ /**
3470
+ * The clock every instant above reads on. Returned rather than assumed: without it a caller
3471
+ * has to fetch the zone from somewhere else and hope it is the same one the grid used.
3472
+ */
3473
+ timeZone: resources[0]?.timeZone ?? null,
3474
+ /** The shop days this answer covers, in order — what a day picker lists. */
3475
+ days: [...new Set(slots.map((slot) => slot.dayKey))]
3476
+ };
3477
+ }
3478
+ function presentAt(resources, rows, outletId) {
3479
+ const byResource = /* @__PURE__ */ new Map();
3480
+ for (const row of rows) {
3481
+ byResource.set(row.resourceId, [...byResource.get(row.resourceId) ?? [], row]);
3482
+ }
3483
+ return resources.flatMap((resource) => {
3484
+ const own = byResource.get(resource.id) ?? [];
3485
+ if (own.length === 0 || !outletId) return [{ resource }];
3486
+ const here = own.find((row) => row.outletId === outletId);
3487
+ return here ? [{ resource, presence: here.weeklyHours }] : [];
3488
+ });
3489
+ }
3490
+
3491
+ // booking/plan.ts
3492
+ var DEFAULT_HOLD_MINUTES = 10;
3493
+ function planWindow({ rule, timeZone, presence, startAt, endAt, now, busy = [] }) {
3494
+ const grid = buildGrid(rule, timeZone, startAt, endAt, now, busy).filter(
3495
+ (slot) => withinPresence(slot, timeZone, presence)
3496
+ );
3497
+ const slots = slotsForWindow(grid, startAt, endAt);
3498
+ const { minStay, maxStay } = rule.slot;
3499
+ if (minStay && slots.length < minStay) {
3500
+ throw new BookingRulesError(`These rules ask for at least ${minStay} period(s), got ${slots.length}`);
3501
+ }
3502
+ if (maxStay && slots.length > maxStay) {
3503
+ throw new BookingRulesError(`These rules allow at most ${maxStay} period(s), got ${slots.length}`);
3504
+ }
3505
+ const last = slots[slots.length - 1];
3506
+ return { slots, endAt: last.endAt, bufferEndAt: last.bufferEndAt };
2785
3507
  }
2786
- var ADYEN_TAX_NUMBER_MIN_LENGTH = 5;
2787
- var ADYEN_JOB_TITLE_ASSOCIATION_TYPES = ["signatory", "uboThroughControl"];
2788
- function adyenRequiresJobTitle(associationType) {
2789
- return ADYEN_JOB_TITLE_ASSOCIATION_TYPES.includes(associationType);
3508
+ var holdExpiresAt = (rule, now) => new Date(now.getTime() + (rule.window.holdMinutes ?? DEFAULT_HOLD_MINUTES) * 6e4);
3509
+
3510
+ // constants/adyen.constants.ts
3511
+ var ADYEN_ROLLOUT_COUNTRY_CODES = /* @__PURE__ */ new Set(["CA"]);
3512
+ function isAdyenRolloutCountry(alpha2) {
3513
+ return ADYEN_ROLLOUT_COUNTRY_CODES.has(alpha2);
2790
3514
  }
2791
3515
  export {
2792
- ADYEN_JOB_TITLE_ASSOCIATION_TYPES,
2793
- ADYEN_LEGAL_ENTITY_STATE_PROVINCE_COUNTRIES,
2794
- ADYEN_NO_TAX_NUMBER_ABSENT_COUNTRIES,
2795
3516
  ADYEN_ROLLOUT_COUNTRY_CODES,
2796
- ADYEN_STORE_STATE_PROVINCE_COUNTRIES,
2797
- ADYEN_TAX_NUMBER_FORMATS,
2798
- ADYEN_TAX_NUMBER_MIN_LENGTH,
2799
3517
  AttributeType,
3518
+ BookingRatePeriod,
3519
+ BookingResourceKind,
3520
+ BookingResourceStatus,
3521
+ BookingRulesError,
3522
+ BookingType,
2800
3523
  CONDITION_OPERATORS,
2801
3524
  COUNTRY_CURRENCIES,
2802
3525
  CURRENCY_CONFIG,
@@ -2807,6 +3530,7 @@ export {
2807
3530
  DEFAULT_CONFIG,
2808
3531
  DEFAULT_CROSS_AXIS_RULES,
2809
3532
  DEFAULT_DISPLAY_STATE_MAP,
3533
+ DEFAULT_HOLD_MINUTES,
2810
3534
  DEFAULT_RESOLVED_CONFIG,
2811
3535
  DEFAULT_UNIT_ID,
2812
3536
  DualWriteStrategy,
@@ -2850,8 +3574,10 @@ export {
2850
3574
  REPORT_NEGATE_PATHS,
2851
3575
  ReceiptSendChannel,
2852
3576
  ReceiptType,
3577
+ ReservationStatus,
2853
3578
  ResidualPaymentMethod,
2854
3579
  ResidualStatus,
3580
+ ResourceAvailabilityState,
2855
3581
  STATE_SCHEMA,
2856
3582
  STRIPE_SUPPORTED_COUNTRIES,
2857
3583
  SYSTEM_FAMILIES,
@@ -2865,23 +3591,29 @@ export {
2865
3591
  TopMetericsSort,
2866
3592
  UserTypes,
2867
3593
  addAmounts,
2868
- adyenLegalEntityRequiresStateOrProvince,
2869
- adyenRequiresJobTitle,
2870
- adyenStoreRequiresStateOrProvince,
2871
- adyenSupportsTaxNumberAbsent,
2872
- adyenTaxNumberFormat,
2873
3594
  amountToString,
3595
+ ancestorChain,
2874
3596
  assertUnitSane,
2875
3597
  availableIn,
2876
3598
  baseFor,
2877
3599
  baseFulfillmentDefinitions,
2878
3600
  baseOf,
2879
3601
  basePaymentDefinitions,
3602
+ buildAvailability,
3603
+ buildGrid,
2880
3604
  buildOrderContext,
2881
3605
  buildPosPresetConfig,
2882
3606
  buildResolvedConfig,
2883
3607
  calculatePercentage,
2884
3608
  canTransition,
3609
+ cheapestCompositeItem,
3610
+ cheapestCompositePrice,
3611
+ compositeItemCost,
3612
+ compositeItemUnavailable,
3613
+ compositeMakeable,
3614
+ compositeModifierMismatches,
3615
+ compositeReservationMismatches,
3616
+ compositeVariantInStock,
2885
3617
  conditionSummary,
2886
3618
  convertFixedFeeToLocalCurrency,
2887
3619
  createAmount,
@@ -2916,8 +3648,11 @@ export {
2916
3648
  getPricingRegionFromCountry,
2917
3649
  getSettlementTotal,
2918
3650
  getTotalCollected,
3651
+ hasSellableChoice,
3652
+ holdExpiresAt,
2919
3653
  inferStatesFromLegacyStatus,
2920
3654
  isAdyenRolloutCountry,
3655
+ isChoiceAvailableAtOutlet,
2921
3656
  isConditionOperator,
2922
3657
  isFulfillmentState,
2923
3658
  isPaymentState,
@@ -2928,25 +3663,39 @@ export {
2928
3663
  mapToLegacyStatus,
2929
3664
  maxPrecisionFor,
2930
3665
  mergeFragment,
3666
+ modifiersSellable,
2931
3667
  multiplyAmount,
2932
3668
  normalizeMongoIdString,
3669
+ occupancyIn,
2933
3670
  parseAmountToMinorUnits,
2934
3671
  parseCurrencyCode,
2935
3672
  parseStripeCurrencyCode,
3673
+ planWindow,
3674
+ poolDemand,
3675
+ poolOf,
3676
+ poolShortfall,
3677
+ poolStockAt,
3678
+ presentAt,
2936
3679
  removeFragment,
2937
3680
  resolveFamily,
3681
+ resolveModifiersForProduct,
2938
3682
  resolveTemplate,
2939
3683
  resolveUnit,
2940
3684
  roundToPrecision,
2941
3685
  sameFamily,
2942
3686
  setOrderState,
3687
+ shopDayKey,
3688
+ shopDayRange,
2943
3689
  signReportForDisplay,
3690
+ slotsForWindow,
2944
3691
  stringifyMinorUnits,
2945
3692
  subtractAmounts,
2946
3693
  toBase,
2947
3694
  toMinorUnits,
2948
3695
  validateConfig,
2949
3696
  validateFragmentCompatibility,
2950
- violatesFinancialInvariant
3697
+ violatesFinancialInvariant,
3698
+ windowEndsAt,
3699
+ withinPresence
2951
3700
  };
2952
3701
  //# sourceMappingURL=index.mjs.map