@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
|
@@ -9,18 +9,20 @@ var CartActionError = class extends Error {
|
|
|
9
9
|
}
|
|
10
10
|
};
|
|
11
11
|
/**
|
|
12
|
-
*
|
|
12
|
+
* Reads the cart change from a cart request body. Use the function in a custom cart route.
|
|
13
13
|
*
|
|
14
|
-
*
|
|
15
|
-
* `multipart/form-data`
|
|
16
|
-
* different subset of intents — JSON produces `discount-update` while
|
|
17
|
-
* FormData produces `discount-apply` / `discount-remove`.
|
|
14
|
+
* The function accepts `application/json`, `application/x-www-form-urlencoded`, and
|
|
15
|
+
* `multipart/form-data` requests. A JSON request can replace every discount code, and a form submission applies or removes one code.
|
|
18
16
|
*
|
|
19
|
-
*
|
|
20
|
-
* should fall back to the cart cookie.
|
|
17
|
+
* A form submission always returns a `null` cart ID. Read the cart ID for a form submission with `getCartId`.
|
|
21
18
|
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
19
|
+
* The function reads the request body, and you can't read the body again afterward. The function throws when the request has an unsupported content type, an unknown intent, or a missing required field, and when a JSON body combines more than one of `lines`, `discountCodes`, `attributes`, and `note`.
|
|
20
|
+
*
|
|
21
|
+
* @param request The incoming cart request.
|
|
22
|
+
* @returns The cart change and, for a JSON body, the cart ID.
|
|
23
|
+
* @throws If the request has an unsupported content type, an unrecognized intent,
|
|
24
|
+
* or missing required fields, or if a JSON body combines more than one of
|
|
25
|
+
* `lines`, `discountCodes`, `attributes`, and `note`.
|
|
24
26
|
*
|
|
25
27
|
* @example
|
|
26
28
|
* ```ts
|
|
@@ -34,6 +36,7 @@ var CartActionError = class extends Error {
|
|
|
34
36
|
* // ...
|
|
35
37
|
* }
|
|
36
38
|
* ```
|
|
39
|
+
* @publicDocs
|
|
37
40
|
*/
|
|
38
41
|
async function parseCartRequest(request) {
|
|
39
42
|
const contentType = request.headers.get("content-type") ?? "";
|
|
@@ -44,9 +47,17 @@ async function parseCartRequest(request) {
|
|
|
44
47
|
};
|
|
45
48
|
throw new CartActionError(`Unsupported content-type: "${contentType}". Expected application/json or form data.`);
|
|
46
49
|
}
|
|
50
|
+
const JSON_MUTATION_FIELDS = [
|
|
51
|
+
"lines",
|
|
52
|
+
"discountCodes",
|
|
53
|
+
"attributes",
|
|
54
|
+
"note"
|
|
55
|
+
];
|
|
47
56
|
function parseJsonBody(body) {
|
|
48
57
|
assertObject(body);
|
|
49
58
|
const cartId = typeof body.cartId === "string" ? normalizeCartId(body.cartId) : null;
|
|
59
|
+
const fields = JSON_MUTATION_FIELDS.filter((field) => body[field] !== void 0);
|
|
60
|
+
if (fields.length > 1) throw new CartActionError(`Request body must contain only one of "lines", "discountCodes", "attributes", or "note", got ${fields.join(", ")}.`);
|
|
50
61
|
if ("note" in body && typeof body.note === "string") return {
|
|
51
62
|
action: {
|
|
52
63
|
intent: "note-update",
|
|
@@ -101,6 +112,7 @@ function partitionLines(rawLines) {
|
|
|
101
112
|
else if (hasId && quantity === 0) removeIds.push(line.id);
|
|
102
113
|
else if (hasId) updates.push({
|
|
103
114
|
id: line.id,
|
|
115
|
+
...hasMerchandiseId && { merchandiseId: line.merchandiseId },
|
|
104
116
|
quantity,
|
|
105
117
|
...optionalFields
|
|
106
118
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"actions.mjs","names":[],"sources":["../../../src/core/cart/actions.ts"],"sourcesContent":["import { normalizeCartId } from \"./cookie\";\nimport { getCartAttributeFormEntries } from \"./form\";\n\n/** A key-value pair attached to the cart or an individual cart line. */\nexport type CartAttributeInput = { key: string; value: string };\n\nexport type CartLineAddInput = {\n /** Storefront API GID of the product variant to add. */\n merchandiseId: string;\n quantity: number;\n attributes?: CartAttributeInput[];\n /** Selling plan GID for subscription line items. */\n sellingPlanId?: string;\n};\n\n/** Input for updating an existing cart line. Setting `quantity` to `0` removes the line. */\nexport type CartLineUpdateInput = {\n /** The `CartLine.id` of the line to update. */\n id: string;\n quantity: number;\n attributes?: CartAttributeInput[];\n sellingPlanId?: string;\n};\n\n/**\n * Discriminated union of all cart mutation intents.\n *\n * {@link parseCartRequest} normalizes both JSON and FormData requests into one\n * of these variants. The `intent` field determines the Storefront API mutation:\n *\n * - `\"add\"` — add new lines (cartLinesAdd, or cartCreate when no cart exists)\n * - `\"update\"` — change quantity or attributes on existing lines (cartLinesUpdate)\n * - `\"remove\"` — remove lines by ID (cartLinesRemove)\n * - `\"discount-update\"` — replace all discount codes (cartDiscountCodesUpdate)\n * - `\"discount-apply\"` — add a single discount code (read-then-write via cartDiscountCodesUpdate)\n * - `\"discount-remove\"` — remove a single discount code (read-then-write via cartDiscountCodesUpdate)\n * - `\"attributes-update\"` — set cart-level attributes (cartAttributesUpdate)\n * - `\"note-update\"` — set the cart note (cartNoteUpdate)\n */\nexport type CartAction =\n | { intent: \"add\"; lines: CartLineAddInput[] }\n | { intent: \"update\"; lines: CartLineUpdateInput[] }\n | { intent: \"remove\"; lineIds: string[] }\n | { intent: \"discount-update\"; discountCodes: string[] }\n | { intent: \"discount-apply\"; code: string }\n | { intent: \"discount-remove\"; code: string }\n | { intent: \"attributes-update\"; attributes: CartAttributeInput[] }\n | { intent: \"note-update\"; note: string };\n\ntype ParsedCartRequest = {\n action: CartAction;\n cartId: string | null;\n};\n\nclass CartActionError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"CartActionError\";\n }\n}\n\n/**\n * Parses an incoming cart mutation request into a typed {@link CartAction}.\n *\n * Accepts `application/json` and `application/x-www-form-urlencoded` /\n * `multipart/form-data` content types. JSON and FormData each support a\n * different subset of intents — JSON produces `discount-update` while\n * FormData produces `discount-apply` / `discount-remove`.\n *\n * FormData requests always return `cartId: null` — the server handler\n * should fall back to the cart cookie.\n *\n * @throws If the content-type is unsupported, the intent is unrecognized,\n * or required fields are missing.\n *\n * @example\n * ```ts\n * const { action, cartId } = await parseCartRequest(request);\n *\n * switch (action.intent) {\n * case \"add\":\n * return cartLinesAdd(cartId ?? getCartId(request), action.lines);\n * case \"remove\":\n * return cartLinesRemove(cartId ?? getCartId(request), action.lineIds);\n * // ...\n * }\n * ```\n */\nexport async function parseCartRequest(request: Request): Promise<ParsedCartRequest> {\n const contentType = request.headers.get(\"content-type\") ?? \"\";\n\n if (contentType.includes(\"application/json\")) {\n return parseJsonBody(await request.json());\n }\n\n if (\n contentType.includes(\"application/x-www-form-urlencoded\") ||\n contentType.includes(\"multipart/form-data\")\n ) {\n return { action: parseFormData(await request.formData()), cartId: null };\n }\n\n throw new CartActionError(\n `Unsupported content-type: \"${contentType}\". Expected application/json or form data.`,\n );\n}\n\n// --- JSON parsing ---\n\nfunction parseJsonBody(body: unknown): ParsedCartRequest {\n assertObject(body);\n const cartId = typeof body.cartId === \"string\" ? normalizeCartId(body.cartId) : null;\n\n if (\"note\" in body && typeof body.note === \"string\") {\n return { action: { intent: \"note-update\", note: body.note }, cartId };\n }\n\n if (\"discountCodes\" in body && Array.isArray(body.discountCodes)) {\n for (const code of body.discountCodes) {\n assertString(code, \"discountCodes entries\");\n }\n return {\n action: { intent: \"discount-update\", discountCodes: body.discountCodes as string[] },\n cartId,\n };\n }\n\n if (\"attributes\" in body && Array.isArray(body.attributes)) {\n return {\n action: { intent: \"attributes-update\", attributes: parseAttributes(body.attributes) },\n cartId,\n };\n }\n\n if (!(\"lines\" in body) || !Array.isArray(body.lines)) {\n throw new CartActionError(\n 'Request body must contain \"lines\", \"discountCodes\", \"attributes\", or \"note\".',\n );\n }\n\n const lines = body.lines as unknown[];\n if (lines.length === 0) {\n throw new CartActionError(\"Lines array must not be empty.\");\n }\n\n return { action: partitionLines(lines), cartId };\n}\n\nfunction partitionLines(rawLines: unknown[]): CartAction {\n const adds: CartLineAddInput[] = [];\n const updates: CartLineUpdateInput[] = [];\n const removeIds: string[] = [];\n\n for (const raw of rawLines) {\n assertObject(raw);\n const line = raw as Record<string, unknown>;\n const hasId = \"id\" in line && typeof line.id === \"string\" && line.id !== \"\";\n const hasMerchandiseId =\n \"merchandiseId\" in line &&\n typeof line.merchandiseId === \"string\" &&\n line.merchandiseId !== \"\";\n\n assertNonNegativeInteger(line.quantity, \"quantity\");\n const quantity = line.quantity as number;\n\n if (!hasId && !hasMerchandiseId) {\n throw new CartActionError(\n 'Each line must have either \"id\" (for update/remove) or \"merchandiseId\" (for add).',\n );\n }\n\n if (hasId && (line.id as string) === \"\") {\n throw new CartActionError('Line \"id\" must not be empty.');\n }\n\n const optionalFields = extractOptionalLineFields(line);\n\n if (!hasId && hasMerchandiseId) {\n adds.push({\n merchandiseId: line.merchandiseId as string,\n quantity,\n ...optionalFields,\n });\n } else if (hasId && quantity === 0) {\n removeIds.push(line.id as string);\n } else if (hasId) {\n updates.push({\n id: line.id as string,\n quantity,\n ...optionalFields,\n });\n }\n }\n\n const populatedBuckets = [adds.length, updates.length, removeIds.length].filter(\n (n) => n > 0,\n ).length;\n\n if (populatedBuckets > 1) {\n throw new CartActionError(\n \"Mixed line operations are not allowed. Separate add, update, and remove into distinct requests.\",\n );\n }\n\n if (adds.length > 0) return { intent: \"add\", lines: adds };\n if (updates.length > 0) return { intent: \"update\", lines: updates };\n return { intent: \"remove\", lineIds: removeIds };\n}\n\nfunction extractOptionalLineFields(\n line: Record<string, unknown>,\n): Pick<CartLineAddInput, \"attributes\" | \"sellingPlanId\"> {\n const result: Pick<CartLineAddInput, \"attributes\" | \"sellingPlanId\"> = {};\n\n if (\"attributes\" in line && Array.isArray(line.attributes)) {\n result.attributes = line.attributes as CartAttributeInput[];\n }\n\n if (\"sellingPlanId\" in line && typeof line.sellingPlanId === \"string\") {\n result.sellingPlanId = line.sellingPlanId;\n }\n\n return result;\n}\n\n// --- FormData parsing ---\n//\n// HTML forms can't express the same structures as JSON. Key compromises:\n//\n// Add line: single item per submission (forms can't batch multiple adds)\n// Update qty: server does the math — form sends current qty, server increments/decrements\n// Remove line: explicit intent field (can't express \"quantity=0 means remove\" implicitly)\n// Discount bulk: no FormData equivalent — forms handle one code at a time\n// Cart attributes: attributes.<key> fields preserve each key/value relationship\n// Selling plan: add only — no way to change selling plan on existing line via form\n//\n// Design decisions:\n// - `intent` field disambiguates operations (forms need explicit routing; JSON infers from structure)\n// - Field names match where possible: merchandiseId, quantity, lineId, discountCode, note\n// - Form submissions get 303 redirect to referer; JSON gets JSON response\n// - `increase`/`decrease` intents exist because forms can't atomically read-modify-write quantity\n\nconst LINE_INTENTS = new Set([\"increase\", \"decrease\", \"remove\", \"set\"]);\nconst DISCOUNT_INTENTS = new Set([\"discount-apply\", \"discount-remove\"]);\n\nfunction parseFormData(form: FormData): CartAction {\n const intent = formString(form, \"intent\");\n const merchandiseId = formString(form, \"merchandiseId\");\n\n if (!intent && merchandiseId) {\n return parseAddIntent(form, merchandiseId);\n }\n\n if (intent === \"add\") {\n if (!merchandiseId) {\n throw new CartActionError('Intent \"add\" requires a \"merchandiseId\" field.');\n }\n return parseAddIntent(form, merchandiseId);\n }\n\n if (!intent) {\n throw new CartActionError(\n 'FormData must include an \"intent\" field or a \"merchandiseId\" field.',\n );\n }\n\n if (LINE_INTENTS.has(intent)) {\n return parseLineIntent(intent, form);\n }\n\n if (DISCOUNT_INTENTS.has(intent)) {\n return parseDiscountIntent(intent as \"discount-apply\" | \"discount-remove\", form);\n }\n\n if (intent === \"note-update\") {\n return parseNoteIntent(form);\n }\n\n if (intent === \"attributes-update\") {\n return parseAttributesIntent(form);\n }\n\n throw new CartActionError(\n `Unknown intent \"${intent}\". Expected one of: add, increase, decrease, remove, set, discount-apply, discount-remove, attributes-update, note-update.`,\n );\n}\n\nfunction parseLineIntent(intent: string, form: FormData): CartAction {\n const lineId = formString(form, \"lineId\");\n if (!lineId) {\n throw new CartActionError(`Intent \"${intent}\" requires a \"lineId\" field.`);\n }\n\n if (intent === \"remove\") {\n return { intent: \"remove\", lineIds: [lineId] };\n }\n\n const quantity = formInt(form, \"quantity\");\n if (quantity === null) {\n throw new CartActionError(`Intent \"${intent}\" requires a \"quantity\" field.`);\n }\n\n if (intent === \"set\") {\n if (quantity <= 0) {\n return { intent: \"remove\", lineIds: [lineId] };\n }\n return { intent: \"update\", lines: [{ id: lineId, quantity }] };\n }\n\n if (intent === \"increase\") {\n return { intent: \"update\", lines: [{ id: lineId, quantity: quantity + 1 }] };\n }\n\n const decremented = quantity - 1;\n if (decremented <= 0) {\n return { intent: \"remove\", lineIds: [lineId] };\n }\n return { intent: \"update\", lines: [{ id: lineId, quantity: decremented }] };\n}\n\nfunction parseDiscountIntent(\n intent: \"discount-apply\" | \"discount-remove\",\n form: FormData,\n): CartAction {\n const code = formString(form, \"discountCode\");\n if (!code) {\n throw new CartActionError(`Intent \"${intent}\" requires a \"discountCode\" field.`);\n }\n return { intent, code };\n}\n\nfunction parseAddIntent(form: FormData, merchandiseId: string): CartAction {\n const quantity = formInt(form, \"quantity\") ?? 1;\n const sellingPlanId = formString(form, \"sellingPlanId\");\n\n const line: CartLineAddInput = { merchandiseId, quantity };\n if (sellingPlanId) line.sellingPlanId = sellingPlanId;\n\n const rawAttributes = getCartAttributeFormEntries(form);\n if (rawAttributes.length > 0) {\n line.attributes = parseAttributes(rawAttributes);\n }\n\n return { intent: \"add\", lines: [line] };\n}\n\nfunction parseNoteIntent(form: FormData): CartAction {\n const noteValue = form.get(\"note\");\n if (noteValue === null) {\n throw new CartActionError('Intent \"note-update\" requires a \"note\" field.');\n }\n return { intent: \"note-update\", note: String(noteValue) };\n}\n\nfunction parseAttributesIntent(form: FormData): CartAction {\n return {\n intent: \"attributes-update\",\n attributes: parseAttributes(getCartAttributeFormEntries(form)),\n };\n}\n\nfunction parseAttributes(values: unknown[]): CartAttributeInput[] {\n return values.map((value, index) => {\n assertObject(value);\n assertString(value.key, `attributes[${index}].key`);\n assertString(value.value, `attributes[${index}].value`);\n if (!value.key) {\n throw new CartActionError(`Expected \"attributes[${index}].key\" not to be empty.`);\n }\n return { key: value.key, value: value.value };\n });\n}\n\n// --- Assertion helpers ---\n\nfunction assertObject(value: unknown): asserts value is Record<string, unknown> {\n if (typeof value !== \"object\" || value === null || Array.isArray(value)) {\n throw new CartActionError(\"Expected an object.\");\n }\n}\n\nfunction assertString(value: unknown, label: string): asserts value is string {\n if (typeof value !== \"string\") {\n throw new CartActionError(`Expected \"${label}\" to be a string.`);\n }\n}\n\nfunction assertNonNegativeInteger(value: unknown, label: string): asserts value is number {\n if (typeof value !== \"number\" || !Number.isInteger(value) || value < 0) {\n throw new CartActionError(\n `Expected \"${label}\" to be a non-negative integer, got ${JSON.stringify(value)}.`,\n );\n }\n}\n\n// --- FormData field helpers ---\n\nfunction formString(form: FormData, key: string): string | null {\n const value = form.get(key);\n if (value === null || typeof value !== \"string\") return null;\n return value || null;\n}\n\nfunction formInt(form: FormData, key: string): number | null {\n const raw = form.get(key);\n if (raw === null || typeof raw !== \"string\") return null;\n const parsed = parseInt(raw, 10);\n if (Number.isNaN(parsed)) return null;\n return parsed;\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"actions.mjs","names":[],"sources":["../../../src/core/cart/actions.ts"],"sourcesContent":["import { normalizeCartId } from \"./cookie\";\nimport { getCartAttributeFormEntries } from \"./form\";\n\n/** A custom key-value pair for the cart or a cart line. */\nexport type CartAttributeInput = {\n /** The attribute name. parseCartRequest throws for an empty name in cart attributes and in form submissions. */\n key: string;\n /** The attribute value. */\n value: string;\n};\n\n/** Input for adding a line to the cart. */\nexport type CartLineAddInput = {\n /** The Storefront API GID of the product variant to add. */\n merchandiseId: string;\n /** Number of units to add. A form submission without a quantity adds `1`. */\n quantity: number;\n /** Custom key-value attributes to set on the new line. */\n attributes?: CartAttributeInput[];\n /** The selling plan GID for a subscription line. */\n sellingPlanId?: string;\n};\n\n/** Input for updating an existing cart line. A quantity of `0` removes the line. */\nexport type CartLineUpdateInput = {\n /** The ID of the cart line to update. */\n id: string;\n /** The GID of a product variant to swap the line to. */\n merchandiseId?: string;\n /** The line's new quantity. */\n quantity: number;\n /** Custom key-value attributes to set on the line. */\n attributes?: CartAttributeInput[];\n /** The selling plan GID to assign to the line for a subscription. */\n sellingPlanId?: string;\n};\n\n/**\n * A cart change that `parseCartRequest` reads from a JSON or form request.\n *\n * The `intent` field names the change and the Storefront API mutation that applies the change:\n *\n * - `add` adds new lines with `cartLinesAdd`, or creates a cart with `cartCreate` when none exists.\n * - `update` changes quantity, merchandise, or attributes on existing lines with `cartLinesUpdate`.\n * - `remove` removes lines by ID with `cartLinesRemove`.\n * - `discount-update` replaces all discount codes with `cartDiscountCodesUpdate`.\n * - `discount-apply` adds one discount code. The handler reads the current codes, then writes the new list with `cartDiscountCodesUpdate`.\n * - `discount-remove` removes one discount code with the same read-then-write approach.\n * - `attributes-update` sets cart-level attributes with `cartAttributesUpdate`.\n * - `note-update` sets the cart note with `cartNoteUpdate`.\n *\n * @publicDocs\n */\nexport type CartAction =\n | { intent: \"add\"; lines: CartLineAddInput[] }\n | { intent: \"update\"; lines: CartLineUpdateInput[] }\n | { intent: \"remove\"; lineIds: string[] }\n | { intent: \"discount-update\"; discountCodes: string[] }\n | { intent: \"discount-apply\"; code: string }\n | { intent: \"discount-remove\"; code: string }\n | { intent: \"attributes-update\"; attributes: CartAttributeInput[] }\n | { intent: \"note-update\"; note: string };\n\n/** The cart change and the cart ID that `parseCartRequest` reads from the request body. */\ntype ParsedCartRequest = {\n action: CartAction;\n /** The cart GID from a JSON body, or `null` for a form submission or a JSON body without a cart ID. The function adds the `gid://shopify/Cart/` prefix to a bare cart token. */\n cartId: string | null;\n};\n\nclass CartActionError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"CartActionError\";\n }\n}\n\n/**\n * Reads the cart change from a cart request body. Use the function in a custom cart route.\n *\n * The function accepts `application/json`, `application/x-www-form-urlencoded`, and\n * `multipart/form-data` requests. A JSON request can replace every discount code, and a form submission applies or removes one code.\n *\n * A form submission always returns a `null` cart ID. Read the cart ID for a form submission with `getCartId`.\n *\n * The function reads the request body, and you can't read the body again afterward. The function throws when the request has an unsupported content type, an unknown intent, or a missing required field, and when a JSON body combines more than one of `lines`, `discountCodes`, `attributes`, and `note`.\n *\n * @param request The incoming cart request.\n * @returns The cart change and, for a JSON body, the cart ID.\n * @throws If the request has an unsupported content type, an unrecognized intent,\n * or missing required fields, or if a JSON body combines more than one of\n * `lines`, `discountCodes`, `attributes`, and `note`.\n *\n * @example\n * ```ts\n * const { action, cartId } = await parseCartRequest(request);\n *\n * switch (action.intent) {\n * case \"add\":\n * return cartLinesAdd(cartId ?? getCartId(request), action.lines);\n * case \"remove\":\n * return cartLinesRemove(cartId ?? getCartId(request), action.lineIds);\n * // ...\n * }\n * ```\n * @publicDocs\n */\nexport async function parseCartRequest(request: Request): Promise<ParsedCartRequest> {\n const contentType = request.headers.get(\"content-type\") ?? \"\";\n\n if (contentType.includes(\"application/json\")) {\n return parseJsonBody(await request.json());\n }\n\n if (\n contentType.includes(\"application/x-www-form-urlencoded\") ||\n contentType.includes(\"multipart/form-data\")\n ) {\n return { action: parseFormData(await request.formData()), cartId: null };\n }\n\n throw new CartActionError(\n `Unsupported content-type: \"${contentType}\". Expected application/json or form data.`,\n );\n}\n\n// --- JSON parsing ---\n\nconst JSON_MUTATION_FIELDS = [\"lines\", \"discountCodes\", \"attributes\", \"note\"] as const;\n\nfunction parseJsonBody(body: unknown): ParsedCartRequest {\n assertObject(body);\n const cartId = typeof body.cartId === \"string\" ? normalizeCartId(body.cartId) : null;\n\n // Each request runs one Storefront API mutation, so any other field would be silently dropped.\n const fields = JSON_MUTATION_FIELDS.filter((field) => body[field] !== undefined);\n if (fields.length > 1) {\n throw new CartActionError(\n `Request body must contain only one of \"lines\", \"discountCodes\", \"attributes\", or \"note\", got ${fields.join(\", \")}.`,\n );\n }\n\n if (\"note\" in body && typeof body.note === \"string\") {\n return { action: { intent: \"note-update\", note: body.note }, cartId };\n }\n\n if (\"discountCodes\" in body && Array.isArray(body.discountCodes)) {\n for (const code of body.discountCodes) {\n assertString(code, \"discountCodes entries\");\n }\n return {\n action: { intent: \"discount-update\", discountCodes: body.discountCodes as string[] },\n cartId,\n };\n }\n\n if (\"attributes\" in body && Array.isArray(body.attributes)) {\n return {\n action: { intent: \"attributes-update\", attributes: parseAttributes(body.attributes) },\n cartId,\n };\n }\n\n if (!(\"lines\" in body) || !Array.isArray(body.lines)) {\n throw new CartActionError(\n 'Request body must contain \"lines\", \"discountCodes\", \"attributes\", or \"note\".',\n );\n }\n\n const lines = body.lines as unknown[];\n if (lines.length === 0) {\n throw new CartActionError(\"Lines array must not be empty.\");\n }\n\n return { action: partitionLines(lines), cartId };\n}\n\nfunction partitionLines(rawLines: unknown[]): CartAction {\n const adds: CartLineAddInput[] = [];\n const updates: CartLineUpdateInput[] = [];\n const removeIds: string[] = [];\n\n for (const raw of rawLines) {\n assertObject(raw);\n const line = raw as Record<string, unknown>;\n const hasId = \"id\" in line && typeof line.id === \"string\" && line.id !== \"\";\n const hasMerchandiseId =\n \"merchandiseId\" in line &&\n typeof line.merchandiseId === \"string\" &&\n line.merchandiseId !== \"\";\n\n assertNonNegativeInteger(line.quantity, \"quantity\");\n const quantity = line.quantity as number;\n\n if (!hasId && !hasMerchandiseId) {\n throw new CartActionError(\n 'Each line must have either \"id\" (for update/remove) or \"merchandiseId\" (for add).',\n );\n }\n\n if (hasId && (line.id as string) === \"\") {\n throw new CartActionError('Line \"id\" must not be empty.');\n }\n\n const optionalFields = extractOptionalLineFields(line);\n\n if (!hasId && hasMerchandiseId) {\n adds.push({\n merchandiseId: line.merchandiseId as string,\n quantity,\n ...optionalFields,\n });\n } else if (hasId && quantity === 0) {\n removeIds.push(line.id as string);\n } else if (hasId) {\n updates.push({\n id: line.id as string,\n ...(hasMerchandiseId && { merchandiseId: line.merchandiseId as string }),\n quantity,\n ...optionalFields,\n });\n }\n }\n\n const populatedBuckets = [adds.length, updates.length, removeIds.length].filter(\n (n) => n > 0,\n ).length;\n\n if (populatedBuckets > 1) {\n throw new CartActionError(\n \"Mixed line operations are not allowed. Separate add, update, and remove into distinct requests.\",\n );\n }\n\n if (adds.length > 0) return { intent: \"add\", lines: adds };\n if (updates.length > 0) return { intent: \"update\", lines: updates };\n return { intent: \"remove\", lineIds: removeIds };\n}\n\nfunction extractOptionalLineFields(\n line: Record<string, unknown>,\n): Pick<CartLineAddInput, \"attributes\" | \"sellingPlanId\"> {\n const result: Pick<CartLineAddInput, \"attributes\" | \"sellingPlanId\"> = {};\n\n if (\"attributes\" in line && Array.isArray(line.attributes)) {\n result.attributes = line.attributes as CartAttributeInput[];\n }\n\n if (\"sellingPlanId\" in line && typeof line.sellingPlanId === \"string\") {\n result.sellingPlanId = line.sellingPlanId;\n }\n\n return result;\n}\n\n// --- FormData parsing ---\n//\n// HTML forms can't express the same structures as JSON. Key compromises:\n//\n// Add line: single item per submission (forms can't batch multiple adds)\n// Update qty: server does the math — form sends current qty, server increments/decrements\n// Remove line: explicit intent field (can't express \"quantity=0 means remove\" implicitly)\n// Discount bulk: no FormData equivalent — forms handle one code at a time\n// Cart attributes: attributes.<key> fields preserve each key/value relationship\n// Selling plan: add only — no way to change selling plan on existing line via form\n//\n// Design decisions:\n// - `intent` field disambiguates operations (forms need explicit routing; JSON infers from structure)\n// - Field names match where possible: merchandiseId, quantity, lineId, discountCode, note\n// - Form submissions get 303 redirect to referer; JSON gets JSON response\n// - `increase`/`decrease` intents exist because forms can't atomically read-modify-write quantity\n\nconst LINE_INTENTS = new Set([\"increase\", \"decrease\", \"remove\", \"set\"]);\nconst DISCOUNT_INTENTS = new Set([\"discount-apply\", \"discount-remove\"]);\n\nfunction parseFormData(form: FormData): CartAction {\n const intent = formString(form, \"intent\");\n const merchandiseId = formString(form, \"merchandiseId\");\n\n if (!intent && merchandiseId) {\n return parseAddIntent(form, merchandiseId);\n }\n\n if (intent === \"add\") {\n if (!merchandiseId) {\n throw new CartActionError('Intent \"add\" requires a \"merchandiseId\" field.');\n }\n return parseAddIntent(form, merchandiseId);\n }\n\n if (!intent) {\n throw new CartActionError(\n 'FormData must include an \"intent\" field or a \"merchandiseId\" field.',\n );\n }\n\n if (LINE_INTENTS.has(intent)) {\n return parseLineIntent(intent, form);\n }\n\n if (DISCOUNT_INTENTS.has(intent)) {\n return parseDiscountIntent(intent as \"discount-apply\" | \"discount-remove\", form);\n }\n\n if (intent === \"note-update\") {\n return parseNoteIntent(form);\n }\n\n if (intent === \"attributes-update\") {\n return parseAttributesIntent(form);\n }\n\n throw new CartActionError(\n `Unknown intent \"${intent}\". Expected one of: add, increase, decrease, remove, set, discount-apply, discount-remove, attributes-update, note-update.`,\n );\n}\n\nfunction parseLineIntent(intent: string, form: FormData): CartAction {\n const lineId = formString(form, \"lineId\");\n if (!lineId) {\n throw new CartActionError(`Intent \"${intent}\" requires a \"lineId\" field.`);\n }\n\n if (intent === \"remove\") {\n return { intent: \"remove\", lineIds: [lineId] };\n }\n\n const quantity = formInt(form, \"quantity\");\n if (quantity === null) {\n throw new CartActionError(`Intent \"${intent}\" requires a \"quantity\" field.`);\n }\n\n if (intent === \"set\") {\n if (quantity <= 0) {\n return { intent: \"remove\", lineIds: [lineId] };\n }\n return { intent: \"update\", lines: [{ id: lineId, quantity }] };\n }\n\n if (intent === \"increase\") {\n return { intent: \"update\", lines: [{ id: lineId, quantity: quantity + 1 }] };\n }\n\n const decremented = quantity - 1;\n if (decremented <= 0) {\n return { intent: \"remove\", lineIds: [lineId] };\n }\n return { intent: \"update\", lines: [{ id: lineId, quantity: decremented }] };\n}\n\nfunction parseDiscountIntent(\n intent: \"discount-apply\" | \"discount-remove\",\n form: FormData,\n): CartAction {\n const code = formString(form, \"discountCode\");\n if (!code) {\n throw new CartActionError(`Intent \"${intent}\" requires a \"discountCode\" field.`);\n }\n return { intent, code };\n}\n\nfunction parseAddIntent(form: FormData, merchandiseId: string): CartAction {\n const quantity = formInt(form, \"quantity\") ?? 1;\n const sellingPlanId = formString(form, \"sellingPlanId\");\n\n const line: CartLineAddInput = { merchandiseId, quantity };\n if (sellingPlanId) line.sellingPlanId = sellingPlanId;\n\n const rawAttributes = getCartAttributeFormEntries(form);\n if (rawAttributes.length > 0) {\n line.attributes = parseAttributes(rawAttributes);\n }\n\n return { intent: \"add\", lines: [line] };\n}\n\nfunction parseNoteIntent(form: FormData): CartAction {\n const noteValue = form.get(\"note\");\n if (noteValue === null) {\n throw new CartActionError('Intent \"note-update\" requires a \"note\" field.');\n }\n return { intent: \"note-update\", note: String(noteValue) };\n}\n\nfunction parseAttributesIntent(form: FormData): CartAction {\n return {\n intent: \"attributes-update\",\n attributes: parseAttributes(getCartAttributeFormEntries(form)),\n };\n}\n\nfunction parseAttributes(values: unknown[]): CartAttributeInput[] {\n return values.map((value, index) => {\n assertObject(value);\n assertString(value.key, `attributes[${index}].key`);\n assertString(value.value, `attributes[${index}].value`);\n if (!value.key) {\n throw new CartActionError(`Expected \"attributes[${index}].key\" not to be empty.`);\n }\n return { key: value.key, value: value.value };\n });\n}\n\n// --- Assertion helpers ---\n\nfunction assertObject(value: unknown): asserts value is Record<string, unknown> {\n if (typeof value !== \"object\" || value === null || Array.isArray(value)) {\n throw new CartActionError(\"Expected an object.\");\n }\n}\n\nfunction assertString(value: unknown, label: string): asserts value is string {\n if (typeof value !== \"string\") {\n throw new CartActionError(`Expected \"${label}\" to be a string.`);\n }\n}\n\nfunction assertNonNegativeInteger(value: unknown, label: string): asserts value is number {\n if (typeof value !== \"number\" || !Number.isInteger(value) || value < 0) {\n throw new CartActionError(\n `Expected \"${label}\" to be a non-negative integer, got ${JSON.stringify(value)}.`,\n );\n }\n}\n\n// --- FormData field helpers ---\n\nfunction formString(form: FormData, key: string): string | null {\n const value = form.get(key);\n if (value === null || typeof value !== \"string\") return null;\n return value || null;\n}\n\nfunction formInt(form: FormData, key: string): number | null {\n const raw = form.get(key);\n if (raw === null || typeof raw !== \"string\") return null;\n const parsed = parseInt(raw, 10);\n if (Number.isNaN(parsed)) return null;\n return parsed;\n}\n"],"mappings":"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"}
|
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
//#region src/core/cart/attach-quantity-input.d.ts
|
|
2
2
|
/**
|
|
3
|
-
*
|
|
3
|
+
* Submits a cart form when the customer changes the form's quantity input.
|
|
4
4
|
*
|
|
5
|
-
*
|
|
6
|
-
* first submit button (which must carry `name="intent" value="set"`). Returns
|
|
7
|
-
* a cleanup function that removes the event listener.
|
|
5
|
+
* Make the form's first submit button the set button from `register("set")`. The function throws when the first submit button is any other button.
|
|
8
6
|
*
|
|
9
|
-
*
|
|
10
|
-
* `register("set")` from `createCartFormRegister` to produce it.
|
|
7
|
+
* In React, register the quantity field with `interactive: true` in `useCartForm`, which calls the function for you. Call the function yourself in Vue and in frameworks without a binding.
|
|
11
8
|
*
|
|
12
|
-
* @
|
|
9
|
+
* @param inputEl The quantity input that submits the form on change.
|
|
10
|
+
* @param formEl The cart form that holds the input and the set button.
|
|
11
|
+
* @returns A function that stops the automatic submits. Call the function when you remove the input.
|
|
12
|
+
* @throws If the form's first submit button isn't the set button, which has `name="intent"` and `value="set"`.
|
|
13
13
|
*
|
|
14
14
|
* @example
|
|
15
15
|
* ```ts
|
|
@@ -24,6 +24,7 @@
|
|
|
24
24
|
* detach();
|
|
25
25
|
* }
|
|
26
26
|
* ```
|
|
27
|
+
* @publicDocs
|
|
27
28
|
*/
|
|
28
29
|
declare function attachQuantityInput(inputEl: HTMLInputElement, formEl: HTMLFormElement): () => void;
|
|
29
30
|
//#endregion
|
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
//#region src/core/cart/attach-quantity-input.ts
|
|
2
2
|
const FIRST_SUBMIT_BUTTON_SELECTOR = "button:not([type=button])";
|
|
3
3
|
/**
|
|
4
|
-
*
|
|
4
|
+
* Submits a cart form when the customer changes the form's quantity input.
|
|
5
5
|
*
|
|
6
|
-
*
|
|
7
|
-
* first submit button (which must carry `name="intent" value="set"`). Returns
|
|
8
|
-
* a cleanup function that removes the event listener.
|
|
6
|
+
* Make the form's first submit button the set button from `register("set")`. The function throws when the first submit button is any other button.
|
|
9
7
|
*
|
|
10
|
-
*
|
|
11
|
-
* `register("set")` from `createCartFormRegister` to produce it.
|
|
8
|
+
* In React, register the quantity field with `interactive: true` in `useCartForm`, which calls the function for you. Call the function yourself in Vue and in frameworks without a binding.
|
|
12
9
|
*
|
|
13
|
-
* @
|
|
10
|
+
* @param inputEl The quantity input that submits the form on change.
|
|
11
|
+
* @param formEl The cart form that holds the input and the set button.
|
|
12
|
+
* @returns A function that stops the automatic submits. Call the function when you remove the input.
|
|
13
|
+
* @throws If the form's first submit button isn't the set button, which has `name="intent"` and `value="set"`.
|
|
14
14
|
*
|
|
15
15
|
* @example
|
|
16
16
|
* ```ts
|
|
@@ -25,6 +25,7 @@ const FIRST_SUBMIT_BUTTON_SELECTOR = "button:not([type=button])";
|
|
|
25
25
|
* detach();
|
|
26
26
|
* }
|
|
27
27
|
* ```
|
|
28
|
+
* @publicDocs
|
|
28
29
|
*/
|
|
29
30
|
function attachQuantityInput(inputEl, formEl) {
|
|
30
31
|
const firstSubmitButton = formEl.querySelector(FIRST_SUBMIT_BUTTON_SELECTOR);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"attach-quantity-input.mjs","names":[],"sources":["../../../src/core/cart/attach-quantity-input.ts"],"sourcesContent":["const FIRST_SUBMIT_BUTTON_SELECTOR = \"button:not([type=button])\";\n\n/**\n *
|
|
1
|
+
{"version":3,"file":"attach-quantity-input.mjs","names":[],"sources":["../../../src/core/cart/attach-quantity-input.ts"],"sourcesContent":["const FIRST_SUBMIT_BUTTON_SELECTOR = \"button:not([type=button])\";\n\n/**\n * Submits a cart form when the customer changes the form's quantity input.\n *\n * Make the form's first submit button the set button from `register(\"set\")`. The function throws when the first submit button is any other button.\n *\n * In React, register the quantity field with `interactive: true` in `useCartForm`, which calls the function for you. Call the function yourself in Vue and in frameworks without a binding.\n *\n * @param inputEl The quantity input that submits the form on change.\n * @param formEl The cart form that holds the input and the set button.\n * @returns A function that stops the automatic submits. Call the function when you remove the input.\n * @throws If the form's first submit button isn't the set button, which has `name=\"intent\"` and `value=\"set\"`.\n *\n * @example\n * ```ts\n * const input = document.querySelector<HTMLInputElement>('input[name=\"quantity\"]');\n * const form = input?.closest('form');\n *\n * if (input && form) {\n * const detach = attachQuantityInput(input, form);\n * // Input changes now auto-submit the form\n *\n * // Clean up when unmounting\n * detach();\n * }\n * ```\n * @publicDocs\n */\nexport function attachQuantityInput(\n inputEl: HTMLInputElement,\n formEl: HTMLFormElement,\n): () => void {\n const firstSubmitButton = formEl.querySelector(\n FIRST_SUBMIT_BUTTON_SELECTOR,\n ) as HTMLButtonElement | null;\n\n if (\n !firstSubmitButton ||\n firstSubmitButton.name !== \"intent\" ||\n firstSubmitButton.value !== \"set\"\n ) {\n throw new Error(\n `The first button in cart form must have a name of \"intent\" and value of \"set\". Your UI will not behave as expected.`,\n { cause: formEl },\n );\n }\n\n const handleChange = (e: Event) => {\n if (formEl.isConnected && e.target === inputEl) {\n formEl.requestSubmit(firstSubmitButton);\n }\n };\n\n inputEl.addEventListener(\"change\", handleChange);\n\n return () => {\n inputEl.removeEventListener(\"change\", handleChange);\n };\n}\n"],"mappings":";AAAA,MAAM,+BAA+B;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA6BrC,SAAgB,oBACd,SACA,QACY;CACZ,MAAM,oBAAoB,OAAO,cAC/B,6BACD;AAED,KACE,CAAC,qBACD,kBAAkB,SAAS,YAC3B,kBAAkB,UAAU,MAE5B,OAAM,IAAI,MACR,uHACA,EAAE,OAAO,QAAQ,CAClB;CAGH,MAAM,gBAAgB,MAAa;AACjC,MAAI,OAAO,eAAe,EAAE,WAAW,QACrC,QAAO,cAAc,kBAAkB;;AAI3C,SAAQ,iBAAiB,UAAU,aAAa;AAEhD,cAAa;AACX,UAAQ,oBAAoB,UAAU,aAAa"}
|
|
@@ -1,20 +1,21 @@
|
|
|
1
1
|
import { StorefrontClient } from "../../client/types.mjs";
|
|
2
2
|
//#region src/core/cart/buyer-identity-sync.d.ts
|
|
3
3
|
declare const cartBuyerIdentitySync: unique symbol;
|
|
4
|
+
/** The request with the `cart` cookie and the Storefront API client that updates the cart. */
|
|
4
5
|
type CartBuyerIdentitySyncContext = {
|
|
5
|
-
request: Request;
|
|
6
|
+
/** The incoming request. The `cart` cookie on the request identifies the cart. */request: Request; /** The Storefront API client that updates the cart's buyer identity. */
|
|
6
7
|
storefrontClient: StorefrontClient;
|
|
7
8
|
};
|
|
8
9
|
/**
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
10
|
+
* Connects the cart to the customer when the customer logs in, and disconnects the cart when the customer logs out.
|
|
11
|
+
*
|
|
12
|
+
* Create the cart server handlers with `customerSession`, then pass the handlers to `createCustomerAccountServerHandlers` through the `cartServerHandlers` option. The customer account handlers then update the cart for you.
|
|
12
13
|
*/
|
|
13
14
|
type CartBuyerIdentitySync = {
|
|
14
|
-
/** Attaches
|
|
15
|
+
/** Attaches the customer to the cart in the request's `cart` cookie, or detaches the customer when the token is `null`. */updateBuyerIdentity(context: CartBuyerIdentitySyncContext, customerAccessToken: string | null): Promise<void>; /** A `Set-Cookie` value that clears the `cart` cookie. The customer account handlers send the value when they can't detach the customer from the cart. */
|
|
15
16
|
readonly expiredCartCookie: string;
|
|
16
17
|
};
|
|
17
|
-
/** Cart server handlers
|
|
18
|
+
/** Cart server handlers that you created with `customerSession`. */
|
|
18
19
|
type CartBuyerIdentitySyncSource = {
|
|
19
20
|
readonly [cartBuyerIdentitySync]: CartBuyerIdentitySync;
|
|
20
21
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"buyer-identity-sync.mjs","names":[],"sources":["../../../src/core/cart/buyer-identity-sync.ts"],"sourcesContent":["import type { StorefrontClient } from \"../../client\";\n\n// Deliberately a tiny standalone module: customer-account handlers import it at\n// runtime, so it must not pull the cart handler graph into their bundle.\nexport const cartBuyerIdentitySync: unique symbol = Symbol(\"hydrogen.cartBuyerIdentitySync\");\n\nexport type CartBuyerIdentitySyncContext = {\n request: Request;\n storefrontClient: StorefrontClient;\n};\n\n/**\n *
|
|
1
|
+
{"version":3,"file":"buyer-identity-sync.mjs","names":[],"sources":["../../../src/core/cart/buyer-identity-sync.ts"],"sourcesContent":["import type { StorefrontClient } from \"../../client\";\n\n// Deliberately a tiny standalone module: customer-account handlers import it at\n// runtime, so it must not pull the cart handler graph into their bundle.\nexport const cartBuyerIdentitySync: unique symbol = Symbol(\"hydrogen.cartBuyerIdentitySync\");\n\n/** The request with the `cart` cookie and the Storefront API client that updates the cart. */\nexport type CartBuyerIdentitySyncContext = {\n /** The incoming request. The `cart` cookie on the request identifies the cart. */\n request: Request;\n /** The Storefront API client that updates the cart's buyer identity. */\n storefrontClient: StorefrontClient;\n};\n\n/**\n * Connects the cart to the customer when the customer logs in, and disconnects the cart when the customer logs out.\n *\n * Create the cart server handlers with `customerSession`, then pass the handlers to `createCustomerAccountServerHandlers` through the `cartServerHandlers` option. The customer account handlers then update the cart for you.\n */\nexport type CartBuyerIdentitySync = {\n /** Attaches the customer to the cart in the request's `cart` cookie, or detaches the customer when the token is `null`. */\n updateBuyerIdentity(\n context: CartBuyerIdentitySyncContext,\n customerAccessToken: string | null,\n ): Promise<void>;\n /** A `Set-Cookie` value that clears the `cart` cookie. The customer account handlers send the value when they can't detach the customer from the cart. */\n readonly expiredCartCookie: string;\n};\n\n/** Cart server handlers that you created with `customerSession`. */\nexport type CartBuyerIdentitySyncSource = {\n readonly [cartBuyerIdentitySync]: CartBuyerIdentitySync;\n};\n\n// Accepts a partial source because JavaScript callers can pass cart handlers\n// created without customerSession; the caller turns undefined into an error.\nexport function getCartBuyerIdentitySync(\n source: Partial<CartBuyerIdentitySyncSource>,\n): CartBuyerIdentitySync | undefined {\n return source[cartBuyerIdentitySync];\n}\n"],"mappings":";AAIA,MAAa,wBAAuC,OAAO,iCAAiC;AAgC5F,SAAgB,yBACd,QACmC;AACnC,QAAO,OAAO"}
|
|
@@ -1,28 +1,32 @@
|
|
|
1
1
|
import { CartData, CartState } from "./state.mjs";
|
|
2
2
|
|
|
3
3
|
//#region src/core/cart/cart.d.ts
|
|
4
|
-
/**
|
|
4
|
+
/** The timeout in milliseconds for cart loads and mutations. */
|
|
5
5
|
declare const STANDARD_ACTION_TIMEOUT_IN_MS = 30000;
|
|
6
6
|
/**
|
|
7
|
-
*
|
|
7
|
+
* The error that a cart load or cart change throws when your cart endpoint responds with a non-2xx status. Read `status` to handle specific failures.
|
|
8
8
|
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
9
|
+
* Without a cart endpoint from `configureCartEndpoint`, cart requests fail with other error types.
|
|
10
|
+
*
|
|
11
|
+
* @publicDocs
|
|
11
12
|
*/
|
|
12
13
|
declare class CartNetworkError extends Error {
|
|
14
|
+
/** The HTTP status code that the cart endpoint returned. */
|
|
13
15
|
readonly status: number;
|
|
16
|
+
/** Always `CartNetworkError`. */
|
|
17
|
+
name: string;
|
|
18
|
+
/** Always `"Something went wrong updating your cart. Please try again."`, including for a failed cart load. */
|
|
19
|
+
message: string;
|
|
20
|
+
/** The stack trace, when the JavaScript runtime records one. */
|
|
21
|
+
stack?: string;
|
|
14
22
|
constructor(status: number);
|
|
15
23
|
}
|
|
16
24
|
/**
|
|
17
|
-
*
|
|
25
|
+
* Holds the customer's cart and runs cart changes in any framework. In React, `CartProvider` creates the store for you.
|
|
18
26
|
*
|
|
19
|
-
* The store
|
|
20
|
-
* *intent*. Cart-level costs and totals are always server-authoritative, but
|
|
21
|
-
* line-level data (quantity, estimated cost) and discount applicability are
|
|
22
|
-
* projected optimistically until the server response arrives.
|
|
27
|
+
* The store shows line and discount code changes before the server responds, then updates the cart from each server response. Cart costs change only when the server responds.
|
|
23
28
|
*
|
|
24
|
-
*
|
|
25
|
-
* forms) or through Shopify Standard Action `shopify:cart:*` events.
|
|
29
|
+
* Cart changes start from cart form submissions or from Standard Actions `shopify:cart:*` events. Cart changes need the Standard Actions script, which `ShopifyScripts` adds to the page. Cart loads and changes time out after 30 seconds.
|
|
26
30
|
*
|
|
27
31
|
* @example
|
|
28
32
|
* ```ts
|
|
@@ -41,67 +45,71 @@ declare class CartNetworkError extends Error {
|
|
|
41
45
|
*/
|
|
42
46
|
type CartStore = {
|
|
43
47
|
/**
|
|
44
|
-
*
|
|
45
|
-
* and triggers the initial cart load if no `initialData` was provided.
|
|
46
|
-
* Idempotent; no-op without `document` (SSR-safe).
|
|
48
|
+
* Starts handling cart events, and loads the cart when you created the store without initial data. Repeated calls have no effect. During server rendering, the method does nothing.
|
|
47
49
|
*/
|
|
48
|
-
connect(): void; /**
|
|
49
|
-
destroy(): void; /** Replaces the
|
|
50
|
-
hydrate(data: CartData): void; /** Returns the current
|
|
50
|
+
connect(): void; /** Stops the store. Cancels in-flight cart requests and stops handling cart events. */
|
|
51
|
+
destroy(): void; /** Replaces the store's cart with a different cart that the server loaded. The method ignores data for the cart that the store already holds, and ignores data while cart changes are in flight. */
|
|
52
|
+
hydrate(data: CartData): void; /** Returns the current cart state, including changes that the server hasn't confirmed yet. */
|
|
51
53
|
getState(): CartState;
|
|
52
54
|
/**
|
|
53
|
-
*
|
|
54
|
-
* Compatible with React's `useSyncExternalStore`.
|
|
55
|
+
* Calls the listener on every state change and returns a function that removes the listener. The method works with React's `useSyncExternalStore`.
|
|
55
56
|
*
|
|
56
|
-
* @param listener -
|
|
57
|
+
* @param listener - The function that receives each new state.
|
|
58
|
+
*/
|
|
59
|
+
subscribe(listener: (state: CartState) => void): () => void;
|
|
60
|
+
/**
|
|
61
|
+
* Loads the full cart from the server.
|
|
62
|
+
*
|
|
63
|
+
* The promise rejects when the load fails, with a `CartNetworkError` when your cart endpoint returns a non-2xx status.
|
|
57
64
|
*/
|
|
58
|
-
subscribe(listener: (state: CartState) => void): () => void; /** Triggers a full cart load from the server endpoint. */
|
|
59
65
|
fetch(): Promise<void>;
|
|
60
66
|
/**
|
|
61
|
-
*
|
|
62
|
-
*
|
|
67
|
+
* Reloads the cart after a cart change that skipped the store, such as a server action. When the store has no cart yet, the method picks up the cart that the server created.
|
|
68
|
+
*
|
|
69
|
+
* A failed reload adds an entry to the network errors in the cart state.
|
|
63
70
|
*/
|
|
64
|
-
refresh(): void; /**
|
|
71
|
+
refresh(): void; /** Clears the cart data and errors and cancels in-flight cart requests. After you connect the store, the method also loads the cart again when the store held a cart. */
|
|
65
72
|
reset(): void;
|
|
66
73
|
/**
|
|
67
|
-
*
|
|
68
|
-
* field values, and dispatches the matching transaction.
|
|
74
|
+
* Runs the cart change that a cart form submission describes. Pass the form's submit event, and call `event.preventDefault()` to stop the browser's own form submission. The method connects the store when you haven't connected it.
|
|
69
75
|
*
|
|
70
|
-
* The
|
|
71
|
-
* `
|
|
72
|
-
* `
|
|
73
|
-
*
|
|
76
|
+
* The submit button's value sets the change: `add`, `increase`,
|
|
77
|
+
* `decrease`, `remove`, `set`, `discount-apply`, `discount-remove`,
|
|
78
|
+
* `note-update`, or `attributes-update`. A button without a value adds
|
|
79
|
+
* the line when the form has a `merchandiseId` field. A `set` with an empty or
|
|
80
|
+
* zero quantity removes the line. When your cart fragment selects the variant's
|
|
81
|
+
* available quantity, the store caps a `set` quantity at that number.
|
|
74
82
|
*
|
|
75
|
-
* The
|
|
76
|
-
*
|
|
77
|
-
*
|
|
83
|
+
* The promise rejects with a type error when the event target isn't a form or
|
|
84
|
+
* the event has no submitter. The promise rejects with an error when the button's
|
|
85
|
+
* value isn't a recognized change, or when the form lacks a required field
|
|
86
|
+
* such as the line ID.
|
|
78
87
|
*/
|
|
79
88
|
handleFormSubmit(event: SubmitEvent, eventDetail?: Record<string, unknown>): Promise<void>;
|
|
80
89
|
};
|
|
81
|
-
/**
|
|
90
|
+
/** The store action that reloads the cart after a cart change outside the store. */
|
|
82
91
|
type CartActions = Pick<CartStore, "refresh">;
|
|
92
|
+
/** The cart that the server loaded for the first render. */
|
|
83
93
|
type CartInitialData<TData extends CartData = CartData> = {
|
|
84
|
-
cart: TData | null;
|
|
94
|
+
/** The loaded cart, or `null` when the server found no cart. */cart: TData | null; /** Load errors from the server. The store ignores the errors. */
|
|
85
95
|
errors?: Array<{
|
|
86
96
|
message: string;
|
|
87
97
|
}>;
|
|
88
98
|
};
|
|
89
|
-
/** Options for
|
|
99
|
+
/** Options for creating a cart store. */
|
|
90
100
|
type CreateCartStoreOptions<TData extends CartData = CartData> = {
|
|
91
101
|
/**
|
|
92
|
-
*
|
|
102
|
+
* The cart that the server loaded, or a promise of the cart for streamed data.
|
|
93
103
|
*
|
|
94
|
-
*
|
|
95
|
-
*
|
|
104
|
+
* Pass the `data` property of the cart GET handler's result. A `null` cart
|
|
105
|
+
* tells the store that no cart exists yet. Without initial data, the store starts empty and loads the cart when you call `connect()`.
|
|
96
106
|
*/
|
|
97
107
|
initialData?: CartInitialData<TData> | PromiseLike<CartInitialData<TData>>;
|
|
98
108
|
};
|
|
99
109
|
/**
|
|
100
|
-
* Creates a
|
|
110
|
+
* Creates a cart store that holds the cart and applies cart changes before the server responds.
|
|
101
111
|
*
|
|
102
|
-
*
|
|
103
|
-
* requests are made and no event listeners are attached at creation time.
|
|
104
|
-
* Framework adapters call `connect()` on mount and `destroy()` on unmount.
|
|
112
|
+
* Call `connect()` to start the store and `destroy()` to stop it. The React and Vue cart providers call both for you.
|
|
105
113
|
*
|
|
106
114
|
* @example
|
|
107
115
|
* ```ts
|
|
@@ -118,18 +126,23 @@ type CreateCartStoreOptions<TData extends CartData = CartData> = {
|
|
|
118
126
|
* // Empty — loads the cart on connect()
|
|
119
127
|
* const store = createCartStore();
|
|
120
128
|
* ```
|
|
129
|
+
* @param options The cart that the server loaded for the first render.
|
|
130
|
+
* @returns The cart store. Read the cart with `getState()` and listen for changes with `subscribe()`.
|
|
131
|
+
* @publicDocs
|
|
121
132
|
*/
|
|
122
133
|
declare function createCartStore<TData extends CartData = CartData>(options?: CreateCartStoreOptions<TData>): CartStore;
|
|
123
134
|
/**
|
|
124
|
-
*
|
|
125
|
-
* Standard Actions default handler.
|
|
135
|
+
* Sends every cart load and cart change on the page to your cart endpoint. Without an endpoint, Standard Actions handles cart requests with its default handler.
|
|
126
136
|
*
|
|
127
|
-
*
|
|
137
|
+
* Call the function before you connect a cart store. The first cart load then goes to your endpoint. A later call with a different endpoint replaces the first endpoint and logs a warning. In React, `CartProvider` sets the endpoint to `/api/cart` when the provider mounts.
|
|
128
138
|
*
|
|
139
|
+
* @param endpoint The URL path of your cart route.
|
|
140
|
+
* @returns Nothing. Later cart loads and changes on the page go to the endpoint.
|
|
129
141
|
* @example
|
|
130
142
|
* ```ts
|
|
131
143
|
* configureCartEndpoint("/storefront/cart");
|
|
132
144
|
* ```
|
|
145
|
+
* @publicDocs
|
|
133
146
|
*/
|
|
134
147
|
declare function configureCartEndpoint(endpoint: string): void;
|
|
135
148
|
//#endregion
|