Skip to content

Commit daadd91

Browse files
authored
Fix NextLink prefetch side effect on unsigned claims persistance (#4325)
1 parent f4469b2 commit daadd91

1 file changed

Lines changed: 33 additions & 3 deletions

File tree

  • packages/gitbook/src/components/primitives

packages/gitbook/src/components/primitives/Link.tsx

Lines changed: 33 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -157,9 +157,14 @@ export function Link(props: LinkProps) {
157157
);
158158
}
159159

160-
// Not sure why yet, but it seems necessary to force prefetch to true
161-
// default behavior doesn't seem to properly use the client router cache.
162-
const _prefetch = prefetch === null || prefetch === undefined ? true : prefetch;
160+
// Not sure why yet, but forcing prefetch to true seems necessary for the
161+
// client router cache to be used properly.
162+
//
163+
// However, we need to disable prefetch for links with query params that
164+
// can trigger server-side side effects, such as persisting visitor claims in a
165+
// cookie or starting the assistant. Automatic RSC prefetch requests can otherwise
166+
// trigger those effects without user intent.
167+
const _prefetch = hasSideEffectQueryParams(href) ? false : (prefetch ?? true);
163168

164169
return (
165170
<NextLink
@@ -175,6 +180,31 @@ export function Link(props: LinkProps) {
175180
);
176181
}
177182

183+
/**
184+
* Whether the given href carries query params that have a server-side side effect when fetched:
185+
* - `visitor.*` params persist unsigned visitor claims into the `gitbook-visitor-public` cookie.
186+
* - `ask` triggers the assistant & `q` the search.
187+
*
188+
* NextLink automatically prefetches links (RSC requests) on hover/viewport, which would fire those
189+
* side effects without any user intent, so such links should not be prefetched.
190+
*/
191+
function hasSideEffectQueryParams(href: string): boolean {
192+
const baseURL = typeof window !== 'undefined' ? window.location.origin : 'http://localhost';
193+
const linkURL = URL.canParse(href) ? new URL(href) : new URL(href, baseURL);
194+
195+
if (linkURL.searchParams.get('ask') !== null || linkURL.searchParams.get('q') !== null) {
196+
return true;
197+
}
198+
199+
for (const key of linkURL.searchParams.keys()) {
200+
if (key.startsWith('visitor.')) {
201+
return true;
202+
}
203+
}
204+
205+
return false;
206+
}
207+
178208
/**
179209
* A box used to contain a link overlay.
180210
* It is used to create a clickable area that can contain other elements.

0 commit comments

Comments
 (0)