@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.
- package/dist/cli/index.mjs +6 -3
- package/dist/client/client.mjs +2 -4
- package/dist/client/client.mjs.map +1 -1
- package/dist/client/i18n-variables.d.mts +13 -0
- package/dist/client/i18n-variables.mjs +84 -0
- package/dist/client/i18n-variables.mjs.map +1 -0
- package/dist/client/types.d.mts +13 -6
- package/dist/core/cart/cart.mjs +110 -23
- package/dist/core/cart/cart.mjs.map +1 -1
- package/dist/core/request-context.mjs +1 -1
- package/dist/core/request-routing/interceptors/proxy.mjs.map +1 -1
- package/dist/core/request-routing/registered-routes.d.mts +5 -1
- package/dist/core/request-routing/registered-routes.mjs +21 -7
- package/dist/core/request-routing/registered-routes.mjs.map +1 -1
- package/dist/core/request-routing/route-types.d.mts +16 -6
- package/dist/core/shopify-scripts/perfkit.mjs +8 -2
- package/dist/core/shopify-scripts/perfkit.mjs.map +1 -1
- package/dist/core/utils/load-script.mjs +6 -6
- package/dist/core/utils/load-script.mjs.map +1 -1
- package/dist/customer-account/client.mjs +1 -1
- package/dist/customer-account/session.mjs +1 -1
- package/dist/development/client/client.mjs +2 -4
- package/dist/development/client/client.mjs.map +1 -1
- package/dist/development/client/i18n-variables.d.mts +13 -0
- package/dist/development/client/i18n-variables.mjs +84 -0
- package/dist/development/client/i18n-variables.mjs.map +1 -0
- package/dist/development/client/types.d.mts +13 -6
- package/dist/development/core/cart/cart.mjs +110 -23
- package/dist/development/core/cart/cart.mjs.map +1 -1
- package/dist/development/core/request-context.mjs +1 -1
- package/dist/development/core/request-routing/interceptors/proxy.mjs.map +1 -1
- package/dist/development/core/request-routing/registered-routes.d.mts +5 -1
- package/dist/development/core/request-routing/registered-routes.mjs +21 -7
- package/dist/development/core/request-routing/registered-routes.mjs.map +1 -1
- package/dist/development/core/request-routing/route-types.d.mts +16 -6
- package/dist/development/core/shopify-scripts/perfkit.mjs +8 -2
- package/dist/development/core/shopify-scripts/perfkit.mjs.map +1 -1
- package/dist/development/core/utils/load-script.mjs +6 -6
- package/dist/development/core/utils/load-script.mjs.map +1 -1
- package/dist/development/graphql/type-resolver.d.mts +27 -1
- package/dist/development/react/cart.mjs +79 -25
- package/dist/development/react/cart.mjs.map +1 -1
- package/dist/graphql/type-resolver.d.mts +27 -1
- package/dist/react/cart.mjs +79 -25
- package/dist/react/cart.mjs.map +1 -1
- package/dist/vite/index.d.mts +11 -1
- package/dist/vite/index.mjs +21 -1
- package/dist/vite/index.mjs.map +1 -0
- package/dist/vue/shopify-scripts.d.mts +1 -1
- package/package.json +1 -1
- package/skills/hydrogen-cart-ui/SKILL.md +2 -2
- package/skills/hydrogen-request-handlers/SKILL.md +2 -2
- package/skills/hydrogen-request-handlers/references/frameworks.md +2 -7
- package/skills/hydrogen-request-handlers/references/nuxt.md +1 -3
- package/skills/hydrogen-storefront-client/SKILL.md +4 -2
- package/skills/migrate-to-hydrogen-2026-10/SKILL.md +332 -0
- package/skills/migrate-to-hydrogen-2026-10/references/analytics.md +24 -0
- package/skills/migrate-to-hydrogen-2026-10/references/cart.md +9 -0
- package/skills/migrate-to-hydrogen-2026-10/references/customer-account.md +10 -0
- package/skills/migrate-to-hydrogen-2026-10/references/dependencies-and-tooling.md +10 -0
- package/skills/migrate-to-hydrogen-2026-10/references/optional-subsystems.md +25 -0
- package/skills/migrate-to-hydrogen-2026-10/references/routing-and-loaders.md +10 -0
- package/skills/migrate-to-hydrogen-2026-10/references/sitemap-and-seo.md +5 -0
- package/skills/migrate-to-hydrogen-2026-10/references/typescript-and-gql-tada.md +7 -0
- 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
|
|
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
|
|
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)
|
|
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(
|
|
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
|
|
292
|
-
if (!transaction.owner) return
|
|
293
|
-
return transaction.signalKeys.
|
|
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
|
-
|
|
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, {
|
|
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
|
-
|
|
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
|
|
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 (
|
|
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
|
-
|
|
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
|
-
}
|
|
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
|
};
|