@asteby/storefront 0.6.0 → 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +34 -0
- package/dist/index.cjs +72 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +74 -1
- package/dist/index.d.ts +74 -1
- package/dist/index.js +66 -1
- package/dist/index.js.map +1 -1
- package/dist/mock.cjs +5 -1
- package/dist/mock.cjs.map +1 -1
- package/dist/mock.d.cts +8 -1
- package/dist/mock.d.ts +8 -1
- package/dist/mock.js +5 -1
- package/dist/mock.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.cts
CHANGED
|
@@ -75,6 +75,18 @@ type StorefrontClientOptions = {
|
|
|
75
75
|
fetch?: FetchLike;
|
|
76
76
|
/** Sent as `Accept-Language`. */
|
|
77
77
|
locale?: string;
|
|
78
|
+
/**
|
|
79
|
+
* Public host of the storefront (`godsnutrition.com`), sent as `X-Storefront-Domain`. Platform (`pt_`)
|
|
80
|
+
* tokens need it to pick the organization; with an org-scoped token it only scopes the market.
|
|
81
|
+
*/
|
|
82
|
+
domain?: string;
|
|
83
|
+
/**
|
|
84
|
+
* Market the request is for (`mx`, `us`), sent as `<domain>#<market>` in `X-Storefront-Domain`. It lets ONE
|
|
85
|
+
* public domain serve several markets (the storefront routes by path prefix, e.g. `/us`): OPS honors it
|
|
86
|
+
* when the organization has that market active and otherwise falls back to the domain's own market.
|
|
87
|
+
* Ignored without `domain`.
|
|
88
|
+
*/
|
|
89
|
+
market?: string;
|
|
78
90
|
/** Extra headers on every request. */
|
|
79
91
|
headers?: Record<string, string>;
|
|
80
92
|
/**
|
|
@@ -285,6 +297,8 @@ declare function createStorefrontClient(config: StorefrontClientOptions): {
|
|
|
285
297
|
};
|
|
286
298
|
};
|
|
287
299
|
type StorefrontClient = ReturnType<typeof createStorefrontClient>;
|
|
300
|
+
/** `X-Storefront-Domain` value: the host, plus `#<market>` when a valid market code is given. */
|
|
301
|
+
declare function storefrontDomainHeader(domain: string, market?: string | null): string;
|
|
288
302
|
|
|
289
303
|
type StorefrontErrorInit = {
|
|
290
304
|
status: number;
|
|
@@ -573,4 +587,63 @@ type PromoProgressView = {
|
|
|
573
587
|
*/
|
|
574
588
|
declare function promoProgressView(promotions: CartPromotions | null | undefined, locale?: string | null, overrides?: Partial<PromotionMessages> | null): PromoProgressView;
|
|
575
589
|
|
|
576
|
-
|
|
590
|
+
/**
|
|
591
|
+
* Detección de mercado por visita (país + idioma), pura y sin DOM: sirve en el servidor de cualquier tienda.
|
|
592
|
+
*
|
|
593
|
+
* Reglas (mejores prácticas de i18n para comercio):
|
|
594
|
+
* - El mercado canónico lo fija el DOMINIO (OPS liga cada dominio verificado a un mercado: org_domains.market_code).
|
|
595
|
+
* Esto no lo decide la geolocalización: así cada URL tiene un solo idioma/moneda (SEO, hreflang, caché de CDN).
|
|
596
|
+
* - La geolocalización solo SUGIERE (banner "¿Ver la tienda de Estados Unidos?"); nunca redirige sola, nunca a rastreadores
|
|
597
|
+
* y respeta la elección guardada del comprador.
|
|
598
|
+
* - Precios, impuestos y envío NO dependen de la IP: dependen del mercado del dominio y de la dirección de entrega.
|
|
599
|
+
* - El país sale de las cabeceras que pone el CDN/proxy (no se puede confiar en ellas si el servidor está expuesto
|
|
600
|
+
* directo a internet: solo se leen con `trustProxy`).
|
|
601
|
+
*/
|
|
602
|
+
type MarketInfo = {
|
|
603
|
+
/** Código del mercado en OPS ("mx", "us"). */
|
|
604
|
+
code: string;
|
|
605
|
+
/** ISO 3166-1 alfa-2 a los que vende. */
|
|
606
|
+
countries: string[];
|
|
607
|
+
/** BCP 47 por defecto ("es-MX", "en-US"). */
|
|
608
|
+
locale: string;
|
|
609
|
+
/** ISO 4217 ("MXN", "USD"). */
|
|
610
|
+
currency: string;
|
|
611
|
+
/** Dominio público del mercado, si es distinto del actual ("godsnutrition.com"). */
|
|
612
|
+
host?: string;
|
|
613
|
+
};
|
|
614
|
+
/** Cabeceras de país de los CDN/proxies más comunes, en orden de preferencia. */
|
|
615
|
+
declare const COUNTRY_HEADERS: readonly ["cf-ipcountry", "cloudfront-viewer-country", "x-vercel-ip-country", "x-appengine-country", "x-country-code"];
|
|
616
|
+
/** País del visitante según el CDN. `null` si no viene, es desconocido (XX), Tor (T1) o no es ISO alfa-2. */
|
|
617
|
+
declare function visitorCountry(headers: Headers, { trustProxy }?: {
|
|
618
|
+
trustProxy?: boolean;
|
|
619
|
+
}): string | null;
|
|
620
|
+
/** Idiomas de Accept-Language ordenados por q (mayor primero), en minúsculas. */
|
|
621
|
+
declare function preferredLanguages(acceptLanguage: string | null | undefined): string[];
|
|
622
|
+
/** Mejor idioma entre los que ofrece la tienda: coincidencia exacta, luego por idioma base; si no, el de `fallback`. */
|
|
623
|
+
declare function negotiateLocale(acceptLanguage: string | null | undefined, available: string[], fallback: string): string;
|
|
624
|
+
type MarketSuggestion = {
|
|
625
|
+
market: MarketInfo;
|
|
626
|
+
/** Qué disparó la sugerencia. El país manda; el idioma solo si el CDN no dio país. */
|
|
627
|
+
reason: 'country' | 'language';
|
|
628
|
+
};
|
|
629
|
+
/**
|
|
630
|
+
* ¿Conviene sugerir otro mercado a esta visita? `null` si no (ya está en el correcto, rastreador, ya eligió, o no hay otro).
|
|
631
|
+
* `chosen` es el código que el comprador guardó al elegir a mano ("market" en cookie): si existe, no se vuelve a sugerir.
|
|
632
|
+
*/
|
|
633
|
+
declare function suggestMarket({ headers, markets, current, chosen, trustProxy, }: {
|
|
634
|
+
headers: Headers;
|
|
635
|
+
markets: MarketInfo[];
|
|
636
|
+
current: string;
|
|
637
|
+
chosen?: string | null;
|
|
638
|
+
trustProxy?: boolean;
|
|
639
|
+
}): MarketSuggestion | null;
|
|
640
|
+
/**
|
|
641
|
+
* Enlaces `hreflang` para <head>: un alterno por mercado con dominio propio, más x-default (el mercado por defecto).
|
|
642
|
+
* `path` es la ruta con query actual ("/productos/x"). Es lo que permite a buscadores servir cada país en su idioma.
|
|
643
|
+
*/
|
|
644
|
+
declare function hreflangLinks(markets: MarketInfo[], path: string, defaultMarket: string, protocol?: string): {
|
|
645
|
+
hrefLang: string;
|
|
646
|
+
href: string;
|
|
647
|
+
}[];
|
|
648
|
+
|
|
649
|
+
export { Address, AddressInput, Article, type ArticleListParams, Availability, type AvailabilityLocale, type AvailabilityMessageKey, type AvailabilityMessages, AvailabilityStatus, type AvailabilityTone, type AvailabilityView, type AvailabilityViewOptions, CLIENT_IP_HEADER, COUNTRY_HEADERS, Cart, CartBuyerInput, CartLineInput, CartLineProblem, type CartLineProblemView, CartLineUpdateInput, CartPromotions, CartRecommendations, CheckoutInput, CheckoutResult, Collection, type CollectionListParams, Connection, Customer, CustomerIdentifier, CustomerSession, CustomerSessionInput, DeclineCode, DeliveryInput, DeliveryOptions, type DiscountBarState, type DiscountBarView, type FetchLike, type FormatMoneyOptions, GeoPoint, InsufficientStockLine, InvoiceRequest, InvoiceResult, Location, LocationListParams, type MarketInfo, type MarketSuggestion, Menu, Money, NOTE_MAX_LENGTH, NearestLocation, Order, Page, type PaginationParams, PaymentAttemptResult, PaymentConfig, type PaymentErrorKind, type PaymentErrorView, type PaymentLocale, type PaymentMessageKey, type PaymentMessages, PaymentMethods, PaymentNextStep, type PaymentOutcome, type PaymentOutcomeKind, PaymentSubmitInput, PaymentVoucher, type PreviewOptions, Product, ProductConnection, type ProductListParams, ProductSort, ProductVariant, type PromoProgressView, type PromotionLocale, type PromotionMessageKey, type PromotionMessages, PromotionReward, PromotionsList, type QueryParams, type QueryValue, RecommendationPlacement, type RequestOptions, type ResolveAvailabilityOptions, RestockSubscriptionInput, RestockSubscriptionResult, ReturnRequest, ReturnRequestInput, type ShippingBarState, type ShippingBarView, Shop, StockLevel, type StorefrontClient, type StorefrontClientOptions, StorefrontError, StorefrontErrorCode, type StorefrontErrorInit, type StorefrontRequestContext, type StorefrontResponseContext, Theme, type WriteOptions, appendParams, availabilityLocale, availabilityMessages, availabilityView, cartLineProblemView, clampPercent, createStorefrontClient, declineMessage, declineMessageKey, formatAvailabilityMessage, formatMoney, formatVoucherExpiry, generateIdempotencyKey, hreflangLinks, isStorefrontError, isTokenPublishable, negotiateLocale, normalizeClientIp, normalizeNote, parseMoney, parseProductFilters, paymentErrorView, paymentIdempotencyKey, paymentMessages, paymentOutcome, preferredLanguages, productAvailability, productListQuery, promoProgressView, promotionMessages, resolveAvailability, rewardLabel, safeHttpsUrl, schemaAvailability, storefrontDomainHeader, suggestMarket, toMoney, visitorCountry };
|
package/dist/index.d.ts
CHANGED
|
@@ -75,6 +75,18 @@ type StorefrontClientOptions = {
|
|
|
75
75
|
fetch?: FetchLike;
|
|
76
76
|
/** Sent as `Accept-Language`. */
|
|
77
77
|
locale?: string;
|
|
78
|
+
/**
|
|
79
|
+
* Public host of the storefront (`godsnutrition.com`), sent as `X-Storefront-Domain`. Platform (`pt_`)
|
|
80
|
+
* tokens need it to pick the organization; with an org-scoped token it only scopes the market.
|
|
81
|
+
*/
|
|
82
|
+
domain?: string;
|
|
83
|
+
/**
|
|
84
|
+
* Market the request is for (`mx`, `us`), sent as `<domain>#<market>` in `X-Storefront-Domain`. It lets ONE
|
|
85
|
+
* public domain serve several markets (the storefront routes by path prefix, e.g. `/us`): OPS honors it
|
|
86
|
+
* when the organization has that market active and otherwise falls back to the domain's own market.
|
|
87
|
+
* Ignored without `domain`.
|
|
88
|
+
*/
|
|
89
|
+
market?: string;
|
|
78
90
|
/** Extra headers on every request. */
|
|
79
91
|
headers?: Record<string, string>;
|
|
80
92
|
/**
|
|
@@ -285,6 +297,8 @@ declare function createStorefrontClient(config: StorefrontClientOptions): {
|
|
|
285
297
|
};
|
|
286
298
|
};
|
|
287
299
|
type StorefrontClient = ReturnType<typeof createStorefrontClient>;
|
|
300
|
+
/** `X-Storefront-Domain` value: the host, plus `#<market>` when a valid market code is given. */
|
|
301
|
+
declare function storefrontDomainHeader(domain: string, market?: string | null): string;
|
|
288
302
|
|
|
289
303
|
type StorefrontErrorInit = {
|
|
290
304
|
status: number;
|
|
@@ -573,4 +587,63 @@ type PromoProgressView = {
|
|
|
573
587
|
*/
|
|
574
588
|
declare function promoProgressView(promotions: CartPromotions | null | undefined, locale?: string | null, overrides?: Partial<PromotionMessages> | null): PromoProgressView;
|
|
575
589
|
|
|
576
|
-
|
|
590
|
+
/**
|
|
591
|
+
* Detección de mercado por visita (país + idioma), pura y sin DOM: sirve en el servidor de cualquier tienda.
|
|
592
|
+
*
|
|
593
|
+
* Reglas (mejores prácticas de i18n para comercio):
|
|
594
|
+
* - El mercado canónico lo fija el DOMINIO (OPS liga cada dominio verificado a un mercado: org_domains.market_code).
|
|
595
|
+
* Esto no lo decide la geolocalización: así cada URL tiene un solo idioma/moneda (SEO, hreflang, caché de CDN).
|
|
596
|
+
* - La geolocalización solo SUGIERE (banner "¿Ver la tienda de Estados Unidos?"); nunca redirige sola, nunca a rastreadores
|
|
597
|
+
* y respeta la elección guardada del comprador.
|
|
598
|
+
* - Precios, impuestos y envío NO dependen de la IP: dependen del mercado del dominio y de la dirección de entrega.
|
|
599
|
+
* - El país sale de las cabeceras que pone el CDN/proxy (no se puede confiar en ellas si el servidor está expuesto
|
|
600
|
+
* directo a internet: solo se leen con `trustProxy`).
|
|
601
|
+
*/
|
|
602
|
+
type MarketInfo = {
|
|
603
|
+
/** Código del mercado en OPS ("mx", "us"). */
|
|
604
|
+
code: string;
|
|
605
|
+
/** ISO 3166-1 alfa-2 a los que vende. */
|
|
606
|
+
countries: string[];
|
|
607
|
+
/** BCP 47 por defecto ("es-MX", "en-US"). */
|
|
608
|
+
locale: string;
|
|
609
|
+
/** ISO 4217 ("MXN", "USD"). */
|
|
610
|
+
currency: string;
|
|
611
|
+
/** Dominio público del mercado, si es distinto del actual ("godsnutrition.com"). */
|
|
612
|
+
host?: string;
|
|
613
|
+
};
|
|
614
|
+
/** Cabeceras de país de los CDN/proxies más comunes, en orden de preferencia. */
|
|
615
|
+
declare const COUNTRY_HEADERS: readonly ["cf-ipcountry", "cloudfront-viewer-country", "x-vercel-ip-country", "x-appengine-country", "x-country-code"];
|
|
616
|
+
/** País del visitante según el CDN. `null` si no viene, es desconocido (XX), Tor (T1) o no es ISO alfa-2. */
|
|
617
|
+
declare function visitorCountry(headers: Headers, { trustProxy }?: {
|
|
618
|
+
trustProxy?: boolean;
|
|
619
|
+
}): string | null;
|
|
620
|
+
/** Idiomas de Accept-Language ordenados por q (mayor primero), en minúsculas. */
|
|
621
|
+
declare function preferredLanguages(acceptLanguage: string | null | undefined): string[];
|
|
622
|
+
/** Mejor idioma entre los que ofrece la tienda: coincidencia exacta, luego por idioma base; si no, el de `fallback`. */
|
|
623
|
+
declare function negotiateLocale(acceptLanguage: string | null | undefined, available: string[], fallback: string): string;
|
|
624
|
+
type MarketSuggestion = {
|
|
625
|
+
market: MarketInfo;
|
|
626
|
+
/** Qué disparó la sugerencia. El país manda; el idioma solo si el CDN no dio país. */
|
|
627
|
+
reason: 'country' | 'language';
|
|
628
|
+
};
|
|
629
|
+
/**
|
|
630
|
+
* ¿Conviene sugerir otro mercado a esta visita? `null` si no (ya está en el correcto, rastreador, ya eligió, o no hay otro).
|
|
631
|
+
* `chosen` es el código que el comprador guardó al elegir a mano ("market" en cookie): si existe, no se vuelve a sugerir.
|
|
632
|
+
*/
|
|
633
|
+
declare function suggestMarket({ headers, markets, current, chosen, trustProxy, }: {
|
|
634
|
+
headers: Headers;
|
|
635
|
+
markets: MarketInfo[];
|
|
636
|
+
current: string;
|
|
637
|
+
chosen?: string | null;
|
|
638
|
+
trustProxy?: boolean;
|
|
639
|
+
}): MarketSuggestion | null;
|
|
640
|
+
/**
|
|
641
|
+
* Enlaces `hreflang` para <head>: un alterno por mercado con dominio propio, más x-default (el mercado por defecto).
|
|
642
|
+
* `path` es la ruta con query actual ("/productos/x"). Es lo que permite a buscadores servir cada país en su idioma.
|
|
643
|
+
*/
|
|
644
|
+
declare function hreflangLinks(markets: MarketInfo[], path: string, defaultMarket: string, protocol?: string): {
|
|
645
|
+
hrefLang: string;
|
|
646
|
+
href: string;
|
|
647
|
+
}[];
|
|
648
|
+
|
|
649
|
+
export { Address, AddressInput, Article, type ArticleListParams, Availability, type AvailabilityLocale, type AvailabilityMessageKey, type AvailabilityMessages, AvailabilityStatus, type AvailabilityTone, type AvailabilityView, type AvailabilityViewOptions, CLIENT_IP_HEADER, COUNTRY_HEADERS, Cart, CartBuyerInput, CartLineInput, CartLineProblem, type CartLineProblemView, CartLineUpdateInput, CartPromotions, CartRecommendations, CheckoutInput, CheckoutResult, Collection, type CollectionListParams, Connection, Customer, CustomerIdentifier, CustomerSession, CustomerSessionInput, DeclineCode, DeliveryInput, DeliveryOptions, type DiscountBarState, type DiscountBarView, type FetchLike, type FormatMoneyOptions, GeoPoint, InsufficientStockLine, InvoiceRequest, InvoiceResult, Location, LocationListParams, type MarketInfo, type MarketSuggestion, Menu, Money, NOTE_MAX_LENGTH, NearestLocation, Order, Page, type PaginationParams, PaymentAttemptResult, PaymentConfig, type PaymentErrorKind, type PaymentErrorView, type PaymentLocale, type PaymentMessageKey, type PaymentMessages, PaymentMethods, PaymentNextStep, type PaymentOutcome, type PaymentOutcomeKind, PaymentSubmitInput, PaymentVoucher, type PreviewOptions, Product, ProductConnection, type ProductListParams, ProductSort, ProductVariant, type PromoProgressView, type PromotionLocale, type PromotionMessageKey, type PromotionMessages, PromotionReward, PromotionsList, type QueryParams, type QueryValue, RecommendationPlacement, type RequestOptions, type ResolveAvailabilityOptions, RestockSubscriptionInput, RestockSubscriptionResult, ReturnRequest, ReturnRequestInput, type ShippingBarState, type ShippingBarView, Shop, StockLevel, type StorefrontClient, type StorefrontClientOptions, StorefrontError, StorefrontErrorCode, type StorefrontErrorInit, type StorefrontRequestContext, type StorefrontResponseContext, Theme, type WriteOptions, appendParams, availabilityLocale, availabilityMessages, availabilityView, cartLineProblemView, clampPercent, createStorefrontClient, declineMessage, declineMessageKey, formatAvailabilityMessage, formatMoney, formatVoucherExpiry, generateIdempotencyKey, hreflangLinks, isStorefrontError, isTokenPublishable, negotiateLocale, normalizeClientIp, normalizeNote, parseMoney, parseProductFilters, paymentErrorView, paymentIdempotencyKey, paymentMessages, paymentOutcome, preferredLanguages, productAvailability, productListQuery, promoProgressView, promotionMessages, resolveAvailability, rewardLabel, safeHttpsUrl, schemaAvailability, storefrontDomainHeader, suggestMarket, toMoney, visitorCountry };
|
package/dist/index.js
CHANGED
|
@@ -203,6 +203,7 @@ function createStorefrontClient(config) {
|
|
|
203
203
|
headers.set("Accept", "application/json");
|
|
204
204
|
headers.set("Authorization", `Bearer ${config.token}`);
|
|
205
205
|
if (config.locale) headers.set("Accept-Language", config.locale);
|
|
206
|
+
if (config.domain) headers.set("X-Storefront-Domain", storefrontDomainHeader(config.domain, config.market));
|
|
206
207
|
const customerToken = options.customerToken !== void 0 ? options.customerToken : config.customerToken;
|
|
207
208
|
if (customerToken) headers.set("X-Customer-Token", customerToken);
|
|
208
209
|
const clientIp = options.clientIp !== void 0 ? options.clientIp : config.clientIp;
|
|
@@ -405,6 +406,11 @@ function createStorefrontClient(config) {
|
|
|
405
406
|
};
|
|
406
407
|
return client;
|
|
407
408
|
}
|
|
409
|
+
function storefrontDomainHeader(domain, market) {
|
|
410
|
+
const host = domain.trim();
|
|
411
|
+
const code = (market ?? "").trim().toLowerCase();
|
|
412
|
+
return code && /^[a-z0-9_-]{1,32}$/.test(code) ? `${host}#${code}` : host;
|
|
413
|
+
}
|
|
408
414
|
|
|
409
415
|
// src/payments.ts
|
|
410
416
|
var ES = {
|
|
@@ -826,6 +832,65 @@ function promoProgressView(promotions, locale, overrides) {
|
|
|
826
832
|
return { discount, shipping };
|
|
827
833
|
}
|
|
828
834
|
|
|
829
|
-
|
|
835
|
+
// src/market.ts
|
|
836
|
+
var COUNTRY_HEADERS = ["cf-ipcountry", "cloudfront-viewer-country", "x-vercel-ip-country", "x-appengine-country", "x-country-code"];
|
|
837
|
+
var BOT = /bot|crawl|spider|slurp|bing|facebookexternalhit|embedly|preview|lighthouse|headless|curl|wget/i;
|
|
838
|
+
function visitorCountry(headers, { trustProxy = true } = {}) {
|
|
839
|
+
if (!trustProxy) return null;
|
|
840
|
+
for (const name of COUNTRY_HEADERS) {
|
|
841
|
+
const value = headers.get(name)?.trim().toUpperCase();
|
|
842
|
+
if (value && /^[A-Z]{2}$/.test(value) && value !== "XX" && value !== "T1") return value;
|
|
843
|
+
}
|
|
844
|
+
return null;
|
|
845
|
+
}
|
|
846
|
+
function preferredLanguages(acceptLanguage) {
|
|
847
|
+
if (!acceptLanguage) return [];
|
|
848
|
+
return acceptLanguage.split(",").map((part, index) => {
|
|
849
|
+
const [tag = "", ...params] = part.trim().split(";");
|
|
850
|
+
const q = Number(params.find((p) => p.trim().startsWith("q="))?.split("=")[1] ?? 1);
|
|
851
|
+
return { tag: tag.trim().toLowerCase(), q: Number.isFinite(q) ? q : 0, index };
|
|
852
|
+
}).filter((l) => l.tag && l.tag !== "*" && l.q > 0).sort((a, b) => b.q - a.q || a.index - b.index).map((l) => l.tag);
|
|
853
|
+
}
|
|
854
|
+
function negotiateLocale(acceptLanguage, available, fallback) {
|
|
855
|
+
const lowered = new Map(available.map((l) => [l.toLowerCase(), l]));
|
|
856
|
+
for (const wanted of preferredLanguages(acceptLanguage)) {
|
|
857
|
+
const exact = lowered.get(wanted);
|
|
858
|
+
if (exact) return exact;
|
|
859
|
+
const base = wanted.split("-")[0];
|
|
860
|
+
const sameBase = available.find((l) => l.toLowerCase().split("-")[0] === base);
|
|
861
|
+
if (sameBase) return sameBase;
|
|
862
|
+
}
|
|
863
|
+
return fallback;
|
|
864
|
+
}
|
|
865
|
+
function suggestMarket({
|
|
866
|
+
headers,
|
|
867
|
+
markets,
|
|
868
|
+
current,
|
|
869
|
+
chosen,
|
|
870
|
+
trustProxy = true
|
|
871
|
+
}) {
|
|
872
|
+
if (chosen || markets.length < 2) return null;
|
|
873
|
+
if (BOT.test(headers.get("user-agent") ?? "")) return null;
|
|
874
|
+
const country = visitorCountry(headers, { trustProxy });
|
|
875
|
+
if (country) {
|
|
876
|
+
const target = markets.find((m) => m.countries.includes(country));
|
|
877
|
+
return target && target.code !== current ? { market: target, reason: "country" } : null;
|
|
878
|
+
}
|
|
879
|
+
for (const tag of preferredLanguages(headers.get("accept-language"))) {
|
|
880
|
+
const region = tag.split("-")[1]?.toUpperCase();
|
|
881
|
+
if (!region) continue;
|
|
882
|
+
const target = markets.find((m) => m.countries.includes(region));
|
|
883
|
+
if (target) return target.code !== current ? { market: target, reason: "language" } : null;
|
|
884
|
+
}
|
|
885
|
+
return null;
|
|
886
|
+
}
|
|
887
|
+
function hreflangLinks(markets, path, defaultMarket, protocol = "https") {
|
|
888
|
+
const links = markets.filter((m) => m.host).map((m) => ({ hrefLang: m.locale, href: `${protocol}://${m.host}${path}` }));
|
|
889
|
+
const fallback = markets.find((m) => m.code === defaultMarket && m.host);
|
|
890
|
+
if (fallback) links.push({ hrefLang: "x-default", href: `${protocol}://${fallback.host}${path}` });
|
|
891
|
+
return links;
|
|
892
|
+
}
|
|
893
|
+
|
|
894
|
+
export { CLIENT_IP_HEADER, COUNTRY_HEADERS, NOTE_MAX_LENGTH, StorefrontError, appendParams, clampPercent, createStorefrontClient, declineMessage, declineMessageKey, formatMoney, formatVoucherExpiry, generateIdempotencyKey, hreflangLinks, isStorefrontError, isTokenPublishable, negotiateLocale, normalizeClientIp, normalizeNote, parseMoney, parseProductFilters, paymentErrorView, paymentIdempotencyKey, paymentMessages, paymentOutcome, preferredLanguages, productListQuery, promoProgressView, promotionMessages, rewardLabel, safeHttpsUrl, storefrontDomainHeader, suggestMarket, toMoney, visitorCountry };
|
|
830
895
|
//# sourceMappingURL=index.js.map
|
|
831
896
|
//# sourceMappingURL=index.js.map
|