@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
|
@@ -6,33 +6,50 @@
|
|
|
6
6
|
* level methods can receive Hydrogen entries. Hydrogen never depends on a
|
|
7
7
|
* specific logging library.
|
|
8
8
|
*/
|
|
9
|
-
/**
|
|
9
|
+
/** The log severities, from lowest to highest. Set `silent` to turn off all output. */
|
|
10
10
|
type LogLevel = "trace" | "debug" | "info" | "warn" | "error" | "fatal" | "silent";
|
|
11
11
|
/**
|
|
12
|
-
* Structured details attached to a log entry.
|
|
13
|
-
*
|
|
14
|
-
*
|
|
12
|
+
* Structured details attached to a log entry. Extra fields pass through to the logger.
|
|
13
|
+
*
|
|
14
|
+
* @publicDocs
|
|
15
15
|
*/
|
|
16
16
|
type LogContext = {
|
|
17
|
-
scope?: string;
|
|
17
|
+
/** The Hydrogen subsystem that wrote the entry, such as `cart` or `analytics`. */scope?: string; /** The caught value when the entry reports a failure. */
|
|
18
18
|
error?: unknown;
|
|
19
19
|
[key: string]: unknown;
|
|
20
20
|
};
|
|
21
|
-
type LogFn = (message: string, context?: LogContext) => void;
|
|
22
21
|
/**
|
|
23
|
-
*
|
|
24
|
-
|
|
22
|
+
* Writes one log entry at the severity of the logger method.
|
|
23
|
+
*/
|
|
24
|
+
type LogFn =
|
|
25
|
+
/**
|
|
26
|
+
* @param message - The entry text, without a prefix.
|
|
27
|
+
* @param context - Structured details about the entry, including the subsystem scope.
|
|
28
|
+
* @returns Nothing. Hydrogen ignores the return value.
|
|
29
|
+
*/
|
|
30
|
+
(message: string, context?: LogContext) => void;
|
|
31
|
+
/**
|
|
32
|
+
* A logger that receives Hydrogen's log entries. Pass your logger to `configureLogging`. Without
|
|
33
|
+
* one, Hydrogen writes to the console.
|
|
34
|
+
*
|
|
35
|
+
* Messages arrive without a prefix, and the context scope names the subsystem. The built-in
|
|
36
|
+
* console logger formats entries as `[hydrogen:<level>:<scope>] <message>`. The console logger
|
|
37
|
+
* writes trace entries with `console.debug` and fatal entries with `console.error`.
|
|
25
38
|
*
|
|
26
|
-
*
|
|
27
|
-
* custom sinks control their own formatting. The built-in console logger
|
|
28
|
-
* formats entries as `[hydrogen:<level>:<scope>] <message>`.
|
|
39
|
+
* When a custom logger throws, Hydrogen reports the failure through the console logger and continues the operation that logged the entry. Catch network errors inside a custom logger to keep a monitoring outage from writing a console error for every entry.
|
|
29
40
|
*/
|
|
30
41
|
interface HydrogenLogger {
|
|
42
|
+
/** Receives trace entries, the lowest severity. */
|
|
31
43
|
trace: LogFn;
|
|
44
|
+
/** Receives debug entries. */
|
|
32
45
|
debug: LogFn;
|
|
46
|
+
/** Receives info entries. Info is the default minimum severity. */
|
|
33
47
|
info: LogFn;
|
|
48
|
+
/** Receives warning entries. */
|
|
34
49
|
warn: LogFn;
|
|
50
|
+
/** Receives error entries. */
|
|
35
51
|
error: LogFn;
|
|
52
|
+
/** Receives fatal entries, the highest severity. */
|
|
36
53
|
fatal: LogFn;
|
|
37
54
|
}
|
|
38
55
|
//#endregion
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.mjs","names":[],"sources":["../../../src/core/logging/types.ts"],"sourcesContent":["/**\n * Logging contract for `@shopify/hydrogen` runtime failures.\n *\n * The interface is intentionally minimal and structural: any logger with these\n * level methods can receive Hydrogen entries. Hydrogen never depends on a\n * specific logging library.\n */\n\n/**
|
|
1
|
+
{"version":3,"file":"types.mjs","names":[],"sources":["../../../src/core/logging/types.ts"],"sourcesContent":["/**\n * Logging contract for `@shopify/hydrogen` runtime failures.\n *\n * The interface is intentionally minimal and structural: any logger with these\n * level methods can receive Hydrogen entries. Hydrogen never depends on a\n * specific logging library.\n */\n\n/** The log severities, from lowest to highest. Set `silent` to turn off all output. */\nexport type LogLevel = \"trace\" | \"debug\" | \"info\" | \"warn\" | \"error\" | \"fatal\" | \"silent\";\n\n/**\n * Structured details attached to a log entry. Extra fields pass through to the logger.\n *\n * @publicDocs\n */\nexport type LogContext = {\n /** The Hydrogen subsystem that wrote the entry, such as `cart` or `analytics`. */\n scope?: string;\n /** The caught value when the entry reports a failure. */\n error?: unknown;\n [key: string]: unknown;\n};\n\n/**\n * Writes one log entry at the severity of the logger method.\n */\ntype LogFn =\n /**\n * @param message - The entry text, without a prefix.\n * @param context - Structured details about the entry, including the subsystem scope.\n * @returns Nothing. Hydrogen ignores the return value.\n */\n (message: string, context?: LogContext) => void;\n\n/**\n * A logger that receives Hydrogen's log entries. Pass your logger to `configureLogging`. Without\n * one, Hydrogen writes to the console.\n *\n * Messages arrive without a prefix, and the context scope names the subsystem. The built-in\n * console logger formats entries as `[hydrogen:<level>:<scope>] <message>`. The console logger\n * writes trace entries with `console.debug` and fatal entries with `console.error`.\n *\n * When a custom logger throws, Hydrogen reports the failure through the console logger and continues the operation that logged the entry. Catch network errors inside a custom logger to keep a monitoring outage from writing a console error for every entry.\n */\nexport interface HydrogenLogger {\n /** Receives trace entries, the lowest severity. */\n trace: LogFn;\n /** Receives debug entries. */\n debug: LogFn;\n /** Receives info entries. Info is the default minimum severity. */\n info: LogFn;\n /** Receives warning entries. */\n warn: LogFn;\n /** Receives error entries. */\n error: LogFn;\n /** Receives fatal entries, the highest severity. */\n fatal: LogFn;\n}\n\nexport const DEFAULT_LOG_LEVEL: LogLevel = \"info\";\n\nconst LOG_LEVEL_VALUES: Record<LogLevel, number> = {\n trace: 10,\n debug: 20,\n info: 30,\n warn: 40,\n error: 50,\n fatal: 60,\n silent: Number.POSITIVE_INFINITY,\n};\n\n/** The severity of a log entry. Every level except `silent`. */\nexport type LogSeverity = Exclude<LogLevel, \"silent\">;\n\nexport function isLevelEnabled(level: LogSeverity, threshold: LogLevel): boolean {\n return LOG_LEVEL_VALUES[level] >= LOG_LEVEL_VALUES[threshold];\n}\n"],"mappings":";AA4DA,MAAa,oBAA8B;AAE3C,MAAM,mBAA6C;CACjD,OAAO;CACP,OAAO;CACP,MAAM;CACN,MAAM;CACN,OAAO;CACP,OAAO;CACP,QAAQ,OAAO;CAChB;AAKD,SAAgB,eAAe,OAAoB,WAA8B;AAC/E,QAAO,iBAAiB,UAAU,iBAAiB"}
|
|
@@ -2,11 +2,13 @@ import { FormatMoneyOptions, FormattedMoney, FormattedMoneyRange, MoneyV2 } from
|
|
|
2
2
|
|
|
3
3
|
//#region src/core/money/format.d.ts
|
|
4
4
|
/**
|
|
5
|
-
*
|
|
5
|
+
* Formats a price or a price range for display. Pass a `MoneyV2` price, or an array of `MoneyV2` prices in one currency for a range, with the active market's locale.
|
|
6
6
|
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
7
|
+
* The returned object converts to the formatted string in template literals and string concatenation. For custom price layouts, read the parts of the formatted price. A range shows the lowest and highest prices. When every price in the range matches, the output shows one price.
|
|
8
|
+
*
|
|
9
|
+
* The function throws an error when an amount isn't numeric, when a range is empty, or when the prices in a range use different currencies.
|
|
10
|
+
*
|
|
11
|
+
* @publicDocs
|
|
10
12
|
*/
|
|
11
13
|
declare function formatMoney(money: MoneyV2, options: FormatMoneyOptions): FormattedMoney;
|
|
12
14
|
declare function formatMoney(money: readonly MoneyV2[], options: FormatMoneyOptions): FormattedMoneyRange;
|
|
@@ -6,7 +6,10 @@ import { getFormatter } from "./cache.mjs";
|
|
|
6
6
|
* Used to extract "19.99" from a formatted string like "$19.99" (or
|
|
7
7
|
* "-19.99" from "-$19.99") by filtering out currency symbols and other
|
|
8
8
|
* non-numeric parts. The minus sign is kept so negative amounts such as
|
|
9
|
-
* refunds and discounts don't render as positive.
|
|
9
|
+
* refunds and discounts don't render as positive. Literals stay in the set
|
|
10
|
+
* because RTL locales such as he-IL and ar-EG emit the bidi marks that bind
|
|
11
|
+
* the minus sign to the digits as literals. Only the literal next to the
|
|
12
|
+
* currency part, the spacer between number and symbol, is dropped.
|
|
10
13
|
*/
|
|
11
14
|
const NUMERIC_PART_TYPES = new Set([
|
|
12
15
|
"decimal",
|
|
@@ -26,8 +29,8 @@ function normalizeCurrencyCode(currencyCode) {
|
|
|
26
29
|
return currencyCode.toUpperCase();
|
|
27
30
|
}
|
|
28
31
|
/**
|
|
29
|
-
* Intl accepts well-formed three-letter currency codes
|
|
30
|
-
*
|
|
32
|
+
* Intl accepts well-formed three-letter currency codes and renders unknown ones as codes.
|
|
33
|
+
* Intl throws for malformed codes such as USDC. The formatter formats those as decimals.
|
|
31
34
|
*/
|
|
32
35
|
function isWellFormedCurrencyCode(currencyCode) {
|
|
33
36
|
return WELL_FORMED_CURRENCY_CODE.test(currencyCode);
|
|
@@ -70,7 +73,9 @@ function appendUnsupportedCurrencyCode(formatted, currencyCode) {
|
|
|
70
73
|
return `${formatted} ${currencyCode}`;
|
|
71
74
|
}
|
|
72
75
|
function extractNumericAmount(parts) {
|
|
73
|
-
|
|
76
|
+
const currency = parts.findIndex((part) => part.type === "currency");
|
|
77
|
+
const isCurrencySpacer = (part, index) => currency !== -1 && part.type === "literal" && Math.abs(index - currency) === 1;
|
|
78
|
+
return parts.filter((part, index) => NUMERIC_PART_TYPES.has(part.type) && !isCurrencySpacer(part, index)).map((part) => part.value).join("");
|
|
74
79
|
}
|
|
75
80
|
function findCurrencyPart(parts) {
|
|
76
81
|
return parts.find((part) => part.type === "currency")?.value ?? "";
|
|
@@ -82,12 +87,12 @@ function formatRange(formatter, min, max) {
|
|
|
82
87
|
function isMoneyRange(money) {
|
|
83
88
|
return Array.isArray(money);
|
|
84
89
|
}
|
|
85
|
-
function buildRangeOptions(currencyCode, supported, options,
|
|
86
|
-
const wholeRange =
|
|
90
|
+
function buildRangeOptions(currencyCode, supported, options, min, max) {
|
|
91
|
+
const wholeRange = hasTrailingZeros(min) && hasTrailingZeros(max);
|
|
87
92
|
return buildFormatOptions(currencyCode, supported, {
|
|
88
93
|
...options,
|
|
89
|
-
withoutTrailingZeros: options.withoutTrailingZeros ??
|
|
90
|
-
},
|
|
94
|
+
withoutTrailingZeros: wholeRange && (options.withoutTrailingZeros ?? !hasExplicitFractionDigits(options))
|
|
95
|
+
}, min);
|
|
91
96
|
}
|
|
92
97
|
function sortMoneyRangeEntries(entries) {
|
|
93
98
|
const sorted = [];
|
|
@@ -230,13 +235,18 @@ var FormattedMoneyRangeValue = class {
|
|
|
230
235
|
return;
|
|
231
236
|
}
|
|
232
237
|
const supported = isWellFormedCurrencyCode(first.currencyCode);
|
|
233
|
-
const formatted = formatRange(getFormatter(options.locale, buildRangeOptions(first.currencyCode, supported, options,
|
|
238
|
+
const formatted = formatRange(getFormatter(options.locale, buildRangeOptions(first.currencyCode, supported, options, first.amount, last.amount)), first.amount, last.amount);
|
|
234
239
|
this.localizedString = supported || options.withoutCurrency === true ? formatted : appendUnsupportedCurrencyCode(formatted, first.currencyCode);
|
|
235
240
|
}
|
|
236
241
|
toString() {
|
|
237
242
|
return this.localizedString;
|
|
238
243
|
}
|
|
239
244
|
};
|
|
245
|
+
/**
|
|
246
|
+
* @param money A price, or an array of prices in one currency for a range.
|
|
247
|
+
* @param options Locale and display settings.
|
|
248
|
+
* @returns The formatted price or range, which converts to a string in template literals.
|
|
249
|
+
*/
|
|
240
250
|
function formatMoney(money, options) {
|
|
241
251
|
return isMoneyRange(money) ? new FormattedMoneyRangeValue(money, options) : new FormattedMoneyValue(money, options);
|
|
242
252
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"format.mjs","names":["#amount","#options","#currencyCode","#supported","#defaultFormatter","#amountString","#currencySymbol","#currencyNarrowSymbol","#currencyName","#parts","#withoutTrailingZeros","#withoutTrailingZerosAndCurrency","#formatterParts"],"sources":["../../../src/core/money/format.ts"],"sourcesContent":["import { getFormatter } from \"./cache\";\nimport type { FormatMoneyOptions, FormattedMoney, FormattedMoneyRange, MoneyV2 } from \"./types\";\n\n/**\n * Intl.NumberFormat part types that make up the raw numeric amount.\n * Used to extract \"19.99\" from a formatted string like \"$19.99\" (or\n * \"-19.99\" from \"-$19.99\") by filtering out currency symbols and other\n * non-numeric parts. The minus sign is kept so negative amounts such as\n * refunds and discounts don't render as positive.\n */\nconst NUMERIC_PART_TYPES = new Set([\n \"decimal\",\n \"fraction\",\n \"group\",\n \"integer\",\n \"literal\",\n \"minusSign\",\n]);\nconst WELL_FORMED_CURRENCY_CODE = /^[a-z]{3}$/i;\n\ntype RangeFormatter = Intl.NumberFormat & {\n formatRange?: (start: number, end: number) => string;\n};\n\ntype MoneyRangeEntry = {\n money: MoneyV2;\n amount: number;\n currencyCode: string;\n};\n\nfunction parseAmount(money: MoneyV2): number {\n const parsed = parseFloat(money.amount);\n if (Number.isNaN(parsed)) {\n throw new Error(\n `formatMoney: \"${money.amount}\" is not a valid numeric amount for currency ${money.currencyCode}`,\n );\n }\n return parsed;\n}\n\nfunction normalizeCurrencyCode(currencyCode: string): string {\n return currencyCode.toUpperCase();\n}\n\n/**\n * Intl accepts well-formed three-letter currency codes. Non-standard but\n * well-formed codes render as codes; malformed codes such as USDC throw.\n */\nfunction isWellFormedCurrencyCode(currencyCode: string): boolean {\n return WELL_FORMED_CURRENCY_CODE.test(currencyCode);\n}\n\nfunction hasTrailingZeros(amount: number): boolean {\n return amount % 1 === 0;\n}\n\nfunction shouldStripTrailingZeros(amount: number, options: FormatMoneyOptions): boolean {\n return options.withoutTrailingZeros === true && hasTrailingZeros(amount);\n}\n\nfunction hasExplicitFractionDigits(options: FormatMoneyOptions): boolean {\n return options.minimumFractionDigits != null || options.maximumFractionDigits != null;\n}\n\nfunction decimalFractionOptions(options: FormatMoneyOptions): Intl.NumberFormatOptions {\n return {\n minimumFractionDigits: options.minimumFractionDigits ?? 2,\n maximumFractionDigits: options.maximumFractionDigits ?? 2,\n };\n}\n\nfunction buildFormatOptions(\n currencyCode: string,\n supported: boolean,\n options: FormatMoneyOptions,\n amount: number,\n): Intl.NumberFormatOptions {\n const withoutCurrency = options.withoutCurrency === true || !supported;\n const trailingZeros = shouldStripTrailingZeros(amount, options);\n\n if (withoutCurrency) {\n return trailingZeros\n ? { minimumFractionDigits: 0, maximumFractionDigits: 0 }\n : decimalFractionOptions(options);\n }\n\n return {\n style: \"currency\",\n currency: currencyCode,\n ...(options.currencyDisplay && { currencyDisplay: options.currencyDisplay }),\n ...(trailingZeros && {\n minimumFractionDigits: 0,\n maximumFractionDigits: 0,\n }),\n ...(!trailingZeros &&\n options.minimumFractionDigits != null && {\n minimumFractionDigits: options.minimumFractionDigits,\n }),\n ...(!trailingZeros &&\n options.maximumFractionDigits != null && {\n maximumFractionDigits: options.maximumFractionDigits,\n }),\n };\n}\n\nfunction appendUnsupportedCurrencyCode(formatted: string, currencyCode: string): string {\n return `${formatted} ${currencyCode}`;\n}\n\nfunction extractNumericAmount(parts: Intl.NumberFormatPart[]): string {\n return parts\n .filter((part) => NUMERIC_PART_TYPES.has(part.type))\n .map((part) => part.value)\n .join(\"\");\n}\n\nfunction findCurrencyPart(parts: Intl.NumberFormatPart[]): string {\n return parts.find((part) => part.type === \"currency\")?.value ?? \"\";\n}\n\nfunction formatRange(formatter: Intl.NumberFormat, min: number, max: number): string {\n const rangeFormatter = formatter as RangeFormatter;\n return rangeFormatter.formatRange\n ? rangeFormatter.formatRange(min, max)\n : `${formatter.format(min)} - ${formatter.format(max)}`;\n}\n\nfunction isMoneyRange(money: MoneyV2 | readonly MoneyV2[]): money is readonly MoneyV2[] {\n return Array.isArray(money);\n}\n\nfunction buildRangeOptions(\n currencyCode: string,\n supported: boolean,\n options: FormatMoneyOptions,\n amounts: number[],\n): Intl.NumberFormatOptions {\n const wholeRange = amounts.every(hasTrailingZeros);\n const rangeOptions = {\n ...options,\n withoutTrailingZeros:\n options.withoutTrailingZeros ?? (wholeRange && !hasExplicitFractionDigits(options)),\n };\n\n return buildFormatOptions(currencyCode, supported, rangeOptions, amounts[0] ?? 0);\n}\n\nfunction sortMoneyRangeEntries(entries: MoneyRangeEntry[]): MoneyRangeEntry[] {\n const sorted: MoneyRangeEntry[] = [];\n\n for (const entry of entries) {\n const index = sorted.findIndex((candidate) => entry.amount < candidate.amount);\n if (index === -1) {\n sorted.push(entry);\n } else {\n sorted.splice(index, 0, entry);\n }\n }\n\n return sorted;\n}\n\n/**\n * All structured fields are computed lazily on first access and cached.\n * localizedString is the exception — it's computed eagerly in the constructor\n * so that the common `${price}` path never touches the getter machinery.\n */\nclass FormattedMoneyValue implements FormattedMoney {\n readonly #amount: number;\n readonly #options: FormatMoneyOptions;\n readonly #currencyCode: string;\n readonly #supported: boolean;\n readonly localizedString: string;\n\n readonly #defaultFormatter: Intl.NumberFormat;\n #formatterParts?: Intl.NumberFormatPart[];\n #parts?: Intl.NumberFormatPart[];\n #amountString?: string;\n #currencySymbol?: string;\n #currencyNarrowSymbol?: string;\n #currencyName?: string;\n #withoutTrailingZeros?: string;\n #withoutTrailingZerosAndCurrency?: string;\n\n constructor(money: MoneyV2, options: FormatMoneyOptions) {\n this.#amount = parseAmount(money);\n this.#options = options;\n this.#currencyCode = normalizeCurrencyCode(money.currencyCode);\n this.#supported = isWellFormedCurrencyCode(money.currencyCode);\n this.#defaultFormatter = getFormatter(\n options.locale,\n buildFormatOptions(this.#currencyCode, this.#supported, options, this.#amount),\n );\n\n const formatted = this.#defaultFormatter.format(this.#amount);\n this.localizedString =\n this.#supported || options.withoutCurrency === true\n ? formatted\n : appendUnsupportedCurrencyCode(formatted, this.#currencyCode);\n }\n\n get amount(): string {\n this.#amountString ??= extractNumericAmount(this.formatterParts);\n return this.#amountString;\n }\n\n get numericAmount(): number {\n return this.#amount;\n }\n\n get currencySymbol(): string {\n this.#currencySymbol ??= this.#supported ? findCurrencyPart(this.parts) : this.#currencyCode;\n return this.#currencySymbol;\n }\n\n get currencyNarrowSymbol(): string {\n this.#currencyNarrowSymbol ??= this.#supported\n ? findCurrencyPart(\n getFormatter(this.#options.locale, {\n style: \"currency\",\n currency: this.#currencyCode,\n currencyDisplay: \"narrowSymbol\",\n ...(this.#options.minimumFractionDigits != null && {\n minimumFractionDigits: this.#options.minimumFractionDigits,\n }),\n ...(this.#options.maximumFractionDigits != null && {\n maximumFractionDigits: this.#options.maximumFractionDigits,\n }),\n }).formatToParts(this.#amount),\n )\n : this.#currencyCode;\n return this.#currencyNarrowSymbol;\n }\n\n get currencyName(): string {\n this.#currencyName ??= this.#supported\n ? findCurrencyPart(\n getFormatter(this.#options.locale, {\n style: \"currency\",\n currency: this.#currencyCode,\n currencyDisplay: \"name\",\n ...(this.#options.minimumFractionDigits != null && {\n minimumFractionDigits: this.#options.minimumFractionDigits,\n }),\n ...(this.#options.maximumFractionDigits != null && {\n maximumFractionDigits: this.#options.maximumFractionDigits,\n }),\n }).formatToParts(this.#amount),\n ) || this.#currencyCode\n : this.#currencyCode;\n return this.#currencyName;\n }\n\n get parts(): Intl.NumberFormatPart[] {\n if (!this.#parts) {\n this.#parts =\n this.#supported || this.#options.withoutCurrency === true\n ? this.formatterParts\n : [\n ...this.formatterParts,\n { type: \"literal\" as const, value: \" \" },\n { type: \"currency\" as const, value: this.#currencyCode },\n ];\n }\n\n return this.#parts;\n }\n\n get withoutTrailingZeros(): string {\n if (!this.#withoutTrailingZeros) {\n const options = { ...this.#options, withoutTrailingZeros: true };\n const formatter = getFormatter(\n options.locale,\n buildFormatOptions(this.#currencyCode, this.#supported, options, this.#amount),\n );\n const formatted = formatter.format(this.#amount);\n this.#withoutTrailingZeros =\n this.#supported || this.#options.withoutCurrency === true\n ? formatted\n : appendUnsupportedCurrencyCode(formatted, this.#currencyCode);\n }\n\n return this.#withoutTrailingZeros;\n }\n\n get withoutTrailingZerosAndCurrency(): string {\n if (!this.#withoutTrailingZerosAndCurrency) {\n const options = { ...this.#options, withoutCurrency: true, withoutTrailingZeros: true };\n this.#withoutTrailingZerosAndCurrency = getFormatter(\n options.locale,\n buildFormatOptions(this.#currencyCode, this.#supported, options, this.#amount),\n ).format(this.#amount);\n }\n\n return this.#withoutTrailingZerosAndCurrency;\n }\n\n private get formatterParts(): Intl.NumberFormatPart[] {\n this.#formatterParts ??= this.#defaultFormatter.formatToParts(this.#amount);\n return this.#formatterParts;\n }\n\n toString(): string {\n return this.localizedString;\n }\n}\n\nclass FormattedMoneyRangeValue implements FormattedMoneyRange {\n readonly localizedString: string;\n readonly min: MoneyV2;\n readonly max: MoneyV2;\n readonly currencyCode: string;\n\n constructor(range: readonly MoneyV2[], options: FormatMoneyOptions) {\n if (range.length === 0) {\n throw new Error(\"formatMoney: money range must contain at least one value\");\n }\n\n const entries = sortMoneyRangeEntries(\n range.map((money) => ({\n money,\n amount: parseAmount(money),\n currencyCode: normalizeCurrencyCode(money.currencyCode),\n })),\n );\n\n const [first] = entries;\n if (!first) {\n throw new Error(\"formatMoney: money range must contain at least one value\");\n }\n\n const mismatched = entries.find((entry) => entry.currencyCode !== first.currencyCode);\n if (mismatched) {\n throw new Error(\n `formatMoney: range values must share one currency, received ${first.currencyCode} and ${mismatched.currencyCode}`,\n );\n }\n\n const last = entries[entries.length - 1] ?? first;\n this.min = first.money;\n this.max = last.money;\n this.currencyCode = first.currencyCode;\n\n if (first.amount === last.amount) {\n this.localizedString = new FormattedMoneyValue(first.money, {\n ...options,\n withoutTrailingZeros: options.withoutTrailingZeros ?? !hasExplicitFractionDigits(options),\n }).localizedString;\n return;\n }\n\n const supported = isWellFormedCurrencyCode(first.currencyCode);\n const formatter = getFormatter(\n options.locale,\n buildRangeOptions(\n first.currencyCode,\n supported,\n options,\n entries.map((entry) => entry.amount),\n ),\n );\n\n const formatted = formatRange(formatter, first.amount, last.amount);\n this.localizedString =\n supported || options.withoutCurrency === true\n ? formatted\n : appendUnsupportedCurrencyCode(formatted, first.currencyCode);\n }\n\n toString(): string {\n return this.localizedString;\n }\n}\n\n/**\n * Format a MoneyV2 value or a same-currency range of MoneyV2 values for display.\n *\n * Returns an object that stringifies to the locale-formatted price via\n * toString(). Structured fields on single prices are computed lazily when\n * accessed, so the common string path stays cheap.\n */\nexport function formatMoney(money: MoneyV2, options: FormatMoneyOptions): FormattedMoney;\nexport function formatMoney(\n money: readonly MoneyV2[],\n options: FormatMoneyOptions,\n): FormattedMoneyRange;\n\n// This is the implementation of the formatMoney function overloading the other two.\n// It checks if the input is a range and creates the appropriate object.\nexport function formatMoney(\n money: MoneyV2 | readonly MoneyV2[],\n options: FormatMoneyOptions,\n): FormattedMoney | FormattedMoneyRange {\n return isMoneyRange(money)\n ? new FormattedMoneyRangeValue(money, options)\n : new FormattedMoneyValue(money, options);\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"format.mjs","names":["#amount","#options","#currencyCode","#supported","#defaultFormatter","#amountString","#currencySymbol","#currencyNarrowSymbol","#currencyName","#parts","#withoutTrailingZeros","#withoutTrailingZerosAndCurrency","#formatterParts"],"sources":["../../../src/core/money/format.ts"],"sourcesContent":["import { getFormatter } from \"./cache\";\nimport type { FormatMoneyOptions, FormattedMoney, FormattedMoneyRange, MoneyV2 } from \"./types\";\n\n/**\n * Intl.NumberFormat part types that make up the raw numeric amount.\n * Used to extract \"19.99\" from a formatted string like \"$19.99\" (or\n * \"-19.99\" from \"-$19.99\") by filtering out currency symbols and other\n * non-numeric parts. The minus sign is kept so negative amounts such as\n * refunds and discounts don't render as positive. Literals stay in the set\n * because RTL locales such as he-IL and ar-EG emit the bidi marks that bind\n * the minus sign to the digits as literals. Only the literal next to the\n * currency part, the spacer between number and symbol, is dropped.\n */\nconst NUMERIC_PART_TYPES = new Set([\n \"decimal\",\n \"fraction\",\n \"group\",\n \"integer\",\n \"literal\",\n \"minusSign\",\n]);\nconst WELL_FORMED_CURRENCY_CODE = /^[a-z]{3}$/i;\n\ntype RangeFormatter = Intl.NumberFormat & {\n formatRange?: (start: number, end: number) => string;\n};\n\ntype MoneyRangeEntry = {\n money: MoneyV2;\n amount: number;\n currencyCode: string;\n};\n\nfunction parseAmount(money: MoneyV2): number {\n const parsed = parseFloat(money.amount);\n if (Number.isNaN(parsed)) {\n throw new Error(\n `formatMoney: \"${money.amount}\" is not a valid numeric amount for currency ${money.currencyCode}`,\n );\n }\n return parsed;\n}\n\nfunction normalizeCurrencyCode(currencyCode: string): string {\n return currencyCode.toUpperCase();\n}\n\n/**\n * Intl accepts well-formed three-letter currency codes and renders unknown ones as codes.\n * Intl throws for malformed codes such as USDC. The formatter formats those as decimals.\n */\nfunction isWellFormedCurrencyCode(currencyCode: string): boolean {\n return WELL_FORMED_CURRENCY_CODE.test(currencyCode);\n}\n\nfunction hasTrailingZeros(amount: number): boolean {\n return amount % 1 === 0;\n}\n\nfunction shouldStripTrailingZeros(amount: number, options: FormatMoneyOptions): boolean {\n return options.withoutTrailingZeros === true && hasTrailingZeros(amount);\n}\n\nfunction hasExplicitFractionDigits(options: FormatMoneyOptions): boolean {\n return options.minimumFractionDigits != null || options.maximumFractionDigits != null;\n}\n\nfunction decimalFractionOptions(options: FormatMoneyOptions): Intl.NumberFormatOptions {\n return {\n minimumFractionDigits: options.minimumFractionDigits ?? 2,\n maximumFractionDigits: options.maximumFractionDigits ?? 2,\n };\n}\n\nfunction buildFormatOptions(\n currencyCode: string,\n supported: boolean,\n options: FormatMoneyOptions,\n amount: number,\n): Intl.NumberFormatOptions {\n const withoutCurrency = options.withoutCurrency === true || !supported;\n const trailingZeros = shouldStripTrailingZeros(amount, options);\n\n if (withoutCurrency) {\n return trailingZeros\n ? { minimumFractionDigits: 0, maximumFractionDigits: 0 }\n : decimalFractionOptions(options);\n }\n\n return {\n style: \"currency\",\n currency: currencyCode,\n ...(options.currencyDisplay && { currencyDisplay: options.currencyDisplay }),\n ...(trailingZeros && {\n minimumFractionDigits: 0,\n maximumFractionDigits: 0,\n }),\n ...(!trailingZeros &&\n options.minimumFractionDigits != null && {\n minimumFractionDigits: options.minimumFractionDigits,\n }),\n ...(!trailingZeros &&\n options.maximumFractionDigits != null && {\n maximumFractionDigits: options.maximumFractionDigits,\n }),\n };\n}\n\nfunction appendUnsupportedCurrencyCode(formatted: string, currencyCode: string): string {\n return `${formatted} ${currencyCode}`;\n}\n\nfunction extractNumericAmount(parts: Intl.NumberFormatPart[]): string {\n const currency = parts.findIndex((part) => part.type === \"currency\");\n const isCurrencySpacer = (part: Intl.NumberFormatPart, index: number) =>\n currency !== -1 && part.type === \"literal\" && Math.abs(index - currency) === 1;\n\n return parts\n .filter((part, index) => NUMERIC_PART_TYPES.has(part.type) && !isCurrencySpacer(part, index))\n .map((part) => part.value)\n .join(\"\");\n}\n\nfunction findCurrencyPart(parts: Intl.NumberFormatPart[]): string {\n return parts.find((part) => part.type === \"currency\")?.value ?? \"\";\n}\n\nfunction formatRange(formatter: Intl.NumberFormat, min: number, max: number): string {\n const rangeFormatter = formatter as RangeFormatter;\n return rangeFormatter.formatRange\n ? rangeFormatter.formatRange(min, max)\n : `${formatter.format(min)} - ${formatter.format(max)}`;\n}\n\nfunction isMoneyRange(money: MoneyV2 | readonly MoneyV2[]): money is readonly MoneyV2[] {\n return Array.isArray(money);\n}\n\nfunction buildRangeOptions(\n currencyCode: string,\n supported: boolean,\n options: FormatMoneyOptions,\n min: number,\n max: number,\n): Intl.NumberFormatOptions {\n const wholeRange = hasTrailingZeros(min) && hasTrailingZeros(max);\n const rangeOptions = {\n ...options,\n withoutTrailingZeros:\n wholeRange && (options.withoutTrailingZeros ?? !hasExplicitFractionDigits(options)),\n };\n\n return buildFormatOptions(currencyCode, supported, rangeOptions, min);\n}\n\nfunction sortMoneyRangeEntries(entries: MoneyRangeEntry[]): MoneyRangeEntry[] {\n const sorted: MoneyRangeEntry[] = [];\n\n for (const entry of entries) {\n const index = sorted.findIndex((candidate) => entry.amount < candidate.amount);\n if (index === -1) {\n sorted.push(entry);\n } else {\n sorted.splice(index, 0, entry);\n }\n }\n\n return sorted;\n}\n\n/**\n * All structured fields are computed lazily on first access and cached.\n * localizedString is the exception — it's computed eagerly in the constructor\n * so that the common `${price}` path never touches the getter machinery.\n */\nclass FormattedMoneyValue implements FormattedMoney {\n readonly #amount: number;\n readonly #options: FormatMoneyOptions;\n readonly #currencyCode: string;\n readonly #supported: boolean;\n readonly localizedString: string;\n\n readonly #defaultFormatter: Intl.NumberFormat;\n #formatterParts?: Intl.NumberFormatPart[];\n #parts?: Intl.NumberFormatPart[];\n #amountString?: string;\n #currencySymbol?: string;\n #currencyNarrowSymbol?: string;\n #currencyName?: string;\n #withoutTrailingZeros?: string;\n #withoutTrailingZerosAndCurrency?: string;\n\n constructor(money: MoneyV2, options: FormatMoneyOptions) {\n this.#amount = parseAmount(money);\n this.#options = options;\n this.#currencyCode = normalizeCurrencyCode(money.currencyCode);\n this.#supported = isWellFormedCurrencyCode(money.currencyCode);\n this.#defaultFormatter = getFormatter(\n options.locale,\n buildFormatOptions(this.#currencyCode, this.#supported, options, this.#amount),\n );\n\n const formatted = this.#defaultFormatter.format(this.#amount);\n this.localizedString =\n this.#supported || options.withoutCurrency === true\n ? formatted\n : appendUnsupportedCurrencyCode(formatted, this.#currencyCode);\n }\n\n get amount(): string {\n this.#amountString ??= extractNumericAmount(this.formatterParts);\n return this.#amountString;\n }\n\n get numericAmount(): number {\n return this.#amount;\n }\n\n get currencySymbol(): string {\n this.#currencySymbol ??= this.#supported ? findCurrencyPart(this.parts) : this.#currencyCode;\n return this.#currencySymbol;\n }\n\n get currencyNarrowSymbol(): string {\n this.#currencyNarrowSymbol ??= this.#supported\n ? findCurrencyPart(\n getFormatter(this.#options.locale, {\n style: \"currency\",\n currency: this.#currencyCode,\n currencyDisplay: \"narrowSymbol\",\n ...(this.#options.minimumFractionDigits != null && {\n minimumFractionDigits: this.#options.minimumFractionDigits,\n }),\n ...(this.#options.maximumFractionDigits != null && {\n maximumFractionDigits: this.#options.maximumFractionDigits,\n }),\n }).formatToParts(this.#amount),\n )\n : this.#currencyCode;\n return this.#currencyNarrowSymbol;\n }\n\n get currencyName(): string {\n this.#currencyName ??= this.#supported\n ? findCurrencyPart(\n getFormatter(this.#options.locale, {\n style: \"currency\",\n currency: this.#currencyCode,\n currencyDisplay: \"name\",\n ...(this.#options.minimumFractionDigits != null && {\n minimumFractionDigits: this.#options.minimumFractionDigits,\n }),\n ...(this.#options.maximumFractionDigits != null && {\n maximumFractionDigits: this.#options.maximumFractionDigits,\n }),\n }).formatToParts(this.#amount),\n ) || this.#currencyCode\n : this.#currencyCode;\n return this.#currencyName;\n }\n\n get parts(): Intl.NumberFormatPart[] {\n if (!this.#parts) {\n this.#parts =\n this.#supported || this.#options.withoutCurrency === true\n ? this.formatterParts\n : [\n ...this.formatterParts,\n { type: \"literal\" as const, value: \" \" },\n { type: \"currency\" as const, value: this.#currencyCode },\n ];\n }\n\n return this.#parts;\n }\n\n get withoutTrailingZeros(): string {\n if (!this.#withoutTrailingZeros) {\n const options = { ...this.#options, withoutTrailingZeros: true };\n const formatter = getFormatter(\n options.locale,\n buildFormatOptions(this.#currencyCode, this.#supported, options, this.#amount),\n );\n const formatted = formatter.format(this.#amount);\n this.#withoutTrailingZeros =\n this.#supported || this.#options.withoutCurrency === true\n ? formatted\n : appendUnsupportedCurrencyCode(formatted, this.#currencyCode);\n }\n\n return this.#withoutTrailingZeros;\n }\n\n get withoutTrailingZerosAndCurrency(): string {\n if (!this.#withoutTrailingZerosAndCurrency) {\n const options = { ...this.#options, withoutCurrency: true, withoutTrailingZeros: true };\n this.#withoutTrailingZerosAndCurrency = getFormatter(\n options.locale,\n buildFormatOptions(this.#currencyCode, this.#supported, options, this.#amount),\n ).format(this.#amount);\n }\n\n return this.#withoutTrailingZerosAndCurrency;\n }\n\n private get formatterParts(): Intl.NumberFormatPart[] {\n this.#formatterParts ??= this.#defaultFormatter.formatToParts(this.#amount);\n return this.#formatterParts;\n }\n\n toString(): string {\n return this.localizedString;\n }\n}\n\nclass FormattedMoneyRangeValue implements FormattedMoneyRange {\n readonly localizedString: string;\n readonly min: MoneyV2;\n readonly max: MoneyV2;\n readonly currencyCode: string;\n\n constructor(range: readonly MoneyV2[], options: FormatMoneyOptions) {\n if (range.length === 0) {\n throw new Error(\"formatMoney: money range must contain at least one value\");\n }\n\n const entries = sortMoneyRangeEntries(\n range.map((money) => ({\n money,\n amount: parseAmount(money),\n currencyCode: normalizeCurrencyCode(money.currencyCode),\n })),\n );\n\n const [first] = entries;\n if (!first) {\n throw new Error(\"formatMoney: money range must contain at least one value\");\n }\n\n const mismatched = entries.find((entry) => entry.currencyCode !== first.currencyCode);\n if (mismatched) {\n throw new Error(\n `formatMoney: range values must share one currency, received ${first.currencyCode} and ${mismatched.currencyCode}`,\n );\n }\n\n const last = entries[entries.length - 1] ?? first;\n this.min = first.money;\n this.max = last.money;\n this.currencyCode = first.currencyCode;\n\n if (first.amount === last.amount) {\n this.localizedString = new FormattedMoneyValue(first.money, {\n ...options,\n withoutTrailingZeros: options.withoutTrailingZeros ?? !hasExplicitFractionDigits(options),\n }).localizedString;\n return;\n }\n\n const supported = isWellFormedCurrencyCode(first.currencyCode);\n const formatter = getFormatter(\n options.locale,\n buildRangeOptions(first.currencyCode, supported, options, first.amount, last.amount),\n );\n\n const formatted = formatRange(formatter, first.amount, last.amount);\n this.localizedString =\n supported || options.withoutCurrency === true\n ? formatted\n : appendUnsupportedCurrencyCode(formatted, first.currencyCode);\n }\n\n toString(): string {\n return this.localizedString;\n }\n}\n\n/**\n * Formats a price or a price range for display. Pass a `MoneyV2` price, or an array of `MoneyV2` prices in one currency for a range, with the active market's locale.\n *\n * The returned object converts to the formatted string in template literals and string concatenation. For custom price layouts, read the parts of the formatted price. A range shows the lowest and highest prices. When every price in the range matches, the output shows one price.\n *\n * The function throws an error when an amount isn't numeric, when a range is empty, or when the prices in a range use different currencies.\n *\n * @publicDocs\n */\nexport function formatMoney(money: MoneyV2, options: FormatMoneyOptions): FormattedMoney;\nexport function formatMoney(\n money: readonly MoneyV2[],\n options: FormatMoneyOptions,\n): FormattedMoneyRange;\n\n// This is the implementation of the formatMoney function overloading the other two.\n// It checks if the input is a range and creates the appropriate object.\n/**\n * @param money A price, or an array of prices in one currency for a range.\n * @param options Locale and display settings.\n * @returns The formatted price or range, which converts to a string in template literals.\n */\nexport function formatMoney(\n money: MoneyV2 | readonly MoneyV2[],\n options: FormatMoneyOptions,\n): FormattedMoney | FormattedMoneyRange {\n return isMoneyRange(money)\n ? new FormattedMoneyRangeValue(money, options)\n : new FormattedMoneyValue(money, options);\n}\n"],"mappings":"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"}
|
|
@@ -1,45 +1,61 @@
|
|
|
1
1
|
//#region src/core/money/types.d.ts
|
|
2
2
|
/**
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
* that may not be in the Storefront API's enum.
|
|
3
|
+
* A price with an amount and a currency code, in the `MoneyV2` shape that the Storefront API and Customer Account API return.
|
|
4
|
+
* The currency code accepts any string, including Customer Account API currencies such as USDC
|
|
5
|
+
* that the Storefront API currency enum doesn't list.
|
|
7
6
|
*/
|
|
8
7
|
type MoneyV2 = {
|
|
9
|
-
amount: string;
|
|
8
|
+
/** Decimal amount as a string, such as `19.99`. `formatMoney` throws an error when the amount doesn't parse as a number. */amount: string;
|
|
9
|
+
/**
|
|
10
|
+
* Currency code, such as `USD`, in any letter case.
|
|
11
|
+
*
|
|
12
|
+
* A code that isn't three letters formats as a decimal amount followed by the code, such as `19.00 USDC`. An unrecognized three-letter code appears as the code, such as `XYZ 19.00`.
|
|
13
|
+
*/
|
|
10
14
|
currencyCode: string;
|
|
11
15
|
};
|
|
16
|
+
/** Locale and display settings for a formatted price or price range. */
|
|
12
17
|
type FormatMoneyOptions = {
|
|
13
|
-
/**
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
18
|
+
/**
|
|
19
|
+
* BCP 47 locale, such as `en-US`, `fr-CA`, or `ja-JP`.
|
|
20
|
+
*
|
|
21
|
+
* In market-aware storefronts, pass the active market's locale. A hardcoded `en-US` formats every market as US English.
|
|
22
|
+
*/
|
|
23
|
+
locale: string; /** Pass `true` to leave out the currency symbol or code. Defaults to `false`. */
|
|
24
|
+
withoutCurrency?: boolean;
|
|
25
|
+
/**
|
|
26
|
+
* Pass `true` to drop the fraction digits of a whole amount, such as `$19.00` to `$19`. Amounts with a fraction keep their digits.
|
|
27
|
+
*
|
|
28
|
+
* Defaults to `false`. For a range, defaults to `true` when every price is a whole amount and you set neither fraction digit option.
|
|
29
|
+
*/
|
|
30
|
+
withoutTrailingZeros?: boolean; /** Fewest fraction digits to show. Defaults to the currency's standard number of digits, such as 0 for JPY. Output without a currency symbol defaults to 2. */
|
|
31
|
+
minimumFractionDigits?: number; /** Most fraction digits to show. Defaults to the currency's standard number of digits, such as 0 for JPY. Output without a currency symbol defaults to 2. */
|
|
17
32
|
maximumFractionDigits?: number;
|
|
18
33
|
/**
|
|
19
|
-
*
|
|
20
|
-
* 'symbol' (default) | 'narrowSymbol' | 'code' | 'name'
|
|
34
|
+
* How the currency appears: `symbol`, `narrowSymbol`, `code`, or `name`. Defaults to `symbol`.
|
|
21
35
|
*
|
|
22
|
-
*
|
|
36
|
+
* In the `en-US` locale, the `symbol` value tells dollar currencies apart, such as `CA$5.00`. The `narrowSymbol` value shows `$5.00` for every dollar currency.
|
|
23
37
|
*/
|
|
24
38
|
currencyDisplay?: Intl.NumberFormatOptions["currencyDisplay"];
|
|
25
39
|
};
|
|
40
|
+
/** A formatted price, with structured parts for custom price layouts. */
|
|
26
41
|
type FormattedMoney = {
|
|
27
|
-
/**
|
|
28
|
-
amount: string; /** Currency
|
|
29
|
-
currencySymbol: string; /** Narrow symbol
|
|
30
|
-
currencyNarrowSymbol: string; /**
|
|
31
|
-
currencyName: string; /**
|
|
32
|
-
numericAmount: number; /**
|
|
33
|
-
parts: Intl.NumberFormatPart[]; /** Formatted
|
|
34
|
-
withoutTrailingZeros: string; /**
|
|
35
|
-
withoutTrailingZerosAndCurrency: string; /** Returns
|
|
42
|
+
/** Formatted price, such as `$1,299.00`. */localizedString: string; /** Formatted number without the currency, such as `1,299.00` or `-19.99`. */
|
|
43
|
+
amount: string; /** Currency part of the formatted price, such as `$`, `€`, or `CA$`. Follows the `currencyDisplay` option. For a code that isn't three letters, holds the code. For other codes, empty when `withoutCurrency` is `true`. */
|
|
44
|
+
currencySymbol: string; /** Narrow currency symbol, such as `$` for both USD and CAD. For a code that isn't three letters, holds the code. */
|
|
45
|
+
currencyNarrowSymbol: string; /** Currency name in the locale's language, such as `US dollars`. Falls back to the currency code. */
|
|
46
|
+
currencyName: string; /** Amount as a number. */
|
|
47
|
+
numericAmount: number; /** Typed pieces of the formatted price, such as the currency, integer, and fraction, for custom layouts. */
|
|
48
|
+
parts: Intl.NumberFormatPart[]; /** Formatted price without the fraction digits of a whole amount, such as `$19`. */
|
|
49
|
+
withoutTrailingZeros: string; /** Formatted amount without the currency and without the fraction digits of a whole amount, such as `19`. */
|
|
50
|
+
withoutTrailingZerosAndCurrency: string; /** Returns the formatted price. Template literals and string concatenation call the method for you. */
|
|
36
51
|
toString(): string;
|
|
37
52
|
};
|
|
53
|
+
/** A formatted price range, with its lowest and highest prices. */
|
|
38
54
|
type FormattedMoneyRange = {
|
|
39
|
-
/** The
|
|
40
|
-
min: MoneyV2; /** Highest
|
|
41
|
-
max: MoneyV2; /**
|
|
42
|
-
currencyCode: string; /** Returns
|
|
55
|
+
/** The formatted range, such as `$10 – $19`. */localizedString: string; /** Lowest price in the input. */
|
|
56
|
+
min: MoneyV2; /** Highest price in the input. */
|
|
57
|
+
max: MoneyV2; /** Currency code that every price in the range shares, in uppercase. */
|
|
58
|
+
currencyCode: string; /** Returns the formatted range. Template literals and string concatenation call the method for you. */
|
|
43
59
|
toString(): string;
|
|
44
60
|
};
|
|
45
61
|
//#endregion
|
|
@@ -1,31 +1,61 @@
|
|
|
1
1
|
//#region src/core/predictive-search/form.d.ts
|
|
2
|
-
/**
|
|
2
|
+
/**
|
|
3
|
+
* Attributes to spread on the search input of a predictive search form.
|
|
4
|
+
*
|
|
5
|
+
* @publicDocs
|
|
6
|
+
*/
|
|
3
7
|
interface PredictiveSearchQueryInputAttributes {
|
|
8
|
+
/** Sends the search term as the `q` URL parameter when the form submits. */
|
|
4
9
|
name: "q";
|
|
10
|
+
/** Renders the input as a search field. */
|
|
5
11
|
type: "search";
|
|
12
|
+
/** Turns off browser autocomplete. */
|
|
6
13
|
autoComplete: "off";
|
|
14
|
+
/** Turns off automatic capitalization of the search term. */
|
|
7
15
|
autoCapitalize: "off";
|
|
16
|
+
/** Turns off spell checking. */
|
|
8
17
|
spellCheck: false;
|
|
9
18
|
}
|
|
10
|
-
/**
|
|
19
|
+
/** Attributes to spread on a predictive search form. */
|
|
11
20
|
interface PredictiveSearchFormAttributes {
|
|
21
|
+
/** Search page path that receives the submitted search term. Defaults to `/search`. */
|
|
12
22
|
action: string;
|
|
23
|
+
/** Submits the form as a GET request, which puts the search term in the URL. */
|
|
13
24
|
method: "get";
|
|
25
|
+
/** Marks the form as a search landmark for assistive technology. */
|
|
14
26
|
role: "search";
|
|
15
27
|
}
|
|
16
28
|
/**
|
|
17
|
-
*
|
|
29
|
+
* Returns the attributes for the search input of a predictive search form.
|
|
18
30
|
*
|
|
19
|
-
*
|
|
31
|
+
* Pass `query`. Any other field name throws an error. The attribute keys use React casing. In DOM code, set each value on the matching lowercase property, such as `autocomplete`.
|
|
20
32
|
*/
|
|
21
33
|
type PredictiveSearchFormRegister = {
|
|
22
34
|
(field: "query"): PredictiveSearchQueryInputAttributes;
|
|
23
35
|
};
|
|
24
|
-
/**
|
|
36
|
+
/**
|
|
37
|
+
* Creates a function that returns the attributes for the search input of a predictive search form. Use the function in DOM code or in UI frameworks without Hydrogen bindings. In React and Vue, use `usePredictiveSearchForm`.
|
|
38
|
+
*
|
|
39
|
+
* @returns A function that takes `query` and returns the search input attributes.
|
|
40
|
+
*
|
|
41
|
+
* @publicDocs
|
|
42
|
+
*/
|
|
25
43
|
declare function createPredictiveSearchFormRegister(): PredictiveSearchFormRegister;
|
|
26
|
-
/**
|
|
44
|
+
/**
|
|
45
|
+
* Returns the attributes for a predictive search form that works without JavaScript.
|
|
46
|
+
*
|
|
47
|
+
* @param action Search page path that the form submits to. Defaults to `/search`.
|
|
48
|
+
* @returns Attributes that submit the search term to the search page as a GET request.
|
|
49
|
+
* @publicDocs
|
|
50
|
+
*/
|
|
27
51
|
declare function getPredictiveSearchFormAttributes(action?: string): PredictiveSearchFormAttributes;
|
|
28
|
-
/**
|
|
52
|
+
/**
|
|
53
|
+
* Reads the search term from a submitted predictive search form.
|
|
54
|
+
*
|
|
55
|
+
* @param formData Form data from the submitted search form.
|
|
56
|
+
* @returns The search term, or an empty string when the form data has no `q` text value.
|
|
57
|
+
* @publicDocs
|
|
58
|
+
*/
|
|
29
59
|
declare function readPredictiveSearchFormTerm(formData: FormData): string;
|
|
30
60
|
//#endregion
|
|
31
61
|
export { PredictiveSearchFormAttributes, PredictiveSearchFormRegister, PredictiveSearchQueryInputAttributes, createPredictiveSearchFormRegister, getPredictiveSearchFormAttributes, readPredictiveSearchFormTerm };
|
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
//#region src/core/predictive-search/form.ts
|
|
2
|
-
/**
|
|
2
|
+
/**
|
|
3
|
+
* Creates a function that returns the attributes for the search input of a predictive search form. Use the function in DOM code or in UI frameworks without Hydrogen bindings. In React and Vue, use `usePredictiveSearchForm`.
|
|
4
|
+
*
|
|
5
|
+
* @returns A function that takes `query` and returns the search input attributes.
|
|
6
|
+
*
|
|
7
|
+
* @publicDocs
|
|
8
|
+
*/
|
|
3
9
|
function createPredictiveSearchFormRegister() {
|
|
4
10
|
return registerPredictiveSearchFormField;
|
|
5
11
|
}
|
|
@@ -13,7 +19,13 @@ function registerPredictiveSearchFormField(field) {
|
|
|
13
19
|
spellCheck: false
|
|
14
20
|
};
|
|
15
21
|
}
|
|
16
|
-
/**
|
|
22
|
+
/**
|
|
23
|
+
* Returns the attributes for a predictive search form that works without JavaScript.
|
|
24
|
+
*
|
|
25
|
+
* @param action Search page path that the form submits to. Defaults to `/search`.
|
|
26
|
+
* @returns Attributes that submit the search term to the search page as a GET request.
|
|
27
|
+
* @publicDocs
|
|
28
|
+
*/
|
|
17
29
|
function getPredictiveSearchFormAttributes(action = "/search") {
|
|
18
30
|
return {
|
|
19
31
|
action,
|
|
@@ -21,7 +33,13 @@ function getPredictiveSearchFormAttributes(action = "/search") {
|
|
|
21
33
|
role: "search"
|
|
22
34
|
};
|
|
23
35
|
}
|
|
24
|
-
/**
|
|
36
|
+
/**
|
|
37
|
+
* Reads the search term from a submitted predictive search form.
|
|
38
|
+
*
|
|
39
|
+
* @param formData Form data from the submitted search form.
|
|
40
|
+
* @returns The search term, or an empty string when the form data has no `q` text value.
|
|
41
|
+
* @publicDocs
|
|
42
|
+
*/
|
|
25
43
|
function readPredictiveSearchFormTerm(formData) {
|
|
26
44
|
const value = formData.get("q");
|
|
27
45
|
return typeof value === "string" ? value : "";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"form.mjs","names":[],"sources":["../../../src/core/predictive-search/form.ts"],"sourcesContent":["import { PREDICTIVE_SEARCH_QUERY_PARAM } from \"./constants\";\n\n
|
|
1
|
+
{"version":3,"file":"form.mjs","names":[],"sources":["../../../src/core/predictive-search/form.ts"],"sourcesContent":["import { PREDICTIVE_SEARCH_QUERY_PARAM } from \"./constants\";\n\n/**\n * Attributes to spread on the search input of a predictive search form.\n *\n * @publicDocs\n */\nexport interface PredictiveSearchQueryInputAttributes {\n /** Sends the search term as the `q` URL parameter when the form submits. */\n name: \"q\";\n /** Renders the input as a search field. */\n type: \"search\";\n /** Turns off browser autocomplete. */\n autoComplete: \"off\";\n /** Turns off automatic capitalization of the search term. */\n autoCapitalize: \"off\";\n /** Turns off spell checking. */\n spellCheck: false;\n}\n\n/** Attributes to spread on a predictive search form. */\nexport interface PredictiveSearchFormAttributes {\n /** Search page path that receives the submitted search term. Defaults to `/search`. */\n action: string;\n /** Submits the form as a GET request, which puts the search term in the URL. */\n method: \"get\";\n /** Marks the form as a search landmark for assistive technology. */\n role: \"search\";\n}\n\n/**\n * Returns the attributes for the search input of a predictive search form.\n *\n * Pass `query`. Any other field name throws an error. The attribute keys use React casing. In DOM code, set each value on the matching lowercase property, such as `autocomplete`.\n */\nexport type PredictiveSearchFormRegister = {\n (field: \"query\"): PredictiveSearchQueryInputAttributes;\n};\n\n/**\n * Creates a function that returns the attributes for the search input of a predictive search form. Use the function in DOM code or in UI frameworks without Hydrogen bindings. In React and Vue, use `usePredictiveSearchForm`.\n *\n * @returns A function that takes `query` and returns the search input attributes.\n *\n * @publicDocs\n */\nexport function createPredictiveSearchFormRegister(): PredictiveSearchFormRegister {\n return registerPredictiveSearchFormField;\n}\n\nfunction registerPredictiveSearchFormField(field: string): PredictiveSearchQueryInputAttributes {\n if (field !== \"query\") {\n throw new Error(`Unknown predictive search form field: \"${field}\".`);\n }\n\n return {\n name: \"q\",\n type: \"search\",\n autoComplete: \"off\",\n autoCapitalize: \"off\",\n spellCheck: false,\n };\n}\n\n/**\n * Returns the attributes for a predictive search form that works without JavaScript.\n *\n * @param action Search page path that the form submits to. Defaults to `/search`.\n * @returns Attributes that submit the search term to the search page as a GET request.\n * @publicDocs\n */\nexport function getPredictiveSearchFormAttributes(\n action: string = \"/search\",\n): PredictiveSearchFormAttributes {\n return {\n action,\n method: \"get\",\n role: \"search\",\n };\n}\n\n/**\n * Reads the search term from a submitted predictive search form.\n *\n * @param formData Form data from the submitted search form.\n * @returns The search term, or an empty string when the form data has no `q` text value.\n * @publicDocs\n */\nexport function readPredictiveSearchFormTerm(formData: FormData): string {\n const value = formData.get(PREDICTIVE_SEARCH_QUERY_PARAM);\n return typeof value === \"string\" ? value : \"\";\n}\n"],"mappings":";;;;;;;;AA8CA,SAAgB,qCAAmE;AACjF,QAAO;;AAGT,SAAS,kCAAkC,OAAqD;AAC9F,KAAI,UAAU,QACZ,OAAM,IAAI,MAAM,0CAA0C,MAAM,IAAI;AAGtE,QAAO;EACL,MAAM;EACN,MAAM;EACN,cAAc;EACd,gBAAgB;EAChB,YAAY;EACb;;;;;;;;;AAUH,SAAgB,kCACd,SAAiB,WACe;AAChC,QAAO;EACL;EACA,QAAQ;EACR,MAAM;EACP;;;;;;;;;AAUH,SAAgB,6BAA6B,UAA4B;CACvE,MAAM,QAAQ,SAAS,QAAkC;AACzD,QAAO,OAAO,UAAU,WAAW,QAAQ"}
|
|
@@ -92,61 +92,49 @@ declare const DEFAULT_QUERY_FRAGMENT: StorefrontQueryString<{
|
|
|
92
92
|
text: string;
|
|
93
93
|
}, never, "\n fragment PredictiveSearchQueryFragment on SearchQuerySuggestion {\n text\n }\n">;
|
|
94
94
|
/**
|
|
95
|
-
*
|
|
95
|
+
* GraphQL fragments that add fields to each type of predictive search result.
|
|
96
96
|
*
|
|
97
|
-
* Each fragment
|
|
98
|
-
*
|
|
99
|
-
*
|
|
100
|
-
* - `product`: `fragment PredictiveSearchProductFragment on Product`
|
|
101
|
-
* - `collection`: `fragment PredictiveSearchCollectionFragment on Collection`
|
|
102
|
-
* - `page`: `fragment PredictiveSearchPageFragment on Page`
|
|
103
|
-
* - `article`: `fragment PredictiveSearchArticleFragment on Article`
|
|
104
|
-
* - `query`: `fragment PredictiveSearchQueryFragment on SearchQuerySuggestion`
|
|
105
|
-
*
|
|
106
|
-
* Hydrogen's own fields (used for URL generation, tracking, and display) are
|
|
107
|
-
* always selected; custom fragments are merged alongside them.
|
|
97
|
+
* Each fragment needs the name and type that its option lists, or `makePredictiveSearchQueries` throws an error.
|
|
98
|
+
* Results keep Hydrogen's built-in fields for links, tracking, and display.
|
|
108
99
|
*/
|
|
109
100
|
type PredictiveSearchFragments = {
|
|
110
|
-
readonly product?: AnyStorefrontQueryString;
|
|
111
|
-
readonly collection?: AnyStorefrontQueryString;
|
|
112
|
-
readonly page?: AnyStorefrontQueryString;
|
|
113
|
-
readonly article?: AnyStorefrontQueryString;
|
|
101
|
+
/** Adds fields to product results. Start the fragment with `fragment PredictiveSearchProductFragment on Product`. */readonly product?: AnyStorefrontQueryString; /** Adds fields to collection results. Start the fragment with `fragment PredictiveSearchCollectionFragment on Collection`. */
|
|
102
|
+
readonly collection?: AnyStorefrontQueryString; /** Adds fields to page results. Start the fragment with `fragment PredictiveSearchPageFragment on Page`. */
|
|
103
|
+
readonly page?: AnyStorefrontQueryString; /** Adds fields to article results. Start the fragment with `fragment PredictiveSearchArticleFragment on Article`. */
|
|
104
|
+
readonly article?: AnyStorefrontQueryString; /** Adds fields to query suggestions. Start the fragment with `fragment PredictiveSearchQueryFragment on SearchQuerySuggestion`. */
|
|
114
105
|
readonly query?: AnyStorefrontQueryString;
|
|
115
106
|
};
|
|
116
|
-
/** Options for
|
|
107
|
+
/** Options for a custom predictive search query. */
|
|
117
108
|
type CreatePredictiveSearchQueriesOptions<TFragments extends PredictiveSearchFragments = PredictiveSearchFragments> = {
|
|
118
|
-
/**
|
|
109
|
+
/** Fragments that add fields to each result type. Results keep Hydrogen's built-in fields. */readonly fragments?: TFragments;
|
|
119
110
|
};
|
|
120
111
|
type FragmentForOptions<TOptions, TKey extends keyof PredictiveSearchFragments, TDefault extends AnyStorefrontQueryString> = TOptions extends {
|
|
121
112
|
readonly fragments: infer TFragments;
|
|
122
113
|
} ? TFragments extends Record<TKey, infer TFragment extends AnyStorefrontQueryString> ? TFragment : TDefault : TDefault;
|
|
123
114
|
type PredictiveSearchQueryFragmentsForOptions<TOptions> = [FragmentForOptions<TOptions, "product", typeof DEFAULT_PRODUCT_FRAGMENT>, FragmentForOptions<TOptions, "collection", typeof DEFAULT_COLLECTION_FRAGMENT>, FragmentForOptions<TOptions, "page", typeof DEFAULT_PAGE_FRAGMENT>, FragmentForOptions<TOptions, "article", typeof DEFAULT_ARTICLE_FRAGMENT>, FragmentForOptions<TOptions, "query", typeof DEFAULT_QUERY_FRAGMENT>];
|
|
124
115
|
type PredictiveSearchQuerySourceForOptions<TOptions> = ComposedSource<SourceOf<typeof PREDICTIVE_SEARCH_QUERY>, PredictiveSearchQueryFragmentsForOptions<TOptions>>;
|
|
116
|
+
/** Type of a custom predictive search query, with result types from your fragments. */
|
|
125
117
|
type PredictiveSearchQueryForOptions<TOptions> = StorefrontQueryString<InferResult<PredictiveSearchQuerySourceForOptions<TOptions>>, InferVariables<PredictiveSearchQuerySourceForOptions<TOptions>>, PredictiveSearchQuerySourceForOptions<TOptions>>;
|
|
126
|
-
/**
|
|
118
|
+
/** Type of the object that makePredictiveSearchQueries returns, with result types from your fragments. */
|
|
127
119
|
type PredictiveSearchQueriesForOptions<TOptions> = {
|
|
128
120
|
readonly predictiveSearch: PredictiveSearchQueryForOptions<TOptions>;
|
|
129
121
|
};
|
|
130
122
|
/**
|
|
131
|
-
*
|
|
132
|
-
* base query and either custom or default fragments.
|
|
133
|
-
*
|
|
134
|
-
* Custom fragments are validated at runtime against their expected names
|
|
135
|
-
* and target GraphQL types. See {@link PredictiveSearchFragments} for the
|
|
136
|
-
* naming contract.
|
|
123
|
+
* Builds a predictive search query that adds the fields from your fragments to the results.
|
|
137
124
|
*
|
|
138
|
-
*
|
|
139
|
-
*
|
|
125
|
+
* Pass the query to `queryPredictiveSearch`, or pass the same fragments to `createPredictiveSearchServerHandlers`. TypeScript types the results from your fragments.
|
|
126
|
+
* The function throws an error when a fragment doesn't use its required name and type.
|
|
140
127
|
*
|
|
141
128
|
* @throws {Error} When a custom fragment does not match its required name or target type.
|
|
129
|
+
* @publicDocs
|
|
142
130
|
*/
|
|
143
131
|
declare function makePredictiveSearchQueries<const TOptions extends CreatePredictiveSearchQueriesOptions>(options: TOptions): PredictiveSearchQueriesForOptions<TOptions>;
|
|
144
132
|
declare function makePredictiveSearchQueries(): PredictiveSearchQueriesForOptions<undefined>;
|
|
145
133
|
/**
|
|
146
|
-
*
|
|
147
|
-
*
|
|
134
|
+
* Hydrogen's built-in predictive search query, under `predictiveSearch`.
|
|
135
|
+
* queryPredictiveSearch uses the query when you omit the `query` option.
|
|
148
136
|
*
|
|
149
|
-
*
|
|
137
|
+
* To add fields to the results, use makePredictiveSearchQueries.
|
|
150
138
|
*/
|
|
151
139
|
declare const predictiveSearchQueries: PredictiveSearchQueriesForOptions<undefined>;
|
|
152
140
|
//#endregion
|
|
@@ -71,14 +71,18 @@ function resolveFragments(fragments) {
|
|
|
71
71
|
fragments?.query ?? DEFAULT_QUERY_FRAGMENT
|
|
72
72
|
];
|
|
73
73
|
}
|
|
74
|
+
/**
|
|
75
|
+
* @param options Fragments that add fields to each result type.
|
|
76
|
+
* @returns An object with the query under `predictiveSearch`.
|
|
77
|
+
*/
|
|
74
78
|
function makePredictiveSearchQueries(options) {
|
|
75
79
|
return { predictiveSearch: gql(PREDICTIVE_SEARCH_QUERY, resolveFragments(options?.fragments)) };
|
|
76
80
|
}
|
|
77
81
|
/**
|
|
78
|
-
*
|
|
79
|
-
*
|
|
82
|
+
* Hydrogen's built-in predictive search query, under `predictiveSearch`.
|
|
83
|
+
* queryPredictiveSearch uses the query when you omit the `query` option.
|
|
80
84
|
*
|
|
81
|
-
*
|
|
85
|
+
* To add fields to the results, use makePredictiveSearchQueries.
|
|
82
86
|
*/
|
|
83
87
|
const predictiveSearchQueries = makePredictiveSearchQueries();
|
|
84
88
|
|