cloudflare-next-intl 0.6.27 → 0.6.29
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.
|
@@ -26,6 +26,12 @@ import { type ReportErrorConfig } from './report_error';
|
|
|
26
26
|
* boundary (Next.js server actions serialize arguments; an `Error` instance
|
|
27
27
|
* doesn't survive that intact) and `isClient: true` is set automatically.
|
|
28
28
|
*
|
|
29
|
+
* Also attaches `requestContext: { path, userAgent, referer }` (best-effort,
|
|
30
|
+
* via `next/headers` — see `resolveRequestContext`) alongside your own
|
|
31
|
+
* `params`, so `onError`/the console report shows WHERE the error happened,
|
|
32
|
+
* not just what it was — useful when diagnosing a client error without a
|
|
33
|
+
* repro, since the page and browser are often the missing piece.
|
|
34
|
+
*
|
|
29
35
|
* @param config Pass the relevant slices of your `RoutingConfig` directly —
|
|
30
36
|
* `{ errorHandling: config.errorHandling, generate: config.generate }`.
|
|
31
37
|
*/
|
|
@@ -1,5 +1,26 @@
|
|
|
1
1
|
import reportError from './report_error';
|
|
2
2
|
import stringifyUnknown from './stringify_unknown';
|
|
3
|
+
/**
|
|
4
|
+
* Reads request context (page path, user agent, referer) via `next/headers`
|
|
5
|
+
* for a client-originated error report. `path` comes from `x-pathname`, set
|
|
6
|
+
* by `intlMiddleware` (this package's own middleware) — falls back to
|
|
7
|
+
* `undefined` when a header is missing (e.g. middleware didn't run for this
|
|
8
|
+
* request) rather than throwing.
|
|
9
|
+
*/
|
|
10
|
+
async function resolveRequestContext() {
|
|
11
|
+
try {
|
|
12
|
+
const { headers } = await import('next/headers');
|
|
13
|
+
const headerList = await headers();
|
|
14
|
+
return {
|
|
15
|
+
path: headerList.get('x-pathname') ?? undefined,
|
|
16
|
+
userAgent: headerList.get('user-agent') ?? undefined,
|
|
17
|
+
referer: headerList.get('referer') ?? undefined,
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
catch {
|
|
21
|
+
return {};
|
|
22
|
+
}
|
|
23
|
+
}
|
|
3
24
|
/**
|
|
4
25
|
* Builds a function that reports a client-originated error via
|
|
5
26
|
* `reportError`, meant to be re-exported directly from your OWN
|
|
@@ -26,15 +47,28 @@ import stringifyUnknown from './stringify_unknown';
|
|
|
26
47
|
* boundary (Next.js server actions serialize arguments; an `Error` instance
|
|
27
48
|
* doesn't survive that intact) and `isClient: true` is set automatically.
|
|
28
49
|
*
|
|
50
|
+
* Also attaches `requestContext: { path, userAgent, referer }` (best-effort,
|
|
51
|
+
* via `next/headers` — see `resolveRequestContext`) alongside your own
|
|
52
|
+
* `params`, so `onError`/the console report shows WHERE the error happened,
|
|
53
|
+
* not just what it was — useful when diagnosing a client error without a
|
|
54
|
+
* repro, since the page and browser are often the missing piece.
|
|
55
|
+
*
|
|
29
56
|
* @param config Pass the relevant slices of your `RoutingConfig` directly —
|
|
30
57
|
* `{ errorHandling: config.errorHandling, generate: config.generate }`.
|
|
31
58
|
*/
|
|
32
59
|
export default function createServerErrorAction(config) {
|
|
33
60
|
return async function reportClientError(error, classOrMethodName, params) {
|
|
61
|
+
const requestContext = await resolveRequestContext();
|
|
62
|
+
const isPlainParamsObject = typeof params === 'object' && params !== null && !Array.isArray(params);
|
|
63
|
+
const mergedParams = params === undefined
|
|
64
|
+
? { requestContext }
|
|
65
|
+
: isPlainParamsObject
|
|
66
|
+
? { ...params, requestContext }
|
|
67
|
+
: { params, requestContext };
|
|
34
68
|
await reportError(config, {
|
|
35
69
|
error: stringifyUnknown(error, true),
|
|
36
70
|
classOrMethodName,
|
|
37
|
-
params,
|
|
71
|
+
params: mergedParams,
|
|
38
72
|
isClient: true,
|
|
39
73
|
});
|
|
40
74
|
};
|
|
@@ -1,7 +1,22 @@
|
|
|
1
1
|
import config from '@intl-config';
|
|
2
2
|
import reportError from '../../error_handling/report_error';
|
|
3
|
-
|
|
4
|
-
|
|
3
|
+
// Matches `firebase-admin`'s own `AppCheckTokenGenerator.createCustomToken`
|
|
4
|
+
// exactly (`token-generator.js`) — this specific audience (the App Check
|
|
5
|
+
// TOKEN EXCHANGE service, not the App Check API resource name itself) is
|
|
6
|
+
// REQUIRED. The wrong-but-plausible-looking
|
|
7
|
+
// `.../google.firebase.appcheck.v1.FirebaseAppCheck` audience gets rejected
|
|
8
|
+
// by `exchangeCustomToken` with an opaque `403 App attestation failed`, with
|
|
9
|
+
// no indication the audience is the problem.
|
|
10
|
+
const APP_CHECK_CUSTOM_TOKEN_AUDIENCE = 'https://firebaseappcheck.googleapis.com/google.firebase.appcheck.v1.TokenExchangeService';
|
|
11
|
+
// `firebase-admin` hardcodes this custom token's own lifetime to 5 minutes
|
|
12
|
+
// and does not expose it as configurable — it's a short-lived credential
|
|
13
|
+
// whose only job is to be immediately exchanged for the actual App Check
|
|
14
|
+
// token (whose own lifetime is controlled by Firebase, separately). Mirrored
|
|
15
|
+
// here as a fixed value rather than the configurable
|
|
16
|
+
// `customTokenLifetime` this used to be: Google's real backend rejects
|
|
17
|
+
// longer lifetimes outright, so making it configurable only offered a
|
|
18
|
+
// footgun with no working range above this default.
|
|
19
|
+
const CUSTOM_TOKEN_LIFETIME = '5m';
|
|
5
20
|
/**
|
|
6
21
|
* Mints a fresh App Check token server-side via a service account, for use
|
|
7
22
|
* when the client-written App Check cookie (see `appCheckTokenCookieName`)
|
|
@@ -34,7 +49,7 @@ export default async function mintServerAppCheckToken(projectId, apiKey, appChec
|
|
|
34
49
|
.setSubject(appCheck.clientEmail)
|
|
35
50
|
.setAudience(APP_CHECK_CUSTOM_TOKEN_AUDIENCE)
|
|
36
51
|
.setIssuedAt()
|
|
37
|
-
.setExpirationTime(
|
|
52
|
+
.setExpirationTime(CUSTOM_TOKEN_LIFETIME)
|
|
38
53
|
.sign(privateKey);
|
|
39
54
|
const url = `https://firebaseappcheck.googleapis.com/v1/projects/${projectId}/apps/${appCheck.appId}:exchangeCustomToken?key=${apiKey}`;
|
|
40
55
|
const res = await fetch(url, {
|
|
@@ -596,17 +596,6 @@ export interface FirebaseAppCheckConfig {
|
|
|
596
596
|
* itself — App Check registers apps separately.
|
|
597
597
|
*/
|
|
598
598
|
appId: string;
|
|
599
|
-
/**
|
|
600
|
-
* Lifetime of the custom JWT signed for the `exchangeCustomToken`
|
|
601
|
-
* server-side mint, as a `jose` `setExpirationTime` duration string
|
|
602
|
-
* (e.g. `'1h'`, `'30m'`, `'7d'`). Defaults to `'1h'`. This is the custom
|
|
603
|
-
* token's own lifetime, not the resulting App Check token's — Firebase
|
|
604
|
-
* controls that separately. Google's custom-token minting generally
|
|
605
|
-
* rejects lifetimes beyond 1 hour regardless of what's set here, so
|
|
606
|
-
* values longer than `'1h'` are unlikely to have any practical effect —
|
|
607
|
-
* kept configurable in case that constraint changes.
|
|
608
|
-
*/
|
|
609
|
-
customTokenLifetime?: string;
|
|
610
599
|
}
|
|
611
600
|
export interface CookieAttributes {
|
|
612
601
|
/**
|