@vtex/faststore 0.0.4 → 0.0.6

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 (306) hide show
  1. package/CHANGELOG.md +67 -0
  2. package/dist/actions.json +181 -0
  3. package/dist/catalog.json +1595 -481
  4. package/dist/client.d.ts +2 -1
  5. package/dist/client.js +2 -1
  6. package/dist/components.d.ts +31 -7
  7. package/dist/components.js +35 -13
  8. package/dist/content-types.json +73 -0
  9. package/dist/docs/access-code-sign-in.md +1 -1
  10. package/dist/docs/account-access-gate.md +153 -0
  11. package/dist/docs/account-entry-point.md +1 -2
  12. package/dist/docs/account-menu.md +164 -0
  13. package/dist/docs/address-summary.md +24 -3
  14. package/dist/docs/alert-dialog.md +204 -0
  15. package/dist/docs/billing-address.md +284 -0
  16. package/dist/docs/buy-button.md +1 -1
  17. package/dist/docs/card-entry.md +250 -0
  18. package/dist/docs/cardholder-document.md +142 -0
  19. package/dist/docs/cart.md +1 -1
  20. package/dist/docs/client-preferences-fieldset.md +148 -0
  21. package/dist/docs/client-preferences.md +2 -1
  22. package/dist/docs/client-profile-fieldset.md +184 -0
  23. package/dist/docs/client-profile.md +1 -1
  24. package/dist/docs/document-field.md +58 -4
  25. package/dist/docs/field.md +317 -0
  26. package/dist/docs/form.md +307 -0
  27. package/dist/docs/identity-edit.md +6 -6
  28. package/dist/docs/identity-summary.md +5 -5
  29. package/dist/docs/installments.md +248 -0
  30. package/dist/docs/my-account-profile.md +1 -0
  31. package/dist/docs/order-placed-payment-summary.md +138 -0
  32. package/dist/docs/payment-method-badges.md +232 -0
  33. package/dist/docs/payment-methods.md +1 -0
  34. package/dist/docs/payment.md +218 -0
  35. package/dist/docs/phone-field.md +58 -4
  36. package/dist/docs/place-order.md +157 -0
  37. package/dist/docs/save-card.md +186 -0
  38. package/dist/docs/saved-cards.md +218 -0
  39. package/dist/docs/select.md +465 -0
  40. package/dist/docs/session.md +1 -1
  41. package/dist/docs/shipping-address.md +110 -258
  42. package/dist/docs/sign-in-redirect.md +1 -1
  43. package/dist/index.d.ts +4 -1
  44. package/dist/internal/client.d.ts +3 -1
  45. package/dist/internal/client.js +3 -1
  46. package/dist/matter/access-code-sign-in/access-code-sign-in.js +9 -8
  47. package/dist/matter/access-code-sign-in/access-code-sign-in.js.map +1 -1
  48. package/dist/matter/account-access-gate/account-access-gate.d.ts +86 -0
  49. package/dist/matter/account-access-gate/account-access-gate.js +283 -0
  50. package/dist/matter/account-access-gate/account-access-gate.js.map +1 -0
  51. package/dist/matter/account-access-gate/sign-out-intent.d.ts +17 -0
  52. package/dist/matter/account-access-gate/sign-out-intent.js +62 -0
  53. package/dist/matter/account-access-gate/sign-out-intent.js.map +1 -0
  54. package/dist/matter/account-entry-point/account-entry-point.js +2 -2
  55. package/dist/matter/account-entry-point/account-entry-point.js.map +1 -1
  56. package/dist/matter/account-menu/account-menu.d.ts +88 -0
  57. package/dist/matter/account-menu/account-menu.js +246 -0
  58. package/dist/matter/account-menu/account-menu.js.map +1 -0
  59. package/dist/matter/billing-address/billing-address.d.ts +165 -0
  60. package/dist/matter/billing-address/billing-address.js +408 -0
  61. package/dist/matter/billing-address/billing-address.js.map +1 -0
  62. package/dist/matter/card-entry/card-capture.d.ts +25 -0
  63. package/dist/matter/card-entry/card-capture.js +25 -0
  64. package/dist/matter/card-entry/card-capture.js.map +1 -0
  65. package/dist/matter/card-entry/card-entry.d.ts +120 -0
  66. package/dist/matter/card-entry/card-entry.js +832 -0
  67. package/dist/matter/card-entry/card-entry.js.map +1 -0
  68. package/dist/matter/cardholder-document/cardholder-document.d.ts +45 -0
  69. package/dist/matter/cardholder-document/cardholder-document.js +70 -0
  70. package/dist/matter/cardholder-document/cardholder-document.js.map +1 -0
  71. package/dist/matter/cart/cart.d.ts +1 -1
  72. package/dist/matter/cart/cart.js +2 -2
  73. package/dist/matter/cart/cart.js.map +1 -1
  74. package/dist/matter/client-preferences/client-preferences-fieldset.d.ts +77 -0
  75. package/dist/matter/client-preferences/client-preferences-fieldset.js +299 -0
  76. package/dist/matter/client-preferences/client-preferences-fieldset.js.map +1 -0
  77. package/dist/matter/client-preferences/client-preferences.d.ts +3 -5
  78. package/dist/matter/client-preferences/client-preferences.js +6 -6
  79. package/dist/matter/client-preferences/client-preferences.js.map +1 -1
  80. package/dist/matter/client-profile/client-profile.d.ts +4 -6
  81. package/dist/matter/client-profile/client-profile.js +9 -6
  82. package/dist/matter/client-profile/client-profile.js.map +1 -1
  83. package/dist/matter/client-profile-form/client-profile-fieldset.d.ts +119 -0
  84. package/dist/matter/client-profile-form/client-profile-fieldset.js +488 -0
  85. package/dist/matter/client-profile-form/client-profile-fieldset.js.map +1 -0
  86. package/dist/matter/delivery-options/delivery-options.js.map +1 -1
  87. package/dist/matter/identity-edit/identity-edit.js +2 -2
  88. package/dist/matter/identity-edit/identity-edit.js.map +1 -1
  89. package/dist/matter/identity-summary/identity-summary.js +2 -2
  90. package/dist/matter/identity-summary/identity-summary.js.map +1 -1
  91. package/dist/matter/installments/installments-select.d.ts +42 -0
  92. package/dist/matter/installments/installments-select.js +167 -0
  93. package/dist/matter/installments/installments-select.js.map +1 -0
  94. package/dist/matter/installments/installments.d.ts +58 -0
  95. package/dist/matter/installments/installments.js +176 -0
  96. package/dist/matter/installments/installments.js.map +1 -0
  97. package/dist/matter/installments/use-installments-selection.d.ts +29 -0
  98. package/dist/matter/installments/use-installments-selection.js +95 -0
  99. package/dist/matter/installments/use-installments-selection.js.map +1 -0
  100. package/dist/matter/my-account-profile/my-account-profile.js +5 -5
  101. package/dist/matter/my-account-profile/my-account-profile.js.map +1 -1
  102. package/dist/matter/order-placed-payment-summary/order-placed-payment-summary.d.ts +63 -0
  103. package/dist/matter/order-placed-payment-summary/order-placed-payment-summary.js +305 -0
  104. package/dist/matter/order-placed-payment-summary/order-placed-payment-summary.js.map +1 -0
  105. package/dist/matter/payment/payment-card-capture.d.ts +19 -0
  106. package/dist/matter/payment/payment-card-capture.js +129 -0
  107. package/dist/matter/payment/payment-card-capture.js.map +1 -0
  108. package/dist/matter/payment/payment-controller.d.ts +24 -0
  109. package/dist/matter/payment/payment-controller.js +107 -0
  110. package/dist/matter/payment/payment-controller.js.map +1 -0
  111. package/dist/matter/payment/payment-method-badges.d.ts +57 -0
  112. package/dist/matter/payment/payment-method-badges.js +192 -0
  113. package/dist/matter/payment/payment-method-badges.js.map +1 -0
  114. package/dist/matter/payment/payment.d.ts +89 -0
  115. package/dist/matter/payment/payment.js +263 -0
  116. package/dist/matter/payment/payment.js.map +1 -0
  117. package/dist/matter/payment/use-payment-method-selection.d.ts +302 -0
  118. package/dist/matter/payment/use-payment-method-selection.js +458 -0
  119. package/dist/matter/payment/use-payment-method-selection.js.map +1 -0
  120. package/dist/matter/payment/use-payment-submit-gate.d.ts +111 -0
  121. package/dist/matter/payment/use-payment-submit-gate.js +144 -0
  122. package/dist/matter/payment/use-payment-submit-gate.js.map +1 -0
  123. package/dist/matter/place-order/place-order.d.ts +74 -0
  124. package/dist/matter/place-order/place-order.js +379 -0
  125. package/dist/matter/place-order/place-order.js.map +1 -0
  126. package/dist/matter/product-list/product-list.d.ts +1 -0
  127. package/dist/matter/product-list/product-list.js.map +1 -1
  128. package/dist/matter/product-shelf/product-shelf.d.ts +1 -0
  129. package/dist/matter/product-shelf/product-shelf.js +2 -0
  130. package/dist/matter/product-shelf/product-shelf.js.map +1 -1
  131. package/dist/matter/region/region.js +1 -1
  132. package/dist/matter/region/region.js.map +1 -1
  133. package/dist/matter/save-card/save-card.d.ts +49 -0
  134. package/dist/matter/save-card/save-card.js +188 -0
  135. package/dist/matter/save-card/save-card.js.map +1 -0
  136. package/dist/matter/saved-cards/saved-cards.d.ts +79 -0
  137. package/dist/matter/saved-cards/saved-cards.js +387 -0
  138. package/dist/matter/saved-cards/saved-cards.js.map +1 -0
  139. package/dist/matter/shipping-address/country-descriptors.js +20 -10
  140. package/dist/matter/shipping-address/country-descriptors.js.map +1 -1
  141. package/dist/matter/shipping-address/shipping-address.d.ts +113 -174
  142. package/dist/matter/shipping-address/shipping-address.js +481 -792
  143. package/dist/matter/shipping-address/shipping-address.js.map +1 -1
  144. package/dist/matter/shipping-simulator/shipping-simulator.js.map +1 -1
  145. package/dist/matter/variant-selector/variant-selector.js +1 -1
  146. package/dist/particles/address-summary/address-summary.d.ts +24 -0
  147. package/dist/particles/address-summary/address-summary.js +83 -2
  148. package/dist/particles/address-summary/address-summary.js.map +1 -1
  149. package/dist/particles/alert-dialog/alert-dialog.d.ts +68 -0
  150. package/dist/particles/alert-dialog/alert-dialog.js +187 -0
  151. package/dist/particles/alert-dialog/alert-dialog.js.map +1 -0
  152. package/dist/particles/checkbox/checkbox.d.ts +7 -3
  153. package/dist/particles/checkbox/checkbox.js +4 -1
  154. package/dist/particles/checkbox/checkbox.js.map +1 -1
  155. package/dist/particles/document-field/document-field.d.ts +48 -5
  156. package/dist/particles/document-field/document-field.js +189 -32
  157. package/dist/particles/document-field/document-field.js.map +1 -1
  158. package/dist/particles/field/bound-field-registry.js +52 -0
  159. package/dist/particles/field/bound-field-registry.js.map +1 -0
  160. package/dist/particles/field/field.d.ts +116 -0
  161. package/dist/particles/field/field.js +497 -6
  162. package/dist/particles/field/field.js.map +1 -1
  163. package/dist/particles/fieldset/fieldset.js +2 -2
  164. package/dist/particles/fieldset/fieldset.js.map +1 -1
  165. package/dist/particles/footer-social/footer-social.js +1 -1
  166. package/dist/particles/form/form.d.ts +104 -0
  167. package/dist/particles/form/form.js +485 -0
  168. package/dist/particles/form/form.js.map +1 -0
  169. package/dist/particles/incentives/incentives.js +1 -1
  170. package/dist/particles/phone-field/phone-field.d.ts +38 -10
  171. package/dist/particles/phone-field/phone-field.js +183 -39
  172. package/dist/particles/phone-field/phone-field.js.map +1 -1
  173. package/dist/particles/postal-code-field/postal-code-field.js +2 -2
  174. package/dist/particles/postal-code-field/postal-code-field.js.map +1 -1
  175. package/dist/particles/select/field-select.d.ts +43 -0
  176. package/dist/particles/select/field-select.js +150 -0
  177. package/dist/particles/select/field-select.js.map +1 -0
  178. package/dist/particles/select/select.d.ts +2 -0
  179. package/dist/particles/select/select.js.map +1 -1
  180. package/dist/particles/shared/compose-refs.js +21 -0
  181. package/dist/particles/shared/compose-refs.js.map +1 -0
  182. package/dist/schema.graphql +701 -2
  183. package/dist/sdk/actions/analytics-action-provider.d.ts +15 -0
  184. package/dist/sdk/actions/analytics-action-provider.js +19 -0
  185. package/dist/sdk/actions/analytics-action-provider.js.map +1 -0
  186. package/dist/sdk/actions/cart-action-provider.d.ts +1 -1
  187. package/dist/sdk/actions/cart-action-provider.js +1 -1
  188. package/dist/sdk/actions/cart-action-provider.js.map +1 -1
  189. package/dist/sdk/actions/index.d.ts +3 -1
  190. package/dist/sdk/actions/index.js +3 -1
  191. package/dist/sdk/actions/payment-action-provider.d.ts +22 -0
  192. package/dist/sdk/actions/payment-action-provider.js +26 -0
  193. package/dist/sdk/actions/payment-action-provider.js.map +1 -0
  194. package/dist/sdk/actions/session-action-provider.d.ts +1 -1
  195. package/dist/sdk/actions/session-action-provider.js +1 -1
  196. package/dist/sdk/actions/session-action-provider.js.map +1 -1
  197. package/dist/sdk/actions/shipping-action-provider.d.ts +1 -1
  198. package/dist/sdk/actions/shipping-action-provider.js +1 -1
  199. package/dist/sdk/actions/shipping-action-provider.js.map +1 -1
  200. package/dist/sdk/actions/types.d.ts +6 -2
  201. package/dist/sdk/analytics/analytics-destination.d.ts +43 -0
  202. package/dist/sdk/analytics/analytics-destination.js +90 -0
  203. package/dist/sdk/analytics/analytics-destination.js.map +1 -0
  204. package/dist/sdk/analytics/use-analytics-actions.d.ts +57 -0
  205. package/dist/sdk/analytics/use-analytics-actions.js +108 -0
  206. package/dist/sdk/analytics/use-analytics-actions.js.map +1 -0
  207. package/dist/sdk/cache/storefront-cache.js +1 -1
  208. package/dist/sdk/cache/storefront-cache.js.map +1 -1
  209. package/dist/sdk/cart/operations.d.ts +4 -4
  210. package/dist/sdk/cart/operations.js +1 -1
  211. package/dist/sdk/cart/operations.js.map +1 -1
  212. package/dist/sdk/cart/optimistic.js +1 -1
  213. package/dist/sdk/cart/optimistic.js.map +1 -1
  214. package/dist/sdk/cart/use-cart-actions.d.ts +1 -1
  215. package/dist/sdk/cart/use-cart-actions.js +4 -4
  216. package/dist/sdk/cart/use-cart-actions.js.map +1 -1
  217. package/dist/sdk/cart/use-cart-validate-on-open.js +1 -1
  218. package/dist/sdk/cart/use-cart-validate-on-open.js.map +1 -1
  219. package/dist/sdk/cart/write-gate.js +1 -1
  220. package/dist/sdk/cart/write-gate.js.map +1 -1
  221. package/dist/sdk/data/entity-resolution-context.js +31 -0
  222. package/dist/sdk/data/entity-resolution-context.js.map +1 -0
  223. package/dist/sdk/data/entity-subscriber.js +18 -8
  224. package/dist/sdk/data/entity-subscriber.js.map +1 -1
  225. package/dist/sdk/data/index.js +1 -0
  226. package/dist/sdk/data/page-reactive-data-root.d.ts +38 -2
  227. package/dist/sdk/data/page-reactive-data-root.js +4 -2
  228. package/dist/sdk/data/page-reactive-data-root.js.map +1 -1
  229. package/dist/sdk/data/reactive-data-provider.d.ts +1 -1
  230. package/dist/sdk/data/reactive-data-provider.js +8 -4
  231. package/dist/sdk/data/reactive-data-provider.js.map +1 -1
  232. package/dist/sdk/data/reactive-list-item-data-provider.d.ts +1 -1
  233. package/dist/sdk/data/reactive-list-item-data-provider.js +13 -5
  234. package/dist/sdk/data/reactive-list-item-data-provider.js.map +1 -1
  235. package/dist/sdk/data/use-reactive-data-settled.js +39 -0
  236. package/dist/sdk/data/use-reactive-data-settled.js.map +1 -0
  237. package/dist/sdk/data/use-reactive-entity.js +6 -2
  238. package/dist/sdk/data/use-reactive-entity.js.map +1 -1
  239. package/dist/sdk/generated/fragment-types.js +2 -0
  240. package/dist/sdk/generated/fragment-types.js.map +1 -1
  241. package/dist/sdk/generated/gql.js +2 -1
  242. package/dist/sdk/generated/gql.js.map +1 -1
  243. package/dist/sdk/generated/graphql.js +85 -1
  244. package/dist/sdk/generated/graphql.js.map +1 -1
  245. package/dist/sdk/i18n/messages/en-US.js +3 -0
  246. package/dist/sdk/i18n/messages/en-US.json +3 -0
  247. package/dist/sdk/i18n/messages/en-US.json.d.ts +3 -0
  248. package/dist/sdk/i18n/messages/es-ES.js +3 -0
  249. package/dist/sdk/i18n/messages/es-ES.json +3 -0
  250. package/dist/sdk/i18n/messages/pt-BR.js +3 -0
  251. package/dist/sdk/i18n/messages/pt-BR.json +3 -0
  252. package/dist/sdk/identity/operations.js.map +1 -1
  253. package/dist/sdk/identity/use-identity-actions.d.ts +1 -1
  254. package/dist/sdk/identity/use-identity-actions.js +3 -2
  255. package/dist/sdk/identity/use-identity-actions.js.map +1 -1
  256. package/dist/sdk/payment/gateway-origin.js +35 -0
  257. package/dist/sdk/payment/gateway-origin.js.map +1 -0
  258. package/dist/sdk/payment/gateway.d.ts +85 -0
  259. package/dist/sdk/payment/gateway.js +183 -0
  260. package/dist/sdk/payment/gateway.js.map +1 -0
  261. package/dist/sdk/payment/operations.d.ts +52 -0
  262. package/dist/sdk/payment/operations.js +223 -0
  263. package/dist/sdk/payment/operations.js.map +1 -0
  264. package/dist/sdk/payment/security-code-mask.js +71 -0
  265. package/dist/sdk/payment/security-code-mask.js.map +1 -0
  266. package/dist/sdk/payment/submission-registry.d.ts +33 -0
  267. package/dist/sdk/payment/submission-registry.js +58 -0
  268. package/dist/sdk/payment/submission-registry.js.map +1 -0
  269. package/dist/sdk/payment/use-payment-actions.d.ts +78 -0
  270. package/dist/sdk/payment/use-payment-actions.js +276 -0
  271. package/dist/sdk/payment/use-payment-actions.js.map +1 -0
  272. package/dist/sdk/payment/use-place-order.d.ts +65 -0
  273. package/dist/sdk/payment/use-place-order.js +640 -0
  274. package/dist/sdk/payment/use-place-order.js.map +1 -0
  275. package/dist/sdk/session/auth-operations.d.ts +1 -1
  276. package/dist/sdk/session/auth-operations.js.map +1 -1
  277. package/dist/sdk/session/use-session-actions.d.ts +1 -1
  278. package/dist/sdk/session/use-session-actions.js.map +1 -1
  279. package/dist/sdk/session/use-session-revalidation.js +65 -0
  280. package/dist/sdk/session/use-session-revalidation.js.map +1 -0
  281. package/dist/sdk/shipping/option-select-operations.d.ts +1 -1
  282. package/dist/sdk/shipping/option-select-operations.js.map +1 -1
  283. package/dist/sdk/shipping/request-timeout.js +1 -1
  284. package/dist/sdk/shipping/request-timeout.js.map +1 -1
  285. package/dist/sdk/shipping/shipping-operations.d.ts +1 -1
  286. package/dist/sdk/shipping/shipping-operations.js.map +1 -1
  287. package/dist/sdk/shipping/use-shipping-actions.d.ts +2 -2
  288. package/dist/sdk/shipping/use-shipping-actions.js +1 -1
  289. package/dist/sdk/shipping/use-shipping-actions.js.map +1 -1
  290. package/dist/sdk/state/cart.d.ts +3 -4
  291. package/dist/sdk/state/cart.js +6 -7
  292. package/dist/sdk/state/cart.js.map +1 -1
  293. package/dist/utils/loggable-error-name.js +49 -0
  294. package/dist/utils/loggable-error-name.js.map +1 -0
  295. package/dist/utils/resolve-authored-copy.js +18 -0
  296. package/dist/utils/resolve-authored-copy.js.map +1 -0
  297. package/dist/utils/safe-href.js +3 -3
  298. package/dist/utils/safe-href.js.map +1 -1
  299. package/dist/utils/substitute-template.js +33 -0
  300. package/dist/utils/substitute-template.js.map +1 -0
  301. package/package.json +2 -1
  302. package/src/styles/tailwind.css +34 -0
  303. package/dist/docs/client-profile-form.md +0 -192
  304. package/dist/matter/client-profile-form/client-profile-form.d.ts +0 -126
  305. package/dist/matter/client-profile-form/client-profile-form.js +0 -662
  306. package/dist/matter/client-profile-form/client-profile-form.js.map +0 -1
@@ -0,0 +1 @@
1
+ {"version":3,"file":"save-card.js","names":[],"sources":["../../../src/matter/save-card/save-card.tsx"],"sourcesContent":["'use client';\n\nimport { defineComponent } from '@vtex/fs-core';\nimport type { ChangeEvent, ComponentPropsWithRef } from 'react';\nimport { useState } from 'react';\nimport { Checkbox } from '../../particles/checkbox/checkbox.js';\nimport { Field } from '../../particles/field/field.js';\nimport { Icon } from '../../particles/icon/icon.js';\nimport { useActionsOverride } from '../../sdk/actions/actions-override-context.js';\nimport type { ComponentActions } from '../../sdk/actions/types.js';\nimport { useDataOverride } from '../../sdk/data/data-override-context.js';\nimport { usePayment } from '../payment/payment.js';\nimport { type SessionContextValue, useSession } from '../session/session.js';\n\n/**\n * The `SaveCard` matter (specs/save-card-control.md, T10 of the Credit Card epic;\n * docs/features/credit-card/rfc.md § D5). Composes `Checkbox` + `Field` into an opt-in, off-by-default\n * toggle plus a custom nickname field, visible only to an authenticated shopper.\n *\n * Writes exactly two client-only facts T7 already provisioned on `PaymentDraft`\n * (`setDraftSaveCard` / `setDraftCardLabel`, via `usePayment()`) — this component never registers on\n * the submission channel and never calls `sendPayments`. `CardEntry` (T7) folds both facts into the\n * card object at submit time (`card-entry.tsx:639-640`); the saved-card path does NOT, because it\n * submits `{ kind: 'saved', accountId, bin, securityCode }` (`saved-cards.tsx:355`) and\n * `PaymentGatewaySavedCard` (`sdk/payment/gateway.ts:103-111`) declares no field either fact could\n * occupy. So on that path both draft writes are inert while the consent below is still granted\n * account-wide. Checking the toggle grants the account-level data-storage consent a saved card\n * requires (`$actions.payment.saveCard(true)`); unchecking never revokes it — see specs/save-card-control.md Decision 1 for the rationale and the\n * last-write-wins hazard against `SavePersonalDataOptInControl`, which writes the same field.\n *\n * Self-gates on `useSession()` (never `SessionGuard`) and requires BOTH the `payment` and `session`\n * contexts — the first component in this package to declare `requiresContext` as an array (Decision 4).\n */\n\n// ---------------------------------------------------------------------------\n// GraphQL Fragment — raw template literal, matching `card-entry.tsx`'s / `payment.tsx`'s convention;\n// the codegen `documents` glob does not scan `src/matter/save-card*`. Deliberately its OWN fragment,\n// never composed under `ClientPreferencesRoot` — that Root's section context is built from a wrapped\n// `$data?.clientPreferences?.savePersonalData` read that is always `false` at runtime for an arity-1\n// fragment (ADR-0015), a live, pre-existing defect this component must not inherit\n// (specs/save-card-control.md Decision 2).\n// ---------------------------------------------------------------------------\n\nconst fragment = /* GraphQL */ `\n fragment SaveCardData on Query {\n clientPreferences {\n __typename\n id\n savePersonalData\n }\n }\n`;\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\n/** The `$data` slice forwarded by the reactive runtime — the unwrapped `ClientPreferences` entity\n * (arity-1 fragment, ADR-0015): the value of the fragment's own `clientPreferences` top-level field,\n * never the wrapping `{ clientPreferences: ... }` shape. Mirrors `card-entry.tsx`'s / `saved-cards.tsx`'s\n * identical arity-1 contract. */\nexport type SaveCardData = {\n savePersonalData?: boolean | null;\n} | null;\n\nexport type SaveCardProps = Omit<ComponentPropsWithRef<'div'>, 'children'> & {\n /** The toggle's own accessible label. */\n label?: string;\n /** Label for the nickname field. Rendered only while the toggle is checked. */\n nicknameLabel?: string;\n nicknamePlaceholder?: string;\n /** Shown beside the toggle while `savePersonalData !== true` (covers both `false` and `null`). */\n consentNoticeLabel?: string;\n /** Disables both the toggle and the nickname field. */\n disabled?: boolean;\n /** This component's own `clientPreferences` slice (arity-1 fragment, ADR-0015). */\n $data?: SaveCardData;\n /** Compiler-injected action surface; carries `payment.saveCard`. */\n $actions?: ComponentActions;\n};\n\n/** Legacy's own cap (`CardLabelInputForm.tsx:15`, `fastcheckout@cd09bec3`), carried here as a plain\n * HTML `maxLength` rather than a schema prop — schema entries are authorable copy, not behavior. */\nconst NICKNAME_MAX_LENGTH = 50;\n\n// ---------------------------------------------------------------------------\n// Pure decision functions — exported so the gate and the tri-state collapse are unit-testable\n// independent of any rendering (specs/save-card-control.md § Resolution logic).\n// ---------------------------------------------------------------------------\n\n/**\n * Whether the control should render at all. Requires BOTH `!loading` and `state === 'loggedIn'` —\n * `useSession()` forces `state: 'anonymous'` while loading (the least-privileged default), so this\n * reads correctly today even on `state` alone, but checking `loading` explicitly makes that\n * invariant load-bearing in this component's own code rather than incidental to `session.tsx`'s\n * current implementation. Takes no closure over component state, so it is safe to call fresh on\n * every render — this is what lets the control appear the instant a loading session resolves to a\n * logged-in shopper, with no stuck-hidden state (specs/save-card-control.md § NFR-1).\n */\nexport function isSaveCardVisible({ loading, state }: Pick<SessionContextValue, 'loading' | 'state'>): boolean {\n return !loading && state === 'loggedIn';\n}\n\n/**\n * Whether the consent notice should render. Collapses `null` (\"the shopper has never chosen\") and\n * `false` (\"explicitly declined at some point\") into the same \"not yet granted\" outcome — checking\n * the toggle is a new grant either way, from this control's point of view\n * (specs/save-card-control.md Decision 2).\n */\nexport function isConsentNoticeVisible(savePersonalData: boolean | null | undefined): boolean {\n return savePersonalData !== true;\n}\n\n// ---------------------------------------------------------------------------\n// Definition\n// ---------------------------------------------------------------------------\n\nexport const SaveCardDefinition = defineComponent<SaveCardProps>({\n name: 'SaveCard',\n importPath: '@vtex/faststore/components',\n component: SaveCard,\n fragment,\n fetchStrategy: 'reactive',\n requiresContext: ['payment', 'session'],\n actions: { payment: ['saveCard'] },\n editor: { label: 'Save Card', category: 'commerce' },\n schema: {\n label: { type: 'string', default: 'Save this card for next time', label: 'Toggle label' },\n nicknameLabel: { type: 'string', default: 'Card nickname', label: 'Nickname field label' },\n nicknamePlaceholder: { type: 'string', label: 'Nickname field placeholder' },\n consentNoticeLabel: {\n type: 'string',\n default: 'Checking this also lets us store your personal data for future purchases.',\n label: 'Consent notice label',\n },\n disabled: { type: 'boolean', default: false, label: 'Disabled' },\n },\n guideline:\n 'Opt-in save-card control for authenticated shoppers only: off by default; checking it writes the draft save-card flag/label AND grants the broader save-personal-data consent ($actions.payment.saveCard(true)); unchecking clears only the draft flag and never revokes that consent (see save-card-control.md Decision 1). The nickname field appears only while checked. Must be inside both a PaymentSectionRoot/PaymentSectionController (T5) and a SessionRoot — self-gates to null while the session is loading or the shopper is not logged in. Renders no card data; the flag and label are folded into the card object by CardEntry/SavedCards at submit time.',\n});\n\n// ---------------------------------------------------------------------------\n// Implementation\n// ---------------------------------------------------------------------------\n\nexport function SaveCard({\n label = 'Save this card for next time',\n nicknameLabel = 'Card nickname',\n nicknamePlaceholder,\n consentNoticeLabel = 'Checking this also lets us store your personal data for future purchases.',\n disabled = false,\n className,\n $data: $dataProp,\n $actions: $actionsProp,\n ...props\n}: SaveCardProps) {\n const $data = useDataOverride($dataProp);\n const $actions = useActionsOverride($actionsProp);\n // Also enforces the SessionRoot ancestor: useSession() throws when rendered outside one.\n const { state, loading } = useSession();\n // Also enforces the PaymentSectionRoot/PaymentSectionController ancestor: usePayment() throws\n // when rendered outside one.\n const { draft, setDraftSaveCard, setDraftCardLabel, placing, cardPaymentCollapsed } = usePayment();\n\n const [pending, setPending] = useState(false);\n const [consentError, setConsentError] = useState<string | null>(null);\n\n // Both hooks above are called unconditionally, before this early return, so neither context\n // requirement is skipped depending on the auth state. T191 (FR-7, Decision 3): nothing renders while\n // the card payment is collapsed either; the flag and label stay on `draft` for when it comes back.\n if (cardPaymentCollapsed || !isSaveCardVisible({ loading, state })) return null;\n\n const checked = draft.saveCard ?? false;\n const fieldDisabled = disabled || pending;\n\n async function handleCheckedChange(next: boolean) {\n setDraftSaveCard(next);\n setConsentError(null);\n // Decision 1: unchecking withdraws only this transaction's local intent — no dispatch of any\n // kind, so a standing `savePersonalData` consent an unrelated control granted is never touched.\n if (!next) return;\n\n const saveCard = $actions?.payment?.saveCard;\n // Silent no-op when the action namespace is not injected, mirroring\n // `usePaymentMethodSelection.selectMethod`'s \"no-op (no error, no throw)\" convention — this is a\n // soft, consent-adjacent write, not the PCI-critical submission itself.\n if (!saveCard) return;\n\n setPending(true);\n try {\n const userErrors = await saveCard(true);\n const [firstError] = userErrors;\n if (firstError) setConsentError(firstError.message);\n } finally {\n setPending(false);\n }\n }\n\n function handleNicknameChange(event: ChangeEvent<HTMLInputElement>) {\n const raw = event.target.value.slice(0, NICKNAME_MAX_LENGTH);\n // Normalizes an emptied field to `null`, never `''` — matches `PaymentDraft`'s existing\n // \"always present, null means absent\" idiom for `bin`/`path`.\n setDraftCardLabel(raw.length > 0 ? raw : null);\n }\n\n return (\n <div\n {...props}\n className={className}\n inert={placing}\n aria-disabled={placing || undefined}\n data-fs-save-card=\"\"\n {...(placing ? { 'data-fs-save-card-placing': '' } : {})}\n {...(pending ? { 'data-fs-save-card-pending': '' } : {})}\n >\n <div data-fs-save-card-toggle-field=\"\">\n <Field.Root disabled={fieldDisabled} data-fs-field-root=\"\">\n <Field.Label>\n <Checkbox.Root checked={checked} onCheckedChange={handleCheckedChange} disabled={fieldDisabled}>\n <Checkbox.Indicator>\n <Icon name=\"checked\" />\n </Checkbox.Indicator>\n </Checkbox.Root>\n {label}\n </Field.Label>\n </Field.Root>\n {isConsentNoticeVisible($data?.savePersonalData) ? (\n <p data-fs-save-card-consent-notice=\"\">{consentNoticeLabel}</p>\n ) : null}\n </div>\n\n {checked ? (\n <div data-fs-save-card-nickname-field=\"\">\n <Field.Root disabled={fieldDisabled} data-fs-field-root=\"\">\n <Field.Label>{nicknameLabel}</Field.Label>\n <Field.Control\n name=\"cardLabel\"\n placeholder={nicknamePlaceholder}\n value={draft.cardLabel ?? ''}\n onChange={handleNicknameChange}\n disabled={fieldDisabled}\n maxLength={NICKNAME_MAX_LENGTH}\n />\n </Field.Root>\n </div>\n ) : null}\n\n {consentError ? (\n <div role=\"alert\" data-fs-save-card-error=\"\">\n {consentError}\n </div>\n ) : null}\n </div>\n );\n}\n"],"mappings":"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"}
@@ -0,0 +1,79 @@
1
+ import { ComponentDefinition } from "../../core/dist/define-component.js";
2
+ import "../../core/dist/index.js";
3
+ import { ComponentActions } from "../../sdk/actions/types.js";
4
+ import { ComponentPropsWithRef, ReactNode } from "react";
5
+ //#region src/matter/saved-cards/saved-cards.d.ts
6
+ /** The minimal per-row identifiers `SavedCards` needs to iterate and to dispatch selection. */
7
+ export type SavedCardsListEntry = Readonly<{
8
+ id: string;
9
+ accountId: string;
10
+ methodId: string;
11
+ bin: string;
12
+ }>;
13
+ export type SavedCardsData = {
14
+ savedCards?: readonly SavedCardsListEntry[];
15
+ } | null;
16
+ export type SavedCardsProps = ComponentPropsWithRef<'div'> & {
17
+ /** Unwrapped `payment` value from the reactive runtime (arity-1 fragment, ADR-0015); `null` until resolved. */
18
+ $data?: SavedCardsData;
19
+ /** The per-row template, typically one `SavedCardOption`, repeated once per saved card. */
20
+ children?: ReactNode;
21
+ /**
22
+ * The radio group's accessible name, rendered as a visible `Fieldset.Legend` (T38, Decision 1).
23
+ * Resolved through `resolveAuthoredCopy`: an authored empty or whitespace-only string falls back to
24
+ * the default rather than rendering a nameless group.
25
+ */
26
+ legend?: string;
27
+ };
28
+ /** The full per-row slice `SavedCardOption` independently resolves via `reactive-list-item`. */
29
+ export type SavedCardOptionSlice = Readonly<{
30
+ accountId: string;
31
+ methodId: string;
32
+ network: string;
33
+ lastFourDigits: string;
34
+ bin: string;
35
+ label: string | null;
36
+ expired: boolean;
37
+ cvvRequired: boolean;
38
+ }>;
39
+ export type SavedCardOptionProps = ComponentPropsWithRef<'div'> & {
40
+ /** Per-row slice from the reactive-list-item runtime; injected/overridden by the wrapper, or passed directly in tests. */
41
+ $data?: SavedCardOptionSlice | null;
42
+ /** Typically a `SavedCardCvvField`. */
43
+ children?: ReactNode;
44
+ };
45
+ export type SavedCardCvvFieldProps = ComponentPropsWithRef<'input'> & {
46
+ /** Compiler-injected action surface; carries `payment.sendPayments`. */
47
+ $actions?: ComponentActions;
48
+ /**
49
+ * Accessible name for the CVV input. No visible `<label>` renders (Accessibility Contract), so this
50
+ * is the input's only accessible name — authorable rather than a hardcoded English string.
51
+ */
52
+ securityCodeLabel?: string;
53
+ /**
54
+ * Helper copy rendered as a sibling `<p>` and linked to the input via `aria-describedby` (this
55
+ * component's root is the input itself, so there is no `Field.Root` to host a `Field.Description` —
56
+ * saved-cards.md Decision 3). An empty string renders nothing. Copy configuration only — never the
57
+ * security code itself.
58
+ */
59
+ securityCodeDescription?: string;
60
+ };
61
+ export declare const SavedCardsDefinition: ComponentDefinition<SavedCardsProps>;
62
+ export declare const SavedCardOptionDefinition: ComponentDefinition<SavedCardOptionProps>;
63
+ export declare const SavedCardCvvFieldDefinition: ComponentDefinition<SavedCardCvvFieldProps>;
64
+ export declare function SavedCards({ $data: $dataProp, legend, className, children, ...props }: SavedCardsProps): import("react/jsx-runtime").JSX.Element | null;
65
+ export declare function SavedCardOption({ $data: $dataProp, className, children, ...props }: SavedCardOptionProps): import("react/jsx-runtime").JSX.Element | null;
66
+ /**
67
+ * The saved-card path's own client-synthesized refusal (T27, specs/place-order-preflight-card-
68
+ * validation.md FR-11/FR-12; Decision 8). Distinct from `card-entry`'s `INVALID_CARD_DATA` (a
69
+ * different path, a different set of fields, and importing it here would be a matter → matter
70
+ * dependency) and from the gateway's own codes.
71
+ *
72
+ * Never name this constant, or its string value, inside `sdk/payment/use-place-order.ts`: it contains
73
+ * `security` and would fail `CARD_FIELD_PATTERN`. The pre-flight only ever handles the opaque
74
+ * `PaymentUserError` (spec NFR-1, Risk R9).
75
+ */
76
+ export declare const SAVED_CARD_INVALID_SECURITY_CODE = "INVALID_SECURITY_CODE";
77
+ export declare function SavedCardCvvField({ $actions: $actionsProp, securityCodeLabel, securityCodeDescription, className, ...props }: SavedCardCvvFieldProps): import("react/jsx-runtime").JSX.Element | null;
78
+ //#endregion
79
+ //# sourceMappingURL=saved-cards.d.ts.map
@@ -0,0 +1,387 @@
1
+ "use client";
2
+ import { defineComponent } from "../../core/dist/define-component.js";
3
+ import "../../core/dist/index.js";
4
+ import { useActionsOverride } from "../../sdk/actions/actions-override-context.js";
5
+ import { useDataOverride } from "../../sdk/data/data-override-context.js";
6
+ import { resolveAuthoredCopy } from "../../utils/resolve-authored-copy.js";
7
+ import { Fieldset } from "../../particles/fieldset/fieldset.js";
8
+ import { RadioGroup, RadioIndicator, RadioRoot } from "../../particles/radio/radio.js";
9
+ import { useRegisterPaymentSubmission } from "../../sdk/payment/submission-registry.js";
10
+ import { PAYMENT_NETWORK_ERROR_CODE } from "../../sdk/payment/operations.js";
11
+ import { usePayment } from "../payment/payment.js";
12
+ import { SECURITY_CODE_MASK_STYLE, isSecurityCodeComplete } from "../../sdk/payment/security-code-mask.js";
13
+ import { ListItemRefContext } from "../../sdk/data/list-item-ref-context.js";
14
+ import { createContext, useCallback, useContext, useId, useRef, useState } from "react";
15
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
16
+ //#region src/matter/saved-cards/saved-cards.tsx
17
+ /**
18
+ * Pick a saved card and enter only the CVV (specs/saved-cards.md, T12 of the Credit Card epic;
19
+ * docs/features/credit-card/rfc.md § D7). Three components, split by data-fetch role rather than by
20
+ * visual grouping (ADR-0012 Option B, per `SavedCard`'s own schema doc comment):
21
+ *
22
+ * - `SavedCards` — the list root. Owns the minimal `{ id, accountId, methodId }` selection needed to
23
+ * iterate and to dispatch selection; publishes each row's entity ref via `ListItemRefContext`,
24
+ * mirroring `CartList`.
25
+ * - `SavedCardOption` — the per-row template. Independently subscribes to its own `SavedCard:<id>`
26
+ * cache entity through the shipped `reactive-list-item` runtime, mirroring `CartItemRoot`.
27
+ * - `SavedCardCvvField` — the one PCI-bearing, action-dispatching leaf. Declares no `fetchStrategy` of
28
+ * its own; it reads its row through `SavedCardOption`'s plain context, mirroring `CartItemRemove`'s
29
+ * split from `CartItemRoot` (spec Decision 1).
30
+ *
31
+ * The submission continuation is the PCI-critical core (epic § "The card-data submission channel"):
32
+ * `SavedCardCvvField` (via its internal `SavedCardCvvInput`) registers the `'saved-card'` continuation
33
+ * on T5's submission registry, closing over the re-entered CVV and the account reference, and calls
34
+ * `$actions.payment.sendPayments` itself. T13's `usePlaceOrder` resolves and invokes it; this module
35
+ * never returns the CVV, only `userErrors`.
36
+ *
37
+ * Selecting a row dispatches `usePayment().selectMethod({ methodId, accountId, bin })` — the
38
+ * `bin` is this row's own projected BIN, never `usePayment().draft.bin`. The epic's and the RFC's
39
+ * own text says the saved-card continuation closes over "the account id and BIN", which conflates
40
+ * two different calls: `paymentMethodSelect` (this dispatch) sends the saved card's own BIN alongside
41
+ * `accountId` — a `bin` that does not match the saved card's own BIN is rejected with
42
+ * `MISMATCHED_BIN_FOR_SAVED_CARD`, verified against
43
+ * `apps/storefront-api-core/src/graphql/modules/checkout/index.ts`. The gateway send (inside
44
+ * `SavedCardCvvInput`, below) legitimately carries both, because it is a different wire contract
45
+ * (spec Decision 2 has the full correction).
46
+ */
47
+ const fragment = `
48
+ fragment SavedCardsListData on Query {
49
+ payment {
50
+ __typename
51
+ id
52
+ savedCards {
53
+ __typename
54
+ id
55
+ accountId
56
+ methodId
57
+ bin
58
+ }
59
+ }
60
+ }
61
+ `;
62
+ const optionFragment = `
63
+ fragment SavedCardOptionData on Query {
64
+ payment {
65
+ savedCards {
66
+ __typename
67
+ id
68
+ accountId
69
+ methodId
70
+ network
71
+ lastFourDigits
72
+ bin
73
+ label
74
+ expired
75
+ cvvRequired
76
+ }
77
+ }
78
+ }
79
+ `;
80
+ const SavedCardDataContext = createContext(null);
81
+ function useSavedCardData() {
82
+ const data = useContext(SavedCardDataContext);
83
+ if (!data) throw new Error("SavedCardCvvField must be used inside a <SavedCardOption> subtree.");
84
+ return data;
85
+ }
86
+ const SavedCardsDefinition = defineComponent({
87
+ name: "SavedCards",
88
+ importPath: "@vtex/faststore/components",
89
+ component: SavedCards,
90
+ fragment,
91
+ fetchStrategy: "reactive",
92
+ requiresContext: "payment",
93
+ providesContext: "saved-card",
94
+ editor: {
95
+ label: "Saved Cards",
96
+ category: "commerce"
97
+ },
98
+ schema: { legend: {
99
+ type: "string",
100
+ default: "Saved cards",
101
+ label: "Legend"
102
+ } },
103
+ guideline: "Radio list of the shopper's saved cards (network, last four, label); expired cards render present but disabled, never selectable. Place a SavedCardOption inside as the per-card template, itself wrapping a SavedCardCvvField so the shopper can complete payment with a stored card — omitting the CVV field means a shopper can select a card but never pay with it. Picking a row dispatches the payment method selection by methodId and accountId only, never a card number or BIN, plus the inline selection error (role=\"alert\") if the server rejects it. \"Use a new card\" is CardEntry composed as a sibling, not a control inside this list. Renders one container div and no chrome of its own; always carries a loading/empty/ready state hook. Must be inside a PaymentSectionRoot or PaymentSectionController. See saved-cards.md."
104
+ });
105
+ const SavedCardOptionDefinition = defineComponent({
106
+ name: "SavedCardOption",
107
+ importPath: "@vtex/faststore/components",
108
+ component: SavedCardOption,
109
+ fragment: optionFragment,
110
+ fetchStrategy: "reactive-list-item",
111
+ requiresContext: "saved-card",
112
+ providesContext: "saved-card-data",
113
+ editor: {
114
+ label: "Saved Card Option",
115
+ category: "commerce"
116
+ },
117
+ schema: {},
118
+ guideline: "One saved card as a radio row: network, masked last four, and its label when set; disabled when expired. Place a SavedCardCvvField inside so the CVV appears once this row is selected. Must be inside SavedCards."
119
+ });
120
+ const SavedCardCvvFieldDefinition = defineComponent({
121
+ name: "SavedCardCvvField",
122
+ importPath: "@vtex/faststore/components",
123
+ component: SavedCardCvvField,
124
+ requiresContext: "saved-card-data",
125
+ actions: { payment: ["sendPayments"] },
126
+ editor: {
127
+ label: "Saved Card CVV Field",
128
+ category: "commerce"
129
+ },
130
+ schema: {
131
+ securityCodeLabel: {
132
+ type: "string",
133
+ default: "Security code",
134
+ label: "Security code label"
135
+ },
136
+ securityCodeDescription: {
137
+ type: "string",
138
+ default: "The 3-4 digit number on your card",
139
+ label: "Security code description"
140
+ }
141
+ },
142
+ guideline: "The CVV re-entry field for its saved card row; renders nothing until that row is selected and requires a CVV. Registers the direct-to-gateway submission for the saved-card path on selection — the CVV never transits our backend, only the gateway leg's userErrors return. Must be inside SavedCardOption."
143
+ });
144
+ /** Mirrors `PaymentMethodList`'s own state hook, so an empty or loading region stays swatch-targetable. */
145
+ function savedCardsState({ loading, count }) {
146
+ if (loading) return "loading";
147
+ if (count === 0) return "empty";
148
+ return "ready";
149
+ }
150
+ /**
151
+ * The account a row should treat as "the current selection." `selectedAccountId` is server state that
152
+ * `selectNewCard()` never clears — only `draft.path` flips to `'new-card'` (`use-payment-method-
153
+ * selection.ts`) — so reading `selectedAccountId` alone leaves the previously picked row wedged
154
+ * checked, with its CVV field still mounted and registered, after the shopper picks "use a new card"
155
+ * instead. Every read site in this module goes through this one function — the radio group's own
156
+ * `value`, a row's `-selected` state, and the CVV field's visibility gate — so the three can never
157
+ * drift out of sync with each other. Reads only `draft.path`, never `draft.bin`: that stays a fully
158
+ * separate concern (Decision 2) that no call site in this module touches.
159
+ */
160
+ function resolveEffectiveSelectedAccountId(selectedAccountId, draftPath) {
161
+ return draftPath === "new-card" ? null : selectedAccountId;
162
+ }
163
+ function SavedCards({ $data: $dataProp, legend, className, children, ...props }) {
164
+ const $data = useDataOverride($dataProp);
165
+ const { selectedAccountId, selectMethod, selectionError, draft, placing, cardPaymentCollapsed } = usePayment();
166
+ const loading = $data == null;
167
+ const savedCards = $data?.savedCards ?? [];
168
+ const effectiveSelectedAccountId = resolveEffectiveSelectedAccountId(selectedAccountId, draft.path);
169
+ const handleValueChange = useCallback((accountId) => {
170
+ const card = savedCards.find((entry) => entry.accountId === accountId);
171
+ if (!card) return;
172
+ selectMethod({
173
+ methodId: card.methodId,
174
+ accountId: card.accountId,
175
+ ...card.bin ? { bin: card.bin } : {}
176
+ });
177
+ }, [savedCards, selectMethod]);
178
+ if (cardPaymentCollapsed) return null;
179
+ return /* @__PURE__ */ jsxs("div", {
180
+ ...props,
181
+ className,
182
+ inert: placing,
183
+ "aria-disabled": placing || void 0,
184
+ "data-fs-saved-cards": "",
185
+ "data-fs-saved-cards-state": savedCardsState({
186
+ loading,
187
+ count: savedCards.length
188
+ }),
189
+ ...placing ? { "data-fs-saved-cards-placing": "" } : {},
190
+ children: [selectionError ? /* @__PURE__ */ jsx("div", {
191
+ role: "alert",
192
+ "data-fs-saved-cards-error": "",
193
+ children: selectionError
194
+ }) : null, savedCards.length > 0 ? /* @__PURE__ */ jsxs(Fieldset.Root, {
195
+ "data-fs-saved-cards-fieldset": "",
196
+ children: [/* @__PURE__ */ jsx(Fieldset.Legend, { children: resolveAuthoredCopy(legend, "Saved cards") }), /* @__PURE__ */ jsx(RadioGroup, {
197
+ value: effectiveSelectedAccountId ?? "",
198
+ onValueChange: handleValueChange,
199
+ children: savedCards.map((card) => /* @__PURE__ */ jsx(ListItemRefContext, {
200
+ value: {
201
+ __typename: "SavedCard",
202
+ id: card.id,
203
+ fallbackData: void 0
204
+ },
205
+ children
206
+ }, card.id))
207
+ })]
208
+ }) : null]
209
+ });
210
+ }
211
+ function SavedCardOption({ $data: $dataProp, className, children, ...props }) {
212
+ const slice = useDataOverride($dataProp) ?? null;
213
+ const { selectedAccountId, draft } = usePayment();
214
+ if (!slice) return null;
215
+ const isSelected = slice.accountId === resolveEffectiveSelectedAccountId(selectedAccountId, draft.path);
216
+ return /* @__PURE__ */ jsx(SavedCardDataContext, {
217
+ value: slice,
218
+ children: /* @__PURE__ */ jsxs("div", {
219
+ ...props,
220
+ className,
221
+ "data-fs-saved-card-option": "",
222
+ ...isSelected ? { "data-fs-saved-card-option-selected": "" } : {},
223
+ ...slice.expired ? { "data-fs-saved-card-option-expired": "" } : {},
224
+ children: [/* @__PURE__ */ jsxs(RadioRoot, {
225
+ value: slice.accountId,
226
+ disabled: slice.expired,
227
+ children: [
228
+ /* @__PURE__ */ jsx(RadioIndicator, { "data-fs-saved-card-option-indicator": "" }),
229
+ /* @__PURE__ */ jsx("span", {
230
+ "data-fs-saved-card-option-network": "",
231
+ children: slice.network
232
+ }),
233
+ /* @__PURE__ */ jsx("span", {
234
+ "data-fs-saved-card-option-last-four": "",
235
+ children: `•••• ${slice.lastFourDigits}`
236
+ }),
237
+ slice.label ? /* @__PURE__ */ jsx("span", {
238
+ "data-fs-saved-card-option-label": "",
239
+ children: slice.label
240
+ }) : null
241
+ ]
242
+ }), children]
243
+ })
244
+ });
245
+ }
246
+ const MISSING_ACTION_ERROR = {
247
+ code: PAYMENT_NETWORK_ERROR_CODE,
248
+ message: "Unable to complete the request. Please try again."
249
+ };
250
+ /**
251
+ * The saved-card path's own client-synthesized refusal (T27, specs/place-order-preflight-card-
252
+ * validation.md FR-11/FR-12; Decision 8). Distinct from `card-entry`'s `INVALID_CARD_DATA` (a
253
+ * different path, a different set of fields, and importing it here would be a matter → matter
254
+ * dependency) and from the gateway's own codes.
255
+ *
256
+ * Never name this constant, or its string value, inside `sdk/payment/use-place-order.ts`: it contains
257
+ * `security` and would fail `CARD_FIELD_PATTERN`. The pre-flight only ever handles the opaque
258
+ * `PaymentUserError` (spec NFR-1, Risk R9).
259
+ */
260
+ const SAVED_CARD_INVALID_SECURITY_CODE = "INVALID_SECURITY_CODE";
261
+ const INVALID_SECURITY_CODE_ERROR = {
262
+ code: SAVED_CARD_INVALID_SECURITY_CODE,
263
+ message: "Enter a valid security code before placing the order."
264
+ };
265
+ /** VTEX's own CVV upper bound across the networks this codebase's masks cover (Decision 4). */
266
+ const CVV_MAX_LENGTH = 4;
267
+ function sanitizeCvv(value) {
268
+ return value.replace(/\D/g, "").slice(0, CVV_MAX_LENGTH);
269
+ }
270
+ /**
271
+ * Three or four digits (T27, FR-11). `sanitizeCvv` already strips non-digits and caps at
272
+ * `CVV_MAX_LENGTH`, so the only rule left is the floor — and three-or-four is `card-entry`'s own
273
+ * fallback for a method that declares no `cvvRegex` (its `isValidCvv`, `/^\d{3,4}$/`). Requiredness is
274
+ * not checked here: `SavedCardCvvInput` renders only when the row is selected and `cvvRequired` is
275
+ * true, so its presence *is* the requiredness signal.
276
+ */
277
+ const SAVED_CARD_CVV_PATTERN = /^\d{3,4}$/;
278
+ /**
279
+ * The length at which this field's security code counts as fully typed, and the mask engages (T173,
280
+ * specs/mask-the-cvv-only-once-it.md, FR-3). Three — the floor of `SAVED_CARD_CVV_PATTERN` above,
281
+ * for the same reason that pattern accepts either length: a saved card carries no per-network CVV
282
+ * length. Its slice exposes a display `network` string, not a `cvvMask`, so nothing here tells a
283
+ * 3-digit network apart from Amex's 4 the way `CardEntry` can, and inferring one from the display
284
+ * name would hardcode a network table this package deliberately does not own.
285
+ *
286
+ * Masking at the floor is the side to err on: a complete code is never left legible on either kind
287
+ * of card, and a 4-digit code is simply already masked when its last digit arrives.
288
+ */
289
+ const SAVED_CARD_CVV_COMPLETE_LENGTH = 3;
290
+ /** Merges a caller-forwarded `aria-describedby` (from `...props`) with the description `<p>`'s own
291
+ * id, rather than clobbering it — the accessibility wiring `Field.Root` would otherwise provide for
292
+ * free (saved-cards.md Decision 3). */
293
+ function mergeDescribedBy(existing, id) {
294
+ return existing ? `${existing} ${id}` : id;
295
+ }
296
+ /**
297
+ * The stateful, registering half of the CVV field — split out from `SavedCardCvvField` so this row's
298
+ * conditional visibility never skips a hook call (spec Decision 5). This component mounts wholesale
299
+ * when its row is selected and requires a CVV, and unmounts wholesale otherwise, so its own hooks
300
+ * always run the same number of times across its own lifetime; only its *presence* is conditional,
301
+ * decided by its caller.
302
+ */
303
+ function SavedCardCvvInput({ accountId, bin, securityCodeLabel, securityCodeDescription, $actions, ref: forwardedRef, ...props }) {
304
+ const [cvv, setCvv] = useState("");
305
+ const inputRef = useRef(null);
306
+ const sendPayments = $actions?.payment?.sendPayments;
307
+ const descriptionId = useId();
308
+ const setInputRef = useCallback((node) => {
309
+ inputRef.current = node;
310
+ if (typeof forwardedRef !== "function") {
311
+ if (forwardedRef) forwardedRef.current = node;
312
+ return;
313
+ }
314
+ const cleanup = forwardedRef(node);
315
+ if (typeof cleanup === "function") return () => {
316
+ inputRef.current = null;
317
+ cleanup();
318
+ };
319
+ }, [forwardedRef]);
320
+ const checkCvv = useCallback(() => {
321
+ if (SAVED_CARD_CVV_PATTERN.test(cvv)) return [];
322
+ inputRef.current?.focus();
323
+ return [INVALID_SECURITY_CODE_ERROR];
324
+ }, [cvv]);
325
+ const submit = useCallback(async (transaction) => {
326
+ const refusals = checkCvv();
327
+ if (refusals.length > 0) return refusals;
328
+ if (!sendPayments) return [MISSING_ACTION_ERROR];
329
+ return sendPayments({
330
+ transaction,
331
+ card: {
332
+ kind: "saved",
333
+ accountId,
334
+ bin,
335
+ securityCode: cvv
336
+ }
337
+ });
338
+ }, [
339
+ checkCvv,
340
+ sendPayments,
341
+ accountId,
342
+ bin,
343
+ cvv
344
+ ]);
345
+ useRegisterPaymentSubmission("saved-card", submit, checkCvv);
346
+ const ariaDescribedBy = securityCodeDescription ? mergeDescribedBy(props["aria-describedby"], descriptionId) : props["aria-describedby"];
347
+ const inputStyle = isSecurityCodeComplete(cvv, SAVED_CARD_CVV_COMPLETE_LENGTH) ? {
348
+ ...props.style,
349
+ ...SECURITY_CODE_MASK_STYLE
350
+ } : props.style;
351
+ return /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx("input", {
352
+ ...props,
353
+ ref: setInputRef,
354
+ type: "text",
355
+ inputMode: "numeric",
356
+ autoComplete: "off",
357
+ "aria-label": securityCodeLabel,
358
+ "aria-describedby": ariaDescribedBy,
359
+ value: cvv,
360
+ onChange: (event) => setCvv(sanitizeCvv(event.target.value)),
361
+ "data-fs-saved-card-cvv-field": "",
362
+ style: inputStyle
363
+ }), securityCodeDescription ? /* @__PURE__ */ jsx("p", {
364
+ id: descriptionId,
365
+ "data-fs-saved-card-cvv-description": "",
366
+ children: securityCodeDescription
367
+ }) : null] });
368
+ }
369
+ function SavedCardCvvField({ $actions: $actionsProp, securityCodeLabel = "Security code", securityCodeDescription = "The 3-4 digit number on your card", className, ...props }) {
370
+ const $actions = useActionsOverride($actionsProp);
371
+ const { accountId, bin, cvvRequired } = useSavedCardData();
372
+ const { selectedAccountId, draft } = usePayment();
373
+ if (accountId !== resolveEffectiveSelectedAccountId(selectedAccountId, draft.path) || !cvvRequired) return null;
374
+ return /* @__PURE__ */ jsx(SavedCardCvvInput, {
375
+ ...props,
376
+ className,
377
+ accountId,
378
+ bin,
379
+ securityCodeLabel,
380
+ securityCodeDescription,
381
+ $actions
382
+ });
383
+ }
384
+ //#endregion
385
+ export { SAVED_CARD_INVALID_SECURITY_CODE, SavedCardCvvField, SavedCardCvvFieldDefinition, SavedCardOption, SavedCardOptionDefinition, SavedCards, SavedCardsDefinition };
386
+
387
+ //# sourceMappingURL=saved-cards.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"saved-cards.js","names":[],"sources":["../../../src/matter/saved-cards/saved-cards.tsx"],"sourcesContent":["'use client';\n\nimport { defineComponent } from '@vtex/fs-core';\nimport {\n type ComponentPropsWithRef,\n createContext,\n type ReactNode,\n useCallback,\n useContext,\n useId,\n useRef,\n useState,\n} from 'react';\nimport { Fieldset } from '../../particles/fieldset/fieldset.js';\nimport { RadioGroup, RadioIndicator, RadioRoot } from '../../particles/radio/radio.js';\nimport { useActionsOverride } from '../../sdk/actions/actions-override-context.js';\nimport type { ComponentActions } from '../../sdk/actions/types.js';\nimport { useDataOverride } from '../../sdk/data/data-override-context.js';\nimport { ListItemRefContext } from '../../sdk/data/list-item-ref-context.js';\nimport { PAYMENT_NETWORK_ERROR_CODE, type PaymentUserError } from '../../sdk/payment/operations.js';\nimport { isSecurityCodeComplete, SECURITY_CODE_MASK_STYLE } from '../../sdk/payment/security-code-mask.js';\nimport {\n type PaymentPreflightCheck,\n type PaymentSubmission,\n useRegisterPaymentSubmission,\n} from '../../sdk/payment/submission-registry.js';\nimport { resolveAuthoredCopy } from '../../utils/resolve-authored-copy.js';\nimport { usePayment } from '../payment/payment.js';\n\n/**\n * Pick a saved card and enter only the CVV (specs/saved-cards.md, T12 of the Credit Card epic;\n * docs/features/credit-card/rfc.md § D7). Three components, split by data-fetch role rather than by\n * visual grouping (ADR-0012 Option B, per `SavedCard`'s own schema doc comment):\n *\n * - `SavedCards` — the list root. Owns the minimal `{ id, accountId, methodId }` selection needed to\n * iterate and to dispatch selection; publishes each row's entity ref via `ListItemRefContext`,\n * mirroring `CartList`.\n * - `SavedCardOption` — the per-row template. Independently subscribes to its own `SavedCard:<id>`\n * cache entity through the shipped `reactive-list-item` runtime, mirroring `CartItemRoot`.\n * - `SavedCardCvvField` — the one PCI-bearing, action-dispatching leaf. Declares no `fetchStrategy` of\n * its own; it reads its row through `SavedCardOption`'s plain context, mirroring `CartItemRemove`'s\n * split from `CartItemRoot` (spec Decision 1).\n *\n * The submission continuation is the PCI-critical core (epic § \"The card-data submission channel\"):\n * `SavedCardCvvField` (via its internal `SavedCardCvvInput`) registers the `'saved-card'` continuation\n * on T5's submission registry, closing over the re-entered CVV and the account reference, and calls\n * `$actions.payment.sendPayments` itself. T13's `usePlaceOrder` resolves and invokes it; this module\n * never returns the CVV, only `userErrors`.\n *\n * Selecting a row dispatches `usePayment().selectMethod({ methodId, accountId, bin })` — the\n * `bin` is this row's own projected BIN, never `usePayment().draft.bin`. The epic's and the RFC's\n * own text says the saved-card continuation closes over \"the account id and BIN\", which conflates\n * two different calls: `paymentMethodSelect` (this dispatch) sends the saved card's own BIN alongside\n * `accountId` — a `bin` that does not match the saved card's own BIN is rejected with\n * `MISMATCHED_BIN_FOR_SAVED_CARD`, verified against\n * `apps/storefront-api-core/src/graphql/modules/checkout/index.ts`. The gateway send (inside\n * `SavedCardCvvInput`, below) legitimately carries both, because it is a different wire contract\n * (spec Decision 2 has the full correction).\n */\n\n// ---------------------------------------------------------------------------\n// GraphQL Fragments — raw template literals, matching the `payment.tsx` sibling: the codegen\n// `documents` glob does not scan `src/matter/saved-cards*`. Both fragments drill the identical\n// `payment.savedCards` path and merge at compile time (ADR-0011/ADR-0012); `data-component-contract.\n// test.ts` validates each against the real schema.\n// ---------------------------------------------------------------------------\n\nconst fragment = /* GraphQL */ `\n fragment SavedCardsListData on Query {\n payment {\n __typename\n id\n savedCards {\n __typename\n id\n accountId\n methodId\n bin\n }\n }\n }\n`;\n\nconst optionFragment = /* GraphQL */ `\n fragment SavedCardOptionData on Query {\n payment {\n savedCards {\n __typename\n id\n accountId\n methodId\n network\n lastFourDigits\n bin\n label\n expired\n cvvRequired\n }\n }\n }\n`;\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\n/** The minimal per-row identifiers `SavedCards` needs to iterate and to dispatch selection. */\nexport type SavedCardsListEntry = Readonly<{\n id: string;\n accountId: string;\n methodId: string;\n bin: string;\n}>;\n\nexport type SavedCardsData = {\n savedCards?: readonly SavedCardsListEntry[];\n} | null;\n\nexport type SavedCardsProps = ComponentPropsWithRef<'div'> & {\n /** Unwrapped `payment` value from the reactive runtime (arity-1 fragment, ADR-0015); `null` until resolved. */\n $data?: SavedCardsData;\n /** The per-row template, typically one `SavedCardOption`, repeated once per saved card. */\n children?: ReactNode;\n /**\n * The radio group's accessible name, rendered as a visible `Fieldset.Legend` (T38, Decision 1).\n * Resolved through `resolveAuthoredCopy`: an authored empty or whitespace-only string falls back to\n * the default rather than rendering a nameless group.\n */\n legend?: string;\n};\n\n/** The full per-row slice `SavedCardOption` independently resolves via `reactive-list-item`. */\nexport type SavedCardOptionSlice = Readonly<{\n accountId: string;\n methodId: string;\n network: string;\n lastFourDigits: string;\n bin: string;\n label: string | null;\n expired: boolean;\n cvvRequired: boolean;\n}>;\n\nexport type SavedCardOptionProps = ComponentPropsWithRef<'div'> & {\n /** Per-row slice from the reactive-list-item runtime; injected/overridden by the wrapper, or passed directly in tests. */\n $data?: SavedCardOptionSlice | null;\n /** Typically a `SavedCardCvvField`. */\n children?: ReactNode;\n};\n\nexport type SavedCardCvvFieldProps = ComponentPropsWithRef<'input'> & {\n /** Compiler-injected action surface; carries `payment.sendPayments`. */\n $actions?: ComponentActions;\n /**\n * Accessible name for the CVV input. No visible `<label>` renders (Accessibility Contract), so this\n * is the input's only accessible name — authorable rather than a hardcoded English string.\n */\n securityCodeLabel?: string;\n /**\n * Helper copy rendered as a sibling `<p>` and linked to the input via `aria-describedby` (this\n * component's root is the input itself, so there is no `Field.Root` to host a `Field.Description` —\n * saved-cards.md Decision 3). An empty string renders nothing. Copy configuration only — never the\n * security code itself.\n */\n securityCodeDescription?: string;\n};\n\n// ---------------------------------------------------------------------------\n// Saved-card-data context — provided by SavedCardOption, read only by SavedCardCvvField. A plain\n// React context, not a data-fetch strategy: SavedCardCvvField has no fragment of its own (Decision 1).\n// ---------------------------------------------------------------------------\n\nconst SavedCardDataContext = createContext<SavedCardOptionSlice | null>(null);\n\nfunction useSavedCardData(): SavedCardOptionSlice {\n const data = useContext(SavedCardDataContext);\n if (!data) {\n throw new Error('SavedCardCvvField must be used inside a <SavedCardOption> subtree.');\n }\n return data;\n}\n\n// ---------------------------------------------------------------------------\n// Definitions\n// ---------------------------------------------------------------------------\n\nexport const SavedCardsDefinition = defineComponent<SavedCardsProps>({\n name: 'SavedCards',\n importPath: '@vtex/faststore/components',\n component: SavedCards,\n fragment,\n fetchStrategy: 'reactive',\n requiresContext: 'payment',\n providesContext: 'saved-card',\n editor: { label: 'Saved Cards', category: 'commerce' },\n schema: {\n // T38: an inline literal, never an identifier — the native catalog builder extracts `schema`\n // statically and silently drops an identifier-valued default (INV-8).\n legend: { type: 'string', default: 'Saved cards', label: 'Legend' },\n },\n guideline:\n 'Radio list of the shopper\\'s saved cards (network, last four, label); expired cards render present but disabled, never selectable. Place a SavedCardOption inside as the per-card template, itself wrapping a SavedCardCvvField so the shopper can complete payment with a stored card — omitting the CVV field means a shopper can select a card but never pay with it. Picking a row dispatches the payment method selection by methodId and accountId only, never a card number or BIN, plus the inline selection error (role=\"alert\") if the server rejects it. \"Use a new card\" is CardEntry composed as a sibling, not a control inside this list. Renders one container div and no chrome of its own; always carries a loading/empty/ready state hook. Must be inside a PaymentSectionRoot or PaymentSectionController. See saved-cards.md.',\n});\n\nexport const SavedCardOptionDefinition = defineComponent<SavedCardOptionProps>({\n name: 'SavedCardOption',\n importPath: '@vtex/faststore/components',\n component: SavedCardOption,\n fragment: optionFragment,\n fetchStrategy: 'reactive-list-item',\n requiresContext: 'saved-card',\n providesContext: 'saved-card-data',\n editor: { label: 'Saved Card Option', category: 'commerce' },\n schema: {},\n guideline:\n 'One saved card as a radio row: network, masked last four, and its label when set; disabled when expired. Place a SavedCardCvvField inside so the CVV appears once this row is selected. Must be inside SavedCards.',\n});\n\nexport const SavedCardCvvFieldDefinition = defineComponent<SavedCardCvvFieldProps>({\n name: 'SavedCardCvvField',\n importPath: '@vtex/faststore/components',\n component: SavedCardCvvField,\n requiresContext: 'saved-card-data',\n actions: { payment: ['sendPayments'] },\n editor: { label: 'Saved Card CVV Field', category: 'commerce' },\n schema: {\n securityCodeLabel: { type: 'string', default: 'Security code', label: 'Security code label' },\n // en-US, per requirement 12.27's own quoted copy (spec mask-the-security-code-field-and.md,\n // OQ-1 Option B) — matches this component's existing en-US securityCodeLabel default.\n securityCodeDescription: {\n type: 'string',\n default: 'The 3-4 digit number on your card',\n label: 'Security code description',\n },\n },\n guideline:\n \"The CVV re-entry field for its saved card row; renders nothing until that row is selected and requires a CVV. Registers the direct-to-gateway submission for the saved-card path on selection — the CVV never transits our backend, only the gateway leg's userErrors return. Must be inside SavedCardOption.\",\n});\n\n// ---------------------------------------------------------------------------\n// Implementations\n// ---------------------------------------------------------------------------\n\n/** Mirrors `PaymentMethodList`'s own state hook, so an empty or loading region stays swatch-targetable. */\nfunction savedCardsState({ loading, count }: { loading: boolean; count: number }): 'loading' | 'empty' | 'ready' {\n if (loading) return 'loading';\n if (count === 0) return 'empty';\n return 'ready';\n}\n\n/**\n * The account a row should treat as \"the current selection.\" `selectedAccountId` is server state that\n * `selectNewCard()` never clears — only `draft.path` flips to `'new-card'` (`use-payment-method-\n * selection.ts`) — so reading `selectedAccountId` alone leaves the previously picked row wedged\n * checked, with its CVV field still mounted and registered, after the shopper picks \"use a new card\"\n * instead. Every read site in this module goes through this one function — the radio group's own\n * `value`, a row's `-selected` state, and the CVV field's visibility gate — so the three can never\n * drift out of sync with each other. Reads only `draft.path`, never `draft.bin`: that stays a fully\n * separate concern (Decision 2) that no call site in this module touches.\n */\nfunction resolveEffectiveSelectedAccountId(selectedAccountId: string | null, draftPath: string | null): string | null {\n return draftPath === 'new-card' ? null : selectedAccountId;\n}\n\nexport function SavedCards({ $data: $dataProp, legend, className, children, ...props }: SavedCardsProps) {\n const $data = useDataOverride($dataProp);\n // `draft` here only for `.path`, to resolve the effective selection below — never `.bin`. The\n // dispatch in handleValueChange still never reads `draft` at all; see its own comment.\n const { selectedAccountId, selectMethod, selectionError, draft, placing, cardPaymentCollapsed } = usePayment();\n const loading = $data == null;\n const savedCards = $data?.savedCards ?? [];\n const effectiveSelectedAccountId = resolveEffectiveSelectedAccountId(selectedAccountId, draft.path);\n\n const handleValueChange = useCallback(\n (accountId: string) => {\n const card = savedCards.find((entry) => entry.accountId === accountId);\n if (!card) return;\n // methodId, accountId, and this row's own bin — from this list's own read model, never\n // usePayment().draft.bin. draft.bin can be non-null on the saved-card path too (e.g.\n // Installments calling setDraftBin for its own BIN-keyed lookup; use-payment-method-selection.ts\n // does not clear it on this transition), but the bin sent here must be the saved card's own\n // so paymentMethodSelect can persist a BIN-precise selection. A bin that does not match the\n // saved card's own BIN is rejected with MISMATCHED_BIN_FOR_SAVED_CARD (spec Decision 2). The\n // gateway send (SavedCardCvvInput, below) is the different call that legitimately carries both\n // bin and accountId, reading bin from this row's own resolved slice, never the draft.\n selectMethod({\n methodId: card.methodId,\n accountId: card.accountId,\n ...(card.bin ? { bin: card.bin } : {}),\n });\n },\n [savedCards, selectMethod],\n );\n\n // T191 (FR-7, Decision 3): nothing renders while the card payment is collapsed — the card option brings\n // the list back. Returned after the last hook, so this instance stays mounted.\n if (cardPaymentCollapsed) return null;\n\n return (\n <div\n {...props}\n className={className}\n inert={placing}\n aria-disabled={placing || undefined}\n data-fs-saved-cards=\"\"\n data-fs-saved-cards-state={savedCardsState({ loading, count: savedCards.length })}\n {...(placing ? { 'data-fs-saved-cards-placing': '' } : {})}\n >\n {selectionError ? (\n <div role=\"alert\" data-fs-saved-cards-error=\"\">\n {selectionError}\n </div>\n ) : null}\n {savedCards.length > 0 ? (\n // T38, Decision 1: Fieldset.Root + Fieldset.Legend, exactly what card-entry.tsx:1101-1112\n // already ships — Base UI propagates the legend's id as aria-labelledby onto the descendant\n // RadioGroup, so this one wrapper names both the fieldset and the radiogroup (measured).\n <Fieldset.Root data-fs-saved-cards-fieldset=\"\">\n <Fieldset.Legend>{resolveAuthoredCopy(legend, 'Saved cards')}</Fieldset.Legend>\n <RadioGroup value={effectiveSelectedAccountId ?? ''} onValueChange={handleValueChange}>\n {savedCards.map((card) => (\n <ListItemRefContext\n key={card.id}\n // No fallback slice: this list's own fragment selects only { id, accountId, methodId }\n // (Decision 3), so it has no complete row to fall back to. An incomplete cache slice\n // should render nothing until it resolves, not a partial, silently-wrong row.\n value={{ __typename: 'SavedCard', id: card.id, fallbackData: undefined }}\n >\n {children}\n </ListItemRefContext>\n ))}\n </RadioGroup>\n </Fieldset.Root>\n ) : null}\n </div>\n );\n}\n\nexport function SavedCardOption({ $data: $dataProp, className, children, ...props }: SavedCardOptionProps) {\n // Delivery-agnostic: the slice arrives through the DataOverrideContext seam the reactive-list-item\n // wrapper publishes (or via $data in tests); this component calls no data hook of its own.\n const slice = useDataOverride<SavedCardOptionSlice | null | undefined>($dataProp) ?? null;\n const { selectedAccountId, draft } = usePayment();\n\n if (!slice) return null;\n\n // Path-aware, not just account-aware — see resolveEffectiveSelectedAccountId's own comment: without\n // this, a row that was picked before \"use a new card\" stays marked selected forever.\n const isSelected = slice.accountId === resolveEffectiveSelectedAccountId(selectedAccountId, draft.path);\n\n return (\n <SavedCardDataContext value={slice}>\n <div\n {...props}\n className={className}\n data-fs-saved-card-option=\"\"\n {...(isSelected ? { 'data-fs-saved-card-option-selected': '' } : {})}\n {...(slice.expired ? { 'data-fs-saved-card-option-expired': '' } : {})}\n >\n <RadioRoot value={slice.accountId} disabled={slice.expired}>\n <RadioIndicator data-fs-saved-card-option-indicator=\"\" />\n <span data-fs-saved-card-option-network=\"\">{slice.network}</span>\n <span data-fs-saved-card-option-last-four=\"\">{`•••• ${slice.lastFourDigits}`}</span>\n {slice.label ? <span data-fs-saved-card-option-label=\"\">{slice.label}</span> : null}\n </RadioRoot>\n {children}\n </div>\n </SavedCardDataContext>\n );\n}\n\nconst MISSING_ACTION_ERROR: PaymentUserError = {\n code: PAYMENT_NETWORK_ERROR_CODE,\n message: 'Unable to complete the request. Please try again.',\n};\n\n/**\n * The saved-card path's own client-synthesized refusal (T27, specs/place-order-preflight-card-\n * validation.md FR-11/FR-12; Decision 8). Distinct from `card-entry`'s `INVALID_CARD_DATA` (a\n * different path, a different set of fields, and importing it here would be a matter → matter\n * dependency) and from the gateway's own codes.\n *\n * Never name this constant, or its string value, inside `sdk/payment/use-place-order.ts`: it contains\n * `security` and would fail `CARD_FIELD_PATTERN`. The pre-flight only ever handles the opaque\n * `PaymentUserError` (spec NFR-1, Risk R9).\n */\nexport const SAVED_CARD_INVALID_SECURITY_CODE = 'INVALID_SECURITY_CODE';\n\nconst INVALID_SECURITY_CODE_ERROR: PaymentUserError = {\n code: SAVED_CARD_INVALID_SECURITY_CODE,\n message: 'Enter a valid security code before placing the order.',\n};\n\n/** VTEX's own CVV upper bound across the networks this codebase's masks cover (Decision 4). */\nconst CVV_MAX_LENGTH = 4;\n\nfunction sanitizeCvv(value: string): string {\n return value.replace(/\\D/g, '').slice(0, CVV_MAX_LENGTH);\n}\n\n/**\n * Three or four digits (T27, FR-11). `sanitizeCvv` already strips non-digits and caps at\n * `CVV_MAX_LENGTH`, so the only rule left is the floor — and three-or-four is `card-entry`'s own\n * fallback for a method that declares no `cvvRegex` (its `isValidCvv`, `/^\\d{3,4}$/`). Requiredness is\n * not checked here: `SavedCardCvvInput` renders only when the row is selected and `cvvRequired` is\n * true, so its presence *is* the requiredness signal.\n */\nconst SAVED_CARD_CVV_PATTERN = /^\\d{3,4}$/;\n\n/**\n * The length at which this field's security code counts as fully typed, and the mask engages (T173,\n * specs/mask-the-cvv-only-once-it.md, FR-3). Three — the floor of `SAVED_CARD_CVV_PATTERN` above,\n * for the same reason that pattern accepts either length: a saved card carries no per-network CVV\n * length. Its slice exposes a display `network` string, not a `cvvMask`, so nothing here tells a\n * 3-digit network apart from Amex's 4 the way `CardEntry` can, and inferring one from the display\n * name would hardcode a network table this package deliberately does not own.\n *\n * Masking at the floor is the side to err on: a complete code is never left legible on either kind\n * of card, and a 4-digit code is simply already masked when its last digit arrives.\n */\nconst SAVED_CARD_CVV_COMPLETE_LENGTH = 3;\n\ntype SavedCardCvvInputProps = ComponentPropsWithRef<'input'> & {\n /** `SavedCard.accountId` from this row's own resolved slice — never `usePayment().draft`. */\n accountId: string;\n /**\n * `SavedCard.bin` from this row's own resolved slice — the one this row is actually being paid\n * with. Deliberately **not** `usePayment().draft.bin`: that field can be populated on the\n * saved-card path too (e.g. Installments calling `setDraftBin` for its own BIN-keyed lookup;\n * `use-payment-method-selection.ts` does not clear it on this transition), so it can carry a\n * different card's BIN, or a stale one, at the moment this continuation runs. Forwarding it here\n * instead of the row's own field would be the same class of bug Decision 2 already closed for the\n * `selectMethod` dispatch — undetectable by type-checking, since both are plain `string`.\n */\n bin: string;\n /** Accessible name applied verbatim as `aria-label` — see `SavedCardCvvFieldProps`'s own doc comment. */\n securityCodeLabel: string;\n /** Helper copy rendered as a sibling `<p>` — see `SavedCardCvvFieldProps`'s own doc comment. Threaded\n * through exactly like `securityCodeLabel`. An empty string renders nothing. */\n securityCodeDescription: string;\n $actions?: ComponentActions;\n};\n\n/** Merges a caller-forwarded `aria-describedby` (from `...props`) with the description `<p>`'s own\n * id, rather than clobbering it — the accessibility wiring `Field.Root` would otherwise provide for\n * free (saved-cards.md Decision 3). */\nfunction mergeDescribedBy(existing: string | undefined, id: string): string {\n return existing ? `${existing} ${id}` : id;\n}\n\n/**\n * The stateful, registering half of the CVV field — split out from `SavedCardCvvField` so this row's\n * conditional visibility never skips a hook call (spec Decision 5). This component mounts wholesale\n * when its row is selected and requires a CVV, and unmounts wholesale otherwise, so its own hooks\n * always run the same number of times across its own lifetime; only its *presence* is conditional,\n * decided by its caller.\n */\nfunction SavedCardCvvInput({\n accountId,\n bin,\n securityCodeLabel,\n securityCodeDescription,\n $actions,\n ref: forwardedRef,\n ...props\n}: SavedCardCvvInputProps) {\n const [cvv, setCvv] = useState('');\n const inputRef = useRef<HTMLInputElement>(null);\n const sendPayments = $actions?.payment?.sendPayments;\n const descriptionId = useId();\n\n // Attaches both this component's own ref (needed to focus the field on refusal, in checkCvv below)\n // and whatever ref a caller already forwarded onto SavedCardCvvField/SavedCardCvvInput — `props`\n // spreads onto the element below and `SavedCardCvvInputProps` is `ComponentPropsWithRef<'input'>`, so\n // writing `ref={inputRef}` after the spread would silently drop a caller-forwarded ref instead.\n const setInputRef = useCallback(\n (node: HTMLInputElement | null) => {\n inputRef.current = node;\n\n if (typeof forwardedRef !== 'function') {\n if (forwardedRef) forwardedRef.current = node;\n return;\n }\n\n // React 19: a ref callback may itself return a cleanup function instead of being re-invoked\n // with `null` on detach. Preserve whichever form the caller used — never call a React-19\n // cleanup-style ref with `null`, a value that form is documented never to receive (fix pass,\n // review F2). A caller using the legacy form (no returned cleanup) keeps today's behaviour:\n // this callback returns nothing either, so React re-invokes it with `null` on detach, which\n // falls into this same branch and calls `forwardedRef(null)`, exactly as before.\n const cleanup = forwardedRef(node);\n if (typeof cleanup === 'function') {\n return () => {\n inputRef.current = null;\n cleanup();\n };\n }\n return;\n },\n [forwardedRef],\n );\n\n // The saved-card check (T27, FR-11/FR-12; Decision 8). Three-or-four digits — see\n // SAVED_CARD_CVV_PATTERN's own doc comment for why nothing narrower is checkable and nothing wider\n // is safe. The only field on this path, so there is no ordering rule and no inline error node to\n // fill (FR-12) — the message reaches the shopper through PlaceOrder's existing role=\"alert\".\n const checkCvv = useCallback<PaymentPreflightCheck>(() => {\n if (SAVED_CARD_CVV_PATTERN.test(cvv)) return [];\n inputRef.current?.focus();\n return [INVALID_SECURITY_CODE_ERROR];\n }, [cvv]);\n\n const submit = useCallback<PaymentSubmission>(\n async (transaction) => {\n // Same function the pre-flight calls (FR-3, applied to this path) — the two moments cannot\n // diverge here either.\n const refusals = checkCvv();\n if (refusals.length > 0) return refusals;\n if (!sendPayments) return [MISSING_ACTION_ERROR];\n // `bin` here is always the `accountId`/`bin` pair from this row's own props — see their doc\n // comments above for why `usePayment().draft.bin` must never substitute for either.\n return sendPayments({ transaction, card: { kind: 'saved', accountId, bin, securityCode: cvv } });\n },\n [checkCvv, sendPayments, accountId, bin, cvv],\n );\n\n useRegisterPaymentSubmission('saved-card', submit, checkCvv);\n\n // Merges with a caller-forwarded aria-describedby rather than clobbering it — mirrors the ref\n // merge above (setInputRef) for the same \"don't drop what a caller already passed\" reason.\n const ariaDescribedBy = securityCodeDescription\n ? mergeDescribedBy(props['aria-describedby'], descriptionId)\n : props['aria-describedby'];\n\n // T101 — the mask merges *over* a caller-forwarded style, inverted from this repo's usual\n // `...props.style`-last idiom (cart-item.tsx, product-list.tsx): deliberately, so a caller can\n // never remove the security-code mask. See security-code-mask.ts.\n //\n // T173 — that merge now happens from the keystroke that completes the code, not from the first\n // one, so a shopper can read back what they typed while they type it. Below the trigger the\n // caller's own style passes through untouched, which is the same thing it did before this field\n // ever had a mask; the guarantee above is about what a caller cannot do once the code is complete.\n const inputStyle = isSecurityCodeComplete(cvv, SAVED_CARD_CVV_COMPLETE_LENGTH)\n ? { ...props.style, ...SECURITY_CODE_MASK_STYLE }\n : props.style;\n\n return (\n <>\n <input\n {...props}\n ref={setInputRef}\n type=\"text\"\n inputMode=\"numeric\"\n autoComplete=\"off\"\n aria-label={securityCodeLabel}\n aria-describedby={ariaDescribedBy}\n value={cvv}\n onChange={(event) => setCvv(sanitizeCvv(event.target.value))}\n data-fs-saved-card-cvv-field=\"\"\n style={inputStyle}\n />\n {securityCodeDescription ? (\n <p id={descriptionId} data-fs-saved-card-cvv-description=\"\">\n {securityCodeDescription}\n </p>\n ) : null}\n </>\n );\n}\n\nexport function SavedCardCvvField({\n $actions: $actionsProp,\n securityCodeLabel = 'Security code',\n securityCodeDescription = 'The 3-4 digit number on your card',\n className,\n ...props\n}: SavedCardCvvFieldProps) {\n const $actions = useActionsOverride($actionsProp);\n const { accountId, bin, cvvRequired } = useSavedCardData();\n const { selectedAccountId, draft } = usePayment();\n\n // Path-aware, matching SavedCardOption's own isSelected — otherwise this field stays mounted and\n // registered after \"use a new card\", with selectedAccountId still equal to this row's own accountId.\n if (accountId !== resolveEffectiveSelectedAccountId(selectedAccountId, draft.path) || !cvvRequired) {\n return null;\n }\n\n return (\n <SavedCardCvvInput\n {...props}\n className={className}\n accountId={accountId}\n bin={bin}\n securityCodeLabel={securityCodeLabel}\n securityCodeDescription={securityCodeDescription}\n $actions={$actions}\n />\n );\n}\n"],"mappings":"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"}
@@ -11,7 +11,8 @@ const countryDescriptors = {
11
11
  fields: [
12
12
  {
13
13
  name: "recipientName",
14
- label: "nome do destinatário"
14
+ label: "nome do destinatário",
15
+ required: true
15
16
  },
16
17
  {
17
18
  name: "street",
@@ -39,41 +40,50 @@ const countryDescriptors = {
39
40
  },
40
41
  {
41
42
  name: "postalCode",
42
- label: "cep"
43
+ label: "cep",
44
+ rule: { pattern: {
45
+ value: /^\d{5}-?\d{3}$/,
46
+ message: "cep inválido"
47
+ } }
43
48
  }
44
49
  ]
45
50
  },
46
51
  USA: {
47
52
  iso: "USA",
48
- label: "Estados Unidos",
53
+ label: "United States",
49
54
  fields: [
50
55
  {
51
56
  name: "recipientName",
52
- label: "nome do destinatário"
57
+ label: "recipient name",
58
+ required: true
53
59
  },
54
60
  {
55
61
  name: "street",
56
- label: "rua"
62
+ label: "street"
57
63
  },
58
64
  {
59
65
  name: "number",
60
- label: "número"
66
+ label: "number"
61
67
  },
62
68
  {
63
69
  name: "complement",
64
- label: "complemento (opcional)"
70
+ label: "apartment, suite, etc. (optional)"
65
71
  },
66
72
  {
67
73
  name: "city",
68
- label: "cidade"
74
+ label: "city"
69
75
  },
70
76
  {
71
77
  name: "state",
72
- label: "estado"
78
+ label: "state"
73
79
  },
74
80
  {
75
81
  name: "postalCode",
76
- label: "código postal"
82
+ label: "zip code",
83
+ rule: { pattern: {
84
+ value: /^\d{5}(-?\d{4})?$/,
85
+ message: "invalid zip code"
86
+ } }
77
87
  }
78
88
  ]
79
89
  }