@vtex/faststore 0.0.10 → 0.0.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +42 -0
- package/README.md +0 -1
- package/dist/actions.json +6 -1
- package/dist/catalog.json +789 -1701
- package/dist/cli/index.d.ts +66 -28
- package/dist/cli/index.js +7371 -512
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/server-runtime.js +41 -60
- package/dist/cli/server-runtime.js.map +1 -1
- package/dist/client.d.ts +3 -3
- package/dist/client.js +2 -2
- package/dist/components.d.ts +63 -57
- package/dist/components.js +60 -54
- package/dist/content-types.json +5785 -754
- package/dist/core/dist/define-component.d.ts +1 -1
- package/dist/core/dist/define-content-type.d.ts +50 -2
- package/dist/core/dist/define-content-type.js +107 -3
- package/dist/core/dist/define-content-type.js.map +1 -1
- package/dist/core/dist/index.d.ts +2 -2
- package/dist/docs/access-code-field.md +1 -1
- package/dist/docs/access-code-sign-in.md +126 -77
- package/dist/docs/account-address-book.md +210 -95
- package/dist/docs/account-menu.md +14 -15
- package/dist/docs/account-saved-cards.md +44 -39
- package/dist/docs/alert-dialog.md +3 -1
- package/dist/docs/avatar.md +4 -2
- package/dist/docs/billing-address.md +99 -30
- package/dist/docs/binding-switcher.md +5 -2
- package/dist/docs/buy-button.md +7 -8
- package/dist/docs/card-entry.md +39 -26
- package/dist/docs/card-summary.md +18 -8
- package/dist/docs/cardholder-document.md +5 -3
- package/dist/docs/carousel.md +5 -4
- package/dist/docs/cart-destination.md +39 -26
- package/dist/docs/cart-item.md +29 -14
- package/dist/docs/cart-totalizer.md +7 -5
- package/dist/docs/cart.md +135 -41
- package/dist/docs/challenge-return.md +135 -0
- package/dist/docs/client-preferences-fieldset.md +10 -5
- package/dist/docs/client-preferences.md +31 -8
- package/dist/docs/client-profile-fieldset.md +20 -6
- package/dist/docs/client-profile.md +12 -3
- package/dist/docs/copy-text.md +9 -7
- package/dist/docs/document-field.md +1 -1
- package/dist/docs/email-field.md +2 -1
- package/dist/docs/email-identification.md +12 -2
- package/dist/docs/footer-copyright.md +4 -2
- package/dist/docs/form.md +54 -18
- package/dist/docs/free-shipping-progress.md +6 -5
- package/dist/docs/identity-edit.md +8 -2
- package/dist/docs/identity-summary.md +18 -5
- package/dist/docs/installments.md +46 -32
- package/dist/docs/my-account-profile.md +16 -11
- package/dist/docs/navigation-link.md +5 -3
- package/dist/docs/order-cancel.md +3 -1
- package/dist/docs/order-delivery-address.md +7 -5
- package/dist/docs/order-delivery-group.md +1 -3
- package/dist/docs/order-details-back.md +1 -1
- package/dist/docs/order-details-header.md +1 -4
- package/dist/docs/order-invoice-download.md +60 -0
- package/dist/docs/order-list-row.md +1 -1
- package/dist/docs/order-list-search.md +13 -20
- package/dist/docs/order-list-views.md +11 -17
- package/dist/docs/order-list.md +12 -5
- package/dist/docs/order-payment.md +9 -7
- package/dist/docs/order-placed-date.md +1 -1
- package/dist/docs/order-placed-payment-summary.md +28 -15
- package/dist/docs/order-placed.md +49 -14
- package/dist/docs/order-progress.md +1 -1
- package/dist/docs/order-status-badge.md +20 -9
- package/dist/docs/order-summary.md +2 -2
- package/dist/docs/order-tracking-link.md +58 -0
- package/dist/docs/payment-method-badges.md +30 -4
- package/dist/docs/payment-methods.md +1 -1
- package/dist/docs/payment.md +36 -17
- package/dist/docs/phone-field.md +4 -4
- package/dist/docs/place-order.md +84 -36
- package/dist/docs/postal-code-field.md +3 -1
- package/dist/docs/pricing.md +21 -23
- package/dist/docs/product-gallery.md +19 -20
- package/dist/docs/product-information.md +13 -9
- package/dist/docs/product-list.md +7 -6
- package/dist/docs/product-shelf.md +0 -1
- package/dist/docs/promotion.md +4 -2
- package/dist/docs/region.md +23 -15
- package/dist/docs/save-card.md +7 -6
- package/dist/docs/saved-cards.md +65 -36
- package/dist/docs/shipping-address.md +57 -21
- package/dist/docs/shipping-simulator.md +53 -15
- package/dist/docs/sign-in-disclosure.md +176 -0
- package/dist/docs/sign-in-form.md +78 -47
- package/dist/docs/sign-in-redirect.md +4 -2
- package/dist/docs/sign-in.md +173 -0
- package/dist/docs/toast.md +15 -4
- package/dist/docs/variant-selector.md +15 -11
- package/dist/graphql/types.d.ts +4 -14
- package/dist/index.d.ts +2 -2
- package/dist/internal/client.d.ts +1 -3
- package/dist/internal/client.js +1 -1
- package/dist/internal/server.d.ts +7 -4
- package/dist/internal/server.js +7 -4
- package/dist/matter/access-code-sign-in/access-code-sign-in.d.ts +250 -66
- package/dist/matter/access-code-sign-in/access-code-sign-in.js +459 -188
- package/dist/matter/access-code-sign-in/access-code-sign-in.js.map +1 -1
- package/dist/matter/account-address-book/account-address-book-delete.d.ts +78 -40
- package/dist/matter/account-address-book/account-address-book-delete.js +86 -50
- package/dist/matter/account-address-book/account-address-book-delete.js.map +1 -1
- package/dist/matter/account-address-book/account-address-book-deliverability.d.ts +21 -4
- package/dist/matter/account-address-book/account-address-book-deliverability.js +45 -17
- package/dist/matter/account-address-book/account-address-book-deliverability.js.map +1 -1
- package/dist/matter/account-address-book/account-address-book-form-parts.d.ts +74 -19
- package/dist/matter/account-address-book/account-address-book-form-parts.js +77 -26
- package/dist/matter/account-address-book/account-address-book-form-parts.js.map +1 -1
- package/dist/matter/account-address-book/account-address-book-profile-notice.d.ts +27 -6
- package/dist/matter/account-address-book/account-address-book-profile-notice.js +55 -28
- package/dist/matter/account-address-book/account-address-book-profile-notice.js.map +1 -1
- package/dist/matter/account-address-book/account-address-book.d.ts +67 -14
- package/dist/matter/account-address-book/account-address-book.js +74 -33
- package/dist/matter/account-address-book/account-address-book.js.map +1 -1
- package/dist/matter/account-address-book/account-address-country-switch.js +3 -3
- package/dist/matter/account-address-book/account-address-country-switch.js.map +1 -1
- package/dist/matter/account-address-book/account-address-country.d.ts +66 -20
- package/dist/matter/account-address-book/account-address-country.js +83 -49
- package/dist/matter/account-address-book/account-address-country.js.map +1 -1
- package/dist/matter/account-address-book/account-address-form-context.d.ts +8 -4
- package/dist/matter/account-address-book/account-address-form-context.js.map +1 -1
- package/dist/matter/account-address-book/account-address-form.d.ts +468 -45
- package/dist/matter/account-address-book/account-address-form.js +518 -155
- package/dist/matter/account-address-book/account-address-form.js.map +1 -1
- package/dist/matter/account-address-book/account-address-record.d.ts +27 -6
- package/dist/matter/account-address-book/account-address-record.js +32 -17
- package/dist/matter/account-address-book/account-address-record.js.map +1 -1
- package/dist/matter/account-menu/account-menu.d.ts +34 -17
- package/dist/matter/account-menu/account-menu.js +46 -54
- package/dist/matter/account-menu/account-menu.js.map +1 -1
- package/dist/matter/account-menu-identity/account-menu-identity.js +1 -1
- package/dist/matter/account-saved-cards/account-saved-cards.d.ts +108 -28
- package/dist/matter/account-saved-cards/account-saved-cards.js +177 -122
- package/dist/matter/account-saved-cards/account-saved-cards.js.map +1 -1
- package/dist/matter/billing-address/billing-address.d.ts +156 -74
- package/dist/matter/billing-address/billing-address.js +227 -156
- package/dist/matter/billing-address/billing-address.js.map +1 -1
- package/dist/matter/breadcrumb/breadcrumb.d.ts +21 -3
- package/dist/matter/breadcrumb/breadcrumb.js +24 -9
- package/dist/matter/breadcrumb/breadcrumb.js.map +1 -1
- package/dist/matter/buy-button/buy-button.d.ts +8 -11
- package/dist/matter/buy-button/buy-button.js +10 -19
- package/dist/matter/buy-button/buy-button.js.map +1 -1
- package/dist/matter/card-entry/card-capture.js +12 -4
- package/dist/matter/card-entry/card-capture.js.map +1 -1
- package/dist/matter/card-entry/card-entry.d.ts +123 -32
- package/dist/matter/card-entry/card-entry.js +348 -221
- package/dist/matter/card-entry/card-entry.js.map +1 -1
- package/dist/matter/cart/cart-namespace.d.ts +7 -1
- package/dist/matter/cart/cart-namespace.js +7 -1
- package/dist/matter/cart/cart-namespace.js.map +1 -1
- package/dist/matter/cart/cart.d.ts +402 -61
- package/dist/matter/cart/cart.js +624 -153
- package/dist/matter/cart/cart.js.map +1 -1
- package/dist/matter/cart-destination/cart-destination.d.ts +30 -19
- package/dist/matter/cart-destination/cart-destination.js +160 -70
- package/dist/matter/cart-destination/cart-destination.js.map +1 -1
- package/dist/matter/cart-item/cart-item.d.ts +144 -17
- package/dist/matter/cart-item/cart-item.js +166 -49
- package/dist/matter/cart-item/cart-item.js.map +1 -1
- package/dist/matter/cart-item-compositions/cart-item-compositions.js +1 -1
- package/dist/matter/cart-item-compositions/cart-item-compositions.js.map +1 -1
- package/dist/matter/cart-item-customizations/cart-item-customizations.js +1 -1
- package/dist/matter/cart-item-customizations/cart-item-customizations.js.map +1 -1
- package/dist/matter/cart-item-discounts/cart-item-discounts.js +1 -1
- package/dist/matter/cart-item-discounts/cart-item-discounts.js.map +1 -1
- package/dist/matter/cart-item-seller-info/cart-item-seller-info.js +1 -1
- package/dist/matter/cart-item-seller-info/cart-item-seller-info.js.map +1 -1
- package/dist/matter/cart-item-variations/cart-item-variations.js +1 -1
- package/dist/matter/cart-item-variations/cart-item-variations.js.map +1 -1
- package/dist/matter/cart-totalizer/cart-totalizer.d.ts +43 -9
- package/dist/matter/cart-totalizer/cart-totalizer.js +46 -17
- package/dist/matter/cart-totalizer/cart-totalizer.js.map +1 -1
- package/dist/matter/challenge-return/challenge-return.d.ts +62 -0
- package/dist/matter/challenge-return/challenge-return.js +241 -0
- package/dist/matter/challenge-return/challenge-return.js.map +1 -0
- package/dist/matter/client-preferences/client-preferences-fieldset.d.ts +86 -17
- package/dist/matter/client-preferences/client-preferences-fieldset.js +126 -54
- package/dist/matter/client-preferences/client-preferences-fieldset.js.map +1 -1
- package/dist/matter/client-preferences/client-preferences.d.ts +50 -7
- package/dist/matter/client-preferences/client-preferences.js +84 -15
- package/dist/matter/client-preferences/client-preferences.js.map +1 -1
- package/dist/matter/client-profile/client-profile.d.ts +44 -9
- package/dist/matter/client-profile/client-profile.js +64 -24
- package/dist/matter/client-profile/client-profile.js.map +1 -1
- package/dist/matter/client-profile-form/client-profile-fieldset.d.ts +119 -22
- package/dist/matter/client-profile-form/client-profile-fieldset.js +161 -73
- package/dist/matter/client-profile-form/client-profile-fieldset.js.map +1 -1
- package/dist/matter/delivery-options/delivery-options-accordion.d.ts +49 -4
- package/dist/matter/delivery-options/delivery-options-accordion.js +67 -14
- package/dist/matter/delivery-options/delivery-options-accordion.js.map +1 -1
- package/dist/matter/delivery-options/delivery-options-controller.d.ts +69 -9
- package/dist/matter/delivery-options/delivery-options-controller.js +80 -14
- package/dist/matter/delivery-options/delivery-options-controller.js.map +1 -1
- package/dist/matter/delivery-options/delivery-options.d.ts +37 -2
- package/dist/matter/delivery-options/delivery-options.js +75 -12
- package/dist/matter/delivery-options/delivery-options.js.map +1 -1
- package/dist/matter/delivery-options/use-delivery-option-selection.d.ts +1 -0
- package/dist/matter/delivery-options/use-delivery-option-selection.js +1 -1
- package/dist/matter/delivery-options/use-delivery-option-selection.js.map +1 -1
- package/dist/matter/email-identification/email-identification.d.ts +25 -3
- package/dist/matter/email-identification/email-identification.js +49 -12
- package/dist/matter/email-identification/email-identification.js.map +1 -1
- package/dist/matter/free-shipping-progress/free-shipping-progress.d.ts +79 -7
- package/dist/matter/free-shipping-progress/free-shipping-progress.js +77 -20
- package/dist/matter/free-shipping-progress/free-shipping-progress.js.map +1 -1
- package/dist/matter/identity-edit/identity-edit.d.ts +52 -10
- package/dist/matter/identity-edit/identity-edit.js +47 -19
- package/dist/matter/identity-edit/identity-edit.js.map +1 -1
- package/dist/matter/identity-summary/identity-summary.d.ts +76 -22
- package/dist/matter/identity-summary/identity-summary.js +95 -45
- package/dist/matter/identity-summary/identity-summary.js.map +1 -1
- package/dist/matter/installments/installments-select.d.ts +98 -16
- package/dist/matter/installments/installments-select.js +156 -48
- package/dist/matter/installments/installments-select.js.map +1 -1
- package/dist/matter/installments/installments.d.ts +96 -16
- package/dist/matter/installments/installments.js +115 -40
- package/dist/matter/installments/installments.js.map +1 -1
- package/dist/matter/installments/use-installments-selection.d.ts +1 -0
- package/dist/matter/installments/use-installments-selection.js +8 -3
- package/dist/matter/installments/use-installments-selection.js.map +1 -1
- package/dist/matter/my-account-profile/my-account-profile.d.ts +358 -103
- package/dist/matter/my-account-profile/my-account-profile.js +370 -174
- package/dist/matter/my-account-profile/my-account-profile.js.map +1 -1
- package/dist/matter/order-cancel/order-cancel.d.ts +65 -19
- package/dist/matter/order-cancel/order-cancel.js +118 -51
- package/dist/matter/order-cancel/order-cancel.js.map +1 -1
- package/dist/matter/order-delivery-address/order-delivery-address.d.ts +8 -18
- package/dist/matter/order-delivery-address/order-delivery-address.js +14 -23
- package/dist/matter/order-delivery-address/order-delivery-address.js.map +1 -1
- package/dist/matter/order-delivery-group/order-delivery-group.d.ts +40 -24
- package/dist/matter/order-delivery-group/order-delivery-group.js +64 -74
- package/dist/matter/order-delivery-group/order-delivery-group.js.map +1 -1
- package/dist/matter/order-details-header/order-details-header.d.ts +32 -15
- package/dist/matter/order-details-header/order-details-header.js +66 -58
- package/dist/matter/order-details-header/order-details-header.js.map +1 -1
- package/dist/matter/order-invoice-download/order-invoice-download.d.ts +78 -0
- package/dist/matter/order-invoice-download/order-invoice-download.js +173 -0
- package/dist/matter/order-invoice-download/order-invoice-download.js.map +1 -0
- package/dist/matter/order-list/order-list.d.ts +164 -19
- package/dist/matter/order-list/order-list.js +229 -87
- package/dist/matter/order-list/order-list.js.map +1 -1
- package/dist/matter/order-list-row/order-list-row.d.ts +6 -1
- package/dist/matter/order-list-row/order-list-row.js +3 -2
- package/dist/matter/order-list-row/order-list-row.js.map +1 -1
- package/dist/matter/order-list-search/order-list-search.d.ts +18 -26
- package/dist/matter/order-list-search/order-list-search.js +30 -69
- package/dist/matter/order-list-search/order-list-search.js.map +1 -1
- package/dist/matter/order-list-views/order-list-views.d.ts +15 -22
- package/dist/matter/order-list-views/order-list-views.js +22 -68
- package/dist/matter/order-list-views/order-list-views.js.map +1 -1
- package/dist/matter/order-payment/order-payment.d.ts +33 -10
- package/dist/matter/order-payment/order-payment.js +44 -39
- package/dist/matter/order-payment/order-payment.js.map +1 -1
- package/dist/matter/order-placed/order-placed-deliveries.d.ts +144 -12
- package/dist/matter/order-placed/order-placed-deliveries.js +115 -40
- package/dist/matter/order-placed/order-placed-deliveries.js.map +1 -1
- package/dist/matter/order-placed/order-placed.d.ts +198 -31
- package/dist/matter/order-placed/order-placed.js +230 -139
- package/dist/matter/order-placed/order-placed.js.map +1 -1
- package/dist/matter/order-placed-date/order-placed-date.d.ts +5 -8
- package/dist/matter/order-placed-date/order-placed-date.js +7 -8
- package/dist/matter/order-placed-date/order-placed-date.js.map +1 -1
- package/dist/matter/order-placed-payment-summary/order-placed-payment-summary.d.ts +90 -17
- package/dist/matter/order-placed-payment-summary/order-placed-payment-summary.js +93 -117
- package/dist/matter/order-placed-payment-summary/order-placed-payment-summary.js.map +1 -1
- package/dist/matter/order-progress/order-progress.d.ts +19 -8
- package/dist/matter/order-progress/order-progress.js +37 -45
- package/dist/matter/order-progress/order-progress.js.map +1 -1
- package/dist/matter/order-summary/order-summary.d.ts +19 -9
- package/dist/matter/order-summary/order-summary.js +38 -44
- package/dist/matter/order-summary/order-summary.js.map +1 -1
- package/dist/matter/order-tracking-link/order-tracking-link.d.ts +43 -0
- package/dist/matter/order-tracking-link/order-tracking-link.js +107 -0
- package/dist/matter/order-tracking-link/order-tracking-link.js.map +1 -0
- package/dist/matter/payment/payment-card-capture.js +17 -10
- package/dist/matter/payment/payment-card-capture.js.map +1 -1
- package/dist/matter/payment/payment-controller.d.ts +4 -2
- package/dist/matter/payment/payment-controller.js +30 -9
- package/dist/matter/payment/payment-controller.js.map +1 -1
- package/dist/matter/payment/payment-method-badges.js +1 -1
- package/dist/matter/payment/payment.d.ts +69 -19
- package/dist/matter/payment/payment.js +101 -35
- package/dist/matter/payment/payment.js.map +1 -1
- package/dist/matter/payment/use-payment-method-selection.d.ts +25 -4
- package/dist/matter/payment/use-payment-method-selection.js +94 -32
- package/dist/matter/payment/use-payment-method-selection.js.map +1 -1
- package/dist/matter/payment/use-payment-submit-gate.d.ts +14 -3
- package/dist/matter/payment/use-payment-submit-gate.js +47 -8
- package/dist/matter/payment/use-payment-submit-gate.js.map +1 -1
- package/dist/matter/place-order/place-order.d.ts +171 -57
- package/dist/matter/place-order/place-order.js +376 -185
- package/dist/matter/place-order/place-order.js.map +1 -1
- package/dist/matter/pricing/pricing.d.ts +152 -13
- package/dist/matter/pricing/pricing.js +153 -52
- package/dist/matter/pricing/pricing.js.map +1 -1
- package/dist/matter/product-information/product-information.d.ts +27 -4
- package/dist/matter/product-information/product-information.js +32 -15
- package/dist/matter/product-information/product-information.js.map +1 -1
- package/dist/matter/product-list/product-list.d.ts +24 -6
- package/dist/matter/product-list/product-list.js +32 -26
- package/dist/matter/product-list/product-list.js.map +1 -1
- package/dist/matter/product-shelf/product-shelf.d.ts +1 -3
- package/dist/matter/product-shelf/product-shelf.js +2 -15
- package/dist/matter/product-shelf/product-shelf.js.map +1 -1
- package/dist/matter/region/region.d.ts +33 -16
- package/dist/matter/region/region.js +59 -52
- package/dist/matter/region/region.js.map +1 -1
- package/dist/matter/save-card/save-card.d.ts +43 -8
- package/dist/matter/save-card/save-card.js +61 -23
- package/dist/matter/save-card/save-card.js.map +1 -1
- package/dist/matter/saved-cards/saved-cards.d.ts +113 -21
- package/dist/matter/saved-cards/saved-cards.js +349 -77
- package/dist/matter/saved-cards/saved-cards.js.map +1 -1
- package/dist/matter/shipping-address/address-payload.js +25 -0
- package/dist/matter/shipping-address/address-payload.js.map +1 -0
- package/dist/matter/shipping-address/country-descriptors.js +71 -71
- package/dist/matter/shipping-address/country-descriptors.js.map +1 -1
- package/dist/matter/shipping-address/library-country-descriptors.js +136 -0
- package/dist/matter/shipping-address/library-country-descriptors.js.map +1 -0
- package/dist/matter/shipping-address/shipping-address.d.ts +312 -45
- package/dist/matter/shipping-address/shipping-address.js +443 -188
- package/dist/matter/shipping-address/shipping-address.js.map +1 -1
- package/dist/matter/shipping-simulator/shipping-simulator.d.ts +250 -24
- package/dist/matter/shipping-simulator/shipping-simulator.js +257 -51
- package/dist/matter/shipping-simulator/shipping-simulator.js.map +1 -1
- package/dist/matter/sign-in/sign-in.d.ts +108 -0
- package/dist/matter/sign-in/sign-in.js +285 -0
- package/dist/matter/sign-in/sign-in.js.map +1 -0
- package/dist/matter/sign-in-disclosure/sign-in-disclosure.d.ts +81 -0
- package/dist/matter/sign-in-disclosure/sign-in-disclosure.js +232 -0
- package/dist/matter/sign-in-disclosure/sign-in-disclosure.js.map +1 -0
- package/dist/matter/sign-in-form/sign-in-form.d.ts +93 -62
- package/dist/matter/sign-in-form/sign-in-form.js +134 -144
- package/dist/matter/sign-in-form/sign-in-form.js.map +1 -1
- package/dist/particles/binding-switcher/binding-switcher.d.ts +29 -8
- package/dist/particles/binding-switcher/binding-switcher.js +37 -26
- package/dist/particles/binding-switcher/binding-switcher.js.map +1 -1
- package/dist/particles/card-summary/card-summary.d.ts +38 -8
- package/dist/particles/card-summary/card-summary.js +55 -22
- package/dist/particles/card-summary/card-summary.js.map +1 -1
- package/dist/particles/carousel/carousel.d.ts +99 -11
- package/dist/particles/carousel/carousel.js +103 -35
- package/dist/particles/carousel/carousel.js.map +1 -1
- package/dist/particles/copy-text/copy-text.js +5 -15
- package/dist/particles/copy-text/copy-text.js.map +1 -1
- package/dist/particles/dialog/dialog.d.ts +14 -6
- package/dist/particles/dialog/dialog.js +6 -3
- package/dist/particles/dialog/dialog.js.map +1 -1
- package/dist/particles/drawer/drawer.js +1 -1
- package/dist/particles/drawer/drawer.js.map +1 -1
- package/dist/particles/email-field/email-field.d.ts +2 -1
- package/dist/particles/email-field/email-field.js +2 -1
- package/dist/particles/email-field/email-field.js.map +1 -1
- package/dist/particles/footer-link-group/footer-link-group.js +1 -0
- package/dist/particles/footer-link-group/footer-link-group.js.map +1 -1
- package/dist/particles/form/form.d.ts +85 -13
- package/dist/particles/form/form.js +91 -42
- package/dist/particles/form/form.js.map +1 -1
- package/dist/particles/gallery/gallery.d.ts +129 -12
- package/dist/particles/gallery/gallery.js +121 -34
- package/dist/particles/gallery/gallery.js.map +1 -1
- package/dist/particles/order-details-back/order-details-back.d.ts +5 -3
- package/dist/particles/order-details-back/order-details-back.js +8 -10
- package/dist/particles/order-details-back/order-details-back.js.map +1 -1
- package/dist/particles/order-status-badge/order-status-badge.d.ts +3 -3
- package/dist/particles/order-status-badge/order-status-badge.js +4 -20
- package/dist/particles/order-status-badge/order-status-badge.js.map +1 -1
- package/dist/particles/password-field/password-field.d.ts +1 -1
- package/dist/particles/password-field/password-field.js +2 -4
- package/dist/particles/password-field/password-field.js.map +1 -1
- package/dist/particles/phone-field/phone-field.d.ts +4 -4
- package/dist/particles/phone-field/phone-field.js +8 -11
- package/dist/particles/phone-field/phone-field.js.map +1 -1
- package/dist/particles/spinner/spinner.d.ts +24 -4
- package/dist/particles/spinner/spinner.js +25 -9
- package/dist/particles/spinner/spinner.js.map +1 -1
- package/dist/particles/switch/switch.d.ts +4 -1
- package/dist/particles/switch/switch.js +3 -1
- package/dist/particles/switch/switch.js.map +1 -1
- package/dist/particles/toast/toast.d.ts +29 -3
- package/dist/particles/toast/toast.js +34 -10
- package/dist/particles/toast/toast.js.map +1 -1
- package/dist/schema.graphql +164 -56
- package/dist/sdk/address/address-fields.js +4 -4
- package/dist/sdk/address/address-fields.js.map +1 -1
- package/dist/sdk/address/constants.js +1 -6
- package/dist/sdk/address/constants.js.map +1 -1
- package/dist/sdk/address/messages.js +44 -44
- package/dist/sdk/address/messages.js.map +1 -1
- package/dist/sdk/address/postal-code.js +15 -2
- package/dist/sdk/address/postal-code.js.map +1 -1
- package/dist/sdk/address/types.d.ts +5 -3
- package/dist/sdk/address/validate-address.js +3 -3
- package/dist/sdk/address/validate-address.js.map +1 -1
- package/dist/sdk/analytics/use-analytics-actions.d.ts +1 -1
- package/dist/sdk/analytics/use-analytics-actions.js.map +1 -1
- package/dist/sdk/cart/use-cart-actions.js.map +1 -1
- package/dist/sdk/data/index.js +2 -2
- package/dist/sdk/data/reactive-list-item-data-provider.js +2 -2
- package/dist/sdk/generated/gql.js +1 -1
- package/dist/sdk/generated/gql.js.map +1 -1
- package/dist/sdk/generated/graphql.d.ts +20 -5
- package/dist/sdk/generated/graphql.js +6 -0
- package/dist/sdk/generated/graphql.js.map +1 -1
- package/dist/sdk/i18n/format-error-code.js +23 -0
- package/dist/sdk/i18n/format-error-code.js.map +1 -0
- package/dist/sdk/i18n/message-formatter.d.ts +22 -0
- package/dist/sdk/i18n/message-formatter.js +140 -0
- package/dist/sdk/i18n/message-formatter.js.map +1 -0
- package/dist/sdk/i18n/use-message-formatter.d.ts +8 -0
- package/dist/sdk/i18n/use-message-formatter.js +18 -0
- package/dist/sdk/i18n/use-message-formatter.js.map +1 -0
- package/dist/sdk/identity/use-identity-actions.js.map +1 -1
- package/dist/sdk/navigation/redirect.d.ts +2 -0
- package/dist/sdk/navigation/redirect.js +19 -1
- package/dist/sdk/navigation/redirect.js.map +1 -1
- package/dist/sdk/navigation/types.d.ts +13 -0
- package/dist/sdk/payment/bin-format.js +20 -0
- package/dist/sdk/payment/bin-format.js.map +1 -0
- package/dist/sdk/payment/challenge-return.js +37 -0
- package/dist/sdk/payment/challenge-return.js.map +1 -0
- package/dist/sdk/payment/confirmation-last-four.js +39 -0
- package/dist/sdk/payment/confirmation-last-four.js.map +1 -0
- package/dist/sdk/payment/gateway.d.ts +1 -1
- package/dist/sdk/payment/gateway.js +8 -2
- package/dist/sdk/payment/gateway.js.map +1 -1
- package/dist/sdk/payment/operations.d.ts +1 -0
- package/dist/sdk/payment/operations.js +28 -6
- package/dist/sdk/payment/operations.js.map +1 -1
- package/dist/sdk/payment/resumed-purchase.d.ts +1 -0
- package/dist/sdk/payment/resumed-purchase.js +119 -0
- package/dist/sdk/payment/resumed-purchase.js.map +1 -0
- package/dist/sdk/payment/security-code-mask.js +16 -23
- package/dist/sdk/payment/security-code-mask.js.map +1 -1
- package/dist/sdk/payment/submission-registry.js +6 -2
- package/dist/sdk/payment/submission-registry.js.map +1 -1
- package/dist/sdk/payment/use-payment-actions.d.ts +18 -3
- package/dist/sdk/payment/use-payment-actions.js +23 -3
- package/dist/sdk/payment/use-payment-actions.js.map +1 -1
- package/dist/sdk/payment/use-place-order.d.ts +10 -2
- package/dist/sdk/payment/use-place-order.js +138 -76
- package/dist/sdk/payment/use-place-order.js.map +1 -1
- package/dist/sdk/provider/storefront-provider.d.ts +1 -7
- package/dist/sdk/provider/storefront-provider.js +4 -11
- package/dist/sdk/provider/storefront-provider.js.map +1 -1
- package/dist/sdk/seo/entity-landing.d.ts +24 -0
- package/dist/sdk/seo/entity-landing.js +30 -0
- package/dist/sdk/seo/entity-landing.js.map +1 -0
- package/dist/sdk/seo/page-alternates.d.ts +3 -2
- package/dist/sdk/seo/page-alternates.js +4 -2
- package/dist/sdk/seo/page-alternates.js.map +1 -1
- package/dist/sdk/seo/search-landing.d.ts +42 -0
- package/dist/sdk/seo/search-landing.js +85 -0
- package/dist/sdk/seo/search-landing.js.map +1 -0
- package/dist/sdk/session/mock-session.d.ts +9 -0
- package/dist/sdk/session/mock-session.js +31 -0
- package/dist/sdk/session/mock-session.js.map +1 -0
- package/dist/sdk/session/read-server-session.d.ts +3 -0
- package/dist/sdk/session/read-server-session.js +5 -0
- package/dist/sdk/session/read-server-session.js.map +1 -1
- package/dist/sdk/session/return-path-segment.d.ts +10 -0
- package/dist/sdk/session/return-path-segment.js +15 -0
- package/dist/sdk/session/return-path-segment.js.map +1 -0
- package/dist/sdk/session/use-session-actions.js.map +1 -1
- package/dist/sdk/shipping/use-cart-shipping-simulation.js.map +1 -1
- package/dist/sdk/shipping/use-shipping-actions.js.map +1 -1
- package/dist/sdk/shipping/use-shipping-simulation.js.map +1 -1
- package/dist/utils/safe-href.d.ts +2 -0
- package/package.json +4 -3
- package/skills/storefront-asset/SKILL.md +2 -2
- package/skills/storefront-asset/references/api-side.md +27 -14
- package/skills/storefront-asset/references/findings.md +5 -2
- package/src/styles/tailwind.css +8 -0
- package/dist/matter/shipping-address/country-descriptors.d.ts +0 -1
- package/dist/message-locales.json +0 -7
- package/dist/sdk/i18n/client.d.ts +0 -4
- package/dist/sdk/i18n/client.js +0 -4
- package/dist/sdk/i18n/locales.d.ts +0 -8
- package/dist/sdk/i18n/locales.js +0 -57
- package/dist/sdk/i18n/locales.js.map +0 -1
- package/dist/sdk/i18n/messages/en-US.js +0 -183
- package/dist/sdk/i18n/messages/en-US.js.map +0 -1
- package/dist/sdk/i18n/messages/en-US.json +0 -178
- package/dist/sdk/i18n/messages/en-US.json.d.ts +0 -182
- package/dist/sdk/i18n/messages/es-ES.js +0 -183
- package/dist/sdk/i18n/messages/es-ES.js.map +0 -1
- package/dist/sdk/i18n/messages/es-ES.json +0 -178
- package/dist/sdk/i18n/messages/pt-BR.js +0 -183
- package/dist/sdk/i18n/messages/pt-BR.js.map +0 -1
- package/dist/sdk/i18n/messages/pt-BR.json +0 -178
- package/dist/sdk/i18n/provider.d.ts +0 -1
- package/dist/sdk/i18n/provider.js +0 -30
- package/dist/sdk/i18n/provider.js.map +0 -1
- package/dist/sdk/i18n/server.d.ts +0 -28
- package/dist/sdk/i18n/server.js +0 -59
- package/dist/sdk/i18n/server.js.map +0 -1
- package/dist/utils/resolve-authored-copy.js +0 -18
- package/dist/utils/resolve-authored-copy.js.map +0 -1
- package/dist/utils/substitute-template.js +0 -33
- package/dist/utils/substitute-template.js.map +0 -1
|
@@ -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;
|
|
@@ -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
|
|
@@ -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. */\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":";;;;;;;;AAkCA,MAAa,iCAAiC,QAAQ;;;;;;;;;CASrD;;AAGD,SAAgB,WAAW,MAAsB;CAC/C,OAAO,KAAK,MAAM,GAAG,CAAC,CAAC,IAAI,kBAAkB,CAAC,CAAC,KAAK,GAAG;AACzD;;;;;AAMA,SAAS,gBAAgB,UAAuC,kBAA8C;CAC5G,MAAM,2BAAW,IAAI,IAA8B;CACnD,KAAK,MAAM,WAAW,UAAU;EAC9B,MAAM,MAAM,QAAQ,OAAO,YAAY;EAEvC,IAAI,CADY,SAAS,IAAI,GAClB,KAAK,QAAQ,cAAc,kBAAkB,SAAS,IAAI,KAAK,OAAO;CACnF;CACA,OAAO,SAAS,QAAQ,YAAY,SAAS,IAAI,QAAQ,OAAO,YAAY,CAAC,MAAM,OAAO;AAC5F;;;;;AAMA,SAAgB,oBAAoB,EAClC,WACA,kBACA,UACA,UACA,cACA,YAC6C;CAC7C,MAAM,OAAO,IAAY,SAAiB,SAAS,IAAI,WAAW,IAAI,CAAC;CACvE,MAAM,UAAU,OAAgB,gBAAgB,OAAO,OAAO,cAAc,EAAE,IAAI,aAAa,MAAM,KAAA;CACrG,MAAM,YAAY,IAAI,WAAW,OAAO,SAAS,KAAK,QAAQ;CAE9D,MAAM,UAAU,gBAAgB,UAAU,gBAAgB,CAAC,CAAC,SAAS,YAAY;EAC/E,MAAM,OAAO,OAAO,QAAQ,SAAS;EACrC,OAAO,SAAS,KAAA,IAAY,CAAC,IAAI,CAAC;GAAE,GAAG;GAAS,MAAM,IAAI,QAAQ,WAAW,IAAI;EAAE,CAAC;CACtF,CAAC;CACD,IAAI,QAAQ,SAAS,KAAK,CAAC,QAAQ,MAAM,WAAW,OAAO,cAAc,SAAS,GAAG,OAAO,EAAE,UAAU;CAExG,MAAM,YAAoC,CAAC;CAC3C,KAAK,MAAM,UAAU,SAAS,UAAU,OAAO,UAAU,OAAO;CAChE,MAAM,gBAAgB,QAAQ,MAAM,WAAW,OAAO,cAAc,gBAAgB;CACpF,IAAI,eAAe,UAAU,eAAe,cAAc;CAC1D,OAAO;EAAE;EAAW;CAAU;AAChC;;AAGA,SAAS,cAAc,SAA0B;CAC/C,OAAO,YAAY,MAAM,YAAY,OAAO,YAAY;AAC1D;;;;;;;;AASA,SAAgB,iBAAiB,OAAe,QAA6B;CAU3E,OAAO,IATU,MACd,MAAM,GAAG,CAAC,CACV,OAAO,OAAO,CAAC,CACf,SAAS,YAAY;EACpB,MAAM,UAAU,sCAAsC,KAAK,OAAO;EAClE,IAAI,CAAC,SAAS,OAAO,CAAC,OAAO;EAC7B,OAAO,CAAC,OAAO,QAAQ,OAAiB,CAAC,CAAC,CAAC,CAAC,KAAK;CACnD,CAAC,CAAC,CACD,OAAO,aACQ,CAAC,CAAC,KAAK,GAAG;AAC9B;AAEA,SAAS,aAAa,MAA+B;CACnD,MAAM,WAAY,MAAkE,cAAc;CAClG,IAAI,CAAC,MAAM,QAAQ,QAAQ,GAAG,OAAO,CAAC;CACtC,OAAO,SAAS,QACb,YAAqC,OAAO,SAAS,WAAW,YAAY,OAAO,SAAS,SAAS,QACxG;AACF;;AAGA,SAAS,sBACP,UACA,UACwB;CACxB,MAAM,QAAgC,CAAC;CACvC,KAAK,MAAM,WAAW,UAAU;EAC9B,MAAM,SAAS,QAAQ,OAAO,YAAY;EAC1C,MAAM,UAAU,SAAS,MAAM,cAAc,UAAU,OAAO,YAAY,MAAM,MAAM;EACtF,IAAI,SAAS,MAAM,QAAQ,aAAa,QAAQ;CAClD;CACA,OAAO;AACT;;;;;AAMA,SAAS,eAAe,OAA+B;CACrD,IAAI,CAAC,sBAAsB,GAAG,KAAK,GAAG,OAAO,kBAAkB,KAAK;CACpE,MAAM,QAAQ,MAAM,OAAO,KAAK,EAAE,iBAAiB,YAAY,IAAI;CACnE,IAAI,MAAM,SAAS,KAAK,MAAM,OAAO,cAAc,cAAc,gBAAgB,GAAG,OAAO;CAI3F,OAHa,MAAM,MAChB,cAAmC,OAAO,cAAc,YAAY,cAAc,gBAE3E,KAAK;AACjB;AAEA,SAAS,aAAa,OAAsB;CAC1C,MAAM,SAAS,eAAe,KAAK;CACnC,IAAI,QAAQ,QAAQ,KAAK,wEAAwE,MAAM;CACvG,OAAO;AACT;;;;;;;AAQA,eAAsB,oBACpB,UACA,MACwC;CACxC,IAAI;EACF,MAAM,EAAE,MAAM,UAAU,MAAM,KAAK;EACnC,IAAI,OAAO,OAAO,aAAa,KAAK;EACpC,MAAM,QAAQ,sBAAsB,UAAU,aAAa,IAAI,CAAC;EAChE,OAAO,OAAO,KAAK,KAAK,CAAC,CAAC,SAAS,IAAI,QAAQ;CACjD,SAAS,OAAO;EACd,OAAO,aAAa,KAAK;CAC3B;AACF"}
|
|
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":";;;;;;;;;AAmCA,MAAa,iCAAiC,QAAQ;;;;;;;;;;CAUrD;;AAGD,SAAgB,WAAW,MAAsB;CAC/C,OAAO,KAAK,MAAM,GAAG,CAAC,CAAC,IAAI,kBAAkB,CAAC,CAAC,KAAK,GAAG;AACzD;;;;;AAMA,SAAS,gBAAgB,UAAuC,kBAA8C;CAC5G,MAAM,2BAAW,IAAI,IAA8B;CACnD,KAAK,MAAM,WAAW,UAAU;EAC9B,MAAM,MAAM,QAAQ,OAAO,YAAY;EAEvC,IAAI,CADY,SAAS,IAAI,GAClB,KAAK,QAAQ,cAAc,kBAAkB,SAAS,IAAI,KAAK,OAAO;CACnF;CACA,OAAO,SAAS,QAAQ,YAAY,SAAS,IAAI,QAAQ,OAAO,YAAY,CAAC,MAAM,OAAO;AAC5F;;;;;AAMA,SAAgB,oBAAoB,EAClC,WACA,kBACA,UACA,UACA,cACA,YAC6C;CAC7C,MAAM,OAAO,IAAY,SAAiB,SAAS,IAAI,WAAW,IAAI,CAAC;CACvE,MAAM,UAAU,OAAgB,gBAAgB,OAAO,OAAO,cAAc,EAAE,IAAI,aAAa,MAAM,KAAA;CACrG,MAAM,YAAY,IAAI,WAAW,OAAO,SAAS,KAAK,QAAQ;CAE9D,MAAM,UAAU,gBAAgB,UAAU,gBAAgB,CAAC,CAAC,SAAS,YAAY;EAC/E,MAAM,OAAO,OAAO,QAAQ,SAAS;EACrC,OAAO,SAAS,KAAA,IAAY,CAAC,IAAI,CAAC;GAAE,GAAG;GAAS,MAAM,IAAI,QAAQ,WAAW,IAAI;EAAE,CAAC;CACtF,CAAC;CACD,IAAI,QAAQ,SAAS,KAAK,CAAC,QAAQ,MAAM,WAAW,OAAO,cAAc,SAAS,GAAG,OAAO,EAAE,UAAU;CAExG,MAAM,YAAoC,CAAC;CAC3C,KAAK,MAAM,UAAU,SAAS,UAAU,OAAO,UAAU,OAAO;CAChE,MAAM,gBAAgB,QAAQ,MAAM,WAAW,OAAO,cAAc,gBAAgB;CACpF,IAAI,eAAe,UAAU,eAAe,cAAc;CAC1D,OAAO;EAAE;EAAW;CAAU;AAChC;;AAGA,SAAS,cAAc,SAA0B;CAC/C,OAAO,YAAY,MAAM,YAAY,OAAO,YAAY;AAC1D;;;;;;;;AASA,SAAgB,iBAAiB,OAAe,QAA6B;CAU3E,OAAO,IATU,MACd,MAAM,GAAG,CAAC,CACV,OAAO,OAAO,CAAC,CACf,SAAS,YAAY;EACpB,MAAM,UAAU,sCAAsC,KAAK,OAAO;EAClE,IAAI,CAAC,SAAS,OAAO,CAAC,OAAO;EAC7B,OAAO,CAAC,OAAO,QAAQ,OAAiB,CAAC,CAAC,CAAC,CAAC,KAAK;CACnD,CAAC,CAAC,CACD,OAAO,aACQ,CAAC,CAAC,KAAK,GAAG;AAC9B;AAEA,SAAS,aAAa,MAA+B;CACnD,MAAM,WAAY,MAAkE,cAAc;CAClG,IAAI,CAAC,MAAM,QAAQ,QAAQ,GAAG,OAAO,CAAC;CACtC,OAAO,SAAS,QACb,YAAqC,OAAO,SAAS,WAAW,YAAY,OAAO,SAAS,SAAS,QACxG;AACF;;AAGA,SAAS,sBACP,UACA,UACwB;CACxB,MAAM,QAAgC,CAAC;CACvC,KAAK,MAAM,WAAW,UAAU;EAC9B,MAAM,SAAS,QAAQ,OAAO,YAAY;EAC1C,MAAM,UAAU,SAAS,MAAM,cAAc,UAAU,OAAO,YAAY,MAAM,MAAM;EACtF,IAAI,SAAS,MAAM,QAAQ,aAAa,QAAQ;CAClD;CACA,OAAO;AACT;;;;;AAMA,SAAS,eAAe,OAA+B;CACrD,IAAI,CAAC,sBAAsB,GAAG,KAAK,GAAG,OAAO,kBAAkB,KAAK;CACpE,MAAM,QAAQ,MAAM,OAAO,KAAK,EAAE,iBAAiB,YAAY,IAAI;CACnE,IAAI,MAAM,SAAS,KAAK,MAAM,OAAO,cAAc,cAAc,gBAAgB,GAAG,OAAO;CAI3F,OAHa,MAAM,MAChB,cAAmC,OAAO,cAAc,YAAY,cAAc,gBAE3E,KAAK;AACjB;AAEA,SAAS,aAAa,OAAsB;CAC1C,MAAM,SAAS,eAAe,KAAK;CACnC,IAAI,QAAQ,QAAQ,KAAK,wEAAwE,MAAM;CACvG,OAAO;AACT;;;;;;;AAQA,eAAsB,oBACpB,UACA,MACwC;CACxC,IAAI;EACF,MAAM,EAAE,MAAM,UAAU,MAAM,KAAK;EACnC,IAAI,OAAO,OAAO,aAAa,KAAK;EACpC,MAAM,QAAQ,sBAAsB,UAAU,aAAa,IAAI,CAAC;EAChE,OAAO,OAAO,KAAK,KAAK,CAAC,CAAC,SAAS,IAAI,QAAQ;CACjD,SAAS,OAAO;EACd,OAAO,aAAa,KAAK;CAC3B;AACF"}
|
|
@@ -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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"use-session-actions.js","names":[],"sources":["../../../src/sdk/session/use-session-actions.ts"],"sourcesContent":["'use client';\n\nimport { 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 {\n SESSION_LOGIN_WITH_PASSWORD,\n SESSION_LOGOUT,\n SESSION_REQUEST_ACCESS_CODE,\n SESSION_VALIDATE_ACCESS_CODE,\n type SessionAccessCodeRequestResult,\n type SessionPasswordLoginPayload,\n type SessionRequestAccessCodePayload,\n type SessionUserError,\n type SessionValidateAccessCodePayload,\n} from './auth-operations.js';\n\n/**\n * The `session` action namespace — the one sanctioned client write surface for authentication\n * (docs/storefront-core/reference-free-particle-model.md § Write side — actions, docs/storefront-core/adr-0010-actions-own-the-write-side.md).\n *\n * Every action is async/pessimistic (Decision 1): the handler awaits the mutation and only the\n * server-confirmed payload changes the cache — authentication cannot be guessed client-side. The\n * handlers are stateless: no reactive vars, no module state; the access-code `authenticationToken`\n * is returned to the caller, which owns it as UI flow state (Decision 4). Each action resolves its\n * payload `userErrors` (empty on success) and never throws for expected outcomes; infrastructure\n * failures resolve as the synthetic `NETWORK_ERROR` (Decision 5). The credentials pass through\n * mutation variables only — never logged, never stored.\n */\nexport type SessionActions = Readonly<{\n /** Password sign-in. Async/pessimistic; resolves the payload userErrors (empty on success). */\n loginWithPassword: (payload: SessionPasswordLoginPayload) => Promise<readonly SessionUserError[]>;\n /** Email an access code; resolves the opaque authenticationToken to round-trip into validate. */\n requestAccessCode: (payload: SessionRequestAccessCodePayload) => Promise<SessionAccessCodeRequestResult>;\n /** Validate an access code + token; logs the shopper in exactly as a password login would. */\n validateAccessCode: (payload: SessionValidateAccessCodePayload) => Promise<readonly SessionUserError[]>;\n /** Sign out. Idempotent for guests. */\n logout: () => Promise<readonly SessionUserError[]>;\n}>;\n\nconst NETWORK_USER_ERROR: SessionUserError = {\n code: 'NETWORK_ERROR',\n message: 'Unable to complete the request. Please try again.',\n};\n\n/**\n * The `session` namespace hook. Registered with the first-party action registry via `sessionActions`\n * below; the compiler-emitted `SessionActionProvider` calls it and threads the narrowed methods into\n * each consumer's `$actions.session`. Login/validate/logout refresh the session purely through the\n * normalized-cache merge of the mutation response (Decision 2) — no refetch, no imperative write.\n */\nexport function useSessionActions(): SessionActions {\n const [loginMutation] = useMutation(SESSION_LOGIN_WITH_PASSWORD);\n const [requestMutation] = useMutation(SESSION_REQUEST_ACCESS_CODE);\n const [validateMutation] = useMutation(SESSION_VALIDATE_ACCESS_CODE);\n const [logoutMutation] = useMutation(SESSION_LOGOUT);\n\n const loginWithPassword = useCallback<SessionActions['loginWithPassword']>(\n async (payload) => {\n try {\n const result = await loginMutation({ variables: { input: payload } });\n return result.data?.sessionLoginWithPassword.userErrors ?? [];\n } catch (error) {\n console.error('[session.loginWithPassword] sessionLoginWithPassword failed', loggableErrorName(error));\n return [NETWORK_USER_ERROR];\n }\n },\n [loginMutation],\n );\n\n const requestAccessCode = useCallback<SessionActions['requestAccessCode']>(\n async (payload) => {\n try {\n const result = await requestMutation({ variables: { input: payload } });\n const data = result.data?.sessionRequestAccessCode;\n return { authenticationToken: data?.authenticationToken ?? null, userErrors: data?.userErrors ?? [] };\n } catch (error) {\n console.error('[session.requestAccessCode] sessionRequestAccessCode failed', loggableErrorName(error));\n return { authenticationToken: null, userErrors: [NETWORK_USER_ERROR] };\n }\n },\n [requestMutation],\n );\n\n const validateAccessCode = useCallback<SessionActions['validateAccessCode']>(\n async (payload) => {\n try {\n const result = await validateMutation({ variables: { input: payload } });\n return result.data?.sessionValidateAccessCode.userErrors ?? [];\n } catch (error) {\n console.error('[session.validateAccessCode] sessionValidateAccessCode failed', loggableErrorName(error));\n return [NETWORK_USER_ERROR];\n }\n },\n [validateMutation],\n );\n\n const logout = useCallback<SessionActions['logout']>(async () => {\n try {\n const result = await logoutMutation();\n return result.data?.sessionLogout.userErrors ?? [];\n } catch (error) {\n console.error('[session.logout] sessionLogout failed', loggableErrorName(error));\n return [NETWORK_USER_ERROR];\n }\n }, [logoutMutation]);\n\n return useMemo(\n () => ({ loginWithPassword, requestAccessCode, validateAccessCode, logout }),\n [loginWithPassword, requestAccessCode, validateAccessCode, logout],\n );\n}\n\n/**\n * Declares the `session` action namespace — the single source of truth for its method/payload\n * metadata, runtime provider, and `use` hook (mirrors `cartActions`). The native catalog builder\n * (`scripts/build-catalog.mjs`) statically reads this call to emit `actions.json`; at runtime\n * `SessionActionProvider` calls `useSessionActions` directly.\n */\nexport const sessionActions = defineActions({\n namespace: 'session',\n description: 'Mutation-backed authentication writes against the VTEX ID session (login, access code, logout).',\n provider: { name: 'SessionActionProvider', importPath: '@vtex/faststore/internal/client' },\n methods: {\n loginWithPassword: {\n description: 'Sign in with email and password (async/pessimistic). Resolves the payload userErrors.',\n payload: [\n { name: 'email', type: 'string' },\n { name: 'password', type: 'string' },\n ],\n },\n requestAccessCode: {\n description: 'Email the shopper an access code; resolves the opaque authenticationToken to round-trip.',\n payload: [{ name: 'email', type: 'string' }],\n },\n validateAccessCode: {\n description: 'Validate an access code with its authenticationToken (async/pessimistic); logs the shopper in.',\n payload: [\n { name: 'email', type: 'string' },\n { name: 'accessCode', type: 'string' },\n { name: 'authenticationToken', type: 'string' },\n ],\n },\n logout: {\n description: 'Sign out, clearing the auth cookies (async/pessimistic). Idempotent for guests.',\n payload: [],\n },\n },\n use: useSessionActions,\n});\n"],"mappings":";;;;;;;;
|
|
1
|
+
{"version":3,"file":"use-session-actions.js","names":[],"sources":["../../../src/sdk/session/use-session-actions.ts"],"sourcesContent":["'use client';\n\nimport { 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 {\n SESSION_LOGIN_WITH_PASSWORD,\n SESSION_LOGOUT,\n SESSION_REQUEST_ACCESS_CODE,\n SESSION_VALIDATE_ACCESS_CODE,\n type SessionAccessCodeRequestResult,\n type SessionPasswordLoginPayload,\n type SessionRequestAccessCodePayload,\n type SessionUserError,\n type SessionValidateAccessCodePayload,\n} from './auth-operations.js';\n\n/**\n * The `session` action namespace — the one sanctioned client write surface for authentication\n * (docs/storefront-core/reference-free-particle-model.md § Write side — actions, docs/storefront-core/adr-0010-actions-own-the-write-side.md).\n *\n * Every action is async/pessimistic (Decision 1): the handler awaits the mutation and only the\n * server-confirmed payload changes the cache — authentication cannot be guessed client-side. The\n * handlers are stateless: no reactive vars, no module state; the access-code `authenticationToken`\n * is returned to the caller, which owns it as UI flow state (Decision 4). Each action resolves its\n * payload `userErrors` (empty on success) and never throws for expected outcomes; infrastructure\n * failures resolve as the synthetic `NETWORK_ERROR` (Decision 5). The credentials pass through\n * mutation variables only — never logged, never stored.\n */\nexport type SessionActions = Readonly<{\n /** Password sign-in. Async/pessimistic; resolves the payload userErrors (empty on success). */\n loginWithPassword: (payload: SessionPasswordLoginPayload) => Promise<readonly SessionUserError[]>;\n /** Email an access code; resolves the opaque authenticationToken to round-trip into validate. */\n requestAccessCode: (payload: SessionRequestAccessCodePayload) => Promise<SessionAccessCodeRequestResult>;\n /** Validate an access code + token; logs the shopper in exactly as a password login would. */\n validateAccessCode: (payload: SessionValidateAccessCodePayload) => Promise<readonly SessionUserError[]>;\n /** Sign out. Idempotent for guests. */\n logout: () => Promise<readonly SessionUserError[]>;\n}>;\n\n// A diagnostic message: components word this code from their own `$content`.\nconst NETWORK_USER_ERROR: SessionUserError = {\n code: 'NETWORK_ERROR',\n message: 'Unable to complete the request. Please try again.',\n};\n\n/**\n * The `session` namespace hook. Registered with the first-party action registry via `sessionActions`\n * below; the compiler-emitted `SessionActionProvider` calls it and threads the narrowed methods into\n * each consumer's `$actions.session`. Login/validate/logout refresh the session purely through the\n * normalized-cache merge of the mutation response (Decision 2) — no refetch, no imperative write.\n */\nexport function useSessionActions(): SessionActions {\n const [loginMutation] = useMutation(SESSION_LOGIN_WITH_PASSWORD);\n const [requestMutation] = useMutation(SESSION_REQUEST_ACCESS_CODE);\n const [validateMutation] = useMutation(SESSION_VALIDATE_ACCESS_CODE);\n const [logoutMutation] = useMutation(SESSION_LOGOUT);\n\n const loginWithPassword = useCallback<SessionActions['loginWithPassword']>(\n async (payload) => {\n try {\n const result = await loginMutation({ variables: { input: payload } });\n return result.data?.sessionLoginWithPassword.userErrors ?? [];\n } catch (error) {\n console.error('[session.loginWithPassword] sessionLoginWithPassword failed', loggableErrorName(error));\n return [NETWORK_USER_ERROR];\n }\n },\n [loginMutation],\n );\n\n const requestAccessCode = useCallback<SessionActions['requestAccessCode']>(\n async (payload) => {\n try {\n const result = await requestMutation({ variables: { input: payload } });\n const data = result.data?.sessionRequestAccessCode;\n return { authenticationToken: data?.authenticationToken ?? null, userErrors: data?.userErrors ?? [] };\n } catch (error) {\n console.error('[session.requestAccessCode] sessionRequestAccessCode failed', loggableErrorName(error));\n return { authenticationToken: null, userErrors: [NETWORK_USER_ERROR] };\n }\n },\n [requestMutation],\n );\n\n const validateAccessCode = useCallback<SessionActions['validateAccessCode']>(\n async (payload) => {\n try {\n const result = await validateMutation({ variables: { input: payload } });\n return result.data?.sessionValidateAccessCode.userErrors ?? [];\n } catch (error) {\n console.error('[session.validateAccessCode] sessionValidateAccessCode failed', loggableErrorName(error));\n return [NETWORK_USER_ERROR];\n }\n },\n [validateMutation],\n );\n\n const logout = useCallback<SessionActions['logout']>(async () => {\n try {\n const result = await logoutMutation();\n return result.data?.sessionLogout.userErrors ?? [];\n } catch (error) {\n console.error('[session.logout] sessionLogout failed', loggableErrorName(error));\n return [NETWORK_USER_ERROR];\n }\n }, [logoutMutation]);\n\n return useMemo(\n () => ({ loginWithPassword, requestAccessCode, validateAccessCode, logout }),\n [loginWithPassword, requestAccessCode, validateAccessCode, logout],\n );\n}\n\n/**\n * Declares the `session` action namespace — the single source of truth for its method/payload\n * metadata, runtime provider, and `use` hook (mirrors `cartActions`). The native catalog builder\n * (`scripts/build-catalog.mjs`) statically reads this call to emit `actions.json`; at runtime\n * `SessionActionProvider` calls `useSessionActions` directly.\n */\nexport const sessionActions = defineActions({\n namespace: 'session',\n description: 'Mutation-backed authentication writes against the VTEX ID session (login, access code, logout).',\n provider: { name: 'SessionActionProvider', importPath: '@vtex/faststore/internal/client' },\n methods: {\n loginWithPassword: {\n description: 'Sign in with email and password (async/pessimistic). Resolves the payload userErrors.',\n payload: [\n { name: 'email', type: 'string' },\n { name: 'password', type: 'string' },\n ],\n },\n requestAccessCode: {\n description: 'Email the shopper an access code; resolves the opaque authenticationToken to round-trip.',\n payload: [{ name: 'email', type: 'string' }],\n },\n validateAccessCode: {\n description: 'Validate an access code with its authenticationToken (async/pessimistic); logs the shopper in.',\n payload: [\n { name: 'email', type: 'string' },\n { name: 'accessCode', type: 'string' },\n { name: 'authenticationToken', type: 'string' },\n ],\n },\n logout: {\n description: 'Sign out, clearing the auth cookies (async/pessimistic). Idempotent for guests.',\n payload: [],\n },\n },\n use: useSessionActions,\n});\n"],"mappings":";;;;;;;;AA0CA,MAAM,qBAAuC;CAC3C,MAAM;CACN,SAAS;AACX;;;;;;;AAQA,SAAgB,oBAAoC;CAClD,MAAM,CAAC,iBAAiB,YAAY,2BAA2B;CAC/D,MAAM,CAAC,mBAAmB,YAAY,2BAA2B;CACjE,MAAM,CAAC,oBAAoB,YAAY,4BAA4B;CACnE,MAAM,CAAC,kBAAkB,YAAY,cAAc;CAEnD,MAAM,oBAAoB,YACxB,OAAO,YAAY;EACjB,IAAI;GAEF,QAAO,MADc,cAAc,EAAE,WAAW,EAAE,OAAO,QAAQ,EAAE,CAAC,EAAA,CACtD,MAAM,yBAAyB,cAAc,CAAC;EAC9D,SAAS,OAAO;GACd,QAAQ,MAAM,+DAA+D,kBAAkB,KAAK,CAAC;GACrG,OAAO,CAAC,kBAAkB;EAC5B;CACF,GACA,CAAC,aAAa,CAChB;CAEA,MAAM,oBAAoB,YACxB,OAAO,YAAY;EACjB,IAAI;GAEF,MAAM,QAAO,MADQ,gBAAgB,EAAE,WAAW,EAAE,OAAO,QAAQ,EAAE,CAAC,EAAA,CAClD,MAAM;GAC1B,OAAO;IAAE,qBAAqB,MAAM,uBAAuB;IAAM,YAAY,MAAM,cAAc,CAAC;GAAE;EACtG,SAAS,OAAO;GACd,QAAQ,MAAM,+DAA+D,kBAAkB,KAAK,CAAC;GACrG,OAAO;IAAE,qBAAqB;IAAM,YAAY,CAAC,kBAAkB;GAAE;EACvE;CACF,GACA,CAAC,eAAe,CAClB;CAEA,MAAM,qBAAqB,YACzB,OAAO,YAAY;EACjB,IAAI;GAEF,QAAO,MADc,iBAAiB,EAAE,WAAW,EAAE,OAAO,QAAQ,EAAE,CAAC,EAAA,CACzD,MAAM,0BAA0B,cAAc,CAAC;EAC/D,SAAS,OAAO;GACd,QAAQ,MAAM,iEAAiE,kBAAkB,KAAK,CAAC;GACvG,OAAO,CAAC,kBAAkB;EAC5B;CACF,GACA,CAAC,gBAAgB,CACnB;CAEA,MAAM,SAAS,YAAsC,YAAY;EAC/D,IAAI;GAEF,QAAO,MADc,eAAe,EAAA,CACtB,MAAM,cAAc,cAAc,CAAC;EACnD,SAAS,OAAO;GACd,QAAQ,MAAM,yCAAyC,kBAAkB,KAAK,CAAC;GAC/E,OAAO,CAAC,kBAAkB;EAC5B;CACF,GAAG,CAAC,cAAc,CAAC;CAEnB,OAAO,eACE;EAAE;EAAmB;EAAmB;EAAoB;CAAO,IAC1E;EAAC;EAAmB;EAAmB;EAAoB;CAAM,CACnE;AACF;AAQ8B,cAAc;CAC1C,WAAW;CACX,aAAa;CACb,UAAU;EAAE,MAAM;EAAyB,YAAY;CAAkC;CACzF,SAAS;EACP,mBAAmB;GACjB,aAAa;GACb,SAAS,CACP;IAAE,MAAM;IAAS,MAAM;GAAS,GAChC;IAAE,MAAM;IAAY,MAAM;GAAS,CACrC;EACF;EACA,mBAAmB;GACjB,aAAa;GACb,SAAS,CAAC;IAAE,MAAM;IAAS,MAAM;GAAS,CAAC;EAC7C;EACA,oBAAoB;GAClB,aAAa;GACb,SAAS;IACP;KAAE,MAAM;KAAS,MAAM;IAAS;IAChC;KAAE,MAAM;KAAc,MAAM;IAAS;IACrC;KAAE,MAAM;KAAuB,MAAM;IAAS;GAChD;EACF;EACA,QAAQ;GACN,aAAa;GACb,SAAS,CAAC;EACZ;CACF;CACA,KAAK;AACP,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"use-cart-shipping-simulation.js","names":[],"sources":["../../../src/sdk/shipping/use-cart-shipping-simulation.ts"],"sourcesContent":["'use client';\n\nimport { getApolloContext } from '@apollo/client/react';\nimport { useCallback, useContext } from 'react';\nimport type {\n ShippingOption,\n ShippingSimulateError,\n ShippingSimulationPayload,\n} from '../../matter/shipping-simulator/shipping-simulator.js';\nimport type { CartShippingSimulationQuery, ShippingSimulateErrorCode } from '../generated/graphql.js';\nimport { CART_SHIPPING_SIMULATION, type CartShippingSimulateInput } from './cart-operations.js';\nimport { withRequestTimeout } from './request-timeout.js';\n\n/**\n * Cart-scoped shipping-simulation SDK hook.\n *\n * Returns the calculator's `onSimulate(input) => Promise<payload>` callback. It runs the\n * `CartShippingSimulation` query imperatively per submit via the Apollo client read from\n * `getApolloContext()` (a non-throwing read, mirroring `useShippingSimulation`). The input carries\n * a destination only — items come from the cart via `x-order-form-id`, resolved server-side. The\n * mapping matches the PDP hook: the structured `ShippingEstimate { value, unit }` is carried through\n * untouched (the component localizes it), the API `Money` is carried as `{ amount, currencyCode }`,\n * and any transport/GraphQL failure is collapsed to one `UNKNOWN` userError so the calculator's\n * success/error transition stays deterministic.\n */\n\n// The calculator's domain payload is the seam's own type, imported from the component so the\n// binding is a direct assignment and the types never drift from the contract the component's tests\n// assert.\ntype RawOption = NonNullable<CartShippingSimulationQuery['cartShippingSimulation']['shippingOptions']>[number];\n\nconst UNKNOWN_ERROR: ShippingSimulateError = {\n code: 'UNKNOWN',\n message: 'Unable to calculate shipping. Try again.',\n};\n\nfunction mapErrorCode(code: ShippingSimulateErrorCode): ShippingSimulateError['code'] {\n return code === 'INVALID_POSTAL_CODE' ? 'INVALID_POSTAL_CODE' : 'UNKNOWN';\n}\n\nfunction mapOption(option: RawOption): ShippingOption {\n return {\n id: option.id,\n name: option.name,\n channel: option.channel,\n shippingEstimate: option.shippingEstimate,\n price: { amount: option.price.amount, currencyCode: option.price.currencyCode },\n listPrice: { amount: option.listPrice.amount, currencyCode: option.listPrice.currencyCode },\n };\n}\n\nexport function useCartShippingSimulation(): (input: CartShippingSimulateInput) => Promise<ShippingSimulationPayload> {\n // Read the client off Apollo's context directly rather than via `useApolloClient`, which throws at\n // render when no provider is present. The hook is the calculator's default `onSimulate` and runs\n // in every Root render, including presentational tests/stories that mount the Root with their own\n // callback and no ApolloProvider; a missing client only matters when the default callback is\n // actually invoked, where it is mapped to the same UNKNOWN userError as any other failure.\n const { client } = useContext(getApolloContext());\n\n return useCallback(\n async (input: CartShippingSimulateInput): Promise<ShippingSimulationPayload> => {\n if (!client) {\n return { shippingOptions: null, userErrors: [UNKNOWN_ERROR] };\n }\n try {\n const result = await withRequestTimeout((signal) =>\n client.query({\n query: CART_SHIPPING_SIMULATION,\n variables: { input },\n fetchPolicy: 'no-cache',\n context: { fetchOptions: { signal } },\n }),\n );\n\n if (result.error || !result.data) {\n return { shippingOptions: null, userErrors: [UNKNOWN_ERROR] };\n }\n\n const payload = result.data.cartShippingSimulation;\n const userErrors = payload.userErrors.map((error) => ({\n code: mapErrorCode(error.code),\n message: error.message,\n }));\n\n if (userErrors.length > 0) {\n return { shippingOptions: null, userErrors };\n }\n\n return {\n shippingOptions: payload.shippingOptions?.map(mapOption) ?? null,\n userErrors: [],\n };\n } catch {\n return { shippingOptions: null, userErrors: [UNKNOWN_ERROR] };\n }\n },\n [client],\n );\n}\n"],"mappings":";;;;;;
|
|
1
|
+
{"version":3,"file":"use-cart-shipping-simulation.js","names":[],"sources":["../../../src/sdk/shipping/use-cart-shipping-simulation.ts"],"sourcesContent":["'use client';\n\nimport { getApolloContext } from '@apollo/client/react';\nimport { useCallback, useContext } from 'react';\nimport type {\n ShippingOption,\n ShippingSimulateError,\n ShippingSimulationPayload,\n} from '../../matter/shipping-simulator/shipping-simulator.js';\nimport type { CartShippingSimulationQuery, ShippingSimulateErrorCode } from '../generated/graphql.js';\nimport { CART_SHIPPING_SIMULATION, type CartShippingSimulateInput } from './cart-operations.js';\nimport { withRequestTimeout } from './request-timeout.js';\n\n/**\n * Cart-scoped shipping-simulation SDK hook.\n *\n * Returns the calculator's `onSimulate(input) => Promise<payload>` callback. It runs the\n * `CartShippingSimulation` query imperatively per submit via the Apollo client read from\n * `getApolloContext()` (a non-throwing read, mirroring `useShippingSimulation`). The input carries\n * a destination only — items come from the cart via `x-order-form-id`, resolved server-side. The\n * mapping matches the PDP hook: the structured `ShippingEstimate { value, unit }` is carried through\n * untouched (the component localizes it), the API `Money` is carried as `{ amount, currencyCode }`,\n * and any transport/GraphQL failure is collapsed to one `UNKNOWN` userError so the calculator's\n * success/error transition stays deterministic.\n */\n\n// The calculator's domain payload is the seam's own type, imported from the component so the\n// binding is a direct assignment and the types never drift from the contract the component's tests\n// assert.\ntype RawOption = NonNullable<CartShippingSimulationQuery['cartShippingSimulation']['shippingOptions']>[number];\n\n// Diagnostic text for logs and `onResult`: `ShippingSimulatorError` words this code from its own `$content`.\nconst UNKNOWN_ERROR: ShippingSimulateError = {\n code: 'UNKNOWN',\n message: 'Unable to calculate shipping. Try again.',\n};\n\nfunction mapErrorCode(code: ShippingSimulateErrorCode): ShippingSimulateError['code'] {\n return code === 'INVALID_POSTAL_CODE' ? 'INVALID_POSTAL_CODE' : 'UNKNOWN';\n}\n\nfunction mapOption(option: RawOption): ShippingOption {\n return {\n id: option.id,\n name: option.name,\n channel: option.channel,\n shippingEstimate: option.shippingEstimate,\n price: { amount: option.price.amount, currencyCode: option.price.currencyCode },\n listPrice: { amount: option.listPrice.amount, currencyCode: option.listPrice.currencyCode },\n };\n}\n\nexport function useCartShippingSimulation(): (input: CartShippingSimulateInput) => Promise<ShippingSimulationPayload> {\n // Read the client off Apollo's context directly rather than via `useApolloClient`, which throws at\n // render when no provider is present. The hook is the calculator's default `onSimulate` and runs\n // in every Root render, including presentational tests/stories that mount the Root with their own\n // callback and no ApolloProvider; a missing client only matters when the default callback is\n // actually invoked, where it is mapped to the same UNKNOWN userError as any other failure.\n const { client } = useContext(getApolloContext());\n\n return useCallback(\n async (input: CartShippingSimulateInput): Promise<ShippingSimulationPayload> => {\n if (!client) {\n return { shippingOptions: null, userErrors: [UNKNOWN_ERROR] };\n }\n try {\n const result = await withRequestTimeout((signal) =>\n client.query({\n query: CART_SHIPPING_SIMULATION,\n variables: { input },\n fetchPolicy: 'no-cache',\n context: { fetchOptions: { signal } },\n }),\n );\n\n if (result.error || !result.data) {\n return { shippingOptions: null, userErrors: [UNKNOWN_ERROR] };\n }\n\n const payload = result.data.cartShippingSimulation;\n const userErrors = payload.userErrors.map((error) => ({\n code: mapErrorCode(error.code),\n message: error.message,\n }));\n\n if (userErrors.length > 0) {\n return { shippingOptions: null, userErrors };\n }\n\n return {\n shippingOptions: payload.shippingOptions?.map(mapOption) ?? null,\n userErrors: [],\n };\n } catch {\n return { shippingOptions: null, userErrors: [UNKNOWN_ERROR] };\n }\n },\n [client],\n );\n}\n"],"mappings":";;;;;;AAgCA,MAAM,gBAAuC;CAC3C,MAAM;CACN,SAAS;AACX;AAEA,SAAS,aAAa,MAAgE;CACpF,OAAO,SAAS,wBAAwB,wBAAwB;AAClE;AAEA,SAAS,UAAU,QAAmC;CACpD,OAAO;EACL,IAAI,OAAO;EACX,MAAM,OAAO;EACb,SAAS,OAAO;EAChB,kBAAkB,OAAO;EACzB,OAAO;GAAE,QAAQ,OAAO,MAAM;GAAQ,cAAc,OAAO,MAAM;EAAa;EAC9E,WAAW;GAAE,QAAQ,OAAO,UAAU;GAAQ,cAAc,OAAO,UAAU;EAAa;CAC5F;AACF;AAEA,SAAgB,4BAAsG;CAMpH,MAAM,EAAE,WAAW,WAAW,iBAAiB,CAAC;CAEhD,OAAO,YACL,OAAO,UAAyE;EAC9E,IAAI,CAAC,QACH,OAAO;GAAE,iBAAiB;GAAM,YAAY,CAAC,aAAa;EAAE;EAE9D,IAAI;GACF,MAAM,SAAS,MAAM,oBAAoB,WACvC,OAAO,MAAM;IACX,OAAO;IACP,WAAW,EAAE,MAAM;IACnB,aAAa;IACb,SAAS,EAAE,cAAc,EAAE,OAAO,EAAE;GACtC,CAAC,CACH;GAEA,IAAI,OAAO,SAAS,CAAC,OAAO,MAC1B,OAAO;IAAE,iBAAiB;IAAM,YAAY,CAAC,aAAa;GAAE;GAG9D,MAAM,UAAU,OAAO,KAAK;GAC5B,MAAM,aAAa,QAAQ,WAAW,KAAK,WAAW;IACpD,MAAM,aAAa,MAAM,IAAI;IAC7B,SAAS,MAAM;GACjB,EAAE;GAEF,IAAI,WAAW,SAAS,GACtB,OAAO;IAAE,iBAAiB;IAAM;GAAW;GAG7C,OAAO;IACL,iBAAiB,QAAQ,iBAAiB,IAAI,SAAS,KAAK;IAC5D,YAAY,CAAC;GACf;EACF,QAAQ;GACN,OAAO;IAAE,iBAAiB;IAAM,YAAY,CAAC,aAAa;GAAE;EAC9D;CACF,GACA,CAAC,MAAM,CACT;AACF"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"use-shipping-actions.js","names":[],"sources":["../../../src/sdk/shipping/use-shipping-actions.ts"],"sourcesContent":["'use client';\n\nimport { 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 { SHIPPING_OPTION_SELECT, type ShippingOptionSelectUserError } from './option-select-operations.js';\nimport {\n SHIPPING_ADDRESS_UPDATE,\n SHIPPING_NETWORK_ERROR_CODE,\n type ShippingAddressInput,\n type ShippingAddressUpdateErrorEntity,\n} from './shipping-operations.js';\n\n/**\n * The `shipping` action namespace — the sanctioned client write surface against the VTEX orderForm\n * shippingData: delivery-address updates (docs/storefront-core/reference-free-particle-model.md § The cart write contract;\n * docs/storefront-core/adr-0010-actions-own-the-write-side.md) and cart-scoped delivery-option\n * selection (docs/storefront-core/reference-free-particle-model.md § The cart write contract). Per Decision 4 the two tasks share this one\n * namespace — T2 (`setAddress`) merged first and owns the registration, T3 extends it with\n * `selectOption` here.\n *\n * Both actions are async/pessimistic (Decision 1 of the address spec): the handler awaits the\n * mutation and only the server-confirmed payload changes the cache. Each is stateless (no reactive\n * vars, no module state), resolves its payload `userErrors` (empty on success), and never throws for\n * expected outcomes; an infrastructure failure (network or an upstream GraphQLError) is caught and\n * collapsed to one synthetic network `userError` — no per-code branching (US-4, FR-5). Inputs pass\n * through mutation variables only — never logged, never stored beyond the cache.\n */\nexport type ShippingActions = Readonly<{\n /** Persist the single delivery address (async/pessimistic). Resolves the payload userErrors. */\n setAddress: (address: ShippingAddressInput) => Promise<readonly ShippingAddressUpdateErrorEntity[]>;\n /** Select a delivery option for the whole cart. Resolves the payload userErrors (empty on success). */\n selectOption: (optionId: string) => Promise<readonly ShippingOptionSelectUserError[]>;\n}>;\n\nconst SET_ADDRESS_NETWORK_ERROR: ShippingAddressUpdateErrorEntity = {\n code: SHIPPING_NETWORK_ERROR_CODE,\n message: 'Unable to complete the request. Please try again.',\n};\n\nconst SELECT_OPTION_NETWORK_ERROR: ShippingOptionSelectUserError = {\n code: 'NETWORK_ERROR',\n message: 'Unable to update the delivery option. Please try again.',\n};\n\n/**\n * The `shipping` namespace hook. Registered with the first-party action registry via `shippingActions`\n * below; the compiler-emitted `ShippingActionProvider` calls it and threads the narrowed methods into\n * each consumer's `$actions.shipping`. Writes persist purely through the normalized-cache merge of\n * each mutation response (Decision 2 of the address spec) — no refetch, no imperative write.\n */\nexport function useShippingActions(): ShippingActions {\n const [addressMutation] = useMutation(SHIPPING_ADDRESS_UPDATE);\n const [selectOptionMutation] = useMutation(SHIPPING_OPTION_SELECT);\n\n const setAddress = useCallback<ShippingActions['setAddress']>(\n async (address) => {\n try {\n const result = await addressMutation({ variables: { input: address } });\n return result.data?.shippingAddressUpdate.userErrors ?? [];\n } catch (error) {\n console.error('[shipping.setAddress] shippingAddressUpdate failed', loggableErrorName(error));\n return [SET_ADDRESS_NETWORK_ERROR];\n }\n },\n [addressMutation],\n );\n\n const selectOption = useCallback<ShippingActions['selectOption']>(\n async (optionId) => {\n try {\n const result = await selectOptionMutation({ variables: { input: { optionId } } });\n return result.data?.shippingOptionSelect.userErrors ?? [];\n } catch (error) {\n console.error('[shipping.selectOption] shippingOptionSelect failed', loggableErrorName(error));\n return [SELECT_OPTION_NETWORK_ERROR];\n }\n },\n [selectOptionMutation],\n );\n\n return useMemo(() => ({ setAddress, selectOption }), [setAddress, selectOption]);\n}\n\n/**\n * Declares the `shipping` action namespace — the single source of truth for its method/payload\n * metadata, runtime provider, and `use` hook (mirrors `cartActions`, `sessionActions`). The native\n * catalog builder (`scripts/build-catalog.mjs`) statically reads this call to emit `actions.json`;\n * at runtime `ShippingActionProvider` calls `useShippingActions` directly.\n */\nexport const shippingActions = defineActions({\n namespace: 'shipping',\n description: 'Mutation-backed delivery writes against the VTEX orderForm shippingData (address in T2; option in T3).',\n provider: { name: 'ShippingActionProvider', importPath: '@vtex/faststore/internal/client' },\n methods: {\n setAddress: {\n description:\n 'Persist the single delivery address in one submit (async/pessimistic). Resolves the payload userErrors.',\n payload: [\n { name: 'savedAddressId', type: 'string' },\n { name: 'recipientName', type: 'string' },\n { name: 'postalCode', type: 'string' },\n { name: 'country', type: 'string' },\n { name: 'street', type: 'string' },\n { name: 'number', type: 'string' },\n { name: 'complement', type: 'string' },\n { name: 'neighborhood', type: 'string' },\n { name: 'city', type: 'string' },\n { name: 'state', type: 'string' },\n ],\n },\n selectOption: {\n description: 'Select a delivery option for the whole cart (async/pessimistic). Resolves the payload userErrors.',\n payload: [{ name: 'optionId', type: 'string' }],\n },\n },\n use: useShippingActions,\n});\n"],"mappings":";;;;;;;;;
|
|
1
|
+
{"version":3,"file":"use-shipping-actions.js","names":[],"sources":["../../../src/sdk/shipping/use-shipping-actions.ts"],"sourcesContent":["'use client';\n\nimport { 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 { SHIPPING_OPTION_SELECT, type ShippingOptionSelectUserError } from './option-select-operations.js';\nimport {\n SHIPPING_ADDRESS_UPDATE,\n SHIPPING_NETWORK_ERROR_CODE,\n type ShippingAddressInput,\n type ShippingAddressUpdateErrorEntity,\n} from './shipping-operations.js';\n\n/**\n * The `shipping` action namespace — the sanctioned client write surface against the VTEX orderForm\n * shippingData: delivery-address updates (docs/storefront-core/reference-free-particle-model.md § The cart write contract;\n * docs/storefront-core/adr-0010-actions-own-the-write-side.md) and cart-scoped delivery-option\n * selection (docs/storefront-core/reference-free-particle-model.md § The cart write contract). Per Decision 4 the two tasks share this one\n * namespace — T2 (`setAddress`) merged first and owns the registration, T3 extends it with\n * `selectOption` here.\n *\n * Both actions are async/pessimistic (Decision 1 of the address spec): the handler awaits the\n * mutation and only the server-confirmed payload changes the cache. Each is stateless (no reactive\n * vars, no module state), resolves its payload `userErrors` (empty on success), and never throws for\n * expected outcomes; an infrastructure failure (network or an upstream GraphQLError) is caught and\n * collapsed to one synthetic network `userError` — no per-code branching (US-4, FR-5). Inputs pass\n * through mutation variables only — never logged, never stored beyond the cache.\n */\nexport type ShippingActions = Readonly<{\n /** Persist the single delivery address (async/pessimistic). Resolves the payload userErrors. */\n setAddress: (address: ShippingAddressInput) => Promise<readonly ShippingAddressUpdateErrorEntity[]>;\n /** Select a delivery option for the whole cart. Resolves the payload userErrors (empty on success). */\n selectOption: (optionId: string) => Promise<readonly ShippingOptionSelectUserError[]>;\n}>;\n\n// The `message` of both constants is diagnostic: every component that shows these codes words them from its\n// own `$content`.\nconst SET_ADDRESS_NETWORK_ERROR: ShippingAddressUpdateErrorEntity = {\n code: SHIPPING_NETWORK_ERROR_CODE,\n message: 'Unable to complete the request. Please try again.',\n};\n\nconst SELECT_OPTION_NETWORK_ERROR: ShippingOptionSelectUserError = {\n code: 'NETWORK_ERROR',\n message: 'Unable to update the delivery option. Please try again.',\n};\n\n/**\n * The `shipping` namespace hook. Registered with the first-party action registry via `shippingActions`\n * below; the compiler-emitted `ShippingActionProvider` calls it and threads the narrowed methods into\n * each consumer's `$actions.shipping`. Writes persist purely through the normalized-cache merge of\n * each mutation response (Decision 2 of the address spec) — no refetch, no imperative write.\n */\nexport function useShippingActions(): ShippingActions {\n const [addressMutation] = useMutation(SHIPPING_ADDRESS_UPDATE);\n const [selectOptionMutation] = useMutation(SHIPPING_OPTION_SELECT);\n\n const setAddress = useCallback<ShippingActions['setAddress']>(\n async (address) => {\n try {\n const result = await addressMutation({ variables: { input: address } });\n return result.data?.shippingAddressUpdate.userErrors ?? [];\n } catch (error) {\n console.error('[shipping.setAddress] shippingAddressUpdate failed', loggableErrorName(error));\n return [SET_ADDRESS_NETWORK_ERROR];\n }\n },\n [addressMutation],\n );\n\n const selectOption = useCallback<ShippingActions['selectOption']>(\n async (optionId) => {\n try {\n const result = await selectOptionMutation({ variables: { input: { optionId } } });\n return result.data?.shippingOptionSelect.userErrors ?? [];\n } catch (error) {\n console.error('[shipping.selectOption] shippingOptionSelect failed', loggableErrorName(error));\n return [SELECT_OPTION_NETWORK_ERROR];\n }\n },\n [selectOptionMutation],\n );\n\n return useMemo(() => ({ setAddress, selectOption }), [setAddress, selectOption]);\n}\n\n/**\n * Declares the `shipping` action namespace — the single source of truth for its method/payload\n * metadata, runtime provider, and `use` hook (mirrors `cartActions`, `sessionActions`). The native\n * catalog builder (`scripts/build-catalog.mjs`) statically reads this call to emit `actions.json`;\n * at runtime `ShippingActionProvider` calls `useShippingActions` directly.\n */\nexport const shippingActions = defineActions({\n namespace: 'shipping',\n description: 'Mutation-backed delivery writes against the VTEX orderForm shippingData (address in T2; option in T3).',\n provider: { name: 'ShippingActionProvider', importPath: '@vtex/faststore/internal/client' },\n methods: {\n setAddress: {\n description:\n 'Persist the single delivery address in one submit (async/pessimistic). Resolves the payload userErrors.',\n payload: [\n { name: 'savedAddressId', type: 'string' },\n { name: 'recipientName', type: 'string' },\n { name: 'postalCode', type: 'string' },\n { name: 'country', type: 'string' },\n { name: 'street', type: 'string' },\n { name: 'number', type: 'string' },\n { name: 'complement', type: 'string' },\n { name: 'neighborhood', type: 'string' },\n { name: 'city', type: 'string' },\n { name: 'state', type: 'string' },\n ],\n },\n selectOption: {\n description: 'Select a delivery option for the whole cart (async/pessimistic). Resolves the payload userErrors.',\n payload: [{ name: 'optionId', type: 'string' }],\n },\n },\n use: useShippingActions,\n});\n"],"mappings":";;;;;;;;;AAsCA,MAAM,4BAA8D;CAClE,MAAM;CACN,SAAS;AACX;AAEA,MAAM,8BAA6D;CACjE,MAAM;CACN,SAAS;AACX;;;;;;;AAQA,SAAgB,qBAAsC;CACpD,MAAM,CAAC,mBAAmB,YAAY,uBAAuB;CAC7D,MAAM,CAAC,wBAAwB,YAAY,sBAAsB;CAEjE,MAAM,aAAa,YACjB,OAAO,YAAY;EACjB,IAAI;GAEF,QAAO,MADc,gBAAgB,EAAE,WAAW,EAAE,OAAO,QAAQ,EAAE,CAAC,EAAA,CACxD,MAAM,sBAAsB,cAAc,CAAC;EAC3D,SAAS,OAAO;GACd,QAAQ,MAAM,sDAAsD,kBAAkB,KAAK,CAAC;GAC5F,OAAO,CAAC,yBAAyB;EACnC;CACF,GACA,CAAC,eAAe,CAClB;CAEA,MAAM,eAAe,YACnB,OAAO,aAAa;EAClB,IAAI;GAEF,QAAO,MADc,qBAAqB,EAAE,WAAW,EAAE,OAAO,EAAE,SAAS,EAAE,EAAE,CAAC,EAAA,CAClE,MAAM,qBAAqB,cAAc,CAAC;EAC1D,SAAS,OAAO;GACd,QAAQ,MAAM,uDAAuD,kBAAkB,KAAK,CAAC;GAC7F,OAAO,CAAC,2BAA2B;EACrC;CACF,GACA,CAAC,oBAAoB,CACvB;CAEA,OAAO,eAAe;EAAE;EAAY;CAAa,IAAI,CAAC,YAAY,YAAY,CAAC;AACjF;AAQ+B,cAAc;CAC3C,WAAW;CACX,aAAa;CACb,UAAU;EAAE,MAAM;EAA0B,YAAY;CAAkC;CAC1F,SAAS;EACP,YAAY;GACV,aACE;GACF,SAAS;IACP;KAAE,MAAM;KAAkB,MAAM;IAAS;IACzC;KAAE,MAAM;KAAiB,MAAM;IAAS;IACxC;KAAE,MAAM;KAAc,MAAM;IAAS;IACrC;KAAE,MAAM;KAAW,MAAM;IAAS;IAClC;KAAE,MAAM;KAAU,MAAM;IAAS;IACjC;KAAE,MAAM;KAAU,MAAM;IAAS;IACjC;KAAE,MAAM;KAAc,MAAM;IAAS;IACrC;KAAE,MAAM;KAAgB,MAAM;IAAS;IACvC;KAAE,MAAM;KAAQ,MAAM;IAAS;IAC/B;KAAE,MAAM;KAAS,MAAM;IAAS;GAClC;EACF;EACA,cAAc;GACZ,aAAa;GACb,SAAS,CAAC;IAAE,MAAM;IAAY,MAAM;GAAS,CAAC;EAChD;CACF;CACA,KAAK;AACP,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"use-shipping-simulation.js","names":[],"sources":["../../../src/sdk/shipping/use-shipping-simulation.ts"],"sourcesContent":["'use client';\n\nimport { getApolloContext } from '@apollo/client/react';\nimport { useCallback, useContext } from 'react';\nimport type {\n ShippingOption,\n ShippingSimulateError,\n ShippingSimulationPayload,\n} from '../../matter/shipping-simulator/shipping-simulator.js';\nimport type { ShippingSimulateErrorCode, ShippingSimulationQuery } from '../generated/graphql.js';\nimport { SHIPPING_SIMULATION, type ShippingSimulateInput } from './operations.js';\nimport { withRequestTimeout } from './request-timeout.js';\n\n/**\n * Shipping-simulation SDK hook (FSPG-815 / specs/fspg-815-minicart-wire-shipping-simulator-pdp.md).\n *\n * Returns the simulator's `onSimulate(input) => Promise<payload>` callback. It runs the\n * `ShippingSimulation` query imperatively per submit via the Apollo client read from\n * `getApolloContext()` (a non-throwing read; see the inline note below). The mapping is thin: the\n * structured `ShippingEstimate { value, unit }` is carried through untouched (the component\n * localizes it, where the `locale` lives), the API `Money` is carried as `{ amount, currencyCode }`,\n * and any transport/GraphQL failure is collapsed to one `UNKNOWN` userError so the component's\n * success/error transition stays deterministic.\n */\n\n// The simulator's domain payload is the seam's own type, imported from the component so the binding\n// is a direct assignment and the types never drift from the contract the component's tests assert.\ntype RawOption = NonNullable<ShippingSimulationQuery['shippingSimulation']['shippingOptions']>[number];\n\nconst UNKNOWN_ERROR: ShippingSimulateError = {\n code: 'UNKNOWN',\n message: 'Unable to calculate shipping. Try again.',\n};\n\nfunction mapErrorCode(code: ShippingSimulateErrorCode): ShippingSimulateError['code'] {\n return code === 'INVALID_POSTAL_CODE' ? 'INVALID_POSTAL_CODE' : 'UNKNOWN';\n}\n\nfunction mapOption(option: RawOption): ShippingOption {\n return {\n id: option.id,\n name: option.name,\n channel: option.channel,\n shippingEstimate: option.shippingEstimate,\n price: { amount: option.price.amount, currencyCode: option.price.currencyCode },\n listPrice: { amount: option.listPrice.amount, currencyCode: option.listPrice.currencyCode },\n };\n}\n\nexport function useShippingSimulation(): (input: ShippingSimulateInput) => Promise<ShippingSimulationPayload> {\n // Read the client off Apollo's context directly rather than via `useApolloClient`, which throws\n // at render when no provider is present. The hook is the default `onSimulate` and runs in every\n // Root render, including presentational tests/stories that mount the Root with their own callback\n // and no ApolloProvider; a missing client only matters when the default callback is actually\n // invoked, where it is mapped to the same UNKNOWN userError as any other failure.\n const { client } = useContext(getApolloContext());\n\n return useCallback(\n async (input: ShippingSimulateInput): Promise<ShippingSimulationPayload> => {\n if (!client) {\n return { shippingOptions: null, userErrors: [UNKNOWN_ERROR] };\n }\n try {\n const result = await withRequestTimeout((signal) =>\n client.query({\n query: SHIPPING_SIMULATION,\n variables: { input },\n fetchPolicy: 'no-cache',\n context: { fetchOptions: { signal } },\n }),\n );\n\n if (result.error || !result.data) {\n return { shippingOptions: null, userErrors: [UNKNOWN_ERROR] };\n }\n\n const payload = result.data.shippingSimulation;\n const userErrors = payload.userErrors.map((error) => ({\n code: mapErrorCode(error.code),\n message: error.message,\n }));\n\n if (userErrors.length > 0) {\n return { shippingOptions: null, userErrors };\n }\n\n return {\n shippingOptions: payload.shippingOptions?.map(mapOption) ?? null,\n userErrors: [],\n };\n } catch {\n return { shippingOptions: null, userErrors: [UNKNOWN_ERROR] };\n }\n },\n [client],\n );\n}\n"],"mappings":";;;;;;
|
|
1
|
+
{"version":3,"file":"use-shipping-simulation.js","names":[],"sources":["../../../src/sdk/shipping/use-shipping-simulation.ts"],"sourcesContent":["'use client';\n\nimport { getApolloContext } from '@apollo/client/react';\nimport { useCallback, useContext } from 'react';\nimport type {\n ShippingOption,\n ShippingSimulateError,\n ShippingSimulationPayload,\n} from '../../matter/shipping-simulator/shipping-simulator.js';\nimport type { ShippingSimulateErrorCode, ShippingSimulationQuery } from '../generated/graphql.js';\nimport { SHIPPING_SIMULATION, type ShippingSimulateInput } from './operations.js';\nimport { withRequestTimeout } from './request-timeout.js';\n\n/**\n * Shipping-simulation SDK hook (FSPG-815 / specs/fspg-815-minicart-wire-shipping-simulator-pdp.md).\n *\n * Returns the simulator's `onSimulate(input) => Promise<payload>` callback. It runs the\n * `ShippingSimulation` query imperatively per submit via the Apollo client read from\n * `getApolloContext()` (a non-throwing read; see the inline note below). The mapping is thin: the\n * structured `ShippingEstimate { value, unit }` is carried through untouched (the component\n * localizes it, where the `locale` lives), the API `Money` is carried as `{ amount, currencyCode }`,\n * and any transport/GraphQL failure is collapsed to one `UNKNOWN` userError so the component's\n * success/error transition stays deterministic.\n */\n\n// The simulator's domain payload is the seam's own type, imported from the component so the binding\n// is a direct assignment and the types never drift from the contract the component's tests assert.\ntype RawOption = NonNullable<ShippingSimulationQuery['shippingSimulation']['shippingOptions']>[number];\n\n// Diagnostic text for logs and `onResult`: `ShippingSimulatorError` words this code from its own `$content`.\nconst UNKNOWN_ERROR: ShippingSimulateError = {\n code: 'UNKNOWN',\n message: 'Unable to calculate shipping. Try again.',\n};\n\nfunction mapErrorCode(code: ShippingSimulateErrorCode): ShippingSimulateError['code'] {\n return code === 'INVALID_POSTAL_CODE' ? 'INVALID_POSTAL_CODE' : 'UNKNOWN';\n}\n\nfunction mapOption(option: RawOption): ShippingOption {\n return {\n id: option.id,\n name: option.name,\n channel: option.channel,\n shippingEstimate: option.shippingEstimate,\n price: { amount: option.price.amount, currencyCode: option.price.currencyCode },\n listPrice: { amount: option.listPrice.amount, currencyCode: option.listPrice.currencyCode },\n };\n}\n\nexport function useShippingSimulation(): (input: ShippingSimulateInput) => Promise<ShippingSimulationPayload> {\n // Read the client off Apollo's context directly rather than via `useApolloClient`, which throws\n // at render when no provider is present. The hook is the default `onSimulate` and runs in every\n // Root render, including presentational tests/stories that mount the Root with their own callback\n // and no ApolloProvider; a missing client only matters when the default callback is actually\n // invoked, where it is mapped to the same UNKNOWN userError as any other failure.\n const { client } = useContext(getApolloContext());\n\n return useCallback(\n async (input: ShippingSimulateInput): Promise<ShippingSimulationPayload> => {\n if (!client) {\n return { shippingOptions: null, userErrors: [UNKNOWN_ERROR] };\n }\n try {\n const result = await withRequestTimeout((signal) =>\n client.query({\n query: SHIPPING_SIMULATION,\n variables: { input },\n fetchPolicy: 'no-cache',\n context: { fetchOptions: { signal } },\n }),\n );\n\n if (result.error || !result.data) {\n return { shippingOptions: null, userErrors: [UNKNOWN_ERROR] };\n }\n\n const payload = result.data.shippingSimulation;\n const userErrors = payload.userErrors.map((error) => ({\n code: mapErrorCode(error.code),\n message: error.message,\n }));\n\n if (userErrors.length > 0) {\n return { shippingOptions: null, userErrors };\n }\n\n return {\n shippingOptions: payload.shippingOptions?.map(mapOption) ?? null,\n userErrors: [],\n };\n } catch {\n return { shippingOptions: null, userErrors: [UNKNOWN_ERROR] };\n }\n },\n [client],\n );\n}\n"],"mappings":";;;;;;AA8BA,MAAM,gBAAuC;CAC3C,MAAM;CACN,SAAS;AACX;AAEA,SAAS,aAAa,MAAgE;CACpF,OAAO,SAAS,wBAAwB,wBAAwB;AAClE;AAEA,SAAS,UAAU,QAAmC;CACpD,OAAO;EACL,IAAI,OAAO;EACX,MAAM,OAAO;EACb,SAAS,OAAO;EAChB,kBAAkB,OAAO;EACzB,OAAO;GAAE,QAAQ,OAAO,MAAM;GAAQ,cAAc,OAAO,MAAM;EAAa;EAC9E,WAAW;GAAE,QAAQ,OAAO,UAAU;GAAQ,cAAc,OAAO,UAAU;EAAa;CAC5F;AACF;AAEA,SAAgB,wBAA8F;CAM5G,MAAM,EAAE,WAAW,WAAW,iBAAiB,CAAC;CAEhD,OAAO,YACL,OAAO,UAAqE;EAC1E,IAAI,CAAC,QACH,OAAO;GAAE,iBAAiB;GAAM,YAAY,CAAC,aAAa;EAAE;EAE9D,IAAI;GACF,MAAM,SAAS,MAAM,oBAAoB,WACvC,OAAO,MAAM;IACX,OAAO;IACP,WAAW,EAAE,MAAM;IACnB,aAAa;IACb,SAAS,EAAE,cAAc,EAAE,OAAO,EAAE;GACtC,CAAC,CACH;GAEA,IAAI,OAAO,SAAS,CAAC,OAAO,MAC1B,OAAO;IAAE,iBAAiB;IAAM,YAAY,CAAC,aAAa;GAAE;GAG9D,MAAM,UAAU,OAAO,KAAK;GAC5B,MAAM,aAAa,QAAQ,WAAW,KAAK,WAAW;IACpD,MAAM,aAAa,MAAM,IAAI;IAC7B,SAAS,MAAM;GACjB,EAAE;GAEF,IAAI,WAAW,SAAS,GACtB,OAAO;IAAE,iBAAiB;IAAM;GAAW;GAG7C,OAAO;IACL,iBAAiB,QAAQ,iBAAiB,IAAI,SAAS,KAAK;IAC5D,YAAY,CAAC;GACf;EACF,QAAQ;GACN,OAAO;IAAE,iBAAiB;IAAM,YAAY,CAAC,aAAa;GAAE;EAC9D;CACF,GACA,CAAC,MAAM,CACT;AACF"}
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { PageRef } from "../sdk/navigation/types.js";
|
|
2
2
|
//#region src/utils/safe-href.d.ts
|
|
3
|
+
/** The parameter carrying the shopper's way back, read by the sign-in route and the platform's login. */
|
|
4
|
+
export declare const RETURN_URL_PARAM = "returnUrl";
|
|
3
5
|
/**
|
|
4
6
|
* The sign-in page, carrying the current logical path so the shopper can be returned where they
|
|
5
7
|
* started. The sender's half only: nothing stops a hand-crafted link, so the sign-in route is to narrow
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vtex/faststore",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.11",
|
|
4
4
|
"description": "FastStore AI Native component library built on Base UI with AI-editable styles",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"homepage": "https://developers.vtex.com/docs/guides/faststore",
|
|
@@ -45,7 +45,6 @@
|
|
|
45
45
|
"./catalog.json": "./dist/catalog.json",
|
|
46
46
|
"./actions.json": "./dist/actions.json",
|
|
47
47
|
"./content-types.json": "./dist/content-types.json",
|
|
48
|
-
"./message-locales.json": "./dist/message-locales.json",
|
|
49
48
|
"./tailwind.css": "./src/styles/tailwind.css"
|
|
50
49
|
},
|
|
51
50
|
"files": [
|
|
@@ -56,11 +55,13 @@
|
|
|
56
55
|
],
|
|
57
56
|
"dependencies": {
|
|
58
57
|
"@base-ui/react": "^1.3.0",
|
|
58
|
+
"@formatjs/icu-messageformat-parser": "^3.5.17",
|
|
59
59
|
"@graphql-typed-document-node/core": "^3.2.0",
|
|
60
60
|
"@vtex/country-forms": "1.14.0",
|
|
61
|
-
"@vtex/faststore-catalog": "0.0.
|
|
61
|
+
"@vtex/faststore-catalog": "0.0.11",
|
|
62
62
|
"esbuild": "~0.27.3",
|
|
63
63
|
"graphql": "^16.13.1",
|
|
64
|
+
"intl-messageformat": "^11.2.14",
|
|
64
65
|
"react-hook-form": "^7.85.0",
|
|
65
66
|
"react-intl": "^10.1.22",
|
|
66
67
|
"rxjs": "^7.8.2",
|
|
@@ -50,7 +50,7 @@ green.
|
|
|
50
50
|
## Layout
|
|
51
51
|
|
|
52
52
|
The provider reads an Asset's sides from `package.json` and two paths. A storefront `asset.yaml`
|
|
53
|
-
holds the envelope and
|
|
53
|
+
holds the envelope and, at most, a `secrets` list, so nothing in it changes the layout.
|
|
54
54
|
|
|
55
55
|
| Side | What marks it | Entry |
|
|
56
56
|
| --- | --- | --- |
|
|
@@ -80,7 +80,7 @@ A store lists the Asset's package as a dependency and compiles its components fr
|
|
|
80
80
|
|
|
81
81
|
`dev` needs `VTEX_ACCOUNT` in the repo root's `.env`, or `STOREFRONT_CORE_URL` to override the core
|
|
82
82
|
URL derived from the account. `VTEX_WORKSPACE` defaults to `master`. `VTEX_TOKEN`, which is
|
|
83
|
-
optional, and the value of every secret
|
|
83
|
+
optional, and the value of every secret the Asset's `asset.yaml` lists go in the root's `.env.local`, which
|
|
84
84
|
git ignores. Keep the Asset's folder free of `.env` files, so one place holds every value.
|
|
85
85
|
`validate` reads none of them.
|
|
86
86
|
|