cloudflare-next-intl 0.6.0 → 0.6.2
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/error_handling/install_console_error_override.d.ts +4 -5
- package/dist/src/error_handling/install_console_error_override.js +4 -10
- package/dist/src/error_handling/report_error.d.ts +11 -9
- package/dist/src/error_handling/report_error.js +38 -9
- package/dist/src/types/types.d.ts +29 -0
- package/package.json +1 -1
|
@@ -8,11 +8,10 @@ import { type ReportErrorConfig } from './report_error';
|
|
|
8
8
|
* own `console`). Only takes effect when `config.errorHandling.overrideConsoleError`
|
|
9
9
|
* is `true`.
|
|
10
10
|
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
* normally, it just stops being reported.
|
|
11
|
+
* A component stuck in a render-error loop calls `console.error` on every
|
|
12
|
+
* render — `reportError`'s own dedup/cap (on by default, see
|
|
13
|
+
* `errorHandling.dedup`/`maxReports`) is what stops that from reporting
|
|
14
|
+
* unboundedly; this function does not duplicate that cap itself.
|
|
16
15
|
*
|
|
17
16
|
* `config.errorHandling.ignoreConsoleErrors` (default
|
|
18
17
|
* `defaultIgnoredConsoleErrors` — this package's own Firebase Auth error
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import reportError from './report_error';
|
|
2
2
|
import stringifyUnknown from './stringify_unknown';
|
|
3
3
|
import { defaultIgnoredConsoleErrors } from './default_ignored_console_errors';
|
|
4
|
-
const MAX_REPORTS_PER_INSTALL = 20;
|
|
5
4
|
/**
|
|
6
5
|
* Replaces the global `console.error` so every `console.error(...)` call is
|
|
7
6
|
* also routed through `config.errorHandling.onError`/`reportError` — the
|
|
@@ -11,11 +10,10 @@ const MAX_REPORTS_PER_INSTALL = 20;
|
|
|
11
10
|
* own `console`). Only takes effect when `config.errorHandling.overrideConsoleError`
|
|
12
11
|
* is `true`.
|
|
13
12
|
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
* normally, it just stops being reported.
|
|
13
|
+
* A component stuck in a render-error loop calls `console.error` on every
|
|
14
|
+
* render — `reportError`'s own dedup/cap (on by default, see
|
|
15
|
+
* `errorHandling.dedup`/`maxReports`) is what stops that from reporting
|
|
16
|
+
* unboundedly; this function does not duplicate that cap itself.
|
|
19
17
|
*
|
|
20
18
|
* `config.errorHandling.ignoreConsoleErrors` (default
|
|
21
19
|
* `defaultIgnoredConsoleErrors` — this package's own Firebase Auth error
|
|
@@ -36,18 +34,14 @@ export default function installConsoleErrorOverride(config, isClient) {
|
|
|
36
34
|
if (console.error.__isErrorHandlingOverride)
|
|
37
35
|
return;
|
|
38
36
|
const originalConsoleError = console.error.bind(console);
|
|
39
|
-
let reportCount = 0;
|
|
40
37
|
const override = (message, ...optionalParams) => {
|
|
41
38
|
originalConsoleError(message, ...optionalParams);
|
|
42
|
-
if (reportCount >= MAX_REPORTS_PER_INSTALL)
|
|
43
|
-
return;
|
|
44
39
|
const stringified = stringifyUnknown(message, isClient);
|
|
45
40
|
const ignoreList = config.errorHandling?.ignoreConsoleErrors ?? defaultIgnoredConsoleErrors;
|
|
46
41
|
if (ignoreList.some((ignored) => stringified.includes(ignored)))
|
|
47
42
|
return;
|
|
48
43
|
if (config.errorHandling?.ignoreConsoleError?.(stringified))
|
|
49
44
|
return;
|
|
50
|
-
reportCount++;
|
|
51
45
|
void reportError(config, { error: message, classOrMethodName: 'Global Console Error Handler', params: optionalParams, isClient });
|
|
52
46
|
};
|
|
53
47
|
override.__isErrorHandlingOverride = true;
|
|
@@ -6,10 +6,11 @@ export interface ReportErrorConfig {
|
|
|
6
6
|
/**
|
|
7
7
|
* Reports `params` via `config.errorHandling.onError` (default
|
|
8
8
|
* `console.error(params.formattedMessage)`), unless
|
|
9
|
-
* `config.errorHandling.enable === false
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
9
|
+
* `config.errorHandling.enable === false`, `params.consent` is set and not
|
|
10
|
+
* `true` (reporting to a third party without cookie consent can itself be
|
|
11
|
+
* GDPR-relevant), or dedup throttles it (on by default — see
|
|
12
|
+
* `errorHandling.dedup`/`throttleMs`/`resetDedup`). Never throws — a broken
|
|
13
|
+
* reporter must not mask the original error.
|
|
13
14
|
*
|
|
14
15
|
* Always overwrites `params.formattedMessage` with a fresh
|
|
15
16
|
* `formatErrorMessage(params)` before reporting — a human-readable one-line
|
|
@@ -17,11 +18,6 @@ export interface ReportErrorConfig {
|
|
|
17
18
|
* instead of the raw `error`/`params` object, for a default reporter (or a
|
|
18
19
|
* simple `onError`) to print directly.
|
|
19
20
|
*
|
|
20
|
-
* No built-in dedup/throttling: this package has no per-request context to
|
|
21
|
-
* safely scope such state to (module-scope state would leak across
|
|
22
|
-
* concurrent requests in a long-lived server process). Do dedup/throttling
|
|
23
|
-
* in your own `onError` if you need it, scoped to your own request context.
|
|
24
|
-
*
|
|
25
21
|
* When `config.generate?.getCloudflareContext` is set, `waitUntil` is called
|
|
26
22
|
* SYNCHRONOUSLY, in the same tick, with the `callOnError(...)` promise —
|
|
27
23
|
* Cloudflare Workers only extends the request's lifetime for work already
|
|
@@ -32,6 +28,12 @@ export interface ReportErrorConfig {
|
|
|
32
28
|
* `onError` directly when `getCloudflareContext`/`ctx.waitUntil` is unset or
|
|
33
29
|
* unavailable (e.g. outside a Cloudflare Worker).
|
|
34
30
|
*
|
|
31
|
+
* Passing `params.error` as `null`/`undefined` with `errorHandling.resetDedup: true`
|
|
32
|
+
* and nothing else is a valid "reset-only" call: the dedup state clears and
|
|
33
|
+
* `reportError` returns immediately, without calling `onError` — useful to
|
|
34
|
+
* clear dedup state once at the very start of a request/cron tick, before
|
|
35
|
+
* any handler that might call `reportError` for a real error runs.
|
|
36
|
+
*
|
|
35
37
|
* @param config Pass the relevant slices of your `RoutingConfig` directly —
|
|
36
38
|
* `{ errorHandling: config.errorHandling, generate: config.generate }`.
|
|
37
39
|
*/
|
|
@@ -1,4 +1,16 @@
|
|
|
1
1
|
import formatErrorMessage from './format_error_message';
|
|
2
|
+
import stringifyUnknown from './stringify_unknown';
|
|
3
|
+
const DEFAULT_THROTTLE_MS = 5000;
|
|
4
|
+
// Module-scope dedup state — safe by default only because a fresh JS realm
|
|
5
|
+
// (isolate/Worker instance) starts with it cleared. In a long-lived server
|
|
6
|
+
// process reused across many requests, pass `resetDedup: true` on the first
|
|
7
|
+
// `reportError` call of each request/cron tick, or one request's errors can
|
|
8
|
+
// suppress another's.
|
|
9
|
+
let lastDedupKey = null;
|
|
10
|
+
let lastReportedAt = 0;
|
|
11
|
+
function buildDedupKey(params) {
|
|
12
|
+
return params.dedupKey ?? `${params.classOrMethodName} ${stringifyUnknown(params.error, params.isClient)} ${params.params ? stringifyUnknown(params.params, params.isClient) : ''}`;
|
|
13
|
+
}
|
|
2
14
|
async function callOnError(config, params) {
|
|
3
15
|
const paramsWithFormattedMessage = { ...params, formattedMessage: formatErrorMessage(params) };
|
|
4
16
|
try {
|
|
@@ -16,10 +28,11 @@ async function callOnError(config, params) {
|
|
|
16
28
|
/**
|
|
17
29
|
* Reports `params` via `config.errorHandling.onError` (default
|
|
18
30
|
* `console.error(params.formattedMessage)`), unless
|
|
19
|
-
* `config.errorHandling.enable === false
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
31
|
+
* `config.errorHandling.enable === false`, `params.consent` is set and not
|
|
32
|
+
* `true` (reporting to a third party without cookie consent can itself be
|
|
33
|
+
* GDPR-relevant), or dedup throttles it (on by default — see
|
|
34
|
+
* `errorHandling.dedup`/`throttleMs`/`resetDedup`). Never throws — a broken
|
|
35
|
+
* reporter must not mask the original error.
|
|
23
36
|
*
|
|
24
37
|
* Always overwrites `params.formattedMessage` with a fresh
|
|
25
38
|
* `formatErrorMessage(params)` before reporting — a human-readable one-line
|
|
@@ -27,11 +40,6 @@ async function callOnError(config, params) {
|
|
|
27
40
|
* instead of the raw `error`/`params` object, for a default reporter (or a
|
|
28
41
|
* simple `onError`) to print directly.
|
|
29
42
|
*
|
|
30
|
-
* No built-in dedup/throttling: this package has no per-request context to
|
|
31
|
-
* safely scope such state to (module-scope state would leak across
|
|
32
|
-
* concurrent requests in a long-lived server process). Do dedup/throttling
|
|
33
|
-
* in your own `onError` if you need it, scoped to your own request context.
|
|
34
|
-
*
|
|
35
43
|
* When `config.generate?.getCloudflareContext` is set, `waitUntil` is called
|
|
36
44
|
* SYNCHRONOUSLY, in the same tick, with the `callOnError(...)` promise —
|
|
37
45
|
* Cloudflare Workers only extends the request's lifetime for work already
|
|
@@ -42,15 +50,36 @@ async function callOnError(config, params) {
|
|
|
42
50
|
* `onError` directly when `getCloudflareContext`/`ctx.waitUntil` is unset or
|
|
43
51
|
* unavailable (e.g. outside a Cloudflare Worker).
|
|
44
52
|
*
|
|
53
|
+
* Passing `params.error` as `null`/`undefined` with `errorHandling.resetDedup: true`
|
|
54
|
+
* and nothing else is a valid "reset-only" call: the dedup state clears and
|
|
55
|
+
* `reportError` returns immediately, without calling `onError` — useful to
|
|
56
|
+
* clear dedup state once at the very start of a request/cron tick, before
|
|
57
|
+
* any handler that might call `reportError` for a real error runs.
|
|
58
|
+
*
|
|
45
59
|
* @param config Pass the relevant slices of your `RoutingConfig` directly —
|
|
46
60
|
* `{ errorHandling: config.errorHandling, generate: config.generate }`.
|
|
47
61
|
*/
|
|
48
62
|
export default async function reportError(config, params) {
|
|
49
63
|
const errorHandling = config?.errorHandling;
|
|
64
|
+
if (errorHandling?.resetDedup) {
|
|
65
|
+
lastDedupKey = null;
|
|
66
|
+
lastReportedAt = 0;
|
|
67
|
+
if (params.error === null || params.error === undefined)
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
50
70
|
if (errorHandling?.enable === false)
|
|
51
71
|
return;
|
|
52
72
|
if (params.consent !== undefined && params.consent !== true)
|
|
53
73
|
return;
|
|
74
|
+
if (errorHandling?.dedup !== false) {
|
|
75
|
+
const throttleMs = errorHandling?.throttleMs ?? DEFAULT_THROTTLE_MS;
|
|
76
|
+
const dedupKey = buildDedupKey(params);
|
|
77
|
+
const now = Date.now();
|
|
78
|
+
if (dedupKey === lastDedupKey && now - lastReportedAt < throttleMs)
|
|
79
|
+
return;
|
|
80
|
+
lastDedupKey = dedupKey;
|
|
81
|
+
lastReportedAt = now;
|
|
82
|
+
}
|
|
54
83
|
const waitUntil = config?.generate?.getCloudflareContext?.({ async: false })?.ctx?.waitUntil;
|
|
55
84
|
if (waitUntil) {
|
|
56
85
|
waitUntil(callOnError(errorHandling, params));
|
|
@@ -149,6 +149,14 @@ export interface ErrorHandlingParams {
|
|
|
149
149
|
* yourself — it's always overwritten.
|
|
150
150
|
*/
|
|
151
151
|
formattedMessage?: string;
|
|
152
|
+
/**
|
|
153
|
+
* Key used by `config.errorHandling.dedupGate` to dedup this report
|
|
154
|
+
* against the immediately preceding one. Defaults to
|
|
155
|
+
* `` `${classOrMethodName} ${stringifyUnknown(error)} ${stringifyUnknown(params ?? '')}` ``
|
|
156
|
+
* when omitted (built by `reportError` itself) — set this explicitly
|
|
157
|
+
* only if you want a coarser/different dedup key.
|
|
158
|
+
*/
|
|
159
|
+
dedupKey?: string;
|
|
152
160
|
}
|
|
153
161
|
export interface ErrorHandlingRoutingConfig {
|
|
154
162
|
/**
|
|
@@ -193,6 +201,27 @@ export interface ErrorHandlingRoutingConfig {
|
|
|
193
201
|
* substring match.
|
|
194
202
|
*/
|
|
195
203
|
ignoreConsoleError?: (message: string) => boolean;
|
|
204
|
+
/**
|
|
205
|
+
* Dedup: `reportError` skips reporting an error whose key (`dedupKey`,
|
|
206
|
+
* or a built-in key derived from `classOrMethodName`/`error`/`params`
|
|
207
|
+
* when omitted) matches the immediately preceding reported error's key,
|
|
208
|
+
* within `throttleMs`. On by default (matches this package's own
|
|
209
|
+
* internal call sites and `installConsoleErrorOverride`'s console-loop
|
|
210
|
+
* guard). Set `false` to report every distinct call with no dedup at
|
|
211
|
+
* all.
|
|
212
|
+
*
|
|
213
|
+
* The dedup state lives inside `reportError`'s own module — this is
|
|
214
|
+
* shared mutable state, safe by default only because a fresh JS realm
|
|
215
|
+
* (isolate/Worker instance) starts with it cleared; in a long-lived
|
|
216
|
+
* server process reused across many requests, pass `resetDedup: true`
|
|
217
|
+
* on the first `reportError` call of each request/cron tick to clear it
|
|
218
|
+
* (otherwise one request's errors can suppress another's).
|
|
219
|
+
*/
|
|
220
|
+
dedup?: boolean;
|
|
221
|
+
/** Throttle window in ms: the same dedup key reported again within this window is skipped. Defaults to `5000`. Only consulted when `dedup` isn't `false`. */
|
|
222
|
+
throttleMs?: number;
|
|
223
|
+
/** Clears the dedup last-key/timestamp state before processing this call. Pass `true` on the first `reportError` call of each request/cron tick in a long-lived server process. */
|
|
224
|
+
resetDedup?: boolean;
|
|
196
225
|
}
|
|
197
226
|
export interface CookieConsentRoutingConfig {
|
|
198
227
|
/**
|