@asteby/storefront 0.5.0 → 0.7.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/dist/index.d.cts CHANGED
@@ -1,7 +1,7 @@
1
1
  import { GeoPoint } from './geo.cjs';
2
2
  export { OpenStatus, OpeningHours } from './geo.cjs';
3
- import { $ as ProductSort, a7 as Shop, Z as ProductConnection, W as Product, q as Connection, p as Collection, G as LocationListParams, L as Location, O as NearestLocation, ab as StockLevel, i as CartLineInput, C as Cart, k as CartLineUpdateInput, d as CartBuyerInput, a as AddressInput, w as DeliveryOptions, U as PaymentMethods, D as DeliveryInput, m as CheckoutInput, o as CheckoutResult, s as CustomerIdentifier, u as CustomerSessionInput, t as CustomerSession, r as Customer, P as Order, a2 as ReturnRequestInput, a1 as ReturnRequest, y as InvoiceRequest, z as InvoiceResult, A as Address, ae as Theme, R as Page, M as Menu, b as Article, ad as StorefrontErrorCode, K as Money } from './types-D300wdPF.cjs';
4
- export { c as CartBuyer, e as CartCost, f as CartDelivery, g as CartDiscountCode, h as CartLine, j as CartLineMerchandise, l as CheckoutInfo, n as CheckoutPayment, v as DeliveryMethod, F as Facet, x as FacetValue, I as Image, B as LocationContacts, E as LocationKind, H as LocationPickup, J as MenuItem, N as MoneyRange, Q as OrderLine, S as PageInfo, T as PaymentMethod, V as PickupOption, X as ProductAttributeValue, Y as ProductCategory, _ as ProductOption, a0 as ProductVariant, a3 as SEO, a4 as Section, a5 as SelectedOption, a6 as ShippingRate, a8 as ShopBranch, a9 as ShopContact, aa as ShopPolicies, ac as StorefrontErrorBody, af as UnavailableRateProvider } from './types-D300wdPF.cjs';
3
+ import { am as ProductSort, aB as Shop, ak as ProductConnection, ah as Product, x as Connection, w as Collection, S as LocationListParams, P as Location, Y as NearestLocation, aF as StockLevel, at as RestockSubscriptionInput, au as RestockSubscriptionResult, l as CartLineInput, C as Cart, o as CartLineUpdateInput, f as CartBuyerInput, a as AddressInput, H as DeliveryOptions, a9 as PaymentMethods, F as DeliveryInput, as as RecommendationPlacement, r as CartRecommendations, t as CheckoutInput, v as CheckoutResult, ar as PromotionsList, a6 as PaymentConfig, ae as PaymentSubmitInput, a2 as PaymentAttemptResult, z as CustomerIdentifier, D as CustomerSessionInput, B as CustomerSession, y as Customer, Z as Order, aw as ReturnRequestInput, av as ReturnRequest, N as InvoiceRequest, O as InvoiceResult, A as Address, aI as Theme, $ as Page, U as Menu, b as Article, aH as StorefrontErrorCode, M as InsufficientStockLine, W as Money, d as AvailabilityStatus, n as CartLineProblem, c as Availability, an as ProductVariant, E as DeclineCode, af as PaymentVoucher, aa as PaymentNextStep, p as CartPromotions, ap as PromotionReward } from './types-CSMuPSQA.cjs';
4
+ export { e as CartBuyer, g as CartCost, h as CartDelivery, i as CartDiscountCode, j as CartDiscountLine, k as CartLine, m as CartLineMerchandise, q as CartRecommendation, s as CheckoutInfo, u as CheckoutPayment, G as DeliveryMethod, I as Facet, J as FacetValue, K as Image, L as InsufficientStockDetails, Q as LocationContacts, R as LocationKind, T as LocationPickup, V as MenuItem, X as MoneyRange, _ as OrderLine, a0 as PageInfo, a1 as PaymentAction, a3 as PaymentAttemptStatus, a4 as PaymentClient, a5 as PaymentClientCsp, a7 as PaymentFlow, a8 as PaymentMethod, ab as PaymentOption, ac as PaymentOptionKey, ad as PaymentPoint, ag as PickupOption, ai as ProductAttributeValue, aj as ProductCategory, al as ProductOption, ao as PromotionAnnouncement, aq as PromotionTier, ax as SEO, ay as Section, az as SelectedOption, aA as ShippingRate, aC as ShopBranch, aD as ShopContact, aE as ShopPolicies, aG as StorefrontErrorBody, aJ as UnavailableRateProvider } from './types-CSMuPSQA.cjs';
5
5
 
6
6
  type QueryValue = string | number | boolean | null | undefined;
7
7
  type QueryParams = Record<string, QueryValue | QueryValue[]>;
@@ -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
  /**
@@ -193,6 +205,13 @@ declare function createStorefrontClient(config: StorefrontClientOptions): {
193
205
  nodes: StockLevel[];
194
206
  }>;
195
207
  };
208
+ restock: {
209
+ /**
210
+ * "Notify me when it is back". Idempotent per contact + SKU; always answers `202 pending`.
211
+ * Only offered when `availability.notify_available` is true. Needs `customer:write`.
212
+ */
213
+ subscribe: (input: RestockSubscriptionInput, options?: WriteOptions) => Promise<RestockSubscriptionResult>;
214
+ };
196
215
  carts: {
197
216
  create: (input?: {
198
217
  lines?: CartLineInput[];
@@ -210,10 +229,34 @@ declare function createStorefrontClient(config: StorefrontClientOptions): {
210
229
  /** Payment providers active for the cart's market; send the chosen `key` as `payment_method`. */
211
230
  paymentMethods: (id: string, options?: RequestOptions) => Promise<PaymentMethods>;
212
231
  setDelivery: (id: string, delivery: DeliveryInput, options?: WriteOptions) => Promise<Cart>;
232
+ /** Order note (up to 1000 characters). `null` or empty clears it. */
233
+ setNote: (id: string, note: string | null, options?: WriteOptions) => Promise<Cart>;
234
+ /**
235
+ * Cross-sell suggestions: only sellable variants, with `unlocks` when adding one reaches the next
236
+ * discount tier. `first` 1-8 (default 4). `private, no-store`; ask on demand, not on every render.
237
+ */
238
+ recommendations: (id: string, params?: {
239
+ first?: number;
240
+ placement?: RecommendationPlacement;
241
+ }, options?: RequestOptions) => Promise<CartRecommendations>;
213
242
  /** Reserves stock and creates the order. Sends `Idempotency-Key` (auto-generated if omitted). */
214
243
  checkout: (id: string, input: CheckoutInput, options?: WriteOptions) => Promise<CheckoutResult>;
215
244
  };
245
+ promotions: {
246
+ /** Public promotions and the entry pop-up announcement for the request's market (cacheable). */
247
+ list: (options?: RequestOptions) => Promise<PromotionsList>;
248
+ };
216
249
  checkouts: {
250
+ /** What to mount for an embedded checkout (public key, options, amount) and whether it can still be paid. Never cache. */
251
+ paymentConfig: (id: string, options?: RequestOptions) => Promise<PaymentConfig | null>;
252
+ /**
253
+ * Pays a checkout in the page. The browser brings only what the provider SDK returned (`provider_data`);
254
+ * the amount is the server's. `idempotencyKey` is REQUIRED (use `paymentIdempotencyKey(checkoutId, attempt)`):
255
+ * repeating a call with the same key never charges twice. Needs a server token (`checkout:write`).
256
+ */
257
+ pay: (id: string, input: PaymentSubmitInput, options: WriteOptions & {
258
+ idempotencyKey: string;
259
+ }) => Promise<PaymentAttemptResult>;
217
260
  /**
218
261
  * Reads a placed checkout (same shape as the `carts.checkout` result). `order.number` is `null` until OPS
219
262
  * has created the order (a few hundred ms after checkout); poll once or twice and, if it is still `null`,
@@ -254,12 +297,16 @@ declare function createStorefrontClient(config: StorefrontClientOptions): {
254
297
  };
255
298
  };
256
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;
257
302
 
258
303
  type StorefrontErrorInit = {
259
304
  status: number;
260
305
  code: StorefrontErrorCode;
261
306
  message: string;
262
307
  field?: string;
308
+ /** Raw `error.details` from the body (untrusted shape). */
309
+ details?: unknown;
263
310
  /** Request URL that failed (without credentials). */
264
311
  url?: string;
265
312
  cause?: unknown;
@@ -273,8 +320,15 @@ declare class StorefrontError extends Error {
273
320
  readonly status: number;
274
321
  readonly code: StorefrontErrorCode;
275
322
  readonly field: string | undefined;
323
+ readonly details: unknown;
276
324
  readonly url: string | undefined;
277
325
  constructor(init: StorefrontErrorInit);
326
+ /**
327
+ * Affected lines of a `409 insufficient_stock` (empty for any other error or a
328
+ * server older than contract 1.1). The cart is untouched: show the lines and
329
+ * let the buyer adjust or remove them.
330
+ */
331
+ get stockLines(): InsufficientStockLine[];
278
332
  toJSON(): {
279
333
  name: string;
280
334
  status: number;
@@ -308,4 +362,288 @@ declare function toMoney(value: number, currencyCode: string): Money;
308
362
  */
309
363
  declare function isTokenPublishable(token: string | null | undefined): boolean;
310
364
 
311
- export { Address, AddressInput, Article, type ArticleListParams, CLIENT_IP_HEADER, Cart, CartBuyerInput, CartLineInput, CartLineUpdateInput, CheckoutInput, CheckoutResult, Collection, type CollectionListParams, Connection, Customer, CustomerIdentifier, CustomerSession, CustomerSessionInput, DeliveryInput, DeliveryOptions, type FetchLike, type FormatMoneyOptions, GeoPoint, InvoiceRequest, InvoiceResult, Location, LocationListParams, Menu, Money, NearestLocation, Order, Page, type PaginationParams, PaymentMethods, type PreviewOptions, Product, ProductConnection, type ProductListParams, ProductSort, type QueryParams, type QueryValue, type RequestOptions, ReturnRequest, ReturnRequestInput, Shop, StockLevel, type StorefrontClient, type StorefrontClientOptions, StorefrontError, StorefrontErrorCode, type StorefrontErrorInit, type StorefrontRequestContext, type StorefrontResponseContext, Theme, type WriteOptions, appendParams, createStorefrontClient, formatMoney, generateIdempotencyKey, isStorefrontError, isTokenPublishable, normalizeClientIp, parseMoney, parseProductFilters, productListQuery, toMoney };
365
+ /**
366
+ * Availability presentation: the single source of the texts and states a
367
+ * storefront shows for stock. Pure functions, no DOM: safe in loaders, SSR and
368
+ * edge runtimes.
369
+ *
370
+ * The server decides the state (`Availability.status`, already filtered by the
371
+ * shop's `show_stock`); this module only turns it into labels. Shops may
372
+ * override texts by key (`messages`) but never the logic. Numbers shown here
373
+ * ("Quedan 3") come from `availability.quantity`, which the server sends only
374
+ * when the shop allows it and past its own threshold: there is no threshold in
375
+ * this code.
376
+ */
377
+
378
+ type AvailabilityTone = 'success' | 'warning' | 'danger' | 'info';
379
+ type AvailabilityLocale = 'es' | 'en';
380
+ /** Text keys, overridable per shop. `{n}` and `{date}` are replaced. */
381
+ type AvailabilityMessageKey = 'status.in_stock' | 'status.low_stock' | 'status.low_stock_n' | 'status.out_of_stock' | 'status.backorder' | 'status.preorder' | 'sub.backorder_eta' | 'sub.preorder_date' | 'cta.add' | 'cta.out_of_stock' | 'cta.backorder' | 'cta.preorder' | 'sr.in_stock' | 'sr.low_stock' | 'sr.low_stock_n' | 'sr.out_of_stock' | 'sr.backorder' | 'sr.preorder' | 'option.sold_out' | 'option.backorder' | 'option.preorder' | 'max_per_order' | 'notify.title' | 'notify.email' | 'notify.phone' | 'notify.consent' | 'notify.submit' | 'notify.done' | 'notify.error' | 'problem.out_of_stock' | 'problem.insufficient_stock' | 'problem.insufficient_stock_n' | 'problem.adjust' | 'problem.remove' | 'conflict.title' | 'conflict.body' | 'conflict.dismiss';
382
+ type AvailabilityMessages = Record<AvailabilityMessageKey, string>;
383
+ /** `es-MX` -> `es`, `en-US` -> `en`; anything else falls back to Spanish. */
384
+ declare function availabilityLocale(locale: string | null | undefined): AvailabilityLocale;
385
+ /** Catalog for a locale with the shop's per-key overrides applied. */
386
+ declare function availabilityMessages(locale: string | null | undefined, overrides?: Partial<AvailabilityMessages> | null): AvailabilityMessages;
387
+ /** Replaces `{name}` placeholders. */
388
+ declare function formatAvailabilityMessage(template: string, values?: Record<string, string | number>): string;
389
+ type ResolveAvailabilityOptions = {
390
+ /**
391
+ * Polyfill only: shop-level "low stock" threshold to derive `low_stock` from
392
+ * `quantity_available` on servers older than contract 1.1. Without it the
393
+ * polyfill never reports `low_stock`. Ignored when the server sends `availability`.
394
+ */
395
+ lowStockThreshold?: number | null;
396
+ };
397
+ type AvailabilitySource = Pick<Product | ProductVariant, 'available_for_sale'> & {
398
+ availability?: Availability | null;
399
+ quantity_available?: number | null;
400
+ };
401
+ /**
402
+ * The `Availability` of a variant or product. Uses the server's `availability`
403
+ * when present; otherwise derives `in_stock | low_stock | out_of_stock` from
404
+ * `available_for_sale` / `quantity_available` so storefronts can migrate before
405
+ * the backend does. A malformed `availability` falls back to the polyfill.
406
+ */
407
+ declare function resolveAvailability(source: AvailabilitySource, options?: ResolveAvailabilityOptions): Availability;
408
+ type AvailabilityView = {
409
+ status: AvailabilityStatus;
410
+ tone: AvailabilityTone;
411
+ /** Short badge text: "Agotado", "Quedan 3", "Bajo pedido"... Never empty. */
412
+ label: string;
413
+ /** ETA / release date text, or `null`. */
414
+ sublabel: string | null;
415
+ /** Primary button text. */
416
+ ctaLabel: string;
417
+ /** The primary button may be pressed (backorder/preorder are purchasable). */
418
+ ctaEnabled: boolean;
419
+ /** Offer the "Notify me when it is back" form. */
420
+ showNotify: boolean;
421
+ /** Full sentence for screen readers. */
422
+ srText: string;
423
+ /** Units to cap the quantity picker at, or `null`. */
424
+ maxPerOrder: number | null;
425
+ /** Show a badge on listing cards (everything but plain `in_stock`: do not clutter). */
426
+ showOnCard: boolean;
427
+ };
428
+ type AvailabilityViewOptions = {
429
+ messages?: Partial<AvailabilityMessages> | null;
430
+ };
431
+ /**
432
+ * Texts and flags for one availability. `availability` accepts anything with an
433
+ * unknown `status` (a future server): it is treated as `out_of_stock`.
434
+ */
435
+ declare function availabilityView(availability: Availability, locale: string | null | undefined, options?: AvailabilityViewOptions): AvailabilityView;
436
+ type CartLineProblemView = {
437
+ code: CartLineProblem['code'];
438
+ /** "Ya no hay existencias" / "Solo puedes llevar 2". */
439
+ message: string;
440
+ /** Quantity to adjust the line to, or `null` (remove only). */
441
+ adjustTo: number | null;
442
+ adjustLabel: string | null;
443
+ removeLabel: string;
444
+ };
445
+ /** Texts and actions for a cart line problem. Offers "Adjust to N" only when `max_quantity` is a positive number different from the line quantity (a refused change leaves the cart at its old quantity). */
446
+ declare function cartLineProblemView(problem: CartLineProblem | InsufficientStockLine, locale: string | null | undefined, options?: AvailabilityViewOptions & {
447
+ lineQuantity?: number;
448
+ }): CartLineProblemView;
449
+ /** schema.org `ItemAvailability` URL for a status (unknown -> `OutOfStock`). */
450
+ declare function schemaAvailability(status: AvailabilityStatus | string): string;
451
+ /** Best state among variants for a product summary (when the server sends none). */
452
+ declare function productAvailability(product: Pick<Product, 'variants' | 'available_for_sale' | 'availability'>, options?: ResolveAvailabilityOptions): Availability;
453
+
454
+ /**
455
+ * Embedded payment presentation: the single source of the texts and states a
456
+ * storefront shows around an in-page payment. Pure functions, no DOM: safe in
457
+ * loaders, SSR and edge runtimes.
458
+ *
459
+ * Rules this module encodes (DISENO-PAGO-INTEGRADO-BRICKS):
460
+ * - The server decides the outcome and the amount. Nothing here computes or
461
+ * compares money; `expected_total` is the server's own number echoed back.
462
+ * - A rejection is explained with an actionable text built from the neutral
463
+ * `decline_code`. A raw provider code is never shown.
464
+ * - Vouchers and redirects only trust https URLs.
465
+ */
466
+
467
+ type PaymentLocale = AvailabilityLocale;
468
+ type PaymentMessageKey = `decline.${DeclineCode}` | 'state.loading' | 'state.processing' | 'state.approved' | 'state.in_review' | 'state.voucher' | 'state.challenge' | 'state.expired' | 'state.cancelled' | 'state.redirect' | 'state.fallback' | 'error.network' | 'error.card_data' | 'error.price_mismatch' | 'error.payment_in_progress' | 'error.expired' | 'error.too_many_attempts' | 'error.generic' | 'action.retry' | 'action.try_again' | 'action.pay' | 'action.cancel' | 'voucher.title' | 'voucher.point' | 'voucher.reference' | 'voucher.amount' | 'voucher.expires' | 'voucher.clabe' | 'voucher.barcode' | 'voucher.open' | 'voucher.email' | 'form.label' | 'challenge.title';
469
+ type PaymentMessages = Record<PaymentMessageKey, string>;
470
+ /** Catalog for a locale (`es-MX` -> es, `en-US` -> en) with the shop's per-key overrides applied. */
471
+ declare function paymentMessages(locale: string | null | undefined, overrides?: Partial<PaymentMessages> | null): PaymentMessages;
472
+ /** i18n key of a decline code; unknown or missing codes map to `declined` (never a raw provider code). */
473
+ declare function declineMessageKey(code: string | null | undefined): `decline.${DeclineCode}`;
474
+ /** Actionable text for a decline code. */
475
+ declare function declineMessage(code: string | null | undefined, locale?: string | null, overrides?: Partial<PaymentMessages> | null): string;
476
+ /** The value only if it is an absolute `https:` URL without credentials; `null` otherwise. */
477
+ declare function safeHttpsUrl(value: unknown): string | null;
478
+ /** What the UI should do with an attempt result. */
479
+ type PaymentOutcomeKind = 'approved' | 'in_review' | 'voucher' | 'challenge' | 'redirect' | 'rejected' | 'expired' | 'cancelled';
480
+ /** A voucher ready to render: https-only link and the amount as `Money` (or `null` when the server sent none). */
481
+ type PaymentOutcomeVoucher = Omit<PaymentVoucher, 'amount' | 'voucher_url' | 'currency'> & {
482
+ voucher_url: string | null;
483
+ amount: Money | null;
484
+ };
485
+ type PaymentOutcome = {
486
+ kind: PaymentOutcomeKind;
487
+ /** Text for the buyer (no codes). */
488
+ message: string;
489
+ decline_code: DeclineCode | null;
490
+ /** `true` when the buyer can try again with the checkout still open. */
491
+ retryable: boolean;
492
+ /** https-only. */
493
+ redirect_url: string | null;
494
+ challenge: {
495
+ url: string | null;
496
+ creq: string | null;
497
+ } | null;
498
+ voucher: PaymentOutcomeVoucher | null;
499
+ attempts_left: number | null;
500
+ /** What to do next (server value, or derived from the decline code): fix the card, use another, rebuild the cart... */
501
+ next_step: PaymentNextStep | null;
502
+ };
503
+ /**
504
+ * Classifies an attempt result. Unknown statuses are treated as `in_review`
505
+ * (never as approved, never as a reason to charge again).
506
+ */
507
+ declare function paymentOutcome(result: PaymentAttemptResult, locale?: string | null, overrides?: Partial<PaymentMessages> | null): PaymentOutcome;
508
+ /** Failures of `POST /payment` the form maps to a specific text and next step. */
509
+ type PaymentErrorKind = 'network' | 'price_mismatch' | 'payment_in_progress' | 'expired' | 'too_many_attempts' | 'generic';
510
+ type PaymentErrorView = {
511
+ kind: PaymentErrorKind;
512
+ message: string;
513
+ retrySameAttempt: boolean;
514
+ canRetry: boolean;
515
+ };
516
+ /**
517
+ * Maps a transport error (a `StorefrontError` or anything with `status`/`code`)
518
+ * to a view. Network failures and 502/503/504 are safe to retry with the same
519
+ * idempotency key; 409/422/429 are answers, not outages.
520
+ */
521
+ declare function paymentErrorView(err: unknown, locale?: string | null, overrides?: Partial<PaymentMessages> | null): PaymentErrorView;
522
+ /** `Vence el 5 de noviembre de 2026, 18:00` style text for a voucher deadline; `null` when absent or invalid. */
523
+ declare function formatVoucherExpiry(expiresAt: string | null | undefined, locale?: string | null, overrides?: Partial<PaymentMessages> | null): string | null;
524
+ /** Builds an idempotency key for a payment attempt: the same attempt always yields the same key. */
525
+ declare function paymentIdempotencyKey(checkoutId: string, attempt: string): string;
526
+
527
+ /**
528
+ * Promotion presentation: turns `Cart.promotions` (computed by OPS) into the
529
+ * labels and bar states a storefront shows. Pure functions, no DOM.
530
+ *
531
+ * The shop never computes a discount here: `remaining`, `percent`, `reached`
532
+ * and `percent_of_max` are the server's. The only arithmetic is clamping a
533
+ * server percentage into 0-100 for `aria-valuenow`.
534
+ */
535
+
536
+ type PromotionLocale = AvailabilityLocale;
537
+ type PromotionMessageKey = 'discount.next' | 'discount.next_upgrade' | 'discount.applied' | 'discount.max' | 'discount.suppressed' | 'discount.unlocks' | 'discount.remaining' | 'shipping.progress' | 'shipping.reached' | 'shipping.always' | 'reward.free_shipping' | 'bar.discount' | 'bar.shipping' | 'popup.close' | 'popup.valid_until' | 'popup.limited_time' | 'popup.discount_tier' | 'popup.free_shipping_min' | 'popup.free_shipping_always' | 'upsell.title' | 'upsell.subtitle' | 'upsell.next_level' | 'upsell.you_miss' | 'upsell.unlocks_reward' | 'upsell.total_saved' | 'upsell.current_benefit' | 'upsell.status' | 'upsell.status_pending' | 'upsell.status_active' | 'upsell.next_reward' | 'upsell.suggestions' | 'upsell.add' | 'upsell.adding' | 'upsell.flavor' | 'upsell.unlocks_badge' | 'upsell.unlocked' | 'upsell.best' | 'upsell.skip' | 'upsell.continue' | 'upsell.footer' | 'upsell.sold_out' | 'upsell.error' | 'note.label' | 'note.placeholder' | 'note.save' | 'note.saving' | 'note.saved' | 'note.error' | 'note.counter';
538
+ type PromotionMessages = Record<PromotionMessageKey, string>;
539
+ /** Catalog for a locale with the shop's per-key overrides applied. */
540
+ declare function promotionMessages(locale: string | null | undefined, overrides?: Partial<PromotionMessages> | null): PromotionMessages;
541
+ /** Order-note limit enforced by the server (`validateNote`). */
542
+ declare const NOTE_MAX_LENGTH = 1000;
543
+ /** Trims and caps a note; empty becomes `null` (the server clears the note). */
544
+ declare function normalizeNote(note: string | null | undefined): string | null;
545
+ /** `5 %` / `7.5 %` (es) or `5%` (en) / `$250` / `Envío gratis`. The values are the server's. */
546
+ declare function rewardLabel(reward: PromotionReward, locale?: string | null, overrides?: Partial<PromotionMessages> | null): string;
547
+ /** Display clamp for a server percentage (`aria-valuenow`). Not a calculation. */
548
+ declare function clampPercent(value: unknown): number;
549
+ type DiscountBarState = 'next' | 'upgrade' | 'max' | 'suppressed';
550
+ type DiscountBarView = {
551
+ state: DiscountBarState;
552
+ /** Server progress 0-100. */
553
+ percent: number;
554
+ text: string;
555
+ /** `aria-valuetext`. */
556
+ valueText: string;
557
+ remaining: Money | null;
558
+ /** Reward of the next tier (or the applied one at max). */
559
+ rewardText: string | null;
560
+ appliedText: string | null;
561
+ nextText: string | null;
562
+ tiers: {
563
+ id: string;
564
+ label: string;
565
+ threshold: string;
566
+ reached: boolean;
567
+ position: number;
568
+ }[];
569
+ };
570
+ type ShippingBarState = 'progress' | 'reached' | 'always';
571
+ type ShippingBarView = {
572
+ state: ShippingBarState;
573
+ percent: number;
574
+ text: string;
575
+ valueText: string;
576
+ remaining: Money | null;
577
+ /** `false` for `always`: there is nothing to fill. */
578
+ showBar: boolean;
579
+ };
580
+ type PromoProgressView = {
581
+ discount: DiscountBarView | null;
582
+ shipping: ShippingBarView | null;
583
+ };
584
+ /**
585
+ * Bars of the cart and the pre-payment modal. Both read the same `Cart.promotions`,
586
+ * so they can never disagree. `null`/absent promotions give `{ discount: null, shipping: null }`.
587
+ */
588
+ declare function promoProgressView(promotions: CartPromotions | null | undefined, locale?: string | null, overrides?: Partial<PromotionMessages> | null): PromoProgressView;
589
+
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 };