@shopify/hydrogen 2026.4.4 → 2026.10.0-preview.1
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 +73 -13
- package/bin/hydrogen.mjs +7 -0
- package/dist/cli/index.mjs +330 -0
- package/dist/client/client.d.mts +34 -0
- package/dist/client/client.mjs +193 -0
- package/dist/client/client.mjs.map +1 -0
- package/dist/client/errors.d.mts +48 -0
- package/dist/client/errors.mjs +52 -0
- package/dist/client/errors.mjs.map +1 -0
- package/dist/client/index.d.mts +4 -0
- package/dist/client/types.d.mts +147 -0
- package/dist/core/analytics/cart-tracker.d.mts +8 -0
- package/dist/core/analytics/cart-tracker.mjs +144 -0
- package/dist/core/analytics/cart-tracker.mjs.map +1 -0
- package/dist/core/analytics/events.d.mts +15 -0
- package/dist/core/analytics/events.mjs +15 -0
- package/dist/core/analytics/events.mjs.map +1 -0
- package/dist/core/analytics/index.d.mts +3 -0
- package/dist/core/analytics/types.d.mts +143 -0
- package/dist/core/analytics/utils/flatten-connection.d.mts +19 -0
- package/dist/core/analytics/utils/flatten-connection.mjs +25 -0
- package/dist/core/analytics/utils/flatten-connection.mjs.map +1 -0
- package/dist/core/cache/cache-status.mjs +17 -0
- package/dist/core/cache/cache-status.mjs.map +1 -0
- package/dist/core/cache/fetch-with-cache.d.mts +43 -0
- package/dist/core/cache/fetch-with-cache.mjs +188 -0
- package/dist/core/cache/fetch-with-cache.mjs.map +1 -0
- package/dist/core/cache/key.d.mts +6 -0
- package/dist/core/cache/key.mjs +27 -0
- package/dist/core/cache/key.mjs.map +1 -0
- package/dist/core/cache/run-with-cache.d.mts +36 -0
- package/dist/core/cache/run-with-cache.mjs +114 -0
- package/dist/core/cache/run-with-cache.mjs.map +1 -0
- package/dist/core/cache/store.d.mts +19 -0
- package/dist/core/cache/store.mjs +62 -0
- package/dist/core/cache/store.mjs.map +1 -0
- package/dist/core/cache/strategies.d.mts +39 -0
- package/dist/core/cache/strategies.mjs +70 -0
- package/dist/core/cache/strategies.mjs.map +1 -0
- package/dist/core/cart/actions.d.mts +50 -0
- package/dist/core/cart/actions.mjs +240 -0
- package/dist/core/cart/actions.mjs.map +1 -0
- package/dist/core/cart/attach-quantity-input.d.mts +5 -0
- package/dist/core/cart/attach-quantity-input.mjs +17 -0
- package/dist/core/cart/attach-quantity-input.mjs.map +1 -0
- package/dist/core/cart/buyer-identity-sync.d.mts +23 -0
- package/dist/core/cart/buyer-identity-sync.mjs +9 -0
- package/dist/core/cart/buyer-identity-sync.mjs.map +1 -0
- package/dist/core/cart/cart.d.mts +32 -0
- package/dist/core/cart/cart.mjs +1687 -0
- package/dist/core/cart/cart.mjs.map +1 -0
- package/dist/core/cart/cookie.d.mts +5 -0
- package/dist/core/cart/cookie.mjs +28 -0
- package/dist/core/cart/cookie.mjs.map +1 -0
- package/dist/core/cart/form.d.mts +130 -0
- package/dist/core/cart/form.mjs +75 -0
- package/dist/core/cart/form.mjs.map +1 -0
- package/dist/core/cart/get-cart.d.mts +25 -0
- package/dist/core/cart/get-cart.mjs +32 -0
- package/dist/core/cart/get-cart.mjs.map +1 -0
- package/dist/core/cart/index.d.mts +10 -0
- package/dist/core/cart/quantity.d.mts +10 -0
- package/dist/core/cart/quantity.mjs +14 -0
- package/dist/core/cart/quantity.mjs.map +1 -0
- package/dist/core/cart/queries.d.mts +4296 -0
- package/dist/core/cart/queries.mjs +70 -0
- package/dist/core/cart/queries.mjs.map +1 -0
- package/dist/core/cart/server-handlers.d.mts +82 -0
- package/dist/core/cart/server-handlers.mjs +322 -0
- package/dist/core/cart/server-handlers.mjs.map +1 -0
- package/dist/core/cart/state.d.mts +137 -0
- package/dist/core/cart/state.mjs +76 -0
- package/dist/core/cart/state.mjs.map +1 -0
- package/dist/core/cart/sync-quantity-inputs.mjs +16 -0
- package/dist/core/cart/sync-quantity-inputs.mjs.map +1 -0
- package/dist/core/collection/collection.d.mts +134 -0
- package/dist/core/collection/collection.mjs +170 -0
- package/dist/core/collection/collection.mjs.map +1 -0
- package/dist/core/collection/index.d.mts +6 -0
- package/dist/core/collection/reconciler.d.mts +32 -0
- package/dist/core/collection/reconciler.mjs +89 -0
- package/dist/core/collection/reconciler.mjs.map +1 -0
- package/dist/core/collection/state.d.mts +66 -0
- package/dist/core/collection/state.mjs +20 -0
- package/dist/core/collection/state.mjs.map +1 -0
- package/dist/core/collection/url.d.mts +70 -0
- package/dist/core/collection/url.mjs +367 -0
- package/dist/core/collection/url.mjs.map +1 -0
- package/dist/core/constants.d.mts +5 -0
- package/dist/core/constants.mjs +8 -0
- package/dist/core/constants.mjs.map +1 -0
- package/dist/core/headers.mjs +44 -0
- package/dist/core/headers.mjs.map +1 -0
- package/dist/core/index.d.mts +55 -0
- package/dist/core/index.mjs +44 -0
- package/dist/core/logging/logging.d.mts +21 -0
- package/dist/core/logging/logging.mjs +99 -0
- package/dist/core/logging/logging.mjs.map +1 -0
- package/dist/core/logging/types.d.mts +40 -0
- package/dist/core/logging/types.mjs +18 -0
- package/dist/core/logging/types.mjs.map +1 -0
- package/dist/core/money/cache.mjs +25 -0
- package/dist/core/money/cache.mjs.map +1 -0
- package/dist/core/money/format.d.mts +15 -0
- package/dist/core/money/format.mjs +243 -0
- package/dist/core/money/format.mjs.map +1 -0
- package/dist/core/money/types.d.mts +47 -0
- package/dist/core/observable.mjs +46 -0
- package/dist/core/observable.mjs.map +1 -0
- package/dist/core/predictive-search/constants.d.mts +5 -0
- package/dist/core/predictive-search/constants.mjs +6 -0
- package/dist/core/predictive-search/constants.mjs.map +1 -0
- package/dist/core/predictive-search/form.d.mts +31 -0
- package/dist/core/predictive-search/form.mjs +31 -0
- package/dist/core/predictive-search/form.mjs.map +1 -0
- package/dist/core/predictive-search/index.d.mts +6 -0
- package/dist/core/predictive-search/queries.d.mts +118 -0
- package/dist/core/predictive-search/queries.mjs +81 -0
- package/dist/core/predictive-search/queries.mjs.map +1 -0
- package/dist/core/predictive-search/search.d.mts +32 -0
- package/dist/core/predictive-search/search.mjs +74 -0
- package/dist/core/predictive-search/search.mjs.map +1 -0
- package/dist/core/predictive-search/server-handlers.d.mts +36 -0
- package/dist/core/predictive-search/server-handlers.mjs +113 -0
- package/dist/core/predictive-search/server-handlers.mjs.map +1 -0
- package/dist/core/predictive-search/store.d.mts +44 -0
- package/dist/core/predictive-search/store.mjs +224 -0
- package/dist/core/predictive-search/store.mjs.map +1 -0
- package/dist/core/predictive-search/url.d.mts +41 -0
- package/dist/core/predictive-search/url.mjs +71 -0
- package/dist/core/predictive-search/url.mjs.map +1 -0
- package/dist/core/product/form.d.mts +63 -0
- package/dist/core/product/form.mjs +44 -0
- package/dist/core/product/form.mjs.map +1 -0
- package/dist/core/product/index.d.mts +4 -0
- package/dist/core/product/options.d.mts +40 -0
- package/dist/core/product/options.mjs +291 -0
- package/dist/core/product/options.mjs.map +1 -0
- package/dist/core/product/product-form.d.mts +72 -0
- package/dist/core/product/product-form.mjs +158 -0
- package/dist/core/product/product-form.mjs.map +1 -0
- package/dist/core/product/state.d.mts +68 -0
- package/dist/core/request-context.d.mts +70 -0
- package/dist/core/request-context.mjs +120 -0
- package/dist/core/request-context.mjs.map +1 -0
- package/dist/core/request-routing/handle-shopify-redirects.d.mts +17 -0
- package/dist/core/request-routing/handle-shopify-redirects.mjs +31 -0
- package/dist/core/request-routing/handle-shopify-redirects.mjs.map +1 -0
- package/dist/core/request-routing/handle-shopify-routes.d.mts +14 -0
- package/dist/core/request-routing/handle-shopify-routes.mjs +42 -0
- package/dist/core/request-routing/handle-shopify-routes.mjs.map +1 -0
- package/dist/core/request-routing/interceptors/admin-redirect.mjs +12 -0
- package/dist/core/request-routing/interceptors/admin-redirect.mjs.map +1 -0
- package/dist/core/request-routing/interceptors/agent-proxy.mjs +19 -0
- package/dist/core/request-routing/interceptors/agent-proxy.mjs.map +1 -0
- package/dist/core/request-routing/interceptors/ajax-api.mjs +16 -0
- package/dist/core/request-routing/interceptors/ajax-api.mjs.map +1 -0
- package/dist/core/request-routing/interceptors/api-proxy.mjs +20 -0
- package/dist/core/request-routing/interceptors/api-proxy.mjs.map +1 -0
- package/dist/core/request-routing/interceptors/checkout.mjs +58 -0
- package/dist/core/request-routing/interceptors/checkout.mjs.map +1 -0
- package/dist/core/request-routing/interceptors/mcp-proxy.mjs +23 -0
- package/dist/core/request-routing/interceptors/mcp-proxy.mjs.map +1 -0
- package/dist/core/request-routing/interceptors/proxy.mjs +68 -0
- package/dist/core/request-routing/interceptors/proxy.mjs.map +1 -0
- package/dist/core/request-routing/interceptors/query-param-redirect.mjs +22 -0
- package/dist/core/request-routing/interceptors/query-param-redirect.mjs.map +1 -0
- package/dist/core/request-routing/interceptors/sfapi-proxy.mjs +27 -0
- package/dist/core/request-routing/interceptors/sfapi-proxy.mjs.map +1 -0
- package/dist/core/request-routing/interceptors/standard-routes.mjs +19 -0
- package/dist/core/request-routing/interceptors/standard-routes.mjs.map +1 -0
- package/dist/core/request-routing/interceptors/url-redirects.mjs +27 -0
- package/dist/core/request-routing/interceptors/url-redirects.mjs.map +1 -0
- package/dist/core/request-routing/interceptors/well-known.mjs +19 -0
- package/dist/core/request-routing/interceptors/well-known.mjs.map +1 -0
- package/dist/core/request-routing/registered-routes.d.mts +7 -0
- package/dist/core/request-routing/registered-routes.mjs +59 -0
- package/dist/core/request-routing/registered-routes.mjs.map +1 -0
- package/dist/core/request-routing/route-types.d.mts +51 -0
- package/dist/core/request-routing/safe-apply-response-headers.mjs +16 -0
- package/dist/core/request-routing/safe-apply-response-headers.mjs.map +1 -0
- package/dist/core/shop-pay/shop-pay.d.mts +102 -0
- package/dist/core/shop-pay/shop-pay.mjs +289 -0
- package/dist/core/shop-pay/shop-pay.mjs.map +1 -0
- package/dist/core/shopify-scripts/analytics.mjs +26 -0
- package/dist/core/shopify-scripts/analytics.mjs.map +1 -0
- package/dist/core/shopify-scripts/consent.mjs +17 -0
- package/dist/core/shopify-scripts/consent.mjs.map +1 -0
- package/dist/core/shopify-scripts/constants.mjs +19 -0
- package/dist/core/shopify-scripts/constants.mjs.map +1 -0
- package/dist/core/shopify-scripts/deprecated-cookies.mjs +97 -0
- package/dist/core/shopify-scripts/deprecated-cookies.mjs.map +1 -0
- package/dist/core/shopify-scripts/global.d.mts +1 -0
- package/dist/core/shopify-scripts/global.mjs +86 -0
- package/dist/core/shopify-scripts/global.mjs.map +1 -0
- package/dist/core/shopify-scripts/index.d.mts +27 -0
- package/dist/core/shopify-scripts/index.mjs +135 -0
- package/dist/core/shopify-scripts/index.mjs.map +1 -0
- package/dist/core/shopify-scripts/initialize.d.mts +18 -0
- package/dist/core/shopify-scripts/initialize.mjs +27 -0
- package/dist/core/shopify-scripts/initialize.mjs.map +1 -0
- package/dist/core/shopify-scripts/page-view.mjs +49 -0
- package/dist/core/shopify-scripts/page-view.mjs.map +1 -0
- package/dist/core/shopify-scripts/perfkit.mjs +33 -0
- package/dist/core/shopify-scripts/perfkit.mjs.map +1 -0
- package/dist/core/shopify-scripts/render.d.mts +1 -0
- package/dist/core/shopify-scripts/render.mjs +23 -0
- package/dist/core/shopify-scripts/render.mjs.map +1 -0
- package/dist/core/shopify-scripts/types.d.mts +76 -0
- package/dist/core/shopify-scripts/utils/cookie-domain.mjs +43 -0
- package/dist/core/shopify-scripts/utils/cookie-domain.mjs.map +1 -0
- package/dist/core/shopify-scripts/utils/inline-script.mjs +8 -0
- package/dist/core/shopify-scripts/utils/inline-script.mjs.map +1 -0
- package/dist/core/shopify-scripts/utils/navigation.mjs +46 -0
- package/dist/core/shopify-scripts/utils/navigation.mjs.map +1 -0
- package/dist/core/shopify-scripts/utils/tracking-values.mjs +65 -0
- package/dist/core/shopify-scripts/utils/tracking-values.mjs.map +1 -0
- package/dist/core/shopify-scripts/utils/uuid.mjs +17 -0
- package/dist/core/shopify-scripts/utils/uuid.mjs.map +1 -0
- package/dist/core/shopify-scripts/webmcp.mjs +22 -0
- package/dist/core/shopify-scripts/webmcp.mjs.map +1 -0
- package/dist/core/standard-routes/build.d.mts +34 -0
- package/dist/core/standard-routes/build.mjs +54 -0
- package/dist/core/standard-routes/build.mjs.map +1 -0
- package/dist/core/standard-routes/defaults.mjs +31 -0
- package/dist/core/standard-routes/defaults.mjs.map +1 -0
- package/dist/core/standard-routes/match.mjs +108 -0
- package/dist/core/standard-routes/match.mjs.map +1 -0
- package/dist/core/standard-routes/path.mjs +58 -0
- package/dist/core/standard-routes/path.mjs.map +1 -0
- package/dist/core/standard-routes/redirects.mjs +51 -0
- package/dist/core/standard-routes/redirects.mjs.map +1 -0
- package/dist/core/standard-routes/types.d.mts +95 -0
- package/dist/core/url.mjs +35 -0
- package/dist/core/url.mjs.map +1 -0
- package/dist/core/utils/load-script.mjs +34 -0
- package/dist/core/utils/load-script.mjs.map +1 -0
- package/dist/core/utils/parse-gid.mjs +33 -0
- package/dist/core/utils/parse-gid.mjs.map +1 -0
- package/dist/core/utils/record.mjs +8 -0
- package/dist/core/utils/record.mjs.map +1 -0
- package/dist/customer-account/client.d.mts +50 -0
- package/dist/customer-account/client.mjs +239 -0
- package/dist/customer-account/client.mjs.map +1 -0
- package/dist/customer-account/errors.d.mts +28 -0
- package/dist/customer-account/errors.mjs +39 -0
- package/dist/customer-account/errors.mjs.map +1 -0
- package/dist/customer-account/graphql.d.mts +29 -0
- package/dist/customer-account/graphql.mjs +39 -0
- package/dist/customer-account/graphql.mjs.map +1 -0
- package/dist/customer-account/index.d.mts +7 -0
- package/dist/customer-account/index.mjs +7 -0
- package/dist/customer-account/session.d.mts +117 -0
- package/dist/customer-account/session.mjs +733 -0
- package/dist/customer-account/session.mjs.map +1 -0
- package/dist/customer-account/type-resolver.d.mts +11 -0
- package/dist/customer-account-api-types.d.ts +3269 -3270
- package/dist/customer-account.schema.json +50753 -1
- package/dist/development/_virtual/_@oxc-project_runtime@0.127.0/helpers/taggedTemplateLiteral.mjs +7 -0
- package/dist/development/client/client.d.mts +34 -0
- package/dist/development/client/client.mjs +193 -0
- package/dist/development/client/client.mjs.map +1 -0
- package/dist/development/client/errors.d.mts +48 -0
- package/dist/development/client/errors.mjs +57 -0
- package/dist/development/client/errors.mjs.map +1 -0
- package/dist/development/client/index.d.mts +4 -0
- package/dist/development/client/types.d.mts +147 -0
- package/dist/development/core/analytics/cart-tracker.d.mts +8 -0
- package/dist/development/core/analytics/cart-tracker.mjs +144 -0
- package/dist/development/core/analytics/cart-tracker.mjs.map +1 -0
- package/dist/development/core/analytics/events.d.mts +15 -0
- package/dist/development/core/analytics/events.mjs +15 -0
- package/dist/development/core/analytics/events.mjs.map +1 -0
- package/dist/development/core/analytics/index.d.mts +3 -0
- package/dist/development/core/analytics/types.d.mts +143 -0
- package/dist/development/core/analytics/utils/flatten-connection.d.mts +19 -0
- package/dist/development/core/analytics/utils/flatten-connection.mjs +25 -0
- package/dist/development/core/analytics/utils/flatten-connection.mjs.map +1 -0
- package/dist/development/core/cache/cache-status.mjs +17 -0
- package/dist/development/core/cache/cache-status.mjs.map +1 -0
- package/dist/development/core/cache/fetch-with-cache.d.mts +43 -0
- package/dist/development/core/cache/fetch-with-cache.mjs +188 -0
- package/dist/development/core/cache/fetch-with-cache.mjs.map +1 -0
- package/dist/development/core/cache/key.d.mts +6 -0
- package/dist/development/core/cache/key.mjs +27 -0
- package/dist/development/core/cache/key.mjs.map +1 -0
- package/dist/development/core/cache/run-with-cache.d.mts +36 -0
- package/dist/development/core/cache/run-with-cache.mjs +114 -0
- package/dist/development/core/cache/run-with-cache.mjs.map +1 -0
- package/dist/development/core/cache/store.d.mts +19 -0
- package/dist/development/core/cache/store.mjs +62 -0
- package/dist/development/core/cache/store.mjs.map +1 -0
- package/dist/development/core/cache/strategies.d.mts +39 -0
- package/dist/development/core/cache/strategies.mjs +70 -0
- package/dist/development/core/cache/strategies.mjs.map +1 -0
- package/dist/development/core/cart/actions.d.mts +50 -0
- package/dist/development/core/cart/actions.mjs +240 -0
- package/dist/development/core/cart/actions.mjs.map +1 -0
- package/dist/development/core/cart/attach-quantity-input.d.mts +5 -0
- package/dist/development/core/cart/attach-quantity-input.mjs +17 -0
- package/dist/development/core/cart/attach-quantity-input.mjs.map +1 -0
- package/dist/development/core/cart/buyer-identity-sync.d.mts +19 -0
- package/dist/development/core/cart/buyer-identity-sync.mjs +6 -0
- package/dist/development/core/cart/buyer-identity-sync.mjs.map +1 -0
- package/dist/development/core/cart/cart.d.mts +32 -0
- package/dist/development/core/cart/cart.mjs +1687 -0
- package/dist/development/core/cart/cart.mjs.map +1 -0
- package/dist/development/core/cart/cookie.d.mts +5 -0
- package/dist/development/core/cart/cookie.mjs +28 -0
- package/dist/development/core/cart/cookie.mjs.map +1 -0
- package/dist/development/core/cart/form.d.mts +130 -0
- package/dist/development/core/cart/form.mjs +75 -0
- package/dist/development/core/cart/form.mjs.map +1 -0
- package/dist/development/core/cart/get-cart.d.mts +25 -0
- package/dist/development/core/cart/get-cart.mjs +32 -0
- package/dist/development/core/cart/get-cart.mjs.map +1 -0
- package/dist/development/core/cart/index.d.mts +10 -0
- package/dist/development/core/cart/quantity.d.mts +10 -0
- package/dist/development/core/cart/quantity.mjs +14 -0
- package/dist/development/core/cart/quantity.mjs.map +1 -0
- package/dist/development/core/cart/queries.d.mts +4296 -0
- package/dist/development/core/cart/queries.mjs +70 -0
- package/dist/development/core/cart/queries.mjs.map +1 -0
- package/dist/development/core/cart/server-handlers.d.mts +82 -0
- package/dist/development/core/cart/server-handlers.mjs +322 -0
- package/dist/development/core/cart/server-handlers.mjs.map +1 -0
- package/dist/development/core/cart/state.d.mts +137 -0
- package/dist/development/core/cart/state.mjs +76 -0
- package/dist/development/core/cart/state.mjs.map +1 -0
- package/dist/development/core/cart/sync-quantity-inputs.mjs +16 -0
- package/dist/development/core/cart/sync-quantity-inputs.mjs.map +1 -0
- package/dist/development/core/collection/collection.d.mts +134 -0
- package/dist/development/core/collection/collection.mjs +170 -0
- package/dist/development/core/collection/collection.mjs.map +1 -0
- package/dist/development/core/collection/index.d.mts +6 -0
- package/dist/development/core/collection/reconciler.d.mts +32 -0
- package/dist/development/core/collection/reconciler.mjs +89 -0
- package/dist/development/core/collection/reconciler.mjs.map +1 -0
- package/dist/development/core/collection/state.d.mts +66 -0
- package/dist/development/core/collection/state.mjs +20 -0
- package/dist/development/core/collection/state.mjs.map +1 -0
- package/dist/development/core/collection/url.d.mts +70 -0
- package/dist/development/core/collection/url.mjs +367 -0
- package/dist/development/core/collection/url.mjs.map +1 -0
- package/dist/development/core/constants.mjs +7 -0
- package/dist/development/core/constants.mjs.map +1 -0
- package/dist/development/core/development.d.mts +55 -0
- package/dist/development/core/development.mjs +44 -0
- package/dist/development/core/headers.mjs +44 -0
- package/dist/development/core/headers.mjs.map +1 -0
- package/dist/development/core/index.d.mts +53 -0
- package/dist/development/core/logging/logging.d.mts +21 -0
- package/dist/development/core/logging/logging.mjs +99 -0
- package/dist/development/core/logging/logging.mjs.map +1 -0
- package/dist/development/core/logging/types.d.mts +40 -0
- package/dist/development/core/logging/types.mjs +18 -0
- package/dist/development/core/logging/types.mjs.map +1 -0
- package/dist/development/core/money/cache.mjs +25 -0
- package/dist/development/core/money/cache.mjs.map +1 -0
- package/dist/development/core/money/format.d.mts +15 -0
- package/dist/development/core/money/format.mjs +243 -0
- package/dist/development/core/money/format.mjs.map +1 -0
- package/dist/development/core/money/types.d.mts +47 -0
- package/dist/development/core/observable.mjs +46 -0
- package/dist/development/core/observable.mjs.map +1 -0
- package/dist/development/core/predictive-search/constants.d.mts +5 -0
- package/dist/development/core/predictive-search/constants.mjs +6 -0
- package/dist/development/core/predictive-search/constants.mjs.map +1 -0
- package/dist/development/core/predictive-search/form.d.mts +31 -0
- package/dist/development/core/predictive-search/form.mjs +31 -0
- package/dist/development/core/predictive-search/form.mjs.map +1 -0
- package/dist/development/core/predictive-search/index.d.mts +6 -0
- package/dist/development/core/predictive-search/queries.d.mts +118 -0
- package/dist/development/core/predictive-search/queries.mjs +81 -0
- package/dist/development/core/predictive-search/queries.mjs.map +1 -0
- package/dist/development/core/predictive-search/search.d.mts +32 -0
- package/dist/development/core/predictive-search/search.mjs +74 -0
- package/dist/development/core/predictive-search/search.mjs.map +1 -0
- package/dist/development/core/predictive-search/server-handlers.d.mts +36 -0
- package/dist/development/core/predictive-search/server-handlers.mjs +113 -0
- package/dist/development/core/predictive-search/server-handlers.mjs.map +1 -0
- package/dist/development/core/predictive-search/store.d.mts +44 -0
- package/dist/development/core/predictive-search/store.mjs +224 -0
- package/dist/development/core/predictive-search/store.mjs.map +1 -0
- package/dist/development/core/predictive-search/url.d.mts +41 -0
- package/dist/development/core/predictive-search/url.mjs +71 -0
- package/dist/development/core/predictive-search/url.mjs.map +1 -0
- package/dist/development/core/product/form.d.mts +63 -0
- package/dist/development/core/product/form.mjs +44 -0
- package/dist/development/core/product/form.mjs.map +1 -0
- package/dist/development/core/product/index.d.mts +4 -0
- package/dist/development/core/product/options.d.mts +40 -0
- package/dist/development/core/product/options.mjs +291 -0
- package/dist/development/core/product/options.mjs.map +1 -0
- package/dist/development/core/product/product-form.d.mts +72 -0
- package/dist/development/core/product/product-form.mjs +158 -0
- package/dist/development/core/product/product-form.mjs.map +1 -0
- package/dist/development/core/product/state.d.mts +68 -0
- package/dist/development/core/request-context.d.mts +70 -0
- package/dist/development/core/request-context.mjs +120 -0
- package/dist/development/core/request-context.mjs.map +1 -0
- package/dist/development/core/request-routing/handle-shopify-redirects.d.mts +17 -0
- package/dist/development/core/request-routing/handle-shopify-redirects.mjs +31 -0
- package/dist/development/core/request-routing/handle-shopify-redirects.mjs.map +1 -0
- package/dist/development/core/request-routing/handle-shopify-routes.d.mts +1 -0
- package/dist/development/core/request-routing/handle-shopify-routes.development.d.mts +11 -0
- package/dist/development/core/request-routing/handle-shopify-routes.development.mjs +16 -0
- package/dist/development/core/request-routing/handle-shopify-routes.development.mjs.map +1 -0
- package/dist/development/core/request-routing/handle-shopify-routes.mjs +42 -0
- package/dist/development/core/request-routing/handle-shopify-routes.mjs.map +1 -0
- package/dist/development/core/request-routing/interceptors/admin-redirect.mjs +12 -0
- package/dist/development/core/request-routing/interceptors/admin-redirect.mjs.map +1 -0
- package/dist/development/core/request-routing/interceptors/agent-proxy.mjs +19 -0
- package/dist/development/core/request-routing/interceptors/agent-proxy.mjs.map +1 -0
- package/dist/development/core/request-routing/interceptors/ajax-api.mjs +16 -0
- package/dist/development/core/request-routing/interceptors/ajax-api.mjs.map +1 -0
- package/dist/development/core/request-routing/interceptors/api-proxy.mjs +20 -0
- package/dist/development/core/request-routing/interceptors/api-proxy.mjs.map +1 -0
- package/dist/development/core/request-routing/interceptors/checkout.mjs +58 -0
- package/dist/development/core/request-routing/interceptors/checkout.mjs.map +1 -0
- package/dist/development/core/request-routing/interceptors/graphiql.mjs +46 -0
- package/dist/development/core/request-routing/interceptors/graphiql.mjs.map +1 -0
- package/dist/development/core/request-routing/interceptors/mcp-proxy.mjs +23 -0
- package/dist/development/core/request-routing/interceptors/mcp-proxy.mjs.map +1 -0
- package/dist/development/core/request-routing/interceptors/proxy.mjs +68 -0
- package/dist/development/core/request-routing/interceptors/proxy.mjs.map +1 -0
- package/dist/development/core/request-routing/interceptors/query-param-redirect.mjs +22 -0
- package/dist/development/core/request-routing/interceptors/query-param-redirect.mjs.map +1 -0
- package/dist/development/core/request-routing/interceptors/sfapi-proxy.mjs +27 -0
- package/dist/development/core/request-routing/interceptors/sfapi-proxy.mjs.map +1 -0
- package/dist/development/core/request-routing/interceptors/standard-routes.mjs +19 -0
- package/dist/development/core/request-routing/interceptors/standard-routes.mjs.map +1 -0
- package/dist/development/core/request-routing/interceptors/url-redirects.mjs +27 -0
- package/dist/development/core/request-routing/interceptors/url-redirects.mjs.map +1 -0
- package/dist/development/core/request-routing/interceptors/well-known.mjs +19 -0
- package/dist/development/core/request-routing/interceptors/well-known.mjs.map +1 -0
- package/dist/development/core/request-routing/registered-routes.d.mts +7 -0
- package/dist/development/core/request-routing/registered-routes.mjs +59 -0
- package/dist/development/core/request-routing/registered-routes.mjs.map +1 -0
- package/dist/development/core/request-routing/route-types.d.mts +51 -0
- package/dist/development/core/request-routing/safe-apply-response-headers.mjs +16 -0
- package/dist/development/core/request-routing/safe-apply-response-headers.mjs.map +1 -0
- package/dist/development/core/shop-pay/shop-pay.d.mts +102 -0
- package/dist/development/core/shop-pay/shop-pay.mjs +289 -0
- package/dist/development/core/shop-pay/shop-pay.mjs.map +1 -0
- package/dist/development/core/shopify-scripts/analytics.mjs +26 -0
- package/dist/development/core/shopify-scripts/analytics.mjs.map +1 -0
- package/dist/development/core/shopify-scripts/consent.mjs +17 -0
- package/dist/development/core/shopify-scripts/consent.mjs.map +1 -0
- package/dist/development/core/shopify-scripts/constants.mjs +19 -0
- package/dist/development/core/shopify-scripts/constants.mjs.map +1 -0
- package/dist/development/core/shopify-scripts/deprecated-cookies.mjs +97 -0
- package/dist/development/core/shopify-scripts/deprecated-cookies.mjs.map +1 -0
- package/dist/development/core/shopify-scripts/global.d.mts +1 -0
- package/dist/development/core/shopify-scripts/global.mjs +86 -0
- package/dist/development/core/shopify-scripts/global.mjs.map +1 -0
- package/dist/development/core/shopify-scripts/index.d.mts +27 -0
- package/dist/development/core/shopify-scripts/index.mjs +145 -0
- package/dist/development/core/shopify-scripts/index.mjs.map +1 -0
- package/dist/development/core/shopify-scripts/initialize.d.mts +18 -0
- package/dist/development/core/shopify-scripts/initialize.mjs +27 -0
- package/dist/development/core/shopify-scripts/initialize.mjs.map +1 -0
- package/dist/development/core/shopify-scripts/page-view.mjs +49 -0
- package/dist/development/core/shopify-scripts/page-view.mjs.map +1 -0
- package/dist/development/core/shopify-scripts/perfkit.mjs +33 -0
- package/dist/development/core/shopify-scripts/perfkit.mjs.map +1 -0
- package/dist/development/core/shopify-scripts/render.d.mts +1 -0
- package/dist/development/core/shopify-scripts/render.mjs +23 -0
- package/dist/development/core/shopify-scripts/render.mjs.map +1 -0
- package/dist/development/core/shopify-scripts/types.d.mts +76 -0
- package/dist/development/core/shopify-scripts/utils/cookie-domain.mjs +43 -0
- package/dist/development/core/shopify-scripts/utils/cookie-domain.mjs.map +1 -0
- package/dist/development/core/shopify-scripts/utils/inline-script.mjs +8 -0
- package/dist/development/core/shopify-scripts/utils/inline-script.mjs.map +1 -0
- package/dist/development/core/shopify-scripts/utils/navigation.mjs +46 -0
- package/dist/development/core/shopify-scripts/utils/navigation.mjs.map +1 -0
- package/dist/development/core/shopify-scripts/utils/tracking-values.mjs +65 -0
- package/dist/development/core/shopify-scripts/utils/tracking-values.mjs.map +1 -0
- package/dist/development/core/shopify-scripts/utils/uuid.mjs +17 -0
- package/dist/development/core/shopify-scripts/utils/uuid.mjs.map +1 -0
- package/dist/development/core/shopify-scripts/webmcp.mjs +22 -0
- package/dist/development/core/shopify-scripts/webmcp.mjs.map +1 -0
- package/dist/development/core/standard-routes/build.d.mts +34 -0
- package/dist/development/core/standard-routes/build.mjs +54 -0
- package/dist/development/core/standard-routes/build.mjs.map +1 -0
- package/dist/development/core/standard-routes/defaults.mjs +31 -0
- package/dist/development/core/standard-routes/defaults.mjs.map +1 -0
- package/dist/development/core/standard-routes/match.mjs +108 -0
- package/dist/development/core/standard-routes/match.mjs.map +1 -0
- package/dist/development/core/standard-routes/path.mjs +58 -0
- package/dist/development/core/standard-routes/path.mjs.map +1 -0
- package/dist/development/core/standard-routes/redirects.mjs +51 -0
- package/dist/development/core/standard-routes/redirects.mjs.map +1 -0
- package/dist/development/core/standard-routes/types.d.mts +95 -0
- package/dist/development/core/types.d.mts +11 -0
- package/dist/development/core/url.mjs +35 -0
- package/dist/development/core/url.mjs.map +1 -0
- package/dist/development/core/utils/load-script.mjs +34 -0
- package/dist/development/core/utils/load-script.mjs.map +1 -0
- package/dist/development/core/utils/parse-gid.mjs +33 -0
- package/dist/development/core/utils/parse-gid.mjs.map +1 -0
- package/dist/development/customer-account/session.d.mts +46 -0
- package/dist/development/customer-account/session.mjs +21 -0
- package/dist/development/customer-account/session.mjs.map +1 -0
- package/dist/development/globals.d.mts +64 -0
- package/dist/development/graphql/generated/customer-account-api-types.d.mts +13 -0
- package/dist/development/graphql/generated/graphql-env.d.mts +19280 -0
- package/dist/development/graphql/generated/storefront-api-types.d.mts +47 -0
- package/dist/development/graphql/graphql.d.mts +40 -0
- package/dist/development/graphql/graphql.mjs +16 -0
- package/dist/development/graphql/graphql.mjs.map +1 -0
- package/dist/development/graphql/index.d.mts +2 -0
- package/dist/development/graphql/scalars.d.mts +15 -0
- package/dist/development/graphql/type-resolver.d.mts +229 -0
- package/dist/development/react/cart.d.mts +40 -0
- package/dist/development/react/cart.mjs +150 -0
- package/dist/development/react/cart.mjs.map +1 -0
- package/dist/development/react/collection.d.mts +91 -0
- package/dist/development/react/collection.mjs +129 -0
- package/dist/development/react/collection.mjs.map +1 -0
- package/dist/development/react/index.d.mts +13 -0
- package/dist/development/react/index.mjs +10 -0
- package/dist/development/react/predictive-search.d.mts +49 -0
- package/dist/development/react/predictive-search.mjs +133 -0
- package/dist/development/react/predictive-search.mjs.map +1 -0
- package/dist/development/react/product.d.mts +72 -0
- package/dist/development/react/product.mjs +127 -0
- package/dist/development/react/product.mjs.map +1 -0
- package/dist/development/react/shop-pay.d.mts +9 -0
- package/dist/development/react/shop-pay.mjs +16 -0
- package/dist/development/react/shop-pay.mjs.map +1 -0
- package/dist/development/react/shopify-scripts.d.mts +20 -0
- package/dist/development/react/shopify-scripts.mjs +39 -0
- package/dist/development/react/shopify-scripts.mjs.map +1 -0
- package/dist/development/vendor/standard-actions.d.mts +134 -0
- package/dist/development/vendor/standard-events.d.mts +1107 -0
- package/dist/development/vue/cart.d.mts +42 -0
- package/dist/development/vue/cart.mjs +118 -0
- package/dist/development/vue/cart.mjs.map +1 -0
- package/dist/development/vue/collection.d.mts +46 -0
- package/dist/development/vue/collection.mjs +108 -0
- package/dist/development/vue/collection.mjs.map +1 -0
- package/dist/development/vue/index.d.mts +11 -0
- package/dist/development/vue/index.mjs +8 -0
- package/dist/development/vue/predictive-search.d.mts +124 -0
- package/dist/development/vue/predictive-search.mjs +171 -0
- package/dist/development/vue/predictive-search.mjs.map +1 -0
- package/dist/development/vue/product.d.mts +57 -0
- package/dist/development/vue/product.mjs +167 -0
- package/dist/development/vue/product.mjs.map +1 -0
- package/dist/development/vue/shop-pay.d.mts +25 -0
- package/dist/development/vue/shop-pay.mjs +35 -0
- package/dist/development/vue/shop-pay.mjs.map +1 -0
- package/dist/development/vue/shopify-scripts.d.mts +127 -0
- package/dist/development/vue/shopify-scripts.mjs +79 -0
- package/dist/development/vue/shopify-scripts.mjs.map +1 -0
- package/dist/globals.d.mts +64 -0
- package/dist/graphql/generated/customer-account-api-types.d.mts +13 -0
- package/dist/graphql/generated/customer-account-graphql-env.d.mts +22170 -0
- package/dist/graphql/generated/graphql-env.d.mts +19280 -0
- package/dist/graphql/generated/storefront-api-types.d.mts +47 -0
- package/dist/graphql/graphql.d.mts +40 -0
- package/dist/graphql/graphql.mjs +16 -0
- package/dist/graphql/graphql.mjs.map +1 -0
- package/dist/graphql/index.d.mts +2 -0
- package/dist/graphql/scalars.d.mts +26 -0
- package/dist/graphql/type-resolver.d.mts +229 -0
- package/dist/react/cart.d.mts +40 -0
- package/dist/react/cart.mjs +150 -0
- package/dist/react/cart.mjs.map +1 -0
- package/dist/react/collection.d.mts +91 -0
- package/dist/react/collection.mjs +129 -0
- package/dist/react/collection.mjs.map +1 -0
- package/dist/react/index.d.mts +13 -0
- package/dist/react/index.mjs +10 -0
- package/dist/react/predictive-search.d.mts +49 -0
- package/dist/react/predictive-search.mjs +133 -0
- package/dist/react/predictive-search.mjs.map +1 -0
- package/dist/react/product.d.mts +72 -0
- package/dist/react/product.mjs +127 -0
- package/dist/react/product.mjs.map +1 -0
- package/dist/react/shop-pay.d.mts +9 -0
- package/dist/react/shop-pay.mjs +16 -0
- package/dist/react/shop-pay.mjs.map +1 -0
- package/dist/react/shopify-scripts.d.mts +20 -0
- package/dist/react/shopify-scripts.mjs +39 -0
- package/dist/react/shopify-scripts.mjs.map +1 -0
- package/dist/storefront-api-types.d.ts +2351 -2390
- package/dist/storefront.schema.json +41534 -1
- package/dist/ts-plugin/index.cjs +68 -0
- package/dist/ts-plugin/index.cjs.map +1 -0
- package/dist/ts-plugin/index.d.cts +4 -0
- package/dist/vendor/standard-actions.d.mts +134 -0
- package/dist/vendor/standard-events.d.mts +1107 -0
- package/dist/vite/index.d.mts +2 -0
- package/dist/vite/index.mjs +3 -0
- package/dist/vite/local-https.d.mts +35 -0
- package/dist/vite/local-https.mjs +202 -0
- package/dist/vite/local-https.mjs.map +1 -0
- package/dist/vue/cart.d.mts +42 -0
- package/dist/vue/cart.mjs +118 -0
- package/dist/vue/cart.mjs.map +1 -0
- package/dist/vue/collection.d.mts +46 -0
- package/dist/vue/collection.mjs +108 -0
- package/dist/vue/collection.mjs.map +1 -0
- package/dist/vue/index.d.mts +11 -0
- package/dist/vue/index.mjs +8 -0
- package/dist/vue/predictive-search.d.mts +124 -0
- package/dist/vue/predictive-search.mjs +171 -0
- package/dist/vue/predictive-search.mjs.map +1 -0
- package/dist/vue/product.d.mts +57 -0
- package/dist/vue/product.mjs +167 -0
- package/dist/vue/product.mjs.map +1 -0
- package/dist/vue/shop-pay.d.mts +25 -0
- package/dist/vue/shop-pay.mjs +35 -0
- package/dist/vue/shop-pay.mjs.map +1 -0
- package/dist/vue/shopify-scripts.d.mts +127 -0
- package/dist/vue/shopify-scripts.mjs +79 -0
- package/dist/vue/shopify-scripts.mjs.map +1 -0
- package/package.json +116 -86
- package/skills/hydrogen-analytics/SKILL.md +80 -0
- package/skills/hydrogen-analytics/references/react.md +160 -0
- package/skills/hydrogen-analytics/references/vue.md +80 -0
- package/skills/hydrogen-cart-drawer/SKILL.md +248 -0
- package/skills/hydrogen-cart-drawer/references/accessibility.md +109 -0
- package/skills/hydrogen-cart-drawer/references/css.md +67 -0
- package/skills/hydrogen-cart-ui/SKILL.md +218 -0
- package/skills/hydrogen-cart-ui/references/nextjs.md +89 -0
- package/skills/hydrogen-cart-ui/references/react.md +263 -0
- package/skills/hydrogen-cart-ui/references/vue.md +204 -0
- package/skills/hydrogen-collection-browser/SKILL.md +80 -0
- package/skills/hydrogen-collection-browser/references/nextjs.md +118 -0
- package/skills/hydrogen-collection-browser/references/nuxt.md +108 -0
- package/skills/hydrogen-collection-browser/references/react.md +237 -0
- package/skills/hydrogen-customer-account/SKILL.md +107 -0
- package/skills/hydrogen-image/SKILL.md +65 -0
- package/skills/hydrogen-local-https/SKILL.md +144 -0
- package/skills/hydrogen-markets/SKILL.md +221 -0
- package/skills/hydrogen-markets/references/nextjs.md +157 -0
- package/skills/hydrogen-money/SKILL.md +67 -0
- package/skills/hydrogen-oxygen/SKILL.md +245 -0
- package/skills/hydrogen-oxygen/references/react-router.md +230 -0
- package/skills/hydrogen-predictive-search/SKILL.md +130 -0
- package/skills/hydrogen-predictive-search/references/react.md +32 -0
- package/skills/hydrogen-predictive-search/references/vue.md +40 -0
- package/skills/hydrogen-request-handlers/SKILL.md +90 -0
- package/skills/hydrogen-request-handlers/references/frameworks.md +174 -0
- package/skills/hydrogen-request-handlers/references/nextjs.md +136 -0
- package/skills/hydrogen-request-handlers/references/nuxt.md +214 -0
- package/skills/hydrogen-routing/SKILL.md +154 -0
- package/skills/hydrogen-setup/SKILL.md +50 -0
- package/skills/hydrogen-setup/references/analytics.md +624 -0
- package/skills/hydrogen-setup/steps/1-inspect-app.md +62 -0
- package/skills/hydrogen-setup/steps/10-analytics.md +10 -0
- package/skills/hydrogen-setup/steps/11-verify.md +31 -0
- package/skills/hydrogen-setup/steps/2-scaffold.md +86 -0
- package/skills/hydrogen-setup/steps/3-build-home-page.md +37 -0
- package/skills/hydrogen-setup/steps/4-collection-and-search.md +13 -0
- package/skills/hydrogen-setup/steps/5-cart.md +10 -0
- package/skills/hydrogen-setup/steps/6-product-detail-page.md +390 -0
- package/skills/hydrogen-setup/steps/7-shopify-runtime-scripts.md +69 -0
- package/skills/hydrogen-setup/steps/8-cart-drawer-and-navbar.md +35 -0
- package/skills/hydrogen-setup/steps/9-account-page.md +54 -0
- package/skills/hydrogen-shop-pay/SKILL.md +79 -0
- package/skills/hydrogen-shop-pay/references/core.md +66 -0
- package/skills/hydrogen-shop-pay/references/react.md +50 -0
- package/skills/hydrogen-shop-pay/references/vue.md +39 -0
- package/skills/hydrogen-smoke-test/SKILL.md +203 -0
- package/skills/hydrogen-storefront-client/SKILL.md +322 -0
- package/skills/hydrogen-storefront-client/references/astro.md +125 -0
- package/skills/hydrogen-storefront-client/references/caching.md +25 -0
- package/skills/hydrogen-storefront-client/references/nextjs.md +121 -0
- package/skills/hydrogen-storefront-client/references/nuxt.md +121 -0
- package/skills/hydrogen-storefront-client/references/query-validation.md +95 -0
- package/skills/hydrogen-storefront-client/references/react-router.md +81 -0
- package/skills/hydrogen-storefront-client/references/solidstart.md +131 -0
- package/skills/hydrogen-storefront-client/references/sveltekit.md +169 -0
- package/skills/hydrogen-variant-form/SKILL.md +250 -0
- package/skills/hydrogen-variant-form/references/nextjs.md +203 -0
- package/skills/hydrogen-variant-form/references/nuxt.md +95 -0
- package/skills/hydrogen-variant-form/references/react.md +71 -0
- package/dist/dev/get-virtual-routes-H72465VB.js +0 -74
- package/dist/dev/hydrogen-routes.d.ts +0 -5
- package/dist/dev/hydrogen-routes.js +0 -25
- package/dist/development/get-virtual-routes-ACFZHU42.js +0 -71
- package/dist/development/get-virtual-routes-ACFZHU42.js.map +0 -1
- package/dist/development/index.cjs +0 -6948
- package/dist/development/index.cjs.map +0 -1
- package/dist/development/index.js +0 -6601
- package/dist/development/index.js.map +0 -1
- package/dist/development/log-seo-tags-IG37ONQ2.js +0 -74
- package/dist/development/log-seo-tags-IG37ONQ2.js.map +0 -1
- package/dist/development/react-router-preset.d.ts +0 -45
- package/dist/development/react-router-preset.js +0 -49
- package/dist/development/react-router-preset.js.map +0 -1
- package/dist/index.cjs +0 -1
- package/dist/oxygen/index.d.ts +0 -197
- package/dist/oxygen/index.js +0 -107
- package/dist/production/get-virtual-routes-ZZEGQQDV.js +0 -3
- package/dist/production/get-virtual-routes-ZZEGQQDV.js.map +0 -1
- package/dist/production/index.cjs +0 -1019
- package/dist/production/index.cjs.map +0 -1
- package/dist/production/index.d.cts +0 -3260
- package/dist/production/index.d.ts +0 -3260
- package/dist/production/index.js +0 -1019
- package/dist/production/index.js.map +0 -1
- package/dist/production/log-seo-tags-TY72EQWZ.js +0 -2
- package/dist/production/log-seo-tags-TY72EQWZ.js.map +0 -1
- package/dist/production/react-router-preset.d.ts +0 -45
- package/dist/production/react-router-preset.js +0 -11
- package/dist/react-router.d.ts +0 -75
- package/dist/vite/chunk-4O5OI3OO.js +0 -127
- package/dist/vite/chunk-FAQ7TVHC.js +0 -9
- package/dist/vite/chunk-QXBQVDMY.js +0 -68
- package/dist/vite/compat-date.d.ts +0 -3
- package/dist/vite/compat-date.js +0 -6
- package/dist/vite/get-virtual-routes.d.ts +0 -21
- package/dist/vite/get-virtual-routes.js +0 -74
- package/dist/vite/hydrogen-middleware.d.ts +0 -9
- package/dist/vite/hydrogen-middleware.js +0 -7
- package/dist/vite/plugin.d.ts +0 -22
- package/dist/vite/plugin.js +0 -171
- package/dist/vite/request-events.d.ts +0 -144
- package/dist/vite/request-events.js +0 -10
- package/dist/vite/types.d.ts +0 -5
- package/dist/vite/types.js +0 -0
- package/dist/vite/virtual-routes/assets/debug-network.css +0 -609
- package/dist/vite/virtual-routes/assets/favicon-dark.svg +0 -20
- package/dist/vite/virtual-routes/assets/favicon.svg +0 -28
- package/dist/vite/virtual-routes/assets/inter-variable-font.woff2 +0 -0
- package/dist/vite/virtual-routes/assets/jetbrainsmono-variable-font.woff2 +0 -0
- package/dist/vite/virtual-routes/assets/styles.css +0 -249
- package/dist/vite/virtual-routes/components/FlameChartWrapper.jsx +0 -123
- package/dist/vite/virtual-routes/components/HydrogenLogoBaseBW.jsx +0 -32
- package/dist/vite/virtual-routes/components/HydrogenLogoBaseColor.jsx +0 -47
- package/dist/vite/virtual-routes/components/IconBanner.jsx +0 -292
- package/dist/vite/virtual-routes/components/IconClose.jsx +0 -38
- package/dist/vite/virtual-routes/components/IconDiscard.jsx +0 -44
- package/dist/vite/virtual-routes/components/IconError.jsx +0 -61
- package/dist/vite/virtual-routes/components/IconGithub.jsx +0 -23
- package/dist/vite/virtual-routes/components/IconTwitter.jsx +0 -21
- package/dist/vite/virtual-routes/components/Layout.jsx +0 -7
- package/dist/vite/virtual-routes/components/RequestDetails.jsx +0 -171
- package/dist/vite/virtual-routes/components/RequestTable.jsx +0 -91
- package/dist/vite/virtual-routes/components/RequestWaterfall.jsx +0 -151
- package/dist/vite/virtual-routes/layout.jsx +0 -33
- package/dist/vite/virtual-routes/lib/useDebugNetworkServer.jsx +0 -178
- package/dist/vite/virtual-routes/routes/[.]well-known.appspecific.com[.]chrome[.]devtools[.]json.jsx +0 -37
- package/dist/vite/virtual-routes/routes/[.]well-known.appspecific.com[.]chrome[.]devtools[.]json.tsx +0 -79
- package/dist/vite/virtual-routes/routes/graphiql.jsx +0 -5
- package/dist/vite/virtual-routes/routes/index.jsx +0 -265
- package/dist/vite/virtual-routes/routes/subrequest-profiler.jsx +0 -243
- package/dist/vite/virtual-routes/virtual-root-with-layout.jsx +0 -63
- package/dist/vite/virtual-routes/virtual-root.jsx +0 -67
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: hydrogen-cart-ui
|
|
3
|
+
description: >
|
|
4
|
+
Behavioral guide for building cart UI with @shopify/hydrogen: line items,
|
|
5
|
+
quantity and remove controls, optimistic updates, discount, note, and cart
|
|
6
|
+
attribute inputs, and the full-page /cart fallback. Use when writing,
|
|
7
|
+
modifying, or reviewing cart line-item UI, quantity/remove controls, or cart
|
|
8
|
+
mutation forms. Framework agnostic.
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
# Cart Primitive
|
|
12
|
+
|
|
13
|
+
The cart primitive is a client-side store that syncs with Shopify via Standard Actions events. It provides optimistic updates, scoped error handling, and a form-based mutation API. The store is framework-neutral; framework-specific bindings are thin wrappers over the core store.
|
|
14
|
+
|
|
15
|
+
## Framework Bindings
|
|
16
|
+
|
|
17
|
+
Before building UI, check whether this skill has a reference file for the app's framework in `references/`. If one exists, read it and use that framework binding first; the reference owns provider setup, state selectors, and form helpers for that runtime.
|
|
18
|
+
|
|
19
|
+
If there is no matching reference, there may be no packaged `@shopify/hydrogen/<framework>` export. Use the framework-neutral `createCartStore` and `createCartFormRegister` from `@shopify/hydrogen` directly, subscribe to store changes with the framework's reactivity primitive, and apply every rule in this skill yourself. Packaged bindings are thin wrappers over these same core APIs — match their behavior, do not invent a new contract.
|
|
20
|
+
|
|
21
|
+
In island-based frameworks, use the binding for the island's UI framework when a matching reference exists; otherwise use the core store directly.
|
|
22
|
+
|
|
23
|
+
## Route Placement
|
|
24
|
+
|
|
25
|
+
When creating a full cart page, use the app's existing route convention when present; otherwise create `/cart`. This page is separate from Hydrogen's `/api/cart` server handler, which is registered with `createCartServerHandlers()` through `handleShopifyRoutes`.
|
|
26
|
+
|
|
27
|
+
**The `/cart` page must render from the server data path.** It is the fallback route for the cart drawer (reachable as a real `/cart` link in the footer), so its line items and totals must not depend only on an ad hoc client store read. Seed `CartProvider` with `initialData` from the server data path (React Router loader, Next.js server layout/page). When the framework can preserve streamed promises, pass the unresolved cart promise as `initialData` instead of awaiting it; the Next.js reference is the best current non-blocking pattern. If strict no-JS HTML must include the real cart today, pass the resolved handler envelope instead. A `/cart` page whose only data source is a client `useCart` read renders "Your cart is empty" without JS even when the shopper has items. See the framework reference for the exact wiring.
|
|
28
|
+
|
|
29
|
+
**The native no-JS add-to-cart POST must set the cart cookie server-side.** With scripting off, the add-to-cart `<form method="post">` submits natively and the cart server handler must respond with the cart cookie so the next `/cart` request server-renders the seeded cart — not only service the hydrated `fetch`. See the `hydrogen-request-handlers` skill for the endpoint contract.
|
|
30
|
+
|
|
31
|
+
## How the store works
|
|
32
|
+
|
|
33
|
+
The store holds a `CartState` and notifies subscribers when it changes. `state.readyPromise` is present while an applicable full-cart load is pending and resolves after the resulting state is published. Mutations flow through Shopify Standard Actions — the store listens for `shopify:cart:lines-update`, `shopify:cart:discount-update`, `shopify:cart:note-update`, and `shopify:cart:attributes-update` DOM events. Each event carries a `promise` that resolves with the server response.
|
|
34
|
+
|
|
35
|
+
On mutation:
|
|
36
|
+
|
|
37
|
+
1. The store applies an **optimistic projection** over its settled state, so the UI-visible state changes immediately.
|
|
38
|
+
2. The affected entity is added to `pending` — a set of in-flight line IDs or discount codes, or a note/attributes boolean.
|
|
39
|
+
3. When the promise resolves, the store folds that transaction into settled state and reapplies remaining projections. Overlapping mutations discard ambiguous response snapshots, then coalesce one authoritative cart refresh after the burst settles.
|
|
40
|
+
4. On failure, the store removes only the failed projection, preserves unrelated work, and clears the matching pending entry.
|
|
41
|
+
|
|
42
|
+
When overlapping mutations make response snapshots ambiguous, the store sets `state.revalidating` to `true`. It starts one authoritative refresh after those mutations settle and clears the flag when the refresh completes or fails. Until then, server-derived values such as costs remain at their last trustworthy value. A refresh failure preserves the locally reconciled cart and appears in `errors.network`.
|
|
43
|
+
|
|
44
|
+
The store supersedes keyed mutations for the same line, discount batch, note, or complete attribute list. Relative additions remain independent so every submitted quantity reaches the server; their projections are reconciled together without disabling controls.
|
|
45
|
+
|
|
46
|
+
## Stable selectors
|
|
47
|
+
|
|
48
|
+
Store selectors should select primitives or stable references from the store. Do not allocate arrays, objects, maps, sets, or derived view models inside a selector unless the framework binding also accepts an equality function.
|
|
49
|
+
|
|
50
|
+
Bad:
|
|
51
|
+
|
|
52
|
+
```ts
|
|
53
|
+
const selectMessages = (state) => [
|
|
54
|
+
...state.errors.network.map((error) => error.message),
|
|
55
|
+
...state.errors.cart.userErrors.map((error) => error.message),
|
|
56
|
+
];
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
That selector returns a new array whenever the store notifies subscribers, so bindings that rely on referential equality cannot skip unchanged output. Select stable slices first, then derive:
|
|
60
|
+
|
|
61
|
+
```ts
|
|
62
|
+
const selectErrors = (state) => state.errors;
|
|
63
|
+
const messages = deriveFromErrors(errors, () => {
|
|
64
|
+
return [
|
|
65
|
+
...errors.network.map((error) => error.message),
|
|
66
|
+
...errors.cart.userErrors.map((error) => error.message),
|
|
67
|
+
];
|
|
68
|
+
});
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
## Pending state
|
|
72
|
+
|
|
73
|
+
`state.pending` tracks what is currently in-flight:
|
|
74
|
+
|
|
75
|
+
- `pending.lines` — `Set<string>` of line IDs with mutations in flight.
|
|
76
|
+
- `pending.discountCodes` — `Set<string>` of discount codes being applied or removed.
|
|
77
|
+
- `pending.cost` — `true` when pending line or discount mutations can leave totals stale.
|
|
78
|
+
- `pending.note` — `boolean` indicating whether a note save is in flight.
|
|
79
|
+
- `pending.attributes` — `boolean` indicating whether a complete attribute-list update is in flight.
|
|
80
|
+
|
|
81
|
+
Any value whose entity is in a pending set is **optimistic and unconfirmed**. The UI must treat it differently from confirmed values.
|
|
82
|
+
|
|
83
|
+
`state.revalidating` is `true` while the store refreshes authoritative cart-wide fields after overlapping mutations. Treat totals as unconfirmed and suppress cart analytics while it is true.
|
|
84
|
+
|
|
85
|
+
## Error state
|
|
86
|
+
|
|
87
|
+
`state.errors` is scoped to the entity that caused the error:
|
|
88
|
+
|
|
89
|
+
- `errors.lines` — `Map<string, CartErrorGroup>` keyed by line ID.
|
|
90
|
+
- `errors.discountCodes` — `Map<string, CartErrorGroup>` keyed by discount code string.
|
|
91
|
+
- `errors.note` — `CartErrorGroup` for note-related errors.
|
|
92
|
+
- `errors.attributes` — `Map<string, CartErrorGroup>` keyed by attribute key.
|
|
93
|
+
- `errors.cart` — `CartErrorGroup` for cart-level errors not attributable to a specific entity.
|
|
94
|
+
- `errors.network` — `CartNetworkEntry[]` for transport failures (timeouts, HTTP errors).
|
|
95
|
+
- `errors.lastUpdatedAt` — timestamp of the most recent error update across any scope. Per-scope timestamps also exist (`linesUpdatedAt`, `discountCodesUpdatedAt`, `attributesUpdatedAt`, etc.).
|
|
96
|
+
|
|
97
|
+
Each `CartErrorGroup` contains `{ userErrors: CartUserError[], warnings: CartWarning[] }`.
|
|
98
|
+
|
|
99
|
+
Errors survive unrelated cart work and clear when a new mutation begins for the same key.
|
|
100
|
+
|
|
101
|
+
---
|
|
102
|
+
|
|
103
|
+
## Rules
|
|
104
|
+
|
|
105
|
+
### Money
|
|
106
|
+
|
|
107
|
+
- **NEVER calculate currency amounts on the client.** Display server-provided amounts (`line.cost.totalAmount`, `state.data.cost.subtotalAmount`, etc.) directly. Client-side arithmetic drifts from the truth when discounts, taxes, duties, or rounding apply. If a value is stale because a mutation is in-flight, show it with pending UI — not a client-computed estimate.
|
|
108
|
+
- **Format with Hydrogen money helpers**, not string concatenation. Use the local `hydrogen-money` skill for an app wrapper around `formatMoney()`. The store provides `amount` (string) and `currencyCode` (string).
|
|
109
|
+
|
|
110
|
+
### Optimistic interactions
|
|
111
|
+
|
|
112
|
+
- **Keep rapid-action controls interactive during pending state.** Increase, decrease, remove, apply, and remove-discount controls must remain interactive. The store's abort-controller pattern makes concurrent mutations safe — a new click supersedes the in-flight request. A save-style editor, such as a gift-message attribute form, may disable its own submit button while its scoped mutation is pending when duplicate saves provide no value. Disabling for a non-pending reason is also fine — for example, the decrease control may be disabled when the quantity is already `<= 1`.
|
|
113
|
+
- **Inventory-aware set-quantity clamping is opt-in.** The default cart query does not request `ProductVariant.quantityAvailable` because that field requires the `unauthenticated_read_product_inventory` Storefront API scope. If a cart UI needs the entered quantity to clamp to sellable inventory before submission, add `quantityAvailable` to the app's custom cart fragment and enable the Hydrogen channel's product inventory permission (`unauthenticated_read_product_inventory`). Without that field, let Shopify validate inventory and surface the returned line error.
|
|
114
|
+
- **NEVER show a spinner or skeleton where a stale value would do.** The user already sees a quantity and a price. Replacing confirmed-looking content with a loading state is a regression. Show the previous value with a visual indicator that it's unconfirmed.
|
|
115
|
+
- **ALWAYS visually indicate unconfirmed data.** Any value whose entity is in a `pending` set must look visually distinct from confirmed values. Reduced opacity is the reference pattern — the stale text acts as a spatial placeholder (like a skeleton), not as readable content. Rules of contrast can be disregarded because the pending value is a signal, not content the user needs to read.
|
|
116
|
+
- **NEVER block navigation during pending.** Cart mutations are fire-and-forget from the user's perspective. No confirmation dialogs on route change.
|
|
117
|
+
|
|
118
|
+
### Errors
|
|
119
|
+
|
|
120
|
+
- **ALWAYS display errors closest to the element they describe.** A line item error appears next to that line item. A discount code error appears next to that discount code. Errors should not only live in a distant banner.
|
|
121
|
+
- **Use a banner only for errors that have no inline home.** This includes: network errors, cart-level errors, and orphaned line errors.
|
|
122
|
+
- **Orphaned line errors** — when `errors.lines` contains entries for line IDs not present in `state.data.lines.nodes`, those errors have no inline target. Surface them in the banner. This happens when a line was removed but the server returned errors referencing its ID.
|
|
123
|
+
- **Inline errors must be accessible** — use `role="alert"` and link them to the nearest interactive element via `aria-describedby`. Mark the associated input as `aria-invalid="true"`.
|
|
124
|
+
- **Use `errors.lastUpdatedAt` for dismissal.** Track a local `dismissedAt` timestamp. If `lastUpdatedAt <= dismissedAt`, the banner is hidden. If a new error arrives with a newer timestamp, the banner reappears.
|
|
125
|
+
|
|
126
|
+
### Note editing
|
|
127
|
+
|
|
128
|
+
- **Maintain a local draft** for the note, synced from the store. When the server responds with an updated note, only overwrite the local draft if `pending.note` is `false`. This prevents the server response from clobbering the user's in-progress typing.
|
|
129
|
+
- **Allow users to click the save button even when the draft matches the stored note** (nothing to save), but prevent any action. This enables progressive enhancement and prevents frustration.
|
|
130
|
+
- **Show a pending indicator** while the note mutation is pending.
|
|
131
|
+
|
|
132
|
+
### Cart attribute editing
|
|
133
|
+
|
|
134
|
+
- **Treat an attribute update as complete-list replacement.** `attributes-update` sends the full next attribute list; attributes omitted from the submission are removed. When editing one attribute, explicitly include every unrelated existing attribute as a hidden keyed `attributeValue` field. Submit no attribute fields to clear all attributes.
|
|
135
|
+
- **Keep each key attached to its value.** Pass the attribute key to every `register("attributeValue", {key, ...})` call. The generated `attributes.<key>` field name encodes that key, so the server never relies on parallel field positions. Storefront API mutation inputs require string values, so normalize a nullable returned value deliberately (usually to `""`) before resubmitting it.
|
|
136
|
+
- **Use the register contract exactly.** Call `register("attributeValue", {key, ...})` rather than spelling its generated `attributes.<key>` field name by hand. The submit intent is kebab-case (`attributes-update`) because action intents follow the existing `discount-apply`, `discount-remove`, and `note-update` convention.
|
|
137
|
+
- **Maintain a local draft** for an editable attribute when the UI can remain mounted across server responses. Only sync confirmed attribute data into that draft while `pending.attributes` is `false`, so a response does not clobber typing that happened during the request.
|
|
138
|
+
- **Show scoped pending and errors.** Use `pending.attributes` for the save state and display entries from `errors.attributes` next to the editor for the matching key. A save-style submit button may be disabled until the attribute promise settles.
|
|
139
|
+
|
|
140
|
+
### Form structure
|
|
141
|
+
|
|
142
|
+
- **Each line item is its own form.** This gives each line its own identity input and its own submit buttons. A single form containing multiple lines creates ambiguity about which line an action targets.
|
|
143
|
+
- **Each line item form must preserve the progressive-enhancement shape.** The rendered structure will vary by framework and design system, but every line item quantity form needs the same Hydrogen contract: `register("set")`, `register("lineId", { value: line.id })`, and a real editable quantity input using `register("quantity", { value: line.quantity, interactive: true })`. Increase, decrease, and remove buttons are additional submit controls, not replacements for the set intent or the quantity input.
|
|
144
|
+
- **The `set` control is a hidden submit button, not a hidden input.** `register("set")` already returns `{ type: "submit", hidden: true }`; render it on a `<button>`. Do not swap it for `<input type="hidden">` — that removes the submit button, so pressing Enter in the quantity input no longer submits the set action.
|
|
145
|
+
- **Each discount "remove" button is its own form** — separate from the "apply" form. The apply form needs input validation (empty/duplicate prevention); each remove form is a single action.
|
|
146
|
+
- **Attribute forms submit keyed values.** Render one `register("attributeValue", {key, value})` control for every attribute in the complete next list, then submit with `register("attributes-update")`.
|
|
147
|
+
|
|
148
|
+
### Loading
|
|
149
|
+
|
|
150
|
+
- **While `loading` is `true`**, show skeleton placeholders — not empty state. The cart hasn't been fetched yet.
|
|
151
|
+
- **When `loading` is `false` and `lines` is empty**, show empty state ("Your cart is empty" or equivalent).
|
|
152
|
+
- **If resolved `initialData` is provided** when creating the store, `loading` starts as `false` and the initial fetch is skipped. `initialData` is the cart handler data envelope (`{cart, errors?}`), not only the cart object. Use resolved data when the response should include full cart HTML. **If promise `initialData` is provided**, `loading` starts as `true`, `state.readyPromise` tracks readiness, and `useSuspenseCart` can suspend cart content while the app shell stays non-blocking. Use promises when the backend can stream them into the HTML response. `{cart: null}` means the server already completed the bootstrap with no usable cart, so the UI should render empty state without a browser retry. `undefined` means no server bootstrap was provided, so the store fetches `/api/cart` after hydration; use this only when a client-side fetch is acceptable, such as an SPA. Cart server handlers log bootstrap errors before returning them, so app loaders should forward handler data instead of logging or throwing those errors again.
|
|
153
|
+
|
|
154
|
+
---
|
|
155
|
+
|
|
156
|
+
## User Acceptance Tests
|
|
157
|
+
|
|
158
|
+
### Line items
|
|
159
|
+
|
|
160
|
+
1. **Increase quantity** — Click the increase control. The displayed quantity increments immediately. The quantity and totals appear in their pending visual state until the server confirms.
|
|
161
|
+
2. **Decrease quantity** — Click the decrease control. Same optimistic behavior. At quantity 1, decreasing removes the line.
|
|
162
|
+
3. **Remove line** — Activate the remove control. The line disappears immediately. Totals update optimistically.
|
|
163
|
+
4. **Rapid clicks** — Click increase five times quickly. Each click increments the displayed quantity by one. The store aborts intermediate requests. The final server-confirmed state matches the quantity the user sees.
|
|
164
|
+
5. **Failure rollback** — If the server rejects a line update, the quantity reverts to the last confirmed value. An error message appears inline next to the affected line item.
|
|
165
|
+
6. **Line-scoped error** — When the server returns a `userError` scoped to a specific line, the message appears adjacent to that line (not only in a banner). The relevant input is marked `aria-invalid`.
|
|
166
|
+
7. **Progressive quantity set** — The line item form contains a hidden `set` submit control, hidden/read-only `lineId`, and an editable quantity input. Pressing Enter in the quantity input submits a set-quantity action.
|
|
167
|
+
8. **No-JS line update** — If JavaScript fails or hydration has not run, the line item form can still submit an explicit quantity value to the cart action endpoint.
|
|
168
|
+
|
|
169
|
+
### Discount codes
|
|
170
|
+
|
|
171
|
+
9. **Apply discount** — Enter a code and submit. The code appears in the list immediately in its pending visual state. When the server confirms, the pending indicator clears and the "applied" / "not applicable" status updates.
|
|
172
|
+
10. **Duplicate prevention** — Submitting a code that is already present does nothing.
|
|
173
|
+
11. **Empty input prevention** — Submitting with a blank input does nothing.
|
|
174
|
+
12. **Remove discount** — Activate the remove control next to a code. The code disappears optimistically.
|
|
175
|
+
13. **Discount-scoped error** — If the server returns an error for a specific code, the message appears next to that code in the list.
|
|
176
|
+
|
|
177
|
+
### Order note
|
|
178
|
+
|
|
179
|
+
14. **Save note** — Edit the text and submit. A pending indicator appears while the mutation is in-flight.
|
|
180
|
+
15. **No-op save** — When the draft matches the stored note, clicking on save does nothing (but can still be clicked).
|
|
181
|
+
16. **Server sync without clobber** — After save completes, the local draft updates to match the server response — but only when `pending.note` is `false`, preserving any typing the user did in the meantime.
|
|
182
|
+
|
|
183
|
+
### Cart attributes
|
|
184
|
+
|
|
185
|
+
17. **Save attribute** — Edit an attribute and submit. The optimistic value appears immediately, `pending.attributes` is `true`, and the editor shows a saving state until confirmation.
|
|
186
|
+
18. **Preserve unrelated attributes** — Saving one attribute includes all unrelated existing key/value pairs; those attributes remain after the server response.
|
|
187
|
+
19. **Clear attributes** — Submitting an empty attribute list removes every cart attribute.
|
|
188
|
+
20. **Failure rollback** — If the update fails, the complete attribute list returns to the last confirmed baseline.
|
|
189
|
+
21. **Attribute-scoped error** — An error for a key appears next to that key's editor and marks its input invalid.
|
|
190
|
+
22. **No draft clobber** — Typing that occurs while an attribute save is pending is not overwritten by the confirming response.
|
|
191
|
+
|
|
192
|
+
### Error banner
|
|
193
|
+
|
|
194
|
+
23. **Network error** — When a mutation fails due to a transport error, a banner appears with the error message and a dismiss control.
|
|
195
|
+
24. **Cart-level error** — Errors not attributable to a line, code, note, or attribute appear in the banner.
|
|
196
|
+
25. **Orphaned line error** — If a line no longer exists in `state.data.lines.nodes` but `errors.lines` has an entry for its ID, that error appears in the banner.
|
|
197
|
+
26. **Dismiss and re-trigger** — Dismissing the banner hides it. A subsequent error (with a newer `lastUpdatedAt`) re-shows it.
|
|
198
|
+
|
|
199
|
+
### Totals
|
|
200
|
+
|
|
201
|
+
27. **Pending totals** — While any line or discount mutation is in-flight, subtotal and total appear in their pending visual state. The amounts shown are the last server-confirmed values — never client-computed.
|
|
202
|
+
28. **Settled totals** — After an ordinary mutation settles, totals display the latest server values. Overlapping mutation bursts retain pending styling and the last trustworthy amounts while one authoritative cart refresh is in flight.
|
|
203
|
+
|
|
204
|
+
### Loading
|
|
205
|
+
|
|
206
|
+
29. **Initial load** — Before the cart is fetched, show skeleton placeholders.
|
|
207
|
+
30. **Empty cart** — After fetch completes with zero lines, show empty state.
|
|
208
|
+
|
|
209
|
+
---
|
|
210
|
+
|
|
211
|
+
## Anti-patterns
|
|
212
|
+
|
|
213
|
+
- **Hand-rolled framework cart state.** If the skill has a matching framework reference, use its provider/hooks/helpers. Otherwise, use the core store directly instead of duplicating cart data in component state or custom reducers.
|
|
214
|
+
- **Client-seeded `/cart` page.** Mounting `CartProvider` with no `initialData` (or reading the cart only through a `"use client"` `useCart` hook) leaves the SSR HTML empty, so the `/cart` page cannot render the server cart. Read the cart in the server data path and pass `initialData`. Use resolved `initialData` when strict no-JS live cart HTML is required; use promise `initialData` when the framework can stream and hydrated cart content is wrapped in Suspense.
|
|
215
|
+
- **Quantity as text only.** Rendering quantity as a `<span>` with only plus/minus buttons breaks the set-quantity path and the no-JS fallback. Use a real input wired with `register("quantity", { value, interactive: true })`.
|
|
216
|
+
- **Plus/minus-only line forms.** Increase/decrease/remove buttons do not replace `register("set")` and the interactive quantity input. Omitting them breaks the form invariant even if hydrated clicks appear to work.
|
|
217
|
+
- **Drawer-specific line form drift.** The cart drawer may have a different layout from the `/cart` page, but its line item forms must keep the same Hydrogen form contract. Prefer sharing line item form components between the page and drawer.
|
|
218
|
+
- **Banner-only errors.** A line-level error displayed far from the line it refers to is effectively invisible. Show inline first; promote to the banner only when there's no inline target.
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
# Next.js App Router
|
|
2
|
+
|
|
3
|
+
Next.js App Router cart bindings. This is the primary reference for promise-based cart seeding. For the framework-agnostic React story, see `react.md`.
|
|
4
|
+
|
|
5
|
+
With `cacheComponents: true`, keep the root layout as a static shell. Render an async `AppShell` inside `<Suspense>`, call `await connection()` there, create the cart read, and pass its promise to a `"use client"` provider wrapper as `initialData`. Do not `await` the cart before rendering the shell when the app is optimized for streaming. `CartProvider` tracks the promise and `useSuspenseCart()` lets cart content suspend locally, so the static shell can stream while cart views wait behind a cart-specific fallback.
|
|
6
|
+
|
|
7
|
+
```tsx
|
|
8
|
+
// app/providers.tsx
|
|
9
|
+
"use client";
|
|
10
|
+
|
|
11
|
+
import type { ReactNode } from "react";
|
|
12
|
+
import { CartProvider } from "@/lib/cart";
|
|
13
|
+
import type { cartHandlers } from "@/lib/cart-handlers";
|
|
14
|
+
|
|
15
|
+
type CartData = Awaited<ReturnType<typeof cartHandlers.get>>["data"];
|
|
16
|
+
|
|
17
|
+
export function Providers({ cartData, children }: { cartData: Promise<CartData>; children: ReactNode }) {
|
|
18
|
+
return <CartProvider initialData={cartData}>{children}</CartProvider>;
|
|
19
|
+
}
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
```tsx
|
|
23
|
+
// app/layout.tsx
|
|
24
|
+
import { Suspense } from "react";
|
|
25
|
+
|
|
26
|
+
import { AppShell } from "./app-shell";
|
|
27
|
+
|
|
28
|
+
export default function RootLayout({ children }: { children: React.ReactNode }) {
|
|
29
|
+
return (
|
|
30
|
+
<html lang="en">
|
|
31
|
+
<body>
|
|
32
|
+
<Suspense fallback={null}>
|
|
33
|
+
<AppShell>{children}</AppShell>
|
|
34
|
+
</Suspense>
|
|
35
|
+
</body>
|
|
36
|
+
</html>
|
|
37
|
+
);
|
|
38
|
+
}
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
```tsx
|
|
42
|
+
// app/app-shell.tsx
|
|
43
|
+
import { connection } from "next/server";
|
|
44
|
+
|
|
45
|
+
import { cartHandlers } from "@/lib/cart-handlers";
|
|
46
|
+
import { getStorefrontClient } from "@/lib/storefront";
|
|
47
|
+
import { Providers } from "./providers";
|
|
48
|
+
|
|
49
|
+
export async function AppShell({ children }: { children: React.ReactNode }) {
|
|
50
|
+
await connection();
|
|
51
|
+
|
|
52
|
+
const cartData = getStorefrontClient().then((storefrontClient) =>
|
|
53
|
+
cartHandlers.get({ storefrontClient }).then(({ data }) => data),
|
|
54
|
+
);
|
|
55
|
+
|
|
56
|
+
return (
|
|
57
|
+
<Providers cartData={cartData}>
|
|
58
|
+
{children}
|
|
59
|
+
</Providers>
|
|
60
|
+
);
|
|
61
|
+
}
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
Wrap cart content that needs the full cart in a local Suspense boundary and read it with `useSuspenseCart()`:
|
|
65
|
+
|
|
66
|
+
```tsx
|
|
67
|
+
// components/CartContent.tsx
|
|
68
|
+
"use client";
|
|
69
|
+
|
|
70
|
+
import { Suspense } from "react";
|
|
71
|
+
import { useSuspenseCart } from "@/lib/cart";
|
|
72
|
+
|
|
73
|
+
function CartLoadingFallback() {
|
|
74
|
+
return <p role="status">Loading cart…</p>;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export function CartContent() {
|
|
78
|
+
return (
|
|
79
|
+
<Suspense fallback={<CartLoadingFallback />}>
|
|
80
|
+
<CartContentBody />
|
|
81
|
+
</Suspense>
|
|
82
|
+
);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function CartContentBody() {
|
|
86
|
+
const cart = useSuspenseCart((state) => state.data);
|
|
87
|
+
return <CartLines cart={cart} />;
|
|
88
|
+
}
|
|
89
|
+
```
|
|
@@ -0,0 +1,263 @@
|
|
|
1
|
+
# React Cart Bindings
|
|
2
|
+
|
|
3
|
+
## Contents
|
|
4
|
+
|
|
5
|
+
- Root Setup
|
|
6
|
+
- Custom Cart Fields
|
|
7
|
+
- Reading Cart State
|
|
8
|
+
- Mutating Cart State
|
|
9
|
+
|
|
10
|
+
For React apps, derive cart bindings once from the cart server handlers. Do not hand-roll `useState` cart state, custom mutation fetchers, or bespoke optimistic reducers unless the app is not using React.
|
|
11
|
+
|
|
12
|
+
```tsx
|
|
13
|
+
import { createCartComponents } from "@shopify/hydrogen/react";
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
## Root Setup
|
|
17
|
+
|
|
18
|
+
Wrap the app in `<CartProvider>`. Keep cart server handlers in a server-only module, separate from React bindings. If the framework can fetch the cart on the server, pass `initialData` so the cart does not depend on a separate client bootstrap. When the framework can preserve streamed promises, pass the unresolved cart promise instead of awaiting it; this keeps the shell non-blocking while `useSuspenseCart()` suspends hydrated cart content. Use the request-scoped private `storefrontClient` created by the request setup. In React Router, the usual shape is:
|
|
19
|
+
|
|
20
|
+
```tsx
|
|
21
|
+
// app/lib/cart-handlers.ts
|
|
22
|
+
import { createCartServerHandlers } from "@shopify/hydrogen";
|
|
23
|
+
|
|
24
|
+
export const cartHandlers = createCartServerHandlers();
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
```tsx
|
|
28
|
+
// app/lib/cart.ts
|
|
29
|
+
import { createCartComponents } from "@shopify/hydrogen/react";
|
|
30
|
+
|
|
31
|
+
import type { cartHandlers } from "./cart-handlers";
|
|
32
|
+
|
|
33
|
+
export const { CartProvider, useCart, useSuspenseCart, useCartForm } =
|
|
34
|
+
createCartComponents<typeof cartHandlers>();
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
```tsx
|
|
38
|
+
// app/root.tsx
|
|
39
|
+
import { storefrontClientContext } from "~/lib/storefront";
|
|
40
|
+
import { CartProvider } from "~/lib/cart";
|
|
41
|
+
import { cartHandlers } from "~/lib/cart-handlers";
|
|
42
|
+
|
|
43
|
+
export async function loader({ context }: Route.LoaderArgs) {
|
|
44
|
+
const storefrontClient = context.get(storefrontClientContext);
|
|
45
|
+
const cartData = cartHandlers.get({ storefrontClient }).then(({ data }) => data);
|
|
46
|
+
|
|
47
|
+
return { cartData };
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export default function App({ loaderData }: Route.ComponentProps) {
|
|
51
|
+
return (
|
|
52
|
+
<CartProvider initialData={loaderData.cartData}>
|
|
53
|
+
<Outlet />
|
|
54
|
+
</CartProvider>
|
|
55
|
+
);
|
|
56
|
+
}
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
Pass the full handler data envelope (`{cart, errors?}`) to `initialData`. Do not unwrap to `data.cart`: `{cart: null}` tells the client the server already checked and found no usable cart, while omitted `initialData` tells the client to fetch `/api/cart` after hydration. The cart server handlers log returned cart errors on the server, so do not throw just to force a route error.
|
|
60
|
+
|
|
61
|
+
If there is no server cart fetch yet, still wrap with `<CartProvider>`; it will fetch `/api/cart` after hydration. Register the `cartHandlers` from the server-only module in the app's central `handleShopifyRoutes` wiring; use the `hydrogen-request-handlers` skill for the full framework-specific setup.
|
|
62
|
+
|
|
63
|
+
> Next.js App Router has its own cart binding shape — see `references/nextjs.md`.
|
|
64
|
+
|
|
65
|
+
Seed `initialData` from the server data path on every framework that can (React Router loader, Next.js server layout/page). Prefer promise `initialData` where the framework supports it and the cart UI is wrapped in local Suspense; the Next.js reference is the main template for that pattern. Reserve the no-`initialData`, fetch-after-hydration path for runtimes with no server cart read.
|
|
66
|
+
|
|
67
|
+
Under `cacheComponents: true`, route segment configs (`export const dynamic`/`revalidate`) are replaced by `use cache`/`cacheLife` (remove them), and `headers()`/`cookies()`/`connection()` outside `<Suspense>` are rejected as runtime/uncached data, so the root layout must be a **static shell wrapping an async cart-seeding child in `<Suspense>`** (use `await connection()` from `next/server` to opt into dynamic rendering inside that child).
|
|
68
|
+
|
|
69
|
+
## Custom Cart Fields
|
|
70
|
+
|
|
71
|
+
If the cart needs extra fields, pass `fragment` with a fragment named `CartFragment` to `createCartServerHandlers()` and derive typed React bindings from the handlers:
|
|
72
|
+
|
|
73
|
+
```tsx
|
|
74
|
+
// app/lib/cart-handlers.ts
|
|
75
|
+
import { createCartServerHandlers, gql } from "@shopify/hydrogen";
|
|
76
|
+
|
|
77
|
+
const cartFragment = gql(`
|
|
78
|
+
fragment CartFragment on Cart {
|
|
79
|
+
lines(first: 250) {
|
|
80
|
+
nodes {
|
|
81
|
+
merchandise {
|
|
82
|
+
... on ProductVariant {
|
|
83
|
+
availableForSale
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
`);
|
|
90
|
+
|
|
91
|
+
export const cartHandlers = createCartServerHandlers({
|
|
92
|
+
fragment: cartFragment,
|
|
93
|
+
});
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
```tsx
|
|
97
|
+
// app/lib/cart.ts
|
|
98
|
+
import { createCartComponents } from "@shopify/hydrogen/react";
|
|
99
|
+
|
|
100
|
+
import type { cartHandlers } from "./cart-handlers";
|
|
101
|
+
|
|
102
|
+
export const { CartProvider, useCart, useSuspenseCart, useCartForm } =
|
|
103
|
+
createCartComponents<typeof cartHandlers>();
|
|
104
|
+
|
|
105
|
+
function CartLines() {
|
|
106
|
+
const lines = useCart((state) => state.data.lines.nodes);
|
|
107
|
+
|
|
108
|
+
return (
|
|
109
|
+
<ul>
|
|
110
|
+
{lines.map((line) => {
|
|
111
|
+
const merchandise = line.merchandise;
|
|
112
|
+
const selectedOptions = merchandise?.selectedOptions ?? [];
|
|
113
|
+
|
|
114
|
+
return (
|
|
115
|
+
<li key={line.id}>
|
|
116
|
+
{merchandise?.image ? (
|
|
117
|
+
<img src={merchandise.image.url} alt={merchandise.image.altText ?? ""} />
|
|
118
|
+
) : null}
|
|
119
|
+
{merchandise?.product.handle ? (
|
|
120
|
+
<a href={`/products/${merchandise.product.handle}`}>
|
|
121
|
+
{merchandise.product.title}
|
|
122
|
+
</a>
|
|
123
|
+
) : (
|
|
124
|
+
<span>{merchandise?.product.title ?? merchandise?.title ?? "Product"}</span>
|
|
125
|
+
)}
|
|
126
|
+
{selectedOptions.map((option) => (
|
|
127
|
+
<span key={option.name}>
|
|
128
|
+
{option.name}: {option.value}
|
|
129
|
+
</span>
|
|
130
|
+
))}
|
|
131
|
+
{merchandise?.availableForSale === false ? <span>Unavailable</span> : null}
|
|
132
|
+
</li>
|
|
133
|
+
);
|
|
134
|
+
})}
|
|
135
|
+
</ul>
|
|
136
|
+
);
|
|
137
|
+
}
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
Cart line merchandise fields are intentionally tolerant because optimistic lines and custom fragments can differ from a complete Storefront API cart response. Treat `line.merchandise`, `merchandise.selectedOptions`, `merchandise.title`, `merchandise.product.handle`, and `merchandise.image` as optional unless the app's custom cart fragment and types prove otherwise.
|
|
141
|
+
|
|
142
|
+
## Reading Cart State
|
|
143
|
+
|
|
144
|
+
Use `useCart(selector)` for reactive state. Select only what the component needs:
|
|
145
|
+
|
|
146
|
+
```tsx
|
|
147
|
+
const totalQuantity = useCart((state) => state.data.totalQuantity);
|
|
148
|
+
const lines = useCart((state) => state.data.lines.nodes);
|
|
149
|
+
const pendingLines = useCart((state) => state.pending.lines);
|
|
150
|
+
const totalsPending = useCart((state) => state.pending.cost === true || state.revalidating === true);
|
|
151
|
+
const lineErrors = useCart((state) => state.errors.lines);
|
|
152
|
+
const attributesPending = useCart((state) => state.pending.attributes);
|
|
153
|
+
const attributeErrors = useCart((state) => state.errors.attributes);
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
Use this for the navbar cart count, cart line list, totals, pending state, and scoped errors. Do not mirror selected cart state into React state; the store already publishes updates.
|
|
157
|
+
|
|
158
|
+
Use `useSuspenseCart(selector)` inside a local `<Suspense>` boundary when a cart view should suspend while full-cart data is loading:
|
|
159
|
+
|
|
160
|
+
```tsx
|
|
161
|
+
function CartContent() {
|
|
162
|
+
return (
|
|
163
|
+
<Suspense fallback={<p role="status">Loading cart…</p>}>
|
|
164
|
+
<CartContentBody />
|
|
165
|
+
</Suspense>
|
|
166
|
+
);
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
function CartContentBody() {
|
|
170
|
+
const cart = useSuspenseCart((state) => state.data);
|
|
171
|
+
return <CartLines cart={cart} />;
|
|
172
|
+
}
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
Keep using `useCart(selector)` for non-blocking UI such as cart counts, pending states, and optimistic controls.
|
|
176
|
+
|
|
177
|
+
## Mutating Cart State
|
|
178
|
+
|
|
179
|
+
Use `useCartForm()` for existing cart forms: line quantity changes, line removal, discount codes, order notes, and cart attributes. It returns `formProps()` and `register()` helpers that encode Hydrogen's cart action contract.
|
|
180
|
+
|
|
181
|
+
Line item quantity forms must keep this shape even when the surrounding markup, styling, or component boundaries differ:
|
|
182
|
+
|
|
183
|
+
```tsx
|
|
184
|
+
function LineItemQuantity({ line }: { line: { id: string; quantity: number } }) {
|
|
185
|
+
const { formProps, register } = useCartForm();
|
|
186
|
+
const pendingLines = useCart((state) => state.pending.lines);
|
|
187
|
+
|
|
188
|
+
return (
|
|
189
|
+
<form {...formProps()}>
|
|
190
|
+
<button type="submit" {...register("set")} hidden />
|
|
191
|
+
<input type="hidden" {...register("lineId", { value: line.id })} />
|
|
192
|
+
<button type="submit" {...register("decrease")}>
|
|
193
|
+
-
|
|
194
|
+
</button>
|
|
195
|
+
<input
|
|
196
|
+
{...register("quantity", { value: line.quantity, interactive: true })}
|
|
197
|
+
className={pendingLines.has(line.id) ? "opacity-30" : ""}
|
|
198
|
+
/>
|
|
199
|
+
<button type="submit" {...register("increase")}>
|
|
200
|
+
+
|
|
201
|
+
</button>
|
|
202
|
+
<button type="submit" {...register("remove")}>
|
|
203
|
+
Remove
|
|
204
|
+
</button>
|
|
205
|
+
</form>
|
|
206
|
+
);
|
|
207
|
+
}
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
Important React form fields:
|
|
211
|
+
|
|
212
|
+
- `formProps()` wires `method`, `action`, and submit handling to the cart store.
|
|
213
|
+
- `register("set")` is the default submit control for explicit line quantity updates; it already returns `{ type: "submit", hidden: true }`. Render it on a `<button>` as shown (see the `set` control rule in the skill's Form structure).
|
|
214
|
+
- `register("lineId", { value })` scopes the form to one line.
|
|
215
|
+
- `register("quantity", { value, interactive: true })` wires an editable quantity input for both hydrated and no-JS submissions. Do not replace it with a text-only `<span>`.
|
|
216
|
+
- `register("increase")`, `register("decrease")`, and `register("remove")` create line item controls.
|
|
217
|
+
- `register("discountCode", { defaultValue: "" })`, `register("discountCode", { value: code })`, `register("discount-apply")`, and `register("discount-remove")` create discount forms.
|
|
218
|
+
- `register("note")` and `register("note-update")` create note forms.
|
|
219
|
+
- Repeated `register("attributeValue", { key, value })` controls define the complete next cart attribute list; `register("attributes-update")` submits it. Preserve unrelated attributes explicitly because omitted attributes are removed.
|
|
220
|
+
|
|
221
|
+
A save-style attribute editor can use the scoped pending boolean to prevent duplicate saves while the promise resolves:
|
|
222
|
+
|
|
223
|
+
```tsx
|
|
224
|
+
function GiftMessage({ attributes }: { attributes: Array<{ key: string; value: string | null }> }) {
|
|
225
|
+
const { formProps, register } = useCartForm();
|
|
226
|
+
const pending = useCart((state) => state.pending.attributes);
|
|
227
|
+
const key = "gift-message";
|
|
228
|
+
|
|
229
|
+
return (
|
|
230
|
+
<form {...formProps()}>
|
|
231
|
+
{attributes.filter((attribute) => attribute.key !== key).map((attribute) => (
|
|
232
|
+
<input
|
|
233
|
+
key={attribute.key}
|
|
234
|
+
type="hidden"
|
|
235
|
+
{...register("attributeValue", {
|
|
236
|
+
key: attribute.key,
|
|
237
|
+
value: attribute.value ?? "",
|
|
238
|
+
})}
|
|
239
|
+
/>
|
|
240
|
+
))}
|
|
241
|
+
<textarea
|
|
242
|
+
{...register("attributeValue", {
|
|
243
|
+
key,
|
|
244
|
+
defaultValue: attributes.find((attribute) => attribute.key === key)?.value ?? "",
|
|
245
|
+
})}
|
|
246
|
+
/>
|
|
247
|
+
<button type="submit" {...register("attributes-update")} disabled={pending} aria-busy={pending}>
|
|
248
|
+
{pending ? "Saving…" : "Save message"}
|
|
249
|
+
</button>
|
|
250
|
+
</form>
|
|
251
|
+
);
|
|
252
|
+
}
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
If the cart page and drawer can render together, give the textarea a `useId()`-generated ID instead of a hard-coded document ID.
|
|
256
|
+
|
|
257
|
+
Each line item still gets its own form; the binding removes boilerplate, not the form identity requirement.
|
|
258
|
+
|
|
259
|
+
For product add-to-cart forms, use `formProps()` and `register()` from `useProductForm()` in the `hydrogen-variant-form` skill. The two helpers share names but encode different form contracts.
|
|
260
|
+
|
|
261
|
+
If you render the same line items in a cart drawer, share this line item form component with the `/cart` page when possible. If the drawer needs different markup, preserve the same `set` + `lineId` + interactive `quantity` contract.
|
|
262
|
+
|
|
263
|
+
When wiring less common cart forms, prefer the exact package types over memory. In an installed app, inspect `node_modules/@shopify/hydrogen/dist/index.d.mts` or use editor hover on `CartFormRegister` to confirm which `register(...)` calls require `{ value }` versus `{ defaultValue }`.
|