@shopify/hydrogen 2026.10.0-preview.4 → 2026.10.0
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/dist/cli/index.mjs +23 -12
- package/dist/client/client.d.mts +18 -15
- package/dist/client/client.mjs +8 -3
- package/dist/client/client.mjs.map +1 -1
- package/dist/client/errors.d.mts +35 -13
- package/dist/client/errors.mjs +23 -13
- package/dist/client/errors.mjs.map +1 -1
- package/dist/client/types.d.mts +140 -90
- package/dist/core/analytics/cart-tracker.d.mts +19 -10
- package/dist/core/analytics/cart-tracker.mjs +18 -10
- package/dist/core/analytics/cart-tracker.mjs.map +1 -1
- package/dist/core/analytics/events.d.mts +17 -11
- package/dist/core/analytics/events.mjs +13 -4
- package/dist/core/analytics/events.mjs.map +1 -1
- package/dist/core/analytics/types.d.mts +207 -118
- package/dist/core/analytics/utils/flatten-connection.d.mts +11 -9
- package/dist/core/analytics/utils/flatten-connection.mjs +7 -6
- package/dist/core/analytics/utils/flatten-connection.mjs.map +1 -1
- package/dist/core/cache/fetch-with-cache.d.mts +24 -4
- package/dist/core/cache/fetch-with-cache.mjs +17 -2
- package/dist/core/cache/fetch-with-cache.mjs.map +1 -1
- package/dist/core/cache/key.d.mts +2 -0
- package/dist/core/cache/key.mjs.map +1 -1
- package/dist/core/cache/run-with-cache.d.mts +56 -8
- package/dist/core/cache/run-with-cache.mjs +17 -6
- package/dist/core/cache/run-with-cache.mjs.map +1 -1
- package/dist/core/cache/store.d.mts +8 -6
- package/dist/core/cache/store.mjs.map +1 -1
- package/dist/core/cache/strategies.d.mts +27 -2
- package/dist/core/cache/strategies.mjs +6 -0
- package/dist/core/cache/strategies.mjs.map +1 -1
- package/dist/core/cart/actions.d.mts +37 -30
- package/dist/core/cart/actions.mjs +21 -9
- package/dist/core/cart/actions.mjs.map +1 -1
- package/dist/core/cart/attach-quantity-input.d.mts +8 -7
- package/dist/core/cart/attach-quantity-input.mjs +8 -7
- package/dist/core/cart/attach-quantity-input.mjs.map +1 -1
- package/dist/core/cart/buyer-identity-sync.d.mts +7 -6
- package/dist/core/cart/buyer-identity-sync.mjs.map +1 -1
- package/dist/core/cart/cart.d.mts +59 -46
- package/dist/core/cart/cart.mjs +46 -29
- package/dist/core/cart/cart.mjs.map +1 -1
- package/dist/core/cart/cookie.d.mts +8 -4
- package/dist/core/cart/cookie.mjs +8 -4
- package/dist/core/cart/cookie.mjs.map +1 -1
- package/dist/core/cart/form.d.mts +34 -20
- package/dist/core/cart/form.mjs +6 -4
- package/dist/core/cart/form.mjs.map +1 -1
- package/dist/core/cart/get-cart.d.mts +27 -15
- package/dist/core/cart/get-cart.mjs +13 -8
- package/dist/core/cart/get-cart.mjs.map +1 -1
- package/dist/core/cart/quantity.d.mts +16 -5
- package/dist/core/cart/quantity.mjs +16 -5
- package/dist/core/cart/quantity.mjs.map +1 -1
- package/dist/core/cart/queries.d.mts +25 -19
- package/dist/core/cart/queries.mjs +15 -1
- package/dist/core/cart/queries.mjs.map +1 -1
- package/dist/core/cart/server-handlers.d.mts +82 -45
- package/dist/core/cart/server-handlers.mjs +4 -0
- package/dist/core/cart/server-handlers.mjs.map +1 -1
- package/dist/core/cart/state.d.mts +110 -64
- package/dist/core/cart/state.mjs +24 -6
- package/dist/core/cart/state.mjs.map +1 -1
- package/dist/core/collection/collection.d.mts +52 -53
- package/dist/core/collection/collection.mjs +5 -3
- package/dist/core/collection/collection.mjs.map +1 -1
- package/dist/core/collection/reconciler.d.mts +23 -16
- package/dist/core/collection/reconciler.mjs +8 -4
- package/dist/core/collection/reconciler.mjs.map +1 -1
- package/dist/core/collection/state.d.mts +31 -26
- package/dist/core/collection/state.mjs +4 -3
- package/dist/core/collection/state.mjs.map +1 -1
- package/dist/core/collection/url.d.mts +70 -29
- package/dist/core/collection/url.mjs +63 -25
- package/dist/core/collection/url.mjs.map +1 -1
- package/dist/core/constants.d.mts +1 -0
- package/dist/core/constants.mjs +1 -0
- package/dist/core/constants.mjs.map +1 -1
- package/dist/core/logging/logging.d.mts +14 -8
- package/dist/core/logging/logging.mjs +15 -10
- package/dist/core/logging/logging.mjs.map +1 -1
- package/dist/core/logging/types.d.mts +28 -11
- package/dist/core/logging/types.mjs.map +1 -1
- package/dist/core/money/format.d.mts +6 -4
- package/dist/core/money/format.mjs +19 -9
- package/dist/core/money/format.mjs.map +1 -1
- package/dist/core/money/types.d.mts +41 -25
- package/dist/core/predictive-search/form.d.mts +37 -7
- package/dist/core/predictive-search/form.mjs +21 -3
- package/dist/core/predictive-search/form.mjs.map +1 -1
- package/dist/core/predictive-search/queries.d.mts +18 -30
- package/dist/core/predictive-search/queries.mjs +7 -3
- package/dist/core/predictive-search/queries.mjs.map +1 -1
- package/dist/core/predictive-search/search.d.mts +25 -27
- package/dist/core/predictive-search/search.mjs +20 -19
- package/dist/core/predictive-search/search.mjs.map +1 -1
- package/dist/core/predictive-search/server-handlers.d.mts +25 -26
- package/dist/core/predictive-search/server-handlers.mjs +4 -0
- package/dist/core/predictive-search/server-handlers.mjs.map +1 -1
- package/dist/core/predictive-search/store.d.mts +65 -24
- package/dist/core/predictive-search/store.mjs +12 -1
- package/dist/core/predictive-search/store.mjs.map +1 -1
- package/dist/core/predictive-search/url.d.mts +36 -37
- package/dist/core/predictive-search/url.mjs +14 -6
- package/dist/core/predictive-search/url.mjs.map +1 -1
- package/dist/core/product/form.d.mts +63 -16
- package/dist/core/product/form.mjs +8 -2
- package/dist/core/product/form.mjs.map +1 -1
- package/dist/core/product/options.d.mts +8 -10
- package/dist/core/product/options.mjs +8 -10
- package/dist/core/product/options.mjs.map +1 -1
- package/dist/core/product/product-form.d.mts +77 -29
- package/dist/core/product/product-form.mjs +32 -10
- package/dist/core/product/product-form.mjs.map +1 -1
- package/dist/core/product/state.d.mts +55 -32
- package/dist/core/product/url.d.mts +19 -13
- package/dist/core/product/url.mjs +10 -10
- package/dist/core/product/url.mjs.map +1 -1
- package/dist/core/request-context.d.mts +77 -19
- package/dist/core/request-context.mjs +5 -1
- package/dist/core/request-context.mjs.map +1 -1
- package/dist/core/request-routing/handle-shopify-redirects.d.mts +12 -4
- package/dist/core/request-routing/handle-shopify-redirects.mjs +9 -2
- package/dist/core/request-routing/handle-shopify-redirects.mjs.map +1 -1
- package/dist/core/request-routing/handle-shopify-routes.d.mts +5 -5
- package/dist/core/request-routing/handle-shopify-routes.mjs +7 -5
- package/dist/core/request-routing/handle-shopify-routes.mjs.map +1 -1
- package/dist/core/request-routing/interceptors/proxy.mjs +79 -43
- package/dist/core/request-routing/interceptors/proxy.mjs.map +1 -1
- package/dist/core/request-routing/interceptors/ucp-profile-proxy.mjs +69 -0
- package/dist/core/request-routing/interceptors/ucp-profile-proxy.mjs.map +1 -0
- package/dist/core/request-routing/interceptors/url-redirects.mjs +6 -1
- package/dist/core/request-routing/interceptors/url-redirects.mjs.map +1 -1
- package/dist/core/request-routing/interceptors/well-known.mjs +1 -1
- package/dist/core/request-routing/interceptors/well-known.mjs.map +1 -1
- package/dist/core/request-routing/registered-routes.d.mts +12 -0
- package/dist/core/request-routing/registered-routes.mjs +12 -0
- package/dist/core/request-routing/registered-routes.mjs.map +1 -1
- package/dist/core/request-routing/route-types.d.mts +43 -18
- package/dist/core/shop-pay/shop-pay.d.mts +71 -35
- package/dist/core/shop-pay/shop-pay.mjs +36 -10
- package/dist/core/shop-pay/shop-pay.mjs.map +1 -1
- package/dist/core/shopify-scripts/index.d.mts +15 -5
- package/dist/core/shopify-scripts/index.mjs +15 -5
- package/dist/core/shopify-scripts/index.mjs.map +1 -1
- package/dist/core/shopify-scripts/initialize.d.mts +12 -4
- package/dist/core/shopify-scripts/initialize.mjs +12 -4
- package/dist/core/shopify-scripts/initialize.mjs.map +1 -1
- package/dist/core/shopify-scripts/types.d.mts +58 -25
- package/dist/core/standard-routes/build.d.mts +11 -12
- package/dist/core/standard-routes/build.mjs +11 -12
- package/dist/core/standard-routes/build.mjs.map +1 -1
- package/dist/core/standard-routes/types.d.mts +30 -24
- package/dist/core/url.mjs +2 -1
- package/dist/core/url.mjs.map +1 -1
- package/dist/customer-account/client.d.mts +36 -69
- package/dist/customer-account/client.mjs +7 -8
- package/dist/customer-account/client.mjs.map +1 -1
- package/dist/customer-account/errors.d.mts +45 -36
- package/dist/customer-account/errors.mjs +21 -36
- package/dist/customer-account/errors.mjs.map +1 -1
- package/dist/customer-account/graphql.d.mts +11 -23
- package/dist/customer-account/graphql.mjs +3 -9
- package/dist/customer-account/graphql.mjs.map +1 -1
- package/dist/customer-account/session.d.mts +130 -100
- package/dist/customer-account/session.mjs +32 -10
- package/dist/customer-account/session.mjs.map +1 -1
- package/dist/customer-account/type-resolver.d.mts +7 -8
- package/dist/development/client/client.d.mts +18 -15
- package/dist/development/client/client.mjs +8 -3
- package/dist/development/client/client.mjs.map +1 -1
- package/dist/development/client/errors.d.mts +35 -13
- package/dist/development/client/errors.mjs +23 -13
- package/dist/development/client/errors.mjs.map +1 -1
- package/dist/development/client/types.d.mts +140 -90
- package/dist/development/core/analytics/cart-tracker.d.mts +19 -10
- package/dist/development/core/analytics/cart-tracker.mjs +18 -10
- package/dist/development/core/analytics/cart-tracker.mjs.map +1 -1
- package/dist/development/core/analytics/events.d.mts +17 -11
- package/dist/development/core/analytics/events.mjs +13 -4
- package/dist/development/core/analytics/events.mjs.map +1 -1
- package/dist/development/core/analytics/types.d.mts +207 -118
- package/dist/development/core/analytics/utils/flatten-connection.d.mts +11 -9
- package/dist/development/core/analytics/utils/flatten-connection.mjs +7 -6
- package/dist/development/core/analytics/utils/flatten-connection.mjs.map +1 -1
- package/dist/development/core/cache/fetch-with-cache.d.mts +24 -4
- package/dist/development/core/cache/fetch-with-cache.mjs +17 -2
- package/dist/development/core/cache/fetch-with-cache.mjs.map +1 -1
- package/dist/development/core/cache/key.d.mts +2 -0
- package/dist/development/core/cache/key.mjs.map +1 -1
- package/dist/development/core/cache/run-with-cache.d.mts +56 -8
- package/dist/development/core/cache/run-with-cache.mjs +17 -6
- package/dist/development/core/cache/run-with-cache.mjs.map +1 -1
- package/dist/development/core/cache/store.d.mts +8 -6
- package/dist/development/core/cache/store.mjs.map +1 -1
- package/dist/development/core/cache/strategies.d.mts +27 -2
- package/dist/development/core/cache/strategies.mjs +6 -0
- package/dist/development/core/cache/strategies.mjs.map +1 -1
- package/dist/development/core/cart/actions.d.mts +37 -30
- package/dist/development/core/cart/actions.mjs +21 -9
- package/dist/development/core/cart/actions.mjs.map +1 -1
- package/dist/development/core/cart/attach-quantity-input.d.mts +8 -7
- package/dist/development/core/cart/attach-quantity-input.mjs +8 -7
- package/dist/development/core/cart/attach-quantity-input.mjs.map +1 -1
- package/dist/development/core/cart/buyer-identity-sync.d.mts +6 -5
- package/dist/development/core/cart/buyer-identity-sync.mjs.map +1 -1
- package/dist/development/core/cart/cart.d.mts +59 -46
- package/dist/development/core/cart/cart.mjs +46 -29
- package/dist/development/core/cart/cart.mjs.map +1 -1
- package/dist/development/core/cart/cookie.d.mts +8 -4
- package/dist/development/core/cart/cookie.mjs +8 -4
- package/dist/development/core/cart/cookie.mjs.map +1 -1
- package/dist/development/core/cart/form.d.mts +34 -20
- package/dist/development/core/cart/form.mjs +6 -4
- package/dist/development/core/cart/form.mjs.map +1 -1
- package/dist/development/core/cart/get-cart.d.mts +27 -15
- package/dist/development/core/cart/get-cart.mjs +13 -8
- package/dist/development/core/cart/get-cart.mjs.map +1 -1
- package/dist/development/core/cart/quantity.d.mts +16 -5
- package/dist/development/core/cart/quantity.mjs +16 -5
- package/dist/development/core/cart/quantity.mjs.map +1 -1
- package/dist/development/core/cart/queries.d.mts +25 -19
- package/dist/development/core/cart/queries.mjs +15 -1
- package/dist/development/core/cart/queries.mjs.map +1 -1
- package/dist/development/core/cart/server-handlers.d.mts +82 -45
- package/dist/development/core/cart/server-handlers.mjs +4 -0
- package/dist/development/core/cart/server-handlers.mjs.map +1 -1
- package/dist/development/core/cart/state.d.mts +110 -64
- package/dist/development/core/cart/state.mjs +24 -6
- package/dist/development/core/cart/state.mjs.map +1 -1
- package/dist/development/core/collection/collection.d.mts +52 -53
- package/dist/development/core/collection/collection.mjs +5 -3
- package/dist/development/core/collection/collection.mjs.map +1 -1
- package/dist/development/core/collection/reconciler.d.mts +23 -16
- package/dist/development/core/collection/reconciler.mjs +8 -4
- package/dist/development/core/collection/reconciler.mjs.map +1 -1
- package/dist/development/core/collection/state.d.mts +31 -26
- package/dist/development/core/collection/state.mjs +4 -3
- package/dist/development/core/collection/state.mjs.map +1 -1
- package/dist/development/core/collection/url.d.mts +70 -29
- package/dist/development/core/collection/url.mjs +63 -25
- package/dist/development/core/collection/url.mjs.map +1 -1
- package/dist/development/core/constants.mjs.map +1 -1
- package/dist/development/core/logging/logging.d.mts +14 -8
- package/dist/development/core/logging/logging.mjs +15 -10
- package/dist/development/core/logging/logging.mjs.map +1 -1
- package/dist/development/core/logging/types.d.mts +28 -11
- package/dist/development/core/logging/types.mjs.map +1 -1
- package/dist/development/core/money/format.d.mts +6 -4
- package/dist/development/core/money/format.mjs +19 -9
- package/dist/development/core/money/format.mjs.map +1 -1
- package/dist/development/core/money/types.d.mts +41 -25
- package/dist/development/core/predictive-search/form.d.mts +37 -7
- package/dist/development/core/predictive-search/form.mjs +21 -3
- package/dist/development/core/predictive-search/form.mjs.map +1 -1
- package/dist/development/core/predictive-search/queries.d.mts +18 -30
- package/dist/development/core/predictive-search/queries.mjs +7 -3
- package/dist/development/core/predictive-search/queries.mjs.map +1 -1
- package/dist/development/core/predictive-search/search.d.mts +25 -27
- package/dist/development/core/predictive-search/search.mjs +20 -19
- package/dist/development/core/predictive-search/search.mjs.map +1 -1
- package/dist/development/core/predictive-search/server-handlers.d.mts +25 -26
- package/dist/development/core/predictive-search/server-handlers.mjs +4 -0
- package/dist/development/core/predictive-search/server-handlers.mjs.map +1 -1
- package/dist/development/core/predictive-search/store.d.mts +65 -24
- package/dist/development/core/predictive-search/store.mjs +12 -1
- package/dist/development/core/predictive-search/store.mjs.map +1 -1
- package/dist/development/core/predictive-search/url.d.mts +36 -37
- package/dist/development/core/predictive-search/url.mjs +14 -6
- package/dist/development/core/predictive-search/url.mjs.map +1 -1
- package/dist/development/core/product/form.d.mts +63 -16
- package/dist/development/core/product/form.mjs +8 -2
- package/dist/development/core/product/form.mjs.map +1 -1
- package/dist/development/core/product/options.d.mts +8 -10
- package/dist/development/core/product/options.mjs +8 -10
- package/dist/development/core/product/options.mjs.map +1 -1
- package/dist/development/core/product/product-form.d.mts +77 -29
- package/dist/development/core/product/product-form.mjs +32 -10
- package/dist/development/core/product/product-form.mjs.map +1 -1
- package/dist/development/core/product/state.d.mts +55 -32
- package/dist/development/core/product/url.d.mts +19 -13
- package/dist/development/core/product/url.mjs +10 -10
- package/dist/development/core/product/url.mjs.map +1 -1
- package/dist/development/core/request-context.d.mts +77 -19
- package/dist/development/core/request-context.mjs +5 -1
- package/dist/development/core/request-context.mjs.map +1 -1
- package/dist/development/core/request-routing/handle-shopify-redirects.d.mts +12 -4
- package/dist/development/core/request-routing/handle-shopify-redirects.mjs +9 -2
- package/dist/development/core/request-routing/handle-shopify-redirects.mjs.map +1 -1
- package/dist/development/core/request-routing/handle-shopify-routes.development.d.mts +9 -1
- package/dist/development/core/request-routing/handle-shopify-routes.development.mjs +7 -0
- package/dist/development/core/request-routing/handle-shopify-routes.development.mjs.map +1 -1
- package/dist/development/core/request-routing/handle-shopify-routes.mjs +7 -5
- package/dist/development/core/request-routing/handle-shopify-routes.mjs.map +1 -1
- package/dist/development/core/request-routing/interceptors/proxy.mjs +79 -43
- package/dist/development/core/request-routing/interceptors/proxy.mjs.map +1 -1
- package/dist/development/core/request-routing/interceptors/ucp-profile-proxy.mjs +69 -0
- package/dist/development/core/request-routing/interceptors/ucp-profile-proxy.mjs.map +1 -0
- package/dist/development/core/request-routing/interceptors/url-redirects.mjs +6 -1
- package/dist/development/core/request-routing/interceptors/url-redirects.mjs.map +1 -1
- package/dist/development/core/request-routing/interceptors/well-known.mjs +1 -1
- package/dist/development/core/request-routing/interceptors/well-known.mjs.map +1 -1
- package/dist/development/core/request-routing/registered-routes.d.mts +12 -0
- package/dist/development/core/request-routing/registered-routes.mjs +12 -0
- package/dist/development/core/request-routing/registered-routes.mjs.map +1 -1
- package/dist/development/core/request-routing/route-types.d.mts +43 -18
- package/dist/development/core/shop-pay/shop-pay.d.mts +71 -35
- package/dist/development/core/shop-pay/shop-pay.mjs +36 -10
- package/dist/development/core/shop-pay/shop-pay.mjs.map +1 -1
- package/dist/development/core/shopify-scripts/index.d.mts +15 -5
- package/dist/development/core/shopify-scripts/index.mjs +15 -5
- package/dist/development/core/shopify-scripts/index.mjs.map +1 -1
- package/dist/development/core/shopify-scripts/initialize.d.mts +12 -4
- package/dist/development/core/shopify-scripts/initialize.mjs +12 -4
- package/dist/development/core/shopify-scripts/initialize.mjs.map +1 -1
- package/dist/development/core/shopify-scripts/types.d.mts +58 -25
- package/dist/development/core/standard-routes/build.d.mts +11 -12
- package/dist/development/core/standard-routes/build.mjs +11 -12
- package/dist/development/core/standard-routes/build.mjs.map +1 -1
- package/dist/development/core/standard-routes/types.d.mts +30 -24
- package/dist/development/core/url.mjs +2 -1
- package/dist/development/core/url.mjs.map +1 -1
- package/dist/development/customer-account/session.d.mts +45 -54
- package/dist/development/customer-account/session.mjs +20 -4
- package/dist/development/customer-account/session.mjs.map +1 -1
- package/dist/development/globals.d.mts +20 -11
- package/dist/development/graphql/generated/storefront-api-types.d.mts +127 -1
- package/dist/development/graphql/graphql.d.mts +17 -16
- package/dist/development/graphql/graphql.mjs +7 -8
- package/dist/development/graphql/graphql.mjs.map +1 -1
- package/dist/development/graphql/type-resolver.d.mts +15 -8
- package/dist/development/react/cart.d.mts +78 -32
- package/dist/development/react/cart.mjs +56 -29
- package/dist/development/react/cart.mjs.map +1 -1
- package/dist/development/react/collection.d.mts +40 -22
- package/dist/development/react/collection.mjs +27 -7
- package/dist/development/react/collection.mjs.map +1 -1
- package/dist/development/react/predictive-search.d.mts +65 -50
- package/dist/development/react/predictive-search.mjs +23 -21
- package/dist/development/react/predictive-search.mjs.map +1 -1
- package/dist/development/react/product.d.mts +45 -16
- package/dist/development/react/product.mjs +13 -11
- package/dist/development/react/product.mjs.map +1 -1
- package/dist/development/react/shop-pay.d.mts +15 -0
- package/dist/development/react/shop-pay.mjs +9 -0
- package/dist/development/react/shop-pay.mjs.map +1 -1
- package/dist/development/react/shopify-scripts.d.mts +23 -2
- package/dist/development/react/shopify-scripts.mjs +9 -0
- package/dist/development/react/shopify-scripts.mjs.map +1 -1
- package/dist/development/vue/cart.d.mts +28 -23
- package/dist/development/vue/cart.mjs +42 -25
- package/dist/development/vue/cart.mjs.map +1 -1
- package/dist/development/vue/collection.d.mts +20 -12
- package/dist/development/vue/collection.mjs +15 -7
- package/dist/development/vue/collection.mjs.map +1 -1
- package/dist/development/vue/index.d.mts +2 -2
- package/dist/development/vue/index.mjs +2 -2
- package/dist/development/vue/predictive-search.d.mts +31 -36
- package/dist/development/vue/predictive-search.mjs +16 -22
- package/dist/development/vue/predictive-search.mjs.map +1 -1
- package/dist/development/vue/product.d.mts +16 -15
- package/dist/development/vue/product.mjs +11 -11
- package/dist/development/vue/product.mjs.map +1 -1
- package/dist/development/vue/shop-pay.d.mts +14 -17
- package/dist/development/vue/shop-pay.mjs +8 -0
- package/dist/development/vue/shop-pay.mjs.map +1 -1
- package/dist/development/vue/shopify-scripts.d.mts +11 -0
- package/dist/development/vue/shopify-scripts.mjs +6 -0
- package/dist/development/vue/shopify-scripts.mjs.map +1 -1
- package/dist/globals.d.mts +20 -11
- package/dist/graphql/generated/storefront-api-types.d.mts +127 -1
- package/dist/graphql/graphql.d.mts +17 -16
- package/dist/graphql/graphql.mjs +7 -8
- package/dist/graphql/graphql.mjs.map +1 -1
- package/dist/graphql/type-resolver.d.mts +15 -8
- package/dist/react/cart.d.mts +78 -32
- package/dist/react/cart.mjs +56 -29
- package/dist/react/cart.mjs.map +1 -1
- package/dist/react/collection.d.mts +40 -22
- package/dist/react/collection.mjs +27 -7
- package/dist/react/collection.mjs.map +1 -1
- package/dist/react/predictive-search.d.mts +65 -50
- package/dist/react/predictive-search.mjs +23 -21
- package/dist/react/predictive-search.mjs.map +1 -1
- package/dist/react/product.d.mts +45 -16
- package/dist/react/product.mjs +13 -11
- package/dist/react/product.mjs.map +1 -1
- package/dist/react/shop-pay.d.mts +15 -0
- package/dist/react/shop-pay.mjs +9 -0
- package/dist/react/shop-pay.mjs.map +1 -1
- package/dist/react/shopify-scripts.d.mts +23 -2
- package/dist/react/shopify-scripts.mjs +9 -0
- package/dist/react/shopify-scripts.mjs.map +1 -1
- package/dist/ts-plugin/index.cjs +6 -0
- package/dist/ts-plugin/index.cjs.map +1 -1
- package/dist/ts-plugin/index.d.cts +6 -0
- package/dist/vite/local-https.d.mts +51 -17
- package/dist/vite/local-https.mjs +26 -5
- package/dist/vite/local-https.mjs.map +1 -1
- package/dist/vue/cart.d.mts +28 -23
- package/dist/vue/cart.mjs +42 -25
- package/dist/vue/cart.mjs.map +1 -1
- package/dist/vue/collection.d.mts +20 -12
- package/dist/vue/collection.mjs +15 -7
- package/dist/vue/collection.mjs.map +1 -1
- package/dist/vue/index.d.mts +2 -2
- package/dist/vue/index.mjs +2 -2
- package/dist/vue/predictive-search.d.mts +31 -36
- package/dist/vue/predictive-search.mjs +16 -22
- package/dist/vue/predictive-search.mjs.map +1 -1
- package/dist/vue/product.d.mts +16 -15
- package/dist/vue/product.mjs +11 -11
- package/dist/vue/product.mjs.map +1 -1
- package/dist/vue/shop-pay.d.mts +14 -17
- package/dist/vue/shop-pay.mjs +8 -0
- package/dist/vue/shop-pay.mjs.map +1 -1
- package/dist/vue/shopify-scripts.d.mts +11 -0
- package/dist/vue/shopify-scripts.mjs +6 -0
- package/dist/vue/shopify-scripts.mjs.map +1 -1
- package/package.json +3 -1
- package/skills/hydrogen-analytics/SKILL.md +1 -1
- package/skills/hydrogen-cart-ui/SKILL.md +28 -2
- package/skills/hydrogen-collection-browser/SKILL.md +9 -1
- package/skills/hydrogen-collection-browser/references/nextjs.md +1 -1
- package/skills/hydrogen-collection-browser/references/nuxt.md +1 -1
- package/skills/hydrogen-collection-browser/references/react.md +151 -32
- package/skills/hydrogen-customer-account/SKILL.md +2 -0
- package/skills/hydrogen-request-handlers/SKILL.md +21 -11
- package/skills/hydrogen-setup/references/analytics.md +14 -8
- package/skills/hydrogen-setup/steps/1-inspect-app.md +2 -2
- package/skills/hydrogen-setup/steps/6-product-detail-page.md +21 -11
- package/skills/hydrogen-storefront-client/SKILL.md +1 -1
- package/skills/hydrogen-storefront-client/references/caching.md +2 -0
- package/skills/hydrogen-variant-form/SKILL.md +25 -7
- package/skills/hydrogen-variant-form/references/nextjs.md +39 -4
- package/skills/hydrogen-variant-form/references/nuxt.md +83 -42
- package/skills/hydrogen-variant-form/references/react.md +45 -9
|
@@ -4,70 +4,111 @@ import { CollectionState } from "./state.mjs";
|
|
|
4
4
|
|
|
5
5
|
//#region src/core/collection/url.d.ts
|
|
6
6
|
/**
|
|
7
|
-
*
|
|
8
|
-
* that is derived from the URL query string.
|
|
7
|
+
* The filters, sort key, and sort direction in a collection URL.
|
|
9
8
|
*/
|
|
10
9
|
interface CollectionParams {
|
|
11
|
-
/**
|
|
10
|
+
/**
|
|
11
|
+
* Active product filters from the `filter.*` URL params.
|
|
12
|
+
*
|
|
13
|
+
* The filters use the Storefront Standard Events filter type. When your query variables use generated Storefront API types, cast the filters to the generated filter type where you pass them as variables.
|
|
14
|
+
*/
|
|
12
15
|
filters: ProductFilter[];
|
|
13
|
-
/** Storefront API sort key
|
|
16
|
+
/** Storefront API sort key from the `sort_by` param, or `undefined` when the param is missing or unrecognized. */
|
|
14
17
|
sortKey: ProductCollectionSortKeys | undefined;
|
|
15
18
|
/** `true` when the `sort_by` value ends with `-descending`. */
|
|
16
19
|
reverse: boolean;
|
|
17
20
|
}
|
|
18
21
|
/**
|
|
19
|
-
*
|
|
22
|
+
* Reads collection filters and sort from URL search params. Pass the result to your Storefront API collection query.
|
|
20
23
|
*
|
|
21
|
-
*
|
|
22
|
-
* and the `sort_by` param (with optional `-descending`/`-ascending` suffix).
|
|
24
|
+
* The function reads Liquid-compatible `filter.p.*` and `filter.v.*` keys and the `sort_by` param.
|
|
23
25
|
*
|
|
24
|
-
*
|
|
26
|
+
* A URL that sets availability to both in stock and out of stock produces no availability filter. The function recognizes the best-selling and manual sort values, plus created, price, and title with an ascending or descending suffix. For other sort values, the sort key is `undefined`. The `parseSortByValue` function also recognizes relevance, collection default, and ID.
|
|
27
|
+
*
|
|
28
|
+
* @param searchParams - URL search params to read.
|
|
29
|
+
* @returns The filters, sort key, and sort direction from the URL.
|
|
30
|
+
* @publicDocs
|
|
25
31
|
*/
|
|
26
32
|
declare function parseCollectionParams(searchParams: URLSearchParams): CollectionParams;
|
|
27
33
|
/**
|
|
28
|
-
*
|
|
34
|
+
* Converts collection filters and sort to URL search params with Liquid-compatible keys. Use the result to build collection URLs.
|
|
35
|
+
*
|
|
36
|
+
* The result holds only the `filter.*` and `sort_by` keys. The function leaves out `sort_by` for the collection's default sort.
|
|
29
37
|
*
|
|
30
|
-
*
|
|
31
|
-
* Omits `sort_by` when `sortKey` is `undefined` (collection default).
|
|
38
|
+
* Category filters convert to `filter.p.category`, which `parseCollectionParams` doesn't read. A price filter with a minimum and a maximum produces two params. To build a filter checkbox, parse a filter value's `input` string and convert the parsed filter. The resulting entries give the checkbox name and value.
|
|
32
39
|
*
|
|
33
|
-
* @param state - The
|
|
40
|
+
* @param state - The filters, sort key, and sort direction to convert.
|
|
41
|
+
* @returns URL search params that hold the filters and sort.
|
|
42
|
+
* @publicDocs
|
|
34
43
|
*/
|
|
35
44
|
declare function serializeCollectionParams(state: Pick<CollectionState, "filters" | "sortKey" | "reverse">): URLSearchParams;
|
|
36
|
-
/**
|
|
45
|
+
/**
|
|
46
|
+
* Removes a leading `?` from a search string. Normalize search strings before you compare them.
|
|
47
|
+
*
|
|
48
|
+
* @param search The search string from your router, with or without a leading `?`.
|
|
49
|
+
* @returns The search string without a leading `?`.
|
|
50
|
+
* @publicDocs
|
|
51
|
+
*/
|
|
37
52
|
declare function normalizeCollectionSearch(search: string): string;
|
|
38
53
|
/**
|
|
39
|
-
* Builds a
|
|
54
|
+
* Builds a query string from the current URL params, minus the given filter. Use the string in plain remove-filter links.
|
|
55
|
+
*
|
|
56
|
+
* The function keeps every other param and drops the `before` and `after` pagination cursors, which sends the customer to the first page of results.
|
|
40
57
|
*
|
|
41
|
-
*
|
|
42
|
-
*
|
|
43
|
-
*
|
|
58
|
+
* @param currentParams The current URL search params.
|
|
59
|
+
* @param filter The filter to remove from the URL.
|
|
60
|
+
* @returns A query string that starts with `?`. The string is `?` alone when no params remain.
|
|
61
|
+
* @publicDocs
|
|
44
62
|
*/
|
|
45
63
|
declare function getFilterRemovalUrl(currentParams: URLSearchParams, filter: ProductFilter): string;
|
|
46
64
|
/**
|
|
47
|
-
* Converts a Storefront API sort key and direction
|
|
48
|
-
* `sort_by`
|
|
65
|
+
* Converts a Storefront API sort key and direction to a Liquid-compatible
|
|
66
|
+
* `sort_by` value, such as `price-ascending` or `best-selling`.
|
|
49
67
|
*
|
|
50
|
-
*
|
|
51
|
-
*
|
|
52
|
-
* the base value regardless of the `reverse` flag.
|
|
68
|
+
* Only the price, title, created, and ID sort keys get a direction suffix.
|
|
69
|
+
* Other sort keys, such as best selling, ignore the direction.
|
|
53
70
|
*
|
|
54
|
-
* @param sortKey - Storefront API
|
|
55
|
-
* @param reverse - `true` for descending, `false` for ascending
|
|
71
|
+
* @param sortKey - Storefront API product collection sort key.
|
|
72
|
+
* @param reverse - `true` for descending, `false` for ascending.
|
|
73
|
+
* @returns The value for the `sort_by` URL param.
|
|
74
|
+
* @publicDocs
|
|
56
75
|
*/
|
|
57
76
|
declare function getSortByValue(sortKey: ProductCollectionSortKeys, reverse: boolean): string;
|
|
58
77
|
/**
|
|
59
|
-
*
|
|
60
|
-
* sort key and direction.
|
|
78
|
+
* Converts a Liquid-compatible `sort_by` value to a Storefront API
|
|
79
|
+
* sort key and direction. The `getSortByValue` function does the opposite conversion.
|
|
61
80
|
*
|
|
62
|
-
*
|
|
81
|
+
* For unrecognized values, the sort key is `undefined`.
|
|
82
|
+
*
|
|
83
|
+
* @param value - A `sort_by` value, such as `price-ascending` or `best-selling`.
|
|
84
|
+
* @returns The sort key and `reverse`, which reads `true` for descending values.
|
|
85
|
+
* @publicDocs
|
|
63
86
|
*/
|
|
64
87
|
declare function parseSortByValue(value: string): {
|
|
65
88
|
sortKey: ProductCollectionSortKeys | undefined;
|
|
66
89
|
reverse: boolean;
|
|
67
90
|
};
|
|
68
|
-
/**
|
|
91
|
+
/**
|
|
92
|
+
* Checks whether two product filters match.
|
|
93
|
+
*
|
|
94
|
+
* Price filters match when the minimum and maximum are equal, including ranges that set only one bound.
|
|
95
|
+
*
|
|
96
|
+
* @param a The first filter to compare.
|
|
97
|
+
* @param b The second filter to compare.
|
|
98
|
+
* @returns `true` when both filters are the same kind with the same values. `false` for different kinds or unrecognized filters.
|
|
99
|
+
* @publicDocs
|
|
100
|
+
*/
|
|
69
101
|
declare function filterEquals(a: ProductFilter, b: ProductFilter): boolean;
|
|
70
|
-
/**
|
|
102
|
+
/**
|
|
103
|
+
* Checks whether a filter value's `input` JSON string from the Storefront API matches an active filter. Use the result to set the checked state of a filter control.
|
|
104
|
+
*
|
|
105
|
+
* The function compares filters the same way as `filterEquals`.
|
|
106
|
+
*
|
|
107
|
+
* @param activeFilters The active filters, such as `filters` from the collection state.
|
|
108
|
+
* @param input The filter value's `input` JSON string.
|
|
109
|
+
* @returns `true` when the parsed input matches an active filter. `false` when the input matches no active filter or isn't valid JSON.
|
|
110
|
+
* @publicDocs
|
|
111
|
+
*/
|
|
71
112
|
declare function isFilterInputActive(activeFilters: ProductFilter[], input: string): boolean;
|
|
72
113
|
//#endregion
|
|
73
114
|
export { CollectionParams, filterEquals, getFilterRemovalUrl, getSortByValue, isFilterInputActive, normalizeCollectionSearch, parseCollectionParams, parseSortByValue, serializeCollectionParams };
|
|
@@ -24,12 +24,15 @@ const STANDARD_EVENTS_COLLECTION_SORT_KEY = {
|
|
|
24
24
|
manual: "MANUAL"
|
|
25
25
|
};
|
|
26
26
|
/**
|
|
27
|
-
*
|
|
27
|
+
* Reads collection filters and sort from URL search params. Pass the result to your Storefront API collection query.
|
|
28
28
|
*
|
|
29
|
-
*
|
|
30
|
-
* and the `sort_by` param (with optional `-descending`/`-ascending` suffix).
|
|
29
|
+
* The function reads Liquid-compatible `filter.p.*` and `filter.v.*` keys and the `sort_by` param.
|
|
31
30
|
*
|
|
32
|
-
*
|
|
31
|
+
* A URL that sets availability to both in stock and out of stock produces no availability filter. The function recognizes the best-selling and manual sort values, plus created, price, and title with an ascending or descending suffix. For other sort values, the sort key is `undefined`. The `parseSortByValue` function also recognizes relevance, collection default, and ID.
|
|
32
|
+
*
|
|
33
|
+
* @param searchParams - URL search params to read.
|
|
34
|
+
* @returns The filters, sort key, and sort direction from the URL.
|
|
35
|
+
* @publicDocs
|
|
33
36
|
*/
|
|
34
37
|
function parseCollectionParams(searchParams) {
|
|
35
38
|
return {
|
|
@@ -39,12 +42,15 @@ function parseCollectionParams(searchParams) {
|
|
|
39
42
|
};
|
|
40
43
|
}
|
|
41
44
|
/**
|
|
42
|
-
*
|
|
45
|
+
* Converts collection filters and sort to URL search params with Liquid-compatible keys. Use the result to build collection URLs.
|
|
46
|
+
*
|
|
47
|
+
* The result holds only the `filter.*` and `sort_by` keys. The function leaves out `sort_by` for the collection's default sort.
|
|
43
48
|
*
|
|
44
|
-
*
|
|
45
|
-
* Omits `sort_by` when `sortKey` is `undefined` (collection default).
|
|
49
|
+
* Category filters convert to `filter.p.category`, which `parseCollectionParams` doesn't read. A price filter with a minimum and a maximum produces two params. To build a filter checkbox, parse a filter value's `input` string and convert the parsed filter. The resulting entries give the checkbox name and value.
|
|
46
50
|
*
|
|
47
|
-
* @param state - The
|
|
51
|
+
* @param state - The filters, sort key, and sort direction to convert.
|
|
52
|
+
* @returns URL search params that hold the filters and sort.
|
|
53
|
+
* @publicDocs
|
|
48
54
|
*/
|
|
49
55
|
function serializeCollectionParams(state) {
|
|
50
56
|
const params = new URLSearchParams();
|
|
@@ -56,7 +62,13 @@ function serializeCollectionParams(state) {
|
|
|
56
62
|
function isStoreOwnedParam(key) {
|
|
57
63
|
return key === "sort_by" || key.startsWith("filter.");
|
|
58
64
|
}
|
|
59
|
-
/**
|
|
65
|
+
/**
|
|
66
|
+
* Removes a leading `?` from a search string. Normalize search strings before you compare them.
|
|
67
|
+
*
|
|
68
|
+
* @param search The search string from your router, with or without a leading `?`.
|
|
69
|
+
* @returns The search string without a leading `?`.
|
|
70
|
+
* @publicDocs
|
|
71
|
+
*/
|
|
60
72
|
function normalizeCollectionSearch(search) {
|
|
61
73
|
return search.startsWith("?") ? search.slice(1) : search;
|
|
62
74
|
}
|
|
@@ -85,11 +97,14 @@ function collectionParamsMatchState(searchParams, state) {
|
|
|
85
97
|
return parsed.sortKey === state.sortKey && parsed.reverse === state.reverse && filtersEqual(parsed.filters, state.filters);
|
|
86
98
|
}
|
|
87
99
|
/**
|
|
88
|
-
* Builds a
|
|
100
|
+
* Builds a query string from the current URL params, minus the given filter. Use the string in plain remove-filter links.
|
|
101
|
+
*
|
|
102
|
+
* The function keeps every other param and drops the `before` and `after` pagination cursors, which sends the customer to the first page of results.
|
|
89
103
|
*
|
|
90
|
-
*
|
|
91
|
-
*
|
|
92
|
-
*
|
|
104
|
+
* @param currentParams The current URL search params.
|
|
105
|
+
* @param filter The filter to remove from the URL.
|
|
106
|
+
* @returns A query string that starts with `?`. The string is `?` alone when no params remain.
|
|
107
|
+
* @publicDocs
|
|
93
108
|
*/
|
|
94
109
|
function getFilterRemovalUrl(currentParams, filter) {
|
|
95
110
|
const result = new URLSearchParams(currentParams);
|
|
@@ -103,15 +118,16 @@ function isDirectionalSortKey(sortKey) {
|
|
|
103
118
|
return sortKey === "PRICE" || sortKey === "TITLE" || sortKey === "CREATED" || sortKey === "ID";
|
|
104
119
|
}
|
|
105
120
|
/**
|
|
106
|
-
* Converts a Storefront API sort key and direction
|
|
107
|
-
* `sort_by`
|
|
121
|
+
* Converts a Storefront API sort key and direction to a Liquid-compatible
|
|
122
|
+
* `sort_by` value, such as `price-ascending` or `best-selling`.
|
|
108
123
|
*
|
|
109
|
-
*
|
|
110
|
-
*
|
|
111
|
-
* the base value regardless of the `reverse` flag.
|
|
124
|
+
* Only the price, title, created, and ID sort keys get a direction suffix.
|
|
125
|
+
* Other sort keys, such as best selling, ignore the direction.
|
|
112
126
|
*
|
|
113
|
-
* @param sortKey - Storefront API
|
|
114
|
-
* @param reverse - `true` for descending, `false` for ascending
|
|
127
|
+
* @param sortKey - Storefront API product collection sort key.
|
|
128
|
+
* @param reverse - `true` for descending, `false` for ascending.
|
|
129
|
+
* @returns The value for the `sort_by` URL param.
|
|
130
|
+
* @publicDocs
|
|
115
131
|
*/
|
|
116
132
|
function getSortByValue(sortKey, reverse) {
|
|
117
133
|
const base = SORT_KEY_TO_SORT_BY[sortKey] ?? sortKey.toLowerCase();
|
|
@@ -119,10 +135,14 @@ function getSortByValue(sortKey, reverse) {
|
|
|
119
135
|
return reverse ? `${base}-descending` : `${base}-ascending`;
|
|
120
136
|
}
|
|
121
137
|
/**
|
|
122
|
-
*
|
|
123
|
-
* sort key and direction.
|
|
138
|
+
* Converts a Liquid-compatible `sort_by` value to a Storefront API
|
|
139
|
+
* sort key and direction. The `getSortByValue` function does the opposite conversion.
|
|
124
140
|
*
|
|
125
|
-
*
|
|
141
|
+
* For unrecognized values, the sort key is `undefined`.
|
|
142
|
+
*
|
|
143
|
+
* @param value - A `sort_by` value, such as `price-ascending` or `best-selling`.
|
|
144
|
+
* @returns The sort key and `reverse`, which reads `true` for descending values.
|
|
145
|
+
* @publicDocs
|
|
126
146
|
*/
|
|
127
147
|
function parseSortByValue(value) {
|
|
128
148
|
if (value.endsWith(SORT_BY_DESCENDING_SUFFIX)) return {
|
|
@@ -283,7 +303,16 @@ function removeFilterParams(params, filter) {
|
|
|
283
303
|
function clearPaginationCursors(params) {
|
|
284
304
|
for (const key of PAGINATION_CURSOR_PARAMS) params.delete(key);
|
|
285
305
|
}
|
|
286
|
-
/**
|
|
306
|
+
/**
|
|
307
|
+
* Checks whether two product filters match.
|
|
308
|
+
*
|
|
309
|
+
* Price filters match when the minimum and maximum are equal, including ranges that set only one bound.
|
|
310
|
+
*
|
|
311
|
+
* @param a The first filter to compare.
|
|
312
|
+
* @param b The second filter to compare.
|
|
313
|
+
* @returns `true` when both filters are the same kind with the same values. `false` for different kinds or unrecognized filters.
|
|
314
|
+
* @publicDocs
|
|
315
|
+
*/
|
|
287
316
|
function filterEquals(a, b) {
|
|
288
317
|
const kindA = filterKind(a);
|
|
289
318
|
if (kindA !== filterKind(b) || kindA == null) return false;
|
|
@@ -325,7 +354,16 @@ function filterEquals(a, b) {
|
|
|
325
354
|
}
|
|
326
355
|
}
|
|
327
356
|
}
|
|
328
|
-
/**
|
|
357
|
+
/**
|
|
358
|
+
* Checks whether a filter value's `input` JSON string from the Storefront API matches an active filter. Use the result to set the checked state of a filter control.
|
|
359
|
+
*
|
|
360
|
+
* The function compares filters the same way as `filterEquals`.
|
|
361
|
+
*
|
|
362
|
+
* @param activeFilters The active filters, such as `filters` from the collection state.
|
|
363
|
+
* @param input The filter value's `input` JSON string.
|
|
364
|
+
* @returns `true` when the parsed input matches an active filter. `false` when the input matches no active filter or isn't valid JSON.
|
|
365
|
+
* @publicDocs
|
|
366
|
+
*/
|
|
329
367
|
function isFilterInputActive(activeFilters, input) {
|
|
330
368
|
let parsed;
|
|
331
369
|
try {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"url.mjs","names":[],"sources":["../../../src/core/collection/url.ts"],"sourcesContent":["import type { ProductFilter } from \"../../../vendor/standard-events\";\nimport type { ProductCollectionSortKeys } from \"../../graphql/generated/storefront-api-types\";\nimport type { CollectionState } from \"./state\";\n\n/**\n * Parsed collection URL parameters — the subset of {@link CollectionState}\n * that is derived from the URL query string.\n */\nexport interface CollectionParams {\n /** Active product filters parsed from `filter.*` URL keys. */\n filters: ProductFilter[];\n /** Storefront API sort key parsed from the `sort_by` param. `undefined` when absent. */\n sortKey: ProductCollectionSortKeys | undefined;\n /** `true` when the `sort_by` value ends with `-descending`. */\n reverse: boolean;\n}\n\nconst SORT_BY_DESCENDING_SUFFIX = \"-descending\";\nconst SORT_BY_ASCENDING_SUFFIX = \"-ascending\";\nconst PAGINATION_CURSOR_PARAMS: readonly string[] = [\"before\", \"after\"];\n\nconst SORT_KEY_TO_SORT_BY: Record<string, string> = {\n BEST_SELLING: \"best-selling\",\n CREATED: \"created\",\n PRICE: \"price\",\n TITLE: \"title\",\n MANUAL: \"manual\",\n ID: \"id\",\n RELEVANCE: \"relevance\",\n COLLECTION_DEFAULT: \"collection-default\",\n};\n\nconst SORT_BY_TO_SORT_KEY: Record<string, ProductCollectionSortKeys> = Object.fromEntries(\n Object.entries(SORT_KEY_TO_SORT_BY).map(([key, value]) => [\n value,\n key as ProductCollectionSortKeys,\n ]),\n);\n\nconst STANDARD_EVENTS_COLLECTION_SORT_KEY: Record<string, ProductCollectionSortKeys> = {\n \"best-selling\": \"BEST_SELLING\",\n \"created-ascending\": \"CREATED\",\n \"created-descending\": \"CREATED\",\n \"price-ascending\": \"PRICE\",\n \"price-descending\": \"PRICE\",\n \"title-ascending\": \"TITLE\",\n \"title-descending\": \"TITLE\",\n manual: \"MANUAL\",\n};\n\n/**\n * Extracts collection state from URL search params.\n *\n * Handles Liquid-compatible filter keys (`filter.p.*`, `filter.v.*`)\n * and the `sort_by` param (with optional `-descending`/`-ascending` suffix).\n *\n * @param searchParams - URL search params to parse\n */\nexport function parseCollectionParams(searchParams: URLSearchParams): CollectionParams {\n const filters = parseProductFilters(searchParams);\n const sortKey = parseCollectionSortKey(searchParams);\n const rawSortBy = searchParams.get(\"sort_by\");\n const reverse = rawSortBy?.endsWith(SORT_BY_DESCENDING_SUFFIX) ?? false;\n\n return { filters, sortKey, reverse };\n}\n\n/**\n * Serializes collection state into URL search params using Liquid-compatible keys.\n *\n * Only emits store-owned keys (`filter.*`, `sort_by`).\n * Omits `sort_by` when `sortKey` is `undefined` (collection default).\n *\n * @param state - The filter/sort slice of collection state\n */\nexport function serializeCollectionParams(\n state: Pick<CollectionState, \"filters\" | \"sortKey\" | \"reverse\">,\n): URLSearchParams {\n const params = new URLSearchParams();\n\n for (const filter of state.filters) {\n serializeFilter(params, filter);\n }\n\n if (state.sortKey) {\n params.set(\"sort_by\", getSortByValue(state.sortKey, state.reverse));\n }\n\n return params;\n}\n\n/** Returns `true` when the param key is owned by the collection store. */\nfunction isStoreOwnedParam(key: string): boolean {\n return key === \"sort_by\" || key.startsWith(\"filter.\");\n}\n\n/** Normalizes a router search string for comparison (strips a leading `?`). */\nexport function normalizeCollectionSearch(search: string): string {\n return search.startsWith(\"?\") ? search.slice(1) : search;\n}\n\n/**\n * Merges store-owned params from `state` into `existing`, preserving\n * unrelated keys (e.g. `grid`, `view`) already present in the URL. Collection\n * pagination cursors (`before`, `after`) are cleared so changed browse intent\n * starts from the first page.\n */\nexport function mergeCollectionParams(\n existing: URLSearchParams,\n state: Pick<CollectionState, \"filters\" | \"sortKey\" | \"reverse\">,\n): URLSearchParams {\n const merged = new URLSearchParams(existing);\n\n for (const key of Array.from(merged.keys())) {\n if (isStoreOwnedParam(key)) {\n merged.delete(key);\n }\n }\n\n clearPaginationCursors(merged);\n\n for (const [key, value] of serializeCollectionParams(state)) {\n merged.append(key, value);\n }\n\n return merged;\n}\n\n/** Returns `true` when two router search strings describe the same collection filters and sort. */\nexport function collectionSearchEqual(a: string, b: string): boolean {\n const parsedA = parseCollectionParams(new URLSearchParams(normalizeCollectionSearch(a)));\n const parsedB = parseCollectionParams(new URLSearchParams(normalizeCollectionSearch(b)));\n\n return (\n parsedA.sortKey === parsedB.sortKey &&\n parsedA.reverse === parsedB.reverse &&\n filtersEqual(parsedA.filters, parsedB.filters)\n );\n}\n\n/** Returns `true` when URL params and collection browse state describe the same filters and sort. */\nexport function collectionParamsMatchState(\n searchParams: URLSearchParams,\n state: Pick<CollectionState, \"filters\" | \"sortKey\" | \"reverse\">,\n): boolean {\n const parsed = parseCollectionParams(searchParams);\n\n return (\n parsed.sortKey === state.sortKey &&\n parsed.reverse === state.reverse &&\n filtersEqual(parsed.filters, state.filters)\n );\n}\n\n/**\n * Builds a URL query string with the given filter removed from the current params.\n *\n * Clears pagination cursors so the changed filter state starts from its first page.\n * Returns `\"?\"` when removing the filter leaves no params. Useful for\n * rendering \"remove filter\" links without updating store state.\n */\nexport function getFilterRemovalUrl(currentParams: URLSearchParams, filter: ProductFilter): string {\n const result = new URLSearchParams(currentParams);\n removeFilterParams(result, filter);\n clearPaginationCursors(result);\n const serialized = result.toString();\n return serialized ? `?${serialized}` : \"?\";\n}\n\n/** Returns `true` when the sort key supports ascending/descending direction suffixes. */\nexport function isDirectionalSortKey(sortKey: ProductCollectionSortKeys): boolean {\n return sortKey === \"PRICE\" || sortKey === \"TITLE\" || sortKey === \"CREATED\" || sortKey === \"ID\";\n}\n\n/**\n * Converts a Storefront API sort key and direction into the Liquid-compatible\n * `sort_by` param value (e.g. `\"price-ascending\"`, `\"best-selling\"`).\n *\n * Direction suffixes are only appended for sort keys that support them\n * (`PRICE`, `TITLE`, `CREATED`, `ID`). Others (like `BEST_SELLING`) return\n * the base value regardless of the `reverse` flag.\n *\n * @param sortKey - Storefront API `ProductCollectionSortKeys` enum value\n * @param reverse - `true` for descending, `false` for ascending\n */\nexport function getSortByValue(sortKey: ProductCollectionSortKeys, reverse: boolean): string {\n const base = SORT_KEY_TO_SORT_BY[sortKey] ?? sortKey.toLowerCase();\n\n if (!isDirectionalSortKey(sortKey)) return base;\n\n return reverse ? `${base}-descending` : `${base}-ascending`;\n}\n\n/**\n * Parses a Liquid-compatible `sort_by` value back into the Storefront API\n * sort key and direction. Inverse of {@link getSortByValue}.\n *\n * @param value - A `sort_by` param value (e.g. `\"price-ascending\"`, `\"best-selling\"`)\n */\nexport function parseSortByValue(value: string): {\n sortKey: ProductCollectionSortKeys | undefined;\n reverse: boolean;\n} {\n if (value.endsWith(SORT_BY_DESCENDING_SUFFIX)) {\n const base = value.slice(0, -SORT_BY_DESCENDING_SUFFIX.length);\n return { sortKey: SORT_BY_TO_SORT_KEY[base], reverse: true };\n }\n\n if (value.endsWith(SORT_BY_ASCENDING_SUFFIX)) {\n const base = value.slice(0, -SORT_BY_ASCENDING_SUFFIX.length);\n return { sortKey: SORT_BY_TO_SORT_KEY[base], reverse: false };\n }\n\n return { sortKey: SORT_BY_TO_SORT_KEY[value], reverse: false };\n}\n\n/**\n * Mirrors `parseProductFilters` from storefront-standard-events.\n *\n * Keep this local because Hydrogen only vendors Standard Events\n * declarations, so importing CollectionUpdateEvent would require runtime JS.\n */\nfunction parseProductFilters(searchParams: URLSearchParams): ProductFilter[] {\n const filters: ProductFilter[] = [];\n const price: ProductFilter[\"price\"] = {};\n const minPrice = searchParams.get(\"filter.v.price.gte\");\n const maxPrice = searchParams.get(\"filter.v.price.lte\");\n\n if (minPrice) price.min = parseLocalizedNumber(minPrice);\n if (maxPrice) price.max = parseLocalizedNumber(maxPrice);\n if (price.min !== undefined || price.max !== undefined) filters.push({ price });\n\n const availability = searchParams.getAll(\"filter.v.availability\");\n const inStock = availability.includes(\"1\");\n const outOfStock = availability.includes(\"0\");\n if (inStock && !outOfStock) filters.push({ available: true });\n if (outOfStock && !inStock) filters.push({ available: false });\n\n for (const productType of searchParams.getAll(\"filter.p.product_type\")) {\n filters.push({ productType });\n }\n\n for (const productVendor of searchParams.getAll(\"filter.p.vendor\")) {\n filters.push({ productVendor });\n }\n\n for (const tag of searchParams.getAll(\"filter.p.tag\")) {\n filters.push({ tag });\n }\n\n for (const [key, value] of searchParams) {\n let match: RegExpMatchArray | null;\n\n if ((match = key.match(/^filter\\.v\\.option\\.(.+)$/))) {\n filters.push({ variantOption: { name: decodeFilterKeyPart(match[1]), value } });\n continue;\n }\n\n if ((match = key.match(/^filter\\.p\\.m\\.([^.]+)\\.(.+)$/))) {\n filters.push({\n productMetafield: {\n namespace: decodeFilterKeyPart(match[1]),\n key: decodeFilterKeyPart(match[2]),\n value,\n },\n });\n continue;\n }\n\n if ((match = key.match(/^filter\\.v\\.m\\.([^.]+)\\.(.+)$/))) {\n filters.push({\n variantMetafield: {\n namespace: decodeFilterKeyPart(match[1]),\n key: decodeFilterKeyPart(match[2]),\n value,\n },\n });\n continue;\n }\n\n if ((match = key.match(/^filter\\.v\\.t\\.([^.]+)\\.(.+)$/))) {\n filters.push({\n taxonomyMetafield: {\n key: `${decodeFilterKeyPart(match[1])}.${decodeFilterKeyPart(match[2])}`,\n value,\n },\n });\n }\n }\n\n return filters;\n}\n\n/** Mirrors `getCollectionSortKey` from storefront-standard-events. */\nfunction parseCollectionSortKey(\n searchParams: URLSearchParams,\n): ProductCollectionSortKeys | undefined {\n const rawSortBy = searchParams.get(\"sort_by\");\n return rawSortBy ? STANDARD_EVENTS_COLLECTION_SORT_KEY[rawSortBy] : undefined;\n}\n\nfunction parseLocalizedNumber(value: string): number {\n try {\n const locale = typeof window !== \"undefined\" ? window.Shopify?.locale : undefined;\n const parts = new Intl.NumberFormat(locale ?? \"en\").formatToParts(1234.5);\n const group = parts.find((part) => part.type === \"group\")?.value ?? \",\";\n const decimal = parts.find((part) => part.type === \"decimal\")?.value ?? \".\";\n const normalized = value.split(group).join(\"\").replace(decimal, \".\");\n const number = Number(normalized);\n if (!Number.isNaN(number)) return number;\n } catch {}\n\n return Number(value.replace(/[^\\d.]/g, \"\")) || 0;\n}\n\nfunction decodeFilterKeyPart(value: string): string {\n return decodeURIComponent(value.replace(/\\+/g, \" \"));\n}\n\nfunction getFilterParamEntries(filter: ProductFilter): Array<{ key: string; value: string }> {\n const entries: Array<{ key: string; value: string }> = [];\n\n if (filter.available != null) {\n entries.push({ key: \"filter.v.availability\", value: filter.available ? \"1\" : \"0\" });\n }\n\n if (filter.price) {\n if (filter.price.min != null) {\n entries.push({ key: \"filter.v.price.gte\", value: String(filter.price.min) });\n }\n if (filter.price.max != null) {\n entries.push({ key: \"filter.v.price.lte\", value: String(filter.price.max) });\n }\n }\n\n if (filter.productType != null) {\n entries.push({ key: \"filter.p.product_type\", value: filter.productType });\n }\n\n if (filter.productVendor != null) {\n entries.push({ key: \"filter.p.vendor\", value: filter.productVendor });\n }\n\n if (filter.tag != null) {\n entries.push({ key: \"filter.p.tag\", value: filter.tag });\n }\n\n if (filter.variantOption) {\n entries.push({\n key: `filter.v.option.${encodeURIComponent(filter.variantOption.name)}`,\n value: filter.variantOption.value ?? \"\",\n });\n }\n\n if (filter.productMetafield) {\n const { namespace, key, value } = filter.productMetafield;\n entries.push({\n key: `filter.p.m.${encodeURIComponent(namespace)}.${encodeURIComponent(key)}`,\n value: value ?? \"\",\n });\n }\n\n if (filter.variantMetafield) {\n const { namespace, key, value } = filter.variantMetafield;\n entries.push({\n key: `filter.v.m.${encodeURIComponent(namespace)}.${encodeURIComponent(key)}`,\n value: value ?? \"\",\n });\n }\n\n if (filter.taxonomyMetafield) {\n const fullKey = filter.taxonomyMetafield.key;\n const dotIdx = fullKey.indexOf(\".\");\n const ns = dotIdx >= 0 ? fullKey.slice(0, dotIdx) : fullKey;\n const k = dotIdx >= 0 ? fullKey.slice(dotIdx + 1) : \"\";\n entries.push({\n key: `filter.v.t.${encodeURIComponent(ns)}.${encodeURIComponent(k)}`,\n value: filter.taxonomyMetafield.value,\n });\n }\n\n if (filter.category) {\n entries.push({ key: \"filter.p.category\", value: filter.category.id });\n }\n\n return entries;\n}\n\nfunction serializeFilter(params: URLSearchParams, filter: ProductFilter): void {\n for (const { key, value } of getFilterParamEntries(filter)) {\n params.append(key, value);\n }\n}\n\nfunction removeFilterParams(params: URLSearchParams, filter: ProductFilter): void {\n for (const { key, value } of getFilterParamEntries(filter)) {\n removeParamValue(params, key, value);\n }\n}\n\nfunction clearPaginationCursors(params: URLSearchParams): void {\n for (const key of PAGINATION_CURSOR_PARAMS) {\n params.delete(key);\n }\n}\n\n/** Compares two individual {@link ProductFilter} objects for semantic equality. */\nexport function filterEquals(a: ProductFilter, b: ProductFilter): boolean {\n const kindA = filterKind(a);\n const kindB = filterKind(b);\n if (kindA !== kindB || kindA == null) return false;\n\n switch (kindA) {\n case \"tag\":\n return a.tag === b.tag;\n case \"available\":\n return a.available === b.available;\n case \"price\":\n return priceRangeEquals(a.price, b.price);\n case \"productType\":\n return a.productType === b.productType;\n case \"productVendor\":\n return a.productVendor === b.productVendor;\n case \"variantOption\": {\n const aOpt = a.variantOption;\n const bOpt = b.variantOption;\n if (aOpt == null || bOpt == null) return false;\n return aOpt.name === bOpt.name && (aOpt.value ?? \"\") === (bOpt.value ?? \"\");\n }\n case \"productMetafield\": {\n const aMeta = a.productMetafield;\n const bMeta = b.productMetafield;\n if (aMeta == null || bMeta == null) return false;\n return metafieldEquals(aMeta, bMeta);\n }\n case \"variantMetafield\": {\n const aMeta = a.variantMetafield;\n const bMeta = b.variantMetafield;\n if (aMeta == null || bMeta == null) return false;\n return metafieldEquals(aMeta, bMeta);\n }\n case \"taxonomyMetafield\": {\n const aMeta = a.taxonomyMetafield;\n const bMeta = b.taxonomyMetafield;\n if (aMeta == null || bMeta == null) return false;\n return aMeta.key === bMeta.key && aMeta.value === bMeta.value;\n }\n case \"category\": {\n const aCat = a.category;\n const bCat = b.category;\n if (aCat == null || bCat == null) return false;\n return aCat.id === bCat.id;\n }\n }\n}\n\n/** Returns `true` when `input` (a Storefront API filter JSON string) matches an active filter. */\nexport function isFilterInputActive(activeFilters: ProductFilter[], input: string): boolean {\n let parsed: ProductFilter;\n try {\n parsed = JSON.parse(input) as ProductFilter;\n } catch {\n return false;\n }\n return activeFilters.some((filter) => filterEquals(filter, parsed));\n}\n\nfunction filterKind(\n filter: ProductFilter,\n):\n | \"tag\"\n | \"available\"\n | \"price\"\n | \"productType\"\n | \"productVendor\"\n | \"variantOption\"\n | \"productMetafield\"\n | \"variantMetafield\"\n | \"taxonomyMetafield\"\n | \"category\"\n | null {\n if (filter.tag != null) return \"tag\";\n if (filter.available != null) return \"available\";\n if (filter.price != null) return \"price\";\n if (filter.productType != null) return \"productType\";\n if (filter.productVendor != null) return \"productVendor\";\n if (filter.variantOption != null) return \"variantOption\";\n if (filter.productMetafield != null) return \"productMetafield\";\n if (filter.variantMetafield != null) return \"variantMetafield\";\n if (filter.taxonomyMetafield != null) return \"taxonomyMetafield\";\n if (filter.category != null) return \"category\";\n return null;\n}\n\nfunction priceRangeEquals(a: ProductFilter[\"price\"], b: ProductFilter[\"price\"]): boolean {\n if (a == null || b == null) return a === b;\n return a.min === b.min && a.max === b.max;\n}\n\nfunction metafieldEquals(\n a: { namespace: string; key: string; value?: string },\n b: { namespace: string; key: string; value?: string },\n): boolean {\n return a.namespace === b.namespace && a.key === b.key && (a.value ?? \"\") === (b.value ?? \"\");\n}\n\nfunction filtersEqual(a: ProductFilter[], b: ProductFilter[]): boolean {\n if (a.length !== b.length) return false;\n\n const remaining = [...b];\n for (const filter of a) {\n const index = remaining.findIndex((candidate) => filterEquals(filter, candidate));\n if (index === -1) return false;\n remaining.splice(index, 1);\n }\n\n return true;\n}\n\nfunction removeParamValue(params: URLSearchParams, name: string, value: string): void {\n const remaining = params.getAll(name).filter((v) => v !== value);\n params.delete(name);\n for (const v of remaining) {\n params.append(name, v);\n }\n}\n"],"mappings":";AAiBA,MAAM,4BAA4B;AAClC,MAAM,2BAA2B;AACjC,MAAM,2BAA8C,CAAC,UAAU,QAAQ;AAEvE,MAAM,sBAA8C;CAClD,cAAc;CACd,SAAS;CACT,OAAO;CACP,OAAO;CACP,QAAQ;CACR,IAAI;CACJ,WAAW;CACX,oBAAoB;CACrB;AAED,MAAM,sBAAiE,OAAO,YAC5E,OAAO,QAAQ,oBAAoB,CAAC,KAAK,CAAC,KAAK,WAAW,CACxD,OACA,IACD,CAAC,CACH;AAED,MAAM,sCAAiF;CACrF,gBAAgB;CAChB,qBAAqB;CACrB,sBAAsB;CACtB,mBAAmB;CACnB,oBAAoB;CACpB,mBAAmB;CACnB,oBAAoB;CACpB,QAAQ;CACT;;;;;;;;;AAUD,SAAgB,sBAAsB,cAAiD;AAMrF,QAAO;EAAE,SALO,oBAAoB,aAKpB;EAAE,SAJF,uBAAuB,aAId;EAAE,SAHT,aAAa,IAAI,UACV,EAAE,SAAS,0BAA0B,IAAI;EAE9B;;;;;;;;;;AAWtC,SAAgB,0BACd,OACiB;CACjB,MAAM,SAAS,IAAI,iBAAiB;AAEpC,MAAK,MAAM,UAAU,MAAM,QACzB,iBAAgB,QAAQ,OAAO;AAGjC,KAAI,MAAM,QACR,QAAO,IAAI,WAAW,eAAe,MAAM,SAAS,MAAM,QAAQ,CAAC;AAGrE,QAAO;;;AAIT,SAAS,kBAAkB,KAAsB;AAC/C,QAAO,QAAQ,aAAa,IAAI,WAAW,UAAU;;;AAIvD,SAAgB,0BAA0B,QAAwB;AAChE,QAAO,OAAO,WAAW,IAAI,GAAG,OAAO,MAAM,EAAE,GAAG;;;;;;;;AASpD,SAAgB,sBACd,UACA,OACiB;CACjB,MAAM,SAAS,IAAI,gBAAgB,SAAS;AAE5C,MAAK,MAAM,OAAO,MAAM,KAAK,OAAO,MAAM,CAAC,CACzC,KAAI,kBAAkB,IAAI,CACxB,QAAO,OAAO,IAAI;AAItB,wBAAuB,OAAO;AAE9B,MAAK,MAAM,CAAC,KAAK,UAAU,0BAA0B,MAAM,CACzD,QAAO,OAAO,KAAK,MAAM;AAG3B,QAAO;;;AAIT,SAAgB,sBAAsB,GAAW,GAAoB;CACnE,MAAM,UAAU,sBAAsB,IAAI,gBAAgB,0BAA0B,EAAE,CAAC,CAAC;CACxF,MAAM,UAAU,sBAAsB,IAAI,gBAAgB,0BAA0B,EAAE,CAAC,CAAC;AAExF,QACE,QAAQ,YAAY,QAAQ,WAC5B,QAAQ,YAAY,QAAQ,WAC5B,aAAa,QAAQ,SAAS,QAAQ,QAAQ;;;AAKlD,SAAgB,2BACd,cACA,OACS;CACT,MAAM,SAAS,sBAAsB,aAAa;AAElD,QACE,OAAO,YAAY,MAAM,WACzB,OAAO,YAAY,MAAM,WACzB,aAAa,OAAO,SAAS,MAAM,QAAQ;;;;;;;;;AAW/C,SAAgB,oBAAoB,eAAgC,QAA+B;CACjG,MAAM,SAAS,IAAI,gBAAgB,cAAc;AACjD,oBAAmB,QAAQ,OAAO;AAClC,wBAAuB,OAAO;CAC9B,MAAM,aAAa,OAAO,UAAU;AACpC,QAAO,aAAa,IAAI,eAAe;;;AAIzC,SAAgB,qBAAqB,SAA6C;AAChF,QAAO,YAAY,WAAW,YAAY,WAAW,YAAY,aAAa,YAAY;;;;;;;;;;;;;AAc5F,SAAgB,eAAe,SAAoC,SAA0B;CAC3F,MAAM,OAAO,oBAAoB,YAAY,QAAQ,aAAa;AAElE,KAAI,CAAC,qBAAqB,QAAQ,CAAE,QAAO;AAE3C,QAAO,UAAU,GAAG,KAAK,eAAe,GAAG,KAAK;;;;;;;;AASlD,SAAgB,iBAAiB,OAG/B;AACA,KAAI,MAAM,SAAS,0BAA0B,CAE3C,QAAO;EAAE,SAAS,oBADL,MAAM,MAAM,GAAG,IACc;EAAG,SAAS;EAAM;AAG9D,KAAI,MAAM,SAAS,yBAAyB,CAE1C,QAAO;EAAE,SAAS,oBADL,MAAM,MAAM,GAAG,IACc;EAAG,SAAS;EAAO;AAG/D,QAAO;EAAE,SAAS,oBAAoB;EAAQ,SAAS;EAAO;;;;;;;;AAShE,SAAS,oBAAoB,cAAgD;CAC3E,MAAM,UAA2B,EAAE;CACnC,MAAM,QAAgC,EAAE;CACxC,MAAM,WAAW,aAAa,IAAI,qBAAqB;CACvD,MAAM,WAAW,aAAa,IAAI,qBAAqB;AAEvD,KAAI,SAAU,OAAM,MAAM,qBAAqB,SAAS;AACxD,KAAI,SAAU,OAAM,MAAM,qBAAqB,SAAS;AACxD,KAAI,MAAM,QAAQ,UAAa,MAAM,QAAQ,OAAW,SAAQ,KAAK,EAAE,OAAO,CAAC;CAE/E,MAAM,eAAe,aAAa,OAAO,wBAAwB;CACjE,MAAM,UAAU,aAAa,SAAS,IAAI;CAC1C,MAAM,aAAa,aAAa,SAAS,IAAI;AAC7C,KAAI,WAAW,CAAC,WAAY,SAAQ,KAAK,EAAE,WAAW,MAAM,CAAC;AAC7D,KAAI,cAAc,CAAC,QAAS,SAAQ,KAAK,EAAE,WAAW,OAAO,CAAC;AAE9D,MAAK,MAAM,eAAe,aAAa,OAAO,wBAAwB,CACpE,SAAQ,KAAK,EAAE,aAAa,CAAC;AAG/B,MAAK,MAAM,iBAAiB,aAAa,OAAO,kBAAkB,CAChE,SAAQ,KAAK,EAAE,eAAe,CAAC;AAGjC,MAAK,MAAM,OAAO,aAAa,OAAO,eAAe,CACnD,SAAQ,KAAK,EAAE,KAAK,CAAC;AAGvB,MAAK,MAAM,CAAC,KAAK,UAAU,cAAc;EACvC,IAAI;AAEJ,MAAK,QAAQ,IAAI,MAAM,4BAA4B,EAAG;AACpD,WAAQ,KAAK,EAAE,eAAe;IAAE,MAAM,oBAAoB,MAAM,GAAG;IAAE;IAAO,EAAE,CAAC;AAC/E;;AAGF,MAAK,QAAQ,IAAI,MAAM,gCAAgC,EAAG;AACxD,WAAQ,KAAK,EACX,kBAAkB;IAChB,WAAW,oBAAoB,MAAM,GAAG;IACxC,KAAK,oBAAoB,MAAM,GAAG;IAClC;IACD,EACF,CAAC;AACF;;AAGF,MAAK,QAAQ,IAAI,MAAM,gCAAgC,EAAG;AACxD,WAAQ,KAAK,EACX,kBAAkB;IAChB,WAAW,oBAAoB,MAAM,GAAG;IACxC,KAAK,oBAAoB,MAAM,GAAG;IAClC;IACD,EACF,CAAC;AACF;;AAGF,MAAK,QAAQ,IAAI,MAAM,gCAAgC,CACrD,SAAQ,KAAK,EACX,mBAAmB;GACjB,KAAK,GAAG,oBAAoB,MAAM,GAAG,CAAC,GAAG,oBAAoB,MAAM,GAAG;GACtE;GACD,EACF,CAAC;;AAIN,QAAO;;;AAIT,SAAS,uBACP,cACuC;CACvC,MAAM,YAAY,aAAa,IAAI,UAAU;AAC7C,QAAO,YAAY,oCAAoC,aAAa;;AAGtE,SAAS,qBAAqB,OAAuB;AACnD,KAAI;EACF,MAAM,SAAS,OAAO,WAAW,cAAc,OAAO,SAAS,SAAS;EACxE,MAAM,QAAQ,IAAI,KAAK,aAAa,UAAU,KAAK,CAAC,cAAc,OAAO;EACzE,MAAM,QAAQ,MAAM,MAAM,SAAS,KAAK,SAAS,QAAQ,EAAE,SAAS;EACpE,MAAM,UAAU,MAAM,MAAM,SAAS,KAAK,SAAS,UAAU,EAAE,SAAS;EACxE,MAAM,aAAa,MAAM,MAAM,MAAM,CAAC,KAAK,GAAG,CAAC,QAAQ,SAAS,IAAI;EACpE,MAAM,SAAS,OAAO,WAAW;AACjC,MAAI,CAAC,OAAO,MAAM,OAAO,CAAE,QAAO;SAC5B;AAER,QAAO,OAAO,MAAM,QAAQ,WAAW,GAAG,CAAC,IAAI;;AAGjD,SAAS,oBAAoB,OAAuB;AAClD,QAAO,mBAAmB,MAAM,QAAQ,OAAO,IAAI,CAAC;;AAGtD,SAAS,sBAAsB,QAA8D;CAC3F,MAAM,UAAiD,EAAE;AAEzD,KAAI,OAAO,aAAa,KACtB,SAAQ,KAAK;EAAE,KAAK;EAAyB,OAAO,OAAO,YAAY,MAAM;EAAK,CAAC;AAGrF,KAAI,OAAO,OAAO;AAChB,MAAI,OAAO,MAAM,OAAO,KACtB,SAAQ,KAAK;GAAE,KAAK;GAAsB,OAAO,OAAO,OAAO,MAAM,IAAI;GAAE,CAAC;AAE9E,MAAI,OAAO,MAAM,OAAO,KACtB,SAAQ,KAAK;GAAE,KAAK;GAAsB,OAAO,OAAO,OAAO,MAAM,IAAI;GAAE,CAAC;;AAIhF,KAAI,OAAO,eAAe,KACxB,SAAQ,KAAK;EAAE,KAAK;EAAyB,OAAO,OAAO;EAAa,CAAC;AAG3E,KAAI,OAAO,iBAAiB,KAC1B,SAAQ,KAAK;EAAE,KAAK;EAAmB,OAAO,OAAO;EAAe,CAAC;AAGvE,KAAI,OAAO,OAAO,KAChB,SAAQ,KAAK;EAAE,KAAK;EAAgB,OAAO,OAAO;EAAK,CAAC;AAG1D,KAAI,OAAO,cACT,SAAQ,KAAK;EACX,KAAK,mBAAmB,mBAAmB,OAAO,cAAc,KAAK;EACrE,OAAO,OAAO,cAAc,SAAS;EACtC,CAAC;AAGJ,KAAI,OAAO,kBAAkB;EAC3B,MAAM,EAAE,WAAW,KAAK,UAAU,OAAO;AACzC,UAAQ,KAAK;GACX,KAAK,cAAc,mBAAmB,UAAU,CAAC,GAAG,mBAAmB,IAAI;GAC3E,OAAO,SAAS;GACjB,CAAC;;AAGJ,KAAI,OAAO,kBAAkB;EAC3B,MAAM,EAAE,WAAW,KAAK,UAAU,OAAO;AACzC,UAAQ,KAAK;GACX,KAAK,cAAc,mBAAmB,UAAU,CAAC,GAAG,mBAAmB,IAAI;GAC3E,OAAO,SAAS;GACjB,CAAC;;AAGJ,KAAI,OAAO,mBAAmB;EAC5B,MAAM,UAAU,OAAO,kBAAkB;EACzC,MAAM,SAAS,QAAQ,QAAQ,IAAI;EACnC,MAAM,KAAK,UAAU,IAAI,QAAQ,MAAM,GAAG,OAAO,GAAG;EACpD,MAAM,IAAI,UAAU,IAAI,QAAQ,MAAM,SAAS,EAAE,GAAG;AACpD,UAAQ,KAAK;GACX,KAAK,cAAc,mBAAmB,GAAG,CAAC,GAAG,mBAAmB,EAAE;GAClE,OAAO,OAAO,kBAAkB;GACjC,CAAC;;AAGJ,KAAI,OAAO,SACT,SAAQ,KAAK;EAAE,KAAK;EAAqB,OAAO,OAAO,SAAS;EAAI,CAAC;AAGvE,QAAO;;AAGT,SAAS,gBAAgB,QAAyB,QAA6B;AAC7E,MAAK,MAAM,EAAE,KAAK,WAAW,sBAAsB,OAAO,CACxD,QAAO,OAAO,KAAK,MAAM;;AAI7B,SAAS,mBAAmB,QAAyB,QAA6B;AAChF,MAAK,MAAM,EAAE,KAAK,WAAW,sBAAsB,OAAO,CACxD,kBAAiB,QAAQ,KAAK,MAAM;;AAIxC,SAAS,uBAAuB,QAA+B;AAC7D,MAAK,MAAM,OAAO,yBAChB,QAAO,OAAO,IAAI;;;AAKtB,SAAgB,aAAa,GAAkB,GAA2B;CACxE,MAAM,QAAQ,WAAW,EAAE;AAE3B,KAAI,UADU,WAAW,EACN,IAAI,SAAS,KAAM,QAAO;AAE7C,SAAQ,OAAR;EACE,KAAK,MACH,QAAO,EAAE,QAAQ,EAAE;EACrB,KAAK,YACH,QAAO,EAAE,cAAc,EAAE;EAC3B,KAAK,QACH,QAAO,iBAAiB,EAAE,OAAO,EAAE,MAAM;EAC3C,KAAK,cACH,QAAO,EAAE,gBAAgB,EAAE;EAC7B,KAAK,gBACH,QAAO,EAAE,kBAAkB,EAAE;EAC/B,KAAK,iBAAiB;GACpB,MAAM,OAAO,EAAE;GACf,MAAM,OAAO,EAAE;AACf,OAAI,QAAQ,QAAQ,QAAQ,KAAM,QAAO;AACzC,UAAO,KAAK,SAAS,KAAK,SAAS,KAAK,SAAS,SAAS,KAAK,SAAS;;EAE1E,KAAK,oBAAoB;GACvB,MAAM,QAAQ,EAAE;GAChB,MAAM,QAAQ,EAAE;AAChB,OAAI,SAAS,QAAQ,SAAS,KAAM,QAAO;AAC3C,UAAO,gBAAgB,OAAO,MAAM;;EAEtC,KAAK,oBAAoB;GACvB,MAAM,QAAQ,EAAE;GAChB,MAAM,QAAQ,EAAE;AAChB,OAAI,SAAS,QAAQ,SAAS,KAAM,QAAO;AAC3C,UAAO,gBAAgB,OAAO,MAAM;;EAEtC,KAAK,qBAAqB;GACxB,MAAM,QAAQ,EAAE;GAChB,MAAM,QAAQ,EAAE;AAChB,OAAI,SAAS,QAAQ,SAAS,KAAM,QAAO;AAC3C,UAAO,MAAM,QAAQ,MAAM,OAAO,MAAM,UAAU,MAAM;;EAE1D,KAAK,YAAY;GACf,MAAM,OAAO,EAAE;GACf,MAAM,OAAO,EAAE;AACf,OAAI,QAAQ,QAAQ,QAAQ,KAAM,QAAO;AACzC,UAAO,KAAK,OAAO,KAAK;;;;;AAM9B,SAAgB,oBAAoB,eAAgC,OAAwB;CAC1F,IAAI;AACJ,KAAI;AACF,WAAS,KAAK,MAAM,MAAM;SACpB;AACN,SAAO;;AAET,QAAO,cAAc,MAAM,WAAW,aAAa,QAAQ,OAAO,CAAC;;AAGrE,SAAS,WACP,QAYO;AACP,KAAI,OAAO,OAAO,KAAM,QAAO;AAC/B,KAAI,OAAO,aAAa,KAAM,QAAO;AACrC,KAAI,OAAO,SAAS,KAAM,QAAO;AACjC,KAAI,OAAO,eAAe,KAAM,QAAO;AACvC,KAAI,OAAO,iBAAiB,KAAM,QAAO;AACzC,KAAI,OAAO,iBAAiB,KAAM,QAAO;AACzC,KAAI,OAAO,oBAAoB,KAAM,QAAO;AAC5C,KAAI,OAAO,oBAAoB,KAAM,QAAO;AAC5C,KAAI,OAAO,qBAAqB,KAAM,QAAO;AAC7C,KAAI,OAAO,YAAY,KAAM,QAAO;AACpC,QAAO;;AAGT,SAAS,iBAAiB,GAA2B,GAAoC;AACvF,KAAI,KAAK,QAAQ,KAAK,KAAM,QAAO,MAAM;AACzC,QAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,QAAQ,EAAE;;AAGxC,SAAS,gBACP,GACA,GACS;AACT,QAAO,EAAE,cAAc,EAAE,aAAa,EAAE,QAAQ,EAAE,QAAQ,EAAE,SAAS,SAAS,EAAE,SAAS;;AAG3F,SAAS,aAAa,GAAoB,GAA6B;AACrE,KAAI,EAAE,WAAW,EAAE,OAAQ,QAAO;CAElC,MAAM,YAAY,CAAC,GAAG,EAAE;AACxB,MAAK,MAAM,UAAU,GAAG;EACtB,MAAM,QAAQ,UAAU,WAAW,cAAc,aAAa,QAAQ,UAAU,CAAC;AACjF,MAAI,UAAU,GAAI,QAAO;AACzB,YAAU,OAAO,OAAO,EAAE;;AAG5B,QAAO;;AAGT,SAAS,iBAAiB,QAAyB,MAAc,OAAqB;CACpF,MAAM,YAAY,OAAO,OAAO,KAAK,CAAC,QAAQ,MAAM,MAAM,MAAM;AAChE,QAAO,OAAO,KAAK;AACnB,MAAK,MAAM,KAAK,UACd,QAAO,OAAO,MAAM,EAAE"}
|
|
1
|
+
{"version":3,"file":"url.mjs","names":[],"sources":["../../../src/core/collection/url.ts"],"sourcesContent":["import type { ProductFilter } from \"../../../vendor/standard-events\";\nimport type { ProductCollectionSortKeys } from \"../../graphql/generated/storefront-api-types\";\nimport type { CollectionState } from \"./state\";\n\n/**\n * The filters, sort key, and sort direction in a collection URL.\n */\nexport interface CollectionParams {\n /**\n * Active product filters from the `filter.*` URL params.\n *\n * The filters use the Storefront Standard Events filter type. When your query variables use generated Storefront API types, cast the filters to the generated filter type where you pass them as variables.\n */\n filters: ProductFilter[];\n /** Storefront API sort key from the `sort_by` param, or `undefined` when the param is missing or unrecognized. */\n sortKey: ProductCollectionSortKeys | undefined;\n /** `true` when the `sort_by` value ends with `-descending`. */\n reverse: boolean;\n}\n\nconst SORT_BY_DESCENDING_SUFFIX = \"-descending\";\nconst SORT_BY_ASCENDING_SUFFIX = \"-ascending\";\nconst PAGINATION_CURSOR_PARAMS: readonly string[] = [\"before\", \"after\"];\n\nconst SORT_KEY_TO_SORT_BY: Record<string, string> = {\n BEST_SELLING: \"best-selling\",\n CREATED: \"created\",\n PRICE: \"price\",\n TITLE: \"title\",\n MANUAL: \"manual\",\n ID: \"id\",\n RELEVANCE: \"relevance\",\n COLLECTION_DEFAULT: \"collection-default\",\n};\n\nconst SORT_BY_TO_SORT_KEY: Record<string, ProductCollectionSortKeys> = Object.fromEntries(\n Object.entries(SORT_KEY_TO_SORT_BY).map(([key, value]) => [\n value,\n key as ProductCollectionSortKeys,\n ]),\n);\n\nconst STANDARD_EVENTS_COLLECTION_SORT_KEY: Record<string, ProductCollectionSortKeys> = {\n \"best-selling\": \"BEST_SELLING\",\n \"created-ascending\": \"CREATED\",\n \"created-descending\": \"CREATED\",\n \"price-ascending\": \"PRICE\",\n \"price-descending\": \"PRICE\",\n \"title-ascending\": \"TITLE\",\n \"title-descending\": \"TITLE\",\n manual: \"MANUAL\",\n};\n\n/**\n * Reads collection filters and sort from URL search params. Pass the result to your Storefront API collection query.\n *\n * The function reads Liquid-compatible `filter.p.*` and `filter.v.*` keys and the `sort_by` param.\n *\n * A URL that sets availability to both in stock and out of stock produces no availability filter. The function recognizes the best-selling and manual sort values, plus created, price, and title with an ascending or descending suffix. For other sort values, the sort key is `undefined`. The `parseSortByValue` function also recognizes relevance, collection default, and ID.\n *\n * @param searchParams - URL search params to read.\n * @returns The filters, sort key, and sort direction from the URL.\n * @publicDocs\n */\nexport function parseCollectionParams(searchParams: URLSearchParams): CollectionParams {\n const filters = parseProductFilters(searchParams);\n const sortKey = parseCollectionSortKey(searchParams);\n const rawSortBy = searchParams.get(\"sort_by\");\n const reverse = rawSortBy?.endsWith(SORT_BY_DESCENDING_SUFFIX) ?? false;\n\n return { filters, sortKey, reverse };\n}\n\n/**\n * Converts collection filters and sort to URL search params with Liquid-compatible keys. Use the result to build collection URLs.\n *\n * The result holds only the `filter.*` and `sort_by` keys. The function leaves out `sort_by` for the collection's default sort.\n *\n * Category filters convert to `filter.p.category`, which `parseCollectionParams` doesn't read. A price filter with a minimum and a maximum produces two params. To build a filter checkbox, parse a filter value's `input` string and convert the parsed filter. The resulting entries give the checkbox name and value.\n *\n * @param state - The filters, sort key, and sort direction to convert.\n * @returns URL search params that hold the filters and sort.\n * @publicDocs\n */\nexport function serializeCollectionParams(\n state: Pick<CollectionState, \"filters\" | \"sortKey\" | \"reverse\">,\n): URLSearchParams {\n const params = new URLSearchParams();\n\n for (const filter of state.filters) {\n serializeFilter(params, filter);\n }\n\n if (state.sortKey) {\n params.set(\"sort_by\", getSortByValue(state.sortKey, state.reverse));\n }\n\n return params;\n}\n\n/** Returns `true` when the param key is owned by the collection store. */\nfunction isStoreOwnedParam(key: string): boolean {\n return key === \"sort_by\" || key.startsWith(\"filter.\");\n}\n\n/**\n * Removes a leading `?` from a search string. Normalize search strings before you compare them.\n *\n * @param search The search string from your router, with or without a leading `?`.\n * @returns The search string without a leading `?`.\n * @publicDocs\n */\nexport function normalizeCollectionSearch(search: string): string {\n return search.startsWith(\"?\") ? search.slice(1) : search;\n}\n\n/**\n * Merges store-owned params from `state` into `existing`, preserving\n * unrelated keys (e.g. `grid`, `view`) already present in the URL. Collection\n * pagination cursors (`before`, `after`) are cleared so changed browse intent\n * starts from the first page.\n */\nexport function mergeCollectionParams(\n existing: URLSearchParams,\n state: Pick<CollectionState, \"filters\" | \"sortKey\" | \"reverse\">,\n): URLSearchParams {\n const merged = new URLSearchParams(existing);\n\n for (const key of Array.from(merged.keys())) {\n if (isStoreOwnedParam(key)) {\n merged.delete(key);\n }\n }\n\n clearPaginationCursors(merged);\n\n for (const [key, value] of serializeCollectionParams(state)) {\n merged.append(key, value);\n }\n\n return merged;\n}\n\n/** Returns `true` when two router search strings describe the same collection filters and sort. */\nexport function collectionSearchEqual(a: string, b: string): boolean {\n const parsedA = parseCollectionParams(new URLSearchParams(normalizeCollectionSearch(a)));\n const parsedB = parseCollectionParams(new URLSearchParams(normalizeCollectionSearch(b)));\n\n return (\n parsedA.sortKey === parsedB.sortKey &&\n parsedA.reverse === parsedB.reverse &&\n filtersEqual(parsedA.filters, parsedB.filters)\n );\n}\n\n/** Returns `true` when URL params and collection browse state describe the same filters and sort. */\nexport function collectionParamsMatchState(\n searchParams: URLSearchParams,\n state: Pick<CollectionState, \"filters\" | \"sortKey\" | \"reverse\">,\n): boolean {\n const parsed = parseCollectionParams(searchParams);\n\n return (\n parsed.sortKey === state.sortKey &&\n parsed.reverse === state.reverse &&\n filtersEqual(parsed.filters, state.filters)\n );\n}\n\n/**\n * Builds a query string from the current URL params, minus the given filter. Use the string in plain remove-filter links.\n *\n * The function keeps every other param and drops the `before` and `after` pagination cursors, which sends the customer to the first page of results.\n *\n * @param currentParams The current URL search params.\n * @param filter The filter to remove from the URL.\n * @returns A query string that starts with `?`. The string is `?` alone when no params remain.\n * @publicDocs\n */\nexport function getFilterRemovalUrl(currentParams: URLSearchParams, filter: ProductFilter): string {\n const result = new URLSearchParams(currentParams);\n removeFilterParams(result, filter);\n clearPaginationCursors(result);\n const serialized = result.toString();\n return serialized ? `?${serialized}` : \"?\";\n}\n\n/** Returns `true` when the sort key supports ascending/descending direction suffixes. */\nexport function isDirectionalSortKey(sortKey: ProductCollectionSortKeys): boolean {\n return sortKey === \"PRICE\" || sortKey === \"TITLE\" || sortKey === \"CREATED\" || sortKey === \"ID\";\n}\n\n/**\n * Converts a Storefront API sort key and direction to a Liquid-compatible\n * `sort_by` value, such as `price-ascending` or `best-selling`.\n *\n * Only the price, title, created, and ID sort keys get a direction suffix.\n * Other sort keys, such as best selling, ignore the direction.\n *\n * @param sortKey - Storefront API product collection sort key.\n * @param reverse - `true` for descending, `false` for ascending.\n * @returns The value for the `sort_by` URL param.\n * @publicDocs\n */\nexport function getSortByValue(sortKey: ProductCollectionSortKeys, reverse: boolean): string {\n const base = SORT_KEY_TO_SORT_BY[sortKey] ?? sortKey.toLowerCase();\n\n if (!isDirectionalSortKey(sortKey)) return base;\n\n return reverse ? `${base}-descending` : `${base}-ascending`;\n}\n\n/**\n * Converts a Liquid-compatible `sort_by` value to a Storefront API\n * sort key and direction. The `getSortByValue` function does the opposite conversion.\n *\n * For unrecognized values, the sort key is `undefined`.\n *\n * @param value - A `sort_by` value, such as `price-ascending` or `best-selling`.\n * @returns The sort key and `reverse`, which reads `true` for descending values.\n * @publicDocs\n */\nexport function parseSortByValue(value: string): {\n sortKey: ProductCollectionSortKeys | undefined;\n reverse: boolean;\n} {\n if (value.endsWith(SORT_BY_DESCENDING_SUFFIX)) {\n const base = value.slice(0, -SORT_BY_DESCENDING_SUFFIX.length);\n return { sortKey: SORT_BY_TO_SORT_KEY[base], reverse: true };\n }\n\n if (value.endsWith(SORT_BY_ASCENDING_SUFFIX)) {\n const base = value.slice(0, -SORT_BY_ASCENDING_SUFFIX.length);\n return { sortKey: SORT_BY_TO_SORT_KEY[base], reverse: false };\n }\n\n return { sortKey: SORT_BY_TO_SORT_KEY[value], reverse: false };\n}\n\n/**\n * Mirrors `parseProductFilters` from storefront-standard-events.\n *\n * Keep this local because Hydrogen only vendors Standard Events\n * declarations, so importing CollectionUpdateEvent would require runtime JS.\n */\nfunction parseProductFilters(searchParams: URLSearchParams): ProductFilter[] {\n const filters: ProductFilter[] = [];\n const price: ProductFilter[\"price\"] = {};\n const minPrice = searchParams.get(\"filter.v.price.gte\");\n const maxPrice = searchParams.get(\"filter.v.price.lte\");\n\n if (minPrice) price.min = parseLocalizedNumber(minPrice);\n if (maxPrice) price.max = parseLocalizedNumber(maxPrice);\n if (price.min !== undefined || price.max !== undefined) filters.push({ price });\n\n const availability = searchParams.getAll(\"filter.v.availability\");\n const inStock = availability.includes(\"1\");\n const outOfStock = availability.includes(\"0\");\n if (inStock && !outOfStock) filters.push({ available: true });\n if (outOfStock && !inStock) filters.push({ available: false });\n\n for (const productType of searchParams.getAll(\"filter.p.product_type\")) {\n filters.push({ productType });\n }\n\n for (const productVendor of searchParams.getAll(\"filter.p.vendor\")) {\n filters.push({ productVendor });\n }\n\n for (const tag of searchParams.getAll(\"filter.p.tag\")) {\n filters.push({ tag });\n }\n\n for (const [key, value] of searchParams) {\n let match: RegExpMatchArray | null;\n\n if ((match = key.match(/^filter\\.v\\.option\\.(.+)$/))) {\n filters.push({ variantOption: { name: decodeFilterKeyPart(match[1]), value } });\n continue;\n }\n\n if ((match = key.match(/^filter\\.p\\.m\\.([^.]+)\\.(.+)$/))) {\n filters.push({\n productMetafield: {\n namespace: decodeFilterKeyPart(match[1]),\n key: decodeFilterKeyPart(match[2]),\n value,\n },\n });\n continue;\n }\n\n if ((match = key.match(/^filter\\.v\\.m\\.([^.]+)\\.(.+)$/))) {\n filters.push({\n variantMetafield: {\n namespace: decodeFilterKeyPart(match[1]),\n key: decodeFilterKeyPart(match[2]),\n value,\n },\n });\n continue;\n }\n\n if ((match = key.match(/^filter\\.v\\.t\\.([^.]+)\\.(.+)$/))) {\n filters.push({\n taxonomyMetafield: {\n key: `${decodeFilterKeyPart(match[1])}.${decodeFilterKeyPart(match[2])}`,\n value,\n },\n });\n }\n }\n\n return filters;\n}\n\n/** Mirrors `getCollectionSortKey` from storefront-standard-events. */\nfunction parseCollectionSortKey(\n searchParams: URLSearchParams,\n): ProductCollectionSortKeys | undefined {\n const rawSortBy = searchParams.get(\"sort_by\");\n return rawSortBy ? STANDARD_EVENTS_COLLECTION_SORT_KEY[rawSortBy] : undefined;\n}\n\nfunction parseLocalizedNumber(value: string): number {\n try {\n const locale = typeof window !== \"undefined\" ? window.Shopify?.locale : undefined;\n const parts = new Intl.NumberFormat(locale ?? \"en\").formatToParts(1234.5);\n const group = parts.find((part) => part.type === \"group\")?.value ?? \",\";\n const decimal = parts.find((part) => part.type === \"decimal\")?.value ?? \".\";\n const normalized = value.split(group).join(\"\").replace(decimal, \".\");\n const number = Number(normalized);\n if (!Number.isNaN(number)) return number;\n } catch {}\n\n return Number(value.replace(/[^\\d.]/g, \"\")) || 0;\n}\n\nfunction decodeFilterKeyPart(value: string): string {\n return decodeURIComponent(value.replace(/\\+/g, \" \"));\n}\n\nfunction getFilterParamEntries(filter: ProductFilter): Array<{ key: string; value: string }> {\n const entries: Array<{ key: string; value: string }> = [];\n\n if (filter.available != null) {\n entries.push({ key: \"filter.v.availability\", value: filter.available ? \"1\" : \"0\" });\n }\n\n if (filter.price) {\n if (filter.price.min != null) {\n entries.push({ key: \"filter.v.price.gte\", value: String(filter.price.min) });\n }\n if (filter.price.max != null) {\n entries.push({ key: \"filter.v.price.lte\", value: String(filter.price.max) });\n }\n }\n\n if (filter.productType != null) {\n entries.push({ key: \"filter.p.product_type\", value: filter.productType });\n }\n\n if (filter.productVendor != null) {\n entries.push({ key: \"filter.p.vendor\", value: filter.productVendor });\n }\n\n if (filter.tag != null) {\n entries.push({ key: \"filter.p.tag\", value: filter.tag });\n }\n\n if (filter.variantOption) {\n entries.push({\n key: `filter.v.option.${encodeURIComponent(filter.variantOption.name)}`,\n value: filter.variantOption.value ?? \"\",\n });\n }\n\n if (filter.productMetafield) {\n const { namespace, key, value } = filter.productMetafield;\n entries.push({\n key: `filter.p.m.${encodeURIComponent(namespace)}.${encodeURIComponent(key)}`,\n value: value ?? \"\",\n });\n }\n\n if (filter.variantMetafield) {\n const { namespace, key, value } = filter.variantMetafield;\n entries.push({\n key: `filter.v.m.${encodeURIComponent(namespace)}.${encodeURIComponent(key)}`,\n value: value ?? \"\",\n });\n }\n\n if (filter.taxonomyMetafield) {\n const fullKey = filter.taxonomyMetafield.key;\n const dotIdx = fullKey.indexOf(\".\");\n const ns = dotIdx >= 0 ? fullKey.slice(0, dotIdx) : fullKey;\n const k = dotIdx >= 0 ? fullKey.slice(dotIdx + 1) : \"\";\n entries.push({\n key: `filter.v.t.${encodeURIComponent(ns)}.${encodeURIComponent(k)}`,\n value: filter.taxonomyMetafield.value,\n });\n }\n\n if (filter.category) {\n entries.push({ key: \"filter.p.category\", value: filter.category.id });\n }\n\n return entries;\n}\n\nfunction serializeFilter(params: URLSearchParams, filter: ProductFilter): void {\n for (const { key, value } of getFilterParamEntries(filter)) {\n params.append(key, value);\n }\n}\n\nfunction removeFilterParams(params: URLSearchParams, filter: ProductFilter): void {\n for (const { key, value } of getFilterParamEntries(filter)) {\n removeParamValue(params, key, value);\n }\n}\n\nfunction clearPaginationCursors(params: URLSearchParams): void {\n for (const key of PAGINATION_CURSOR_PARAMS) {\n params.delete(key);\n }\n}\n\n/**\n * Checks whether two product filters match.\n *\n * Price filters match when the minimum and maximum are equal, including ranges that set only one bound.\n *\n * @param a The first filter to compare.\n * @param b The second filter to compare.\n * @returns `true` when both filters are the same kind with the same values. `false` for different kinds or unrecognized filters.\n * @publicDocs\n */\nexport function filterEquals(a: ProductFilter, b: ProductFilter): boolean {\n const kindA = filterKind(a);\n const kindB = filterKind(b);\n if (kindA !== kindB || kindA == null) return false;\n\n switch (kindA) {\n case \"tag\":\n return a.tag === b.tag;\n case \"available\":\n return a.available === b.available;\n case \"price\":\n return priceRangeEquals(a.price, b.price);\n case \"productType\":\n return a.productType === b.productType;\n case \"productVendor\":\n return a.productVendor === b.productVendor;\n case \"variantOption\": {\n const aOpt = a.variantOption;\n const bOpt = b.variantOption;\n if (aOpt == null || bOpt == null) return false;\n return aOpt.name === bOpt.name && (aOpt.value ?? \"\") === (bOpt.value ?? \"\");\n }\n case \"productMetafield\": {\n const aMeta = a.productMetafield;\n const bMeta = b.productMetafield;\n if (aMeta == null || bMeta == null) return false;\n return metafieldEquals(aMeta, bMeta);\n }\n case \"variantMetafield\": {\n const aMeta = a.variantMetafield;\n const bMeta = b.variantMetafield;\n if (aMeta == null || bMeta == null) return false;\n return metafieldEquals(aMeta, bMeta);\n }\n case \"taxonomyMetafield\": {\n const aMeta = a.taxonomyMetafield;\n const bMeta = b.taxonomyMetafield;\n if (aMeta == null || bMeta == null) return false;\n return aMeta.key === bMeta.key && aMeta.value === bMeta.value;\n }\n case \"category\": {\n const aCat = a.category;\n const bCat = b.category;\n if (aCat == null || bCat == null) return false;\n return aCat.id === bCat.id;\n }\n }\n}\n\n/**\n * Checks whether a filter value's `input` JSON string from the Storefront API matches an active filter. Use the result to set the checked state of a filter control.\n *\n * The function compares filters the same way as `filterEquals`.\n *\n * @param activeFilters The active filters, such as `filters` from the collection state.\n * @param input The filter value's `input` JSON string.\n * @returns `true` when the parsed input matches an active filter. `false` when the input matches no active filter or isn't valid JSON.\n * @publicDocs\n */\nexport function isFilterInputActive(activeFilters: ProductFilter[], input: string): boolean {\n let parsed: ProductFilter;\n try {\n parsed = JSON.parse(input) as ProductFilter;\n } catch {\n return false;\n }\n return activeFilters.some((filter) => filterEquals(filter, parsed));\n}\n\nfunction filterKind(\n filter: ProductFilter,\n):\n | \"tag\"\n | \"available\"\n | \"price\"\n | \"productType\"\n | \"productVendor\"\n | \"variantOption\"\n | \"productMetafield\"\n | \"variantMetafield\"\n | \"taxonomyMetafield\"\n | \"category\"\n | null {\n if (filter.tag != null) return \"tag\";\n if (filter.available != null) return \"available\";\n if (filter.price != null) return \"price\";\n if (filter.productType != null) return \"productType\";\n if (filter.productVendor != null) return \"productVendor\";\n if (filter.variantOption != null) return \"variantOption\";\n if (filter.productMetafield != null) return \"productMetafield\";\n if (filter.variantMetafield != null) return \"variantMetafield\";\n if (filter.taxonomyMetafield != null) return \"taxonomyMetafield\";\n if (filter.category != null) return \"category\";\n return null;\n}\n\nfunction priceRangeEquals(a: ProductFilter[\"price\"], b: ProductFilter[\"price\"]): boolean {\n if (a == null || b == null) return a === b;\n return a.min === b.min && a.max === b.max;\n}\n\nfunction metafieldEquals(\n a: { namespace: string; key: string; value?: string },\n b: { namespace: string; key: string; value?: string },\n): boolean {\n return a.namespace === b.namespace && a.key === b.key && (a.value ?? \"\") === (b.value ?? \"\");\n}\n\nfunction filtersEqual(a: ProductFilter[], b: ProductFilter[]): boolean {\n if (a.length !== b.length) return false;\n\n const remaining = [...b];\n for (const filter of a) {\n const index = remaining.findIndex((candidate) => filterEquals(filter, candidate));\n if (index === -1) return false;\n remaining.splice(index, 1);\n }\n\n return true;\n}\n\nfunction removeParamValue(params: URLSearchParams, name: string, value: string): void {\n const remaining = params.getAll(name).filter((v) => v !== value);\n params.delete(name);\n for (const v of remaining) {\n params.append(name, v);\n }\n}\n"],"mappings":"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"}
|
package/dist/core/constants.mjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"constants.mjs","names":[],"sources":["../../src/core/constants.ts"],"sourcesContent":["export const STOREFRONT_API_VERSION = \"2026-10\";\nexport const CUSTOMER_ACCOUNT_API_VERSION = \"2026-10\";\nexport const DEFAULT_TIMEOUT_IN_MS = 30_000;\n"],"mappings":";AAAA,MAAa,yBAAyB
|
|
1
|
+
{"version":3,"file":"constants.mjs","names":[],"sources":["../../src/core/constants.ts"],"sourcesContent":["export const STOREFRONT_API_VERSION = \"2026-10\";\n/** @publicDocs */\nexport const CUSTOMER_ACCOUNT_API_VERSION = \"2026-10\";\nexport const DEFAULT_TIMEOUT_IN_MS = 30_000;\n"],"mappings":";AAAA,MAAa,yBAAyB;;AAEtC,MAAa,+BAA+B;AAC5C,MAAa,wBAAwB"}
|
|
@@ -1,19 +1,25 @@
|
|
|
1
1
|
import { HydrogenLogger, LogLevel } from "./types.mjs";
|
|
2
2
|
|
|
3
3
|
//#region src/core/logging/logging.d.ts
|
|
4
|
+
/** The logger and minimum severity for Hydrogen log entries. */
|
|
4
5
|
type ConfigureLoggingOptions = {
|
|
5
|
-
/** Receives all entries at or above `level`. Defaults to the built-in console logger. */logger?: HydrogenLogger; /**
|
|
6
|
+
/** Receives all entries at or above `level`. Defaults to the built-in console logger. */logger?: HydrogenLogger; /** The minimum severity that Hydrogen forwards to the logger. Defaults to `info`. */
|
|
6
7
|
level?: LogLevel;
|
|
7
8
|
};
|
|
8
9
|
/**
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
* server) before Hydrogen helpers run.
|
|
10
|
+
* Sends Hydrogen's log entries to your logger at the minimum level you choose. Use the function to
|
|
11
|
+
* forward Hydrogen errors to a monitoring service or to reduce console output.
|
|
12
12
|
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
13
|
+
* Call the function once at startup. In the browser, call the function at app entry. On the
|
|
14
|
+
* server, call the function during module initialization. The configuration applies to every
|
|
15
|
+
* Hydrogen helper in the current JavaScript context, including helpers that loaded before the call.
|
|
16
|
+
* Each call replaces the previous configuration, and options that the call omits reset to their
|
|
17
|
+
* defaults. Hydrogen's inline analytics and consent scripts run outside your app bundle, and they
|
|
18
|
+
* always write to the console.
|
|
19
|
+
*
|
|
20
|
+
* @param options The logger that receives entries, and the minimum severity to send to the logger.
|
|
21
|
+
* @returns Nothing.
|
|
22
|
+
* @publicDocs
|
|
17
23
|
*/
|
|
18
24
|
declare function configureLogging(options: ConfigureLoggingOptions): void;
|
|
19
25
|
//#endregion
|
|
@@ -20,9 +20,9 @@ function writeToConsole(level, message, context) {
|
|
|
20
20
|
console[CONSOLE_METHODS[level]](...args);
|
|
21
21
|
}
|
|
22
22
|
/**
|
|
23
|
-
*
|
|
24
|
-
* level
|
|
25
|
-
* context fields.
|
|
23
|
+
* The built-in logger. Writes each entry to the matching console method, with a
|
|
24
|
+
* `[hydrogen:<level>:<scope>]` prefix when the entry has a scope, followed by the error and any
|
|
25
|
+
* extra context fields. Trace entries use `console.debug`, and fatal entries use `console.error`.
|
|
26
26
|
*/
|
|
27
27
|
const consoleLogger = {
|
|
28
28
|
trace: (message, context) => writeToConsole("trace", message, context),
|
|
@@ -37,14 +37,19 @@ const state = {
|
|
|
37
37
|
level: DEFAULT_LOG_LEVEL
|
|
38
38
|
};
|
|
39
39
|
/**
|
|
40
|
-
*
|
|
41
|
-
*
|
|
42
|
-
* server) before Hydrogen helpers run.
|
|
40
|
+
* Sends Hydrogen's log entries to your logger at the minimum level you choose. Use the function to
|
|
41
|
+
* forward Hydrogen errors to a monitoring service or to reduce console output.
|
|
43
42
|
*
|
|
44
|
-
*
|
|
45
|
-
*
|
|
46
|
-
*
|
|
47
|
-
*
|
|
43
|
+
* Call the function once at startup. In the browser, call the function at app entry. On the
|
|
44
|
+
* server, call the function during module initialization. The configuration applies to every
|
|
45
|
+
* Hydrogen helper in the current JavaScript context, including helpers that loaded before the call.
|
|
46
|
+
* Each call replaces the previous configuration, and options that the call omits reset to their
|
|
47
|
+
* defaults. Hydrogen's inline analytics and consent scripts run outside your app bundle, and they
|
|
48
|
+
* always write to the console.
|
|
49
|
+
*
|
|
50
|
+
* @param options The logger that receives entries, and the minimum severity to send to the logger.
|
|
51
|
+
* @returns Nothing.
|
|
52
|
+
* @publicDocs
|
|
48
53
|
*/
|
|
49
54
|
function configureLogging(options) {
|
|
50
55
|
const logger = options.logger ?? consoleLogger;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"logging.mjs","names":[],"sources":["../../../src/core/logging/logging.ts"],"sourcesContent":["import {\n DEFAULT_LOG_LEVEL,\n isLevelEnabled,\n type HydrogenLogger,\n type LogContext,\n type LogLevel,\n type LogSeverity,\n} from \"./types\";\n\nconst CONSOLE_METHODS: Record<LogSeverity, \"debug\" | \"info\" | \"warn\" | \"error\"> = {\n // `console.trace` prints a stack trace for every call, which is too noisy\n // for level-gated trace logs; route it to `console.debug` instead.\n trace: \"debug\",\n debug: \"debug\",\n info: \"info\",\n warn: \"warn\",\n error: \"error\",\n fatal: \"error\",\n};\n\nfunction formatLogPrefix(level: LogSeverity, scope: string): string {\n return `[hydrogen:${level}:${scope}]`;\n}\n\nfunction writeToConsole(level: LogSeverity, message: string, context?: LogContext): void {\n const { scope, error, ...rest } = context ?? {};\n const prefixed = scope ? `${formatLogPrefix(level, scope)} ${message}` : message;\n const args: unknown[] = [prefixed];\n if (error !== undefined) args.push(error);\n if (Object.keys(rest).length > 0) args.push(rest);\n\n // oxlint-disable-next-line no-console -- The built-in sink is the one sanctioned console call site.\n console[CONSOLE_METHODS[level]](...args);\n}\n\n/**\n *
|
|
1
|
+
{"version":3,"file":"logging.mjs","names":[],"sources":["../../../src/core/logging/logging.ts"],"sourcesContent":["import {\n DEFAULT_LOG_LEVEL,\n isLevelEnabled,\n type HydrogenLogger,\n type LogContext,\n type LogLevel,\n type LogSeverity,\n} from \"./types\";\n\nconst CONSOLE_METHODS: Record<LogSeverity, \"debug\" | \"info\" | \"warn\" | \"error\"> = {\n // `console.trace` prints a stack trace for every call, which is too noisy\n // for level-gated trace logs; route it to `console.debug` instead.\n trace: \"debug\",\n debug: \"debug\",\n info: \"info\",\n warn: \"warn\",\n error: \"error\",\n fatal: \"error\",\n};\n\nfunction formatLogPrefix(level: LogSeverity, scope: string): string {\n return `[hydrogen:${level}:${scope}]`;\n}\n\nfunction writeToConsole(level: LogSeverity, message: string, context?: LogContext): void {\n const { scope, error, ...rest } = context ?? {};\n const prefixed = scope ? `${formatLogPrefix(level, scope)} ${message}` : message;\n const args: unknown[] = [prefixed];\n if (error !== undefined) args.push(error);\n if (Object.keys(rest).length > 0) args.push(rest);\n\n // oxlint-disable-next-line no-console -- The built-in sink is the one sanctioned console call site.\n console[CONSOLE_METHODS[level]](...args);\n}\n\n/**\n * The built-in logger. Writes each entry to the matching console method, with a\n * `[hydrogen:<level>:<scope>]` prefix when the entry has a scope, followed by the error and any\n * extra context fields. Trace entries use `console.debug`, and fatal entries use `console.error`.\n */\nexport const consoleLogger: HydrogenLogger = {\n trace: (message, context) => writeToConsole(\"trace\", message, context),\n debug: (message, context) => writeToConsole(\"debug\", message, context),\n info: (message, context) => writeToConsole(\"info\", message, context),\n warn: (message, context) => writeToConsole(\"warn\", message, context),\n error: (message, context) => writeToConsole(\"error\", message, context),\n fatal: (message, context) => writeToConsole(\"fatal\", message, context),\n};\n\n/** The logger and minimum severity for Hydrogen log entries. */\nexport type ConfigureLoggingOptions = {\n /** Receives all entries at or above `level`. Defaults to the built-in console logger. */\n logger?: HydrogenLogger;\n /** The minimum severity that Hydrogen forwards to the logger. Defaults to `info`. */\n level?: LogLevel;\n};\n\ntype LoggingState = {\n logger: HydrogenLogger;\n level: LogLevel;\n};\n\nconst state: LoggingState = {\n logger: consoleLogger,\n level: DEFAULT_LOG_LEVEL,\n};\n\n/**\n * Sends Hydrogen's log entries to your logger at the minimum level you choose. Use the function to\n * forward Hydrogen errors to a monitoring service or to reduce console output.\n *\n * Call the function once at startup. In the browser, call the function at app entry. On the\n * server, call the function during module initialization. The configuration applies to every\n * Hydrogen helper in the current JavaScript context, including helpers that loaded before the call.\n * Each call replaces the previous configuration, and options that the call omits reset to their\n * defaults. Hydrogen's inline analytics and consent scripts run outside your app bundle, and they\n * always write to the console.\n *\n * @param options The logger that receives entries, and the minimum severity to send to the logger.\n * @returns Nothing.\n * @publicDocs\n */\nexport function configureLogging(options: ConfigureLoggingOptions): void {\n const logger = options.logger ?? consoleLogger;\n const level = options.level ?? DEFAULT_LOG_LEVEL;\n\n state.logger = logger;\n state.level = level;\n}\n\n/**\n * Scoped logger used by Hydrogen internals. Entries are level-gated and\n * tagged with `scope`, then forwarded to the configured sink. Resolution is\n * lazy: `configureLogging` affects loggers obtained before or after the call.\n */\ntype ScopedLogContext = Omit<LogContext, \"scope\"> & { scope?: never };\n\ntype ScopedLogger = Record<LogSeverity, (message: string, context?: ScopedLogContext) => void>;\n\nfunction emit(scope: string, level: LogSeverity, message: string, context?: LogContext): void {\n if (!isLevelEnabled(level, state.level)) return;\n\n try {\n state.logger[level](message, { ...context, scope });\n } catch (error) {\n reportLoggerFailure(error, { level, scope, message, context });\n }\n}\n\ntype FailedLogEntry = {\n level: LogSeverity;\n scope: string;\n message: string;\n context?: LogContext;\n};\n\nfunction reportLoggerFailure(error: unknown, failedEntry: FailedLogEntry): void {\n if (state.logger === consoleLogger) return;\n\n const fallbackContext: LogContext = {\n scope: \"logging\",\n error,\n originalLevel: failedEntry.level,\n originalScope: failedEntry.scope,\n originalMessage: failedEntry.message,\n };\n if (failedEntry.context !== undefined) fallbackContext.originalContext = failedEntry.context;\n\n try {\n consoleLogger.error(\"configured logger failed\", fallbackContext);\n } catch {\n // Logging failures must never change the runtime path that triggered the log.\n }\n}\n\n/** @internal Returns the lazily-resolved scoped logger for a Hydrogen subsystem. */\nexport function getLogger(scope: string): ScopedLogger {\n return {\n trace: (message, context) => emit(scope, \"trace\", message, context),\n debug: (message, context) => emit(scope, \"debug\", message, context),\n info: (message, context) => emit(scope, \"info\", message, context),\n warn: (message, context) => emit(scope, \"warn\", message, context),\n error: (message, context) => emit(scope, \"error\", message, context),\n fatal: (message, context) => emit(scope, \"fatal\", message, context),\n };\n}\n"],"mappings":";;;AASA,MAAM,kBAA4E;CAGhF,OAAO;CACP,OAAO;CACP,MAAM;CACN,MAAM;CACN,OAAO;CACP,OAAO;CACR;AAED,SAAS,gBAAgB,OAAoB,OAAuB;AAClE,QAAO,aAAa,MAAM,GAAG,MAAM;;AAGrC,SAAS,eAAe,OAAoB,SAAiB,SAA4B;CACvF,MAAM,EAAE,OAAO,OAAO,GAAG,SAAS,WAAW,EAAE;CAE/C,MAAM,OAAkB,CADP,QAAQ,GAAG,gBAAgB,OAAO,MAAM,CAAC,GAAG,YAAY,QACvC;AAClC,KAAI,UAAU,OAAW,MAAK,KAAK,MAAM;AACzC,KAAI,OAAO,KAAK,KAAK,CAAC,SAAS,EAAG,MAAK,KAAK,KAAK;AAGjD,SAAQ,gBAAgB,QAAQ,GAAG,KAAK;;;;;;;AAQ1C,MAAa,gBAAgC;CAC3C,QAAQ,SAAS,YAAY,eAAe,SAAS,SAAS,QAAQ;CACtE,QAAQ,SAAS,YAAY,eAAe,SAAS,SAAS,QAAQ;CACtE,OAAO,SAAS,YAAY,eAAe,QAAQ,SAAS,QAAQ;CACpE,OAAO,SAAS,YAAY,eAAe,QAAQ,SAAS,QAAQ;CACpE,QAAQ,SAAS,YAAY,eAAe,SAAS,SAAS,QAAQ;CACtE,QAAQ,SAAS,YAAY,eAAe,SAAS,SAAS,QAAQ;CACvE;AAeD,MAAM,QAAsB;CAC1B,QAAQ;CACR,OAAO;CACR;;;;;;;;;;;;;;;;AAiBD,SAAgB,iBAAiB,SAAwC;CACvE,MAAM,SAAS,QAAQ,UAAU;CACjC,MAAM,QAAQ,QAAQ;AAEtB,OAAM,SAAS;AACf,OAAM,QAAQ;;AAYhB,SAAS,KAAK,OAAe,OAAoB,SAAiB,SAA4B;AAC5F,KAAI,CAAC,eAAe,OAAO,MAAM,MAAM,CAAE;AAEzC,KAAI;AACF,QAAM,OAAO,OAAO,SAAS;GAAE,GAAG;GAAS;GAAO,CAAC;UAC5C,OAAO;AACd,sBAAoB,OAAO;GAAE;GAAO;GAAO;GAAS;GAAS,CAAC;;;AAWlE,SAAS,oBAAoB,OAAgB,aAAmC;AAC9E,KAAI,MAAM,WAAW,cAAe;CAEpC,MAAM,kBAA8B;EAClC,OAAO;EACP;EACA,eAAe,YAAY;EAC3B,eAAe,YAAY;EAC3B,iBAAiB,YAAY;EAC9B;AACD,KAAI,YAAY,YAAY,OAAW,iBAAgB,kBAAkB,YAAY;AAErF,KAAI;AACF,gBAAc,MAAM,4BAA4B,gBAAgB;SAC1D;;;AAMV,SAAgB,UAAU,OAA6B;AACrD,QAAO;EACL,QAAQ,SAAS,YAAY,KAAK,OAAO,SAAS,SAAS,QAAQ;EACnE,QAAQ,SAAS,YAAY,KAAK,OAAO,SAAS,SAAS,QAAQ;EACnE,OAAO,SAAS,YAAY,KAAK,OAAO,QAAQ,SAAS,QAAQ;EACjE,OAAO,SAAS,YAAY,KAAK,OAAO,QAAQ,SAAS,QAAQ;EACjE,QAAQ,SAAS,YAAY,KAAK,OAAO,SAAS,SAAS,QAAQ;EACnE,QAAQ,SAAS,YAAY,KAAK,OAAO,SAAS,SAAS,QAAQ;EACpE"}
|