@shopify/hydrogen 2026.10.0 → 2026.10.2

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.
Files changed (65) hide show
  1. package/dist/cli/index.mjs +6 -3
  2. package/dist/client/client.mjs +2 -4
  3. package/dist/client/client.mjs.map +1 -1
  4. package/dist/client/i18n-variables.d.mts +13 -0
  5. package/dist/client/i18n-variables.mjs +84 -0
  6. package/dist/client/i18n-variables.mjs.map +1 -0
  7. package/dist/client/types.d.mts +13 -6
  8. package/dist/core/cart/cart.mjs +110 -23
  9. package/dist/core/cart/cart.mjs.map +1 -1
  10. package/dist/core/request-context.mjs +1 -1
  11. package/dist/core/request-routing/interceptors/proxy.mjs.map +1 -1
  12. package/dist/core/request-routing/registered-routes.d.mts +5 -1
  13. package/dist/core/request-routing/registered-routes.mjs +21 -7
  14. package/dist/core/request-routing/registered-routes.mjs.map +1 -1
  15. package/dist/core/request-routing/route-types.d.mts +16 -6
  16. package/dist/core/shopify-scripts/perfkit.mjs +8 -2
  17. package/dist/core/shopify-scripts/perfkit.mjs.map +1 -1
  18. package/dist/core/utils/load-script.mjs +6 -6
  19. package/dist/core/utils/load-script.mjs.map +1 -1
  20. package/dist/customer-account/client.mjs +1 -1
  21. package/dist/customer-account/session.mjs +1 -1
  22. package/dist/development/client/client.mjs +2 -4
  23. package/dist/development/client/client.mjs.map +1 -1
  24. package/dist/development/client/i18n-variables.d.mts +13 -0
  25. package/dist/development/client/i18n-variables.mjs +84 -0
  26. package/dist/development/client/i18n-variables.mjs.map +1 -0
  27. package/dist/development/client/types.d.mts +13 -6
  28. package/dist/development/core/cart/cart.mjs +110 -23
  29. package/dist/development/core/cart/cart.mjs.map +1 -1
  30. package/dist/development/core/request-context.mjs +1 -1
  31. package/dist/development/core/request-routing/interceptors/proxy.mjs.map +1 -1
  32. package/dist/development/core/request-routing/registered-routes.d.mts +5 -1
  33. package/dist/development/core/request-routing/registered-routes.mjs +21 -7
  34. package/dist/development/core/request-routing/registered-routes.mjs.map +1 -1
  35. package/dist/development/core/request-routing/route-types.d.mts +16 -6
  36. package/dist/development/core/shopify-scripts/perfkit.mjs +8 -2
  37. package/dist/development/core/shopify-scripts/perfkit.mjs.map +1 -1
  38. package/dist/development/core/utils/load-script.mjs +6 -6
  39. package/dist/development/core/utils/load-script.mjs.map +1 -1
  40. package/dist/development/graphql/type-resolver.d.mts +27 -1
  41. package/dist/development/react/cart.mjs +79 -25
  42. package/dist/development/react/cart.mjs.map +1 -1
  43. package/dist/graphql/type-resolver.d.mts +27 -1
  44. package/dist/react/cart.mjs +79 -25
  45. package/dist/react/cart.mjs.map +1 -1
  46. package/dist/vite/index.d.mts +11 -1
  47. package/dist/vite/index.mjs +21 -1
  48. package/dist/vite/index.mjs.map +1 -0
  49. package/dist/vue/shopify-scripts.d.mts +1 -1
  50. package/package.json +1 -1
  51. package/skills/hydrogen-cart-ui/SKILL.md +2 -2
  52. package/skills/hydrogen-request-handlers/SKILL.md +2 -2
  53. package/skills/hydrogen-request-handlers/references/frameworks.md +2 -7
  54. package/skills/hydrogen-request-handlers/references/nuxt.md +1 -3
  55. package/skills/hydrogen-storefront-client/SKILL.md +4 -2
  56. package/skills/migrate-to-hydrogen-2026-10/SKILL.md +332 -0
  57. package/skills/migrate-to-hydrogen-2026-10/references/analytics.md +24 -0
  58. package/skills/migrate-to-hydrogen-2026-10/references/cart.md +9 -0
  59. package/skills/migrate-to-hydrogen-2026-10/references/customer-account.md +10 -0
  60. package/skills/migrate-to-hydrogen-2026-10/references/dependencies-and-tooling.md +10 -0
  61. package/skills/migrate-to-hydrogen-2026-10/references/optional-subsystems.md +25 -0
  62. package/skills/migrate-to-hydrogen-2026-10/references/routing-and-loaders.md +10 -0
  63. package/skills/migrate-to-hydrogen-2026-10/references/sitemap-and-seo.md +5 -0
  64. package/skills/migrate-to-hydrogen-2026-10/references/typescript-and-gql-tada.md +7 -0
  65. package/skills/migrate-to-hydrogen-2026-10/references/ui-and-components.md +9 -0
@@ -203,13 +203,18 @@ function reuseVisibleReferences(previous, next) {
203
203
  pending
204
204
  };
205
205
  }
206
- function derivePending(state, transactions) {
206
+ function getVisiblePendingKeys(store, transaction) {
207
+ if (transaction.type !== "change_lines_quantity") return transaction.pendingKeys;
208
+ const ownedKeys = getOwnedSignalKeys(store, transaction);
209
+ return transaction.pendingKeys.filter((key) => !transaction.signalKeys.includes(key) || ownedKeys.includes(key));
210
+ }
211
+ function derivePending(store, state) {
207
212
  const lines = /* @__PURE__ */ new Set();
208
213
  const discountCodes = /* @__PURE__ */ new Set();
209
214
  let note = false;
210
215
  let attributes = false;
211
216
  let cost = false;
212
- for (const transaction of transactions) for (const key of transaction.pendingKeys) {
217
+ for (const transaction of store.transactions) for (const key of getVisiblePendingKeys(store, transaction)) {
213
218
  if (key === NOTE_KEY) note = true;
214
219
  if (key === ATTRIBUTES_KEY) attributes = true;
215
220
  if (key.startsWith(LINE_KEY_PREFIX)) {
@@ -236,10 +241,13 @@ function derivePending(state, transactions) {
236
241
  }
237
242
  function projectVisibleState(store) {
238
243
  let state = store.settled;
239
- for (const transaction of store.transactions) state = transaction.projectPayload(state);
244
+ for (const transaction of store.transactions) {
245
+ const ownedKeys = getOwnedSignalKeys(store, transaction);
246
+ state = transaction.projectPayload(state, (key) => ownedKeys.includes(key));
247
+ }
240
248
  state = {
241
249
  ...state,
242
- pending: derivePending(state, store.transactions)
250
+ pending: derivePending(store, state)
243
251
  };
244
252
  for (const error of store.projectedErrors) state = error.project(state);
245
253
  if (store.revalidation.visible) state = {
@@ -288,9 +296,9 @@ function createOwner(store, keys) {
288
296
  }
289
297
  return owner;
290
298
  }
291
- function ownsSignalKeys(store, transaction) {
292
- if (!transaction.owner) return true;
293
- return transaction.signalKeys.every((key) => store.keyedOwners.get(key)?.token === transaction.owner?.token);
299
+ function getOwnedSignalKeys(store, transaction) {
300
+ if (!transaction.owner) return transaction.signalKeys;
301
+ return transaction.signalKeys.filter((key) => store.keyedOwners.get(key)?.token === transaction.owner?.token);
294
302
  }
295
303
  function releaseSignalKeys(store, transaction) {
296
304
  if (!transaction.owner) return;
@@ -413,7 +421,9 @@ function writeNetworkError(state, error, timestampMs) {
413
421
  }
414
422
  function projectLineErrors(state, failure, lineIds, timestampMs) {
415
423
  if (!failure) return state;
416
- const grouped = groupLineErrors(failure, lineIds);
424
+ return writeLineErrors(state, groupLineErrors(failure, lineIds), timestampMs);
425
+ }
426
+ function writeLineErrors(state, grouped, timestampMs) {
417
427
  return {
418
428
  ...state,
419
429
  errors: {
@@ -567,6 +577,22 @@ function mergeServerLine(previous, next) {
567
577
  ...merchandise ? { merchandise } : {}
568
578
  };
569
579
  }
580
+ function addOwnedLineErrors(failure, lineIds, ownedLineIds, addError) {
581
+ const grouped = groupLineErrors(failure, lineIds);
582
+ for (const [lineId, group] of grouped.lines) {
583
+ if (lineIds.includes(lineId) && !ownedLineIds.has(lineId)) continue;
584
+ const lines = new Map([[lineId, group]]);
585
+ addError((current, timestampMs) => writeLineErrors(current, {
586
+ lines,
587
+ cart: createEmptyErrorGroup()
588
+ }, timestampMs), [lineKey(lineId)]);
589
+ }
590
+ if (grouped.cart.userErrors.length === 0 && grouped.cart.warnings.length === 0) return;
591
+ addError((current, timestampMs) => writeLineErrors(current, {
592
+ lines: /* @__PURE__ */ new Map(),
593
+ cart: grouped.cart
594
+ }, timestampMs), [...ownedLineIds].map(lineKey));
595
+ }
570
596
  function hasProjectedErrors(result) {
571
597
  return (result?.userErrors?.length ?? 0) > 0 || (result?.warnings?.length ?? 0) > 0;
572
598
  }
@@ -579,6 +605,31 @@ function transportAddToCart(payload, signal, updateCart, cartId) {
579
605
  ...payload.eventDetail && { event: { detail: payload.eventDetail } }
580
606
  });
581
607
  }
608
+ function projectLineQuantity(state, payload) {
609
+ const previous = getLines(state.data);
610
+ if (!previous.some((line) => line.id === payload.lineId)) return state;
611
+ const lines = payload.quantity === 0 ? previous.filter((line) => line.id !== payload.lineId) : previous.map((line) => line.id === payload.lineId ? {
612
+ ...line,
613
+ quantity: payload.quantity
614
+ } : line);
615
+ return {
616
+ ...state,
617
+ data: reconcileCartLines(state.data, lines)
618
+ };
619
+ }
620
+ function settleLineQuantity(state, serverLines, payload, requestRevalidation) {
621
+ const previous = getLines(state.data);
622
+ const matching = serverLines.find((line) => line.id === payload.lineId);
623
+ if (!matching && payload.quantity > 0) {
624
+ requestRevalidation();
625
+ return state;
626
+ }
627
+ const lines = matching ? previous.map((line) => line.id === payload.lineId ? mergeServerLine(line, matching) : line) : previous.filter((line) => line.id !== payload.lineId);
628
+ return {
629
+ ...state,
630
+ data: reconcileCartLines(state.data, lines)
631
+ };
632
+ }
582
633
  const CART_TRANSACTION_TYPES = defineTransactionTypes({
583
634
  add_to_cart: {
584
635
  payload: {},
@@ -702,6 +753,22 @@ const CART_TRANSACTION_TYPES = defineTransactionTypes({
702
753
  },
703
754
  getSignalKeys: (_state, payload) => lineKey(payload.lineId)
704
755
  },
756
+ change_lines_quantity: {
757
+ payload: {},
758
+ transport: (payload, signal, updateCart) => updateCart({ lines: payload.lines.map(({ lineId, quantity }) => ({
759
+ id: lineId,
760
+ quantity
761
+ })) }, { signal }),
762
+ projectPayload: (state, payload, ownsKey) => payload.lines.filter(({ lineId }) => ownsKey(lineKey(lineId))).reduce(projectLineQuantity, state),
763
+ projectPromise: (state, result, payload, addError, { ownsKey, requestRevalidation }) => {
764
+ const owned = payload.lines.filter(({ lineId }) => ownsKey(lineKey(lineId)));
765
+ if (hasProjectedErrors(result)) addOwnedLineErrors(result, payload.lines.map(({ lineId }) => lineId), new Set(owned.map(({ lineId }) => lineId)), addError);
766
+ if (!result.cart) return state;
767
+ const serverLines = getLines(cartResponseFromStandardEvent(result.cart));
768
+ return owned.reduce((current, line) => settleLineQuantity(current, serverLines, line, requestRevalidation), state);
769
+ },
770
+ getSignalKeys: (_state, payload) => payload.lines.map(({ lineId }) => lineKey(lineId))
771
+ },
705
772
  set_discount_codes: {
706
773
  payload: {},
707
774
  transport: (payload, signal, updateCart) => updateCart({ discountCodes: payload.discountCodes }, { signal }),
@@ -844,8 +911,12 @@ function createPendingTransaction(store, type, definition, payload, promise, res
844
911
  requiresRevalidation,
845
912
  owner,
846
913
  promise,
847
- projectPayload: (current) => definition.projectPayload(current, payload),
848
- projectPromise: (current, result, addError) => definition.projectPromise(current, result, payload, addError, { mergeServerCart: !transaction.requiresRevalidation }),
914
+ projectPayload: (current, ownsKey) => definition.projectPayload(current, payload, ownsKey),
915
+ projectPromise: (current, result, addError, ownsKey, requestRevalidation) => definition.projectPromise(current, result, payload, addError, {
916
+ mergeServerCart: !transaction.requiresRevalidation,
917
+ ownsKey,
918
+ requestRevalidation
919
+ }),
849
920
  trimAfter: (successful) => trimPendingTransaction(store, type, definition, payload, promise, {
850
921
  id,
851
922
  sequence,
@@ -915,15 +986,16 @@ function fulfillTransaction(store, transactionId, result) {
915
986
  const transaction = store.transactions.find((candidate) => candidate.id === transactionId);
916
987
  if (!transaction || transaction.generation !== store.generation) return;
917
988
  store.observedPromises.delete(transaction.promise);
918
- if (!ownsSignalKeys(store, transaction)) {
989
+ const ownedKeys = getOwnedSignalKeys(store, transaction);
990
+ if (ownedKeys.length === 0 && transaction.signalKeys.length > 0) {
919
991
  removeTransaction(store, transactionId);
920
992
  publishVisibleState(store);
921
993
  revalidateCartWhenIdle(store);
922
994
  return;
923
995
  }
924
- store.settled = transaction.projectPromise(store.settled, result, (projector, keys) => addProjectedError(store, keys ?? transaction.errorKeys, projector));
996
+ store.settled = transaction.projectPromise(store.settled, result, (projector, keys) => addProjectedError(store, keys ?? transaction.errorKeys, projector), (key) => ownedKeys.includes(key), () => requestCartRevalidation(store));
925
997
  store.settled = addSnapshotIdentity(store.settled, result);
926
- if (result.cart && !transaction.requiresRevalidation && transaction.sequence >= store.lastSnapshotSequence) {
998
+ if (result.cart && ownedKeys.length === transaction.signalKeys.length && !transaction.requiresRevalidation && transaction.sequence >= store.lastSnapshotSequence) {
927
999
  store.settled = addSnapshotFields(store.settled, result);
928
1000
  store.lastSnapshotSequence = transaction.sequence;
929
1001
  }
@@ -943,13 +1015,14 @@ function rejectTransaction(store, transactionId, error) {
943
1015
  const transaction = store.transactions.find((candidate) => candidate.id === transactionId);
944
1016
  if (!transaction || transaction.generation !== store.generation) return;
945
1017
  store.observedPromises.delete(transaction.promise);
946
- const owned = ownsSignalKeys(store, transaction);
1018
+ const ownedKeys = getOwnedSignalKeys(store, transaction);
1019
+ const ownsAllKeys = ownedKeys.length === transaction.signalKeys.length;
947
1020
  removeTransaction(store, transactionId);
948
1021
  releaseSignalKeys(store, transaction);
949
- if (owned) {
1022
+ if (ownedKeys.length > 0 || ownsAllKeys) {
950
1023
  const failure = extractCartActionFailure(error);
951
- if (failure) transaction.projectPromise(store.settled, rejectedResult(failure), (projector, keys) => addProjectedError(store, keys ?? transaction.errorKeys, projector));
952
- if (!failure && !isAbortError(error)) addProjectedError(store, transaction.errorKeys, (state, timestampMs) => writeNetworkError(state, error, timestampMs));
1024
+ if (failure) transaction.projectPromise(store.settled, rejectedResult(failure), (projector, keys) => addProjectedError(store, keys ?? transaction.errorKeys, projector), (key) => ownedKeys.includes(key), NOOP);
1025
+ if (!failure && !isAbortError(error)) addProjectedError(store, ownsAllKeys ? transaction.errorKeys : ownedKeys, (state, timestampMs) => writeNetworkError(state, error, timestampMs));
953
1026
  }
954
1027
  publishVisibleState(store);
955
1028
  revalidateCartWhenIdle(store);
@@ -980,7 +1053,11 @@ function enqueueTransaction(store, type, payload, promise, reservedTransaction,
980
1053
  promise.then((result) => fulfillTransaction(store, transaction.id, result), (error) => rejectTransaction(store, transaction.id, error));
981
1054
  }
982
1055
  function markOverlappingTransactionForRevalidation(store, transaction) {
983
- if (store.transactions.length === 0 && !store.revalidation.active && store.activeMutationTransports.size === 0) return;
1056
+ if (store.transactions.length === 0 && !store.revalidation.active && store.activeMutationTransports.size === 0 && !store.activeCartLoad) return;
1057
+ if (store.activeCartLoad) store.settled = {
1058
+ ...store.settled,
1059
+ loading: false
1060
+ };
984
1061
  transaction.requiresRevalidation = true;
985
1062
  for (const pending of store.transactions) pending.requiresRevalidation = true;
986
1063
  requestCartRevalidation(store);
@@ -1173,12 +1250,16 @@ function handleLinesEvent(store, event) {
1173
1250
  enqueueTransaction(store, "add_to_cart", payloadFromAddEvent(event), event.promise, void 0, eventToken);
1174
1251
  return;
1175
1252
  }
1176
- if (event.lines.length !== 1) return;
1177
- const line = event.lines[0];
1178
- enqueueTransaction(store, "change_line_quantity", {
1253
+ const lines = event.lines.map((line) => ({
1179
1254
  lineId: line.id,
1180
1255
  quantity: line.quantity
1181
- }, event.promise, void 0, eventToken);
1256
+ }));
1257
+ if (lines.length === 0) return;
1258
+ if (lines.length === 1) {
1259
+ enqueueTransaction(store, "change_line_quantity", lines[0], event.promise, void 0, eventToken);
1260
+ return;
1261
+ }
1262
+ enqueueTransaction(store, "change_lines_quantity", { lines }, event.promise, void 0, eventToken);
1182
1263
  }
1183
1264
  function handleDiscountEvent(store, event) {
1184
1265
  enqueueTransaction(store, "set_discount_codes", { discountCodes: event.discountCodes.map((discount) => discount.code) }, event.promise, void 0, getTransactionEventToken(event.detail));
@@ -1201,6 +1282,10 @@ function connectCartStore(store, handlers) {
1201
1282
  document.addEventListener("shopify:cart:note-update", handlers.note);
1202
1283
  document.addEventListener("shopify:cart:attributes-update", handlers.attributes);
1203
1284
  getShopifyStandardActions().catch(() => {});
1285
+ if (store.revalidateOnConnect) {
1286
+ store.revalidateOnConnect = false;
1287
+ refreshCartInStore(store);
1288
+ }
1204
1289
  return true;
1205
1290
  }
1206
1291
  function cancelCartRevalidation(store) {
@@ -1218,7 +1303,6 @@ function clearPendingTransactions(store) {
1218
1303
  store.transactions = [];
1219
1304
  store.projectedErrors = [];
1220
1305
  store.observedPromises.clear();
1221
- store.activeMutationTransports.clear();
1222
1306
  store.expectedEvents = [];
1223
1307
  store.reservation = null;
1224
1308
  store.identityTransportGate.active = null;
@@ -1227,6 +1311,7 @@ function clearPendingTransactions(store) {
1227
1311
  cancelCartRevalidation(store);
1228
1312
  }
1229
1313
  function destroyCartStore(store, handlers) {
1314
+ store.revalidateOnConnect ||= store.revalidation.requested || store.revalidation.active !== null;
1230
1315
  clearPendingTransactions(store);
1231
1316
  if (typeof document !== "undefined" && store.cartSyncAttached) {
1232
1317
  document.removeEventListener("shopify:cart:lines-update", handlers.lines);
@@ -1323,6 +1408,7 @@ function resetCartStore(store) {
1323
1408
  const hadActiveRevalidation = store.revalidation.active !== null;
1324
1409
  const shouldReloadAfterReset = store.cartSyncAttached && !hadActiveRevalidation && (hadActiveCartLoad || hasLocalCartData(store.observable.state));
1325
1410
  clearPendingTransactions(store);
1411
+ store.activeMutationTransports.clear();
1326
1412
  invalidateActiveCartLoad(store);
1327
1413
  cancelCartRevalidation(store);
1328
1414
  store.lastSnapshotSequence = 0;
@@ -1435,6 +1521,7 @@ function createCartStore(options = {}) {
1435
1521
  waiting: []
1436
1522
  },
1437
1523
  cartSyncAttached: false,
1524
+ revalidateOnConnect: false,
1438
1525
  reservation: null,
1439
1526
  lastSnapshotSequence: 0
1440
1527
  };