@final-commerce/common 2.5.1-online.1 → 2.5.1-online.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
@@ -30,6 +30,7 @@ __export(index_exports, {
30
30
  BookingResourceStatus: () => BookingResourceStatus,
31
31
  BookingRulesError: () => BookingRulesError,
32
32
  BookingType: () => BookingType,
33
+ CANADA_PROVINCES: () => CANADA_PROVINCES,
33
34
  CONDITION_OPERATORS: () => CONDITION_OPERATORS,
34
35
  COUNTRY_CURRENCIES: () => COUNTRY_CURRENCIES,
35
36
  CURRENCY_CONFIG: () => CURRENCY_CONFIG,
@@ -46,6 +47,7 @@ __export(index_exports, {
46
47
  DualWriteStrategy: () => DualWriteStrategy,
47
48
  EUFixedRates: () => EUFixedRates,
48
49
  EUROPE: () => EUROPE,
50
+ EVERYWHERE_KEY: () => EVERYWHERE_KEY,
49
51
  ExtensionCategory: () => ExtensionCategory,
50
52
  FINANCIAL_INVARIANTS: () => FINANCIAL_INVARIANTS,
51
53
  FULFILLMENT_STATES: () => FULFILLMENT_STATES,
@@ -96,6 +98,12 @@ __export(index_exports, {
96
98
  SYSTEM_FAMILY_IDS: () => SYSTEM_FAMILY_IDS,
97
99
  SYSTEM_UNITS: () => SYSTEM_UNITS,
98
100
  SYSTEM_UNIT_IDS: () => SYSTEM_UNIT_IDS,
101
+ ShippingChannel: () => ShippingChannel,
102
+ ShippingNoOptionReason: () => ShippingNoOptionReason,
103
+ ShippingOptionType: () => ShippingOptionType,
104
+ ShippingRateType: () => ShippingRateType,
105
+ ShippingZoneError: () => ShippingZoneError,
106
+ ShippingZoneKind: () => ShippingZoneKind,
99
107
  StationStatus: () => StationStatus,
100
108
  StripeCurrencyCode: () => StripeCurrencyCode,
101
109
  StripeStatus: () => StripeStatus,
@@ -103,6 +111,7 @@ __export(index_exports, {
103
111
  TopMetericsSort: () => TopMetericsSort,
104
112
  UserTypes: () => UserTypes,
105
113
  addAmounts: () => addAmounts,
114
+ addressPlaceKeys: () => addressPlaceKeys,
106
115
  amountToString: () => amountToString,
107
116
  assertUnitSane: () => assertUnitSane,
108
117
  availableIn: () => availableIn,
@@ -163,19 +172,31 @@ __export(index_exports, {
163
172
  isSystemFamilyCode: () => isSystemFamilyCode,
164
173
  isValidQuantity: () => isValidQuantity,
165
174
  isWhole: () => isWhole,
175
+ listCountries: () => listCountries,
176
+ listProvinces: () => listProvinces,
166
177
  mapToLegacyStatus: () => mapToLegacyStatus,
167
178
  maxPrecisionFor: () => maxPrecisionFor,
168
179
  mergeFragment: () => mergeFragment,
169
180
  multiplyAmount: () => multiplyAmount,
181
+ normalizeAddress: () => normalizeAddress,
182
+ normalizeCityKey: () => normalizeCityKey,
183
+ normalizeCountry: () => normalizeCountry,
170
184
  normalizeMongoIdString: () => normalizeMongoIdString,
185
+ normalizePostCode: () => normalizePostCode,
186
+ normalizeProvince: () => normalizeProvince,
171
187
  occupancyIn: () => occupancyIn,
172
188
  parseAmountToMinorUnits: () => parseAmountToMinorUnits,
173
189
  parseCurrencyCode: () => parseCurrencyCode,
174
190
  parseStripeCurrencyCode: () => parseStripeCurrencyCode,
191
+ placeKey: () => placeKey,
175
192
  planWindow: () => planWindow,
193
+ postCodeMatchesProvince: () => postCodeMatchesProvince,
176
194
  presentAt: () => presentAt,
195
+ priceForTotal: () => priceForTotal,
196
+ provincesForPostCode: () => provincesForPostCode,
177
197
  removeFragment: () => removeFragment,
178
198
  resolveFamily: () => resolveFamily,
199
+ resolveShipping: () => resolveShipping,
179
200
  resolveTemplate: () => resolveTemplate,
180
201
  resolveUnit: () => resolveUnit,
181
202
  roundToPrecision: () => roundToPrecision,
@@ -191,6 +212,7 @@ __export(index_exports, {
191
212
  toMinorUnits: () => toMinorUnits,
192
213
  validateConfig: () => validateConfig,
193
214
  validateFragmentCompatibility: () => validateFragmentCompatibility,
215
+ validateShippingZone: () => validateShippingZone,
194
216
  violatesFinancialInvariant: () => violatesFinancialInvariant,
195
217
  windowEndsAt: () => windowEndsAt,
196
218
  withinPresence: () => withinPresence
@@ -685,6 +707,50 @@ var ResourceAvailabilityState = /* @__PURE__ */ ((ResourceAvailabilityState2) =>
685
707
  return ResourceAvailabilityState2;
686
708
  })(ResourceAvailabilityState || {});
687
709
 
710
+ // enums/shipping.enum.ts
711
+ var ShippingZoneKind = /* @__PURE__ */ ((ShippingZoneKind2) => {
712
+ ShippingZoneKind2["COUNTRY"] = "country";
713
+ ShippingZoneKind2["PROVINCE"] = "province";
714
+ ShippingZoneKind2["CITY"] = "city";
715
+ ShippingZoneKind2["EVERYWHERE"] = "everywhere";
716
+ return ShippingZoneKind2;
717
+ })(ShippingZoneKind || {});
718
+ var ShippingOptionType = /* @__PURE__ */ ((ShippingOptionType2) => {
719
+ ShippingOptionType2["SHIP"] = "ship";
720
+ ShippingOptionType2["PICKUP"] = "pickup";
721
+ return ShippingOptionType2;
722
+ })(ShippingOptionType || {});
723
+ var ShippingRateType = /* @__PURE__ */ ((ShippingRateType2) => {
724
+ ShippingRateType2["FLAT"] = "flat";
725
+ return ShippingRateType2;
726
+ })(ShippingRateType || {});
727
+ var ShippingChannel = /* @__PURE__ */ ((ShippingChannel2) => {
728
+ ShippingChannel2["WEB"] = "web";
729
+ ShippingChannel2["POS"] = "pos";
730
+ return ShippingChannel2;
731
+ })(ShippingChannel || {});
732
+ var ShippingNoOptionReason = /* @__PURE__ */ ((ShippingNoOptionReason2) => {
733
+ ShippingNoOptionReason2["INCOMPLETE"] = "INCOMPLETE";
734
+ ShippingNoOptionReason2["ADDRESS_MISMATCH"] = "ADDRESS_MISMATCH";
735
+ ShippingNoOptionReason2["NO_COVERAGE"] = "NO_COVERAGE";
736
+ ShippingNoOptionReason2["BLOCKED"] = "BLOCKED";
737
+ ShippingNoOptionReason2["NO_OPTION"] = "NO_OPTION";
738
+ return ShippingNoOptionReason2;
739
+ })(ShippingNoOptionReason || {});
740
+ var ShippingZoneError = /* @__PURE__ */ ((ShippingZoneError2) => {
741
+ ShippingZoneError2["NO_PLACES"] = "NO_PLACES";
742
+ ShippingZoneError2["INVALID_PLACE"] = "INVALID_PLACE";
743
+ ShippingZoneError2["DUPLICATE_PLACE"] = "DUPLICATE_PLACE";
744
+ ShippingZoneError2["NO_WEBSITE_OPTION"] = "NO_WEBSITE_OPTION";
745
+ ShippingZoneError2["INVALID_PRICE"] = "INVALID_PRICE";
746
+ ShippingZoneError2["INVALID_DAYS"] = "INVALID_DAYS";
747
+ ShippingZoneError2["INVALID_TIERS"] = "INVALID_TIERS";
748
+ ShippingZoneError2["NO_CHANNEL"] = "NO_CHANNEL";
749
+ ShippingZoneError2["PICKUP_WITHOUT_STORE"] = "PICKUP_WITHOUT_STORE";
750
+ ShippingZoneError2["MISSING_NAME"] = "MISSING_NAME";
751
+ return ShippingZoneError2;
752
+ })(ShippingZoneError || {});
753
+
688
754
  // utils/currency.util.ts
689
755
  var CURRENCY_CONFIG = {
690
756
  // Major currencies with 2 decimal places
@@ -3404,6 +3470,318 @@ function planWindow({ rule, timeZone, presence, startAt, endAt, now, busy = [] }
3404
3470
  }
3405
3471
  var holdExpiresAt = (rule, now) => new Date(now.getTime() + (rule.window.holdMinutes ?? DEFAULT_HOLD_MINUTES) * 6e4);
3406
3472
 
3473
+ // shipping/fold-text.ts
3474
+ function foldText(value) {
3475
+ return value.normalize("NFD").replace(/[̀-ͯ]/g, "").toLowerCase().replace(/&/g, " and ").replace(/[^a-z0-9]+/g, " ").trim();
3476
+ }
3477
+
3478
+ // shipping/countries.ts
3479
+ var NOT_COUNTRIES = /* @__PURE__ */ new Set(["AC", "CP", "CQ", "DG", "EA", "EU", "EZ", "IC", "QO", "TA", "UN", "XA", "XB", "ZZ"]);
3480
+ var ALIASES = {
3481
+ usa: "US",
3482
+ "united states of america": "US",
3483
+ uk: "GB",
3484
+ "great britain": "GB",
3485
+ england: "GB",
3486
+ uae: "AE",
3487
+ can: "CA",
3488
+ mex: "MX",
3489
+ aus: "AU",
3490
+ gbr: "GB"
3491
+ };
3492
+ var NAME_LOCALES = ["en", "fr"];
3493
+ function regionNames(locale) {
3494
+ try {
3495
+ return new Intl.DisplayNames([locale], { type: "region", fallback: "none" });
3496
+ } catch {
3497
+ return null;
3498
+ }
3499
+ }
3500
+ function canonicalCode(code) {
3501
+ try {
3502
+ return new Intl.Locale(`und-${code}`).maximize().region ?? code;
3503
+ } catch {
3504
+ return code;
3505
+ }
3506
+ }
3507
+ function allCodes() {
3508
+ const names = regionNames("en");
3509
+ if (!names) return [];
3510
+ const codes2 = [];
3511
+ for (let a = 65; a <= 90; a++) {
3512
+ for (let b = 65; b <= 90; b++) {
3513
+ const code = String.fromCharCode(a, b);
3514
+ if (!NOT_COUNTRIES.has(code) && names.of(code) && canonicalCode(code) === code) codes2.push(code);
3515
+ }
3516
+ }
3517
+ return codes2;
3518
+ }
3519
+ var lookup = null;
3520
+ var codes = null;
3521
+ function countryCodes() {
3522
+ codes ??= allCodes();
3523
+ return codes;
3524
+ }
3525
+ function countryLookup() {
3526
+ if (lookup) return lookup;
3527
+ lookup = new Map(Object.entries(ALIASES));
3528
+ for (const locale of NAME_LOCALES) {
3529
+ const names = regionNames(locale);
3530
+ if (!names) continue;
3531
+ for (const code of countryCodes()) {
3532
+ const name = names.of(code);
3533
+ if (name) lookup.set(foldText(name), code);
3534
+ }
3535
+ }
3536
+ return lookup;
3537
+ }
3538
+ function normalizeCountry(value) {
3539
+ const text = (value ?? "").trim();
3540
+ if (!text) return void 0;
3541
+ const upper = text.toUpperCase();
3542
+ if (upper.length === 2) {
3543
+ if (NOT_COUNTRIES.has(upper)) return void 0;
3544
+ const code = canonicalCode(upper);
3545
+ if (countryCodes().includes(code)) return code;
3546
+ }
3547
+ return countryLookup().get(foldText(text));
3548
+ }
3549
+ function listCountries(locale = "en") {
3550
+ const names = regionNames(locale) ?? regionNames("en");
3551
+ return countryCodes().map((code) => ({ code, name: names?.of(code) ?? code })).sort((a, b) => a.name.localeCompare(b.name, locale));
3552
+ }
3553
+
3554
+ // shipping/provinces.ts
3555
+ var CANADA_PROVINCES = [
3556
+ { code: "AB", name: "Alberta", nameFr: "Alberta", postalLetters: ["T"] },
3557
+ { code: "BC", name: "British Columbia", nameFr: "Colombie-Britannique", postalLetters: ["V"] },
3558
+ { code: "MB", name: "Manitoba", nameFr: "Manitoba", postalLetters: ["R"] },
3559
+ { code: "NB", name: "New Brunswick", nameFr: "Nouveau-Brunswick", postalLetters: ["E"] },
3560
+ {
3561
+ code: "NL",
3562
+ name: "Newfoundland and Labrador",
3563
+ nameFr: "Terre-Neuve-et-Labrador",
3564
+ postalLetters: ["A"],
3565
+ aliases: ["NF", "LB", "Newfoundland", "Labrador"]
3566
+ },
3567
+ { code: "NS", name: "Nova Scotia", nameFr: "Nouvelle-\xC9cosse", postalLetters: ["B"] },
3568
+ { code: "NT", name: "Northwest Territories", nameFr: "Territoires du Nord-Ouest", postalLetters: ["X"] },
3569
+ { code: "NU", name: "Nunavut", nameFr: "Nunavut", postalLetters: ["X"] },
3570
+ { code: "ON", name: "Ontario", nameFr: "Ontario", postalLetters: ["K", "L", "M", "N", "P"] },
3571
+ {
3572
+ code: "PE",
3573
+ name: "Prince Edward Island",
3574
+ nameFr: "\xCEle-du-Prince-\xC9douard",
3575
+ postalLetters: ["C"],
3576
+ aliases: ["PEI"]
3577
+ },
3578
+ { code: "QC", name: "Quebec", nameFr: "Qu\xE9bec", postalLetters: ["G", "H", "J"], aliases: ["PQ"] },
3579
+ { code: "SK", name: "Saskatchewan", nameFr: "Saskatchewan", postalLetters: ["S"] },
3580
+ { code: "YT", name: "Yukon", nameFr: "Yukon", postalLetters: ["Y"], aliases: ["YK"] }
3581
+ ];
3582
+ var PROVINCES_BY_COUNTRY = { CA: CANADA_PROVINCES };
3583
+ var lookups = /* @__PURE__ */ new Map();
3584
+ function provinceLookup(country) {
3585
+ const provinces = PROVINCES_BY_COUNTRY[country];
3586
+ if (!provinces) return void 0;
3587
+ let lookup2 = lookups.get(country);
3588
+ if (!lookup2) {
3589
+ lookup2 = /* @__PURE__ */ new Map();
3590
+ for (const p of provinces) {
3591
+ for (const label of [p.code, p.name, p.nameFr, ...p.aliases ?? []]) lookup2.set(foldText(label), p.code);
3592
+ }
3593
+ lookups.set(country, lookup2);
3594
+ }
3595
+ return lookup2;
3596
+ }
3597
+ function normalizeProvince(country, value) {
3598
+ if (!country || !value?.trim()) return void 0;
3599
+ return provinceLookup(country)?.get(foldText(value));
3600
+ }
3601
+ function listProvinces(country) {
3602
+ return PROVINCES_BY_COUNTRY[country] ?? [];
3603
+ }
3604
+ function provincesForPostCode(country, postCode) {
3605
+ const letter = postCode?.charAt(0);
3606
+ if (!country || !letter) return [];
3607
+ return listProvinces(country).filter((p) => p.postalLetters.includes(letter)).map((p) => p.code);
3608
+ }
3609
+
3610
+ // shipping/normalize.ts
3611
+ var EVERYWHERE_KEY = "*";
3612
+ function normalizeCityKey(value) {
3613
+ const words = foldText(value ?? "").split(" ").filter(Boolean).map((word) => word === "saint" ? "st" : word === "sainte" ? "ste" : word);
3614
+ return words.length ? words.join("") : void 0;
3615
+ }
3616
+ function normalizePostCode(value) {
3617
+ const compact = (value ?? "").replace(/[^0-9a-z]/gi, "").toUpperCase();
3618
+ return compact || void 0;
3619
+ }
3620
+ function normalizeAddress(address) {
3621
+ if (!address) return {};
3622
+ const country = normalizeCountry(address.country);
3623
+ const postCode = normalizePostCode(address.postCode ?? address.postalCode ?? address.zip);
3624
+ const fromPostCode = provincesForPostCode(country, postCode);
3625
+ const province = normalizeProvince(country, address.state ?? address.province) ?? (fromPostCode.length === 1 ? fromPostCode[0] : void 0);
3626
+ return { country, province, cityKey: normalizeCityKey(address.city), postCode };
3627
+ }
3628
+ function postCodeMatchesProvince(address) {
3629
+ const candidates = provincesForPostCode(address.country, address.postCode);
3630
+ if (!address.province || candidates.length === 0) return true;
3631
+ return candidates.includes(address.province);
3632
+ }
3633
+ function placeKey(place) {
3634
+ const country = normalizeCountry(place.country);
3635
+ if (!country) return void 0;
3636
+ if (!place.province) return country;
3637
+ const province = normalizeProvince(country, place.province);
3638
+ if (!province) return void 0;
3639
+ if (!place.city) return `${country}-${province}`;
3640
+ const city = normalizeCityKey(place.city);
3641
+ return city ? `${country}-${province}/${city}` : void 0;
3642
+ }
3643
+ function addressPlaceKeys(address) {
3644
+ const keys = [];
3645
+ if (address.country && address.province && address.cityKey) {
3646
+ keys.push(`${address.country}-${address.province}/${address.cityKey}`);
3647
+ }
3648
+ if (address.country && address.province) keys.push(`${address.country}-${address.province}`);
3649
+ if (address.country) keys.push(address.country);
3650
+ keys.push(EVERYWHERE_KEY);
3651
+ return keys;
3652
+ }
3653
+
3654
+ // shipping/resolve.ts
3655
+ function resolveShipping({ zones, address, itemsTotal, channel }) {
3656
+ const shipsToCountries = countriesShippedTo(zones);
3657
+ const none = (reason, zone2) => ({
3658
+ zoneId: zone2?.id ?? null,
3659
+ options: [],
3660
+ reason,
3661
+ message: zone2?.message?.trim() || null,
3662
+ shipsToCountries
3663
+ });
3664
+ const normalized = normalizeAddress(address);
3665
+ if (!normalized.country) return none("INCOMPLETE" /* INCOMPLETE */);
3666
+ if (!postCodeMatchesProvince(normalized)) return none("ADDRESS_MISMATCH" /* ADDRESS_MISMATCH */);
3667
+ const zone = findZone(zones, addressPlaceKeys(normalized));
3668
+ if (!zone) return none("NO_COVERAGE" /* NO_COVERAGE */);
3669
+ const offered = zone.options.filter((option) => isOffered(option, zone, channel));
3670
+ if (offered.length === 0) {
3671
+ return none(zone.ships ? "NO_OPTION" /* NO_OPTION */ : "BLOCKED" /* BLOCKED */, zone);
3672
+ }
3673
+ const quotes = offered.map((option) => quote(option, zone.id, itemsTotal));
3674
+ const ship = quotes.filter((q) => q.type === "ship" /* SHIP */).sort((a, b) => a.amount - b.amount);
3675
+ const pickup = quotes.filter((q) => q.type === "pickup" /* PICKUP */).sort((a, b) => a.amount - b.amount);
3676
+ return {
3677
+ zoneId: zone.id,
3678
+ options: [...ship, ...pickup],
3679
+ reason: null,
3680
+ message: zone.ships ? null : zone.message?.trim() || null,
3681
+ shipsToCountries
3682
+ };
3683
+ }
3684
+ function findZone(zones, keys) {
3685
+ const byKey = /* @__PURE__ */ new Map();
3686
+ for (const zone of zones) {
3687
+ for (const key of zoneKeys(zone)) if (!byKey.has(key)) byKey.set(key, zone);
3688
+ }
3689
+ for (const key of keys) {
3690
+ const zone = byKey.get(key);
3691
+ if (zone) return zone;
3692
+ }
3693
+ return void 0;
3694
+ }
3695
+ function zoneKeys(zone) {
3696
+ if (zone.kind === "everywhere" /* EVERYWHERE */) return [EVERYWHERE_KEY];
3697
+ return zone.places.map(placeKey).filter((key) => Boolean(key));
3698
+ }
3699
+ function isOffered(option, zone, channel) {
3700
+ if (!option.channels.some((c) => c === channel)) return false;
3701
+ if (option.type === "pickup" /* PICKUP */) return channel !== "pos" /* POS */;
3702
+ return zone.ships;
3703
+ }
3704
+ function priceForTotal(option, itemsTotal) {
3705
+ const tiers = [...option.priceByTotal ?? []].sort((a, b) => a.from - b.from);
3706
+ let price = option.price;
3707
+ for (const tier of tiers) if (itemsTotal >= tier.from) price = tier.price;
3708
+ if (option.freeOver != null && itemsTotal >= option.freeOver) return 0;
3709
+ return price;
3710
+ }
3711
+ function quote(option, zoneId, itemsTotal) {
3712
+ const amount = priceForTotal(option, itemsTotal);
3713
+ const freeOver = option.freeOver ?? null;
3714
+ return {
3715
+ optionId: option.id,
3716
+ zoneId,
3717
+ type: option.type,
3718
+ name: option.name?.trim() || null,
3719
+ amount,
3720
+ amountToFree: freeOver != null && amount > 0 ? freeOver - itemsTotal : null,
3721
+ minDays: option.minDays ?? null,
3722
+ maxDays: option.maxDays ?? null,
3723
+ outletId: option.outletId ?? null
3724
+ };
3725
+ }
3726
+ function countriesShippedTo(zones) {
3727
+ const countries = /* @__PURE__ */ new Set();
3728
+ for (const zone of zones) {
3729
+ if (!zone.ships) continue;
3730
+ for (const key of zoneKeys(zone)) if (key !== EVERYWHERE_KEY) countries.add(key.slice(0, 2));
3731
+ }
3732
+ return [...countries].sort();
3733
+ }
3734
+
3735
+ // shipping/validate.ts
3736
+ function validateShippingZone(zone) {
3737
+ const problems = [...placeProblems(zone)];
3738
+ for (const option of zone.options) problems.push(...optionProblems(option));
3739
+ const reachesWebsite = zone.options.some((option) => option.channels.includes("web" /* WEB */));
3740
+ if (zone.ships && !reachesWebsite) problems.push({ code: "NO_WEBSITE_OPTION" /* NO_WEBSITE_OPTION */ });
3741
+ return problems;
3742
+ }
3743
+ function placeProblems(zone) {
3744
+ if (zone.kind === "everywhere" /* EVERYWHERE */) {
3745
+ return zone.places.length ? [{ code: "INVALID_PLACE" /* INVALID_PLACE */ }] : [];
3746
+ }
3747
+ if (zone.places.length === 0) return [{ code: "NO_PLACES" /* NO_PLACES */ }];
3748
+ if (!zone.places.every((place) => fitsKind(place, zone.kind))) return [{ code: "INVALID_PLACE" /* INVALID_PLACE */ }];
3749
+ const keys = zone.places.map(placeKey);
3750
+ if (keys.some((key) => !key)) return [{ code: "INVALID_PLACE" /* INVALID_PLACE */ }];
3751
+ if (new Set(keys).size !== keys.length) return [{ code: "DUPLICATE_PLACE" /* DUPLICATE_PLACE */ }];
3752
+ return [];
3753
+ }
3754
+ function fitsKind(place, kind) {
3755
+ const hasProvince = Boolean(place.province?.trim());
3756
+ const hasCity = Boolean(place.city?.trim());
3757
+ if (kind === "country" /* COUNTRY */) return !hasProvince && !hasCity;
3758
+ if (kind === "province" /* PROVINCE */) return hasProvince && !hasCity;
3759
+ if (kind === "city" /* CITY */) return hasProvince && hasCity;
3760
+ return false;
3761
+ }
3762
+ var isAmount = (value) => Number.isInteger(value) && value >= 0;
3763
+ var isDays = (value) => value == null || isAmount(value);
3764
+ function optionProblems(option) {
3765
+ const problems = [];
3766
+ const isPickup = option.type === "pickup" /* PICKUP */;
3767
+ if (!isAmount(option.price) || option.freeOver != null && !isAmount(option.freeOver)) {
3768
+ problems.push("INVALID_PRICE" /* INVALID_PRICE */);
3769
+ }
3770
+ if (!isPickup && !option.name?.trim()) problems.push("MISSING_NAME" /* MISSING_NAME */);
3771
+ if (!isDays(option.minDays) || !isDays(option.maxDays) || (option.minDays ?? 0) > (option.maxDays ?? Infinity)) {
3772
+ problems.push("INVALID_DAYS" /* INVALID_DAYS */);
3773
+ }
3774
+ if (!tiersAreValid(option)) problems.push("INVALID_TIERS" /* INVALID_TIERS */);
3775
+ if (option.channels.length === 0) problems.push("NO_CHANNEL" /* NO_CHANNEL */);
3776
+ if (isPickup && !option.outletId) problems.push("PICKUP_WITHOUT_STORE" /* PICKUP_WITHOUT_STORE */);
3777
+ return problems.map((code) => ({ code, optionId: option.id }));
3778
+ }
3779
+ function tiersAreValid(option) {
3780
+ const tiers = option.priceByTotal ?? [];
3781
+ const froms = tiers.map((tier) => tier.from);
3782
+ return tiers.every((tier) => Number.isInteger(tier.from) && tier.from > 0 && isAmount(tier.price)) && new Set(froms).size === froms.length;
3783
+ }
3784
+
3407
3785
  // constants/adyen.constants.ts
3408
3786
  var ADYEN_ROLLOUT_COUNTRY_CODES = /* @__PURE__ */ new Set(["CA"]);
3409
3787
  function isAdyenRolloutCountry(alpha2) {
@@ -3462,6 +3840,7 @@ var PAYMENT_CHANNELS = ["inPerson", "online"];
3462
3840
  BookingResourceStatus,
3463
3841
  BookingRulesError,
3464
3842
  BookingType,
3843
+ CANADA_PROVINCES,
3465
3844
  CONDITION_OPERATORS,
3466
3845
  COUNTRY_CURRENCIES,
3467
3846
  CURRENCY_CONFIG,
@@ -3478,6 +3857,7 @@ var PAYMENT_CHANNELS = ["inPerson", "online"];
3478
3857
  DualWriteStrategy,
3479
3858
  EUFixedRates,
3480
3859
  EUROPE,
3860
+ EVERYWHERE_KEY,
3481
3861
  ExtensionCategory,
3482
3862
  FINANCIAL_INVARIANTS,
3483
3863
  FULFILLMENT_STATES,
@@ -3528,6 +3908,12 @@ var PAYMENT_CHANNELS = ["inPerson", "online"];
3528
3908
  SYSTEM_FAMILY_IDS,
3529
3909
  SYSTEM_UNITS,
3530
3910
  SYSTEM_UNIT_IDS,
3911
+ ShippingChannel,
3912
+ ShippingNoOptionReason,
3913
+ ShippingOptionType,
3914
+ ShippingRateType,
3915
+ ShippingZoneError,
3916
+ ShippingZoneKind,
3531
3917
  StationStatus,
3532
3918
  StripeCurrencyCode,
3533
3919
  StripeStatus,
@@ -3535,6 +3921,7 @@ var PAYMENT_CHANNELS = ["inPerson", "online"];
3535
3921
  TopMetericsSort,
3536
3922
  UserTypes,
3537
3923
  addAmounts,
3924
+ addressPlaceKeys,
3538
3925
  amountToString,
3539
3926
  assertUnitSane,
3540
3927
  availableIn,
@@ -3595,19 +3982,31 @@ var PAYMENT_CHANNELS = ["inPerson", "online"];
3595
3982
  isSystemFamilyCode,
3596
3983
  isValidQuantity,
3597
3984
  isWhole,
3985
+ listCountries,
3986
+ listProvinces,
3598
3987
  mapToLegacyStatus,
3599
3988
  maxPrecisionFor,
3600
3989
  mergeFragment,
3601
3990
  multiplyAmount,
3991
+ normalizeAddress,
3992
+ normalizeCityKey,
3993
+ normalizeCountry,
3602
3994
  normalizeMongoIdString,
3995
+ normalizePostCode,
3996
+ normalizeProvince,
3603
3997
  occupancyIn,
3604
3998
  parseAmountToMinorUnits,
3605
3999
  parseCurrencyCode,
3606
4000
  parseStripeCurrencyCode,
4001
+ placeKey,
3607
4002
  planWindow,
4003
+ postCodeMatchesProvince,
3608
4004
  presentAt,
4005
+ priceForTotal,
4006
+ provincesForPostCode,
3609
4007
  removeFragment,
3610
4008
  resolveFamily,
4009
+ resolveShipping,
3611
4010
  resolveTemplate,
3612
4011
  resolveUnit,
3613
4012
  roundToPrecision,
@@ -3623,6 +4022,7 @@ var PAYMENT_CHANNELS = ["inPerson", "online"];
3623
4022
  toMinorUnits,
3624
4023
  validateConfig,
3625
4024
  validateFragmentCompatibility,
4025
+ validateShippingZone,
3626
4026
  violatesFinancialInvariant,
3627
4027
  windowEndsAt,
3628
4028
  withinPresence