@shopify/hydrogen 2026.10.0-preview.2 → 2026.10.0-preview.4
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/README.md +34 -3
- package/dist/cli/index.mjs +578 -85
- package/dist/client/client.mjs +2 -2
- package/dist/client/errors.d.mts +19 -19
- package/dist/client/errors.mjs +19 -16
- package/dist/client/errors.mjs.map +1 -1
- package/dist/client/types.d.mts +91 -10
- package/dist/core/analytics/cart-tracker.d.mts +14 -0
- package/dist/core/analytics/cart-tracker.mjs +15 -1
- package/dist/core/analytics/cart-tracker.mjs.map +1 -1
- package/dist/core/analytics/custom-consent.mjs +12 -0
- package/dist/core/analytics/custom-consent.mjs.map +1 -0
- package/dist/core/analytics/events.d.mts +7 -0
- package/dist/core/analytics/events.mjs +7 -0
- package/dist/core/analytics/events.mjs.map +1 -1
- package/dist/core/analytics/index.d.mts +1 -1
- package/dist/core/analytics/types.d.mts +152 -16
- package/dist/core/cart/actions.d.mts +47 -3
- package/dist/core/cart/actions.mjs +29 -0
- package/dist/core/cart/actions.mjs.map +1 -1
- package/dist/core/cart/attach-quantity-input.d.mts +26 -0
- package/dist/core/cart/attach-quantity-input.mjs +26 -0
- package/dist/core/cart/attach-quantity-input.mjs.map +1 -1
- package/dist/core/cart/cart.d.mts +106 -6
- package/dist/core/cart/cart.mjs +57 -6
- package/dist/core/cart/cart.mjs.map +1 -1
- package/dist/core/cart/cookie.d.mts +15 -0
- package/dist/core/cart/cookie.mjs +15 -0
- package/dist/core/cart/cookie.mjs.map +1 -1
- package/dist/core/cart/form.d.mts +61 -0
- package/dist/core/cart/form.mjs +18 -0
- package/dist/core/cart/form.mjs.map +1 -1
- package/dist/core/cart/get-cart.d.mts +43 -2
- package/dist/core/cart/get-cart.mjs +33 -0
- package/dist/core/cart/get-cart.mjs.map +1 -1
- package/dist/core/cart/index.d.mts +1 -1
- package/dist/core/cart/quantity.d.mts +17 -0
- package/dist/core/cart/quantity.mjs +17 -0
- package/dist/core/cart/quantity.mjs.map +1 -1
- package/dist/core/cart/queries.d.mts +402 -369
- package/dist/core/cart/queries.mjs +2 -1
- package/dist/core/cart/queries.mjs.map +1 -1
- package/dist/core/cart/server-handlers.d.mts +69 -0
- package/dist/core/cart/server-handlers.mjs +2 -10
- package/dist/core/cart/server-handlers.mjs.map +1 -1
- package/dist/core/cart/state.d.mts +133 -0
- package/dist/core/cart/state.mjs +8 -0
- package/dist/core/cart/state.mjs.map +1 -1
- package/dist/core/collection/collection.d.mts +18 -2
- package/dist/core/collection/collection.mjs +11 -0
- package/dist/core/collection/collection.mjs.map +1 -1
- package/dist/core/collection/index.d.mts +1 -1
- package/dist/core/collection/reconciler.d.mts +13 -4
- package/dist/core/collection/reconciler.mjs.map +1 -1
- package/dist/core/collection/state.d.mts +7 -2
- package/dist/core/collection/state.mjs.map +1 -1
- package/dist/core/collection/url.d.mts +3 -0
- package/dist/core/collection/url.mjs.map +1 -1
- package/dist/core/constants.d.mts +1 -1
- package/dist/core/constants.mjs +2 -2
- package/dist/core/constants.mjs.map +1 -1
- package/dist/core/headers.mjs +2 -2
- package/dist/core/headers.mjs.map +1 -1
- package/dist/core/index.d.mts +5 -5
- package/dist/core/logging/logging.mjs.map +1 -1
- package/dist/core/money/format.mjs +6 -3
- package/dist/core/money/format.mjs.map +1 -1
- package/dist/core/observable.mjs +0 -3
- package/dist/core/observable.mjs.map +1 -1
- package/dist/core/predictive-search/form.d.mts +1 -0
- package/dist/core/predictive-search/form.mjs +1 -0
- package/dist/core/predictive-search/form.mjs.map +1 -1
- package/dist/core/predictive-search/index.d.mts +1 -1
- package/dist/core/predictive-search/queries.d.mts +40 -4
- package/dist/core/predictive-search/queries.mjs +7 -1
- package/dist/core/predictive-search/queries.mjs.map +1 -1
- package/dist/core/predictive-search/search.d.mts +43 -10
- package/dist/core/predictive-search/search.mjs +34 -0
- package/dist/core/predictive-search/search.mjs.map +1 -1
- package/dist/core/predictive-search/server-handlers.d.mts +31 -5
- package/dist/core/predictive-search/server-handlers.mjs +5 -3
- package/dist/core/predictive-search/server-handlers.mjs.map +1 -1
- package/dist/core/predictive-search/store.d.mts +3 -1
- package/dist/core/predictive-search/store.mjs.map +1 -1
- package/dist/core/predictive-search/url.d.mts +50 -8
- package/dist/core/predictive-search/url.mjs +9 -0
- package/dist/core/predictive-search/url.mjs.map +1 -1
- package/dist/core/product/form.d.mts +21 -2
- package/dist/core/product/form.mjs +13 -0
- package/dist/core/product/form.mjs.map +1 -1
- package/dist/core/product/index.d.mts +1 -1
- package/dist/core/product/options.d.mts +2 -2
- package/dist/core/product/options.mjs +2 -2
- package/dist/core/product/options.mjs.map +1 -1
- package/dist/core/product/product-form.d.mts +33 -1
- package/dist/core/product/product-form.mjs +30 -0
- package/dist/core/product/product-form.mjs.map +1 -1
- package/dist/core/product/state.d.mts +41 -0
- package/dist/core/product/url.d.mts +6 -0
- package/dist/core/product/url.mjs.map +1 -1
- package/dist/core/request-context.d.mts +8 -5
- package/dist/core/request-context.mjs +17 -39
- package/dist/core/request-context.mjs.map +1 -1
- package/dist/core/request-routing/handle-shopify-routes.mjs +4 -1
- package/dist/core/request-routing/handle-shopify-routes.mjs.map +1 -1
- package/dist/core/request-routing/interceptors/ajax-api.mjs +1 -1
- package/dist/core/request-routing/interceptors/api-proxy.mjs +1 -1
- package/dist/core/request-routing/interceptors/checkout.mjs +22 -6
- package/dist/core/request-routing/interceptors/checkout.mjs.map +1 -1
- package/dist/core/request-routing/interceptors/mcp-proxy.mjs +1 -1
- package/dist/core/request-routing/interceptors/proxy.mjs +36 -10
- package/dist/core/request-routing/interceptors/proxy.mjs.map +1 -1
- package/dist/core/request-routing/interceptors/query-param-redirect.mjs +5 -11
- package/dist/core/request-routing/interceptors/query-param-redirect.mjs.map +1 -1
- package/dist/core/request-routing/interceptors/sfapi-proxy.mjs +19 -1
- package/dist/core/request-routing/interceptors/sfapi-proxy.mjs.map +1 -1
- package/dist/core/request-routing/interceptors/ucp-mcp-proxy.mjs +35 -0
- package/dist/core/request-routing/interceptors/ucp-mcp-proxy.mjs.map +1 -0
- package/dist/core/request-routing/interceptors/well-known.mjs +1 -1
- package/dist/core/shopify-scripts/analytics.mjs +2 -2
- package/dist/core/shopify-scripts/analytics.mjs.map +1 -1
- package/dist/core/shopify-scripts/constants.mjs +4 -2
- package/dist/core/shopify-scripts/constants.mjs.map +1 -1
- package/dist/core/shopify-scripts/global.mjs +9 -4
- package/dist/core/shopify-scripts/global.mjs.map +1 -1
- package/dist/core/shopify-scripts/index.d.mts +1 -0
- package/dist/core/shopify-scripts/index.mjs +14 -12
- package/dist/core/shopify-scripts/index.mjs.map +1 -1
- package/dist/core/shopify-scripts/initialize.d.mts +1 -0
- package/dist/core/shopify-scripts/initialize.mjs +10 -3
- package/dist/core/shopify-scripts/initialize.mjs.map +1 -1
- package/dist/core/shopify-scripts/types.d.mts +6 -0
- package/dist/core/url.mjs +21 -2
- package/dist/core/url.mjs.map +1 -1
- package/dist/customer-account/client.d.mts +128 -4
- package/dist/customer-account/client.mjs +27 -1
- package/dist/customer-account/client.mjs.map +1 -1
- package/dist/customer-account/errors.d.mts +46 -0
- package/dist/customer-account/errors.mjs +46 -0
- package/dist/customer-account/errors.mjs.map +1 -1
- package/dist/customer-account/graphql.d.mts +68 -0
- package/dist/customer-account/graphql.mjs +47 -0
- package/dist/customer-account/graphql.mjs.map +1 -1
- package/dist/customer-account/session.d.mts +114 -23
- package/dist/customer-account/session.mjs +27 -12
- package/dist/customer-account/session.mjs.map +1 -1
- package/dist/customer-account/type-resolver.d.mts +23 -0
- package/dist/customer-account-api-types.d.ts +570 -374
- package/dist/customer-account.schema.json +2981 -1527
- package/dist/development/client/client.mjs +2 -2
- package/dist/development/client/errors.d.mts +19 -19
- package/dist/development/client/errors.mjs +20 -20
- package/dist/development/client/errors.mjs.map +1 -1
- package/dist/development/client/types.d.mts +91 -10
- package/dist/development/core/analytics/cart-tracker.d.mts +14 -0
- package/dist/development/core/analytics/cart-tracker.mjs +15 -1
- package/dist/development/core/analytics/cart-tracker.mjs.map +1 -1
- package/dist/development/core/analytics/custom-consent.mjs +12 -0
- package/dist/development/core/analytics/custom-consent.mjs.map +1 -0
- package/dist/development/core/analytics/events.d.mts +7 -0
- package/dist/development/core/analytics/events.mjs +7 -0
- package/dist/development/core/analytics/events.mjs.map +1 -1
- package/dist/development/core/analytics/index.d.mts +1 -1
- package/dist/development/core/analytics/types.d.mts +152 -16
- package/dist/development/core/cart/actions.d.mts +47 -3
- package/dist/development/core/cart/actions.mjs +29 -0
- package/dist/development/core/cart/actions.mjs.map +1 -1
- package/dist/development/core/cart/attach-quantity-input.d.mts +26 -0
- package/dist/development/core/cart/attach-quantity-input.mjs +26 -0
- package/dist/development/core/cart/attach-quantity-input.mjs.map +1 -1
- package/dist/development/core/cart/cart.d.mts +106 -6
- package/dist/development/core/cart/cart.mjs +57 -6
- package/dist/development/core/cart/cart.mjs.map +1 -1
- package/dist/development/core/cart/cookie.d.mts +15 -0
- package/dist/development/core/cart/cookie.mjs +15 -0
- package/dist/development/core/cart/cookie.mjs.map +1 -1
- package/dist/development/core/cart/form.d.mts +61 -0
- package/dist/development/core/cart/form.mjs +18 -0
- package/dist/development/core/cart/form.mjs.map +1 -1
- package/dist/development/core/cart/get-cart.d.mts +43 -2
- package/dist/development/core/cart/get-cart.mjs +33 -0
- package/dist/development/core/cart/get-cart.mjs.map +1 -1
- package/dist/development/core/cart/index.d.mts +1 -1
- package/dist/development/core/cart/quantity.d.mts +17 -0
- package/dist/development/core/cart/quantity.mjs +17 -0
- package/dist/development/core/cart/quantity.mjs.map +1 -1
- package/dist/development/core/cart/queries.d.mts +345 -312
- package/dist/development/core/cart/queries.mjs +2 -1
- package/dist/development/core/cart/queries.mjs.map +1 -1
- package/dist/development/core/cart/server-handlers.d.mts +69 -0
- package/dist/development/core/cart/server-handlers.mjs +2 -10
- package/dist/development/core/cart/server-handlers.mjs.map +1 -1
- package/dist/development/core/cart/state.d.mts +133 -0
- package/dist/development/core/cart/state.mjs +8 -0
- package/dist/development/core/cart/state.mjs.map +1 -1
- package/dist/development/core/collection/collection.d.mts +18 -2
- package/dist/development/core/collection/collection.mjs +11 -0
- package/dist/development/core/collection/collection.mjs.map +1 -1
- package/dist/development/core/collection/index.d.mts +1 -1
- package/dist/development/core/collection/reconciler.d.mts +13 -4
- package/dist/development/core/collection/reconciler.mjs.map +1 -1
- package/dist/development/core/collection/state.d.mts +7 -2
- package/dist/development/core/collection/state.mjs.map +1 -1
- package/dist/development/core/collection/url.d.mts +3 -0
- package/dist/development/core/collection/url.mjs.map +1 -1
- package/dist/development/core/constants.mjs +1 -1
- package/dist/development/core/constants.mjs.map +1 -1
- package/dist/development/core/development.d.mts +5 -5
- package/dist/development/core/headers.mjs +2 -2
- package/dist/development/core/headers.mjs.map +1 -1
- package/dist/development/core/index.d.mts +4 -4
- package/dist/development/core/logging/logging.mjs.map +1 -1
- package/dist/development/core/money/format.mjs +6 -3
- package/dist/development/core/money/format.mjs.map +1 -1
- package/dist/development/core/observable.mjs +0 -3
- package/dist/development/core/observable.mjs.map +1 -1
- package/dist/development/core/predictive-search/form.d.mts +1 -0
- package/dist/development/core/predictive-search/form.mjs +1 -0
- package/dist/development/core/predictive-search/form.mjs.map +1 -1
- package/dist/development/core/predictive-search/index.d.mts +1 -1
- package/dist/development/core/predictive-search/queries.d.mts +38 -2
- package/dist/development/core/predictive-search/queries.mjs +7 -1
- package/dist/development/core/predictive-search/queries.mjs.map +1 -1
- package/dist/development/core/predictive-search/search.d.mts +43 -10
- package/dist/development/core/predictive-search/search.mjs +34 -0
- package/dist/development/core/predictive-search/search.mjs.map +1 -1
- package/dist/development/core/predictive-search/server-handlers.d.mts +31 -5
- package/dist/development/core/predictive-search/server-handlers.mjs +5 -3
- package/dist/development/core/predictive-search/server-handlers.mjs.map +1 -1
- package/dist/development/core/predictive-search/store.d.mts +3 -1
- package/dist/development/core/predictive-search/store.mjs.map +1 -1
- package/dist/development/core/predictive-search/url.d.mts +50 -8
- package/dist/development/core/predictive-search/url.mjs +9 -0
- package/dist/development/core/predictive-search/url.mjs.map +1 -1
- package/dist/development/core/product/form.d.mts +21 -2
- package/dist/development/core/product/form.mjs +13 -0
- package/dist/development/core/product/form.mjs.map +1 -1
- package/dist/development/core/product/index.d.mts +1 -1
- package/dist/development/core/product/options.d.mts +2 -2
- package/dist/development/core/product/options.mjs +2 -2
- package/dist/development/core/product/options.mjs.map +1 -1
- package/dist/development/core/product/product-form.d.mts +33 -1
- package/dist/development/core/product/product-form.mjs +30 -0
- package/dist/development/core/product/product-form.mjs.map +1 -1
- package/dist/development/core/product/state.d.mts +41 -0
- package/dist/development/core/product/url.d.mts +6 -0
- package/dist/development/core/product/url.mjs.map +1 -1
- package/dist/development/core/request-context.d.mts +8 -5
- package/dist/development/core/request-context.mjs +17 -39
- package/dist/development/core/request-context.mjs.map +1 -1
- package/dist/development/core/request-routing/handle-shopify-routes.mjs +4 -1
- package/dist/development/core/request-routing/handle-shopify-routes.mjs.map +1 -1
- package/dist/development/core/request-routing/interceptors/ajax-api.mjs +1 -1
- package/dist/development/core/request-routing/interceptors/api-proxy.mjs +1 -1
- package/dist/development/core/request-routing/interceptors/checkout.mjs +22 -6
- package/dist/development/core/request-routing/interceptors/checkout.mjs.map +1 -1
- package/dist/development/core/request-routing/interceptors/mcp-proxy.mjs +1 -1
- package/dist/development/core/request-routing/interceptors/proxy.mjs +36 -10
- package/dist/development/core/request-routing/interceptors/proxy.mjs.map +1 -1
- package/dist/development/core/request-routing/interceptors/query-param-redirect.mjs +5 -11
- package/dist/development/core/request-routing/interceptors/query-param-redirect.mjs.map +1 -1
- package/dist/development/core/request-routing/interceptors/sfapi-proxy.mjs +19 -1
- package/dist/development/core/request-routing/interceptors/sfapi-proxy.mjs.map +1 -1
- package/dist/development/core/request-routing/interceptors/ucp-mcp-proxy.mjs +35 -0
- package/dist/development/core/request-routing/interceptors/ucp-mcp-proxy.mjs.map +1 -0
- package/dist/development/core/request-routing/interceptors/well-known.mjs +1 -1
- package/dist/development/core/shopify-scripts/analytics.mjs +2 -2
- package/dist/development/core/shopify-scripts/analytics.mjs.map +1 -1
- package/dist/development/core/shopify-scripts/constants.mjs +4 -2
- package/dist/development/core/shopify-scripts/constants.mjs.map +1 -1
- package/dist/development/core/shopify-scripts/global.mjs +9 -4
- package/dist/development/core/shopify-scripts/global.mjs.map +1 -1
- package/dist/development/core/shopify-scripts/index.d.mts +1 -0
- package/dist/development/core/shopify-scripts/index.mjs +14 -12
- package/dist/development/core/shopify-scripts/index.mjs.map +1 -1
- package/dist/development/core/shopify-scripts/initialize.d.mts +1 -0
- package/dist/development/core/shopify-scripts/initialize.mjs +10 -3
- package/dist/development/core/shopify-scripts/initialize.mjs.map +1 -1
- package/dist/development/core/shopify-scripts/types.d.mts +6 -0
- package/dist/development/core/url.mjs +21 -2
- package/dist/development/core/url.mjs.map +1 -1
- package/dist/development/customer-account/session.d.mts +69 -13
- package/dist/development/customer-account/session.mjs +4 -0
- package/dist/development/customer-account/session.mjs.map +1 -1
- package/dist/development/globals.d.mts +6 -5
- package/dist/development/graphql/generated/graphql-env.d.mts +459 -16
- package/dist/development/graphql/generated/storefront-api-types.d.mts +1 -1
- package/dist/development/graphql/graphql.d.mts +33 -0
- package/dist/development/graphql/graphql.mjs +30 -0
- package/dist/development/graphql/graphql.mjs.map +1 -1
- package/dist/development/graphql/type-resolver.d.mts +14 -0
- package/dist/development/react/cart.d.mts +85 -14
- package/dist/development/react/cart.mjs +123 -3
- package/dist/development/react/cart.mjs.map +1 -1
- package/dist/development/react/collection.d.mts +2 -4
- package/dist/development/react/collection.mjs.map +1 -1
- package/dist/development/react/index.d.mts +8 -6
- package/dist/development/react/index.mjs +2 -2
- package/dist/development/react/predictive-search.d.mts +68 -7
- package/dist/development/react/predictive-search.mjs +54 -12
- package/dist/development/react/predictive-search.mjs.map +1 -1
- package/dist/development/react/product.d.mts +1 -0
- package/dist/development/react/product.mjs +5 -2
- package/dist/development/react/product.mjs.map +1 -1
- package/dist/development/react/shopify-scripts.mjs +1 -0
- package/dist/development/react/shopify-scripts.mjs.map +1 -1
- package/dist/development/vue/cart.d.mts +85 -6
- package/dist/development/vue/cart.mjs +95 -0
- package/dist/development/vue/cart.mjs.map +1 -1
- package/dist/development/vue/collection.d.mts +83 -5
- package/dist/development/vue/collection.mjs +52 -0
- package/dist/development/vue/collection.mjs.map +1 -1
- package/dist/development/vue/index.d.mts +6 -4
- package/dist/development/vue/predictive-search.d.mts +53 -6
- package/dist/development/vue/predictive-search.mjs +33 -0
- package/dist/development/vue/predictive-search.mjs.map +1 -1
- package/dist/development/vue/product.d.mts +6 -2
- package/dist/development/vue/product.mjs.map +1 -1
- package/dist/development/vue/shopify-scripts.d.mts +30 -3
- package/dist/development/vue/shopify-scripts.mjs +6 -0
- package/dist/development/vue/shopify-scripts.mjs.map +1 -1
- package/dist/globals.d.mts +6 -5
- package/dist/graphql/generated/customer-account-graphql-env.d.mts +1717 -1090
- package/dist/graphql/generated/graphql-env.d.mts +459 -16
- package/dist/graphql/generated/storefront-api-types.d.mts +1 -1
- package/dist/graphql/graphql.d.mts +33 -0
- package/dist/graphql/graphql.mjs +30 -0
- package/dist/graphql/graphql.mjs.map +1 -1
- package/dist/graphql/type-resolver.d.mts +14 -0
- package/dist/react/cart.d.mts +85 -14
- package/dist/react/cart.mjs +123 -3
- package/dist/react/cart.mjs.map +1 -1
- package/dist/react/collection.d.mts +2 -4
- package/dist/react/collection.mjs.map +1 -1
- package/dist/react/index.d.mts +8 -6
- package/dist/react/index.mjs +2 -2
- package/dist/react/predictive-search.d.mts +68 -7
- package/dist/react/predictive-search.mjs +54 -12
- package/dist/react/predictive-search.mjs.map +1 -1
- package/dist/react/product.d.mts +1 -0
- package/dist/react/product.mjs +5 -2
- package/dist/react/product.mjs.map +1 -1
- package/dist/react/shopify-scripts.mjs +1 -0
- package/dist/react/shopify-scripts.mjs.map +1 -1
- package/dist/storefront-api-types.d.ts +165 -20
- package/dist/storefront.schema.json +782 -57
- package/dist/vite/customer-account.mjs +191 -0
- package/dist/vite/customer-account.mjs.map +1 -0
- package/dist/vite/local-https.mjs +13 -29
- package/dist/vite/local-https.mjs.map +1 -1
- package/dist/vite/mkcert.mjs +1 -1
- package/dist/vue/cart.d.mts +85 -6
- package/dist/vue/cart.mjs +95 -0
- package/dist/vue/cart.mjs.map +1 -1
- package/dist/vue/collection.d.mts +83 -5
- package/dist/vue/collection.mjs +52 -0
- package/dist/vue/collection.mjs.map +1 -1
- package/dist/vue/index.d.mts +6 -4
- package/dist/vue/predictive-search.d.mts +53 -6
- package/dist/vue/predictive-search.mjs +33 -0
- package/dist/vue/predictive-search.mjs.map +1 -1
- package/dist/vue/product.d.mts +6 -2
- package/dist/vue/product.mjs.map +1 -1
- package/dist/vue/shopify-scripts.d.mts +31 -4
- package/dist/vue/shopify-scripts.mjs +6 -0
- package/dist/vue/shopify-scripts.mjs.map +1 -1
- package/package.json +5 -2
- package/skills/hydrogen-analytics/SKILL.md +6 -5
- package/skills/hydrogen-analytics/references/custom-consent.md +52 -0
- package/skills/hydrogen-analytics/references/react.md +1 -1
- package/skills/hydrogen-cart-metafields/SKILL.md +133 -0
- package/skills/hydrogen-cart-ui/SKILL.md +2 -1
- package/skills/hydrogen-customer-account/SKILL.md +3 -3
- package/skills/hydrogen-local-https/SKILL.md +22 -7
- package/skills/hydrogen-request-handlers/SKILL.md +5 -5
- package/skills/hydrogen-request-handlers/references/nuxt.md +0 -6
- package/skills/hydrogen-setup/references/analytics.md +24 -23
- package/skills/hydrogen-setup/steps/1-inspect-app.md +1 -1
- package/skills/hydrogen-setup/steps/11-verify.md +1 -0
- package/skills/hydrogen-setup/steps/2-scaffold.md +22 -1
- package/skills/hydrogen-smoke-test/SKILL.md +2 -2
- package/skills/hydrogen-storefront-client/SKILL.md +4 -2
- package/skills/hydrogen-variant-form/SKILL.md +10 -4
- package/ts-plugin/package.json +5 -0
- package/dist/core/shopify-scripts/consent.mjs +0 -17
- package/dist/core/shopify-scripts/consent.mjs.map +0 -1
- package/dist/core/shopify-scripts/deprecated-cookies.mjs +0 -97
- package/dist/core/shopify-scripts/deprecated-cookies.mjs.map +0 -1
- package/dist/core/shopify-scripts/utils/cookie-domain.mjs +0 -43
- package/dist/core/shopify-scripts/utils/cookie-domain.mjs.map +0 -1
- package/dist/core/shopify-scripts/utils/tracking-values.mjs +0 -65
- package/dist/core/shopify-scripts/utils/tracking-values.mjs.map +0 -1
- package/dist/core/shopify-scripts/utils/uuid.mjs +0 -17
- package/dist/core/shopify-scripts/utils/uuid.mjs.map +0 -1
- package/dist/development/core/shopify-scripts/consent.mjs +0 -17
- package/dist/development/core/shopify-scripts/consent.mjs.map +0 -1
- package/dist/development/core/shopify-scripts/deprecated-cookies.mjs +0 -97
- package/dist/development/core/shopify-scripts/deprecated-cookies.mjs.map +0 -1
- package/dist/development/core/shopify-scripts/utils/cookie-domain.mjs +0 -43
- package/dist/development/core/shopify-scripts/utils/cookie-domain.mjs.map +0 -1
- package/dist/development/core/shopify-scripts/utils/tracking-values.mjs +0 -65
- package/dist/development/core/shopify-scripts/utils/tracking-values.mjs.map +0 -1
- package/dist/development/core/shopify-scripts/utils/uuid.mjs +0 -17
- package/dist/development/core/shopify-scripts/utils/uuid.mjs.map +0 -1
package/dist/client/client.mjs
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
+
import { normalizeStoreDomain } from "../core/url.mjs";
|
|
1
2
|
import { getHydrogenCacheStatus } from "../core/cache/cache-status.mjs";
|
|
2
3
|
import { createFetchWithCache } from "../core/cache/fetch-with-cache.mjs";
|
|
3
4
|
import { DEFAULT_TIMEOUT_IN_MS, STOREFRONT_API_VERSION } from "../core/constants.mjs";
|
|
4
5
|
import { HYDROGEN_VERSION_HEADER, REQUEST_GROUP_ID_HEADER, SDK_VARIANT_HEADER, SDK_VARIANT_SOURCE_HEADER, SDK_VERSION_HEADER, SHOPIFY_STOREFRONT_S_HEADER, SHOPIFY_STOREFRONT_Y_HEADER, SHOPIFY_UNIQUE_TOKEN_HEADER, SHOPIFY_VISIT_TOKEN_HEADER, STOREFRONT_ACCESS_TOKEN_HEADER, STOREFRONT_BUYER_IP_HEADER, STOREFRONT_ID_HEADER, STOREFRONT_PRIVATE_TOKEN_HEADER } from "../core/headers.mjs";
|
|
5
|
-
import { normalizeStoreDomain } from "../core/url.mjs";
|
|
6
6
|
import { StorefrontApiError, StorefrontTimeoutError } from "./errors.mjs";
|
|
7
7
|
|
|
8
8
|
//#region src/client/client.ts
|
|
@@ -42,7 +42,7 @@ function createStorefrontClient(args) {
|
|
|
42
42
|
const { config, requestContext, type: clientType } = args;
|
|
43
43
|
if (!config.storeDomain) throw new Error("storeDomain is required and must be non-empty");
|
|
44
44
|
const storeUrl = normalizeStoreDomain(config.storeDomain);
|
|
45
|
-
const apiUrl = `${storeUrl}/api/${config.apiVersion ?? "2026-
|
|
45
|
+
const apiUrl = `${storeUrl}/api/${config.apiVersion ?? "2026-10"}/graphql.json`;
|
|
46
46
|
const originFetch = config.fetch ?? globalThis.fetch;
|
|
47
47
|
if (typeof originFetch !== "function") throw new Error("No fetch function available. Pass a fetch option or ensure globalThis.fetch exists.");
|
|
48
48
|
const resolvedFetch = config.cache ? createFetchWithCache({
|
package/dist/client/errors.d.mts
CHANGED
|
@@ -5,41 +5,41 @@ interface StorefrontApiErrorOptions {
|
|
|
5
5
|
cause?: unknown;
|
|
6
6
|
queryText?: string;
|
|
7
7
|
variables?: Record<string, unknown>;
|
|
8
|
-
locations?: ReadonlyArray<{
|
|
9
|
-
line: number;
|
|
10
|
-
column: number;
|
|
11
|
-
}>;
|
|
12
|
-
path?: ReadonlyArray<string | number>;
|
|
13
|
-
extensions?: Record<string, unknown>;
|
|
14
8
|
}
|
|
9
|
+
/**
|
|
10
|
+
* Thrown when a Storefront API request fails — HTTP error, network failure,
|
|
11
|
+
* or an unparseable or unexpected response body.
|
|
12
|
+
*
|
|
13
|
+
* In development, `queryText` and `variables` are attached when available.
|
|
14
|
+
* GraphQL errors (including `THROTTLED`) are not thrown; read them from `result.errors`.
|
|
15
|
+
*/
|
|
15
16
|
declare class StorefrontApiError extends Error {
|
|
17
|
+
/** Shopify `x-request-id` header, when available. Useful for support requests. */
|
|
16
18
|
readonly requestId?: string;
|
|
19
|
+
/** HTTP response status code, when the request reached the server. */
|
|
17
20
|
readonly status?: number;
|
|
21
|
+
/** The GraphQL query text. Only populated in development builds. */
|
|
18
22
|
readonly queryText?: string;
|
|
23
|
+
/** The variables sent with the request. Only populated in development builds. */
|
|
19
24
|
readonly variables?: Record<string, unknown>;
|
|
20
|
-
readonly locations?: ReadonlyArray<{
|
|
21
|
-
line: number;
|
|
22
|
-
column: number;
|
|
23
|
-
}>;
|
|
24
|
-
readonly path?: ReadonlyArray<string | number>;
|
|
25
|
-
readonly extensions?: Record<string, unknown>;
|
|
26
25
|
constructor(message: string, options?: StorefrontApiErrorOptions);
|
|
27
26
|
get [Symbol.toStringTag](): string;
|
|
28
|
-
|
|
27
|
+
/** Serializes the error. `queryText`, `variables`, `cause`, and `stack` are always omitted. */
|
|
29
28
|
toJSON(): {
|
|
30
29
|
name: string;
|
|
31
30
|
message: string;
|
|
32
31
|
requestId?: string;
|
|
33
32
|
status?: number;
|
|
34
|
-
locations?: ReadonlyArray<{
|
|
35
|
-
line: number;
|
|
36
|
-
column: number;
|
|
37
|
-
}>;
|
|
38
|
-
path?: ReadonlyArray<string | number>;
|
|
39
|
-
extensions?: Record<string, unknown>;
|
|
40
33
|
};
|
|
41
34
|
}
|
|
35
|
+
/**
|
|
36
|
+
* Thrown when a Storefront API request exceeds the configured `defaultTimeoutInMs`.
|
|
37
|
+
*
|
|
38
|
+
* Subclass of {@link StorefrontApiError}, so catching `StorefrontApiError` also handles timeouts.
|
|
39
|
+
* Aborts from the request context or a per-call `signal` are rethrown as-is, not wrapped.
|
|
40
|
+
*/
|
|
42
41
|
declare class StorefrontTimeoutError extends StorefrontApiError {
|
|
42
|
+
/** The timeout threshold that was exceeded, in milliseconds. */
|
|
43
43
|
readonly timeoutInMs: number;
|
|
44
44
|
constructor(timeoutInMs: number, options?: Omit<StorefrontApiErrorOptions, "cause">);
|
|
45
45
|
}
|
package/dist/client/errors.mjs
CHANGED
|
@@ -1,34 +1,30 @@
|
|
|
1
1
|
//#region src/client/errors.ts
|
|
2
|
+
/**
|
|
3
|
+
* Thrown when a Storefront API request fails — HTTP error, network failure,
|
|
4
|
+
* or an unparseable or unexpected response body.
|
|
5
|
+
*
|
|
6
|
+
* In development, `queryText` and `variables` are attached when available.
|
|
7
|
+
* GraphQL errors (including `THROTTLED`) are not thrown; read them from `result.errors`.
|
|
8
|
+
*/
|
|
2
9
|
var StorefrontApiError = class extends Error {
|
|
10
|
+
/** Shopify `x-request-id` header, when available. Useful for support requests. */
|
|
3
11
|
requestId;
|
|
12
|
+
/** HTTP response status code, when the request reached the server. */
|
|
4
13
|
status;
|
|
14
|
+
/** The GraphQL query text. Only populated in development builds. */
|
|
5
15
|
queryText;
|
|
16
|
+
/** The variables sent with the request. Only populated in development builds. */
|
|
6
17
|
variables;
|
|
7
|
-
locations;
|
|
8
|
-
path;
|
|
9
|
-
extensions;
|
|
10
18
|
constructor(message, options) {
|
|
11
19
|
super(message, options?.cause ? { cause: options.cause } : void 0);
|
|
12
20
|
this.name = "StorefrontApiError";
|
|
13
21
|
this.requestId = options?.requestId;
|
|
14
22
|
this.status = options?.status;
|
|
15
|
-
this.locations = options?.locations;
|
|
16
|
-
this.path = options?.path;
|
|
17
|
-
this.extensions = options?.extensions;
|
|
18
23
|
}
|
|
19
24
|
get [Symbol.toStringTag]() {
|
|
20
25
|
return this.name;
|
|
21
26
|
}
|
|
22
|
-
|
|
23
|
-
let result = `${this.name}: ${this.message}`;
|
|
24
|
-
if (this.path) try {
|
|
25
|
-
result += ` | path: ${JSON.stringify(this.path)}`;
|
|
26
|
-
} catch {}
|
|
27
|
-
if (this.extensions) try {
|
|
28
|
-
result += ` | extensions: ${JSON.stringify(this.extensions)}`;
|
|
29
|
-
} catch {}
|
|
30
|
-
return result;
|
|
31
|
-
}
|
|
27
|
+
/** Serializes the error. `queryText`, `variables`, `cause`, and `stack` are always omitted. */
|
|
32
28
|
toJSON() {
|
|
33
29
|
return {
|
|
34
30
|
name: this.name,
|
|
@@ -38,7 +34,14 @@ var StorefrontApiError = class extends Error {
|
|
|
38
34
|
};
|
|
39
35
|
}
|
|
40
36
|
};
|
|
37
|
+
/**
|
|
38
|
+
* Thrown when a Storefront API request exceeds the configured `defaultTimeoutInMs`.
|
|
39
|
+
*
|
|
40
|
+
* Subclass of {@link StorefrontApiError}, so catching `StorefrontApiError` also handles timeouts.
|
|
41
|
+
* Aborts from the request context or a per-call `signal` are rethrown as-is, not wrapped.
|
|
42
|
+
*/
|
|
41
43
|
var StorefrontTimeoutError = class extends StorefrontApiError {
|
|
44
|
+
/** The timeout threshold that was exceeded, in milliseconds. */
|
|
42
45
|
timeoutInMs;
|
|
43
46
|
constructor(timeoutInMs, options) {
|
|
44
47
|
super(`Storefront API request timed out after ${timeoutInMs}ms`, options);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"errors.mjs","names":[],"sources":["../../src/client/errors.ts"],"sourcesContent":["interface StorefrontApiErrorOptions {\n requestId?: string;\n status?: number;\n cause?: unknown;\n queryText?: string;\n variables?: Record<string, unknown>;\n
|
|
1
|
+
{"version":3,"file":"errors.mjs","names":[],"sources":["../../src/client/errors.ts"],"sourcesContent":["interface StorefrontApiErrorOptions {\n requestId?: string;\n status?: number;\n cause?: unknown;\n queryText?: string;\n variables?: Record<string, unknown>;\n}\n\n/**\n * Thrown when a Storefront API request fails — HTTP error, network failure,\n * or an unparseable or unexpected response body.\n *\n * In development, `queryText` and `variables` are attached when available.\n * GraphQL errors (including `THROTTLED`) are not thrown; read them from `result.errors`.\n */\nexport class StorefrontApiError extends Error {\n /** Shopify `x-request-id` header, when available. Useful for support requests. */\n readonly requestId?: string;\n /** HTTP response status code, when the request reached the server. */\n readonly status?: number;\n /** The GraphQL query text. Only populated in development builds. */\n readonly queryText?: string;\n /** The variables sent with the request. Only populated in development builds. */\n readonly variables?: Record<string, unknown>;\n\n constructor(message: string, options?: StorefrontApiErrorOptions) {\n super(message, options?.cause ? { cause: options.cause } : undefined);\n this.name = \"StorefrontApiError\";\n this.requestId = options?.requestId;\n this.status = options?.status;\n\n if (__DEV__) {\n this.queryText = options?.queryText;\n this.variables = options?.variables;\n }\n }\n\n get [Symbol.toStringTag]() {\n return this.name;\n }\n\n /** Serializes the error. `queryText`, `variables`, `cause`, and `stack` are always omitted. */\n toJSON(): { name: string; message: string; requestId?: string; status?: number } {\n return {\n name: this.name,\n message: this.message,\n ...(this.requestId != null && { requestId: this.requestId }),\n ...(this.status != null && { status: this.status }),\n };\n }\n}\n\n/**\n * Thrown when a Storefront API request exceeds the configured `defaultTimeoutInMs`.\n *\n * Subclass of {@link StorefrontApiError}, so catching `StorefrontApiError` also handles timeouts.\n * Aborts from the request context or a per-call `signal` are rethrown as-is, not wrapped.\n */\nexport class StorefrontTimeoutError extends StorefrontApiError {\n /** The timeout threshold that was exceeded, in milliseconds. */\n readonly timeoutInMs: number;\n\n constructor(timeoutInMs: number, options?: Omit<StorefrontApiErrorOptions, \"cause\">) {\n super(`Storefront API request timed out after ${timeoutInMs}ms`, options);\n this.name = \"StorefrontTimeoutError\";\n this.timeoutInMs = timeoutInMs;\n }\n}\n"],"mappings":";;;;;;;;AAeA,IAAa,qBAAb,cAAwC,MAAM;;CAE5C,AAAS;;CAET,AAAS;;CAET,AAAS;;CAET,AAAS;CAET,YAAY,SAAiB,SAAqC;AAChE,QAAM,SAAS,SAAS,QAAQ,EAAE,OAAO,QAAQ,OAAO,GAAG,OAAU;AACrE,OAAK,OAAO;AACZ,OAAK,YAAY,SAAS;AAC1B,OAAK,SAAS,SAAS;;CAQzB,KAAK,OAAO,eAAe;AACzB,SAAO,KAAK;;;CAId,SAAiF;AAC/E,SAAO;GACL,MAAM,KAAK;GACX,SAAS,KAAK;GACd,GAAI,KAAK,aAAa,QAAQ,EAAE,WAAW,KAAK,WAAW;GAC3D,GAAI,KAAK,UAAU,QAAQ,EAAE,QAAQ,KAAK,QAAQ;GACnD;;;;;;;;;AAUL,IAAa,yBAAb,cAA4C,mBAAmB;;CAE7D,AAAS;CAET,YAAY,aAAqB,SAAoD;AACnF,QAAM,0CAA0C,YAAY,KAAK,QAAQ;AACzE,OAAK,OAAO;AACZ,OAAK,cAAc"}
|
package/dist/client/types.d.mts
CHANGED
|
@@ -15,17 +15,27 @@ type ResultOfDoc<Doc> = Doc extends StorefrontQueryString<infer Result, infer _V
|
|
|
15
15
|
type VariablesOfDoc<Doc> = Doc extends StorefrontQueryString<infer _Result, infer Variables, string> ? Variables : Doc extends TadaDocumentNode<any, any> ? VariablesOf<Doc> : never;
|
|
16
16
|
type StorefrontApiResultOf<Doc extends DocLike> = ResultOf<Doc>;
|
|
17
17
|
type StorefrontApiVariablesOf<Doc extends DocLike> = VariablesOf<Doc>;
|
|
18
|
+
/**
|
|
19
|
+
* Minimal shape matching the GraphQL spec's error format, defined locally to
|
|
20
|
+
* avoid a runtime dependency on the `graphql` package.
|
|
21
|
+
*
|
|
22
|
+
* Present on the `errors` arm of {@link StorefrontGraphqlResult}.
|
|
23
|
+
*/
|
|
18
24
|
interface GraphQLFormattedError {
|
|
25
|
+
/** Human-readable error description. */
|
|
19
26
|
readonly message: string;
|
|
27
|
+
/** Source locations in the query where the error originated. */
|
|
20
28
|
readonly locations?: ReadonlyArray<{
|
|
21
29
|
line: number;
|
|
22
30
|
column: number;
|
|
23
31
|
}>;
|
|
32
|
+
/** Response path to the field that triggered the error. */
|
|
24
33
|
readonly path?: ReadonlyArray<string | number>;
|
|
34
|
+
/** Vendor extensions (e.g. Shopify error codes). */
|
|
25
35
|
readonly extensions?: Record<string, unknown>;
|
|
26
36
|
}
|
|
27
37
|
type CommonOptions = {
|
|
28
|
-
storeDomain: string;
|
|
38
|
+
/** Shopify store domain — e.g. `"my-store.myshopify.com"`. Normalized internally. */storeDomain: string;
|
|
29
39
|
/**
|
|
30
40
|
* Identifies the Hydrogen storefront making Storefront API requests so Shopify can attribute
|
|
31
41
|
* cart analytics to the correct storefront.
|
|
@@ -38,9 +48,9 @@ type CommonOptions = {
|
|
|
38
48
|
* `apiVersion` so version routing stays explicit. Hydrogen's bundled gql.tada schema still
|
|
39
49
|
* controls type inference, so queries against fields outside that schema need their own typing.
|
|
40
50
|
*/
|
|
41
|
-
apiVersion?: string;
|
|
42
|
-
defaultTimeoutInMs?: number;
|
|
43
|
-
cache?: CacheInstance;
|
|
51
|
+
apiVersion?: string; /** Per-request timeout in milliseconds. `0` disables the timeout. Defaults to 30,000 ms. */
|
|
52
|
+
defaultTimeoutInMs?: number; /** Shared cache instance that enables per-query caching via the `cache` option on `graphql()`. */
|
|
53
|
+
cache?: CacheInstance; /** Worker-style `waitUntil` to extend the request lifetime for background cache writes. */
|
|
44
54
|
waitUntil?: WaitUntil;
|
|
45
55
|
};
|
|
46
56
|
type AnyFetch = typeof globalThis.fetch | ((...args: never[]) => Promise<Response>);
|
|
@@ -53,7 +63,9 @@ type AnyFetch = typeof globalThis.fetch | ((...args: never[]) => Promise<Respons
|
|
|
53
63
|
* product tags, metaobjects, metafields, menus, and customers.
|
|
54
64
|
*/
|
|
55
65
|
interface PublicClientOptions<Fetch extends AnyFetch | undefined = typeof globalThis.fetch> extends CommonOptions {
|
|
66
|
+
/** Custom fetch implementation. Falls back to `globalThis.fetch`. */
|
|
56
67
|
fetch?: Fetch;
|
|
68
|
+
/** Public Storefront Access Token. Omit for tokenless access (limited fields). */
|
|
57
69
|
publicStorefrontToken?: string | undefined;
|
|
58
70
|
}
|
|
59
71
|
/**
|
|
@@ -67,7 +79,9 @@ interface PublicClientOptions<Fetch extends AnyFetch | undefined = typeof global
|
|
|
67
79
|
* product tags, metaobjects, metafields, menus, and customers.
|
|
68
80
|
*/
|
|
69
81
|
interface PrivateClientOptions<Fetch extends AnyFetch | undefined = typeof globalThis.fetch> extends CommonOptions {
|
|
82
|
+
/** Custom fetch implementation. Falls back to `globalThis.fetch`. */
|
|
70
83
|
fetch?: Fetch;
|
|
84
|
+
/** Private Storefront Access Token. Must never be exposed to browsers. */
|
|
71
85
|
privateStorefrontToken: string;
|
|
72
86
|
}
|
|
73
87
|
/**
|
|
@@ -78,10 +92,20 @@ interface PrivateClientOptions<Fetch extends AnyFetch | undefined = typeof globa
|
|
|
78
92
|
* shopper identity is available.
|
|
79
93
|
*/
|
|
80
94
|
interface PrivateNoBuyerContextClientOptions<Fetch extends AnyFetch | undefined = typeof globalThis.fetch> extends CommonOptions {
|
|
95
|
+
/** Custom fetch implementation. Falls back to `globalThis.fetch`. */
|
|
81
96
|
fetch?: Fetch;
|
|
97
|
+
/** Private Storefront Access Token. Must never be exposed to browsers. */
|
|
82
98
|
privateStorefrontToken: string;
|
|
83
99
|
}
|
|
100
|
+
/** Union of all config shapes passed as the `config` field of {@link CreateStorefrontClientArgs}. */
|
|
84
101
|
type StorefrontClientOptions = PublicClientOptions | PrivateClientOptions | PrivateNoBuyerContextClientOptions;
|
|
102
|
+
/**
|
|
103
|
+
* Discriminated union accepted by `createStorefrontClient`.
|
|
104
|
+
*
|
|
105
|
+
* Discriminated on `type`: `"public"` requires a public (or no) token,
|
|
106
|
+
* `"private"` requires a private token plus a request context with `buyerIp`,
|
|
107
|
+
* and `"private_no_buyer_context"` requires a private token; `buyerIp` is not forwarded.
|
|
108
|
+
*/
|
|
85
109
|
type CreateStorefrontClientArgs<RequestContext extends ShopifyRequestContext = ShopifyRequestContext, Type extends ClientType = ClientType, CacheConfig extends CacheInstance | undefined = CacheInstance | undefined> = {
|
|
86
110
|
type: "public" & Type;
|
|
87
111
|
requestContext: RequestContext;
|
|
@@ -104,15 +128,38 @@ type CreateStorefrontClientArgs<RequestContext extends ShopifyRequestContext = S
|
|
|
104
128
|
type AutoAddedVariableNames = "country" | "language";
|
|
105
129
|
type UserVariables<Doc> = Omit<VariablesOfDoc<Doc>, AutoAddedVariableNames>;
|
|
106
130
|
type HasNoRequiredKeys<T> = Record<string, never> extends T ? true : false;
|
|
131
|
+
/** Per-call options common to every `storefront.graphql()` invocation. */
|
|
107
132
|
type StorefrontGraphqlOptions = {
|
|
108
|
-
signal?: AbortSignal;
|
|
133
|
+
/** Abort signal forwarded to the underlying fetch. Combined with the request-context signal via `AbortSignal.any`. */signal?: AbortSignal;
|
|
109
134
|
};
|
|
110
135
|
type MergedOptions<Doc extends DocLike, Extra extends Record<string, unknown>> = Extra & StorefrontGraphqlOptions & (HasNoRequiredKeys<UserVariables<Doc>> extends true ? {
|
|
111
136
|
variables?: UserVariables<Doc>;
|
|
112
137
|
} : {
|
|
113
138
|
variables: UserVariables<Doc>;
|
|
114
139
|
});
|
|
140
|
+
/**
|
|
141
|
+
* Rest-parameter tuple for `storefront.graphql()`.
|
|
142
|
+
*
|
|
143
|
+
* Resolves to `[options]` (required) when the document declares required
|
|
144
|
+
* variables, or `[options?]` (optional) when all variables are optional or
|
|
145
|
+
* auto-injected (e.g. `$country`, `$language`).
|
|
146
|
+
*/
|
|
115
147
|
type GqlRestParam<Doc extends DocLike, Extra extends Record<string, unknown> = {}> = HasNoRequiredKeys<MergedOptions<Doc, GraphqlExtraOptionsForDoc<Doc, Extra>>> extends true ? [options?: MergedOptions<Doc, GraphqlExtraOptionsForDoc<Doc, Extra>>] : [options: MergedOptions<Doc, GraphqlExtraOptionsForDoc<Doc, Extra>>];
|
|
148
|
+
/**
|
|
149
|
+
* Discriminated union returned by `storefront.graphql()`.
|
|
150
|
+
*
|
|
151
|
+
* Discriminated on `errors`:
|
|
152
|
+
*
|
|
153
|
+
* - **No `errors`** — `data` is non-null and fully typed by the document.
|
|
154
|
+
* - **`errors` present** — `data` may be `null` (request-level error, or a non-null
|
|
155
|
+
* field error that propagated to the root) or
|
|
156
|
+
* partial (field-level error absorbed by a nullable ancestor). Always
|
|
157
|
+
* inspect `errors` first.
|
|
158
|
+
*
|
|
159
|
+
* `Partial` is deliberately not used: a non-null `data` never has absent
|
|
160
|
+
* top-level keys — failures surface as `null` values already covered by the
|
|
161
|
+
* schema types.
|
|
162
|
+
*/
|
|
116
163
|
type StorefrontGraphqlResult<Doc extends DocLike> = {
|
|
117
164
|
data: ResultOfDoc<Doc>;
|
|
118
165
|
errors?: undefined;
|
|
@@ -122,24 +169,58 @@ type StorefrontGraphqlResult<Doc extends DocLike> = {
|
|
|
122
169
|
errors: GraphQLFormattedError[];
|
|
123
170
|
headers: Headers;
|
|
124
171
|
};
|
|
172
|
+
/** Base callable signature of `StorefrontClient.graphql` without per-client extra options. Accepts a `gql()` document or a plain string. */
|
|
125
173
|
type StorefrontGraphql = <const Doc extends DocLike | string>(doc: Doc, ...options: GqlRestParam<ResolveDoc<Doc>>) => Promise<StorefrontGraphqlResult<ResolveDoc<Doc>>>;
|
|
126
174
|
type ClientType = /** Public access token or tokenless access. Best for: browser requests. */"public" /** Private token with trusted buyer context. Best for: SSR. */ | "private" /** Private token without buyer context. Best for: background jobs, webhooks. */ | "private_no_buyer_context";
|
|
175
|
+
/**
|
|
176
|
+
* A type-safe Storefront API client returned by `createStorefrontClient`.
|
|
177
|
+
*
|
|
178
|
+
* Call `client.graphql(doc, options?)` to execute a query or mutation against
|
|
179
|
+
* the Storefront API. The result is typed by the document you pass in.
|
|
180
|
+
*/
|
|
127
181
|
type StorefrontClient<Extra extends Record<string, unknown> = {}, Type extends ClientType = ClientType, RequestContext extends ShopifyRequestContext = ShopifyRequestContext> = {
|
|
128
|
-
type: Type;
|
|
182
|
+
/** Which client variant was created (`"public"`, `"private"`, or `"private_no_buyer_context"`). */type: Type; /** Resolved i18n config (country + language) used for automatic variable injection. */
|
|
129
183
|
i18n: RequestContext["i18n"];
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
184
|
+
/**
|
|
185
|
+
* Execute a Storefront API GraphQL operation.
|
|
186
|
+
*
|
|
187
|
+
* Accepts a `gql()` document or a plain query string. Variables
|
|
188
|
+
* declared as `$country` / `$language` are auto-injected from the client's
|
|
189
|
+
* i18n config.
|
|
190
|
+
*/
|
|
191
|
+
graphql: <const Doc extends DocLike | string>(doc: Doc, ...options: GqlRestParam<ResolveDoc<Doc>, Extra>) => Promise<StorefrontGraphqlResult<ResolveDoc<Doc>>>; /** Full Storefront API GraphQL endpoint URL. */
|
|
192
|
+
apiUrl: string; /** Normalized store URL (e.g. `"https://my-store.myshopify.com"`). */
|
|
193
|
+
storeUrl: string; /** Storefront identifier for cart analytics attribution, when provided. */
|
|
194
|
+
storefrontId?: string; /** The request context used for request-scoped headers, abort signals, and i18n. */
|
|
134
195
|
requestContext: RequestContext;
|
|
135
196
|
};
|
|
197
|
+
/** A {@link StorefrontClient} narrowed to `type: "public"`. */
|
|
136
198
|
type PublicStorefrontClient<Extra extends Record<string, unknown> = {}, RequestContext extends ShopifyRequestContext = ShopifyRequestContext> = StorefrontClient<Extra, "public", RequestContext>;
|
|
199
|
+
/** A {@link StorefrontClient} narrowed to `type: "private"`. Requires a request context with `buyerIp`. */
|
|
137
200
|
type PrivateStorefrontClient<Extra extends Record<string, unknown> = {}, RequestContext extends ShopifyRequestContextWithBuyerIp = ShopifyRequestContextWithBuyerIp> = StorefrontClient<Extra, "private", RequestContext>;
|
|
201
|
+
/** Convenience alias: a private client whose request context is always the concrete `ShopifyRequestContextWithBuyerIp`. */
|
|
138
202
|
type RequestScopedPrivateStorefrontClient<Extra extends Record<string, unknown> = {}> = PrivateStorefrontClient<Extra, ShopifyRequestContextWithBuyerIp>;
|
|
203
|
+
/** A {@link StorefrontClient} narrowed to `type: "private_no_buyer_context"`. Best for background jobs and webhooks. */
|
|
139
204
|
type PrivateNoBuyerContextStorefrontClient<Extra extends Record<string, unknown> = {}, RequestContext extends ShopifyRequestContext = ShopifyRequestContext> = StorefrontClient<Extra, "private_no_buyer_context", RequestContext>;
|
|
205
|
+
/**
|
|
206
|
+
* Namespace for extracting result and variable types from Storefront API documents.
|
|
207
|
+
*
|
|
208
|
+
* @example
|
|
209
|
+
* ```ts
|
|
210
|
+
* import { gql, type StorefrontApi } from "@shopify/hydrogen";
|
|
211
|
+
*
|
|
212
|
+
* const PRODUCT_QUERY = gql(`query Product($handle: String!) { product(handle: $handle) { title } }`);
|
|
213
|
+
*
|
|
214
|
+
* type Product = StorefrontApi.ResultOf<typeof PRODUCT_QUERY>;
|
|
215
|
+
* type Vars = StorefrontApi.VariablesOf<typeof PRODUCT_QUERY>;
|
|
216
|
+
* ```
|
|
217
|
+
*/
|
|
140
218
|
declare namespace StorefrontApi {
|
|
219
|
+
/** Extracts the typed result shape from a Storefront API document. */
|
|
141
220
|
type ResultOf<Doc extends DocLike> = StorefrontApiResultOf<Doc>;
|
|
221
|
+
/** Extracts the typed variables shape from a Storefront API document. */
|
|
142
222
|
type VariablesOf<Doc extends DocLike> = StorefrontApiVariablesOf<Doc>;
|
|
223
|
+
/** A typed Storefront API document — a `gql()` document or a `TadaDocumentNode`. */
|
|
143
224
|
type DocumentNode = DocLike;
|
|
144
225
|
}
|
|
145
226
|
//#endregion
|
|
@@ -2,6 +2,20 @@ import { CartStore } from "../cart/cart.mjs";
|
|
|
2
2
|
|
|
3
3
|
//#region src/core/analytics/cart-tracker.d.ts
|
|
4
4
|
type CartAnalyticsStore = Pick<CartStore, "getState" | "subscribe">;
|
|
5
|
+
/**
|
|
6
|
+
* Subscribes to a cart store and publishes analytics events when the cart changes.
|
|
7
|
+
*
|
|
8
|
+
* Emits `cart_updated` whenever a settled cart's `updatedAt` changes, plus
|
|
9
|
+
* `product_added_to_cart` / `product_removed_from_cart` for each line that was
|
|
10
|
+
* added, removed, or changed quantity. Deduplicates on `updatedAt` with an
|
|
11
|
+
* in-memory cursor (within this subscription) and `localStorage` (across full
|
|
12
|
+
* page loads and tabs). A store filled from your own cart query must select
|
|
13
|
+
* `updatedAt`, or deduplication can't work.
|
|
14
|
+
*
|
|
15
|
+
* @throws {Error} If `window.Shopify.analytics` is not set (including on the server).
|
|
16
|
+
* Render `ShopifyScripts` (or the `getShopifyScriptTags()` output) first.
|
|
17
|
+
* @returns An unsubscribe function that stops tracking.
|
|
18
|
+
*/
|
|
5
19
|
declare function trackCartAnalytics(store: CartAnalyticsStore): () => void;
|
|
6
20
|
//#endregion
|
|
7
21
|
export { trackCartAnalytics };
|
|
@@ -2,6 +2,20 @@ import { AnalyticsEvent } from "./events.mjs";
|
|
|
2
2
|
import { flattenConnection } from "./utils/flatten-connection.mjs";
|
|
3
3
|
|
|
4
4
|
//#region src/core/analytics/cart-tracker.ts
|
|
5
|
+
/**
|
|
6
|
+
* Subscribes to a cart store and publishes analytics events when the cart changes.
|
|
7
|
+
*
|
|
8
|
+
* Emits `cart_updated` whenever a settled cart's `updatedAt` changes, plus
|
|
9
|
+
* `product_added_to_cart` / `product_removed_from_cart` for each line that was
|
|
10
|
+
* added, removed, or changed quantity. Deduplicates on `updatedAt` with an
|
|
11
|
+
* in-memory cursor (within this subscription) and `localStorage` (across full
|
|
12
|
+
* page loads and tabs). A store filled from your own cart query must select
|
|
13
|
+
* `updatedAt`, or deduplication can't work.
|
|
14
|
+
*
|
|
15
|
+
* @throws {Error} If `window.Shopify.analytics` is not set (including on the server).
|
|
16
|
+
* Render `ShopifyScripts` (or the `getShopifyScriptTags()` output) first.
|
|
17
|
+
* @returns An unsubscribe function that stops tracking.
|
|
18
|
+
*/
|
|
5
19
|
function trackCartAnalytics(store) {
|
|
6
20
|
const analytics = getGlobalAnalytics();
|
|
7
21
|
const state = {
|
|
@@ -120,7 +134,7 @@ function hasPendingCartWork({ pending, revalidating }) {
|
|
|
120
134
|
return revalidating === true || hasPendingCost || pending.note || pending.attributes;
|
|
121
135
|
}
|
|
122
136
|
function getCartUpdatedAt(cart) {
|
|
123
|
-
return
|
|
137
|
+
return cart.updatedAt ?? (/* @__PURE__ */ new Date()).toISOString();
|
|
124
138
|
}
|
|
125
139
|
function syncShopifyCurrency(cart) {
|
|
126
140
|
if (typeof window === "undefined") return;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cart-tracker.mjs","names":[],"sources":["../../../src/core/analytics/cart-tracker.ts"],"sourcesContent":["import type { CartStore } from \"../cart/cart\";\nimport type { CartData, CartState } from \"../cart/state\";\nimport { AnalyticsEvent } from \"./events\";\nimport type {\n AnalyticsCart,\n AnalyticsCartLine,\n CartUpdatePayload,\n StorefrontAnalytics,\n} from \"./types\";\nimport { flattenConnection } from \"./utils/flatten-connection\";\n\ntype CartTrackerAnalytics = Pick<StorefrontAnalytics, \"publish\" | \"getConfig\">;\ntype CartAnalyticsStore = Pick<CartStore, \"getState\" | \"subscribe\">;\n\ntype CartStorage = {\n updatedAt: string;\n id: string;\n};\n\ntype CartTrackerState = {\n prevCart: AnalyticsCart | null;\n lastEventId: string | null;\n};\n\nexport function trackCartAnalytics(store: CartAnalyticsStore): () => void {\n const analytics = getGlobalAnalytics();\n const state: CartTrackerState = {\n prevCart: toAnalyticsCart(store.getState()),\n lastEventId: null,\n };\n\n if (state.prevCart) syncShopifyCurrency(state.prevCart);\n\n return store.subscribe((cartState) => {\n const cart = toAnalyticsCart(cartState);\n if (!cart) return;\n\n trackCartChange(analytics, state, cart);\n });\n}\n\nfunction trackCartChange(\n analytics: CartTrackerAnalytics,\n state: CartTrackerState,\n cart: AnalyticsCart,\n): void {\n if (!cart || !cart.updatedAt) return;\n\n syncShopifyCurrency(cart);\n\n if (cart.updatedAt === state.prevCart?.updatedAt) return;\n\n let cartLastUpdatedAt: CartStorage | null;\n try {\n cartLastUpdatedAt = JSON.parse(localStorage.getItem(\"cartLastUpdatedAt\") || \"\");\n } catch {\n cartLastUpdatedAt = null;\n }\n\n if (cart.id === cartLastUpdatedAt?.id && cart.updatedAt === cartLastUpdatedAt?.updatedAt) {\n state.prevCart = cart;\n return;\n }\n\n if (cart.updatedAt === state.lastEventId) return;\n state.lastEventId = cart.updatedAt;\n\n const config = analytics.getConfig();\n const payload: CartUpdatePayload = {\n eventTimestamp: Date.now(),\n cart,\n prevCart: state.prevCart,\n shop: config.shop,\n customData: config.customData,\n };\n\n analytics.publish(AnalyticsEvent.CART_UPDATED, payload);\n\n try {\n localStorage.setItem(\n \"cartLastUpdatedAt\",\n JSON.stringify({ id: cart.id, updatedAt: cart.updatedAt }),\n );\n } catch {\n // Safari private browsing or storage quota exceeded; analytics\n // deduplication will rely on in-memory prevCart only for this session.\n }\n\n const previousCartLines = flattenConnection<AnalyticsCartLine>(state.prevCart?.lines);\n const currentCartLines = flattenConnection<AnalyticsCartLine>(cart.lines);\n\n previousCartLines.forEach((prevLine) => {\n const matchedLineId = currentCartLines.filter((line) => prevLine.id === line.id);\n if (matchedLineId.length === 1) {\n const matchedLine = matchedLineId[0];\n if (prevLine.quantity < matchedLine.quantity) {\n analytics.publish(AnalyticsEvent.PRODUCT_ADD_TO_CART, {\n ...payload,\n prevLine,\n currentLine: matchedLine,\n });\n } else if (prevLine.quantity > matchedLine.quantity) {\n analytics.publish(AnalyticsEvent.PRODUCT_REMOVED_FROM_CART, {\n ...payload,\n prevLine,\n currentLine: matchedLine,\n });\n }\n } else {\n analytics.publish(AnalyticsEvent.PRODUCT_REMOVED_FROM_CART, {\n ...payload,\n prevLine,\n });\n }\n });\n\n currentCartLines.forEach((line) => {\n const matchedLineId = previousCartLines.filter((previousLine) => line.id === previousLine.id);\n if (!matchedLineId || matchedLineId.length === 0) {\n analytics.publish(AnalyticsEvent.PRODUCT_ADD_TO_CART, {\n ...payload,\n currentLine: line,\n });\n }\n });\n\n state.prevCart = cart;\n}\n\nfunction getGlobalAnalytics(): CartTrackerAnalytics {\n const analytics = typeof window !== \"undefined\" ? window.Shopify?.analytics : undefined;\n if (!analytics) {\n throw new Error(\n \"Shopify analytics bus is not available. Render ShopifyScripts before calling trackCartAnalytics().\",\n );\n }\n\n return analytics;\n}\n\nfunction toAnalyticsCart(state: CartState): AnalyticsCart | null {\n const cart = state.data;\n if (!cart.id || hasPendingCartWork(state)) return null;\n\n return {\n id: cart.id,\n updatedAt: getCartUpdatedAt(cart),\n cost: cart.cost,\n lines: {\n nodes: cart.lines.nodes.flatMap((line): AnalyticsCartLine[] => {\n const merchandise = line.merchandise;\n if (!merchandise) return [];\n\n const product = merchandise.product;\n const productId = typeof product.id === \"string\" ? product.id : \"\";\n const vendor = typeof product.vendor === \"string\" ? product.vendor : \"\";\n const productType =\n typeof product.productType === \"string\" ? product.productType : undefined;\n\n return [\n {\n id: line.id,\n quantity: line.quantity,\n merchandise: {\n id: merchandise.id,\n title: merchandise.title ?? product.title,\n price: line.cost.amountPerQuantity,\n sku: typeof merchandise.sku === \"string\" ? merchandise.sku : null,\n product: {\n id: productId,\n title: product.title,\n vendor,\n productType,\n handle: product.handle,\n },\n },\n },\n ];\n }),\n },\n };\n}\n\nfunction hasPendingCartWork({ pending, revalidating }: CartState): boolean {\n const hasPendingCost = pending.cost ?? (pending.lines.size > 0 || pending.discountCodes.size > 0);\n return revalidating === true || hasPendingCost || pending.note || pending.attributes;\n}\n\nfunction getCartUpdatedAt(cart: CartData): string {\n return typeof cart.updatedAt === \"string\" ? cart.updatedAt : new Date().toISOString();\n}\n\nfunction syncShopifyCurrency(cart: AnalyticsCart): void {\n if (typeof window === \"undefined\") return;\n\n const currencyCode = getCartCurrencyCode(cart);\n if (!currencyCode) return;\n\n const normalizedCurrencyCode = currencyCode.toUpperCase();\n const shopifyWindow: { Shopify?: { currency?: { active: string } } } = window;\n const shopify = (shopifyWindow.Shopify ??= {});\n if (shopify.currency?.active === normalizedCurrencyCode) return;\n\n shopify.currency = { active: normalizedCurrencyCode };\n}\n\nfunction getCartCurrencyCode(cart: AnalyticsCart): string | undefined {\n return (\n nonEmptyCurrencyCode(cart.cost?.totalAmount?.currencyCode) ??\n nonEmptyCurrencyCode(cart.cost?.subtotalAmount?.currencyCode) ??\n flattenConnection<AnalyticsCartLine>(cart.lines).find(\n (line) => line.merchandise.price.currencyCode,\n )?.merchandise.price.currencyCode\n );\n}\n\nfunction nonEmptyCurrencyCode(currencyCode: string | undefined): string | undefined {\n return currencyCode || undefined;\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"cart-tracker.mjs","names":[],"sources":["../../../src/core/analytics/cart-tracker.ts"],"sourcesContent":["import type { CartStore } from \"../cart/cart\";\nimport type { CartData, CartState } from \"../cart/state\";\nimport { AnalyticsEvent } from \"./events\";\nimport type {\n AnalyticsCart,\n AnalyticsCartLine,\n CartUpdatePayload,\n StorefrontAnalytics,\n} from \"./types\";\nimport { flattenConnection } from \"./utils/flatten-connection\";\n\ntype CartTrackerAnalytics = Pick<StorefrontAnalytics, \"publish\" | \"getConfig\">;\ntype CartAnalyticsStore = Pick<CartStore, \"getState\" | \"subscribe\">;\n\ntype CartStorage = {\n updatedAt: string;\n id: string;\n};\n\ntype CartTrackerState = {\n prevCart: AnalyticsCart | null;\n lastEventId: string | null;\n};\n\n/**\n * Subscribes to a cart store and publishes analytics events when the cart changes.\n *\n * Emits `cart_updated` whenever a settled cart's `updatedAt` changes, plus\n * `product_added_to_cart` / `product_removed_from_cart` for each line that was\n * added, removed, or changed quantity. Deduplicates on `updatedAt` with an\n * in-memory cursor (within this subscription) and `localStorage` (across full\n * page loads and tabs). A store filled from your own cart query must select\n * `updatedAt`, or deduplication can't work.\n *\n * @throws {Error} If `window.Shopify.analytics` is not set (including on the server).\n * Render `ShopifyScripts` (or the `getShopifyScriptTags()` output) first.\n * @returns An unsubscribe function that stops tracking.\n */\nexport function trackCartAnalytics(store: CartAnalyticsStore): () => void {\n const analytics = getGlobalAnalytics();\n const state: CartTrackerState = {\n prevCart: toAnalyticsCart(store.getState()),\n lastEventId: null,\n };\n\n if (state.prevCart) syncShopifyCurrency(state.prevCart);\n\n return store.subscribe((cartState) => {\n const cart = toAnalyticsCart(cartState);\n if (!cart) return;\n\n trackCartChange(analytics, state, cart);\n });\n}\n\nfunction trackCartChange(\n analytics: CartTrackerAnalytics,\n state: CartTrackerState,\n cart: AnalyticsCart,\n): void {\n if (!cart || !cart.updatedAt) return;\n\n syncShopifyCurrency(cart);\n\n if (cart.updatedAt === state.prevCart?.updatedAt) return;\n\n let cartLastUpdatedAt: CartStorage | null;\n try {\n cartLastUpdatedAt = JSON.parse(localStorage.getItem(\"cartLastUpdatedAt\") || \"\");\n } catch {\n cartLastUpdatedAt = null;\n }\n\n if (cart.id === cartLastUpdatedAt?.id && cart.updatedAt === cartLastUpdatedAt?.updatedAt) {\n state.prevCart = cart;\n return;\n }\n\n if (cart.updatedAt === state.lastEventId) return;\n state.lastEventId = cart.updatedAt;\n\n const config = analytics.getConfig();\n const payload: CartUpdatePayload = {\n eventTimestamp: Date.now(),\n cart,\n prevCart: state.prevCart,\n shop: config.shop,\n customData: config.customData,\n };\n\n analytics.publish(AnalyticsEvent.CART_UPDATED, payload);\n\n try {\n localStorage.setItem(\n \"cartLastUpdatedAt\",\n JSON.stringify({ id: cart.id, updatedAt: cart.updatedAt }),\n );\n } catch {\n // Safari private browsing or storage quota exceeded; analytics\n // deduplication will rely on in-memory prevCart only for this session.\n }\n\n const previousCartLines = flattenConnection<AnalyticsCartLine>(state.prevCart?.lines);\n const currentCartLines = flattenConnection<AnalyticsCartLine>(cart.lines);\n\n previousCartLines.forEach((prevLine) => {\n const matchedLineId = currentCartLines.filter((line) => prevLine.id === line.id);\n if (matchedLineId.length === 1) {\n const matchedLine = matchedLineId[0];\n if (prevLine.quantity < matchedLine.quantity) {\n analytics.publish(AnalyticsEvent.PRODUCT_ADD_TO_CART, {\n ...payload,\n prevLine,\n currentLine: matchedLine,\n });\n } else if (prevLine.quantity > matchedLine.quantity) {\n analytics.publish(AnalyticsEvent.PRODUCT_REMOVED_FROM_CART, {\n ...payload,\n prevLine,\n currentLine: matchedLine,\n });\n }\n } else {\n analytics.publish(AnalyticsEvent.PRODUCT_REMOVED_FROM_CART, {\n ...payload,\n prevLine,\n });\n }\n });\n\n currentCartLines.forEach((line) => {\n const matchedLineId = previousCartLines.filter((previousLine) => line.id === previousLine.id);\n if (!matchedLineId || matchedLineId.length === 0) {\n analytics.publish(AnalyticsEvent.PRODUCT_ADD_TO_CART, {\n ...payload,\n currentLine: line,\n });\n }\n });\n\n state.prevCart = cart;\n}\n\nfunction getGlobalAnalytics(): CartTrackerAnalytics {\n const analytics = typeof window !== \"undefined\" ? window.Shopify?.analytics : undefined;\n if (!analytics) {\n throw new Error(\n \"Shopify analytics bus is not available. Render ShopifyScripts before calling trackCartAnalytics().\",\n );\n }\n\n return analytics;\n}\n\nfunction toAnalyticsCart(state: CartState): AnalyticsCart | null {\n const cart = state.data;\n if (!cart.id || hasPendingCartWork(state)) return null;\n\n return {\n id: cart.id,\n updatedAt: getCartUpdatedAt(cart),\n cost: cart.cost,\n lines: {\n nodes: cart.lines.nodes.flatMap((line): AnalyticsCartLine[] => {\n const merchandise = line.merchandise;\n if (!merchandise) return [];\n\n const product = merchandise.product;\n const productId = typeof product.id === \"string\" ? product.id : \"\";\n const vendor = typeof product.vendor === \"string\" ? product.vendor : \"\";\n const productType =\n typeof product.productType === \"string\" ? product.productType : undefined;\n\n return [\n {\n id: line.id,\n quantity: line.quantity,\n merchandise: {\n id: merchandise.id,\n title: merchandise.title ?? product.title,\n price: line.cost.amountPerQuantity,\n sku: typeof merchandise.sku === \"string\" ? merchandise.sku : null,\n product: {\n id: productId,\n title: product.title,\n vendor,\n productType,\n handle: product.handle,\n },\n },\n },\n ];\n }),\n },\n };\n}\n\nfunction hasPendingCartWork({ pending, revalidating }: CartState): boolean {\n const hasPendingCost = pending.cost ?? (pending.lines.size > 0 || pending.discountCodes.size > 0);\n return revalidating === true || hasPendingCost || pending.note || pending.attributes;\n}\n\nfunction getCartUpdatedAt(cart: CartData): string {\n return cart.updatedAt ?? new Date().toISOString();\n}\n\nfunction syncShopifyCurrency(cart: AnalyticsCart): void {\n if (typeof window === \"undefined\") return;\n\n const currencyCode = getCartCurrencyCode(cart);\n if (!currencyCode) return;\n\n const normalizedCurrencyCode = currencyCode.toUpperCase();\n const shopifyWindow: { Shopify?: { currency?: { active: string } } } = window;\n const shopify = (shopifyWindow.Shopify ??= {});\n if (shopify.currency?.active === normalizedCurrencyCode) return;\n\n shopify.currency = { active: normalizedCurrencyCode };\n}\n\nfunction getCartCurrencyCode(cart: AnalyticsCart): string | undefined {\n return (\n nonEmptyCurrencyCode(cart.cost?.totalAmount?.currencyCode) ??\n nonEmptyCurrencyCode(cart.cost?.subtotalAmount?.currencyCode) ??\n flattenConnection<AnalyticsCartLine>(cart.lines).find(\n (line) => line.merchandise.price.currencyCode,\n )?.merchandise.price.currencyCode\n );\n}\n\nfunction nonEmptyCurrencyCode(currencyCode: string | undefined): string | undefined {\n return currencyCode || undefined;\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
//#region src/core/analytics/custom-consent.ts
|
|
2
|
+
const CUSTOM_CONSENT = Symbol.for("shopify.hydrogen.custom-consent");
|
|
3
|
+
function initializeCustomConsent(setup) {
|
|
4
|
+
if (typeof window === "undefined") return;
|
|
5
|
+
const initialize = (window.Shopify?.analytics)?.[CUSTOM_CONSENT];
|
|
6
|
+
if (!initialize) throw new Error("Custom consent requires Shopify script tags rendered with consent.mode = \"custom-banner\".");
|
|
7
|
+
initialize(setup);
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
//#endregion
|
|
11
|
+
export { initializeCustomConsent };
|
|
12
|
+
//# sourceMappingURL=custom-consent.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"custom-consent.mjs","names":[],"sources":["../../../src/core/analytics/custom-consent.ts"],"sourcesContent":["import type { ConsentSetup, StorefrontAnalytics } from \"./types\";\n\n// The inline bus and hydrated app are separate bundles. This private hook passes\n// the app's setup callback to the bus without serializing it into HTML.\nexport const CUSTOM_CONSENT = Symbol.for(\"shopify.hydrogen.custom-consent\");\n\ntype AnalyticsWithCustomConsent = StorefrontAnalytics & {\n [CUSTOM_CONSENT]?: (setup: ConsentSetup) => void;\n};\n\nexport function initializeCustomConsent(setup: ConsentSetup): void {\n if (typeof window === \"undefined\") return;\n\n const analytics: AnalyticsWithCustomConsent | undefined = window.Shopify?.analytics;\n const initialize = analytics?.[CUSTOM_CONSENT];\n if (!initialize) {\n throw new Error(\n 'Custom consent requires Shopify script tags rendered with consent.mode = \"custom-banner\".',\n );\n }\n initialize(setup);\n}\n"],"mappings":";AAIA,MAAa,iBAAiB,OAAO,IAAI,kCAAkC;AAM3E,SAAgB,wBAAwB,OAA2B;AACjE,KAAI,OAAO,WAAW,YAAa;CAGnC,MAAM,cADoD,OAAO,SAAS,aAC3C;AAC/B,KAAI,CAAC,WACH,OAAM,IAAI,MACR,8FACD;AAEH,YAAW,MAAM"}
|
|
@@ -1,4 +1,11 @@
|
|
|
1
1
|
//#region src/core/analytics/events.d.ts
|
|
2
|
+
/**
|
|
3
|
+
* Canonical event names for the Hydrogen analytics bus.
|
|
4
|
+
*
|
|
5
|
+
* Use these as the `event` argument to `publish()` and a destination's `subscribe()`. Raw string
|
|
6
|
+
* literals are type-checked and narrow the payload the same way; the constants add
|
|
7
|
+
* autocomplete and a single place to rename.
|
|
8
|
+
*/
|
|
2
9
|
declare const AnalyticsEvent: {
|
|
3
10
|
PAGE_VIEWED: "page_viewed";
|
|
4
11
|
PRODUCT_VIEWED: "product_viewed";
|
|
@@ -1,4 +1,11 @@
|
|
|
1
1
|
//#region src/core/analytics/events.ts
|
|
2
|
+
/**
|
|
3
|
+
* Canonical event names for the Hydrogen analytics bus.
|
|
4
|
+
*
|
|
5
|
+
* Use these as the `event` argument to `publish()` and a destination's `subscribe()`. Raw string
|
|
6
|
+
* literals are type-checked and narrow the payload the same way; the constants add
|
|
7
|
+
* autocomplete and a single place to rename.
|
|
8
|
+
*/
|
|
2
9
|
const AnalyticsEvent = {
|
|
3
10
|
PAGE_VIEWED: "page_viewed",
|
|
4
11
|
PRODUCT_VIEWED: "product_viewed",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"events.mjs","names":[],"sources":["../../../src/core/analytics/events.ts"],"sourcesContent":["
|
|
1
|
+
{"version":3,"file":"events.mjs","names":[],"sources":["../../../src/core/analytics/events.ts"],"sourcesContent":["/**\n * Canonical event names for the Hydrogen analytics bus.\n *\n * Use these as the `event` argument to `publish()` and a destination's `subscribe()`. Raw string\n * literals are type-checked and narrow the payload the same way; the constants add\n * autocomplete and a single place to rename.\n */\nexport const AnalyticsEvent = {\n PAGE_VIEWED: \"page_viewed\" as const,\n PRODUCT_VIEWED: \"product_viewed\" as const,\n COLLECTION_VIEWED: \"collection_viewed\" as const,\n CART_VIEWED: \"cart_viewed\" as const,\n SEARCH_VIEWED: \"search_viewed\" as const,\n\n CART_UPDATED: \"cart_updated\" as const,\n PRODUCT_ADD_TO_CART: \"product_added_to_cart\" as const,\n PRODUCT_REMOVED_FROM_CART: \"product_removed_from_cart\" as const,\n};\n\nexport type AnalyticsEventName = (typeof AnalyticsEvent)[keyof typeof AnalyticsEvent];\n"],"mappings":";;;;;;;;AAOA,MAAa,iBAAiB;CAC5B,aAAa;CACb,gBAAgB;CAChB,mBAAmB;CACnB,aAAa;CACb,eAAe;CAEf,cAAc;CACd,qBAAqB;CACrB,2BAA2B;CAC5B"}
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
import { trackCartAnalytics } from "./cart-tracker.mjs";
|
|
2
2
|
import { AnalyticsEvent, AnalyticsEventName } from "./events.mjs";
|
|
3
|
-
import { AnalyticsCart, AnalyticsCartLine, AnalyticsEventMap, CartLineUpdatePayload, CartUpdatePayload, CartViewPayload, CollectionViewPayload, ConsentConfig,
|
|
3
|
+
import { AnalyticsCart, AnalyticsCartLine, AnalyticsEventMap, AnalyticsTrackingValues, CartLineUpdatePayload, CartUpdatePayload, CartViewPayload, CollectionViewPayload, ConsentConfig, ConsentPreferences, ConsentSetup, OtherData, PageViewPayload, PayloadFor, ProductPayload, ProductViewPayload, SearchViewPayload, ShopAnalytics, StorefrontAnalytics, StorefrontAnalyticsConfig, StorefrontAnalyticsDestination, StorefrontAnalyticsDestinationEventContext, StorefrontAnalyticsDestinationSetupContext } from "./types.mjs";
|