@vtex/faststore 0.0.10 → 0.0.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +42 -0
- package/README.md +0 -1
- package/dist/actions.json +6 -1
- package/dist/catalog.json +789 -1701
- package/dist/cli/index.d.ts +66 -28
- package/dist/cli/index.js +7371 -512
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/server-runtime.js +41 -60
- package/dist/cli/server-runtime.js.map +1 -1
- package/dist/client.d.ts +3 -3
- package/dist/client.js +2 -2
- package/dist/components.d.ts +63 -57
- package/dist/components.js +60 -54
- package/dist/content-types.json +5785 -754
- package/dist/core/dist/define-component.d.ts +1 -1
- package/dist/core/dist/define-content-type.d.ts +50 -2
- package/dist/core/dist/define-content-type.js +107 -3
- package/dist/core/dist/define-content-type.js.map +1 -1
- package/dist/core/dist/index.d.ts +2 -2
- package/dist/docs/access-code-field.md +1 -1
- package/dist/docs/access-code-sign-in.md +126 -77
- package/dist/docs/account-address-book.md +210 -95
- package/dist/docs/account-menu.md +14 -15
- package/dist/docs/account-saved-cards.md +44 -39
- package/dist/docs/alert-dialog.md +3 -1
- package/dist/docs/avatar.md +4 -2
- package/dist/docs/billing-address.md +99 -30
- package/dist/docs/binding-switcher.md +5 -2
- package/dist/docs/buy-button.md +7 -8
- package/dist/docs/card-entry.md +39 -26
- package/dist/docs/card-summary.md +18 -8
- package/dist/docs/cardholder-document.md +5 -3
- package/dist/docs/carousel.md +5 -4
- package/dist/docs/cart-destination.md +39 -26
- package/dist/docs/cart-item.md +29 -14
- package/dist/docs/cart-totalizer.md +7 -5
- package/dist/docs/cart.md +135 -41
- package/dist/docs/challenge-return.md +135 -0
- package/dist/docs/client-preferences-fieldset.md +10 -5
- package/dist/docs/client-preferences.md +31 -8
- package/dist/docs/client-profile-fieldset.md +20 -6
- package/dist/docs/client-profile.md +12 -3
- package/dist/docs/copy-text.md +9 -7
- package/dist/docs/document-field.md +1 -1
- package/dist/docs/email-field.md +2 -1
- package/dist/docs/email-identification.md +12 -2
- package/dist/docs/footer-copyright.md +4 -2
- package/dist/docs/form.md +54 -18
- package/dist/docs/free-shipping-progress.md +6 -5
- package/dist/docs/identity-edit.md +8 -2
- package/dist/docs/identity-summary.md +18 -5
- package/dist/docs/installments.md +46 -32
- package/dist/docs/my-account-profile.md +16 -11
- package/dist/docs/navigation-link.md +5 -3
- package/dist/docs/order-cancel.md +3 -1
- package/dist/docs/order-delivery-address.md +7 -5
- package/dist/docs/order-delivery-group.md +1 -3
- package/dist/docs/order-details-back.md +1 -1
- package/dist/docs/order-details-header.md +1 -4
- package/dist/docs/order-invoice-download.md +60 -0
- package/dist/docs/order-list-row.md +1 -1
- package/dist/docs/order-list-search.md +13 -20
- package/dist/docs/order-list-views.md +11 -17
- package/dist/docs/order-list.md +12 -5
- package/dist/docs/order-payment.md +9 -7
- package/dist/docs/order-placed-date.md +1 -1
- package/dist/docs/order-placed-payment-summary.md +28 -15
- package/dist/docs/order-placed.md +49 -14
- package/dist/docs/order-progress.md +1 -1
- package/dist/docs/order-status-badge.md +20 -9
- package/dist/docs/order-summary.md +2 -2
- package/dist/docs/order-tracking-link.md +58 -0
- package/dist/docs/payment-method-badges.md +30 -4
- package/dist/docs/payment-methods.md +1 -1
- package/dist/docs/payment.md +36 -17
- package/dist/docs/phone-field.md +4 -4
- package/dist/docs/place-order.md +84 -36
- package/dist/docs/postal-code-field.md +3 -1
- package/dist/docs/pricing.md +21 -23
- package/dist/docs/product-gallery.md +19 -20
- package/dist/docs/product-information.md +13 -9
- package/dist/docs/product-list.md +7 -6
- package/dist/docs/product-shelf.md +0 -1
- package/dist/docs/promotion.md +4 -2
- package/dist/docs/region.md +23 -15
- package/dist/docs/save-card.md +7 -6
- package/dist/docs/saved-cards.md +65 -36
- package/dist/docs/shipping-address.md +57 -21
- package/dist/docs/shipping-simulator.md +53 -15
- package/dist/docs/sign-in-disclosure.md +176 -0
- package/dist/docs/sign-in-form.md +78 -47
- package/dist/docs/sign-in-redirect.md +4 -2
- package/dist/docs/sign-in.md +173 -0
- package/dist/docs/toast.md +15 -4
- package/dist/docs/variant-selector.md +15 -11
- package/dist/graphql/types.d.ts +4 -14
- package/dist/index.d.ts +2 -2
- package/dist/internal/client.d.ts +1 -3
- package/dist/internal/client.js +1 -1
- package/dist/internal/server.d.ts +7 -4
- package/dist/internal/server.js +7 -4
- package/dist/matter/access-code-sign-in/access-code-sign-in.d.ts +250 -66
- package/dist/matter/access-code-sign-in/access-code-sign-in.js +459 -188
- package/dist/matter/access-code-sign-in/access-code-sign-in.js.map +1 -1
- package/dist/matter/account-address-book/account-address-book-delete.d.ts +78 -40
- package/dist/matter/account-address-book/account-address-book-delete.js +86 -50
- package/dist/matter/account-address-book/account-address-book-delete.js.map +1 -1
- package/dist/matter/account-address-book/account-address-book-deliverability.d.ts +21 -4
- package/dist/matter/account-address-book/account-address-book-deliverability.js +45 -17
- package/dist/matter/account-address-book/account-address-book-deliverability.js.map +1 -1
- package/dist/matter/account-address-book/account-address-book-form-parts.d.ts +74 -19
- package/dist/matter/account-address-book/account-address-book-form-parts.js +77 -26
- package/dist/matter/account-address-book/account-address-book-form-parts.js.map +1 -1
- package/dist/matter/account-address-book/account-address-book-profile-notice.d.ts +27 -6
- package/dist/matter/account-address-book/account-address-book-profile-notice.js +55 -28
- package/dist/matter/account-address-book/account-address-book-profile-notice.js.map +1 -1
- package/dist/matter/account-address-book/account-address-book.d.ts +67 -14
- package/dist/matter/account-address-book/account-address-book.js +74 -33
- package/dist/matter/account-address-book/account-address-book.js.map +1 -1
- package/dist/matter/account-address-book/account-address-country-switch.js +3 -3
- package/dist/matter/account-address-book/account-address-country-switch.js.map +1 -1
- package/dist/matter/account-address-book/account-address-country.d.ts +66 -20
- package/dist/matter/account-address-book/account-address-country.js +83 -49
- package/dist/matter/account-address-book/account-address-country.js.map +1 -1
- package/dist/matter/account-address-book/account-address-form-context.d.ts +8 -4
- package/dist/matter/account-address-book/account-address-form-context.js.map +1 -1
- package/dist/matter/account-address-book/account-address-form.d.ts +468 -45
- package/dist/matter/account-address-book/account-address-form.js +518 -155
- package/dist/matter/account-address-book/account-address-form.js.map +1 -1
- package/dist/matter/account-address-book/account-address-record.d.ts +27 -6
- package/dist/matter/account-address-book/account-address-record.js +32 -17
- package/dist/matter/account-address-book/account-address-record.js.map +1 -1
- package/dist/matter/account-menu/account-menu.d.ts +34 -17
- package/dist/matter/account-menu/account-menu.js +46 -54
- package/dist/matter/account-menu/account-menu.js.map +1 -1
- package/dist/matter/account-menu-identity/account-menu-identity.js +1 -1
- package/dist/matter/account-saved-cards/account-saved-cards.d.ts +108 -28
- package/dist/matter/account-saved-cards/account-saved-cards.js +177 -122
- package/dist/matter/account-saved-cards/account-saved-cards.js.map +1 -1
- package/dist/matter/billing-address/billing-address.d.ts +156 -74
- package/dist/matter/billing-address/billing-address.js +227 -156
- package/dist/matter/billing-address/billing-address.js.map +1 -1
- package/dist/matter/breadcrumb/breadcrumb.d.ts +21 -3
- package/dist/matter/breadcrumb/breadcrumb.js +24 -9
- package/dist/matter/breadcrumb/breadcrumb.js.map +1 -1
- package/dist/matter/buy-button/buy-button.d.ts +8 -11
- package/dist/matter/buy-button/buy-button.js +10 -19
- package/dist/matter/buy-button/buy-button.js.map +1 -1
- package/dist/matter/card-entry/card-capture.js +12 -4
- package/dist/matter/card-entry/card-capture.js.map +1 -1
- package/dist/matter/card-entry/card-entry.d.ts +123 -32
- package/dist/matter/card-entry/card-entry.js +348 -221
- package/dist/matter/card-entry/card-entry.js.map +1 -1
- package/dist/matter/cart/cart-namespace.d.ts +7 -1
- package/dist/matter/cart/cart-namespace.js +7 -1
- package/dist/matter/cart/cart-namespace.js.map +1 -1
- package/dist/matter/cart/cart.d.ts +402 -61
- package/dist/matter/cart/cart.js +624 -153
- package/dist/matter/cart/cart.js.map +1 -1
- package/dist/matter/cart-destination/cart-destination.d.ts +30 -19
- package/dist/matter/cart-destination/cart-destination.js +160 -70
- package/dist/matter/cart-destination/cart-destination.js.map +1 -1
- package/dist/matter/cart-item/cart-item.d.ts +144 -17
- package/dist/matter/cart-item/cart-item.js +166 -49
- package/dist/matter/cart-item/cart-item.js.map +1 -1
- package/dist/matter/cart-item-compositions/cart-item-compositions.js +1 -1
- package/dist/matter/cart-item-compositions/cart-item-compositions.js.map +1 -1
- package/dist/matter/cart-item-customizations/cart-item-customizations.js +1 -1
- package/dist/matter/cart-item-customizations/cart-item-customizations.js.map +1 -1
- package/dist/matter/cart-item-discounts/cart-item-discounts.js +1 -1
- package/dist/matter/cart-item-discounts/cart-item-discounts.js.map +1 -1
- package/dist/matter/cart-item-seller-info/cart-item-seller-info.js +1 -1
- package/dist/matter/cart-item-seller-info/cart-item-seller-info.js.map +1 -1
- package/dist/matter/cart-item-variations/cart-item-variations.js +1 -1
- package/dist/matter/cart-item-variations/cart-item-variations.js.map +1 -1
- package/dist/matter/cart-totalizer/cart-totalizer.d.ts +43 -9
- package/dist/matter/cart-totalizer/cart-totalizer.js +46 -17
- package/dist/matter/cart-totalizer/cart-totalizer.js.map +1 -1
- package/dist/matter/challenge-return/challenge-return.d.ts +62 -0
- package/dist/matter/challenge-return/challenge-return.js +241 -0
- package/dist/matter/challenge-return/challenge-return.js.map +1 -0
- package/dist/matter/client-preferences/client-preferences-fieldset.d.ts +86 -17
- package/dist/matter/client-preferences/client-preferences-fieldset.js +126 -54
- package/dist/matter/client-preferences/client-preferences-fieldset.js.map +1 -1
- package/dist/matter/client-preferences/client-preferences.d.ts +50 -7
- package/dist/matter/client-preferences/client-preferences.js +84 -15
- package/dist/matter/client-preferences/client-preferences.js.map +1 -1
- package/dist/matter/client-profile/client-profile.d.ts +44 -9
- package/dist/matter/client-profile/client-profile.js +64 -24
- package/dist/matter/client-profile/client-profile.js.map +1 -1
- package/dist/matter/client-profile-form/client-profile-fieldset.d.ts +119 -22
- package/dist/matter/client-profile-form/client-profile-fieldset.js +161 -73
- package/dist/matter/client-profile-form/client-profile-fieldset.js.map +1 -1
- package/dist/matter/delivery-options/delivery-options-accordion.d.ts +49 -4
- package/dist/matter/delivery-options/delivery-options-accordion.js +67 -14
- package/dist/matter/delivery-options/delivery-options-accordion.js.map +1 -1
- package/dist/matter/delivery-options/delivery-options-controller.d.ts +69 -9
- package/dist/matter/delivery-options/delivery-options-controller.js +80 -14
- package/dist/matter/delivery-options/delivery-options-controller.js.map +1 -1
- package/dist/matter/delivery-options/delivery-options.d.ts +37 -2
- package/dist/matter/delivery-options/delivery-options.js +75 -12
- package/dist/matter/delivery-options/delivery-options.js.map +1 -1
- package/dist/matter/delivery-options/use-delivery-option-selection.d.ts +1 -0
- package/dist/matter/delivery-options/use-delivery-option-selection.js +1 -1
- package/dist/matter/delivery-options/use-delivery-option-selection.js.map +1 -1
- package/dist/matter/email-identification/email-identification.d.ts +25 -3
- package/dist/matter/email-identification/email-identification.js +49 -12
- package/dist/matter/email-identification/email-identification.js.map +1 -1
- package/dist/matter/free-shipping-progress/free-shipping-progress.d.ts +79 -7
- package/dist/matter/free-shipping-progress/free-shipping-progress.js +77 -20
- package/dist/matter/free-shipping-progress/free-shipping-progress.js.map +1 -1
- package/dist/matter/identity-edit/identity-edit.d.ts +52 -10
- package/dist/matter/identity-edit/identity-edit.js +47 -19
- package/dist/matter/identity-edit/identity-edit.js.map +1 -1
- package/dist/matter/identity-summary/identity-summary.d.ts +76 -22
- package/dist/matter/identity-summary/identity-summary.js +95 -45
- package/dist/matter/identity-summary/identity-summary.js.map +1 -1
- package/dist/matter/installments/installments-select.d.ts +98 -16
- package/dist/matter/installments/installments-select.js +156 -48
- package/dist/matter/installments/installments-select.js.map +1 -1
- package/dist/matter/installments/installments.d.ts +96 -16
- package/dist/matter/installments/installments.js +115 -40
- package/dist/matter/installments/installments.js.map +1 -1
- package/dist/matter/installments/use-installments-selection.d.ts +1 -0
- package/dist/matter/installments/use-installments-selection.js +8 -3
- package/dist/matter/installments/use-installments-selection.js.map +1 -1
- package/dist/matter/my-account-profile/my-account-profile.d.ts +358 -103
- package/dist/matter/my-account-profile/my-account-profile.js +370 -174
- package/dist/matter/my-account-profile/my-account-profile.js.map +1 -1
- package/dist/matter/order-cancel/order-cancel.d.ts +65 -19
- package/dist/matter/order-cancel/order-cancel.js +118 -51
- package/dist/matter/order-cancel/order-cancel.js.map +1 -1
- package/dist/matter/order-delivery-address/order-delivery-address.d.ts +8 -18
- package/dist/matter/order-delivery-address/order-delivery-address.js +14 -23
- package/dist/matter/order-delivery-address/order-delivery-address.js.map +1 -1
- package/dist/matter/order-delivery-group/order-delivery-group.d.ts +40 -24
- package/dist/matter/order-delivery-group/order-delivery-group.js +64 -74
- package/dist/matter/order-delivery-group/order-delivery-group.js.map +1 -1
- package/dist/matter/order-details-header/order-details-header.d.ts +32 -15
- package/dist/matter/order-details-header/order-details-header.js +66 -58
- package/dist/matter/order-details-header/order-details-header.js.map +1 -1
- package/dist/matter/order-invoice-download/order-invoice-download.d.ts +78 -0
- package/dist/matter/order-invoice-download/order-invoice-download.js +173 -0
- package/dist/matter/order-invoice-download/order-invoice-download.js.map +1 -0
- package/dist/matter/order-list/order-list.d.ts +164 -19
- package/dist/matter/order-list/order-list.js +229 -87
- package/dist/matter/order-list/order-list.js.map +1 -1
- package/dist/matter/order-list-row/order-list-row.d.ts +6 -1
- package/dist/matter/order-list-row/order-list-row.js +3 -2
- package/dist/matter/order-list-row/order-list-row.js.map +1 -1
- package/dist/matter/order-list-search/order-list-search.d.ts +18 -26
- package/dist/matter/order-list-search/order-list-search.js +30 -69
- package/dist/matter/order-list-search/order-list-search.js.map +1 -1
- package/dist/matter/order-list-views/order-list-views.d.ts +15 -22
- package/dist/matter/order-list-views/order-list-views.js +22 -68
- package/dist/matter/order-list-views/order-list-views.js.map +1 -1
- package/dist/matter/order-payment/order-payment.d.ts +33 -10
- package/dist/matter/order-payment/order-payment.js +44 -39
- package/dist/matter/order-payment/order-payment.js.map +1 -1
- package/dist/matter/order-placed/order-placed-deliveries.d.ts +144 -12
- package/dist/matter/order-placed/order-placed-deliveries.js +115 -40
- package/dist/matter/order-placed/order-placed-deliveries.js.map +1 -1
- package/dist/matter/order-placed/order-placed.d.ts +198 -31
- package/dist/matter/order-placed/order-placed.js +230 -139
- package/dist/matter/order-placed/order-placed.js.map +1 -1
- package/dist/matter/order-placed-date/order-placed-date.d.ts +5 -8
- package/dist/matter/order-placed-date/order-placed-date.js +7 -8
- package/dist/matter/order-placed-date/order-placed-date.js.map +1 -1
- package/dist/matter/order-placed-payment-summary/order-placed-payment-summary.d.ts +90 -17
- package/dist/matter/order-placed-payment-summary/order-placed-payment-summary.js +93 -117
- package/dist/matter/order-placed-payment-summary/order-placed-payment-summary.js.map +1 -1
- package/dist/matter/order-progress/order-progress.d.ts +19 -8
- package/dist/matter/order-progress/order-progress.js +37 -45
- package/dist/matter/order-progress/order-progress.js.map +1 -1
- package/dist/matter/order-summary/order-summary.d.ts +19 -9
- package/dist/matter/order-summary/order-summary.js +38 -44
- package/dist/matter/order-summary/order-summary.js.map +1 -1
- package/dist/matter/order-tracking-link/order-tracking-link.d.ts +43 -0
- package/dist/matter/order-tracking-link/order-tracking-link.js +107 -0
- package/dist/matter/order-tracking-link/order-tracking-link.js.map +1 -0
- package/dist/matter/payment/payment-card-capture.js +17 -10
- package/dist/matter/payment/payment-card-capture.js.map +1 -1
- package/dist/matter/payment/payment-controller.d.ts +4 -2
- package/dist/matter/payment/payment-controller.js +30 -9
- package/dist/matter/payment/payment-controller.js.map +1 -1
- package/dist/matter/payment/payment-method-badges.js +1 -1
- package/dist/matter/payment/payment.d.ts +69 -19
- package/dist/matter/payment/payment.js +101 -35
- package/dist/matter/payment/payment.js.map +1 -1
- package/dist/matter/payment/use-payment-method-selection.d.ts +25 -4
- package/dist/matter/payment/use-payment-method-selection.js +94 -32
- package/dist/matter/payment/use-payment-method-selection.js.map +1 -1
- package/dist/matter/payment/use-payment-submit-gate.d.ts +14 -3
- package/dist/matter/payment/use-payment-submit-gate.js +47 -8
- package/dist/matter/payment/use-payment-submit-gate.js.map +1 -1
- package/dist/matter/place-order/place-order.d.ts +171 -57
- package/dist/matter/place-order/place-order.js +376 -185
- package/dist/matter/place-order/place-order.js.map +1 -1
- package/dist/matter/pricing/pricing.d.ts +152 -13
- package/dist/matter/pricing/pricing.js +153 -52
- package/dist/matter/pricing/pricing.js.map +1 -1
- package/dist/matter/product-information/product-information.d.ts +27 -4
- package/dist/matter/product-information/product-information.js +32 -15
- package/dist/matter/product-information/product-information.js.map +1 -1
- package/dist/matter/product-list/product-list.d.ts +24 -6
- package/dist/matter/product-list/product-list.js +32 -26
- package/dist/matter/product-list/product-list.js.map +1 -1
- package/dist/matter/product-shelf/product-shelf.d.ts +1 -3
- package/dist/matter/product-shelf/product-shelf.js +2 -15
- package/dist/matter/product-shelf/product-shelf.js.map +1 -1
- package/dist/matter/region/region.d.ts +33 -16
- package/dist/matter/region/region.js +59 -52
- package/dist/matter/region/region.js.map +1 -1
- package/dist/matter/save-card/save-card.d.ts +43 -8
- package/dist/matter/save-card/save-card.js +61 -23
- package/dist/matter/save-card/save-card.js.map +1 -1
- package/dist/matter/saved-cards/saved-cards.d.ts +113 -21
- package/dist/matter/saved-cards/saved-cards.js +349 -77
- package/dist/matter/saved-cards/saved-cards.js.map +1 -1
- package/dist/matter/shipping-address/address-payload.js +25 -0
- package/dist/matter/shipping-address/address-payload.js.map +1 -0
- package/dist/matter/shipping-address/country-descriptors.js +71 -71
- package/dist/matter/shipping-address/country-descriptors.js.map +1 -1
- package/dist/matter/shipping-address/library-country-descriptors.js +136 -0
- package/dist/matter/shipping-address/library-country-descriptors.js.map +1 -0
- package/dist/matter/shipping-address/shipping-address.d.ts +312 -45
- package/dist/matter/shipping-address/shipping-address.js +443 -188
- package/dist/matter/shipping-address/shipping-address.js.map +1 -1
- package/dist/matter/shipping-simulator/shipping-simulator.d.ts +250 -24
- package/dist/matter/shipping-simulator/shipping-simulator.js +257 -51
- package/dist/matter/shipping-simulator/shipping-simulator.js.map +1 -1
- package/dist/matter/sign-in/sign-in.d.ts +108 -0
- package/dist/matter/sign-in/sign-in.js +285 -0
- package/dist/matter/sign-in/sign-in.js.map +1 -0
- package/dist/matter/sign-in-disclosure/sign-in-disclosure.d.ts +81 -0
- package/dist/matter/sign-in-disclosure/sign-in-disclosure.js +232 -0
- package/dist/matter/sign-in-disclosure/sign-in-disclosure.js.map +1 -0
- package/dist/matter/sign-in-form/sign-in-form.d.ts +93 -62
- package/dist/matter/sign-in-form/sign-in-form.js +134 -144
- package/dist/matter/sign-in-form/sign-in-form.js.map +1 -1
- package/dist/particles/binding-switcher/binding-switcher.d.ts +29 -8
- package/dist/particles/binding-switcher/binding-switcher.js +37 -26
- package/dist/particles/binding-switcher/binding-switcher.js.map +1 -1
- package/dist/particles/card-summary/card-summary.d.ts +38 -8
- package/dist/particles/card-summary/card-summary.js +55 -22
- package/dist/particles/card-summary/card-summary.js.map +1 -1
- package/dist/particles/carousel/carousel.d.ts +99 -11
- package/dist/particles/carousel/carousel.js +103 -35
- package/dist/particles/carousel/carousel.js.map +1 -1
- package/dist/particles/copy-text/copy-text.js +5 -15
- package/dist/particles/copy-text/copy-text.js.map +1 -1
- package/dist/particles/dialog/dialog.d.ts +14 -6
- package/dist/particles/dialog/dialog.js +6 -3
- package/dist/particles/dialog/dialog.js.map +1 -1
- package/dist/particles/drawer/drawer.js +1 -1
- package/dist/particles/drawer/drawer.js.map +1 -1
- package/dist/particles/email-field/email-field.d.ts +2 -1
- package/dist/particles/email-field/email-field.js +2 -1
- package/dist/particles/email-field/email-field.js.map +1 -1
- package/dist/particles/footer-link-group/footer-link-group.js +1 -0
- package/dist/particles/footer-link-group/footer-link-group.js.map +1 -1
- package/dist/particles/form/form.d.ts +85 -13
- package/dist/particles/form/form.js +91 -42
- package/dist/particles/form/form.js.map +1 -1
- package/dist/particles/gallery/gallery.d.ts +129 -12
- package/dist/particles/gallery/gallery.js +121 -34
- package/dist/particles/gallery/gallery.js.map +1 -1
- package/dist/particles/order-details-back/order-details-back.d.ts +5 -3
- package/dist/particles/order-details-back/order-details-back.js +8 -10
- package/dist/particles/order-details-back/order-details-back.js.map +1 -1
- package/dist/particles/order-status-badge/order-status-badge.d.ts +3 -3
- package/dist/particles/order-status-badge/order-status-badge.js +4 -20
- package/dist/particles/order-status-badge/order-status-badge.js.map +1 -1
- package/dist/particles/password-field/password-field.d.ts +1 -1
- package/dist/particles/password-field/password-field.js +2 -4
- package/dist/particles/password-field/password-field.js.map +1 -1
- package/dist/particles/phone-field/phone-field.d.ts +4 -4
- package/dist/particles/phone-field/phone-field.js +8 -11
- package/dist/particles/phone-field/phone-field.js.map +1 -1
- package/dist/particles/spinner/spinner.d.ts +24 -4
- package/dist/particles/spinner/spinner.js +25 -9
- package/dist/particles/spinner/spinner.js.map +1 -1
- package/dist/particles/switch/switch.d.ts +4 -1
- package/dist/particles/switch/switch.js +3 -1
- package/dist/particles/switch/switch.js.map +1 -1
- package/dist/particles/toast/toast.d.ts +29 -3
- package/dist/particles/toast/toast.js +34 -10
- package/dist/particles/toast/toast.js.map +1 -1
- package/dist/schema.graphql +164 -56
- package/dist/sdk/address/address-fields.js +4 -4
- package/dist/sdk/address/address-fields.js.map +1 -1
- package/dist/sdk/address/constants.js +1 -6
- package/dist/sdk/address/constants.js.map +1 -1
- package/dist/sdk/address/messages.js +44 -44
- package/dist/sdk/address/messages.js.map +1 -1
- package/dist/sdk/address/postal-code.js +15 -2
- package/dist/sdk/address/postal-code.js.map +1 -1
- package/dist/sdk/address/types.d.ts +5 -3
- package/dist/sdk/address/validate-address.js +3 -3
- package/dist/sdk/address/validate-address.js.map +1 -1
- package/dist/sdk/analytics/use-analytics-actions.d.ts +1 -1
- package/dist/sdk/analytics/use-analytics-actions.js.map +1 -1
- package/dist/sdk/cart/use-cart-actions.js.map +1 -1
- package/dist/sdk/data/index.js +2 -2
- package/dist/sdk/data/reactive-list-item-data-provider.js +2 -2
- package/dist/sdk/generated/gql.js +1 -1
- package/dist/sdk/generated/gql.js.map +1 -1
- package/dist/sdk/generated/graphql.d.ts +20 -5
- package/dist/sdk/generated/graphql.js +6 -0
- package/dist/sdk/generated/graphql.js.map +1 -1
- package/dist/sdk/i18n/format-error-code.js +23 -0
- package/dist/sdk/i18n/format-error-code.js.map +1 -0
- package/dist/sdk/i18n/message-formatter.d.ts +22 -0
- package/dist/sdk/i18n/message-formatter.js +140 -0
- package/dist/sdk/i18n/message-formatter.js.map +1 -0
- package/dist/sdk/i18n/use-message-formatter.d.ts +8 -0
- package/dist/sdk/i18n/use-message-formatter.js +18 -0
- package/dist/sdk/i18n/use-message-formatter.js.map +1 -0
- package/dist/sdk/identity/use-identity-actions.js.map +1 -1
- package/dist/sdk/navigation/redirect.d.ts +2 -0
- package/dist/sdk/navigation/redirect.js +19 -1
- package/dist/sdk/navigation/redirect.js.map +1 -1
- package/dist/sdk/navigation/types.d.ts +13 -0
- package/dist/sdk/payment/bin-format.js +20 -0
- package/dist/sdk/payment/bin-format.js.map +1 -0
- package/dist/sdk/payment/challenge-return.js +37 -0
- package/dist/sdk/payment/challenge-return.js.map +1 -0
- package/dist/sdk/payment/confirmation-last-four.js +39 -0
- package/dist/sdk/payment/confirmation-last-four.js.map +1 -0
- package/dist/sdk/payment/gateway.d.ts +1 -1
- package/dist/sdk/payment/gateway.js +8 -2
- package/dist/sdk/payment/gateway.js.map +1 -1
- package/dist/sdk/payment/operations.d.ts +1 -0
- package/dist/sdk/payment/operations.js +28 -6
- package/dist/sdk/payment/operations.js.map +1 -1
- package/dist/sdk/payment/resumed-purchase.d.ts +1 -0
- package/dist/sdk/payment/resumed-purchase.js +119 -0
- package/dist/sdk/payment/resumed-purchase.js.map +1 -0
- package/dist/sdk/payment/security-code-mask.js +16 -23
- package/dist/sdk/payment/security-code-mask.js.map +1 -1
- package/dist/sdk/payment/submission-registry.js +6 -2
- package/dist/sdk/payment/submission-registry.js.map +1 -1
- package/dist/sdk/payment/use-payment-actions.d.ts +18 -3
- package/dist/sdk/payment/use-payment-actions.js +23 -3
- package/dist/sdk/payment/use-payment-actions.js.map +1 -1
- package/dist/sdk/payment/use-place-order.d.ts +10 -2
- package/dist/sdk/payment/use-place-order.js +138 -76
- package/dist/sdk/payment/use-place-order.js.map +1 -1
- package/dist/sdk/provider/storefront-provider.d.ts +1 -7
- package/dist/sdk/provider/storefront-provider.js +4 -11
- package/dist/sdk/provider/storefront-provider.js.map +1 -1
- package/dist/sdk/seo/entity-landing.d.ts +24 -0
- package/dist/sdk/seo/entity-landing.js +30 -0
- package/dist/sdk/seo/entity-landing.js.map +1 -0
- package/dist/sdk/seo/page-alternates.d.ts +3 -2
- package/dist/sdk/seo/page-alternates.js +4 -2
- package/dist/sdk/seo/page-alternates.js.map +1 -1
- package/dist/sdk/seo/search-landing.d.ts +42 -0
- package/dist/sdk/seo/search-landing.js +85 -0
- package/dist/sdk/seo/search-landing.js.map +1 -0
- package/dist/sdk/session/mock-session.d.ts +9 -0
- package/dist/sdk/session/mock-session.js +31 -0
- package/dist/sdk/session/mock-session.js.map +1 -0
- package/dist/sdk/session/read-server-session.d.ts +3 -0
- package/dist/sdk/session/read-server-session.js +5 -0
- package/dist/sdk/session/read-server-session.js.map +1 -1
- package/dist/sdk/session/return-path-segment.d.ts +10 -0
- package/dist/sdk/session/return-path-segment.js +15 -0
- package/dist/sdk/session/return-path-segment.js.map +1 -0
- package/dist/sdk/session/use-session-actions.js.map +1 -1
- package/dist/sdk/shipping/use-cart-shipping-simulation.js.map +1 -1
- package/dist/sdk/shipping/use-shipping-actions.js.map +1 -1
- package/dist/sdk/shipping/use-shipping-simulation.js.map +1 -1
- package/dist/utils/safe-href.d.ts +2 -0
- package/package.json +4 -3
- package/skills/storefront-asset/SKILL.md +2 -2
- package/skills/storefront-asset/references/api-side.md +27 -14
- package/skills/storefront-asset/references/findings.md +5 -2
- package/src/styles/tailwind.css +8 -0
- package/dist/matter/shipping-address/country-descriptors.d.ts +0 -1
- package/dist/message-locales.json +0 -7
- package/dist/sdk/i18n/client.d.ts +0 -4
- package/dist/sdk/i18n/client.js +0 -4
- package/dist/sdk/i18n/locales.d.ts +0 -8
- package/dist/sdk/i18n/locales.js +0 -57
- package/dist/sdk/i18n/locales.js.map +0 -1
- package/dist/sdk/i18n/messages/en-US.js +0 -183
- package/dist/sdk/i18n/messages/en-US.js.map +0 -1
- package/dist/sdk/i18n/messages/en-US.json +0 -178
- package/dist/sdk/i18n/messages/en-US.json.d.ts +0 -182
- package/dist/sdk/i18n/messages/es-ES.js +0 -183
- package/dist/sdk/i18n/messages/es-ES.js.map +0 -1
- package/dist/sdk/i18n/messages/es-ES.json +0 -178
- package/dist/sdk/i18n/messages/pt-BR.js +0 -183
- package/dist/sdk/i18n/messages/pt-BR.js.map +0 -1
- package/dist/sdk/i18n/messages/pt-BR.json +0 -178
- package/dist/sdk/i18n/provider.d.ts +0 -1
- package/dist/sdk/i18n/provider.js +0 -30
- package/dist/sdk/i18n/provider.js.map +0 -1
- package/dist/sdk/i18n/server.d.ts +0 -28
- package/dist/sdk/i18n/server.js +0 -59
- package/dist/sdk/i18n/server.js.map +0 -1
- package/dist/utils/resolve-authored-copy.js +0 -18
- package/dist/utils/resolve-authored-copy.js.map +0 -1
- package/dist/utils/substitute-template.js +0 -33
- package/dist/utils/substitute-template.js.map +0 -1
|
@@ -1 +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 hardened: true,\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 {\n selectedAccountId,\n selectMethod,\n selectionError,\n draft,\n placing: placingNow,\n attempt,\n cardPaymentCollapsed,\n } = usePayment();\n // T202: frozen while the order attempt is resume-only too (`sent` / `unresolved`): the card may already have\n // been sent and `placeOrder` only resumes, so a security code typed here would never be submitted or cleared.\n const placing = placingNow || attempt?.phase === 'sent' || attempt?.phase === 'unresolved';\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 // Both refusals precede any request, so each reports `reachedGateway: false` (T112): the attempt is\n // freed rather than left resume-only on a card that was never sent.\n const refusals = checkCvv();\n if (refusals.length > 0) return { reachedGateway: false, userErrors: refusals };\n // T202: past the field's own check the code is not needed again, whatever the outcome. A throw is\n // re-thrown untouched; the `finally` is the one place certain to run.\n try {\n if (!sendPayments) return { reachedGateway: false, userErrors: [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 await sendPayments({ transaction, card: { kind: 'saved', accountId, bin, securityCode: cvv } });\n } finally {\n setCvv('');\n }\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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"}
|
|
1
|
+
{"version":3,"file":"saved-cards.js","names":[],"sources":["../../../src/matter/saved-cards/saved-cards.tsx"],"sourcesContent":["'use client';\n\nimport { useApolloClient } from '@apollo/client/react';\nimport {\n type ContentValue,\n defineComponent,\n defineContentType,\n type MessageFieldName,\n type MessageLocaleProp,\n} 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 { Icon } from '../../particles/icon/icon.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, useListItemRef } from '../../sdk/data/list-item-ref-context.js';\nimport { formatErrorCode } from '../../sdk/i18n/format-error-code.js';\nimport { useMessageFormatter } from '../../sdk/i18n/use-message-formatter.js';\nimport { PAYMENT_NETWORK_ERROR_CODE, type PaymentUserError } from '../../sdk/payment/operations.js';\nimport { isSecurityCodeMasked, 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 { loggableErrorName } from '../../utils/loggable-error-name.js';\nimport { NETWORK_ICON_BY_KEY } from '../card-entry/card-entry.js';\nimport { usePayment } from '../payment/payment.js';\nimport type { PaymentMethodSelectErrorCode } from '../payment/use-payment-method-selection.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 * A card whose `cvvRequired` is a literal `false` has no code to enter (T39): `SavedCardOption` registers the\n * `'saved-card'` continuation itself, one that sends nothing and tells `usePlaceOrder` the order is placed by\n * Start Transaction alone. Any other flag value, including an absent one, keeps the field and the coded path.\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 expired\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/**\n * The minimal per-row facts `SavedCards` needs to iterate, to dispatch selection, and to tell whether the\n * persisted selection is an expired card (which is never the radio group's value).\n */\nexport type SavedCardsListEntry = Readonly<{\n id: string;\n accountId: string;\n methodId: string;\n bin: string;\n expired: boolean;\n}>;\n\nexport type SavedCardsData = {\n savedCards?: readonly SavedCardsListEntry[];\n} | null;\n\nexport const SavedCardsContent = defineContentType({\n id: 'saved-cards',\n title: 'Saved Cards',\n description: 'Copy for the saved-card group legend and a refused card selection.',\n schema: {\n type: 'object',\n additionalProperties: false,\n properties: {\n legend: {\n type: 'string',\n title: 'Legend',\n description: \"The radio group's visible accessible name.\",\n format: 'icu-message',\n messageSource: 'Saved cards',\n },\n savedCardUnavailableMessage: {\n type: 'string',\n title: 'Saved card unavailable message',\n description: 'Shown when the picked saved card can no longer be used.',\n format: 'icu-message',\n messageSource: 'This saved card is no longer available. Choose another card.',\n },\n genericErrorMessage: {\n type: 'string',\n title: 'Selection failure message',\n description: 'Shown when a card selection fails: a network failure or a fault the shopper cannot fix.',\n format: 'icu-message',\n messageSource: 'Unable to select this card. Please try again.',\n },\n },\n },\n});\n\n/**\n * The message for each code a method selection can return. A saved card the server no longer matches is\n * one the shopper can replace; the rest, and a code this map does not know, share one message.\n */\nexport const SAVED_CARDS_ERROR_FIELDS = {\n MISSING_CART_CONTEXT: 'genericErrorMessage',\n CART_NOT_FOUND: 'genericErrorMessage',\n INVALID_METHOD_ID: 'genericErrorMessage',\n INVALID_SAVED_CARD_ACCOUNT_ID: 'savedCardUnavailableMessage',\n MISMATCHED_BIN_FOR_SAVED_CARD: 'savedCardUnavailableMessage',\n INVALID_BIN_FORMAT: 'genericErrorMessage',\n CONFLICTING_BIN_AND_ACCOUNT_ID: 'genericErrorMessage',\n NETWORK_ERROR: 'genericErrorMessage',\n} as const satisfies Record<PaymentMethodSelectErrorCode, MessageFieldName<typeof SavedCardsContent>>;\n\nexport const SavedCardCvvFieldContent = defineContentType({\n id: 'saved-card-cvv-field',\n title: 'Saved Card CVV Field',\n description: 'Copy for the security code input of a saved card.',\n schema: {\n type: 'object',\n additionalProperties: false,\n properties: {\n securityCodeLabel: {\n type: 'string',\n title: 'Security code label',\n description: \"The input's accessible name; no visible label renders.\",\n format: 'icu-message',\n messageSource: 'Security code',\n },\n securityCodeDescription: {\n type: 'string',\n title: 'Security code description',\n description: 'Helper text linked to the input; blank renders nothing.',\n format: 'icu-message',\n messageSource: 'The 3-4 digit number on your card',\n },\n },\n },\n});\n\nexport const SavedCardRemoveContent = defineContentType({\n id: 'saved-card-remove',\n title: 'Saved Card Remove',\n description: 'Copy for the remove control of an expired saved card.',\n schema: {\n type: 'object',\n additionalProperties: false,\n properties: {\n label: { type: 'string', title: 'Label', format: 'icu-message', messageSource: 'Remove card' },\n failureMessage: {\n type: 'string',\n title: 'Failure message',\n description: \"Rendered when the removal did not complete, never the server's text.\",\n format: 'icu-message',\n messageSource: 'The card was not removed. Please try again.',\n },\n },\n },\n});\n\nexport type SavedCardsProps = ComponentPropsWithRef<'div'> &\n MessageLocaleProp & {\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 /** Merchant-authored copy: the radio group's visible legend (T38). */\n $content?: ContentValue<typeof SavedCardsContent>;\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 MessageLocaleProp & {\n /** Compiler-injected action surface; carries `payment.sendPayments`. */\n $actions?: ComponentActions;\n /**\n * Merchant-authored copy. No visible `<label>` renders (Accessibility Contract), so\n * `securityCodeLabel` is the input's only accessible name. `securityCodeDescription` renders as a\n * sibling `<p>` linked through `aria-describedby` (the root is the input itself, so there is no\n * `Field.Root` to host a `Field.Description` — saved-cards.md Decision 3).\n */\n $content?: ContentValue<typeof SavedCardCvvFieldContent>;\n };\n\nexport type SavedCardRemoveProps = Omit<ComponentPropsWithRef<'button'>, 'children'> &\n MessageLocaleProp & {\n /** Compiler-injected action surface; carries `savedCard.remove`. */\n $actions?: ComponentActions;\n /** Merchant-authored copy: the control's text and the message when the removal did not complete. */\n $content?: ContentValue<typeof SavedCardRemoveContent>;\n };\n\n// ---------------------------------------------------------------------------\n// Saved-card-data context — provided by SavedCardOption, read by SavedCardCvvField and SavedCardRemove. 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(consumer: 'SavedCardCvvField' | 'SavedCardRemove'): SavedCardOptionSlice {\n const data = useContext(SavedCardDataContext);\n if (!data) {\n throw new Error(`${consumer} 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 content: SavedCardsContent,\n editor: { label: 'Saved Cards', category: 'commerce' },\n schema: {},\n guideline:\n 'Radio list of the shopper\\'s saved cards (network, last four, label); expired cards render present but disabled, never selectable, and a persisted selection that points at an expired card counts as no selection (no row checked, no security-code field, nothing to submit). Place a SavedCardOption inside as the per-card template, itself wrapping a SavedCardCvvField so the shopper can complete payment with a stored card that needs a security code; a card that needs none is payable through SavedCardOption alone. 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: the network mark (its name as text when the network has no mark), the masked last four, and its label when set, separated by a dot; disabled when expired. An expired card is never the selection and mounts no field. A card that needs no security code shows no field and places the order with one action: this component alone makes it payable. Place a SavedCardCvvField inside so the CVV appears once a card that needs one 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 hardened: true,\n content: SavedCardCvvFieldContent,\n editor: { label: 'Saved Card CVV Field', category: 'commerce' },\n schema: {},\n guideline:\n \"The CVV re-entry field for its saved card row; renders nothing until that row is selected, is not expired and requires a CVV (only a card flagged as needing none skips it; an unknown flag requires it). 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. Only needed for cards that need a code: a card that needs none is payable without it. Must be inside SavedCardOption.\",\n});\n\nexport const SavedCardRemoveDefinition = defineComponent<SavedCardRemoveProps>({\n name: 'SavedCardRemove',\n importPath: '@vtex/faststore/components',\n component: SavedCardRemove,\n requiresContext: 'saved-card-data',\n actions: { savedCard: ['remove'] },\n content: SavedCardRemoveContent,\n editor: { label: 'Saved Card Remove', category: 'commerce' },\n schema: {},\n guideline:\n 'A button that removes this saved card from the shopper\\'s account, rendered only for an expired card: a card that can still be used shows no control. Place it inside SavedCardOption, beside SavedCardCvvField. If the removal does not complete the card stays listed and an alert (role=\"alert\") says so, in the failureMessage $content field and never the server\\'s text. It never changes the selected payment method or card. 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. The same goes for a persisted selection whose card has since expired: the row is disabled, so\n * it can never be the choice, and counts as no selection at all (T39). Every read site in this module goes\n * through this one function — the radio group's own `value`, a row's `-selected` state, the CVV field's\n * visibility gate and the no-code submission's — so they can never drift out of sync with each other.\n * Reads only `draft.path`, never `draft.bin`: that stays a fully separate concern (Decision 2) that no\n * call site in this module touches.\n */\nfunction resolveEffectiveSelectedAccountId({\n selectedAccountId,\n draftPath,\n selectedCardExpired,\n}: {\n selectedAccountId: string | null;\n draftPath: string | null;\n /** Whether the card behind `selectedAccountId` is expired. Irrelevant while nothing is selected. */\n selectedCardExpired: boolean;\n}): string | null {\n return draftPath === 'new-card' || selectedCardExpired ? null : selectedAccountId;\n}\n\n/**\n * Fail closed: only a literal `false` on a found list entry means \"not expired\". An entry that lacks\n * the flag (a cache entry written before the fragment selected it) counts as expired, so it can never\n * hold the radio group's value. No entry at all is not an expired card.\n */\nfunction isListEntryExpired(entry: { expired: boolean } | undefined): boolean {\n return entry !== undefined && entry.expired !== false;\n}\n\n/**\n * `SavedCard.network` is the upstream `paymentSystemName`, free text. Own-property lookup on the icon\n * table, because a plain-object read lets a name that normalises to `constructor` resolve to the\n * inherited `Object` function, which `Icon` then throws on. Same normalisation as card entry's\n * `networkIconName`.\n */\nfunction savedCardNetworkIcon(network: string): string | null {\n const key = network.toLowerCase().replaceAll(/[^a-z]/g, '');\n return Object.hasOwn(NETWORK_ICON_BY_KEY, key) ? (NETWORK_ICON_BY_KEY[key] ?? null) : null;\n}\n\nexport function SavedCards({ $data: $dataProp, $content, $locale, className, children, ...props }: SavedCardsProps) {\n const $data = useDataOverride($dataProp);\n const { format } = useMessageFormatter(SavedCardsContent, $content);\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 {\n selectedAccountId,\n selectMethod,\n selectionError,\n draft,\n placing: placingNow,\n attempt,\n cardPaymentCollapsed,\n } = usePayment();\n // T202: frozen while the order attempt is resume-only too (`sent` / `unresolved`): the card may already have\n // been sent and `placeOrder` only resumes, so a security code typed here would never be submitted or cleared.\n const placing = placingNow || attempt?.phase === 'sent' || attempt?.phase === 'unresolved';\n const loading = $data == null;\n const savedCards = $data?.savedCards ?? [];\n const effectiveSelectedAccountId = resolveEffectiveSelectedAccountId({\n selectedAccountId,\n draftPath: draft.path,\n selectedCardExpired: isListEntryExpired(savedCards.find((entry) => entry.accountId === selectedAccountId)),\n });\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 {formatErrorCode(SavedCardsContent, SAVED_CARDS_ERROR_FIELDS, selectionError, format, 'genericErrorMessage')}\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>{format('legend')}</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, bin,\n // expired } (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. Expiry-aware too:\n // this row's own `expired` is the persisted account's, whenever this row is that account.\n const isSelected =\n slice.accountId ===\n resolveEffectiveSelectedAccountId({ selectedAccountId, draftPath: draft.path, selectedCardExpired: slice.expired });\n const networkIcon = savedCardNetworkIcon(slice.network);\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=\"\">\n {networkIcon ? (\n // Not decorative, unlike card entry's mark: here the network is part of the card's identity and\n // the radio's accessible name comes from its content (Decision 3).\n <Icon\n name={networkIcon}\n aria-hidden={false}\n role=\"img\"\n aria-label={slice.network}\n data-fs-saved-card-option-network-icon=\"\"\n />\n ) : (\n slice.network\n )}\n </span>\n {slice.network ? (\n <>\n {' '}\n <span aria-hidden=\"true\" data-fs-saved-card-option-separator=\"\">\n ·\n </span>{' '}\n </>\n ) : null}\n <span data-fs-saved-card-option-last-four=\"\">{`•••• ${slice.lastFourDigits}`}</span>\n {slice.label ? (\n <>\n {' '}\n <span aria-hidden=\"true\" data-fs-saved-card-option-separator=\"\">\n ·\n </span>{' '}\n <span data-fs-saved-card-option-label=\"\">{slice.label}</span>\n </>\n ) : null}\n </RadioRoot>\n {isSelected && slice.cvvRequired === false ? <SavedCardNoCodeSubmission /> : null}\n {children}\n </div>\n </SavedCardDataContext>\n );\n}\n\n// Diagnostic `message` (logs, `onResult`); `PlaceOrder` words the code.\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\n// Diagnostic `message` (logs, `onResult`); `PlaceOrder` words the code.\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, not expired and its\n * `cvvRequired` is not a literal `false`, so its presence *is* the requiredness signal.\n */\nconst SAVED_CARD_CVV_PATTERN = /^\\d{3,4}$/;\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`, formatted by `SavedCardCvvField`. */\n securityCodeLabel: string;\n /** Helper copy rendered as a sibling `<p>`, formatted by `SavedCardCvvField`. 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, is not expired 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 onFocus,\n onBlur,\n ...props\n}: SavedCardCvvInputProps) {\n const [cvv, setCvv] = useState('');\n // T34: whether this input has focus, local to it. It lives and dies with this component, so it is\n // reset by the unmount that deselecting the row causes; a *disabled* input is counted as unfocused\n // where the mask is derived below, since a browser fires no `blur` when a focused input is disabled.\n const [focused, setFocused] = useState(false);\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 // Both refusals precede any request, so each reports `reachedGateway: false` (T112): the attempt is\n // freed rather than left resume-only on a card that was never sent.\n const refusals = checkCvv();\n if (refusals.length > 0) return { reachedGateway: false, userErrors: refusals };\n // T202: past the field's own check the code is not needed again, whatever the outcome. A throw is\n // re-thrown untouched; the `finally` is the one place certain to run.\n try {\n if (!sendPayments) return { reachedGateway: false, userErrors: [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 await sendPayments({ transaction, card: { kind: 'saved', accountId, bin, securityCode: cvv } });\n } finally {\n setCvv('');\n }\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 // T34 — the merge applies while the input has no focus (or is disabled) and holds a code, so a\n // shopper can read back what they type while they type it. While focused the caller's own style\n // passes through untouched, which is the same thing it did before this field ever had a mask; the\n // guarantee above is about what a caller cannot do once the code is out of focus.\n const inputStyle = isSecurityCodeMasked(cvv, focused && !props.disabled)\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 onFocus={(event) => {\n setFocused(true);\n onFocus?.(event);\n }}\n onBlur={(event) => {\n setFocused(false);\n onBlur?.(event);\n }}\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\n/**\n * The submission of a selected, non-expired card whose flag is a literal `false` (T39): no input mounts, so\n * there is no code and nothing to send. It registers a continuation that issues no request and tells\n * `usePlaceOrder` the order is placed by Start Transaction alone (`completesWithoutGateway`), so\n * `sendPayments` and the gateway callback are never called. Mounted by `SavedCardOption`, never by\n * `SavedCardCvvField`: a composition that leaves the field out still has to make this card payable. No\n * pre-flight is registered (nothing to validate), and it logs nothing.\n */\nfunction SavedCardNoCodeSubmission() {\n const submit = useCallback<PaymentSubmission>(\n async () => ({ reachedGateway: false, userErrors: [], completesWithoutGateway: true }),\n [],\n );\n\n useRegisterPaymentSubmission('saved-card', submit);\n\n return null;\n}\n\nexport function SavedCardCvvField({\n $actions: $actionsProp,\n $content,\n $locale,\n className,\n ...props\n}: SavedCardCvvFieldProps) {\n const $actions = useActionsOverride($actionsProp);\n const { format } = useMessageFormatter(SavedCardCvvFieldContent, $content);\n const { accountId, bin, cvvRequired, expired } = useSavedCardData('SavedCardCvvField');\n const { selectedAccountId, draft } = usePayment();\n\n // Path-aware and expiry-aware, matching SavedCardOption's own isSelected — otherwise this field stays\n // mounted and registered after \"use a new card\", with selectedAccountId still equal to this row's own\n // accountId. Only a literal `false` skips the code (T39, INV-2): an absent or malformed flag is an\n // unknown, and an unknown must never become a silent skip.\n const effectiveSelectedAccountId = resolveEffectiveSelectedAccountId({\n selectedAccountId,\n draftPath: draft.path,\n selectedCardExpired: expired,\n });\n if (accountId !== effectiveSelectedAccountId || cvvRequired === false) {\n return null;\n }\n\n return (\n <SavedCardCvvInput\n {...props}\n className={className}\n accountId={accountId}\n bin={bin}\n securityCodeLabel={format('securityCodeLabel')}\n securityCodeDescription={format('securityCodeDescription')}\n $actions={$actions}\n />\n );\n}\n\n/**\n * The `CustomerSavedCard` opaque id the removal API takes. The checkout row's own id is a `SavedCard` id\n * (a different entity set), but both wrap the same platform `accountId`, so the account-side id is derived\n * from it: standard-alphabet, padded base64 of `CustomerSavedCard:<accountId>` (the server's\n * `encodeOpaqueId`). `btoa` over `TextEncoder` bytes, because `Buffer` does not exist in the browser. A\n * drift from the server's encoding is refused there as `CARD_NOT_FOUND`, which lands on the failure path.\n */\nfunction toAccountSavedCardId(accountId: string): string {\n const bytes = new TextEncoder().encode(`CustomerSavedCard:${accountId}`);\n let binary = '';\n for (const byte of bytes) binary += String.fromCodePoint(byte);\n return btoa(binary);\n}\n\n/**\n * Removes an expired saved card (T216). Renders only for a row whose `expired` is the literal `true`. A\n * success evicts the row's `SavedCard` entity, which is what the checkout list reads; the SDK action only\n * updates the account's own `CustomerSavedCards` entity. Anything else, including a missing action, leaves\n * the row and says so with its failureMessage, never the server's message. A caller's `onClick` runs first, and\n * calling `event.preventDefault()` there cancels the removal.\n */\nexport function SavedCardRemove({\n $actions: $actionsProp,\n $content,\n $locale,\n className,\n onClick,\n ...props\n}: SavedCardRemoveProps) {\n const $actions = useActionsOverride($actionsProp);\n const { format } = useMessageFormatter(SavedCardRemoveContent, $content);\n const { accountId, network, lastFourDigits, expired } = useSavedCardData('SavedCardRemove');\n const listItemRef = useListItemRef();\n const { cache } = useApolloClient();\n const [pending, setPending] = useState(false);\n const [failed, setFailed] = useState(false);\n // Re-entrancy latch. A ref, not `pending`: two clicks in one tick read the same pre-render state.\n const inFlight = useRef(false);\n\n if (expired !== true) return null;\n\n async function handleClick(event: React.MouseEvent<HTMLButtonElement>) {\n // A click on a pending button is ignored, and the store's handler does not hear it either.\n if (inFlight.current) return;\n onClick?.(event);\n if (event.defaultPrevented) return;\n inFlight.current = true;\n setPending(true);\n setFailed(false);\n\n let removed = false;\n try {\n const remove = $actions?.savedCard?.remove;\n if (typeof remove === 'function') {\n const userErrors = await remove({ id: toAccountSavedCardId(accountId) });\n // `remove` resolves with its errors instead of rejecting, so only an empty list is a removal.\n removed = Array.isArray(userErrors) && userErrors.length === 0;\n }\n } catch (error) {\n console.error('[SavedCardRemove] remove failed', loggableErrorName(error));\n } finally {\n inFlight.current = false;\n setPending(false);\n }\n\n if (!removed) {\n setFailed(true);\n return;\n }\n const entityId = listItemRef\n ? cache.identify({ __typename: listItemRef.__typename, id: listItemRef.id })\n : undefined;\n if (entityId) {\n cache.evict({ id: entityId });\n cache.gc();\n }\n }\n\n const resolvedLabel = format('label');\n const accessibleName = [resolvedLabel, network, lastFourDigits ? `•••• ${lastFourDigits}` : '']\n .filter(Boolean)\n .join(' ');\n\n return (\n <>\n <button\n {...props}\n type=\"button\"\n className={className}\n aria-label={accessibleName}\n onClick={handleClick}\n data-fs-saved-card-remove=\"\"\n {...(pending ? { 'aria-busy': true, 'aria-disabled': true, 'data-fs-saved-card-remove-pending': '' } : {})}\n >\n {resolvedLabel}\n </button>\n {failed ? (\n <p role=\"alert\" data-fs-saved-card-remove-error=\"\">\n {format('failureMessage')}\n </p>\n ) : null}\n </>\n );\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
//#region src/matter/shipping-address/address-payload.ts
|
|
2
|
+
/** The six text fields a country's descriptor may list; `postalCode`, `country` and `recipientName` stay each matter's own. */
|
|
3
|
+
const ADDRESS_TEXT_FIELD_NAMES = [
|
|
4
|
+
"street",
|
|
5
|
+
"number",
|
|
6
|
+
"complement",
|
|
7
|
+
"neighborhood",
|
|
8
|
+
"city",
|
|
9
|
+
"state"
|
|
10
|
+
];
|
|
11
|
+
/** The value a field submits: its `normalize` applied when the list has that field and it carries one, else `value` as typed. */
|
|
12
|
+
function normalizeAddressField(fields, name, value) {
|
|
13
|
+
const normalize = fields.find((field) => field.name === name)?.normalize;
|
|
14
|
+
return normalize ? normalize(value) : value;
|
|
15
|
+
}
|
|
16
|
+
/** Of the six text fields, only those `fields` lists, each with its value from `values` verbatim. */
|
|
17
|
+
function pickActiveTextFields(fields, values) {
|
|
18
|
+
const result = {};
|
|
19
|
+
for (const name of ADDRESS_TEXT_FIELD_NAMES) if (fields.some((field) => field.name === name)) result[name] = values[name];
|
|
20
|
+
return result;
|
|
21
|
+
}
|
|
22
|
+
//#endregion
|
|
23
|
+
export { ADDRESS_TEXT_FIELD_NAMES, normalizeAddressField, pickActiveTextFields };
|
|
24
|
+
|
|
25
|
+
//# sourceMappingURL=address-payload.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"address-payload.js","names":[],"sources":["../../../src/matter/shipping-address/address-payload.ts"],"sourcesContent":["import type { FieldDescriptor } from './country-descriptors.js';\n\n/** The six text fields a country's descriptor may list; `postalCode`, `country` and `recipientName` stay each matter's own. */\nexport const ADDRESS_TEXT_FIELD_NAMES = ['street', 'number', 'complement', 'neighborhood', 'city', 'state'] as const;\n\nexport type AddressTextFieldName = (typeof ADDRESS_TEXT_FIELD_NAMES)[number];\n\n/** The value a field submits: its `normalize` applied when the list has that field and it carries one, else `value` as typed. */\nexport function normalizeAddressField(fields: readonly FieldDescriptor[], name: string, value: string): string {\n const normalize = fields.find((field) => field.name === name)?.normalize;\n return normalize ? normalize(value) : value;\n}\n\n/** Of the six text fields, only those `fields` lists, each with its value from `values` verbatim. */\nexport function pickActiveTextFields(\n fields: readonly FieldDescriptor[],\n values: Readonly<Record<string, string>>,\n): Partial<Record<AddressTextFieldName, string>> {\n const result: Partial<Record<AddressTextFieldName, string>> = {};\n for (const name of ADDRESS_TEXT_FIELD_NAMES) {\n if (fields.some((field) => field.name === name)) result[name] = values[name];\n }\n return result;\n}\n"],"mappings":";;AAGA,MAAa,2BAA2B;CAAC;CAAU;CAAU;CAAc;CAAgB;CAAQ;AAAO;;AAK1G,SAAgB,sBAAsB,QAAoC,MAAc,OAAuB;CAC7G,MAAM,YAAY,OAAO,MAAM,UAAU,MAAM,SAAS,IAAI,CAAC,EAAE;CAC/D,OAAO,YAAY,UAAU,KAAK,IAAI;AACxC;;AAGA,SAAgB,qBACd,QACA,QAC+C;CAC/C,MAAM,SAAwD,CAAC;CAC/D,KAAK,MAAM,QAAQ,0BACjB,IAAI,OAAO,MAAM,UAAU,MAAM,SAAS,IAAI,GAAG,OAAO,QAAQ,OAAO;CAEzE,OAAO;AACT"}
|
|
@@ -1,5 +1,39 @@
|
|
|
1
|
+
import { LIBRARY_COUNTRIES, getLoadedLibraryCountry } from "./library-country-descriptors.js";
|
|
1
2
|
//#region src/matter/shipping-address/country-descriptors.ts
|
|
2
3
|
/**
|
|
4
|
+
* Country-adaptive shipping address form descriptors.
|
|
5
|
+
*
|
|
6
|
+
* Each descriptor declares the form shape (field set and order) for a specific country. Descriptors are
|
|
7
|
+
* structure only: a `FieldDescriptor` may also carry `required` and `rule`, the two validation facts a
|
|
8
|
+
* country varies, and never a string. `ShippingAddressFieldset` is the only reader of `required`
|
|
9
|
+
* (specs/shipping-address-fieldset.md, Decision 3): a field whose descriptor entry is absent is never
|
|
10
|
+
* rendered, so it can carry no requiredness at all. `rule.pattern` has two readers: the fieldset, through
|
|
11
|
+
* react-hook-form, and `satisfiesFieldPattern` below, which `BillingAddress` applies to its own submit gate
|
|
12
|
+
* and field errors. Every word a form shows (field labels, the required and invalid messages, the country
|
|
13
|
+
* names) comes from the form's own message fields and `Intl.DisplayNames`, so no copy lives here.
|
|
14
|
+
* Country adaptation is data-driven. `BRA` and `USA` are the two entries written here; adding another
|
|
15
|
+
* hand-written country is a new entry in `countryDescriptors`. The countries `@vtex/country-forms` covers
|
|
16
|
+
* are not copied: `library-country-descriptors.ts` builds `CAN` and `MEX` descriptors from the library's own
|
|
17
|
+
* rules at runtime, and `getCountryDescriptor` and `getAvailableCountries` read them once they have loaded.
|
|
18
|
+
*
|
|
19
|
+
* Canonical field names must be shared/consistent across descriptors for fields both countries have
|
|
20
|
+
* (recipientName, postalCode, street, number, complement, city, state), so that switching countries
|
|
21
|
+
* preserves shared field values in the batched draft state (Decision 1/Invariant).
|
|
22
|
+
*
|
|
23
|
+
* Per Decision 2, this narrows the RFC's descriptor scope to form shape plus two per-field validation
|
|
24
|
+
* facts: the non-blank recipient name and, per country, the postal code's format. Both descriptors'
|
|
25
|
+
* `postalCode` entry carries a `pattern` rule matching that country's CEP or ZIP shape; every other
|
|
26
|
+
* field's `rule` stays undefined. A library-backed country's `postalCode` entry carries a `validate`
|
|
27
|
+
* predicate instead, since the library's rule is not a regular expression. There is no server-side
|
|
28
|
+
* postal-code validation to fall back on: `shippingAddressUpdate` types `postalCode` as a plain
|
|
29
|
+
* `z.string()`, forwards it verbatim with `clearAddressIfPostalCodeNotFound: false`, and has no
|
|
30
|
+
* try/catch around the upstream call, so a malformed value still returns HTTP 200 with an empty
|
|
31
|
+
* `userErrors` array. This client-side rule is the only postal-code guard in the system today. The two
|
|
32
|
+
* forms apply it on different triggers: the billing form judges a touched field on every render (through
|
|
33
|
+
* `getFieldIssue`, which reads `satisfiesFieldRule`), touching it on blur or when a refused submit focuses
|
|
34
|
+
* it, while the delivery form's react-hook-form rule runs on submit and re-validates after it.
|
|
35
|
+
*/
|
|
36
|
+
/**
|
|
3
37
|
* Whether `value` satisfies the field's declared `rule.pattern`. A field with no pattern, or a blank
|
|
4
38
|
* value (empty or whitespace-only: blankness is the required rule's business, and a form that trims
|
|
5
39
|
* before judging, as `BillingAddress` does, reads such a value as required, never as a format failure),
|
|
@@ -14,98 +48,63 @@
|
|
|
14
48
|
function satisfiesFieldPattern(field, value) {
|
|
15
49
|
const pattern = field.rule?.pattern;
|
|
16
50
|
if (!pattern || value.trim() === "") return true;
|
|
17
|
-
return value.search(pattern
|
|
51
|
+
return value.search(pattern) !== -1;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Whether `value` satisfies everything the field's `rule` can judge on its own: `rule.pattern` (as
|
|
55
|
+
* `satisfiesFieldPattern`) and a function-valued `rule.validate` (anything else is refused). A blank
|
|
56
|
+
* value is satisfied, for the same reason: blankness is the required rule's business, so a validator is
|
|
57
|
+
* never called with one. A validator passes only on `true`, so both forms agree on every non-blank value.
|
|
58
|
+
*/
|
|
59
|
+
function satisfiesFieldRule(field, value) {
|
|
60
|
+
if (!satisfiesFieldPattern(field, value)) return false;
|
|
61
|
+
const validate = field.rule?.validate;
|
|
62
|
+
if (validate === void 0 || value.trim() === "") return true;
|
|
63
|
+
if (typeof validate !== "function") return false;
|
|
64
|
+
return validate(value) === true;
|
|
18
65
|
}
|
|
19
66
|
/**
|
|
20
|
-
*
|
|
67
|
+
* The hand-written country descriptors, keyed by ISO alpha-3 code: `BRA` and `USA`.
|
|
21
68
|
*
|
|
22
69
|
* The country selector lists only countries with a descriptor; a descriptor-less country is unreachable.
|
|
23
|
-
*
|
|
70
|
+
* The countries `@vtex/country-forms` covers are not here: they are built from the library at runtime and
|
|
71
|
+
* read through `getCountryDescriptor` (see `library-country-descriptors.ts`).
|
|
24
72
|
*/
|
|
25
73
|
const countryDescriptors = {
|
|
26
74
|
BRA: {
|
|
27
75
|
iso: "BRA",
|
|
28
|
-
label: "Brasil",
|
|
29
|
-
countryLabel: "país",
|
|
30
|
-
requiredSuffix: "é obrigatório",
|
|
31
76
|
fields: [
|
|
32
77
|
{
|
|
33
78
|
name: "recipientName",
|
|
34
|
-
label: "nome do destinatário",
|
|
35
79
|
required: true
|
|
36
80
|
},
|
|
37
|
-
{
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
},
|
|
41
|
-
{
|
|
42
|
-
|
|
43
|
-
label: "número"
|
|
44
|
-
},
|
|
45
|
-
{
|
|
46
|
-
name: "complement",
|
|
47
|
-
label: "complemento (opcional)"
|
|
48
|
-
},
|
|
49
|
-
{
|
|
50
|
-
name: "neighborhood",
|
|
51
|
-
label: "bairro"
|
|
52
|
-
},
|
|
53
|
-
{
|
|
54
|
-
name: "city",
|
|
55
|
-
label: "cidade"
|
|
56
|
-
},
|
|
57
|
-
{
|
|
58
|
-
name: "state",
|
|
59
|
-
label: "estado"
|
|
60
|
-
},
|
|
81
|
+
{ name: "street" },
|
|
82
|
+
{ name: "number" },
|
|
83
|
+
{ name: "complement" },
|
|
84
|
+
{ name: "neighborhood" },
|
|
85
|
+
{ name: "city" },
|
|
86
|
+
{ name: "state" },
|
|
61
87
|
{
|
|
62
88
|
name: "postalCode",
|
|
63
|
-
|
|
64
|
-
rule: { pattern: {
|
|
65
|
-
value: /^\d{5}-?\d{3}$/,
|
|
66
|
-
message: "cep inválido"
|
|
67
|
-
} }
|
|
89
|
+
rule: { pattern: /^\d{5}-?\d{3}$/ }
|
|
68
90
|
}
|
|
69
91
|
]
|
|
70
92
|
},
|
|
71
93
|
USA: {
|
|
72
94
|
iso: "USA",
|
|
73
|
-
label: "United States",
|
|
74
|
-
countryLabel: "country",
|
|
75
|
-
requiredSuffix: "is required",
|
|
76
95
|
fields: [
|
|
77
96
|
{
|
|
78
97
|
name: "recipientName",
|
|
79
|
-
label: "recipient name",
|
|
80
98
|
required: true
|
|
81
99
|
},
|
|
82
|
-
{
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
},
|
|
86
|
-
{
|
|
87
|
-
name: "number",
|
|
88
|
-
label: "number"
|
|
89
|
-
},
|
|
90
|
-
{
|
|
91
|
-
name: "complement",
|
|
92
|
-
label: "apartment, suite, etc. (optional)"
|
|
93
|
-
},
|
|
94
|
-
{
|
|
95
|
-
name: "city",
|
|
96
|
-
label: "city"
|
|
97
|
-
},
|
|
98
|
-
{
|
|
99
|
-
name: "state",
|
|
100
|
-
label: "state"
|
|
101
|
-
},
|
|
100
|
+
{ name: "street" },
|
|
101
|
+
{ name: "number" },
|
|
102
|
+
{ name: "complement" },
|
|
103
|
+
{ name: "city" },
|
|
104
|
+
{ name: "state" },
|
|
102
105
|
{
|
|
103
106
|
name: "postalCode",
|
|
104
|
-
|
|
105
|
-
rule: { pattern: {
|
|
106
|
-
value: /^\d{5}(-?\d{4})?$/,
|
|
107
|
-
message: "invalid zip code"
|
|
108
|
-
} }
|
|
107
|
+
rule: { pattern: /^\d{5}(-?\d{4})?$/ }
|
|
109
108
|
}
|
|
110
109
|
]
|
|
111
110
|
}
|
|
@@ -113,21 +112,22 @@ const countryDescriptors = {
|
|
|
113
112
|
/**
|
|
114
113
|
* Returns an ordered list of available country ISO codes (those with a descriptor).
|
|
115
114
|
*
|
|
116
|
-
* @returns
|
|
115
|
+
* @returns `BRA` and `USA`, then each library-backed country that has loaded, in the order
|
|
116
|
+
* `LIBRARY_COUNTRIES` declares them.
|
|
117
117
|
*/
|
|
118
118
|
function getAvailableCountries() {
|
|
119
|
-
return Object.keys(countryDescriptors);
|
|
119
|
+
return [...Object.keys(countryDescriptors), ...LIBRARY_COUNTRIES.filter((iso) => getLoadedLibraryCountry(iso))];
|
|
120
120
|
}
|
|
121
121
|
/**
|
|
122
122
|
* Retrieves the descriptor for a given country.
|
|
123
123
|
*
|
|
124
|
-
* @param iso - ISO 3166-1 alpha-3 country code (e.g., "BRA", "USA")
|
|
125
|
-
* @returns The
|
|
124
|
+
* @param iso - ISO 3166-1 alpha-3 country code (e.g., "BRA", "USA", "CAN")
|
|
125
|
+
* @returns The hand-written descriptor, else the library-backed one once loaded, or undefined if neither exists.
|
|
126
126
|
*/
|
|
127
127
|
function getCountryDescriptor(iso) {
|
|
128
|
-
return countryDescriptors[iso];
|
|
128
|
+
return countryDescriptors[iso] ?? getLoadedLibraryCountry(iso);
|
|
129
129
|
}
|
|
130
130
|
//#endregion
|
|
131
|
-
export { countryDescriptors, getAvailableCountries, getCountryDescriptor, satisfiesFieldPattern };
|
|
131
|
+
export { countryDescriptors, getAvailableCountries, getCountryDescriptor, satisfiesFieldPattern, satisfiesFieldRule };
|
|
132
132
|
|
|
133
133
|
//# sourceMappingURL=country-descriptors.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"country-descriptors.js","names":[],"sources":["../../../src/matter/shipping-address/country-descriptors.ts"],"sourcesContent":["/**\n * Country-adaptive shipping address form descriptors.\n *\n * Each descriptor declares the form shape (field set, order, and labels) for a specific country.\n * Descriptors are primarily data — field set, order, and labels — but a `FieldDescriptor` may also\n * carry `required` and `rule`, the two validation facts a country varies. `ShippingAddressFieldset`\n * is the only reader of `required` (specs/shipping-address-fieldset.md, Decision 3): a field whose\n * descriptor entry is absent is never rendered, so it can carry no requiredness at all. `rule.pattern`\n * has two readers: the fieldset, through react-hook-form, and `satisfiesFieldPattern` below, which\n * `BillingAddress` applies to its own submit gate and field errors. A descriptor also carries the two\n * strings a country's form speaks for itself — the Country label and the required-message suffix — so\n * no copy of the delivery form is hardcoded in the fieldset.\n * Country adaptation is data-driven; adding a country is a data-only change (a new descriptor entry).\n *\n * Canonical field names must be shared/consistent across descriptors for fields both countries have\n * (recipientName, postalCode, street, number, complement, city, state), so that switching countries\n * preserves shared field values in the batched draft state (Decision 1/Invariant).\n *\n * Per Decision 2, this narrows the RFC's descriptor scope to form shape plus two per-field validation\n * facts: the non-blank recipient name and, per country, the postal code's format. Both descriptors'\n * `postalCode` entry carries a `pattern` rule matching that country's CEP or ZIP shape; every other\n * field's `rule` stays undefined. There is no server-side postal-code validation to fall back on:\n * `shippingAddressUpdate` types `postalCode` as a plain `z.string()`, forwards it verbatim with\n * `clearAddressIfPostalCodeNotFound: false`, and has no try/catch around the upstream call, so a\n * malformed value still returns HTTP 200 with an empty `userErrors` array. This client-side rule is\n * the only postal-code guard in the system today. The two forms apply it on different triggers: the\n * billing form judges a touched field on every render (through `getFieldIssue`, which reads\n * `satisfiesFieldPattern`), touching it on blur or when a refused submit focuses it, while the\n * delivery form's react-hook-form rule runs on submit and re-validates after it. A future\n * @vtex/country-forms adoption remains deferred.\n */\n\nimport type { FieldRule, FieldRuleConstraint } from '../../particles/field/field.js';\n\n/**\n * A descriptor's validation rule: a `FieldRule` whose `pattern` always carries the message a failing value\n * shows. A bare `RegExp` is not accepted, so both forms of a country's address read the one message for\n * one rule and no consumer needs a fallback string of its own.\n */\nexport type FieldDescriptorRule = Omit<FieldRule, 'pattern'> &\n Readonly<{\n pattern?: FieldRuleConstraint<RegExp>;\n }>;\n\n/**\n * Describes a single form field within a country descriptor.\n *\n * @property name - Canonical field name (e.g., \"recipientName\", \"street\", \"neighborhood\").\n * Shared field names across descriptors enable draft state preservation on country switch.\n * @property label - Human-readable label for the field. Named `label` (not a key) to allow future\n * swapping with i18n keys once the codebase adopts an i18n layer.\n */\nexport type FieldDescriptor = Readonly<{\n name: string;\n label: string;\n /**\n * Whether the fieldset requires a non-blank value. Read only by ShippingAddressFieldset, which\n * turns it into the field contract's `rule`. A field whose descriptor entry is absent is not\n * rendered, so it can carry no requiredness at all — that is the point (Decision 3).\n */\n required?: boolean;\n /**\n * Extra react-hook-form rules for this field in this country: pattern, minLength, and so on.\n * Merged under the generated `required`. Only `postalCode` carries one today. `pattern` is also read\n * by `satisfiesFieldPattern`, for forms that are not built on react-hook-form, and its `message` is the\n * copy every form shows for it.\n */\n rule?: FieldDescriptorRule;\n}>;\n\n/**\n * Describes the form shape for a single country.\n *\n * @property iso - ISO 3166-1 alpha-3 country code (e.g., \"BRA\", \"USA\"). Must be a valid 3-letter code\n * matching the Address.country field type (plain String!, ISO alpha-3 by convention).\n * @property label - Human-readable country name for the country selector (e.g., \"Brazil\", \"United States\").\n * Named `label` (not a key) to allow future swapping with i18n keys.\n * @property countryLabel - Default label of the country selector on this country's form, in the language of\n * the form's other copy. An authored `label` on the control wins.\n * @property requiredSuffix - Ends a required field's message (`${label} ${requiredSuffix}`), in the same\n * language as the field labels.\n * @property fields - Ordered field set for this country. Order defines the visual order in the form.\n * Only fields this country uses are listed; absent fields are not rendered.\n */\nexport type CountryDescriptor = Readonly<{\n iso: string;\n label: string;\n /** Default label of the country selector on this country's form; an authored `label` wins. */\n countryLabel: string;\n /** Closes a required field's message: `${label} ${requiredSuffix}`. */\n requiredSuffix: string;\n fields: readonly FieldDescriptor[];\n}>;\n\n/**\n * Whether `value` satisfies the field's declared `rule.pattern`. A field with no pattern, or a blank\n * value (empty or whitespace-only: blankness is the required rule's business, and a form that trims\n * before judging, as `BillingAddress` does, reads such a value as required, never as a format failure),\n * satisfies it. A non-blank value is tested raw, equivalent to the predicate react-hook-form applies\n * (`value.match(regex) !== null`), so both forms agree on every non-blank value. Blank values are the\n * one deliberate divergence: react-hook-form skips the pattern only for `''`, so for `' '` the\n * delivery form runs the pattern and reports the format, while this helper reports it satisfied and\n * leaves the verdict to the required rule. A caller that needs react-hook-form's exact verdict on a\n * blank value must not rely on this helper alone. `search` ignores a global regex's `lastIndex`, so\n * the descriptor's shared regex is never written to.\n */\nexport function satisfiesFieldPattern(field: FieldDescriptor, value: string): boolean {\n const pattern = field.rule?.pattern;\n if (!pattern || value.trim() === '') return true;\n return value.search(pattern.value) !== -1;\n}\n\n/**\n * Brazil (BRA) address form descriptor.\n *\n * Field order:\n * 1. recipientName — recipient of the package\n * 2. street — street name\n * 3. number — street number\n * 4. complement — apartment, suite, etc. (optional per GraphQL but no form-level validation)\n * 5. neighborhood — bairro (BR-specific, not in US form)\n * 6. city — city name\n * 7. state — state/UF code (canonical name shared with US, though US calls it state abbreviation)\n * 8. postalCode — CEP format, client-validated via `rule` (8 digits, hyphen after the fifth optional)\n */\nconst brDescriptor: CountryDescriptor = {\n iso: 'BRA',\n label: 'Brasil',\n countryLabel: 'país',\n requiredSuffix: 'é obrigatório',\n fields: [\n { name: 'recipientName', label: 'nome do destinatário', required: true },\n { name: 'street', label: 'rua' },\n { name: 'number', label: 'número' },\n { name: 'complement', label: 'complemento (opcional)' },\n { name: 'neighborhood', label: 'bairro' },\n { name: 'city', label: 'cidade' },\n { name: 'state', label: 'estado' },\n {\n name: 'postalCode',\n label: 'cep',\n rule: { pattern: { value: /^\\d{5}-?\\d{3}$/, message: 'cep inválido' } },\n },\n ],\n};\n\n/**\n * United States (USA) address form descriptor.\n *\n * Field order:\n * 1. recipientName — recipient of the package\n * 2. street — street name\n * 3. number — street number\n * 4. complement — apartment, suite, etc. (optional per GraphQL but no form-level validation)\n * 5. city — city name\n * 6. state — state abbreviation (canonical name shared with BR for draft preservation)\n * 7. postalCode — ZIP/ZIP+4 format, client-validated via `rule` (5 digits, optional 4-digit\n * extension, hyphen before it optional either way — accepts both `02101-1234` and `021011234`)\n *\n * Note: neighborhood is not included in the US v1 form. If the shopper switches from BR to US,\n * the neighborhood value is retained in the draft but hidden (descriptor controls visibility only);\n * switching back to BR resurfaces it.\n */\nconst usaDescriptor: CountryDescriptor = {\n iso: 'USA',\n label: 'United States',\n countryLabel: 'country',\n requiredSuffix: 'is required',\n fields: [\n { name: 'recipientName', label: 'recipient name', required: true },\n { name: 'street', label: 'street' },\n { name: 'number', label: 'number' },\n { name: 'complement', label: 'apartment, suite, etc. (optional)' },\n { name: 'city', label: 'city' },\n { name: 'state', label: 'state' },\n {\n name: 'postalCode',\n label: 'zip code',\n rule: { pattern: { value: /^\\d{5}(-?\\d{4})?$/, message: 'invalid zip code' } },\n },\n ],\n};\n\n/**\n * All available country descriptors, keyed by ISO alpha-3 code.\n *\n * The country selector lists only countries with a descriptor; a descriptor-less country is unreachable.\n * Adding a country is a data-only change: add a new CountryDescriptor entry here.\n */\nexport const countryDescriptors: Readonly<Record<string, CountryDescriptor>> = {\n BRA: brDescriptor,\n USA: usaDescriptor,\n};\n\n/**\n * Returns an ordered list of available country ISO codes (those with a descriptor).\n *\n * @returns ISO alpha-3 codes of all implemented countries, in definition order.\n */\nexport function getAvailableCountries(): readonly string[] {\n return Object.keys(countryDescriptors);\n}\n\n/**\n * Retrieves the descriptor for a given country.\n *\n * @param iso - ISO 3166-1 alpha-3 country code (e.g., \"BRA\", \"USA\")\n * @returns The CountryDescriptor for the country, or undefined if not implemented.\n */\nexport function getCountryDescriptor(iso: string): CountryDescriptor | undefined {\n return countryDescriptors[iso];\n}\n"],"mappings":";;;;;;;;;;;;;AA0GA,SAAgB,sBAAsB,OAAwB,OAAwB;CACpF,MAAM,UAAU,MAAM,MAAM;CAC5B,IAAI,CAAC,WAAW,MAAM,KAAK,MAAM,IAAI,OAAO;CAC5C,OAAO,MAAM,OAAO,QAAQ,KAAK,MAAM;AACzC;;;;;;;AA+EA,MAAa,qBAAkE;CAC7E,KAAK;EAhEL,KAAK;EACL,OAAO;EACP,cAAc;EACd,gBAAgB;EAChB,QAAQ;GACN;IAAE,MAAM;IAAiB,OAAO;IAAwB,UAAU;GAAK;GACvE;IAAE,MAAM;IAAU,OAAO;GAAM;GAC/B;IAAE,MAAM;IAAU,OAAO;GAAS;GAClC;IAAE,MAAM;IAAc,OAAO;GAAyB;GACtD;IAAE,MAAM;IAAgB,OAAO;GAAS;GACxC;IAAE,MAAM;IAAQ,OAAO;GAAS;GAChC;IAAE,MAAM;IAAS,OAAO;GAAS;GACjC;IACE,MAAM;IACN,OAAO;IACP,MAAM,EAAE,SAAS;KAAE,OAAO;KAAkB,SAAS;IAAe,EAAE;GACxE;EACF;CA+CK;CACL,KAAK;EA3BL,KAAK;EACL,OAAO;EACP,cAAc;EACd,gBAAgB;EAChB,QAAQ;GACN;IAAE,MAAM;IAAiB,OAAO;IAAkB,UAAU;GAAK;GACjE;IAAE,MAAM;IAAU,OAAO;GAAS;GAClC;IAAE,MAAM;IAAU,OAAO;GAAS;GAClC;IAAE,MAAM;IAAc,OAAO;GAAoC;GACjE;IAAE,MAAM;IAAQ,OAAO;GAAO;GAC9B;IAAE,MAAM;IAAS,OAAO;GAAQ;GAChC;IACE,MAAM;IACN,OAAO;IACP,MAAM,EAAE,SAAS;KAAE,OAAO;KAAqB,SAAS;IAAmB,EAAE;GAC/E;EACF;CAWK;AACP;;;;;;AAOA,SAAgB,wBAA2C;CACzD,OAAO,OAAO,KAAK,kBAAkB;AACvC;;;;;;;AAQA,SAAgB,qBAAqB,KAA4C;CAC/E,OAAO,mBAAmB;AAC5B"}
|
|
1
|
+
{"version":3,"file":"country-descriptors.js","names":[],"sources":["../../../src/matter/shipping-address/country-descriptors.ts"],"sourcesContent":["/**\n * Country-adaptive shipping address form descriptors.\n *\n * Each descriptor declares the form shape (field set and order) for a specific country. Descriptors are\n * structure only: a `FieldDescriptor` may also carry `required` and `rule`, the two validation facts a\n * country varies, and never a string. `ShippingAddressFieldset` is the only reader of `required`\n * (specs/shipping-address-fieldset.md, Decision 3): a field whose descriptor entry is absent is never\n * rendered, so it can carry no requiredness at all. `rule.pattern` has two readers: the fieldset, through\n * react-hook-form, and `satisfiesFieldPattern` below, which `BillingAddress` applies to its own submit gate\n * and field errors. Every word a form shows (field labels, the required and invalid messages, the country\n * names) comes from the form's own message fields and `Intl.DisplayNames`, so no copy lives here.\n * Country adaptation is data-driven. `BRA` and `USA` are the two entries written here; adding another\n * hand-written country is a new entry in `countryDescriptors`. The countries `@vtex/country-forms` covers\n * are not copied: `library-country-descriptors.ts` builds `CAN` and `MEX` descriptors from the library's own\n * rules at runtime, and `getCountryDescriptor` and `getAvailableCountries` read them once they have loaded.\n *\n * Canonical field names must be shared/consistent across descriptors for fields both countries have\n * (recipientName, postalCode, street, number, complement, city, state), so that switching countries\n * preserves shared field values in the batched draft state (Decision 1/Invariant).\n *\n * Per Decision 2, this narrows the RFC's descriptor scope to form shape plus two per-field validation\n * facts: the non-blank recipient name and, per country, the postal code's format. Both descriptors'\n * `postalCode` entry carries a `pattern` rule matching that country's CEP or ZIP shape; every other\n * field's `rule` stays undefined. A library-backed country's `postalCode` entry carries a `validate`\n * predicate instead, since the library's rule is not a regular expression. There is no server-side\n * postal-code validation to fall back on: `shippingAddressUpdate` types `postalCode` as a plain\n * `z.string()`, forwards it verbatim with `clearAddressIfPostalCodeNotFound: false`, and has no\n * try/catch around the upstream call, so a malformed value still returns HTTP 200 with an empty\n * `userErrors` array. This client-side rule is the only postal-code guard in the system today. The two\n * forms apply it on different triggers: the billing form judges a touched field on every render (through\n * `getFieldIssue`, which reads `satisfiesFieldRule`), touching it on blur or when a refused submit focuses\n * it, while the delivery form's react-hook-form rule runs on submit and re-validates after it.\n */\n\nimport { getLoadedLibraryCountry, LIBRARY_COUNTRIES } from './library-country-descriptors.js';\n\n/**\n * A descriptor's validation rule: structure only. A form that applies it supplies the message a failing\n * value shows, from its own message fields, so both forms of a country's address judge one rule and word\n * it in the store's language. `validate` passes on `true` and refuses on `false`.\n */\nexport type FieldDescriptorRule = Readonly<{\n pattern?: RegExp;\n validate?: (value: string) => boolean;\n}>;\n\n/**\n * Describes a single form field within a country descriptor.\n *\n * @property name - Canonical field name (e.g., \"recipientName\", \"street\", \"neighborhood\").\n * Shared field names across descriptors enable draft state preservation on country switch.\n */\nexport type FieldDescriptor = Readonly<{\n name: string;\n /**\n * Whether the fieldset requires a non-blank value. Read only by ShippingAddressFieldset, which\n * turns it into the field contract's `rule`. A field whose descriptor entry is absent is not\n * rendered, so it can carry no requiredness at all — that is the point (Decision 3).\n */\n required?: boolean;\n /**\n * The format this field takes in this country. Only `postalCode` carries one today. `pattern` is also\n * read by `satisfiesFieldPattern`, for forms that are not built on react-hook-form.\n */\n rule?: FieldDescriptorRule;\n /**\n * Applied to the value a form submits for this field. Absent means the value is sent as typed. Only a\n * library-backed `postalCode` carries one: the library's stored form (`K1A 0A9`).\n */\n normalize?: (value: string) => string;\n}>;\n\n/**\n * Describes the form shape for a single country.\n *\n * @property iso - ISO 3166-1 alpha-3 country code (e.g., \"BRA\", \"USA\"). Must be a valid 3-letter code\n * matching the Address.country field type (plain String!, ISO alpha-3 by convention). The\n * country's name is `countryDisplayName(iso)` in the store's locale.\n * @property fields - Ordered field set for this country. Order defines the visual order in the form.\n * Only fields this country uses are listed; absent fields are not rendered.\n */\nexport type CountryDescriptor = Readonly<{\n iso: string;\n fields: readonly FieldDescriptor[];\n}>;\n\n/**\n * Whether `value` satisfies the field's declared `rule.pattern`. A field with no pattern, or a blank\n * value (empty or whitespace-only: blankness is the required rule's business, and a form that trims\n * before judging, as `BillingAddress` does, reads such a value as required, never as a format failure),\n * satisfies it. A non-blank value is tested raw, equivalent to the predicate react-hook-form applies\n * (`value.match(regex) !== null`), so both forms agree on every non-blank value. Blank values are the\n * one deliberate divergence: react-hook-form skips the pattern only for `''`, so for `' '` the\n * delivery form runs the pattern and reports the format, while this helper reports it satisfied and\n * leaves the verdict to the required rule. A caller that needs react-hook-form's exact verdict on a\n * blank value must not rely on this helper alone. `search` ignores a global regex's `lastIndex`, so\n * the descriptor's shared regex is never written to.\n */\nexport function satisfiesFieldPattern(field: FieldDescriptor, value: string): boolean {\n const pattern = field.rule?.pattern;\n if (!pattern || value.trim() === '') return true;\n return value.search(pattern) !== -1;\n}\n\n/**\n * Whether `value` satisfies everything the field's `rule` can judge on its own: `rule.pattern` (as\n * `satisfiesFieldPattern`) and a function-valued `rule.validate` (anything else is refused). A blank\n * value is satisfied, for the same reason: blankness is the required rule's business, so a validator is\n * never called with one. A validator passes only on `true`, so both forms agree on every non-blank value.\n */\nexport function satisfiesFieldRule(field: FieldDescriptor, value: string): boolean {\n if (!satisfiesFieldPattern(field, value)) return false;\n const validate = field.rule?.validate;\n if (validate === undefined || value.trim() === '') return true;\n // Anything but a function is refused, never waved through: this keeps a descriptor that dodged the type\n // from enabling a form the delivery form would refuse.\n if (typeof validate !== 'function') return false;\n return validate(value) === true;\n}\n\n/**\n * Brazil (BRA) address form descriptor.\n *\n * Field order:\n * 1. recipientName — recipient of the package\n * 2. street — street name\n * 3. number — street number\n * 4. complement — apartment, suite, etc. (optional per GraphQL but no form-level validation)\n * 5. neighborhood — bairro (BR-specific, not in US form)\n * 6. city — city name\n * 7. state — state/UF code (canonical name shared with US, though US calls it state abbreviation)\n * 8. postalCode — CEP format, client-validated via `rule` (8 digits, hyphen after the fifth optional)\n */\nconst brDescriptor: CountryDescriptor = {\n iso: 'BRA',\n fields: [\n { name: 'recipientName', required: true },\n { name: 'street' },\n { name: 'number' },\n { name: 'complement' },\n { name: 'neighborhood' },\n { name: 'city' },\n { name: 'state' },\n { name: 'postalCode', rule: { pattern: /^\\d{5}-?\\d{3}$/ } },\n ],\n};\n\n/**\n * United States (USA) address form descriptor.\n *\n * Field order:\n * 1. recipientName — recipient of the package\n * 2. street — street name\n * 3. number — street number\n * 4. complement — apartment, suite, etc. (optional per GraphQL but no form-level validation)\n * 5. city — city name\n * 6. state — state abbreviation (canonical name shared with BR for draft preservation)\n * 7. postalCode — ZIP/ZIP+4 format, client-validated via `rule` (5 digits, optional 4-digit\n * extension, hyphen before it optional either way — accepts both `02101-1234` and `021011234`)\n *\n * Note: neighborhood is not included in the US v1 form. If the shopper switches from BR to US,\n * the neighborhood value is retained in the draft but hidden (descriptor controls visibility only);\n * switching back to BR resurfaces it.\n */\nconst usaDescriptor: CountryDescriptor = {\n iso: 'USA',\n fields: [\n { name: 'recipientName', required: true },\n { name: 'street' },\n { name: 'number' },\n { name: 'complement' },\n { name: 'city' },\n { name: 'state' },\n { name: 'postalCode', rule: { pattern: /^\\d{5}(-?\\d{4})?$/ } },\n ],\n};\n\n/**\n * The hand-written country descriptors, keyed by ISO alpha-3 code: `BRA` and `USA`.\n *\n * The country selector lists only countries with a descriptor; a descriptor-less country is unreachable.\n * The countries `@vtex/country-forms` covers are not here: they are built from the library at runtime and\n * read through `getCountryDescriptor` (see `library-country-descriptors.ts`).\n */\nexport const countryDescriptors: Readonly<Record<string, CountryDescriptor>> = {\n BRA: brDescriptor,\n USA: usaDescriptor,\n};\n\n/**\n * Returns an ordered list of available country ISO codes (those with a descriptor).\n *\n * @returns `BRA` and `USA`, then each library-backed country that has loaded, in the order\n * `LIBRARY_COUNTRIES` declares them.\n */\nexport function getAvailableCountries(): readonly string[] {\n return [...Object.keys(countryDescriptors), ...LIBRARY_COUNTRIES.filter((iso) => getLoadedLibraryCountry(iso))];\n}\n\n/**\n * Retrieves the descriptor for a given country.\n *\n * @param iso - ISO 3166-1 alpha-3 country code (e.g., \"BRA\", \"USA\", \"CAN\")\n * @returns The hand-written descriptor, else the library-backed one once loaded, or undefined if neither exists.\n */\nexport function getCountryDescriptor(iso: string): CountryDescriptor | undefined {\n return countryDescriptors[iso] ?? getLoadedLibraryCountry(iso);\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAkGA,SAAgB,sBAAsB,OAAwB,OAAwB;CACpF,MAAM,UAAU,MAAM,MAAM;CAC5B,IAAI,CAAC,WAAW,MAAM,KAAK,MAAM,IAAI,OAAO;CAC5C,OAAO,MAAM,OAAO,OAAO,MAAM;AACnC;;;;;;;AAQA,SAAgB,mBAAmB,OAAwB,OAAwB;CACjF,IAAI,CAAC,sBAAsB,OAAO,KAAK,GAAG,OAAO;CACjD,MAAM,WAAW,MAAM,MAAM;CAC7B,IAAI,aAAa,KAAA,KAAa,MAAM,KAAK,MAAM,IAAI,OAAO;CAG1D,IAAI,OAAO,aAAa,YAAY,OAAO;CAC3C,OAAO,SAAS,KAAK,MAAM;AAC7B;;;;;;;;AAkEA,MAAa,qBAAkE;CAC7E,KAAK;EAnDL,KAAK;EACL,QAAQ;GACN;IAAE,MAAM;IAAiB,UAAU;GAAK;GACxC,EAAE,MAAM,SAAS;GACjB,EAAE,MAAM,SAAS;GACjB,EAAE,MAAM,aAAa;GACrB,EAAE,MAAM,eAAe;GACvB,EAAE,MAAM,OAAO;GACf,EAAE,MAAM,QAAQ;GAChB;IAAE,MAAM;IAAc,MAAM,EAAE,SAAS,iBAAiB;GAAE;EAC5D;CAyCK;CACL,KAAK;EArBL,KAAK;EACL,QAAQ;GACN;IAAE,MAAM;IAAiB,UAAU;GAAK;GACxC,EAAE,MAAM,SAAS;GACjB,EAAE,MAAM,SAAS;GACjB,EAAE,MAAM,aAAa;GACrB,EAAE,MAAM,OAAO;GACf,EAAE,MAAM,QAAQ;GAChB;IAAE,MAAM;IAAc,MAAM,EAAE,SAAS,oBAAoB;GAAE;EAC/D;CAYK;AACP;;;;;;;AAQA,SAAgB,wBAA2C;CACzD,OAAO,CAAC,GAAG,OAAO,KAAK,kBAAkB,GAAG,GAAG,kBAAkB,QAAQ,QAAQ,wBAAwB,GAAG,CAAC,CAAC;AAChH;;;;;;;AAQA,SAAgB,qBAAqB,KAA4C;CAC/E,OAAO,mBAAmB,QAAQ,wBAAwB,GAAG;AAC/D"}
|