cloudflare-next-intl 0.8.37 → 0.8.38
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.
|
@@ -225,6 +225,7 @@ export default async function updateSession(request, baseResponse, locale, rebui
|
|
|
225
225
|
if (rawPath.startsWith('/_next') || /\.[a-zA-Z0-9]+$/.test(lastSegment)) {
|
|
226
226
|
return baseResponse;
|
|
227
227
|
}
|
|
228
|
+
const isPrefetch = isPrefetchRequest(request);
|
|
228
229
|
const localePrefix = locale === config.defaultLocale ? '' : requestPrefix;
|
|
229
230
|
const localeUrl = (target) => new URL(withRedirectQuery(`${localePrefix}${target === '/' ? '' : target}` || '/', request.nextUrl.search), request.url);
|
|
230
231
|
// Emailed Firebase action links all arrive on the single project-wide
|
|
@@ -283,7 +284,7 @@ export default async function updateSession(request, baseResponse, locale, rebui
|
|
|
283
284
|
}
|
|
284
285
|
}
|
|
285
286
|
parsed.search = request.nextUrl.search;
|
|
286
|
-
return buildRedirect(baseResponse, parsed);
|
|
287
|
+
return buildRedirect(baseResponse, parsed, isPrefetch);
|
|
287
288
|
}
|
|
288
289
|
}
|
|
289
290
|
catch {
|
|
@@ -308,7 +309,7 @@ export default async function updateSession(request, baseResponse, locale, rebui
|
|
|
308
309
|
url.searchParams.delete(key);
|
|
309
310
|
}
|
|
310
311
|
}
|
|
311
|
-
return buildRedirect(baseResponse, url);
|
|
312
|
+
return buildRedirect(baseResponse, url, isPrefetch);
|
|
312
313
|
}
|
|
313
314
|
}
|
|
314
315
|
}
|
|
@@ -444,14 +445,14 @@ export default async function updateSession(request, baseResponse, locale, rebui
|
|
|
444
445
|
response = baseResponse;
|
|
445
446
|
}
|
|
446
447
|
else if (!hasSession || clearInvalidSession) {
|
|
447
|
-
response = isAuthPage ? baseResponse : buildRedirect(baseResponse, localeUrl(fa.redirectAuthPath));
|
|
448
|
+
response = isAuthPage ? baseResponse : buildRedirect(baseResponse, localeUrl(fa.redirectAuthPath), isPrefetch);
|
|
448
449
|
}
|
|
449
450
|
else if (unverifiedEmail) {
|
|
450
451
|
// Checked before the auth-page redirect: an unverified signed-in
|
|
451
452
|
// user must land on verifyEmailPath even if they navigated to an
|
|
452
453
|
// auth page like /login — homePath is not a state they're allowed
|
|
453
454
|
// to reach yet either.
|
|
454
|
-
response = buildRedirect(baseResponse, localeUrl(fa.verifyEmailPath));
|
|
455
|
+
response = buildRedirect(baseResponse, localeUrl(fa.verifyEmailPath), isPrefetch);
|
|
455
456
|
}
|
|
456
457
|
else if (isAuthPage || (isVerifyEmailPage && decodeJwtPayload(token)?.email_verified === true)) {
|
|
457
458
|
// A verified user has no reason to be on verifyEmailPath either —
|
|
@@ -468,7 +469,7 @@ export default async function updateSession(request, baseResponse, locale, rebui
|
|
|
468
469
|
// boolean `user.emailVerified`. The two disagreeing caused an
|
|
469
470
|
// infinite client<->server redirect loop on this exact page when a
|
|
470
471
|
// token's claim was merely absent rather than `false`.
|
|
471
|
-
response = buildRedirect(baseResponse, localeUrl(fa.homePath));
|
|
472
|
+
response = buildRedirect(baseResponse, localeUrl(fa.homePath), isPrefetch);
|
|
472
473
|
}
|
|
473
474
|
else {
|
|
474
475
|
response = baseResponse;
|
|
@@ -499,7 +500,27 @@ export default async function updateSession(request, baseResponse, locale, rebui
|
|
|
499
500
|
return response;
|
|
500
501
|
}
|
|
501
502
|
/** A redirect response can't carry forward `baseResponse`'s rewrite/next decision, so this copies its cookies/headers across instead of dropping them. */
|
|
502
|
-
|
|
503
|
+
// A router prefetch must never be answered with a redirect. Next's segment
|
|
504
|
+
// cache stores the entry under the REQUESTED url while `fetch` transparently
|
|
505
|
+
// follows the 3xx, so the entry it caches describes a different route than the
|
|
506
|
+
// key it is filed under; the router then keeps re-requesting it, which
|
|
507
|
+
// re-redirects, an unbounded prefetch loop that hammers the origin (every
|
|
508
|
+
// signed-out page carrying a `<Link>` to a guarded route reproduced it).
|
|
509
|
+
// An empty 204 is treated as an un-cacheable prefetch miss instead: the router
|
|
510
|
+
// backs off, and the guard still runs in full on the real navigation, which
|
|
511
|
+
// is never a prefetch.
|
|
512
|
+
function isPrefetchRequest(request) {
|
|
513
|
+
return request.headers.get('next-router-prefetch') === '1'
|
|
514
|
+
|| request.headers.get('purpose') === 'prefetch'
|
|
515
|
+
|| request.headers.get('x-purpose') === 'prefetch';
|
|
516
|
+
}
|
|
517
|
+
function buildRedirect(baseResponse, url, isPrefetch = false) {
|
|
518
|
+
if (isPrefetch) {
|
|
519
|
+
return new NextResponse(null, {
|
|
520
|
+
status: 204,
|
|
521
|
+
headers: { 'Cache-Control': 'private, no-cache, no-store, max-age=0, must-revalidate' },
|
|
522
|
+
});
|
|
523
|
+
}
|
|
503
524
|
const redirectResponse = NextResponse.redirect(url);
|
|
504
525
|
baseResponse.cookies.getAll().forEach((cookie) => redirectResponse.cookies.set(cookie));
|
|
505
526
|
baseResponse.headers.forEach((value, key) => redirectResponse.headers.set(key, value));
|