cloudflare-next-intl 0.8.31 → 0.8.33
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/src/firebase_auth/client/components/auto_firebase_performance_events.d.ts +3 -4
- package/dist/src/firebase_auth/client/components/auto_firebase_performance_events.js +3 -71
- package/dist/src/firebase_auth/client/firebase_client.js +1 -1
- package/dist/src/firebase_auth/middleware/update_session.js +17 -1
- package/dist/src/types/types.d.ts +12 -2
- package/package.json +1 -1
|
@@ -2,9 +2,8 @@
|
|
|
2
2
|
* Auto-rendered alongside `FirebaseAuthClientProvider` when
|
|
3
3
|
* `firebaseAuth.performance` isn't `false` — records Firebase Performance
|
|
4
4
|
* custom traces for Web Vitals metrics (`web_cls`, `web_fcp`, `web_fid`,
|
|
5
|
-
* `web_lcp`, `web_ttfb`, `web_inp`)
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
* never resolves an instance).
|
|
5
|
+
* `web_lcp`, `web_ttfb`, `web_inp`), alongside Firebase Performance's own
|
|
6
|
+
* automatic page-load/network traces. No-ops if `firebaseAuth.performance`
|
|
7
|
+
* is disabled (`getFirebasePerformanceSync` never resolves an instance).
|
|
9
8
|
*/
|
|
10
9
|
export default function AutoFirebasePerformanceEvents(): null;
|
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
'use client';
|
|
2
|
-
import { usePathname } from 'next/navigation';
|
|
3
2
|
import { useReportWebVitals } from 'next/web-vitals';
|
|
4
|
-
import { useEffect, useRef } from 'react';
|
|
5
3
|
import reportError from '../../../error_handling/report_error';
|
|
6
4
|
import { getFirebaseAuthClient, getFirebasePerformanceSync } from '../firebase_client';
|
|
7
5
|
/**
|
|
@@ -28,84 +26,18 @@ async function recordFirebaseTrace(name, durationMs, attributes, metrics) {
|
|
|
28
26
|
});
|
|
29
27
|
}
|
|
30
28
|
}
|
|
31
|
-
const SLOW_RESOURCE_THRESHOLD_MS = 1000;
|
|
32
29
|
/**
|
|
33
30
|
* Auto-rendered alongside `FirebaseAuthClientProvider` when
|
|
34
31
|
* `firebaseAuth.performance` isn't `false` — records Firebase Performance
|
|
35
32
|
* custom traces for Web Vitals metrics (`web_cls`, `web_fcp`, `web_fid`,
|
|
36
|
-
* `web_lcp`, `web_ttfb`, `web_inp`)
|
|
37
|
-
*
|
|
38
|
-
*
|
|
39
|
-
* never resolves an instance).
|
|
33
|
+
* `web_lcp`, `web_ttfb`, `web_inp`), alongside Firebase Performance's own
|
|
34
|
+
* automatic page-load/network traces. No-ops if `firebaseAuth.performance`
|
|
35
|
+
* is disabled (`getFirebasePerformanceSync` never resolves an instance).
|
|
40
36
|
*/
|
|
41
37
|
export default function AutoFirebasePerformanceEvents() {
|
|
42
38
|
useReportWebVitals((metric) => {
|
|
43
39
|
const value = Math.round(metric.name === 'CLS' ? metric.value * 1000 : metric.value);
|
|
44
40
|
void recordFirebaseTrace(`web_${metric.name.toLowerCase()}`, value, { rating: metric.rating });
|
|
45
41
|
});
|
|
46
|
-
const path = usePathname();
|
|
47
|
-
const isFirstRoute = useRef(true);
|
|
48
|
-
const lastRouteChangeRef = useRef(Date.now());
|
|
49
|
-
const longTaskStats = useRef({ count: 0, totalDuration: 0 });
|
|
50
|
-
// Mount-only: registers a PerformanceObserver for long tasks (main-thread
|
|
51
|
-
// blocks >= 50ms). Not all browsers support the 'longtask' entry type, so
|
|
52
|
-
// this is a best-effort signal. The final route's tail of accumulated
|
|
53
|
-
// long tasks between the last route change and unmount is intentionally
|
|
54
|
-
// dropped rather than flushed here — this is a monitoring signal, not a
|
|
55
|
-
// billing metric, and an occasional dropped tail sample is acceptable.
|
|
56
|
-
useEffect(() => {
|
|
57
|
-
if (typeof PerformanceObserver === 'undefined' || !PerformanceObserver.supportedEntryTypes?.includes('longtask'))
|
|
58
|
-
return;
|
|
59
|
-
const observer = new PerformanceObserver((list) => {
|
|
60
|
-
for (const entry of list.getEntries()) {
|
|
61
|
-
longTaskStats.current.count += 1;
|
|
62
|
-
longTaskStats.current.totalDuration += entry.duration;
|
|
63
|
-
}
|
|
64
|
-
});
|
|
65
|
-
observer.observe({ type: 'longtask', buffered: true });
|
|
66
|
-
return () => observer.disconnect();
|
|
67
|
-
}, []);
|
|
68
|
-
// Mount-only: registers a PerformanceObserver for slow non-fetch/XHR
|
|
69
|
-
// resources (scripts, images, stylesheets, fonts, etc). Firebase
|
|
70
|
-
// Performance's own automatic network monitoring already instruments
|
|
71
|
-
// fetch/XHR, so those are skipped here to avoid duplicate signals.
|
|
72
|
-
// `buffered: true` also catches resources that loaded before this
|
|
73
|
-
// component mounted (it only mounts after `AuthUserProvider`).
|
|
74
|
-
useEffect(() => {
|
|
75
|
-
if (typeof PerformanceObserver === 'undefined' || !PerformanceObserver.supportedEntryTypes?.includes('resource'))
|
|
76
|
-
return;
|
|
77
|
-
const observer = new PerformanceObserver((list) => {
|
|
78
|
-
for (const entry of list.getEntries()) {
|
|
79
|
-
if (entry.initiatorType === 'fetch' || entry.initiatorType === 'xmlhttprequest')
|
|
80
|
-
continue;
|
|
81
|
-
if (entry.duration < SLOW_RESOURCE_THRESHOLD_MS)
|
|
82
|
-
continue;
|
|
83
|
-
void recordFirebaseTrace('slow_resource', entry.duration, {
|
|
84
|
-
initiator_type: entry.initiatorType,
|
|
85
|
-
resource: entry.name.slice(-100),
|
|
86
|
-
}, { transfer_size_bytes: Math.round(entry.transferSize ?? 0) });
|
|
87
|
-
}
|
|
88
|
-
});
|
|
89
|
-
observer.observe({ type: 'resource', buffered: true });
|
|
90
|
-
return () => observer.disconnect();
|
|
91
|
-
}, []);
|
|
92
|
-
useEffect(() => {
|
|
93
|
-
const now = Date.now();
|
|
94
|
-
const duration = now - lastRouteChangeRef.current;
|
|
95
|
-
lastRouteChangeRef.current = now;
|
|
96
|
-
if (isFirstRoute.current) {
|
|
97
|
-
isFirstRoute.current = false;
|
|
98
|
-
return;
|
|
99
|
-
}
|
|
100
|
-
// Approximates navigation duration as time between path-change commits —
|
|
101
|
-
// App Router exposes no public "navigation start" event this package
|
|
102
|
-
// can hook into, so this is not a precise navigation timing.
|
|
103
|
-
void recordFirebaseTrace('route_change', duration, { path: path.slice(-100) });
|
|
104
|
-
const { count, totalDuration } = longTaskStats.current;
|
|
105
|
-
longTaskStats.current = { count: 0, totalDuration: 0 };
|
|
106
|
-
if (count > 0) {
|
|
107
|
-
void recordFirebaseTrace('route_long_tasks', totalDuration, { path: path.slice(-100) }, { long_task_count: count });
|
|
108
|
-
}
|
|
109
|
-
}, [path]);
|
|
110
42
|
return null;
|
|
111
43
|
}
|
|
@@ -62,7 +62,7 @@ export async function getFirebaseAuthClient() {
|
|
|
62
62
|
measurementId: fa.measurementId,
|
|
63
63
|
};
|
|
64
64
|
const app = getApps().length ? getApp() : initializeApp(firebaseConfig);
|
|
65
|
-
if (fa.appCheck) {
|
|
65
|
+
if (fa.appCheck && typeof window !== 'undefined') {
|
|
66
66
|
cachedAppCheck = await initializeFirebaseAppCheck(app, fa.appCheck);
|
|
67
67
|
}
|
|
68
68
|
if (perfModule) {
|
|
@@ -160,6 +160,7 @@ export async function refreshIdToken(apiKey, refreshToken, options) {
|
|
|
160
160
|
method: 'POST',
|
|
161
161
|
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
|
|
162
162
|
body: `grant_type=refresh_token&refresh_token=${encodeURIComponent(refreshToken)}`,
|
|
163
|
+
cache: 'no-store',
|
|
163
164
|
});
|
|
164
165
|
if (!res.ok) {
|
|
165
166
|
if (res.status === 400) {
|
|
@@ -243,7 +244,11 @@ export default async function updateSession(request, baseResponse, locale, rebui
|
|
|
243
244
|
const mode = request.nextUrl.searchParams.get('mode');
|
|
244
245
|
if (mode) {
|
|
245
246
|
let target = resolveActionModePaths(fa)[mode];
|
|
246
|
-
|
|
247
|
+
// Already on the page this mode routes to: the link has arrived.
|
|
248
|
+
// Following `continueUrl` from here would send it back to the
|
|
249
|
+
// action URL, which forwards to this same target again — an
|
|
250
|
+
// endless 307 ping-pong.
|
|
251
|
+
if (fa.followSameOriginContinueUrl !== false && target !== path) {
|
|
247
252
|
const continueUrl = request.nextUrl.searchParams.get('continueUrl');
|
|
248
253
|
if (continueUrl) {
|
|
249
254
|
try {
|
|
@@ -291,6 +296,15 @@ export default async function updateSession(request, baseResponse, locale, rebui
|
|
|
291
296
|
if (target && target !== path) {
|
|
292
297
|
const url = localeUrl(target);
|
|
293
298
|
url.search = request.nextUrl.search;
|
|
299
|
+
// Staying on this origin: only `oobCode` (plus anything the app
|
|
300
|
+
// put there itself) is still needed. Dropping Firebase's own
|
|
301
|
+
// routing params keeps the landed URL clean and makes a second
|
|
302
|
+
// forwarding pass impossible.
|
|
303
|
+
if (fa.stripActionLinkQuery !== false) {
|
|
304
|
+
for (const key of ['mode', 'apiKey', 'lang', 'continueUrl']) {
|
|
305
|
+
url.searchParams.delete(key);
|
|
306
|
+
}
|
|
307
|
+
}
|
|
294
308
|
return buildRedirect(baseResponse, url);
|
|
295
309
|
}
|
|
296
310
|
}
|
|
@@ -486,5 +500,7 @@ function buildRedirect(baseResponse, url) {
|
|
|
486
500
|
const redirectResponse = NextResponse.redirect(url);
|
|
487
501
|
baseResponse.cookies.getAll().forEach((cookie) => redirectResponse.cookies.set(cookie));
|
|
488
502
|
baseResponse.headers.forEach((value, key) => redirectResponse.headers.set(key, value));
|
|
503
|
+
// Explicitly prevent OpenNext/Cloudflare from caching these auth redirects.
|
|
504
|
+
redirectResponse.headers.set('Cache-Control', 'private, no-cache, no-store, max-age=0, must-revalidate');
|
|
489
505
|
return redirectResponse;
|
|
490
506
|
}
|
|
@@ -546,14 +546,24 @@ export interface FirebaseAuthRoutingConfig {
|
|
|
546
546
|
* auto-corrects a missing leading slash with a warning.
|
|
547
547
|
*/
|
|
548
548
|
actionLinkPath?: string;
|
|
549
|
+
/**
|
|
550
|
+
* Whether a same-origin emailed-action-link forward strips Firebase's own
|
|
551
|
+
* `mode`/`apiKey`/`lang`/`continueUrl` params, landing the user on a clean
|
|
552
|
+
* `?oobCode=` URL. Defaults to `true`. Set `false` to keep the full query
|
|
553
|
+
* when the destination page reads those params itself. Cross-origin
|
|
554
|
+
* redirects always keep the full query.
|
|
555
|
+
*/
|
|
556
|
+
stripActionLinkQuery?: boolean;
|
|
549
557
|
/**
|
|
550
558
|
* Whether the middleware's own redirects (`redirectAuthPath`, `homePath`,
|
|
551
559
|
* `verifyEmailPath`) carry over the original request's query string —
|
|
552
560
|
* e.g. `/login?ref=abc` stays `/login?ref=abc` after redirecting to
|
|
553
561
|
* `homePath` for a signed-in user, instead of dropping to `/`. Defaults
|
|
554
562
|
* to `true`. The emailed-action-link forward (see
|
|
555
|
-
* {@link resetPasswordPath}) always preserves
|
|
556
|
-
*
|
|
563
|
+
* {@link resetPasswordPath}) always preserves `oobCode` regardless of
|
|
564
|
+
* this setting, since it must survive that hop; when the forward stays on
|
|
565
|
+
* this origin it drops Firebase's own `mode`/`apiKey`/`lang`/`continueUrl`
|
|
566
|
+
* params, which the destination page no longer needs.
|
|
557
567
|
*/
|
|
558
568
|
preserveRedirectQuery?: boolean;
|
|
559
569
|
/** Returns true if the given (locale-stripped) path is an auth page (login/signup/etc). */
|