@vtex/faststore 0.0.8 → 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 +101 -0
- package/README.md +0 -1
- package/dist/actions.json +48 -1
- package/dist/catalog.json +1397 -1594
- 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 +67 -56
- package/dist/components.js +64 -53
- package/dist/content-types.json +5886 -555
- package/dist/core/dist/define-component.d.ts +7 -2
- 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-entry-point.md +4 -4
- package/dist/docs/account-menu.md +16 -17
- package/dist/docs/account-saved-cards.md +306 -0
- 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 +157 -0
- package/dist/docs/buy-button.md +7 -8
- package/dist/docs/card-entry.md +42 -29
- package/dist/docs/card-summary.md +185 -0
- package/dist/docs/cardholder-document.md +5 -3
- package/dist/docs/carousel.md +5 -4
- package/dist/docs/cart-destination.md +251 -0
- package/dist/docs/cart-item.md +48 -15
- package/dist/docs/cart-totalizer.md +8 -6
- package/dist/docs/cart.md +240 -39
- 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/footer-social.md +1 -1
- package/dist/docs/form.md +54 -18
- package/dist/docs/free-shipping-progress.md +18 -13
- package/dist/docs/identity-edit.md +8 -2
- package/dist/docs/identity-summary.md +18 -5
- package/dist/docs/incentives.md +2 -2
- package/dist/docs/installments.md +46 -32
- package/dist/docs/logo.md +1 -1
- 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 +32 -6
- package/dist/docs/payment-methods.md +2 -2
- 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 +14 -10
- package/dist/docs/product-list.md +7 -6
- package/dist/docs/product-shelf.md +7 -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/select.md +3 -4
- 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 +5 -3
- package/dist/docs/sign-in.md +173 -0
- package/dist/docs/toast.md +45 -16
- package/dist/docs/variant-selector.md +15 -11
- package/dist/graphql/types.d.ts +4 -14
- package/dist/index.d.ts +3 -2
- package/dist/internal/client.d.ts +4 -3
- package/dist/internal/client.js +5 -2
- package/dist/internal/server.d.ts +10 -5
- package/dist/internal/server.js +9 -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 +463 -0
- package/dist/matter/account-saved-cards/account-saved-cards.js +1011 -0
- package/dist/matter/account-saved-cards/account-saved-cards.js.map +1 -0
- 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 +11 -1
- package/dist/matter/cart/cart-namespace.js +11 -1
- package/dist/matter/cart/cart-namespace.js.map +1 -1
- package/dist/matter/cart/cart.d.ts +416 -45
- package/dist/matter/cart/cart.js +782 -101
- package/dist/matter/cart/cart.js.map +1 -1
- package/dist/matter/cart-destination/cart-destination.d.ts +233 -0
- package/dist/matter/cart-destination/cart-destination.js +703 -0
- package/dist/matter/cart-destination/cart-destination.js.map +1 -0
- package/dist/matter/cart-item/cart-item.d.ts +145 -16
- package/dist/matter/cart-item/cart-item.js +230 -54
- 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 +49 -19
- 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 +85 -10
- package/dist/matter/free-shipping-progress/free-shipping-progress.js +153 -53
- 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 +8 -3
- package/dist/matter/product-shelf/product-shelf.js +111 -10
- 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 +251 -25
- package/dist/matter/shipping-simulator/shipping-simulator.js +261 -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 +59 -0
- package/dist/particles/binding-switcher/binding-switcher.js +194 -0
- package/dist/particles/binding-switcher/binding-switcher.js.map +1 -0
- package/dist/particles/card-summary/card-network.d.ts +17 -0
- package/dist/particles/card-summary/card-network.js +25 -0
- package/dist/particles/card-summary/card-network.js.map +1 -0
- package/dist/particles/card-summary/card-summary.d.ts +113 -0
- package/dist/particles/card-summary/card-summary.js +221 -0
- package/dist/particles/card-summary/card-summary.js.map +1 -0
- 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/footer-social/footer-social.d.ts +1 -1
- package/dist/particles/footer-social/footer-social.js +1 -1
- package/dist/particles/footer-social/footer-social.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/incentives/incentives.d.ts +1 -1
- package/dist/particles/incentives/incentives.js +1 -1
- package/dist/particles/incentives/incentives.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/payment-methods/payment-methods.d.ts +1 -1
- package/dist/particles/payment-methods/payment-methods.js +1 -1
- package/dist/particles/payment-methods/payment-methods.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 +49 -9
- package/dist/particles/toast/toast.js +81 -15
- package/dist/particles/toast/toast.js.map +1 -1
- package/dist/schema.graphql +461 -89
- package/dist/sdk/actions/index.d.ts +3 -1
- package/dist/sdk/actions/index.js +3 -1
- package/dist/sdk/actions/locale-action-provider.d.ts +16 -0
- package/dist/sdk/actions/locale-action-provider.js +20 -0
- package/dist/sdk/actions/locale-action-provider.js.map +1 -0
- package/dist/sdk/actions/saved-card-action-provider.d.ts +12 -0
- package/dist/sdk/actions/saved-card-action-provider.js +16 -0
- package/dist/sdk/actions/saved-card-action-provider.js.map +1 -0
- package/dist/sdk/actions/types.d.ts +6 -2
- 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/cache/storefront-cache.js +2 -1
- package/dist/sdk/cache/storefront-cache.js.map +1 -1
- package/dist/sdk/cart/operations.d.ts +2 -1
- package/dist/sdk/cart/operations.js +55 -1
- package/dist/sdk/cart/operations.js.map +1 -1
- package/dist/sdk/cart/optimistic.d.ts +30 -7
- package/dist/sdk/cart/optimistic.js +78 -41
- package/dist/sdk/cart/optimistic.js.map +1 -1
- package/dist/sdk/cart/use-cart-actions.d.ts +7 -0
- package/dist/sdk/cart/use-cart-actions.js +61 -15
- package/dist/sdk/cart/use-cart-actions.js.map +1 -1
- package/dist/sdk/cart/use-cart-sync-sales-channel.js +70 -0
- package/dist/sdk/cart/use-cart-sync-sales-channel.js.map +1 -0
- 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 +16 -12
- package/dist/sdk/generated/gql.js.map +1 -1
- package/dist/sdk/generated/graphql.d.ts +197 -51
- package/dist/sdk/generated/graphql.js +2318 -91
- 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/locale/binding-switch-provider.d.ts +13 -0
- package/dist/sdk/locale/binding-switch-provider.js +20 -0
- package/dist/sdk/locale/binding-switch-provider.js.map +1 -0
- package/dist/sdk/locale/use-locale-actions.d.ts +16 -0
- package/dist/sdk/locale/use-locale-actions.js +70 -0
- package/dist/sdk/locale/use-locale-actions.js.map +1 -0
- package/dist/sdk/navigation/binding-switch.d.ts +39 -0
- package/dist/sdk/navigation/binding-switch.js +129 -0
- package/dist/sdk/navigation/binding-switch.js.map +1 -0
- package/dist/sdk/navigation/decode-route-params.d.ts +11 -0
- package/dist/sdk/navigation/decode-route-params.js +32 -0
- package/dist/sdk/navigation/decode-route-params.js.map +1 -0
- 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/saved-cards/operations.d.ts +16 -0
- package/dist/sdk/saved-cards/operations.js +41 -0
- package/dist/sdk/saved-cards/operations.js.map +1 -0
- package/dist/sdk/saved-cards/use-saved-card-actions.d.ts +26 -0
- package/dist/sdk/saved-cards/use-saved-card-actions.js +86 -0
- package/dist/sdk/saved-cards/use-saved-card-actions.js.map +1 -0
- 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 +8 -5
- package/dist/sdk/seo/page-alternates.js +11 -14
- 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/shipping-operations.d.ts +1 -1
- package/dist/sdk/shipping/shipping-operations.js +13 -0
- package/dist/sdk/shipping/shipping-operations.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/sdk/state/cart.d.ts +9 -2
- package/dist/sdk/state/cart.js +9 -2
- package/dist/sdk/state/cart.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 +9 -2
- 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 -164
- package/dist/sdk/i18n/messages/en-US.js.map +0 -1
- package/dist/sdk/i18n/messages/en-US.json +0 -159
- package/dist/sdk/i18n/messages/en-US.json.d.ts +0 -163
- package/dist/sdk/i18n/messages/es-ES.js +0 -164
- package/dist/sdk/i18n/messages/es-ES.js.map +0 -1
- package/dist/sdk/i18n/messages/es-ES.json +0 -159
- package/dist/sdk/i18n/messages/pt-BR.js +0 -164
- package/dist/sdk/i18n/messages/pt-BR.js.map +0 -1
- package/dist/sdk/i18n/messages/pt-BR.json +0 -159
- 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
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"operations.js","names":[],"sources":["../../../src/sdk/saved-cards/operations.ts"],"sourcesContent":["import type { TypedDocumentNode } from '@apollo/client';\nimport { gql } from '../graphql-tag.js';\n\n/**\n * Saved-card GraphQL operations: the account's writes against the shopper's saved cards.\n *\n * Authored with the serializable `gql` tag and excluded from the codegen `documents` glob (see\n * `codegen.ts`), like the identity operations: the pluck would otherwise duplicate these\n * hand-written types in `sdk/generated/`.\n */\n\n/** A single domain error returned by a saved-card write. */\nexport type SavedCardUserError = Readonly<{ code: string; message: string }>;\n\n/** The account's list of saved cards, as `customerSavedCards` reads it. */\nexport type CustomerSavedCardsEntity = Readonly<{\n __typename?: 'CustomerSavedCards';\n id: string;\n cards: ReadonlyArray<{\n __typename?: 'CustomerSavedCard';\n id: string;\n network: { __typename?: 'CardNetwork'; id: string; name: string };\n lastDigits: string;\n expiry: { __typename?: 'CardExpiry'; month: number; year: number } | null;\n status: 'USABLE' | 'EXPIRED' | 'UNUSABLE';\n }>;\n}>;\n\nexport type CustomerSavedCardRemoveMutation = {\n customerSavedCardRemove: {\n __typename?: 'CustomerSavedCardRemovePayload';\n customerSavedCards: CustomerSavedCardsEntity | null;\n userErrors: SavedCardUserError[];\n };\n};\n\nexport type CustomerSavedCardRemoveMutationVariables = {\n id: string;\n};\n\n/**\n * Selects every field the read exposes: the payload replaces the cached list, so a partial\n * selection would leave the remaining cards holding stale fields.\n */\nexport const CUSTOMER_SAVED_CARD_REMOVE: TypedDocumentNode<\n CustomerSavedCardRemoveMutation,\n CustomerSavedCardRemoveMutationVariables\n> = gql`\n mutation CustomerSavedCardRemove($id: ID!) {\n customerSavedCardRemove(id: $id) {\n __typename\n customerSavedCards {\n __typename\n id\n cards {\n __typename\n id\n network {\n __typename\n id\n name\n }\n lastDigits\n expiry {\n __typename\n month\n year\n }\n status\n }\n }\n userErrors {\n code\n message\n }\n }\n }\n`;\n"],"mappings":";;;;;;AA4CA,MAAa,6BAGT,GAAG"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import "../../core/dist/index.js";
|
|
2
|
+
import { SavedCardUserError } from "./operations.js";
|
|
3
|
+
//#region src/sdk/saved-cards/use-saved-card-actions.d.ts
|
|
4
|
+
/**
|
|
5
|
+
* The `savedCard` action namespace — the account's writes against the shopper's saved cards
|
|
6
|
+
* (docs/storefront-core/adr-0010-actions-own-the-write-side.md).
|
|
7
|
+
*
|
|
8
|
+
* Every write is pessimistic: the caller awaits the server, and the returned `CustomerSavedCards`
|
|
9
|
+
* replaces the cached list, so the removed row leaves every subscriber with no refetch. A failure
|
|
10
|
+
* leaves the cache as it was.
|
|
11
|
+
*
|
|
12
|
+
* The removal prompt cannot close while a removal is pending, and the browser client has no request
|
|
13
|
+
* timeout, so `remove` bounds its own request. Past the bound it aborts through the `AbortSignal` in
|
|
14
|
+
* `context.fetchOptions` (honored by `HttpLink`) and resolves `NETWORK_ERROR`. The mutation skips
|
|
15
|
+
* the cache, and `remove` writes only an answer that arrived inside the bound, so a late answer from a
|
|
16
|
+
* link that ignores the abort cannot change the list after the shopper was told it failed.
|
|
17
|
+
*/
|
|
18
|
+
export type SavedCardRemovePayload = Readonly<{
|
|
19
|
+
id: string;
|
|
20
|
+
}>;
|
|
21
|
+
export type SavedCardActions = Readonly<{
|
|
22
|
+
/** Removes one saved card. Resolves to the payload's `userErrors` (empty on success). */
|
|
23
|
+
remove: (payload: SavedCardRemovePayload) => Promise<readonly SavedCardUserError[]>;
|
|
24
|
+
}>;
|
|
25
|
+
//#endregion
|
|
26
|
+
//# sourceMappingURL=use-saved-card-actions.d.ts.map
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { defineActions } from "../../core/dist/actions/registry.js";
|
|
3
|
+
import "../../core/dist/index.js";
|
|
4
|
+
import { loggableErrorName } from "../../utils/loggable-error-name.js";
|
|
5
|
+
import { CUSTOMER_SAVED_CARD_REMOVE } from "./operations.js";
|
|
6
|
+
import { useApolloClient, useMutation } from "@apollo/client/react";
|
|
7
|
+
import { useCallback, useMemo } from "react";
|
|
8
|
+
import { CombinedGraphQLErrors } from "@apollo/client/errors";
|
|
9
|
+
//#region src/sdk/saved-cards/use-saved-card-actions.ts
|
|
10
|
+
const SAVED_CARD_REMOVE_TIMEOUT_MS = 45e3;
|
|
11
|
+
var SavedCardRemoveTimeoutError = class extends Error {
|
|
12
|
+
constructor() {
|
|
13
|
+
super("Saved card removal timed out.");
|
|
14
|
+
this.name = "SavedCardRemoveTimeoutError";
|
|
15
|
+
}
|
|
16
|
+
};
|
|
17
|
+
const NETWORK_ERROR = {
|
|
18
|
+
code: "NETWORK_ERROR",
|
|
19
|
+
message: "Unable to remove the card. Please try again."
|
|
20
|
+
};
|
|
21
|
+
const UNAUTHORIZED_ERROR = {
|
|
22
|
+
code: "UNAUTHORIZED",
|
|
23
|
+
message: "Your session has expired. Please sign in again."
|
|
24
|
+
};
|
|
25
|
+
/** An expired session needs sign-in, not a retry, so it keeps its own code. */
|
|
26
|
+
function toSavedCardError(error) {
|
|
27
|
+
if (CombinedGraphQLErrors.is(error)) {
|
|
28
|
+
if (error.errors.some(({ extensions }) => extensions?.code === "UNAUTHORIZED")) return UNAUTHORIZED_ERROR;
|
|
29
|
+
}
|
|
30
|
+
return NETWORK_ERROR;
|
|
31
|
+
}
|
|
32
|
+
function useSavedCardActions() {
|
|
33
|
+
const client = useApolloClient();
|
|
34
|
+
const [removeMutation] = useMutation(CUSTOMER_SAVED_CARD_REMOVE);
|
|
35
|
+
const remove = useCallback(async ({ id }) => {
|
|
36
|
+
const controller = new AbortController();
|
|
37
|
+
let timer;
|
|
38
|
+
const timeout = new Promise((_, reject) => {
|
|
39
|
+
timer = setTimeout(() => {
|
|
40
|
+
controller.abort();
|
|
41
|
+
reject(new SavedCardRemoveTimeoutError());
|
|
42
|
+
}, SAVED_CARD_REMOVE_TIMEOUT_MS);
|
|
43
|
+
});
|
|
44
|
+
try {
|
|
45
|
+
const result = await Promise.race([removeMutation({
|
|
46
|
+
variables: { id },
|
|
47
|
+
context: { fetchOptions: { signal: controller.signal } },
|
|
48
|
+
fetchPolicy: "no-cache"
|
|
49
|
+
}), timeout]);
|
|
50
|
+
if (result.data) client.cache.writeQuery({
|
|
51
|
+
id: "ROOT_MUTATION",
|
|
52
|
+
query: CUSTOMER_SAVED_CARD_REMOVE,
|
|
53
|
+
variables: { id },
|
|
54
|
+
data: result.data
|
|
55
|
+
});
|
|
56
|
+
const payload = result.data?.customerSavedCardRemove;
|
|
57
|
+
return payload ? payload.userErrors : [NETWORK_ERROR];
|
|
58
|
+
} catch (error) {
|
|
59
|
+
console.error("[savedCard.remove] customerSavedCardRemove failed", loggableErrorName(error));
|
|
60
|
+
return [toSavedCardError(error)];
|
|
61
|
+
} finally {
|
|
62
|
+
clearTimeout(timer);
|
|
63
|
+
}
|
|
64
|
+
}, [client, removeMutation]);
|
|
65
|
+
return useMemo(() => ({ remove }), [remove]);
|
|
66
|
+
}
|
|
67
|
+
defineActions({
|
|
68
|
+
namespace: "savedCard",
|
|
69
|
+
description: "Mutation-backed writes against the signed-in shopper's saved cards.",
|
|
70
|
+
provider: {
|
|
71
|
+
name: "SavedCardActionProvider",
|
|
72
|
+
importPath: "@vtex/faststore/internal/client"
|
|
73
|
+
},
|
|
74
|
+
methods: { remove: {
|
|
75
|
+
description: "Remove one saved card (async strategy). The row stays until the server answers.",
|
|
76
|
+
payload: [{
|
|
77
|
+
name: "id",
|
|
78
|
+
type: "string"
|
|
79
|
+
}]
|
|
80
|
+
} },
|
|
81
|
+
use: useSavedCardActions
|
|
82
|
+
});
|
|
83
|
+
//#endregion
|
|
84
|
+
export { SAVED_CARD_REMOVE_TIMEOUT_MS, useSavedCardActions };
|
|
85
|
+
|
|
86
|
+
//# sourceMappingURL=use-saved-card-actions.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"use-saved-card-actions.js","names":[],"sources":["../../../src/sdk/saved-cards/use-saved-card-actions.ts"],"sourcesContent":["'use client';\n\nimport { CombinedGraphQLErrors } from '@apollo/client/errors';\nimport { useApolloClient, useMutation } from '@apollo/client/react';\nimport { defineActions } from '@vtex/fs-core';\nimport { useCallback, useMemo } from 'react';\nimport { loggableErrorName } from '../../utils/loggable-error-name.js';\nimport { CUSTOMER_SAVED_CARD_REMOVE, type SavedCardUserError } from './operations.js';\n\n/**\n * The `savedCard` action namespace — the account's writes against the shopper's saved cards\n * (docs/storefront-core/adr-0010-actions-own-the-write-side.md).\n *\n * Every write is pessimistic: the caller awaits the server, and the returned `CustomerSavedCards`\n * replaces the cached list, so the removed row leaves every subscriber with no refetch. A failure\n * leaves the cache as it was.\n *\n * The removal prompt cannot close while a removal is pending, and the browser client has no request\n * timeout, so `remove` bounds its own request. Past the bound it aborts through the `AbortSignal` in\n * `context.fetchOptions` (honored by `HttpLink`) and resolves `NETWORK_ERROR`. The mutation skips\n * the cache, and `remove` writes only an answer that arrived inside the bound, so a late answer from a\n * link that ignores the abort cannot change the list after the shopper was told it failed.\n */\n\nexport type SavedCardRemovePayload = Readonly<{ id: string }>;\n\nexport type SavedCardActions = Readonly<{\n /** Removes one saved card. Resolves to the payload's `userErrors` (empty on success). */\n remove: (payload: SavedCardRemovePayload) => Promise<readonly SavedCardUserError[]>;\n}>;\n\n// Above the resolver's own upstream budget (10 s per call, up to four in series), so the bound fires\n// only on a genuine hang.\nexport const SAVED_CARD_REMOVE_TIMEOUT_MS = 45_000;\n\nclass SavedCardRemoveTimeoutError extends Error {\n constructor() {\n super('Saved card removal timed out.');\n this.name = 'SavedCardRemoveTimeoutError';\n }\n}\n\nconst NETWORK_ERROR: SavedCardUserError = {\n code: 'NETWORK_ERROR',\n message: 'Unable to remove the card. Please try again.',\n};\n\nconst UNAUTHORIZED_ERROR: SavedCardUserError = {\n code: 'UNAUTHORIZED',\n message: 'Your session has expired. Please sign in again.',\n};\n\n/** An expired session needs sign-in, not a retry, so it keeps its own code. */\nfunction toSavedCardError(error: unknown): SavedCardUserError {\n if (CombinedGraphQLErrors.is(error)) {\n const unauthorized = error.errors.some(({ extensions }) => extensions?.code === 'UNAUTHORIZED');\n if (unauthorized) return UNAUTHORIZED_ERROR;\n }\n return NETWORK_ERROR;\n}\n\nexport function useSavedCardActions(): SavedCardActions {\n const client = useApolloClient();\n const [removeMutation] = useMutation(CUSTOMER_SAVED_CARD_REMOVE);\n\n const remove = useCallback<SavedCardActions['remove']>(\n async ({ id }) => {\n const controller = new AbortController();\n let timer: ReturnType<typeof setTimeout> | undefined;\n const timeout = new Promise<never>((_, reject) => {\n timer = setTimeout(() => {\n controller.abort();\n reject(new SavedCardRemoveTimeoutError());\n }, SAVED_CARD_REMOVE_TIMEOUT_MS);\n });\n\n try {\n const result = await Promise.race([\n removeMutation({\n variables: { id },\n context: { fetchOptions: { signal: controller.signal } },\n fetchPolicy: 'no-cache',\n }),\n timeout,\n ]);\n if (result.data) {\n client.cache.writeQuery({\n id: 'ROOT_MUTATION',\n query: CUSTOMER_SAVED_CARD_REMOVE,\n variables: { id },\n data: result.data,\n });\n }\n // A missing payload removed nothing, so it must never read as success.\n const payload = result.data?.customerSavedCardRemove;\n return payload ? payload.userErrors : [NETWORK_ERROR];\n } catch (error) {\n console.error('[savedCard.remove] customerSavedCardRemove failed', loggableErrorName(error));\n return [toSavedCardError(error)];\n } finally {\n clearTimeout(timer);\n }\n },\n [client, removeMutation],\n );\n\n return useMemo(() => ({ remove }), [remove]);\n}\n\n/**\n * Declares the `savedCard` action namespace. The catalog builder reads this call to emit\n * `actions.json`; at runtime `SavedCardActionProvider` calls `useSavedCardActions` directly.\n */\nexport const savedCardActions = defineActions({\n namespace: 'savedCard',\n description: \"Mutation-backed writes against the signed-in shopper's saved cards.\",\n provider: { name: 'SavedCardActionProvider', importPath: '@vtex/faststore/internal/client' },\n methods: {\n remove: {\n description: 'Remove one saved card (async strategy). The row stays until the server answers.',\n payload: [{ name: 'id', type: 'string' }],\n },\n },\n use: useSavedCardActions,\n});\n"],"mappings":";;;;;;;;;AAiCA,MAAa,+BAA+B;AAE5C,IAAM,8BAAN,cAA0C,MAAM;CAC9C,cAAc;EACZ,MAAM,+BAA+B;EACrC,KAAK,OAAO;CACd;AACF;AAEA,MAAM,gBAAoC;CACxC,MAAM;CACN,SAAS;AACX;AAEA,MAAM,qBAAyC;CAC7C,MAAM;CACN,SAAS;AACX;;AAGA,SAAS,iBAAiB,OAAoC;CAC5D,IAAI,sBAAsB,GAAG,KAAK,GACX;MAAA,MAAM,OAAO,MAAM,EAAE,iBAAiB,YAAY,SAAS,cACjE,GAAG,OAAO;CAAA;CAE3B,OAAO;AACT;AAEA,SAAgB,sBAAwC;CACtD,MAAM,SAAS,gBAAgB;CAC/B,MAAM,CAAC,kBAAkB,YAAY,0BAA0B;CAE/D,MAAM,SAAS,YACb,OAAO,EAAE,SAAS;EAChB,MAAM,aAAa,IAAI,gBAAgB;EACvC,IAAI;EACJ,MAAM,UAAU,IAAI,SAAgB,GAAG,WAAW;GAChD,QAAQ,iBAAiB;IACvB,WAAW,MAAM;IACjB,OAAO,IAAI,4BAA4B,CAAC;GAC1C,GAAG,4BAA4B;EACjC,CAAC;EAED,IAAI;GACF,MAAM,SAAS,MAAM,QAAQ,KAAK,CAChC,eAAe;IACb,WAAW,EAAE,GAAG;IAChB,SAAS,EAAE,cAAc,EAAE,QAAQ,WAAW,OAAO,EAAE;IACvD,aAAa;GACf,CAAC,GACD,OACF,CAAC;GACD,IAAI,OAAO,MACT,OAAO,MAAM,WAAW;IACtB,IAAI;IACJ,OAAO;IACP,WAAW,EAAE,GAAG;IAChB,MAAM,OAAO;GACf,CAAC;GAGH,MAAM,UAAU,OAAO,MAAM;GAC7B,OAAO,UAAU,QAAQ,aAAa,CAAC,aAAa;EACtD,SAAS,OAAO;GACd,QAAQ,MAAM,qDAAqD,kBAAkB,KAAK,CAAC;GAC3F,OAAO,CAAC,iBAAiB,KAAK,CAAC;EACjC,UAAU;GACR,aAAa,KAAK;EACpB;CACF,GACA,CAAC,QAAQ,cAAc,CACzB;CAEA,OAAO,eAAe,EAAE,OAAO,IAAI,CAAC,MAAM,CAAC;AAC7C;AAMgC,cAAc;CAC5C,WAAW;CACX,aAAa;CACb,UAAU;EAAE,MAAM;EAA2B,YAAY;CAAkC;CAC3F,SAAS,EACP,QAAQ;EACN,aAAa;EACb,SAAS,CAAC;GAAE,MAAM;GAAM,MAAM;EAAS,CAAC;CAC1C,EACF;CACA,KAAK;AACP,CAAC"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { RouteMap } from "../navigation/types.js";
|
|
2
|
+
//#region src/sdk/seo/entity-landing.d.ts
|
|
3
|
+
export type EntityLandingLookupOptions = {
|
|
4
|
+
routeMap: RouteMap;
|
|
5
|
+
};
|
|
6
|
+
export type EntityLandingQuery = {
|
|
7
|
+
bindingId: string;
|
|
8
|
+
/** The listing's `CATALOG_ROUTE_ALTERNATES_QUERY` read, shared with its cluster lookup. */
|
|
9
|
+
load: () => Promise<{
|
|
10
|
+
data: unknown;
|
|
11
|
+
error?: unknown;
|
|
12
|
+
}>;
|
|
13
|
+
};
|
|
14
|
+
/** Resolves the logical path of the Entity Landing Page that claims a listing, or `null` when none does. */
|
|
15
|
+
export type EntityLandingLookup = (query: EntityLandingQuery) => Promise<string | null>;
|
|
16
|
+
/**
|
|
17
|
+
* The standard listings' claim lookup (#958). Claims come from the route map, keyed by the claimed entity's
|
|
18
|
+
* node ID, and match the `id` `catalogRoute` returns for the listing's path. A claimant the rendered binding
|
|
19
|
+
* does not serve leaves the listing its own canonical. A failed read never fails the page: the listing
|
|
20
|
+
* keeps its own canonical, and the cluster lookup that shares the read reports the failure.
|
|
21
|
+
*/
|
|
22
|
+
export declare function createEntityLandingLookup({ routeMap }: EntityLandingLookupOptions): EntityLandingLookup;
|
|
23
|
+
//#endregion
|
|
24
|
+
//# sourceMappingURL=entity-landing.d.ts.map
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { servedPath } from "./search-landing.js";
|
|
2
|
+
//#region src/sdk/seo/entity-landing.ts
|
|
3
|
+
function routeNodeId(data) {
|
|
4
|
+
return data?.catalogRoute?.id;
|
|
5
|
+
}
|
|
6
|
+
/**
|
|
7
|
+
* The standard listings' claim lookup (#958). Claims come from the route map, keyed by the claimed entity's
|
|
8
|
+
* node ID, and match the `id` `catalogRoute` returns for the listing's path. A claimant the rendered binding
|
|
9
|
+
* does not serve leaves the listing its own canonical. A failed read never fails the page: the listing
|
|
10
|
+
* keeps its own canonical, and the cluster lookup that shares the read reports the failure.
|
|
11
|
+
*/
|
|
12
|
+
function createEntityLandingLookup({ routeMap }) {
|
|
13
|
+
const claimants = /* @__PURE__ */ new Map();
|
|
14
|
+
for (const entry of Object.values(routeMap)) if (entry.canonicalFor && !claimants.has(entry.canonicalFor)) claimants.set(entry.canonicalFor, entry);
|
|
15
|
+
return async function entityLandingPath({ bindingId, load }) {
|
|
16
|
+
if (claimants.size === 0) return null;
|
|
17
|
+
let id;
|
|
18
|
+
try {
|
|
19
|
+
id = routeNodeId((await load()).data);
|
|
20
|
+
} catch {
|
|
21
|
+
return null;
|
|
22
|
+
}
|
|
23
|
+
const claimant = typeof id === "string" ? claimants.get(id) : void 0;
|
|
24
|
+
return claimant ? servedPath(claimant, bindingId) : null;
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
//#endregion
|
|
28
|
+
export { createEntityLandingLookup };
|
|
29
|
+
|
|
30
|
+
//# sourceMappingURL=entity-landing.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"entity-landing.js","names":[],"sources":["../../../src/sdk/seo/entity-landing.ts"],"sourcesContent":["import type { RouteMap, RouteMapEntry } from '../navigation/types.js';\nimport { servedPath } from './search-landing.js';\n\nexport type EntityLandingLookupOptions = { routeMap: RouteMap };\n\nexport type EntityLandingQuery = {\n bindingId: string;\n /** The listing's `CATALOG_ROUTE_ALTERNATES_QUERY` read, shared with its cluster lookup. */\n load: () => Promise<{ data: unknown; error?: unknown }>;\n};\n\n/** Resolves the logical path of the Entity Landing Page that claims a listing, or `null` when none does. */\nexport type EntityLandingLookup = (query: EntityLandingQuery) => Promise<string | null>;\n\nfunction routeNodeId(data: unknown): unknown {\n return (data as { catalogRoute?: { id?: unknown } | null } | null)?.catalogRoute?.id;\n}\n\n/**\n * The standard listings' claim lookup (#958). Claims come from the route map, keyed by the claimed entity's\n * node ID, and match the `id` `catalogRoute` returns for the listing's path. A claimant the rendered binding\n * does not serve leaves the listing its own canonical. A failed read never fails the page: the listing\n * keeps its own canonical, and the cluster lookup that shares the read reports the failure.\n */\nexport function createEntityLandingLookup({ routeMap }: EntityLandingLookupOptions): EntityLandingLookup {\n const claimants = new Map<string, RouteMapEntry>();\n for (const entry of Object.values(routeMap)) {\n if (entry.canonicalFor && !claimants.has(entry.canonicalFor)) claimants.set(entry.canonicalFor, entry);\n }\n\n return async function entityLandingPath({ bindingId, load }) {\n if (claimants.size === 0) return null;\n let id: unknown;\n try {\n id = routeNodeId((await load()).data);\n } catch {\n return null;\n }\n const claimant = typeof id === 'string' ? claimants.get(id) : undefined;\n return claimant ? servedPath(claimant, bindingId) : null;\n };\n}\n"],"mappings":";;AAcA,SAAS,YAAY,MAAwB;CAC3C,OAAQ,MAA4D,cAAc;AACpF;;;;;;;AAQA,SAAgB,0BAA0B,EAAE,YAA6D;CACvG,MAAM,4BAAY,IAAI,IAA2B;CACjD,KAAK,MAAM,SAAS,OAAO,OAAO,QAAQ,GACxC,IAAI,MAAM,gBAAgB,CAAC,UAAU,IAAI,MAAM,YAAY,GAAG,UAAU,IAAI,MAAM,cAAc,KAAK;CAGvG,OAAO,eAAe,kBAAkB,EAAE,WAAW,QAAQ;EAC3D,IAAI,UAAU,SAAS,GAAG,OAAO;EACjC,IAAI;EACJ,IAAI;GACF,KAAK,aAAa,MAAM,KAAK,EAAA,CAAG,IAAI;EACtC,QAAQ;GACN,OAAO;EACT;EACA,MAAM,WAAW,OAAO,OAAO,WAAW,UAAU,IAAI,EAAE,IAAI,KAAA;EAC9D,OAAO,WAAW,WAAW,UAAU,SAAS,IAAI;CACtD;AACF"}
|
|
@@ -24,8 +24,9 @@ export type BuildPageAlternatesOptions = {
|
|
|
24
24
|
buildUrl: (bindingId: string, logicalPath: string) => string;
|
|
25
25
|
};
|
|
26
26
|
/**
|
|
27
|
-
* The only GraphQL text the alternates feature owns. `catalogClusterPaths` reads its
|
|
28
|
-
* package tests validate it against the Storefront API
|
|
27
|
+
* The only GraphQL text the alternates feature owns. `catalogClusterPaths` reads its variants, the
|
|
28
|
+
* entity landing lookup reads its `id`, and the package tests validate it against the Storefront API
|
|
29
|
+
* schema.
|
|
29
30
|
*/
|
|
30
31
|
export declare const CATALOG_ROUTE_ALTERNATES_QUERY: import("@graphql-typed-document-node/core").TypedDocumentNode<CatalogRouteAlternatesQuery, {
|
|
31
32
|
path: string;
|
|
@@ -36,9 +37,11 @@ export declare const CATALOG_ROUTE_ALTERNATES_QUERY: import("@graphql-typed-docu
|
|
|
36
37
|
*/
|
|
37
38
|
export declare function buildPageAlternates({ bindingId, defaultBindingId, bindings, selfPath, clusterPaths, buildUrl }: BuildPageAlternatesOptions): PageAlternates;
|
|
38
39
|
/**
|
|
39
|
-
* The
|
|
40
|
-
* `('/[slug]/p', { slug: '
|
|
41
|
-
*
|
|
40
|
+
* The logical path a route pattern names under these params, which arrive decoded
|
|
41
|
+
* (`decodeRouteParams`): `('/[slug]/p', { slug: 'café' })` -> `'/café/p'`. Catch-alls join with `/`;
|
|
42
|
+
* empty -> `'/'`.
|
|
43
|
+
* Empty, `.` and `..` values are dropped. It never decodes: a caller must decode first, or an
|
|
44
|
+
* encoded dot segment (`%2E%2E`) stays an inert literal segment.
|
|
42
45
|
*/
|
|
43
46
|
export declare function routeLogicalPath(route: string, params: RouteParams): string;
|
|
44
47
|
/**
|
|
@@ -3,12 +3,14 @@ import { loggableErrorName } from "../../utils/loggable-error-name.js";
|
|
|
3
3
|
import { CombinedGraphQLErrors } from "@apollo/client/errors";
|
|
4
4
|
//#region src/sdk/seo/page-alternates.ts
|
|
5
5
|
/**
|
|
6
|
-
* The only GraphQL text the alternates feature owns. `catalogClusterPaths` reads its
|
|
7
|
-
* package tests validate it against the Storefront API
|
|
6
|
+
* The only GraphQL text the alternates feature owns. `catalogClusterPaths` reads its variants, the
|
|
7
|
+
* entity landing lookup reads its `id`, and the package tests validate it against the Storefront API
|
|
8
|
+
* schema.
|
|
8
9
|
*/
|
|
9
10
|
const CATALOG_ROUTE_ALTERNATES_QUERY = graphql(`
|
|
10
11
|
query CatalogRouteAlternates($path: String!) {
|
|
11
12
|
catalogRoute(input: { path: $path }) {
|
|
13
|
+
id
|
|
12
14
|
variants {
|
|
13
15
|
locale
|
|
14
16
|
path
|
|
@@ -57,27 +59,22 @@ function buildPageAlternates({ bindingId, defaultBindingId, bindings, selfPath,
|
|
|
57
59
|
languages
|
|
58
60
|
};
|
|
59
61
|
}
|
|
60
|
-
function decodeSegment(segment) {
|
|
61
|
-
try {
|
|
62
|
-
return decodeURIComponent(segment);
|
|
63
|
-
} catch {
|
|
64
|
-
return segment;
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
62
|
/** `.` and `..` would make the URL a crawler resolves differ from the page that states it. */
|
|
68
63
|
function isPathSegment(segment) {
|
|
69
64
|
return segment !== "" && segment !== "." && segment !== "..";
|
|
70
65
|
}
|
|
71
66
|
/**
|
|
72
|
-
* The
|
|
73
|
-
* `('/[slug]/p', { slug: '
|
|
74
|
-
*
|
|
67
|
+
* The logical path a route pattern names under these params, which arrive decoded
|
|
68
|
+
* (`decodeRouteParams`): `('/[slug]/p', { slug: 'café' })` -> `'/café/p'`. Catch-alls join with `/`;
|
|
69
|
+
* empty -> `'/'`.
|
|
70
|
+
* Empty, `.` and `..` values are dropped. It never decodes: a caller must decode first, or an
|
|
71
|
+
* encoded dot segment (`%2E%2E`) stays an inert literal segment.
|
|
75
72
|
*/
|
|
76
73
|
function routeLogicalPath(route, params) {
|
|
77
74
|
return `/${route.split("/").filter(Boolean).flatMap((segment) => {
|
|
78
75
|
const dynamic = /^\[{1,2}(?:\.\.\.)?([^\]]+)\]{1,2}$/.exec(segment);
|
|
79
76
|
if (!dynamic) return [segment];
|
|
80
|
-
return [params[dynamic[1]] ?? []].flat()
|
|
77
|
+
return [params[dynamic[1]] ?? []].flat();
|
|
81
78
|
}).filter(isPathSegment).join("/")}`;
|
|
82
79
|
}
|
|
83
80
|
function readVariants(data) {
|
|
@@ -127,6 +124,6 @@ async function catalogClusterPaths(bindings, load) {
|
|
|
127
124
|
}
|
|
128
125
|
}
|
|
129
126
|
//#endregion
|
|
130
|
-
export { CATALOG_ROUTE_ALTERNATES_QUERY, buildPageAlternates, catalogClusterPaths, routeLogicalPath };
|
|
127
|
+
export { CATALOG_ROUTE_ALTERNATES_QUERY, buildPageAlternates, catalogClusterPaths, encodePath, routeLogicalPath };
|
|
131
128
|
|
|
132
129
|
//# sourceMappingURL=page-alternates.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"page-alternates.js","names":[],"sources":["../../../src/sdk/seo/page-alternates.ts"],"sourcesContent":["import { CombinedGraphQLErrors } from '@apollo/client/errors';\nimport { loggableErrorName } from '../../utils/loggable-error-name.js';\nimport { graphql } from '../generated/gql.js';\nimport type { CatalogRouteAlternatesQuery } from '../generated/graphql.js';\n\nexport type AlternateBinding = { bindingId: string; locale: string };\n\nexport type RouteVariant = NonNullable<CatalogRouteAlternatesQuery['catalogRoute']>['variants'][number];\n\n/** Structurally assignable to Next.js `Metadata['alternates']`. */\nexport type PageAlternates = {\n canonical: string;\n /** BCP 47 tag or `x-default` -> absolute URL. */\n languages?: Record<string, string>;\n};\n\nexport type RouteParams = Readonly<Record<string, string | string[] | undefined>>;\n\nexport type BuildPageAlternatesOptions = {\n bindingId: string;\n defaultBindingId: string;\n /** Every declared binding, in authoring order. */\n bindings: readonly AlternateBinding[];\n /** Decoded logical path of the request. */\n selfPath: string;\n /** Binding id -> decoded logical path, for the cluster members only. `null` means no cluster source. */\n clusterPaths: Readonly<Record<string, string>> | null;\n buildUrl: (bindingId: string, logicalPath: string) => string;\n};\n\n/**\n * The only GraphQL text the alternates feature owns. `catalogClusterPaths` reads its result, and the\n * package tests validate it against the Storefront API schema.\n */\nexport const CATALOG_ROUTE_ALTERNATES_QUERY = graphql(`\n query CatalogRouteAlternates($path: String!) {\n catalogRoute(input: { path: $path }) {\n variants {\n locale\n path\n }\n }\n }\n`);\n\n/** Encodes each segment and keeps `/` literal, so a decoded path is encoded exactly once. */\nfunction encodePath(path: string): string {\n return path.split('/').map(encodeURIComponent).join('/');\n}\n\n/**\n * One binding per locale: `hreflang` maps a tag to a single URL. The default binding represents a\n * locale it declares; otherwise the first binding declaring it does.\n */\nfunction representatives(bindings: readonly AlternateBinding[], defaultBindingId: string): AlternateBinding[] {\n const byLocale = new Map<string, AlternateBinding>();\n for (const binding of bindings) {\n const key = binding.locale.toLowerCase();\n const current = byLocale.get(key);\n if (!current || binding.bindingId === defaultBindingId) byLocale.set(key, binding);\n }\n return bindings.filter((binding) => byLocale.get(binding.locale.toLowerCase()) === binding);\n}\n\n/**\n * The canonical URL plus, when a reciprocal cluster exists, `languages`. A cluster needs two members\n * and must include the rendered binding: search engines ignore a set that does not reference itself.\n */\nexport function buildPageAlternates({\n bindingId,\n defaultBindingId,\n bindings,\n selfPath,\n clusterPaths,\n buildUrl,\n}: BuildPageAlternatesOptions): PageAlternates {\n const url = (id: string, path: string) => buildUrl(id, encodePath(path));\n const pathOf = (id: string) => (clusterPaths && Object.hasOwn(clusterPaths, id) ? clusterPaths[id] : undefined);\n const canonical = url(bindingId, pathOf(bindingId) ?? selfPath);\n\n const members = representatives(bindings, defaultBindingId).flatMap((binding) => {\n const path = pathOf(binding.bindingId);\n return path === undefined ? [] : [{ ...binding, href: url(binding.bindingId, path) }];\n });\n if (members.length < 2 || !members.some((member) => member.bindingId === bindingId)) return { canonical };\n\n const languages: Record<string, string> = {};\n for (const member of members) languages[member.locale] = member.href;\n const defaultMember = members.find((member) => member.bindingId === defaultBindingId);\n if (defaultMember) languages['x-default'] = defaultMember.href;\n return { canonical, languages };\n}\n\nfunction decodeSegment(segment: string): string {\n try {\n return decodeURIComponent(segment);\n } catch {\n return segment;\n }\n}\n\n/** `.` and `..` would make the URL a crawler resolves differ from the page that states it. */\nfunction isPathSegment(segment: string): boolean {\n return segment !== '' && segment !== '.' && segment !== '..';\n}\n\n/**\n * The decoded logical path a route pattern names under these params:\n * `('/[slug]/p', { slug: 'caf%C3%A9' })` -> `'/café/p'`. Catch-alls join with `/`; empty -> `'/'`.\n * Empty, `.` and `..` values are dropped.\n */\nexport function routeLogicalPath(route: string, params: RouteParams): string {\n const segments = route\n .split('/')\n .filter(Boolean)\n .flatMap((segment) => {\n const dynamic = /^\\[{1,2}(?:\\.\\.\\.)?([^\\]]+)\\]{1,2}$/.exec(segment);\n if (!dynamic) return [segment];\n return [params[dynamic[1] as string] ?? []].flat().map(decodeSegment);\n })\n .filter(isPathSegment);\n return `/${segments.join('/')}`;\n}\n\nfunction readVariants(data: unknown): RouteVariant[] {\n const variants = (data as { catalogRoute?: { variants?: unknown } | null } | null)?.catalogRoute?.variants;\n if (!Array.isArray(variants)) return [];\n return variants.filter(\n (variant): variant is RouteVariant => typeof variant?.locale === 'string' && typeof variant?.path === 'string',\n );\n}\n\n/** Binding id -> confirmed variant path, matching BCP 47 tags case-insensitively and nothing looser. */\nfunction variantPathsByBinding(\n bindings: readonly AlternateBinding[],\n variants: readonly RouteVariant[],\n): Record<string, string> {\n const paths: Record<string, string> = {};\n for (const binding of bindings) {\n const locale = binding.locale.toLowerCase();\n const variant = variants.find((candidate) => candidate.locale.toLowerCase() === locale);\n if (variant) paths[binding.bindingId] = variant.path;\n }\n return paths;\n}\n\n/**\n * Why a lookup degraded, safe to log: the GraphQL error code, else the allow-listed error name. `null`\n * for `BAD_USER_INPUT`, which a path `catalogRoute` cannot parse (a slug with a dot) always answers.\n */\nfunction degradedReason(error: unknown): string | null {\n if (!CombinedGraphQLErrors.is(error)) return loggableErrorName(error);\n const codes = error.errors.map(({ extensions }) => extensions?.code);\n if (codes.length > 0 && codes.every((candidate) => candidate === 'BAD_USER_INPUT')) return null;\n const code = codes.find(\n (candidate): candidate is string => typeof candidate === 'string' && candidate !== 'BAD_USER_INPUT',\n );\n return code ?? 'GRAPHQL_ERROR';\n}\n\nfunction warnDegraded(error: unknown): null {\n const reason = degradedReason(error);\n if (reason) console.warn('[seo.catalogClusterPaths] catalogRoute lookup degraded to no cluster', reason);\n return null;\n}\n\n/**\n * Binding id -> variant path for the bindings whose locale a confirmed variant matches. Never\n * rejects: a thrown loader, an `error`, a missing or `null` `catalogRoute`, or no matching variant\n * all resolve to `null`, so a catalog lookup can never fail the page it describes. A thrown loader\n * or an `error` is logged by code or error name only, never with the path or the upstream message.\n */\nexport async function catalogClusterPaths(\n bindings: readonly AlternateBinding[],\n load: () => Promise<{ data: unknown; error?: unknown }>,\n): Promise<Record<string, string> | null> {\n try {\n const { data, error } = await load();\n if (error) return warnDegraded(error);\n const paths = variantPathsByBinding(bindings, readVariants(data));\n return Object.keys(paths).length > 0 ? paths : null;\n } catch (error) {\n return warnDegraded(error);\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"page-alternates.js","names":[],"sources":["../../../src/sdk/seo/page-alternates.ts"],"sourcesContent":["import { CombinedGraphQLErrors } from '@apollo/client/errors';\nimport { loggableErrorName } from '../../utils/loggable-error-name.js';\nimport { graphql } from '../generated/gql.js';\nimport type { CatalogRouteAlternatesQuery } from '../generated/graphql.js';\n\nexport type AlternateBinding = { bindingId: string; locale: string };\n\nexport type RouteVariant = NonNullable<CatalogRouteAlternatesQuery['catalogRoute']>['variants'][number];\n\n/** Structurally assignable to Next.js `Metadata['alternates']`. */\nexport type PageAlternates = {\n canonical: string;\n /** BCP 47 tag or `x-default` -> absolute URL. */\n languages?: Record<string, string>;\n};\n\nexport type RouteParams = Readonly<Record<string, string | string[] | undefined>>;\n\nexport type BuildPageAlternatesOptions = {\n bindingId: string;\n defaultBindingId: string;\n /** Every declared binding, in authoring order. */\n bindings: readonly AlternateBinding[];\n /** Decoded logical path of the request. */\n selfPath: string;\n /** Binding id -> decoded logical path, for the cluster members only. `null` means no cluster source. */\n clusterPaths: Readonly<Record<string, string>> | null;\n buildUrl: (bindingId: string, logicalPath: string) => string;\n};\n\n/**\n * The only GraphQL text the alternates feature owns. `catalogClusterPaths` reads its variants, the\n * entity landing lookup reads its `id`, and the package tests validate it against the Storefront API\n * schema.\n */\nexport const CATALOG_ROUTE_ALTERNATES_QUERY = graphql(`\n query CatalogRouteAlternates($path: String!) {\n catalogRoute(input: { path: $path }) {\n id\n variants {\n locale\n path\n }\n }\n }\n`);\n\n/** Encodes each segment and keeps `/` literal, so a decoded path is encoded exactly once. */\nexport function encodePath(path: string): string {\n return path.split('/').map(encodeURIComponent).join('/');\n}\n\n/**\n * One binding per locale: `hreflang` maps a tag to a single URL. The default binding represents a\n * locale it declares; otherwise the first binding declaring it does.\n */\nfunction representatives(bindings: readonly AlternateBinding[], defaultBindingId: string): AlternateBinding[] {\n const byLocale = new Map<string, AlternateBinding>();\n for (const binding of bindings) {\n const key = binding.locale.toLowerCase();\n const current = byLocale.get(key);\n if (!current || binding.bindingId === defaultBindingId) byLocale.set(key, binding);\n }\n return bindings.filter((binding) => byLocale.get(binding.locale.toLowerCase()) === binding);\n}\n\n/**\n * The canonical URL plus, when a reciprocal cluster exists, `languages`. A cluster needs two members\n * and must include the rendered binding: search engines ignore a set that does not reference itself.\n */\nexport function buildPageAlternates({\n bindingId,\n defaultBindingId,\n bindings,\n selfPath,\n clusterPaths,\n buildUrl,\n}: BuildPageAlternatesOptions): PageAlternates {\n const url = (id: string, path: string) => buildUrl(id, encodePath(path));\n const pathOf = (id: string) => (clusterPaths && Object.hasOwn(clusterPaths, id) ? clusterPaths[id] : undefined);\n const canonical = url(bindingId, pathOf(bindingId) ?? selfPath);\n\n const members = representatives(bindings, defaultBindingId).flatMap((binding) => {\n const path = pathOf(binding.bindingId);\n return path === undefined ? [] : [{ ...binding, href: url(binding.bindingId, path) }];\n });\n if (members.length < 2 || !members.some((member) => member.bindingId === bindingId)) return { canonical };\n\n const languages: Record<string, string> = {};\n for (const member of members) languages[member.locale] = member.href;\n const defaultMember = members.find((member) => member.bindingId === defaultBindingId);\n if (defaultMember) languages['x-default'] = defaultMember.href;\n return { canonical, languages };\n}\n\n/** `.` and `..` would make the URL a crawler resolves differ from the page that states it. */\nfunction isPathSegment(segment: string): boolean {\n return segment !== '' && segment !== '.' && segment !== '..';\n}\n\n/**\n * The logical path a route pattern names under these params, which arrive decoded\n * (`decodeRouteParams`): `('/[slug]/p', { slug: 'café' })` -> `'/café/p'`. Catch-alls join with `/`;\n * empty -> `'/'`.\n * Empty, `.` and `..` values are dropped. It never decodes: a caller must decode first, or an\n * encoded dot segment (`%2E%2E`) stays an inert literal segment.\n */\nexport function routeLogicalPath(route: string, params: RouteParams): string {\n const segments = route\n .split('/')\n .filter(Boolean)\n .flatMap((segment) => {\n const dynamic = /^\\[{1,2}(?:\\.\\.\\.)?([^\\]]+)\\]{1,2}$/.exec(segment);\n if (!dynamic) return [segment];\n return [params[dynamic[1] as string] ?? []].flat();\n })\n .filter(isPathSegment);\n return `/${segments.join('/')}`;\n}\n\nfunction readVariants(data: unknown): RouteVariant[] {\n const variants = (data as { catalogRoute?: { variants?: unknown } | null } | null)?.catalogRoute?.variants;\n if (!Array.isArray(variants)) return [];\n return variants.filter(\n (variant): variant is RouteVariant => typeof variant?.locale === 'string' && typeof variant?.path === 'string',\n );\n}\n\n/** Binding id -> confirmed variant path, matching BCP 47 tags case-insensitively and nothing looser. */\nfunction variantPathsByBinding(\n bindings: readonly AlternateBinding[],\n variants: readonly RouteVariant[],\n): Record<string, string> {\n const paths: Record<string, string> = {};\n for (const binding of bindings) {\n const locale = binding.locale.toLowerCase();\n const variant = variants.find((candidate) => candidate.locale.toLowerCase() === locale);\n if (variant) paths[binding.bindingId] = variant.path;\n }\n return paths;\n}\n\n/**\n * Why a lookup degraded, safe to log: the GraphQL error code, else the allow-listed error name. `null`\n * for `BAD_USER_INPUT`, which a path `catalogRoute` cannot parse (a slug with a dot) always answers.\n */\nfunction degradedReason(error: unknown): string | null {\n if (!CombinedGraphQLErrors.is(error)) return loggableErrorName(error);\n const codes = error.errors.map(({ extensions }) => extensions?.code);\n if (codes.length > 0 && codes.every((candidate) => candidate === 'BAD_USER_INPUT')) return null;\n const code = codes.find(\n (candidate): candidate is string => typeof candidate === 'string' && candidate !== 'BAD_USER_INPUT',\n );\n return code ?? 'GRAPHQL_ERROR';\n}\n\nfunction warnDegraded(error: unknown): null {\n const reason = degradedReason(error);\n if (reason) console.warn('[seo.catalogClusterPaths] catalogRoute lookup degraded to no cluster', reason);\n return null;\n}\n\n/**\n * Binding id -> variant path for the bindings whose locale a confirmed variant matches. Never\n * rejects: a thrown loader, an `error`, a missing or `null` `catalogRoute`, or no matching variant\n * all resolve to `null`, so a catalog lookup can never fail the page it describes. A thrown loader\n * or an `error` is logged by code or error name only, never with the path or the upstream message.\n */\nexport async function catalogClusterPaths(\n bindings: readonly AlternateBinding[],\n load: () => Promise<{ data: unknown; error?: unknown }>,\n): Promise<Record<string, string> | null> {\n try {\n const { data, error } = await load();\n if (error) return warnDegraded(error);\n const paths = variantPathsByBinding(bindings, readVariants(data));\n return Object.keys(paths).length > 0 ? paths : null;\n } catch (error) {\n return warnDegraded(error);\n }\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { RouteMap } from "../navigation/types.js";
|
|
2
|
+
//#region src/sdk/seo/search-landing.d.ts
|
|
3
|
+
/**
|
|
4
|
+
* The one search-term normalization (#957): NFD, combining diacritics stripped, trimmed, lower-cased,
|
|
5
|
+
* every whitespace run collapsed to one hyphen. No stop words. A CMS term entry's slug has this shape,
|
|
6
|
+
* so the singleton term map and the read by slug share this function.
|
|
7
|
+
*/
|
|
8
|
+
export declare function normalizeSearchTerm(term: string): string;
|
|
9
|
+
/**
|
|
10
|
+
* One content item read by slug. Structurally the content runtime's `readContentItem`, which this module
|
|
11
|
+
* does not import: the server entry must not pull the content runtime into every page's graph.
|
|
12
|
+
*/
|
|
13
|
+
export type SearchLandingItemRead = (args: {
|
|
14
|
+
contentTypeId: string;
|
|
15
|
+
slug: string;
|
|
16
|
+
locale: string;
|
|
17
|
+
}) => Promise<{
|
|
18
|
+
status: 'found' | 'not-found' | 'unavailable';
|
|
19
|
+
}>;
|
|
20
|
+
export type SearchLandingLookupOptions = {
|
|
21
|
+
routeMap: RouteMap;
|
|
22
|
+
/** Present when the store has page-level content types; without it, CMS term entries are never read. */
|
|
23
|
+
readContentItem?: SearchLandingItemRead;
|
|
24
|
+
now?: () => number;
|
|
25
|
+
};
|
|
26
|
+
export type SearchLandingQuery = {
|
|
27
|
+
bindingId: string;
|
|
28
|
+
locale: string;
|
|
29
|
+
/** The search page's `q` as Next.js hands it over. A repeated key reads its first value. */
|
|
30
|
+
query: string | string[] | undefined;
|
|
31
|
+
};
|
|
32
|
+
/** Resolves the logical path of the Search Landing Page for a query, or `null` when it has none. */
|
|
33
|
+
export type SearchLandingLookup = (query: SearchLandingQuery) => Promise<string | null>;
|
|
34
|
+
/**
|
|
35
|
+
* The search page's landing lookup. Singleton terms come from the route map, normalized once here, and a
|
|
36
|
+
* singleton wins its term. Any other term costs one content read by its normalized slug, unless the same
|
|
37
|
+
* term answered `not-found` less than a minute ago. `unavailable` is never cached and never fails the page:
|
|
38
|
+
* the search page keeps its own canonical.
|
|
39
|
+
*/
|
|
40
|
+
export declare function createSearchLandingLookup({ routeMap, readContentItem, now }: SearchLandingLookupOptions): SearchLandingLookup;
|
|
41
|
+
//#endregion
|
|
42
|
+
//# sourceMappingURL=search-landing.d.ts.map
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { routeLogicalPath } from "./page-alternates.js";
|
|
2
|
+
//#region src/sdk/seo/search-landing.ts
|
|
3
|
+
/** How long a `not-found` term answer is reused, matching the Data Plane's own 404 `max-age`. */
|
|
4
|
+
const SEARCH_LANDING_MISS_TTL_MS = 6e4;
|
|
5
|
+
/** The most `not-found` answers kept at once. The key comes from the shopper, so the cache is bounded. */
|
|
6
|
+
const SEARCH_LANDING_MISS_LIMIT = 1e4;
|
|
7
|
+
const COMBINING_DIACRITICS = /[̀-ͯ]/g;
|
|
8
|
+
const WHITESPACE_RUN = /\s+/g;
|
|
9
|
+
/**
|
|
10
|
+
* The one search-term normalization (#957): NFD, combining diacritics stripped, trimmed, lower-cased,
|
|
11
|
+
* every whitespace run collapsed to one hyphen. No stop words. A CMS term entry's slug has this shape,
|
|
12
|
+
* so the singleton term map and the read by slug share this function.
|
|
13
|
+
*/
|
|
14
|
+
function normalizeSearchTerm(term) {
|
|
15
|
+
return term.normalize("NFD").replaceAll(COMBINING_DIACRITICS, "").trim().toLowerCase().replaceAll(WHITESPACE_RUN, "-");
|
|
16
|
+
}
|
|
17
|
+
/** The path `entry` is served at in a binding: its alias there, else its pattern. `null` when unserved. */
|
|
18
|
+
function servedPath(entry, bindingId) {
|
|
19
|
+
if (entry.bindings && !entry.bindings.includes(bindingId)) return null;
|
|
20
|
+
return entry.aliases?.[bindingId] ?? entry.pattern;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* The search page's landing lookup. Singleton terms come from the route map, normalized once here, and a
|
|
24
|
+
* singleton wins its term. Any other term costs one content read by its normalized slug, unless the same
|
|
25
|
+
* term answered `not-found` less than a minute ago. `unavailable` is never cached and never fails the page:
|
|
26
|
+
* the search page keeps its own canonical.
|
|
27
|
+
*/
|
|
28
|
+
function createSearchLandingLookup({ routeMap, readContentItem, now = Date.now }) {
|
|
29
|
+
const singletons = /* @__PURE__ */ new Map();
|
|
30
|
+
let content;
|
|
31
|
+
for (const entry of Object.values(routeMap)) {
|
|
32
|
+
const { searchTerm } = entry;
|
|
33
|
+
if (typeof searchTerm === "string") {
|
|
34
|
+
const term = normalizeSearchTerm(searchTerm);
|
|
35
|
+
if (term !== "" && !singletons.has(term)) singletons.set(term, entry);
|
|
36
|
+
} else if (searchTerm && !content) content = {
|
|
37
|
+
entry,
|
|
38
|
+
...searchTerm
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
const misses = /* @__PURE__ */ new Map();
|
|
42
|
+
function missed(key) {
|
|
43
|
+
const expiresAt = misses.get(key);
|
|
44
|
+
if (expiresAt === void 0) return false;
|
|
45
|
+
if (expiresAt > now()) return true;
|
|
46
|
+
misses.delete(key);
|
|
47
|
+
return false;
|
|
48
|
+
}
|
|
49
|
+
function remember(key) {
|
|
50
|
+
if (misses.size >= 1e4) {
|
|
51
|
+
const at = now();
|
|
52
|
+
for (const [stale, expiresAt] of misses) if (expiresAt <= at) misses.delete(stale);
|
|
53
|
+
if (misses.size >= 1e4) misses.delete(misses.keys().next().value);
|
|
54
|
+
}
|
|
55
|
+
misses.set(key, now() + SEARCH_LANDING_MISS_TTL_MS);
|
|
56
|
+
}
|
|
57
|
+
return async function searchLandingPath({ bindingId, locale, query }) {
|
|
58
|
+
const raw = Array.isArray(query) ? query[0] : query;
|
|
59
|
+
const term = raw === void 0 ? "" : normalizeSearchTerm(raw);
|
|
60
|
+
if (term === "") return null;
|
|
61
|
+
const singleton = singletons.get(term);
|
|
62
|
+
const singletonPath = singleton && servedPath(singleton, bindingId);
|
|
63
|
+
if (singletonPath) return singletonPath;
|
|
64
|
+
const pattern = content && readContentItem ? servedPath(content.entry, bindingId) : null;
|
|
65
|
+
if (!content || !readContentItem || pattern === null) return null;
|
|
66
|
+
if (!isSingleSegment(term)) return null;
|
|
67
|
+
const key = `${content.contentType}\u0000${locale}\u0000${term}`;
|
|
68
|
+
if (missed(key)) return null;
|
|
69
|
+
const { status } = await readContentItem({
|
|
70
|
+
contentTypeId: content.contentType,
|
|
71
|
+
slug: term,
|
|
72
|
+
locale
|
|
73
|
+
});
|
|
74
|
+
if (status === "not-found") remember(key);
|
|
75
|
+
return status === "found" ? routeLogicalPath(pattern, { [content.param]: term }) : null;
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
/** Whether a normalized term fills one route segment: no `/`, and not a dot segment. */
|
|
79
|
+
function isSingleSegment(term) {
|
|
80
|
+
return !term.includes("/") && term !== "." && term !== "..";
|
|
81
|
+
}
|
|
82
|
+
//#endregion
|
|
83
|
+
export { SEARCH_LANDING_MISS_LIMIT, SEARCH_LANDING_MISS_TTL_MS, createSearchLandingLookup, normalizeSearchTerm, servedPath };
|
|
84
|
+
|
|
85
|
+
//# sourceMappingURL=search-landing.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"search-landing.js","names":[],"sources":["../../../src/sdk/seo/search-landing.ts"],"sourcesContent":["import type { RouteMap, RouteMapEntry } from '../navigation/types.js';\nimport { routeLogicalPath } from './page-alternates.js';\n\n/** How long a `not-found` term answer is reused, matching the Data Plane's own 404 `max-age`. */\nexport const SEARCH_LANDING_MISS_TTL_MS = 60_000;\n\n/** The most `not-found` answers kept at once. The key comes from the shopper, so the cache is bounded. */\nexport const SEARCH_LANDING_MISS_LIMIT = 10_000;\n\nconst COMBINING_DIACRITICS = /[̀-ͯ]/g;\nconst WHITESPACE_RUN = /\\s+/g;\n\n/**\n * The one search-term normalization (#957): NFD, combining diacritics stripped, trimmed, lower-cased,\n * every whitespace run collapsed to one hyphen. No stop words. A CMS term entry's slug has this shape,\n * so the singleton term map and the read by slug share this function.\n */\nexport function normalizeSearchTerm(term: string): string {\n return term\n .normalize('NFD')\n .replaceAll(COMBINING_DIACRITICS, '')\n .trim()\n .toLowerCase()\n .replaceAll(WHITESPACE_RUN, '-');\n}\n\n/**\n * One content item read by slug. Structurally the content runtime's `readContentItem`, which this module\n * does not import: the server entry must not pull the content runtime into every page's graph.\n */\nexport type SearchLandingItemRead = (args: {\n contentTypeId: string;\n slug: string;\n locale: string;\n}) => Promise<{ status: 'found' | 'not-found' | 'unavailable' }>;\n\nexport type SearchLandingLookupOptions = {\n routeMap: RouteMap;\n /** Present when the store has page-level content types; without it, CMS term entries are never read. */\n readContentItem?: SearchLandingItemRead;\n now?: () => number;\n};\n\nexport type SearchLandingQuery = {\n bindingId: string;\n locale: string;\n /** The search page's `q` as Next.js hands it over. A repeated key reads its first value. */\n query: string | string[] | undefined;\n};\n\n/** Resolves the logical path of the Search Landing Page for a query, or `null` when it has none. */\nexport type SearchLandingLookup = (query: SearchLandingQuery) => Promise<string | null>;\n\ntype ContentLanding = { entry: RouteMapEntry; contentType: string; param: string };\n\n/** The path `entry` is served at in a binding: its alias there, else its pattern. `null` when unserved. */\nexport function servedPath(entry: RouteMapEntry, bindingId: string): string | null {\n if (entry.bindings && !entry.bindings.includes(bindingId)) return null;\n return entry.aliases?.[bindingId] ?? entry.pattern;\n}\n\n/**\n * The search page's landing lookup. Singleton terms come from the route map, normalized once here, and a\n * singleton wins its term. Any other term costs one content read by its normalized slug, unless the same\n * term answered `not-found` less than a minute ago. `unavailable` is never cached and never fails the page:\n * the search page keeps its own canonical.\n */\nexport function createSearchLandingLookup({\n routeMap,\n readContentItem,\n now = Date.now,\n}: SearchLandingLookupOptions): SearchLandingLookup {\n const singletons = new Map<string, RouteMapEntry>();\n let content: ContentLanding | undefined;\n for (const entry of Object.values(routeMap)) {\n const { searchTerm } = entry;\n if (typeof searchTerm === 'string') {\n const term = normalizeSearchTerm(searchTerm);\n if (term !== '' && !singletons.has(term)) singletons.set(term, entry);\n } else if (searchTerm && !content) {\n content = { entry, ...searchTerm };\n }\n }\n const misses = new Map<string, number>();\n\n function missed(key: string): boolean {\n const expiresAt = misses.get(key);\n if (expiresAt === undefined) return false;\n if (expiresAt > now()) return true;\n misses.delete(key);\n return false;\n }\n\n function remember(key: string): void {\n if (misses.size >= SEARCH_LANDING_MISS_LIMIT) {\n const at = now();\n for (const [stale, expiresAt] of misses) if (expiresAt <= at) misses.delete(stale);\n // Insertion order is expiry order, so the first key is the one closest to expiring.\n if (misses.size >= SEARCH_LANDING_MISS_LIMIT) misses.delete(misses.keys().next().value as string);\n }\n misses.set(key, now() + SEARCH_LANDING_MISS_TTL_MS);\n }\n\n return async function searchLandingPath({ bindingId, locale, query }) {\n const raw = Array.isArray(query) ? query[0] : query;\n const term = raw === undefined ? '' : normalizeSearchTerm(raw);\n if (term === '') return null;\n\n const singleton = singletons.get(term);\n const singletonPath = singleton && servedPath(singleton, bindingId);\n if (singletonPath) return singletonPath;\n\n const pattern = content && readContentItem ? servedPath(content.entry, bindingId) : null;\n if (!content || !readContentItem || pattern === null) return null;\n // The CMS page has one `[param]` segment, so a term that cannot fill one has no entry to read.\n if (!isSingleSegment(term)) return null;\n const key = `${content.contentType}\\u0000${locale}\\u0000${term}`;\n if (missed(key)) return null;\n const { status } = await readContentItem({ contentTypeId: content.contentType, slug: term, locale });\n if (status === 'not-found') remember(key);\n // The path stays decoded, like every logical path: `routeLogicalPath` never decodes, and the\n // canonical's `url()` encodes the path once on the way out.\n return status === 'found' ? routeLogicalPath(pattern, { [content.param]: term }) : null;\n };\n}\n\n/** Whether a normalized term fills one route segment: no `/`, and not a dot segment. */\nfunction isSingleSegment(term: string): boolean {\n return !term.includes('/') && term !== '.' && term !== '..';\n}\n"],"mappings":";;;AAIA,MAAa,6BAA6B;;AAG1C,MAAa,4BAA4B;AAEzC,MAAM,uBAAuB;AAC7B,MAAM,iBAAiB;;;;;;AAOvB,SAAgB,oBAAoB,MAAsB;CACxD,OAAO,KACJ,UAAU,KAAK,CAAC,CAChB,WAAW,sBAAsB,EAAE,CAAC,CACpC,KAAK,CAAC,CACN,YAAY,CAAC,CACb,WAAW,gBAAgB,GAAG;AACnC;;AAgCA,SAAgB,WAAW,OAAsB,WAAkC;CACjF,IAAI,MAAM,YAAY,CAAC,MAAM,SAAS,SAAS,SAAS,GAAG,OAAO;CAClE,OAAO,MAAM,UAAU,cAAc,MAAM;AAC7C;;;;;;;AAQA,SAAgB,0BAA0B,EACxC,UACA,iBACA,MAAM,KAAK,OACuC;CAClD,MAAM,6BAAa,IAAI,IAA2B;CAClD,IAAI;CACJ,KAAK,MAAM,SAAS,OAAO,OAAO,QAAQ,GAAG;EAC3C,MAAM,EAAE,eAAe;EACvB,IAAI,OAAO,eAAe,UAAU;GAClC,MAAM,OAAO,oBAAoB,UAAU;GAC3C,IAAI,SAAS,MAAM,CAAC,WAAW,IAAI,IAAI,GAAG,WAAW,IAAI,MAAM,KAAK;EACtE,OAAO,IAAI,cAAc,CAAC,SACxB,UAAU;GAAE;GAAO,GAAG;EAAW;CAErC;CACA,MAAM,yBAAS,IAAI,IAAoB;CAEvC,SAAS,OAAO,KAAsB;EACpC,MAAM,YAAY,OAAO,IAAI,GAAG;EAChC,IAAI,cAAc,KAAA,GAAW,OAAO;EACpC,IAAI,YAAY,IAAI,GAAG,OAAO;EAC9B,OAAO,OAAO,GAAG;EACjB,OAAO;CACT;CAEA,SAAS,SAAS,KAAmB;EACnC,IAAI,OAAO,QAAA,KAAmC;GAC5C,MAAM,KAAK,IAAI;GACf,KAAK,MAAM,CAAC,OAAO,cAAc,QAAQ,IAAI,aAAa,IAAI,OAAO,OAAO,KAAK;GAEjF,IAAI,OAAO,QAAA,KAAmC,OAAO,OAAO,OAAO,KAAK,CAAC,CAAC,KAAK,CAAC,CAAC,KAAe;EAClG;EACA,OAAO,IAAI,KAAK,IAAI,IAAI,0BAA0B;CACpD;CAEA,OAAO,eAAe,kBAAkB,EAAE,WAAW,QAAQ,SAAS;EACpE,MAAM,MAAM,MAAM,QAAQ,KAAK,IAAI,MAAM,KAAK;EAC9C,MAAM,OAAO,QAAQ,KAAA,IAAY,KAAK,oBAAoB,GAAG;EAC7D,IAAI,SAAS,IAAI,OAAO;EAExB,MAAM,YAAY,WAAW,IAAI,IAAI;EACrC,MAAM,gBAAgB,aAAa,WAAW,WAAW,SAAS;EAClE,IAAI,eAAe,OAAO;EAE1B,MAAM,UAAU,WAAW,kBAAkB,WAAW,QAAQ,OAAO,SAAS,IAAI;EACpF,IAAI,CAAC,WAAW,CAAC,mBAAmB,YAAY,MAAM,OAAO;EAE7D,IAAI,CAAC,gBAAgB,IAAI,GAAG,OAAO;EACnC,MAAM,MAAM,GAAG,QAAQ,YAAY,QAAQ,OAAO,QAAQ;EAC1D,IAAI,OAAO,GAAG,GAAG,OAAO;EACxB,MAAM,EAAE,WAAW,MAAM,gBAAgB;GAAE,eAAe,QAAQ;GAAa,MAAM;GAAM;EAAO,CAAC;EACnG,IAAI,WAAW,aAAa,SAAS,GAAG;EAGxC,OAAO,WAAW,UAAU,iBAAiB,SAAS,GAAG,QAAQ,QAAQ,KAAK,CAAC,IAAI;CACrF;AACF;;AAGA,SAAS,gBAAgB,MAAuB;CAC9C,OAAO,CAAC,KAAK,SAAS,GAAG,KAAK,SAAS,OAAO,SAAS;AACzD"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
//#region src/sdk/session/mock-session.d.ts
|
|
2
|
+
/**
|
|
3
|
+
* The development-only mock session. A process that sets `FS_MOCK_SESSION` to a token treats a
|
|
4
|
+
* request carrying the cookie `fs-mock-session` with that same token as signed in, so the session gate can
|
|
5
|
+
* be exercised without a real shopper. Production deployments never set the variable.
|
|
6
|
+
*/
|
|
7
|
+
export declare const MOCK_SESSION_COOKIE = "fs-mock-session";
|
|
8
|
+
//#endregion
|
|
9
|
+
//# sourceMappingURL=mock-session.d.ts.map
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { cookies } from "next/headers";
|
|
2
|
+
//#region src/sdk/session/mock-session.ts
|
|
3
|
+
/**
|
|
4
|
+
* The development-only mock session. A process that sets `FS_MOCK_SESSION` to a token treats a
|
|
5
|
+
* request carrying the cookie `fs-mock-session` with that same token as signed in, so the session gate can
|
|
6
|
+
* be exercised without a real shopper. Production deployments never set the variable.
|
|
7
|
+
*/
|
|
8
|
+
const MOCK_SESSION_COOKIE = "fs-mock-session";
|
|
9
|
+
/** The template route handler that sets the mock cookie and sends the shopper back. */
|
|
10
|
+
const MOCK_SESSION_ROUTE = "/api/mock-session";
|
|
11
|
+
/** The token `FS_MOCK_SESSION` carries, or `null` when the variable is unset or empty. */
|
|
12
|
+
function mockSessionToken() {
|
|
13
|
+
return process.env.FS_MOCK_SESSION || null;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Whether the request carries the mock cookie with the configured token. Fakes `loggedIn` only: no
|
|
17
|
+
* customer data comes with it. Outside a request scope `cookies()` throws, and there is no mock session.
|
|
18
|
+
*/
|
|
19
|
+
async function hasMockSession() {
|
|
20
|
+
const token = mockSessionToken();
|
|
21
|
+
if (token === null) return false;
|
|
22
|
+
try {
|
|
23
|
+
return (await cookies()).get(MOCK_SESSION_COOKIE)?.value === token;
|
|
24
|
+
} catch {
|
|
25
|
+
return false;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
//#endregion
|
|
29
|
+
export { MOCK_SESSION_COOKIE, MOCK_SESSION_ROUTE, hasMockSession, mockSessionToken };
|
|
30
|
+
|
|
31
|
+
//# sourceMappingURL=mock-session.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mock-session.js","names":[],"sources":["../../../src/sdk/session/mock-session.ts"],"sourcesContent":["import { cookies } from 'next/headers';\n\n/**\n * The development-only mock session. A process that sets `FS_MOCK_SESSION` to a token treats a\n * request carrying the cookie `fs-mock-session` with that same token as signed in, so the session gate can\n * be exercised without a real shopper. Production deployments never set the variable.\n */\nexport const MOCK_SESSION_COOKIE = 'fs-mock-session';\n\n/** The template route handler that sets the mock cookie and sends the shopper back. */\nexport const MOCK_SESSION_ROUTE = '/api/mock-session';\n\n/** The token `FS_MOCK_SESSION` carries, or `null` when the variable is unset or empty. */\nexport function mockSessionToken(): string | null {\n const token = process.env.FS_MOCK_SESSION;\n return token || null;\n}\n\n/**\n * Whether the request carries the mock cookie with the configured token. Fakes `loggedIn` only: no\n * customer data comes with it. Outside a request scope `cookies()` throws, and there is no mock session.\n */\nexport async function hasMockSession(): Promise<boolean> {\n const token = mockSessionToken();\n if (token === null) return false;\n try {\n return (await cookies()).get(MOCK_SESSION_COOKIE)?.value === token;\n } catch {\n return false;\n }\n}\n"],"mappings":";;;;;;;AAOA,MAAa,sBAAsB;;AAGnC,MAAa,qBAAqB;;AAGlC,SAAgB,mBAAkC;CAEhD,OADc,QAAQ,IAAI,mBACV;AAClB;;;;;AAMA,eAAsB,iBAAmC;CACvD,MAAM,QAAQ,iBAAiB;CAC/B,IAAI,UAAU,MAAM,OAAO;CAC3B,IAAI;EACF,QAAQ,MAAM,QAAQ,EAAA,CAAG,IAAI,mBAAmB,CAAC,EAAE,UAAU;CAC/D,QAAQ;EACN,OAAO;CACT;AACF"}
|
|
@@ -7,6 +7,9 @@ import { CreateClientOptions } from "../client/create-client.js";
|
|
|
7
7
|
* throw, a GraphQL error, a missing `session`, or a timeout is `failed`, including a partial
|
|
8
8
|
* payload that says `loggedIn: true`. The result carries no session payload. No `Cookie` header
|
|
9
9
|
* is set here, so `createClient` keeps forwarding `cookies()`.
|
|
10
|
+
*
|
|
11
|
+
* A mock session (`FS_MOCK_SESSION` plus a matching `fs-mock-session` cookie) resolves `signed-in`
|
|
12
|
+
* without querying the API, and logs `[session-gate] mock session` on every such read.
|
|
10
13
|
*/
|
|
11
14
|
export type ServerSessionRead = {
|
|
12
15
|
status: 'signed-in';
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { ServerSessionReadDocument } from "../generated/graphql.js";
|
|
2
2
|
import { createClient } from "../client/create-client.js";
|
|
3
|
+
import { hasMockSession } from "./mock-session.js";
|
|
3
4
|
import { CombinedGraphQLErrors } from "@apollo/client/errors";
|
|
4
5
|
//#region src/sdk/session/read-server-session.ts
|
|
5
6
|
/**
|
|
@@ -8,6 +9,10 @@ import { CombinedGraphQLErrors } from "@apollo/client/errors";
|
|
|
8
9
|
*/
|
|
9
10
|
const SERVER_SESSION_READ_TIMEOUT_MS = 5e3;
|
|
10
11
|
async function readServerSession(options) {
|
|
12
|
+
if (await hasMockSession()) {
|
|
13
|
+
console.warn("[session-gate] mock session");
|
|
14
|
+
return { status: "signed-in" };
|
|
15
|
+
}
|
|
11
16
|
try {
|
|
12
17
|
const result = await createClient(options).query({
|
|
13
18
|
query: ServerSessionReadDocument,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"read-server-session.js","names":[],"sources":["../../../src/sdk/session/read-server-session.ts"],"sourcesContent":["import { CombinedGraphQLErrors } from '@apollo/client/errors';\nimport type { CreateClientOptions } from '../client/create-client.js';\nimport { createClient } from '../client/create-client.js';\nimport { ServerSessionReadDocument } from '../generated/graphql.js';\n\n/**\n * Local bound for the session read. Not the content runtime's request timeout: that module must\n * stay off the server barrel.\n */\nconst SERVER_SESSION_READ_TIMEOUT_MS = 5_000;\n\n/**\n * Classifies `Query.session` for a server caller.\n *\n * Always resolves. `signed-in` is `loggedIn === true`. An identified guest is `anonymous`. A\n * throw, a GraphQL error, a missing `session`, or a timeout is `failed`, including a partial\n * payload that says `loggedIn: true`. The result carries no session payload. No `Cookie` header\n * is set here, so `createClient` keeps forwarding `cookies()`.\n */\nexport type ServerSessionRead =\n | { status: 'signed-in' }\n | { status: 'anonymous' }\n | { status: 'failed'; reason: string };\n\nexport async function readServerSession(options?: CreateClientOptions): Promise<ServerSessionRead> {\n try {\n const client = createClient(options);\n const result = await client.query({\n query: ServerSessionReadDocument,\n errorPolicy: 'all',\n fetchPolicy: 'no-cache',\n context: { fetchOptions: { signal: AbortSignal.timeout(SERVER_SESSION_READ_TIMEOUT_MS) } },\n });\n\n // A timeout lands on `result.error` under `errorPolicy: 'all'` rather than rejecting.\n if (isTimeoutError(result.error)) {\n return { status: 'failed', reason: 'timeout' };\n }\n\n if (result.error || result.data?.session == null) {\n return { status: 'failed', reason: graphqlFailureReason(result.error) };\n }\n\n if (result.data.session.loggedIn === true) {\n return { status: 'signed-in' };\n }\n\n return { status: 'anonymous' };\n } catch (error) {\n if (isTimeoutError(error)) {\n return { status: 'failed', reason: 'timeout' };\n }\n if (error instanceof Error && error.message.length > 0) {\n return { status: 'failed', reason: error.message };\n }\n return { status: 'failed', reason: 'network error' };\n }\n}\n\nfunction graphqlFailureReason(error: unknown): string {\n if (CombinedGraphQLErrors.is(error)) {\n const message = error.errors[0]?.message;\n if (typeof message === 'string' && message.length > 0) return message;\n }\n return 'graphql error';\n}\n\nfunction isTimeoutError(error: unknown): boolean {\n if (hasAbortName(error)) return true;\n if (typeof error === 'object' && error !== null && 'cause' in error) {\n return hasAbortName(error.cause);\n }\n return false;\n}\n\nfunction hasAbortName(error: unknown): boolean {\n return (\n typeof error === 'object' &&\n error !== null &&\n 'name' in error &&\n (error.name === 'TimeoutError' || error.name === 'AbortError')\n );\n}\n"],"mappings":"
|
|
1
|
+
{"version":3,"file":"read-server-session.js","names":[],"sources":["../../../src/sdk/session/read-server-session.ts"],"sourcesContent":["import { CombinedGraphQLErrors } from '@apollo/client/errors';\nimport type { CreateClientOptions } from '../client/create-client.js';\nimport { createClient } from '../client/create-client.js';\nimport { ServerSessionReadDocument } from '../generated/graphql.js';\nimport { hasMockSession } from './mock-session.js';\n\n/**\n * Local bound for the session read. Not the content runtime's request timeout: that module must\n * stay off the server barrel.\n */\nconst SERVER_SESSION_READ_TIMEOUT_MS = 5_000;\n\n/**\n * Classifies `Query.session` for a server caller.\n *\n * Always resolves. `signed-in` is `loggedIn === true`. An identified guest is `anonymous`. A\n * throw, a GraphQL error, a missing `session`, or a timeout is `failed`, including a partial\n * payload that says `loggedIn: true`. The result carries no session payload. No `Cookie` header\n * is set here, so `createClient` keeps forwarding `cookies()`.\n *\n * A mock session (`FS_MOCK_SESSION` plus a matching `fs-mock-session` cookie) resolves `signed-in`\n * without querying the API, and logs `[session-gate] mock session` on every such read.\n */\nexport type ServerSessionRead =\n | { status: 'signed-in' }\n | { status: 'anonymous' }\n | { status: 'failed'; reason: string };\n\nexport async function readServerSession(options?: CreateClientOptions): Promise<ServerSessionRead> {\n if (await hasMockSession()) {\n console.warn('[session-gate] mock session');\n return { status: 'signed-in' };\n }\n\n try {\n const client = createClient(options);\n const result = await client.query({\n query: ServerSessionReadDocument,\n errorPolicy: 'all',\n fetchPolicy: 'no-cache',\n context: { fetchOptions: { signal: AbortSignal.timeout(SERVER_SESSION_READ_TIMEOUT_MS) } },\n });\n\n // A timeout lands on `result.error` under `errorPolicy: 'all'` rather than rejecting.\n if (isTimeoutError(result.error)) {\n return { status: 'failed', reason: 'timeout' };\n }\n\n if (result.error || result.data?.session == null) {\n return { status: 'failed', reason: graphqlFailureReason(result.error) };\n }\n\n if (result.data.session.loggedIn === true) {\n return { status: 'signed-in' };\n }\n\n return { status: 'anonymous' };\n } catch (error) {\n if (isTimeoutError(error)) {\n return { status: 'failed', reason: 'timeout' };\n }\n if (error instanceof Error && error.message.length > 0) {\n return { status: 'failed', reason: error.message };\n }\n return { status: 'failed', reason: 'network error' };\n }\n}\n\nfunction graphqlFailureReason(error: unknown): string {\n if (CombinedGraphQLErrors.is(error)) {\n const message = error.errors[0]?.message;\n if (typeof message === 'string' && message.length > 0) return message;\n }\n return 'graphql error';\n}\n\nfunction isTimeoutError(error: unknown): boolean {\n if (hasAbortName(error)) return true;\n if (typeof error === 'object' && error !== null && 'cause' in error) {\n return hasAbortName(error.cause);\n }\n return false;\n}\n\nfunction hasAbortName(error: unknown): boolean {\n return (\n typeof error === 'object' &&\n error !== null &&\n 'name' in error &&\n (error.name === 'TimeoutError' || error.name === 'AbortError')\n );\n}\n"],"mappings":";;;;;;;;;AAUA,MAAM,iCAAiC;AAkBvC,eAAsB,kBAAkB,SAA2D;CACjG,IAAI,MAAM,eAAe,GAAG;EAC1B,QAAQ,KAAK,6BAA6B;EAC1C,OAAO,EAAE,QAAQ,YAAY;CAC/B;CAEA,IAAI;EAEF,MAAM,SAAS,MADA,aAAa,OACF,CAAC,CAAC,MAAM;GAChC,OAAO;GACP,aAAa;GACb,aAAa;GACb,SAAS,EAAE,cAAc,EAAE,QAAQ,YAAY,QAAQ,8BAA8B,EAAE,EAAE;EAC3F,CAAC;EAGD,IAAI,eAAe,OAAO,KAAK,GAC7B,OAAO;GAAE,QAAQ;GAAU,QAAQ;EAAU;EAG/C,IAAI,OAAO,SAAS,OAAO,MAAM,WAAW,MAC1C,OAAO;GAAE,QAAQ;GAAU,QAAQ,qBAAqB,OAAO,KAAK;EAAE;EAGxE,IAAI,OAAO,KAAK,QAAQ,aAAa,MACnC,OAAO,EAAE,QAAQ,YAAY;EAG/B,OAAO,EAAE,QAAQ,YAAY;CAC/B,SAAS,OAAO;EACd,IAAI,eAAe,KAAK,GACtB,OAAO;GAAE,QAAQ;GAAU,QAAQ;EAAU;EAE/C,IAAI,iBAAiB,SAAS,MAAM,QAAQ,SAAS,GACnD,OAAO;GAAE,QAAQ;GAAU,QAAQ,MAAM;EAAQ;EAEnD,OAAO;GAAE,QAAQ;GAAU,QAAQ;EAAgB;CACrD;AACF;AAEA,SAAS,qBAAqB,OAAwB;CACpD,IAAI,sBAAsB,GAAG,KAAK,GAAG;EACnC,MAAM,UAAU,MAAM,OAAO,EAAE,EAAE;EACjC,IAAI,OAAO,YAAY,YAAY,QAAQ,SAAS,GAAG,OAAO;CAChE;CACA,OAAO;AACT;AAEA,SAAS,eAAe,OAAyB;CAC/C,IAAI,aAAa,KAAK,GAAG,OAAO;CAChC,IAAI,OAAO,UAAU,YAAY,UAAU,QAAQ,WAAW,OAC5D,OAAO,aAAa,MAAM,KAAK;CAEjC,OAAO;AACT;AAEA,SAAS,aAAa,OAAyB;CAC7C,OACE,OAAO,UAAU,YACjB,UAAU,QACV,UAAU,UACT,MAAM,SAAS,kBAAkB,MAAM,SAAS;AAErD"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
//#region src/sdk/session/return-path-segment.d.ts
|
|
2
|
+
/**
|
|
3
|
+
* One dynamic route segment of the session gate's return path, with its leading `/`, filled from the
|
|
4
|
+
* awaited params: a `[name]` value is encoded, a `[...name]` value is encoded part by part with a `/`
|
|
5
|
+
* before each, and the absent value of an optional catch-all (`[[...name]]`) is empty, so the path
|
|
6
|
+
* ends at the literal before it with no trailing `/`.
|
|
7
|
+
*/
|
|
8
|
+
export declare function returnPathSegment(value: string | readonly string[] | undefined): string;
|
|
9
|
+
//#endregion
|
|
10
|
+
//# sourceMappingURL=return-path-segment.d.ts.map
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
//#region src/sdk/session/return-path-segment.ts
|
|
2
|
+
/**
|
|
3
|
+
* One dynamic route segment of the session gate's return path, with its leading `/`, filled from the
|
|
4
|
+
* awaited params: a `[name]` value is encoded, a `[...name]` value is encoded part by part with a `/`
|
|
5
|
+
* before each, and the absent value of an optional catch-all (`[[...name]]`) is empty, so the path
|
|
6
|
+
* ends at the literal before it with no trailing `/`.
|
|
7
|
+
*/
|
|
8
|
+
function returnPathSegment(value) {
|
|
9
|
+
if (value === void 0) return "";
|
|
10
|
+
return (typeof value === "string" ? [value] : value).map((part) => `/${encodeURIComponent(part)}`).join("");
|
|
11
|
+
}
|
|
12
|
+
//#endregion
|
|
13
|
+
export { returnPathSegment };
|
|
14
|
+
|
|
15
|
+
//# sourceMappingURL=return-path-segment.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"return-path-segment.js","names":[],"sources":["../../../src/sdk/session/return-path-segment.ts"],"sourcesContent":["/**\n * One dynamic route segment of the session gate's return path, with its leading `/`, filled from the\n * awaited params: a `[name]` value is encoded, a `[...name]` value is encoded part by part with a `/`\n * before each, and the absent value of an optional catch-all (`[[...name]]`) is empty, so the path\n * ends at the literal before it with no trailing `/`.\n */\nexport function returnPathSegment(value: string | readonly string[] | undefined): string {\n if (value === undefined) return '';\n const parts = typeof value === 'string' ? [value] : value;\n return parts.map((part) => `/${encodeURIComponent(part)}`).join('');\n}\n"],"mappings":";;;;;;;AAMA,SAAgB,kBAAkB,OAAuD;CACvF,IAAI,UAAU,KAAA,GAAW,OAAO;CAEhC,QADc,OAAO,UAAU,WAAW,CAAC,KAAK,IAAI,MAAA,CACvC,KAAK,SAAS,IAAI,mBAAmB,IAAI,GAAG,CAAC,CAAC,KAAK,EAAE;AACpE"}
|