Commit 6cf6ae33444 for woocommerce
commit 6cf6ae33444463b0bacde771430362da7df1c444
Author: Anand Rajaram <anandrajaram21@gmail.com>
Date: Mon Aug 31 18:31:17 2026 -0700
Fix Mini-Cart mutations aborted during navigation (#68165)
* Enable keepalive for batched mutations
* Add changefile(s) from automation for the following project(s): woocommerce
* Limit keepalive to supported mutation payload sizes
---------
Co-authored-by: woocommercebot <woocommercebot@users.noreply.github.com>
Co-authored-by: Tung Du <dinhtungdu@gmail.com>
diff --git a/plugins/woocommerce/changelog/68165-65956-mini-cart b/plugins/woocommerce/changelog/68165-65956-mini-cart
new file mode 100644
index 00000000000..76fcfcd1267
--- /dev/null
+++ b/plugins/woocommerce/changelog/68165-65956-mini-cart
@@ -0,0 +1,4 @@
+Significance: patch
+Type: fix
+
+Prevent Mini-Cart item removals from being cancelled when navigating away.
diff --git a/plugins/woocommerce/client/blocks/assets/js/base/stores/woocommerce/mutation-batcher.ts b/plugins/woocommerce/client/blocks/assets/js/base/stores/woocommerce/mutation-batcher.ts
index 5195ee0a8c3..36ab31f8bcc 100644
--- a/plugins/woocommerce/client/blocks/assets/js/base/stores/woocommerce/mutation-batcher.ts
+++ b/plugins/woocommerce/client/blocks/assets/js/base/stores/woocommerce/mutation-batcher.ts
@@ -79,6 +79,8 @@ type TrackedRequest< TMeta = unknown > = {
reject: ( error: Error ) => void;
};
+const KEEPALIVE_PAYLOAD_LIMIT = 64 * 1024;
+
export function createMutationQueue< TState, TMeta = unknown >(
config: MutationQueueConfig< TState, TMeta >
) {
@@ -256,14 +258,18 @@ export function createMutationQueue< TState, TMeta = unknown >(
};
} )
.filter( Boolean );
+ const body = JSON.stringify( { requests } );
const response = await fetchHandler( endpoint, {
method: 'POST',
+ keepalive:
+ new TextEncoder().encode( body ).byteLength <
+ KEEPALIVE_PAYLOAD_LIMIT,
headers: {
'Content-Type': 'application/json',
...requestHeaders,
},
- body: JSON.stringify( { requests } ),
+ body,
} );
if ( ! response.ok ) {
diff --git a/plugins/woocommerce/client/blocks/assets/js/base/stores/woocommerce/test/mutation-batcher.test.ts b/plugins/woocommerce/client/blocks/assets/js/base/stores/woocommerce/test/mutation-batcher.test.ts
index a7c3efbc10e..01b78f713a1 100644
--- a/plugins/woocommerce/client/blocks/assets/js/base/stores/woocommerce/test/mutation-batcher.test.ts
+++ b/plugins/woocommerce/client/blocks/assets/js/base/stores/woocommerce/test/mutation-batcher.test.ts
@@ -103,6 +103,7 @@ describe( 'createMutationQueue', () => {
// Should have made exactly ONE fetch call with all 3 requests
expect( mockFetch ).toHaveBeenCalledTimes( 1 );
+ expect( mockFetch.mock.calls[ 0 ][ 1 ].keepalive ).toBe( true );
const requestBody = JSON.parse(
mockFetch.mock.calls[ 0 ][ 1 ].body
);
@@ -112,6 +113,28 @@ describe( 'createMutationQueue', () => {
).toEqual( [ '/a', '/b', '/c' ] );
} );
+ it( 'disables keepalive for oversized payloads', async () => {
+ const mockFetch = createMockFetch( [
+ { status: 200, body: { value: 10 } },
+ ] );
+ global.fetch = mockFetch;
+
+ const queue = createMutationQueue( {
+ endpoint: '/batch',
+ getHeaders: () => ( {} ),
+ ...stateHandler,
+ } );
+
+ await queue.submit( {
+ path: '/a',
+ method: 'POST',
+ body: { description: '🚀'.repeat( 17_000 ) },
+ } );
+
+ expect( mockFetch ).toHaveBeenCalledTimes( 1 );
+ expect( mockFetch.mock.calls[ 0 ][ 1 ].keepalive ).toBe( false );
+ } );
+
it( 'takes snapshot once at start of cycle, not per request', async () => {
const mockFetch = createMockFetch( [
{ status: 200, body: { value: 100 } },