@vtex/faststore 0.0.8 → 0.0.10

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.
Files changed (151) hide show
  1. package/CHANGELOG.md +59 -0
  2. package/dist/actions.json +42 -0
  3. package/dist/catalog.json +731 -16
  4. package/dist/cli/index.js +1 -1
  5. package/dist/cli/index.js.map +1 -1
  6. package/dist/components.d.ts +9 -4
  7. package/dist/components.js +11 -6
  8. package/dist/content-types.json +300 -0
  9. package/dist/core/dist/define-component.d.ts +6 -1
  10. package/dist/docs/account-entry-point.md +4 -4
  11. package/dist/docs/account-menu.md +2 -2
  12. package/dist/docs/account-saved-cards.md +301 -0
  13. package/dist/docs/binding-switcher.md +154 -0
  14. package/dist/docs/card-entry.md +3 -3
  15. package/dist/docs/card-summary.md +175 -0
  16. package/dist/docs/cart-destination.md +238 -0
  17. package/dist/docs/cart-item.md +21 -3
  18. package/dist/docs/cart-totalizer.md +1 -1
  19. package/dist/docs/cart.md +118 -11
  20. package/dist/docs/footer-social.md +1 -1
  21. package/dist/docs/free-shipping-progress.md +13 -9
  22. package/dist/docs/incentives.md +2 -2
  23. package/dist/docs/logo.md +1 -1
  24. package/dist/docs/payment-method-badges.md +2 -2
  25. package/dist/docs/payment-methods.md +1 -1
  26. package/dist/docs/product-information.md +2 -2
  27. package/dist/docs/product-shelf.md +7 -0
  28. package/dist/docs/select.md +3 -4
  29. package/dist/docs/sign-in-redirect.md +1 -1
  30. package/dist/docs/toast.md +30 -12
  31. package/dist/index.d.ts +2 -1
  32. package/dist/internal/client.d.ts +4 -1
  33. package/dist/internal/client.js +4 -1
  34. package/dist/internal/server.d.ts +4 -2
  35. package/dist/internal/server.js +4 -2
  36. package/dist/matter/account-saved-cards/account-saved-cards.d.ts +383 -0
  37. package/dist/matter/account-saved-cards/account-saved-cards.js +956 -0
  38. package/dist/matter/account-saved-cards/account-saved-cards.js.map +1 -0
  39. package/dist/matter/billing-address/billing-address.js.map +1 -1
  40. package/dist/matter/cart/cart-namespace.d.ts +5 -1
  41. package/dist/matter/cart/cart-namespace.js +5 -1
  42. package/dist/matter/cart/cart-namespace.js.map +1 -1
  43. package/dist/matter/cart/cart.d.ts +34 -4
  44. package/dist/matter/cart/cart.js +238 -28
  45. package/dist/matter/cart/cart.js.map +1 -1
  46. package/dist/matter/cart-destination/cart-destination.d.ts +222 -0
  47. package/dist/matter/cart-destination/cart-destination.js +613 -0
  48. package/dist/matter/cart-destination/cart-destination.js.map +1 -0
  49. package/dist/matter/cart-item/cart-item.d.ts +4 -2
  50. package/dist/matter/cart-item/cart-item.js +75 -16
  51. package/dist/matter/cart-item/cart-item.js.map +1 -1
  52. package/dist/matter/cart-totalizer/cart-totalizer.js +3 -2
  53. package/dist/matter/cart-totalizer/cart-totalizer.js.map +1 -1
  54. package/dist/matter/free-shipping-progress/free-shipping-progress.d.ts +6 -3
  55. package/dist/matter/free-shipping-progress/free-shipping-progress.js +76 -33
  56. package/dist/matter/free-shipping-progress/free-shipping-progress.js.map +1 -1
  57. package/dist/matter/pricing/pricing.js.map +1 -1
  58. package/dist/matter/product-shelf/product-shelf.d.ts +7 -0
  59. package/dist/matter/product-shelf/product-shelf.js +115 -1
  60. package/dist/matter/product-shelf/product-shelf.js.map +1 -1
  61. package/dist/matter/shipping-address/shipping-address.js.map +1 -1
  62. package/dist/matter/shipping-simulator/shipping-simulator.d.ts +1 -1
  63. package/dist/matter/shipping-simulator/shipping-simulator.js +6 -2
  64. package/dist/matter/shipping-simulator/shipping-simulator.js.map +1 -1
  65. package/dist/particles/binding-switcher/binding-switcher.d.ts +38 -0
  66. package/dist/particles/binding-switcher/binding-switcher.js +183 -0
  67. package/dist/particles/binding-switcher/binding-switcher.js.map +1 -0
  68. package/dist/particles/card-summary/card-network.d.ts +17 -0
  69. package/dist/particles/card-summary/card-network.js +25 -0
  70. package/dist/particles/card-summary/card-network.js.map +1 -0
  71. package/dist/particles/card-summary/card-summary.d.ts +83 -0
  72. package/dist/particles/card-summary/card-summary.js +188 -0
  73. package/dist/particles/card-summary/card-summary.js.map +1 -0
  74. package/dist/particles/footer-social/footer-social.d.ts +1 -1
  75. package/dist/particles/footer-social/footer-social.js +1 -1
  76. package/dist/particles/footer-social/footer-social.js.map +1 -1
  77. package/dist/particles/incentives/incentives.d.ts +1 -1
  78. package/dist/particles/incentives/incentives.js +1 -1
  79. package/dist/particles/incentives/incentives.js.map +1 -1
  80. package/dist/particles/payment-methods/payment-methods.d.ts +1 -1
  81. package/dist/particles/payment-methods/payment-methods.js +1 -1
  82. package/dist/particles/payment-methods/payment-methods.js.map +1 -1
  83. package/dist/particles/toast/toast.d.ts +20 -6
  84. package/dist/particles/toast/toast.js +49 -7
  85. package/dist/particles/toast/toast.js.map +1 -1
  86. package/dist/schema.graphql +298 -34
  87. package/dist/sdk/actions/index.d.ts +3 -1
  88. package/dist/sdk/actions/index.js +3 -1
  89. package/dist/sdk/actions/locale-action-provider.d.ts +16 -0
  90. package/dist/sdk/actions/locale-action-provider.js +20 -0
  91. package/dist/sdk/actions/locale-action-provider.js.map +1 -0
  92. package/dist/sdk/actions/saved-card-action-provider.d.ts +12 -0
  93. package/dist/sdk/actions/saved-card-action-provider.js +16 -0
  94. package/dist/sdk/actions/saved-card-action-provider.js.map +1 -0
  95. package/dist/sdk/actions/types.d.ts +6 -2
  96. package/dist/sdk/cache/storefront-cache.js +2 -1
  97. package/dist/sdk/cache/storefront-cache.js.map +1 -1
  98. package/dist/sdk/cart/operations.d.ts +2 -1
  99. package/dist/sdk/cart/operations.js +55 -1
  100. package/dist/sdk/cart/operations.js.map +1 -1
  101. package/dist/sdk/cart/optimistic.d.ts +30 -7
  102. package/dist/sdk/cart/optimistic.js +78 -41
  103. package/dist/sdk/cart/optimistic.js.map +1 -1
  104. package/dist/sdk/cart/use-cart-actions.d.ts +7 -0
  105. package/dist/sdk/cart/use-cart-actions.js +61 -15
  106. package/dist/sdk/cart/use-cart-actions.js.map +1 -1
  107. package/dist/sdk/cart/use-cart-sync-sales-channel.js +70 -0
  108. package/dist/sdk/cart/use-cart-sync-sales-channel.js.map +1 -0
  109. package/dist/sdk/generated/gql.js +15 -11
  110. package/dist/sdk/generated/gql.js.map +1 -1
  111. package/dist/sdk/generated/graphql.d.ts +177 -46
  112. package/dist/sdk/generated/graphql.js +2314 -93
  113. package/dist/sdk/generated/graphql.js.map +1 -1
  114. package/dist/sdk/i18n/messages/en-US.js +19 -0
  115. package/dist/sdk/i18n/messages/en-US.json +19 -0
  116. package/dist/sdk/i18n/messages/en-US.json.d.ts +19 -0
  117. package/dist/sdk/i18n/messages/es-ES.js +19 -0
  118. package/dist/sdk/i18n/messages/es-ES.json +19 -0
  119. package/dist/sdk/i18n/messages/pt-BR.js +19 -0
  120. package/dist/sdk/i18n/messages/pt-BR.json +19 -0
  121. package/dist/sdk/i18n/provider.js +1 -1
  122. package/dist/sdk/i18n/provider.js.map +1 -1
  123. package/dist/sdk/locale/binding-switch-provider.d.ts +13 -0
  124. package/dist/sdk/locale/binding-switch-provider.js +20 -0
  125. package/dist/sdk/locale/binding-switch-provider.js.map +1 -0
  126. package/dist/sdk/locale/use-locale-actions.d.ts +16 -0
  127. package/dist/sdk/locale/use-locale-actions.js +70 -0
  128. package/dist/sdk/locale/use-locale-actions.js.map +1 -0
  129. package/dist/sdk/navigation/binding-switch.d.ts +39 -0
  130. package/dist/sdk/navigation/binding-switch.js +129 -0
  131. package/dist/sdk/navigation/binding-switch.js.map +1 -0
  132. package/dist/sdk/navigation/decode-route-params.d.ts +11 -0
  133. package/dist/sdk/navigation/decode-route-params.js +32 -0
  134. package/dist/sdk/navigation/decode-route-params.js.map +1 -0
  135. package/dist/sdk/saved-cards/operations.d.ts +16 -0
  136. package/dist/sdk/saved-cards/operations.js +41 -0
  137. package/dist/sdk/saved-cards/operations.js.map +1 -0
  138. package/dist/sdk/saved-cards/use-saved-card-actions.d.ts +26 -0
  139. package/dist/sdk/saved-cards/use-saved-card-actions.js +86 -0
  140. package/dist/sdk/saved-cards/use-saved-card-actions.js.map +1 -0
  141. package/dist/sdk/seo/page-alternates.d.ts +5 -3
  142. package/dist/sdk/seo/page-alternates.js +7 -12
  143. package/dist/sdk/seo/page-alternates.js.map +1 -1
  144. package/dist/sdk/shipping/shipping-operations.d.ts +1 -1
  145. package/dist/sdk/shipping/shipping-operations.js +13 -0
  146. package/dist/sdk/shipping/shipping-operations.js.map +1 -1
  147. package/dist/sdk/state/cart.d.ts +9 -2
  148. package/dist/sdk/state/cart.js +9 -2
  149. package/dist/sdk/state/cart.js.map +1 -1
  150. package/package.json +2 -2
  151. package/src/styles/tailwind.css +1 -2
@@ -64,6 +64,7 @@ var en_US_default = {
64
64
  "fs/address.field.town": "Town",
65
65
  "fs/address.placeholder.postal-code": "Example: {example}",
66
66
  "fs/address.placeholder.street": "Address line 1",
67
+ "fs/card.ending-in": "ending in {lastDigits}",
67
68
  "fs/form.clear": "Clear",
68
69
  "fs/form.reset": "Reset",
69
70
  "fs/form.submit": "Continue",
@@ -143,6 +144,24 @@ var en_US_default = {
143
144
  "fs/region.error.network": "Unable to set your region. Please try again.",
144
145
  "fs/region.error.unserved-postal-code": "We do not deliver to this postal code yet.",
145
146
  "fs/route.not-found": "Page not found",
147
+ "fs/saved-cards.empty": "You have no saved cards yet.",
148
+ "fs/saved-cards.expired": "Expired",
149
+ "fs/saved-cards.expired-no-date": "Expired",
150
+ "fs/saved-cards.expires": "Expires",
151
+ "fs/saved-cards.heading": "Saved cards",
152
+ "fs/saved-cards.load-failed": "We couldn't load your saved cards. Please try again later.",
153
+ "fs/saved-cards.loading": "Loading your saved cards…",
154
+ "fs/saved-cards.remove.cancel": "Cancel",
155
+ "fs/saved-cards.remove.confirm": "Remove card",
156
+ "fs/saved-cards.remove.description": "It will no longer be offered when you pay. This can't be undone.",
157
+ "fs/saved-cards.remove.failed": "We couldn't remove the card. Please try again.",
158
+ "fs/saved-cards.remove.pending": "Removing…",
159
+ "fs/saved-cards.remove.success": "The card was removed.",
160
+ "fs/saved-cards.remove.title": "Remove this card?",
161
+ "fs/saved-cards.remove.trigger": "Remove",
162
+ "fs/saved-cards.session-expired": "Your session has expired.",
163
+ "fs/saved-cards.sign-in": "Sign in again",
164
+ "fs/saved-cards.unusable": "This card can't be used",
146
165
  "fs/shipping.calculating": "Calculating…",
147
166
  "fs/shipping.calculation-failed": "Unable to calculate shipping. Try again.",
148
167
  "fs/shipping.estimate.in-business-days": "in {days, plural, one {# business day} other {# business days}}",
@@ -63,6 +63,7 @@
63
63
  "fs/address.field.town": "Town",
64
64
  "fs/address.placeholder.postal-code": "Example: {example}",
65
65
  "fs/address.placeholder.street": "Address line 1",
66
+ "fs/card.ending-in": "ending in {lastDigits}",
66
67
  "fs/form.clear": "Clear",
67
68
  "fs/form.reset": "Reset",
68
69
  "fs/form.submit": "Continue",
@@ -142,6 +143,24 @@
142
143
  "fs/region.error.network": "Unable to set your region. Please try again.",
143
144
  "fs/region.error.unserved-postal-code": "We do not deliver to this postal code yet.",
144
145
  "fs/route.not-found": "Page not found",
146
+ "fs/saved-cards.empty": "You have no saved cards yet.",
147
+ "fs/saved-cards.expired": "Expired",
148
+ "fs/saved-cards.expired-no-date": "Expired",
149
+ "fs/saved-cards.expires": "Expires",
150
+ "fs/saved-cards.heading": "Saved cards",
151
+ "fs/saved-cards.load-failed": "We couldn't load your saved cards. Please try again later.",
152
+ "fs/saved-cards.loading": "Loading your saved cards…",
153
+ "fs/saved-cards.remove.cancel": "Cancel",
154
+ "fs/saved-cards.remove.confirm": "Remove card",
155
+ "fs/saved-cards.remove.description": "It will no longer be offered when you pay. This can't be undone.",
156
+ "fs/saved-cards.remove.failed": "We couldn't remove the card. Please try again.",
157
+ "fs/saved-cards.remove.pending": "Removing…",
158
+ "fs/saved-cards.remove.success": "The card was removed.",
159
+ "fs/saved-cards.remove.title": "Remove this card?",
160
+ "fs/saved-cards.remove.trigger": "Remove",
161
+ "fs/saved-cards.session-expired": "Your session has expired.",
162
+ "fs/saved-cards.sign-in": "Sign in again",
163
+ "fs/saved-cards.unusable": "This card can't be used",
145
164
  "fs/shipping.calculating": "Calculating…",
146
165
  "fs/shipping.calculation-failed": "Unable to calculate shipping. Try again.",
147
166
  "fs/shipping.estimate.in-business-days": "in {days, plural, one {# business day} other {# business days}}",
@@ -64,6 +64,7 @@ declare const _exports: {
64
64
  "fs/address.field.town": string;
65
65
  "fs/address.placeholder.postal-code": string;
66
66
  "fs/address.placeholder.street": string;
67
+ "fs/card.ending-in": string;
67
68
  "fs/form.clear": string;
68
69
  "fs/form.reset": string;
69
70
  "fs/form.submit": string;
@@ -143,6 +144,24 @@ declare const _exports: {
143
144
  "fs/region.error.network": string;
144
145
  "fs/region.error.unserved-postal-code": string;
145
146
  "fs/route.not-found": string;
147
+ "fs/saved-cards.empty": string;
148
+ "fs/saved-cards.expired": string;
149
+ "fs/saved-cards.expired-no-date": string;
150
+ "fs/saved-cards.expires": string;
151
+ "fs/saved-cards.heading": string;
152
+ "fs/saved-cards.load-failed": string;
153
+ "fs/saved-cards.loading": string;
154
+ "fs/saved-cards.remove.cancel": string;
155
+ "fs/saved-cards.remove.confirm": string;
156
+ "fs/saved-cards.remove.description": string;
157
+ "fs/saved-cards.remove.failed": string;
158
+ "fs/saved-cards.remove.pending": string;
159
+ "fs/saved-cards.remove.success": string;
160
+ "fs/saved-cards.remove.title": string;
161
+ "fs/saved-cards.remove.trigger": string;
162
+ "fs/saved-cards.session-expired": string;
163
+ "fs/saved-cards.sign-in": string;
164
+ "fs/saved-cards.unusable": string;
146
165
  "fs/shipping.calculating": string;
147
166
  "fs/shipping.calculation-failed": string;
148
167
  "fs/shipping.estimate.in-business-days": string;
@@ -64,6 +64,7 @@ var es_ES_default = {
64
64
  "fs/address.field.town": "Pueblo / ciudad",
65
65
  "fs/address.placeholder.postal-code": "Ejemplo: {example}",
66
66
  "fs/address.placeholder.street": "Dirección línea 1",
67
+ "fs/card.ending-in": "terminada en {lastDigits}",
67
68
  "fs/form.clear": "Limpiar",
68
69
  "fs/form.reset": "Restablecer",
69
70
  "fs/form.submit": "Continuar",
@@ -143,6 +144,24 @@ var es_ES_default = {
143
144
  "fs/region.error.network": "No se pudo definir tu región. Inténtalo de nuevo.",
144
145
  "fs/region.error.unserved-postal-code": "Todavía no hacemos entregas en este código postal.",
145
146
  "fs/route.not-found": "Página no encontrada",
147
+ "fs/saved-cards.empty": "Aún no tienes tarjetas guardadas.",
148
+ "fs/saved-cards.expired": "Venció el",
149
+ "fs/saved-cards.expired-no-date": "Vencida",
150
+ "fs/saved-cards.expires": "Vence el",
151
+ "fs/saved-cards.heading": "Tarjetas guardadas",
152
+ "fs/saved-cards.load-failed": "No pudimos cargar tus tarjetas guardadas. Inténtalo de nuevo más tarde.",
153
+ "fs/saved-cards.loading": "Cargando tus tarjetas guardadas…",
154
+ "fs/saved-cards.remove.cancel": "Cancelar",
155
+ "fs/saved-cards.remove.confirm": "Eliminar tarjeta",
156
+ "fs/saved-cards.remove.description": "Ya no se ofrecerá al pagar. Esta acción no se puede deshacer.",
157
+ "fs/saved-cards.remove.failed": "No pudimos eliminar la tarjeta. Inténtalo de nuevo.",
158
+ "fs/saved-cards.remove.pending": "Eliminando…",
159
+ "fs/saved-cards.remove.success": "La tarjeta fue eliminada.",
160
+ "fs/saved-cards.remove.title": "¿Eliminar esta tarjeta?",
161
+ "fs/saved-cards.remove.trigger": "Eliminar",
162
+ "fs/saved-cards.session-expired": "Tu sesión ha caducado.",
163
+ "fs/saved-cards.sign-in": "Iniciar sesión de nuevo",
164
+ "fs/saved-cards.unusable": "Esta tarjeta no se puede usar",
146
165
  "fs/shipping.calculating": "Calculando…",
147
166
  "fs/shipping.calculation-failed": "No se pudo calcular el envío. Inténtalo de nuevo.",
148
167
  "fs/shipping.estimate.in-business-days": "en {days, plural, one {# día hábil} other {# días hábiles}}",
@@ -63,6 +63,7 @@
63
63
  "fs/address.field.town": "Pueblo / ciudad",
64
64
  "fs/address.placeholder.postal-code": "Ejemplo: {example}",
65
65
  "fs/address.placeholder.street": "Dirección línea 1",
66
+ "fs/card.ending-in": "terminada en {lastDigits}",
66
67
  "fs/form.clear": "Limpiar",
67
68
  "fs/form.reset": "Restablecer",
68
69
  "fs/form.submit": "Continuar",
@@ -142,6 +143,24 @@
142
143
  "fs/region.error.network": "No se pudo definir tu región. Inténtalo de nuevo.",
143
144
  "fs/region.error.unserved-postal-code": "Todavía no hacemos entregas en este código postal.",
144
145
  "fs/route.not-found": "Página no encontrada",
146
+ "fs/saved-cards.empty": "Aún no tienes tarjetas guardadas.",
147
+ "fs/saved-cards.expired": "Venció el",
148
+ "fs/saved-cards.expired-no-date": "Vencida",
149
+ "fs/saved-cards.expires": "Vence el",
150
+ "fs/saved-cards.heading": "Tarjetas guardadas",
151
+ "fs/saved-cards.load-failed": "No pudimos cargar tus tarjetas guardadas. Inténtalo de nuevo más tarde.",
152
+ "fs/saved-cards.loading": "Cargando tus tarjetas guardadas…",
153
+ "fs/saved-cards.remove.cancel": "Cancelar",
154
+ "fs/saved-cards.remove.confirm": "Eliminar tarjeta",
155
+ "fs/saved-cards.remove.description": "Ya no se ofrecerá al pagar. Esta acción no se puede deshacer.",
156
+ "fs/saved-cards.remove.failed": "No pudimos eliminar la tarjeta. Inténtalo de nuevo.",
157
+ "fs/saved-cards.remove.pending": "Eliminando…",
158
+ "fs/saved-cards.remove.success": "La tarjeta fue eliminada.",
159
+ "fs/saved-cards.remove.title": "¿Eliminar esta tarjeta?",
160
+ "fs/saved-cards.remove.trigger": "Eliminar",
161
+ "fs/saved-cards.session-expired": "Tu sesión ha caducado.",
162
+ "fs/saved-cards.sign-in": "Iniciar sesión de nuevo",
163
+ "fs/saved-cards.unusable": "Esta tarjeta no se puede usar",
145
164
  "fs/shipping.calculating": "Calculando…",
146
165
  "fs/shipping.calculation-failed": "No se pudo calcular el envío. Inténtalo de nuevo.",
147
166
  "fs/shipping.estimate.in-business-days": "en {days, plural, one {# día hábil} other {# días hábiles}}",
@@ -64,6 +64,7 @@ var pt_BR_default = {
64
64
  "fs/address.field.town": "Cidade",
65
65
  "fs/address.placeholder.postal-code": "Exemplo: {example}",
66
66
  "fs/address.placeholder.street": "Endereço",
67
+ "fs/card.ending-in": "final {lastDigits}",
67
68
  "fs/form.clear": "Limpar",
68
69
  "fs/form.reset": "Redefinir",
69
70
  "fs/form.submit": "Continuar",
@@ -143,6 +144,24 @@ var pt_BR_default = {
143
144
  "fs/region.error.network": "Não foi possível definir sua região. Tente novamente.",
144
145
  "fs/region.error.unserved-postal-code": "Ainda não entregamos neste CEP.",
145
146
  "fs/route.not-found": "Página não encontrada",
147
+ "fs/saved-cards.empty": "Você ainda não tem cartões salvos.",
148
+ "fs/saved-cards.expired": "Venceu em",
149
+ "fs/saved-cards.expired-no-date": "Vencido",
150
+ "fs/saved-cards.expires": "Vence em",
151
+ "fs/saved-cards.heading": "Cartões salvos",
152
+ "fs/saved-cards.load-failed": "Não foi possível carregar seus cartões salvos. Tente novamente mais tarde.",
153
+ "fs/saved-cards.loading": "Carregando seus cartões salvos…",
154
+ "fs/saved-cards.remove.cancel": "Cancelar",
155
+ "fs/saved-cards.remove.confirm": "Remover cartão",
156
+ "fs/saved-cards.remove.description": "Ele não será mais oferecido no pagamento. Essa ação não pode ser desfeita.",
157
+ "fs/saved-cards.remove.failed": "Não foi possível remover o cartão. Tente novamente.",
158
+ "fs/saved-cards.remove.pending": "Removendo…",
159
+ "fs/saved-cards.remove.success": "O cartão foi removido.",
160
+ "fs/saved-cards.remove.title": "Remover este cartão?",
161
+ "fs/saved-cards.remove.trigger": "Remover",
162
+ "fs/saved-cards.session-expired": "Sua sessão expirou.",
163
+ "fs/saved-cards.sign-in": "Entrar novamente",
164
+ "fs/saved-cards.unusable": "Este cartão não pode ser usado",
146
165
  "fs/shipping.calculating": "Calculando…",
147
166
  "fs/shipping.calculation-failed": "Não foi possível calcular o frete. Tente novamente.",
148
167
  "fs/shipping.estimate.in-business-days": "em {days, plural, one {# dia útil} other {# dias úteis}}",
@@ -63,6 +63,7 @@
63
63
  "fs/address.field.town": "Cidade",
64
64
  "fs/address.placeholder.postal-code": "Exemplo: {example}",
65
65
  "fs/address.placeholder.street": "Endereço",
66
+ "fs/card.ending-in": "final {lastDigits}",
66
67
  "fs/form.clear": "Limpar",
67
68
  "fs/form.reset": "Redefinir",
68
69
  "fs/form.submit": "Continuar",
@@ -142,6 +143,24 @@
142
143
  "fs/region.error.network": "Não foi possível definir sua região. Tente novamente.",
143
144
  "fs/region.error.unserved-postal-code": "Ainda não entregamos neste CEP.",
144
145
  "fs/route.not-found": "Página não encontrada",
146
+ "fs/saved-cards.empty": "Você ainda não tem cartões salvos.",
147
+ "fs/saved-cards.expired": "Venceu em",
148
+ "fs/saved-cards.expired-no-date": "Vencido",
149
+ "fs/saved-cards.expires": "Vence em",
150
+ "fs/saved-cards.heading": "Cartões salvos",
151
+ "fs/saved-cards.load-failed": "Não foi possível carregar seus cartões salvos. Tente novamente mais tarde.",
152
+ "fs/saved-cards.loading": "Carregando seus cartões salvos…",
153
+ "fs/saved-cards.remove.cancel": "Cancelar",
154
+ "fs/saved-cards.remove.confirm": "Remover cartão",
155
+ "fs/saved-cards.remove.description": "Ele não será mais oferecido no pagamento. Essa ação não pode ser desfeita.",
156
+ "fs/saved-cards.remove.failed": "Não foi possível remover o cartão. Tente novamente.",
157
+ "fs/saved-cards.remove.pending": "Removendo…",
158
+ "fs/saved-cards.remove.success": "O cartão foi removido.",
159
+ "fs/saved-cards.remove.title": "Remover este cartão?",
160
+ "fs/saved-cards.remove.trigger": "Remover",
161
+ "fs/saved-cards.session-expired": "Sua sessão expirou.",
162
+ "fs/saved-cards.sign-in": "Entrar novamente",
163
+ "fs/saved-cards.unusable": "Este cartão não pode ser usado",
145
164
  "fs/shipping.calculating": "Calculando…",
146
165
  "fs/shipping.calculation-failed": "Não foi possível calcular o frete. Tente novamente.",
147
166
  "fs/shipping.estimate.in-business-days": "em {days, plural, one {# dia útil} other {# dias úteis}}",
@@ -7,7 +7,7 @@ import { IntlProvider } from "react-intl";
7
7
  * Mounted by `StorefrontProvider`, never by a store.
8
8
  *
9
9
  * The record arrives already resolved: the server merged the locale's catalog over the fallback
10
- * chain `global.json` declares, so SSR and hydration read the same bytes and only one locale reaches
10
+ * chain the store's foundation declares, so SSR and hydration read the same bytes and only one locale reaches
11
11
  * the browser.
12
12
  *
13
13
  * The tag is normalized for the same reason `getIntl` normalizes it: when no catalog ships for the
@@ -1 +1 @@
1
- {"version":3,"file":"provider.js","names":[],"sources":["../../../src/sdk/i18n/provider.tsx"],"sourcesContent":["'use client';\n\nimport type { ReactNode } from 'react';\nimport { IntlProvider } from 'react-intl';\nimport { asIntlMessages, DEFAULT_LOCALE, type Messages, normalizeLocale, onIntlError } from './locales.js';\n\nexport type MessageProviderProps = Readonly<{\n locale: string;\n messages: Messages;\n children: ReactNode;\n}>;\n\n/**\n * Mounted by `StorefrontProvider`, never by a store.\n *\n * The record arrives already resolved: the server merged the locale's catalog over the fallback\n * chain `global.json` declares, so SSR and hydration read the same bytes and only one locale reaches\n * the browser.\n *\n * The tag is normalized for the same reason `getIntl` normalizes it: when no catalog ships for the\n * declared locale the strings rendered are the source catalog's, and plural categories have to be\n * selected under the language those strings are written in. Without it a Server Component and a\n * Client Component could pluralize the same English string under different rules.\n */\nexport function MessageProvider({ locale, messages, children }: MessageProviderProps) {\n return (\n <IntlProvider\n locale={normalizeLocale(locale) ?? DEFAULT_LOCALE}\n defaultLocale={DEFAULT_LOCALE}\n messages={asIntlMessages(messages)}\n onError={onIntlError}\n >\n {children}\n </IntlProvider>\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;;;AAwBA,SAAgB,gBAAgB,EAAE,QAAQ,UAAU,YAAkC;CACpF,OACE,oBAAC,cAAD;EACE,QAAQ,gBAAgB,MAAM,KAAA;EAC9B,eAAe;EACf,UAAU,eAAe,QAAQ;EACjC,SAAS;EAER;CACW,CAAA;AAElB"}
1
+ {"version":3,"file":"provider.js","names":[],"sources":["../../../src/sdk/i18n/provider.tsx"],"sourcesContent":["'use client';\n\nimport type { ReactNode } from 'react';\nimport { IntlProvider } from 'react-intl';\nimport { asIntlMessages, DEFAULT_LOCALE, type Messages, normalizeLocale, onIntlError } from './locales.js';\n\nexport type MessageProviderProps = Readonly<{\n locale: string;\n messages: Messages;\n children: ReactNode;\n}>;\n\n/**\n * Mounted by `StorefrontProvider`, never by a store.\n *\n * The record arrives already resolved: the server merged the locale's catalog over the fallback\n * chain the store's foundation declares, so SSR and hydration read the same bytes and only one locale reaches\n * the browser.\n *\n * The tag is normalized for the same reason `getIntl` normalizes it: when no catalog ships for the\n * declared locale the strings rendered are the source catalog's, and plural categories have to be\n * selected under the language those strings are written in. Without it a Server Component and a\n * Client Component could pluralize the same English string under different rules.\n */\nexport function MessageProvider({ locale, messages, children }: MessageProviderProps) {\n return (\n <IntlProvider\n locale={normalizeLocale(locale) ?? DEFAULT_LOCALE}\n defaultLocale={DEFAULT_LOCALE}\n messages={asIntlMessages(messages)}\n onError={onIntlError}\n >\n {children}\n </IntlProvider>\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;;;AAwBA,SAAgB,gBAAgB,EAAE,QAAQ,UAAU,YAAkC;CACpF,OACE,oBAAC,cAAD;EACE,QAAQ,gBAAgB,MAAM,KAAA;EAC9B,eAAe;EACf,UAAU,eAAe,QAAQ;EACjC,SAAS;EAER;CACW,CAAA;AAElB"}
@@ -0,0 +1,13 @@
1
+ import { BindingSwitchRequest, BindingSwitchResult } from "../navigation/binding-switch.js";
2
+ import { ReactNode } from "react";
3
+ //#region src/sdk/locale/binding-switch-provider.d.ts
4
+ export type SwitchBinding = (request: BindingSwitchRequest) => Promise<BindingSwitchResult>;
5
+ export type BindingSwitchProviderProps = Readonly<{
6
+ /** The app's `switchBinding` server action. The package cannot import the generated binding table. */
7
+ switchBinding: SwitchBinding;
8
+ children: ReactNode;
9
+ }>;
10
+ /** Hands the generated app's `switchBinding` server action to `locale.set`. */
11
+ export declare function BindingSwitchProvider({ switchBinding, children }: BindingSwitchProviderProps): import("react/jsx-runtime").JSX.Element;
12
+ //#endregion
13
+ //# sourceMappingURL=binding-switch-provider.d.ts.map
@@ -0,0 +1,20 @@
1
+ "use client";
2
+ import { createContext, useContext } from "react";
3
+ import { jsx } from "react/jsx-runtime";
4
+ //#region src/sdk/locale/binding-switch-provider.tsx
5
+ const Context = createContext(null);
6
+ /** Hands the generated app's `switchBinding` server action to `locale.set`. */
7
+ function BindingSwitchProvider({ switchBinding, children }) {
8
+ return /* @__PURE__ */ jsx(Context.Provider, {
9
+ value: switchBinding,
10
+ children
11
+ });
12
+ }
13
+ /** The provided server action, or `null` outside a `BindingSwitchProvider`. */
14
+ function useSwitchBinding() {
15
+ return useContext(Context);
16
+ }
17
+ //#endregion
18
+ export { BindingSwitchProvider, useSwitchBinding };
19
+
20
+ //# sourceMappingURL=binding-switch-provider.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"binding-switch-provider.js","names":[],"sources":["../../../src/sdk/locale/binding-switch-provider.tsx"],"sourcesContent":["'use client';\n\nimport { createContext, type ReactNode, useContext } from 'react';\nimport type { BindingSwitchRequest, BindingSwitchResult } from '../navigation/binding-switch.js';\n\nexport type SwitchBinding = (request: BindingSwitchRequest) => Promise<BindingSwitchResult>;\n\nexport type BindingSwitchProviderProps = Readonly<{\n /** The app's `switchBinding` server action. The package cannot import the generated binding table. */\n switchBinding: SwitchBinding;\n children: ReactNode;\n}>;\n\nconst Context = createContext<SwitchBinding | null>(null);\n\n/** Hands the generated app's `switchBinding` server action to `locale.set`. */\nexport function BindingSwitchProvider({ switchBinding, children }: BindingSwitchProviderProps) {\n return <Context.Provider value={switchBinding}>{children}</Context.Provider>;\n}\n\n/** The provided server action, or `null` outside a `BindingSwitchProvider`. */\nexport function useSwitchBinding(): SwitchBinding | null {\n return useContext(Context);\n}\n"],"mappings":";;;;AAaA,MAAM,UAAU,cAAoC,IAAI;;AAGxD,SAAgB,sBAAsB,EAAE,eAAe,YAAwC;CAC7F,OAAO,oBAAC,QAAQ,UAAT;EAAkB,OAAO;EAAgB;CAA2B,CAAA;AAC7E;;AAGA,SAAgB,mBAAyC;CACvD,OAAO,WAAW,OAAO;AAC3B"}
@@ -0,0 +1,16 @@
1
+ import "../../core/dist/index.js";
2
+ //#region src/sdk/locale/use-locale-actions.d.ts
3
+ /**
4
+ * The `locale` action namespace. `set` switches the whole binding (locale, currency, sales channel):
5
+ * the app's server action decides the equivalent URL, or the no-equivalent fallback, and the browser
6
+ * goes there with a document navigation. `set` always resolves, never navigates on an error, and
7
+ * resolves `true` only when it started the navigation.
8
+ */
9
+ export type LocaleActions = Readonly<{
10
+ /** Switches to the binding `bindingId` names. `true` once the navigation starts; `false` after logging. */
11
+ set: (payload: {
12
+ bindingId: string;
13
+ }) => Promise<boolean>;
14
+ }>;
15
+ //#endregion
16
+ //# sourceMappingURL=use-locale-actions.d.ts.map
@@ -0,0 +1,70 @@
1
+ "use client";
2
+ import { defineActions } from "../../core/dist/actions/registry.js";
3
+ import "../../core/dist/index.js";
4
+ import { useStorefrontContext } from "../context/storefront-context.js";
5
+ import { useStorefrontRoute } from "../navigation/use-storefront-route.js";
6
+ import { loggableErrorName } from "../../utils/loggable-error-name.js";
7
+ import { useSwitchBinding } from "./binding-switch-provider.js";
8
+ import { useCallback, useMemo } from "react";
9
+ //#region src/sdk/locale/use-locale-actions.ts
10
+ /**
11
+ * The `locale` namespace hook. Reads the route at call time: `search` and `hash` come from
12
+ * `location`, because `useStorefrontRoute().search` is `null` during hydration. The page name is
13
+ * not sent: a layout has none, and the server derives the page from the path and params.
14
+ */
15
+ function useLocaleActions() {
16
+ const switchBinding = useSwitchBinding();
17
+ const { params, path } = useStorefrontRoute();
18
+ const { bindingId: currentBindingId } = useStorefrontContext();
19
+ const set = useCallback(async ({ bindingId }) => {
20
+ if (!switchBinding) {
21
+ console.error("[locale.set] no BindingSwitchProvider");
22
+ return false;
23
+ }
24
+ try {
25
+ const { url } = await switchBinding({
26
+ bindingId,
27
+ currentBindingId,
28
+ params,
29
+ path,
30
+ search: globalThis.location.search,
31
+ hash: globalThis.location.hash
32
+ });
33
+ if (url === null) {
34
+ console.error("[locale.set] no URL for the binding");
35
+ return false;
36
+ }
37
+ globalThis.location.assign(url);
38
+ return true;
39
+ } catch (error) {
40
+ console.error("[locale.set] switchBinding failed", loggableErrorName(error));
41
+ return false;
42
+ }
43
+ }, [
44
+ switchBinding,
45
+ currentBindingId,
46
+ params,
47
+ path
48
+ ]);
49
+ return useMemo(() => ({ set }), [set]);
50
+ }
51
+ defineActions({
52
+ namespace: "locale",
53
+ description: "Switches the whole binding (locale, currency, sales channel) by a document navigation to the equivalent URL, or to the destination home with a notice.",
54
+ provider: {
55
+ name: "LocaleActionProvider",
56
+ importPath: "@vtex/faststore/internal/client"
57
+ },
58
+ methods: { set: {
59
+ description: "Switch to another binding: navigates to the equivalent URL of the current page, or to the destination home with fs_notice=no-equivalent.",
60
+ payload: [{
61
+ name: "bindingId",
62
+ type: "string"
63
+ }]
64
+ } },
65
+ use: useLocaleActions
66
+ });
67
+ //#endregion
68
+ export { useLocaleActions };
69
+
70
+ //# sourceMappingURL=use-locale-actions.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-locale-actions.js","names":[],"sources":["../../../src/sdk/locale/use-locale-actions.ts"],"sourcesContent":["'use client';\n\nimport { defineActions } from '@vtex/fs-core';\nimport { useCallback, useMemo } from 'react';\nimport { loggableErrorName } from '../../utils/loggable-error-name.js';\nimport { useStorefrontContext } from '../context/storefront-context.js';\nimport { useStorefrontRoute } from '../navigation/use-storefront-route.js';\nimport { useSwitchBinding } from './binding-switch-provider.js';\n\n/**\n * The `locale` action namespace. `set` switches the whole binding (locale, currency, sales channel):\n * the app's server action decides the equivalent URL, or the no-equivalent fallback, and the browser\n * goes there with a document navigation. `set` always resolves, never navigates on an error, and\n * resolves `true` only when it started the navigation.\n */\nexport type LocaleActions = Readonly<{\n /** Switches to the binding `bindingId` names. `true` once the navigation starts; `false` after logging. */\n set: (payload: { bindingId: string }) => Promise<boolean>;\n}>;\n\n/**\n * The `locale` namespace hook. Reads the route at call time: `search` and `hash` come from\n * `location`, because `useStorefrontRoute().search` is `null` during hydration. The page name is\n * not sent: a layout has none, and the server derives the page from the path and params.\n */\nexport function useLocaleActions(): LocaleActions {\n const switchBinding = useSwitchBinding();\n const { params, path } = useStorefrontRoute();\n const { bindingId: currentBindingId } = useStorefrontContext();\n\n const set = useCallback<LocaleActions['set']>(\n async ({ bindingId }) => {\n if (!switchBinding) {\n console.error('[locale.set] no BindingSwitchProvider');\n return false;\n }\n try {\n const { url } = await switchBinding({\n bindingId,\n currentBindingId,\n params,\n path,\n search: globalThis.location.search,\n hash: globalThis.location.hash,\n });\n if (url === null) {\n console.error('[locale.set] no URL for the binding');\n return false;\n }\n globalThis.location.assign(url);\n return true;\n } catch (error) {\n console.error('[locale.set] switchBinding failed', loggableErrorName(error));\n return false;\n }\n },\n [switchBinding, currentBindingId, params, path],\n );\n\n return useMemo(() => ({ set }), [set]);\n}\n\n/**\n * Declares the `locale` action namespace. The native catalog builder reads this call to emit\n * `actions.json`; at runtime `LocaleActionProvider` calls `useLocaleActions` directly.\n */\nexport const localeActions = defineActions({\n namespace: 'locale',\n description:\n 'Switches the whole binding (locale, currency, sales channel) by a document navigation to the equivalent URL, or to the destination home with a notice.',\n provider: { name: 'LocaleActionProvider', importPath: '@vtex/faststore/internal/client' },\n methods: {\n set: {\n description:\n 'Switch to another binding: navigates to the equivalent URL of the current page, or to the destination home with fs_notice=no-equivalent.',\n payload: [{ name: 'bindingId', type: 'string' }],\n },\n },\n use: useLocaleActions,\n});\n"],"mappings":";;;;;;;;;;;;;;AAyBA,SAAgB,mBAAkC;CAChD,MAAM,gBAAgB,iBAAiB;CACvC,MAAM,EAAE,QAAQ,SAAS,mBAAmB;CAC5C,MAAM,EAAE,WAAW,qBAAqB,qBAAqB;CAE7D,MAAM,MAAM,YACV,OAAO,EAAE,gBAAgB;EACvB,IAAI,CAAC,eAAe;GAClB,QAAQ,MAAM,uCAAuC;GACrD,OAAO;EACT;EACA,IAAI;GACF,MAAM,EAAE,QAAQ,MAAM,cAAc;IAClC;IACA;IACA;IACA;IACA,QAAQ,WAAW,SAAS;IAC5B,MAAM,WAAW,SAAS;GAC5B,CAAC;GACD,IAAI,QAAQ,MAAM;IAChB,QAAQ,MAAM,qCAAqC;IACnD,OAAO;GACT;GACA,WAAW,SAAS,OAAO,GAAG;GAC9B,OAAO;EACT,SAAS,OAAO;GACd,QAAQ,MAAM,qCAAqC,kBAAkB,KAAK,CAAC;GAC3E,OAAO;EACT;CACF,GACA;EAAC;EAAe;EAAkB;EAAQ;CAAI,CAChD;CAEA,OAAO,eAAe,EAAE,IAAI,IAAI,CAAC,GAAG,CAAC;AACvC;AAM6B,cAAc;CACzC,WAAW;CACX,aACE;CACF,UAAU;EAAE,MAAM;EAAwB,YAAY;CAAkC;CACxF,SAAS,EACP,KAAK;EACH,aACE;EACF,SAAS,CAAC;GAAE,MAAM;GAAa,MAAM;EAAS,CAAC;CACjD,EACF;CACA,KAAK;AACP,CAAC"}
@@ -0,0 +1,39 @@
1
+ import { RouteMap } from "./types.js";
2
+ import { AlternateBinding } from "../seo/page-alternates.js";
3
+ import { ApolloClient } from "@apollo/client";
4
+ //#region src/sdk/navigation/binding-switch.d.ts
5
+ export type BindingSwitchRequest = {
6
+ /** Destination binding id. */
7
+ bindingId: string;
8
+ /** The binding the shopper is on. */
9
+ currentBindingId: string;
10
+ params: Record<string, string | string[]>;
11
+ /** Decoded logical path of the current page. */
12
+ path: string;
13
+ /** `location.search`: `''` or `'?…'`. */
14
+ search: string;
15
+ /** `location.hash`: `''` or `'#…'`. */
16
+ hash: string;
17
+ };
18
+ export type BindingSwitchResult = {
19
+ url: string | null;
20
+ };
21
+ export type ResolveBindingSwitchOptions = {
22
+ request: BindingSwitchRequest;
23
+ knownBindingId: (candidate: string | null | undefined) => string | null;
24
+ bindings: readonly AlternateBinding[];
25
+ routeMap: RouteMap;
26
+ buildUrl: (bindingId: string, logicalPath: string) => string;
27
+ clientFor: (bindingId: string) => ApolloClient;
28
+ };
29
+ /**
30
+ * The equivalent URL of the current page in the destination binding, or the destination's home with
31
+ * `fs_notice=no-equivalent` when none exists or the lookup fails. The page is the one route-map
32
+ * entry whose href in the current binding equals the logical path; none or several take the
33
+ * fallback. Every URL is built by `buildUrl` from an id `knownBindingId` accepted; an undeclared
34
+ * destination resolves `{ url: null }`. The request comes off the wire, so each field is read as
35
+ * unchecked. Never throws for an expected outcome, writes no cookie, and reads no request header.
36
+ */
37
+ export declare function resolveBindingSwitch({ request, knownBindingId, bindings, routeMap, buildUrl, clientFor }: ResolveBindingSwitchOptions): Promise<BindingSwitchResult>;
38
+ //#endregion
39
+ //# sourceMappingURL=binding-switch.d.ts.map
@@ -0,0 +1,129 @@
1
+ import { resolveStorefrontHref } from "./resolve-storefront-href.js";
2
+ import { CATALOG_ROUTE_ALTERNATES_QUERY, catalogClusterPaths, encodePath } from "../seo/page-alternates.js";
3
+ import { decodeRouteParams } from "./decode-route-params.js";
4
+ //#region src/sdk/navigation/binding-switch.ts
5
+ /** Bounds the `catalogRoute` lookup: a slow API takes the no-equivalent fallback instead of stalling the switch. */
6
+ const BINDING_SWITCH_LOOKUP_TIMEOUT_MS = 3e3;
7
+ const CATALOG_STANDARDS = /* @__PURE__ */ new Set([
8
+ "product",
9
+ "category",
10
+ "brand",
11
+ "collection"
12
+ ]);
13
+ const NO_EQUIVALENT_QUERY = "?fs_notice=no-equivalent";
14
+ /**
15
+ * The equivalent URL of the current page in the destination binding, or the destination's home with
16
+ * `fs_notice=no-equivalent` when none exists or the lookup fails. The page is the one route-map
17
+ * entry whose href in the current binding equals the logical path; none or several take the
18
+ * fallback. Every URL is built by `buildUrl` from an id `knownBindingId` accepted; an undeclared
19
+ * destination resolves `{ url: null }`. The request comes off the wire, so each field is read as
20
+ * unchecked. Never throws for an expected outcome, writes no cookie, and reads no request header.
21
+ */
22
+ async function resolveBindingSwitch({ request, knownBindingId, bindings, routeMap, buildUrl, clientFor }) {
23
+ const destination = knownBindingId(stringOr(request.bindingId, null));
24
+ if (destination === null) {
25
+ console.error("[locale.set] unknown binding");
26
+ return { url: null };
27
+ }
28
+ const fallback = { url: `${buildUrl(destination, "/")}${NO_EQUIVALENT_QUERY}` };
29
+ const path = stringOr(request.path, "");
30
+ const params = decodeRouteParams(paramsOf(request.params));
31
+ const page = pageMatchingPath(routeMap, stringOr(request.currentBindingId, ""), path, params);
32
+ const entry = page === void 0 ? void 0 : ownEntry(routeMap, page);
33
+ if (page === void 0 || entry === void 0) return fallback;
34
+ if (entry.bindings !== void 0 && !entry.bindings.includes(destination)) return fallback;
35
+ const tail = `${searchOf(request.search)}${hashOf(request.hash)}`;
36
+ if (entry.standard !== void 0 && CATALOG_STANDARDS.has(entry.standard)) {
37
+ const current = knownBindingId(stringOr(request.currentBindingId, null));
38
+ if (current === null || path === "" || path === "/") return fallback;
39
+ const paths = await catalogClusterPaths(bindings, async () => {
40
+ const { data, error } = await clientFor(current).query({
41
+ query: CATALOG_ROUTE_ALTERNATES_QUERY,
42
+ variables: { path },
43
+ errorPolicy: "all",
44
+ fetchPolicy: "no-cache",
45
+ context: { fetchOptions: { signal: AbortSignal.timeout(BINDING_SWITCH_LOOKUP_TIMEOUT_MS) } }
46
+ });
47
+ return {
48
+ data,
49
+ error
50
+ };
51
+ });
52
+ const variantPath = paths && Object.hasOwn(paths, destination) ? paths[destination] : void 0;
53
+ if (variantPath === void 0) return fallback;
54
+ return { url: `${buildUrl(destination, encodePath(variantPath))}${tail}` };
55
+ }
56
+ const { href } = resolveStorefrontHref({
57
+ bindingId: destination,
58
+ basePath: "/"
59
+ }, routeMap, {
60
+ page,
61
+ params
62
+ });
63
+ if (href === null) return fallback;
64
+ return { url: `${buildUrl(destination, href)}${tail}` };
65
+ }
66
+ function stringOr(value, empty) {
67
+ return typeof value === "string" ? value : empty;
68
+ }
69
+ function paramsOf(value) {
70
+ return typeof value === "object" && value !== null && !Array.isArray(value) ? value : {};
71
+ }
72
+ /**
73
+ * The single page whose href in the current binding is this logical path. A miss and a tie are both
74
+ * no page: the switch falls back instead of guessing. An entry that cannot be resolved (a missing
75
+ * param, a binding it does not serve) is skipped.
76
+ */
77
+ function pageMatchingPath(routeMap, bindingId, path, params) {
78
+ const matches = [];
79
+ for (const name of Object.keys(routeMap)) {
80
+ const { href } = resolveStorefrontHref({
81
+ bindingId,
82
+ basePath: "/"
83
+ }, routeMap, {
84
+ page: name,
85
+ params
86
+ });
87
+ if (href === null || !sameDecodedPath(href, path)) continue;
88
+ matches.push(name);
89
+ }
90
+ return matches.length === 1 ? matches[0] : void 0;
91
+ }
92
+ /**
93
+ * `resolveStorefrontHref` percent-encodes params, so the href is decoded once. The request path is
94
+ * compared as it arrived first, so a decoded slug like `100%-off` or `%41` is not decoded again; an
95
+ * encoded path still matches through its decoded form.
96
+ */
97
+ function sameDecodedPath(href, path) {
98
+ const decodedHref = decodeOrNull(href);
99
+ if (decodedHref === null) return false;
100
+ return decodedHref === path || decodedHref === decodeOrNull(path);
101
+ }
102
+ function decodeOrNull(text) {
103
+ try {
104
+ return decodeURIComponent(text);
105
+ } catch {
106
+ return null;
107
+ }
108
+ }
109
+ function ownEntry(routeMap, page) {
110
+ return Object.hasOwn(routeMap, page) ? routeMap[page] : void 0;
111
+ }
112
+ /** Kept verbatim when it is only a query: a leading `?`, no `#`, and no control character. */
113
+ function searchOf(search) {
114
+ const text = stringOr(search, "");
115
+ if (text.length < 2 || !text.startsWith("?")) return "";
116
+ for (const char of text) {
117
+ const code = char.codePointAt(0) ?? 0;
118
+ if (char === "#" || code < 32 || code === 127) return "";
119
+ }
120
+ return text;
121
+ }
122
+ function hashOf(hash) {
123
+ const text = stringOr(hash, "");
124
+ return text.startsWith("#") && text.length > 1 ? text : "";
125
+ }
126
+ //#endregion
127
+ export { resolveBindingSwitch };
128
+
129
+ //# sourceMappingURL=binding-switch.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"binding-switch.js","names":[],"sources":["../../../src/sdk/navigation/binding-switch.ts"],"sourcesContent":["import type { ApolloClient } from '@apollo/client';\nimport {\n type AlternateBinding,\n CATALOG_ROUTE_ALTERNATES_QUERY,\n catalogClusterPaths,\n encodePath,\n} from '../seo/page-alternates.js';\nimport { decodeRouteParams } from './decode-route-params.js';\nimport { resolveStorefrontHref } from './resolve-storefront-href.js';\nimport type { RouteMap, RouteMapEntry, StandardName } from './types.js';\n\n/** Bounds the `catalogRoute` lookup: a slow API takes the no-equivalent fallback instead of stalling the switch. */\nconst BINDING_SWITCH_LOOKUP_TIMEOUT_MS = 3_000;\n\nconst CATALOG_STANDARDS: ReadonlySet<StandardName> = new Set(['product', 'category', 'brand', 'collection']);\n\nconst NO_EQUIVALENT_QUERY = '?fs_notice=no-equivalent';\n\nexport type BindingSwitchRequest = {\n /** Destination binding id. */\n bindingId: string;\n /** The binding the shopper is on. */\n currentBindingId: string;\n params: Record<string, string | string[]>;\n /** Decoded logical path of the current page. */\n path: string;\n /** `location.search`: `''` or `'?…'`. */\n search: string;\n /** `location.hash`: `''` or `'#…'`. */\n hash: string;\n};\n\nexport type BindingSwitchResult = { url: string | null };\n\nexport type ResolveBindingSwitchOptions = {\n request: BindingSwitchRequest;\n knownBindingId: (candidate: string | null | undefined) => string | null;\n bindings: readonly AlternateBinding[];\n routeMap: RouteMap;\n buildUrl: (bindingId: string, logicalPath: string) => string;\n clientFor: (bindingId: string) => ApolloClient;\n};\n\n/**\n * The equivalent URL of the current page in the destination binding, or the destination's home with\n * `fs_notice=no-equivalent` when none exists or the lookup fails. The page is the one route-map\n * entry whose href in the current binding equals the logical path; none or several take the\n * fallback. Every URL is built by `buildUrl` from an id `knownBindingId` accepted; an undeclared\n * destination resolves `{ url: null }`. The request comes off the wire, so each field is read as\n * unchecked. Never throws for an expected outcome, writes no cookie, and reads no request header.\n */\nexport async function resolveBindingSwitch({\n request,\n knownBindingId,\n bindings,\n routeMap,\n buildUrl,\n clientFor,\n}: ResolveBindingSwitchOptions): Promise<BindingSwitchResult> {\n const destination = knownBindingId(stringOr(request.bindingId, null));\n if (destination === null) {\n console.error('[locale.set] unknown binding');\n return { url: null };\n }\n\n const fallback = { url: `${buildUrl(destination, '/')}${NO_EQUIVALENT_QUERY}` };\n const path = stringOr(request.path, '');\n // `useParams()` hands the segments over percent-encoded; the route map fills patterns with decoded values.\n const params = decodeRouteParams(paramsOf(request.params));\n const page = pageMatchingPath(routeMap, stringOr(request.currentBindingId, ''), path, params);\n const entry = page === undefined ? undefined : ownEntry(routeMap, page);\n if (page === undefined || entry === undefined) return fallback;\n if (entry.bindings !== undefined && !entry.bindings.includes(destination)) return fallback;\n\n const tail = `${searchOf(request.search)}${hashOf(request.hash)}`;\n\n if (entry.standard !== undefined && CATALOG_STANDARDS.has(entry.standard)) {\n const current = knownBindingId(stringOr(request.currentBindingId, null));\n if (current === null || path === '' || path === '/') return fallback;\n\n const paths = await catalogClusterPaths(bindings, async () => {\n const { data, error } = await clientFor(current).query({\n query: CATALOG_ROUTE_ALTERNATES_QUERY,\n variables: { path },\n errorPolicy: 'all',\n fetchPolicy: 'no-cache',\n context: { fetchOptions: { signal: AbortSignal.timeout(BINDING_SWITCH_LOOKUP_TIMEOUT_MS) } },\n });\n return { data, error };\n });\n const variantPath = paths && Object.hasOwn(paths, destination) ? paths[destination] : undefined;\n if (variantPath === undefined) return fallback;\n return { url: `${buildUrl(destination, encodePath(variantPath))}${tail}` };\n }\n\n const { href } = resolveStorefrontHref({ bindingId: destination, basePath: '/' }, routeMap, { page, params });\n if (href === null) return fallback;\n return { url: `${buildUrl(destination, href)}${tail}` };\n}\n\nfunction stringOr<T extends string | null>(value: unknown, empty: T): string | T {\n return typeof value === 'string' ? value : empty;\n}\n\nfunction paramsOf(value: unknown): BindingSwitchRequest['params'] {\n return typeof value === 'object' && value !== null && !Array.isArray(value)\n ? (value as BindingSwitchRequest['params'])\n : {};\n}\n\n/**\n * The single page whose href in the current binding is this logical path. A miss and a tie are both\n * no page: the switch falls back instead of guessing. An entry that cannot be resolved (a missing\n * param, a binding it does not serve) is skipped.\n */\nfunction pageMatchingPath(\n routeMap: RouteMap,\n bindingId: string,\n path: string,\n params: BindingSwitchRequest['params'],\n): string | undefined {\n const matches: string[] = [];\n for (const name of Object.keys(routeMap)) {\n const { href } = resolveStorefrontHref({ bindingId, basePath: '/' }, routeMap, { page: name, params });\n if (href === null || !sameDecodedPath(href, path)) continue;\n matches.push(name);\n }\n return matches.length === 1 ? matches[0] : undefined;\n}\n\n/**\n * `resolveStorefrontHref` percent-encodes params, so the href is decoded once. The request path is\n * compared as it arrived first, so a decoded slug like `100%-off` or `%41` is not decoded again; an\n * encoded path still matches through its decoded form.\n */\nfunction sameDecodedPath(href: string, path: string): boolean {\n const decodedHref = decodeOrNull(href);\n if (decodedHref === null) return false;\n return decodedHref === path || decodedHref === decodeOrNull(path);\n}\n\nfunction decodeOrNull(text: string): string | null {\n try {\n return decodeURIComponent(text);\n } catch {\n return null;\n }\n}\n\nfunction ownEntry(routeMap: RouteMap, page: string): RouteMapEntry | undefined {\n return Object.hasOwn(routeMap, page) ? routeMap[page] : undefined;\n}\n\n/** Kept verbatim when it is only a query: a leading `?`, no `#`, and no control character. */\nfunction searchOf(search: unknown): string {\n const text = stringOr(search, '');\n if (text.length < 2 || !text.startsWith('?')) return '';\n for (const char of text) {\n const code = char.codePointAt(0) ?? 0;\n if (char === '#' || code < 0x20 || code === 0x7f) return '';\n }\n return text;\n}\n\nfunction hashOf(hash: unknown): string {\n const text = stringOr(hash, '');\n return text.startsWith('#') && text.length > 1 ? text : '';\n}\n"],"mappings":"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"}
@@ -0,0 +1,11 @@
1
+ //#region src/sdk/navigation/decode-route-params.d.ts
2
+ type RouteParamValue = string | string[] | undefined;
3
+ /**
4
+ * The route params a generated page receives, decoded: Next hands over a dynamic segment
5
+ * percent-encoded, so an id ending in `=` arrives as `%3D`. A value with a malformed sequence is kept
6
+ * as received, so a request can never turn into an exception. A list (catch-all) is decoded segment
7
+ * by segment, and the keys are unchanged.
8
+ */
9
+ export declare function decodeRouteParams<T extends Record<string, RouteParamValue>>(params: T): T;
10
+ //#endregion
11
+ //# sourceMappingURL=decode-route-params.d.ts.map
@@ -0,0 +1,32 @@
1
+ //#region src/sdk/navigation/decode-route-params.ts
2
+ function decodeSegment(segment) {
3
+ try {
4
+ return decodeURIComponent(segment);
5
+ } catch {
6
+ return segment;
7
+ }
8
+ }
9
+ function decodeValue(value) {
10
+ if (value === void 0) return void 0;
11
+ return Array.isArray(value) ? value.map(decodeSegment) : decodeSegment(value);
12
+ }
13
+ /**
14
+ * The route params a generated page receives, decoded: Next hands over a dynamic segment
15
+ * percent-encoded, so an id ending in `=` arrives as `%3D`. A value with a malformed sequence is kept
16
+ * as received, so a request can never turn into an exception. A list (catch-all) is decoded segment
17
+ * by segment, and the keys are unchanged.
18
+ */
19
+ function decodeRouteParams(params) {
20
+ const decoded = {};
21
+ for (const [key, value] of Object.entries(params)) Object.defineProperty(decoded, key, {
22
+ value: decodeValue(value),
23
+ enumerable: true,
24
+ writable: true,
25
+ configurable: true
26
+ });
27
+ return decoded;
28
+ }
29
+ //#endregion
30
+ export { decodeRouteParams };
31
+
32
+ //# sourceMappingURL=decode-route-params.js.map