cloudflare-next-intl 0.5.7 → 0.6.0

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.
Files changed (28) hide show
  1. package/README.md +64 -5
  2. package/dist/src/client/components/client_provider.js +2 -0
  3. package/dist/src/cookie_consent/gdpr_countries.d.ts +2 -2
  4. package/dist/src/cookie_consent/gdpr_countries.js +15 -4
  5. package/dist/src/error_handling/default_ignored_console_errors.d.ts +13 -0
  6. package/dist/src/error_handling/default_ignored_console_errors.js +27 -0
  7. package/dist/src/error_handling/format_error_message.d.ts +9 -0
  8. package/dist/src/error_handling/format_error_message.js +23 -0
  9. package/dist/src/error_handling/index.d.ts +9 -0
  10. package/dist/src/error_handling/index.js +6 -0
  11. package/dist/src/error_handling/install_console_error_override.bench.d.ts +1 -0
  12. package/dist/src/error_handling/install_console_error_override.bench.js +10 -0
  13. package/dist/src/error_handling/install_console_error_override.d.ts +30 -0
  14. package/dist/src/error_handling/install_console_error_override.js +55 -0
  15. package/dist/src/error_handling/report_error.bench.d.ts +1 -0
  16. package/dist/src/error_handling/report_error.bench.js +25 -0
  17. package/dist/src/error_handling/report_error.d.ts +38 -0
  18. package/dist/src/error_handling/report_error.js +60 -0
  19. package/dist/src/error_handling/stringify_unknown.bench.d.ts +1 -0
  20. package/dist/src/error_handling/stringify_unknown.bench.js +29 -0
  21. package/dist/src/error_handling/stringify_unknown.d.ts +12 -0
  22. package/dist/src/error_handling/stringify_unknown.js +42 -0
  23. package/dist/src/error_handling/with_error_handling.d.ts +15 -0
  24. package/dist/src/error_handling/with_error_handling.js +18 -0
  25. package/dist/src/firebase_auth/server/firebase_server.js +3 -1
  26. package/dist/src/server/components/server_provider.js +15 -4
  27. package/dist/src/types/types.d.ts +116 -25
  28. package/package.json +21 -1
package/README.md CHANGED
@@ -11,6 +11,8 @@ and Cloudflare environment.
11
11
  Components.
12
12
  - **Fast and Efficient**: Low overhead and minimal bundle size.
13
13
  - **Tree-shaking**: Properly architected for optimal tree-shaking.
14
+ - **Error handling**: shared, opt-in `console.error` override and
15
+ `reportError`/`withErrorHandling` helpers, GDPR-aware (consent-gated).
14
16
 
15
17
  ## Installation
16
18
 
@@ -200,16 +202,21 @@ import { getCloudflareContext } from "@opennextjs/cloudflare";
200
202
  export default setIntlConfig({
201
203
  locales: ["en", "de"],
202
204
  defaultLocale: "en",
205
+ // Shared request-time resolvers — used by cookieConsent's GDPR gating
206
+ // below AND by error_handling's ctx.waitUntil backgrounding.
207
+ generate: {
208
+ // Pass @opennextjs/cloudflare's getCloudflareContext directly — its
209
+ // exact overloaded signature is accepted as-is, called internally
210
+ // with { async: true } (cookieConsent) or { async: false } (error_handling).
211
+ getCloudflareContext,
212
+ },
203
213
  cookieConsent: {
204
214
  privacyPolicyDate: "2026-01-01",
205
215
  // privacyPolicyPath: "/privacy-policy", // default; used by the
206
216
  // dialogs' auto-rendered link. Set false to disable that link.
207
217
  // Optional: gate the banner to GDPR-region visitors only. Omit both
208
- // getters to disable country-based gating (consent always implicit).
209
- // Pass @opennextjs/cloudflare's getCloudflareContext directly — its
210
- // exact overloaded signature is accepted as-is, called internally
211
- // with { async: true }.
212
- getCloudflareContext,
218
+ // getCountryCode and generate.getCloudflareContext to disable
219
+ // country-based gating (consent always implicit).
213
220
  // gdprCountries: [...], // defaults to EU/EEA + UK + Switzerland
214
221
  // enableAnalyticsInDevMode: true, // analytics stay off in dev otherwise
215
222
  // autoWireDialogs: false, // opt out and render the dialogs yourself
@@ -246,6 +253,58 @@ const { consent, setConsent } = useCookieConsent();
246
253
 
247
254
  See [`package/src/cookie_consent/README.md`](package/src/cookie_consent/README.md) for layout, customization, and gotchas.
248
255
 
256
+ ### Error handling
257
+
258
+ Every risky call this package makes internally (Cloudflare-context
259
+ resolution, Firebase server auth, `cookieConsent.getAnalytics()`) reports
260
+ through a shared `error_handling` submodule you can also use in your own
261
+ app code. Enabled by default — no config needed to get the default
262
+ `console.error`-based reporting; set `errorHandling.onError` to plug in
263
+ your own transport (Sentry, Telegram, etc).
264
+
265
+ ```typescript
266
+ // intl-config.ts
267
+ export default setIntlConfig({
268
+ locales: ["en", "de"],
269
+ defaultLocale: "en",
270
+ generate: { getCloudflareContext }, // reports background via ctx.waitUntil when set
271
+ errorHandling: {
272
+ // enable: false, // fully disable reporting (errors still rethrow from withErrorHandling)
273
+ onError: ({ formattedMessage, error, classOrMethodName, consent }) => {
274
+ // formattedMessage is a ready-to-print "[classOrMethodName] Error: ..." string
275
+ myErrorTracker.capture(formattedMessage);
276
+ },
277
+ // overrideConsoleError: true, // route every console.error(...) call through onError too
278
+ // ignoreConsoleErrors: [...], // defaults to defaultIgnoredConsoleErrors (this package's
279
+ // // own Firebase Auth codes for expected user-input failures);
280
+ // // pass [] to report everything, or your own list to replace it
281
+ // ignoreConsoleError: (message) => message.includes("known noisy warning"),
282
+ },
283
+ });
284
+ ```
285
+
286
+ Use `reportError`/`withErrorHandling` directly in your own code:
287
+
288
+ ```typescript
289
+ import { reportError, withErrorHandling } from "cloudflare-next-intl/errorHandling";
290
+ import config from "./intl-config";
291
+
292
+ // Wrap a function — reports then rethrows on failure.
293
+ const safeFetch = withErrorHandling(fetchSomething, "fetchSomething", { config });
294
+
295
+ // Or report manually inside your own try/catch.
296
+ try {
297
+ await riskyThing();
298
+ } catch (error) {
299
+ await reportError(config, { error, classOrMethodName: "riskyThing" });
300
+ }
301
+ ```
302
+
303
+ **GDPR note:** pass `consent` (from `useCookieConsent()` or your own
304
+ server-side resolution) on `ErrorHandlingParams` — reporting is skipped
305
+ whenever `consent` is set and not `true`, since sending error reports to a
306
+ third party without consent can itself be GDPR-relevant.
307
+
249
308
  ## License
250
309
 
251
310
  MIT
@@ -4,6 +4,7 @@ import { setLocaleCache, setMessageForLocaleCache } from "../../general/cache_va
4
4
  import { createContext, useMemo } from "react";
5
5
  import dynamic from "next/dynamic";
6
6
  import config from "@intl-config";
7
+ import installConsoleErrorOverride from "../../error_handling/install_console_error_override";
7
8
  export const LocaleContext = createContext(undefined);
8
9
  // Hoisted to module scope — calling `dynamic()` inside the component body
9
10
  // creates a brand-new component identity every render, forcing React to
@@ -20,6 +21,7 @@ const PrivacyPolicyUpdateDialog = dynamic(() => import("../../cookie_consent/cli
20
21
  export default function LocationzationClientProvider({ language, messages, initialAuthUser = null, skipAuthProvider = false, analyticsConfig, requiresConsent = true, autoWireDialogs = true, dialogProps, updateDialogProps, children }) {
21
22
  setLocaleCache(language);
22
23
  setMessageForLocaleCache(language, messages);
24
+ installConsoleErrorOverride(config, true);
23
25
  // `LocaleContext.Provider` stays the outermost element here — the
24
26
  // client `AuthUserProvider` (and its descendants calling
25
27
  // usePathname()/useLocale()) must render as a CHILD of it, not a
@@ -1,4 +1,4 @@
1
- import type { CookieConsentGetCloudflareContext } from '../types/types';
1
+ import type { CookieConsentGetCloudflareContext, ErrorHandlingRoutingConfig } from '../types/types';
2
2
  /**
3
3
  * Default `cookieConsent.gdprCountries` — EU/EEA member states (GDPR),
4
4
  * Iceland/Liechtenstein/Norway (EEA), the UK (UK-GDPR), and Switzerland
@@ -15,4 +15,4 @@ export declare const defaultGdprCountries: readonly string[];
15
15
  * `gdprCountries` skips the banner. `getCountryCode` takes precedence
16
16
  * over `getCloudflareContext` when both are set.
17
17
  */
18
- export default function resolveRequiresConsent(getCountryCode: (() => string | undefined | Promise<string | undefined>) | undefined, getCloudflareContext: CookieConsentGetCloudflareContext | undefined, gdprCountries: readonly string[] | undefined): Promise<boolean>;
18
+ export default function resolveRequiresConsent(getCountryCode: (() => string | undefined | Promise<string | undefined>) | undefined, getCloudflareContext: CookieConsentGetCloudflareContext | undefined, gdprCountries: readonly string[] | undefined, errorHandlingConfig?: ErrorHandlingRoutingConfig): Promise<boolean>;
@@ -1,3 +1,4 @@
1
+ import reportError from '../error_handling/report_error';
1
2
  /**
2
3
  * Default `cookieConsent.gdprCountries` — EU/EEA member states (GDPR),
3
4
  * Iceland/Liechtenstein/Norway (EEA), the UK (UK-GDPR), and Switzerland
@@ -40,12 +41,22 @@ function getGdprCountriesSet(gdprCountries) {
40
41
  * `gdprCountries` skips the banner. `getCountryCode` takes precedence
41
42
  * over `getCloudflareContext` when both are set.
42
43
  */
43
- export default async function resolveRequiresConsent(getCountryCode, getCloudflareContext, gdprCountries) {
44
+ export default async function resolveRequiresConsent(getCountryCode, getCloudflareContext, gdprCountries, errorHandlingConfig) {
44
45
  if (!getCountryCode && !getCloudflareContext)
45
46
  return true;
46
- const countryCode = getCountryCode
47
- ? await getCountryCode()
48
- : (await getCloudflareContext({ async: true }))?.cf?.country;
47
+ let countryCode;
48
+ if (getCountryCode) {
49
+ countryCode = await getCountryCode();
50
+ }
51
+ else {
52
+ try {
53
+ countryCode = (await getCloudflareContext({ async: true }))?.cf?.country;
54
+ }
55
+ catch (error) {
56
+ await reportError({ errorHandling: errorHandlingConfig, generate: { getCloudflareContext } }, { error, classOrMethodName: 'resolveRequiresConsent' });
57
+ return true;
58
+ }
59
+ }
49
60
  if (typeof countryCode !== 'string' || !countryCode)
50
61
  return true;
51
62
  return getGdprCountriesSet(gdprCountries).has(countryCode);
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Firebase Auth error codes this package's own `createLoginAction`/
3
+ * `createSignUpAction`/`createForgotPasswordAction` already catch and
4
+ * translate into a localized message (see
5
+ * `firebase_auth/error_messages/firebase_auth_error_helper.ts`) — expected
6
+ * user-input failures (wrong password, email already in use, etc.), not
7
+ * bugs. They never reach `console.error`/`reportError` through this
8
+ * package's own code; this list exists as defense-in-depth for consumers
9
+ * whose own code logs one of these codes directly. Passing your own
10
+ * `ignoreConsoleError` array replaces this default entirely — pass `[]` to
11
+ * report everything.
12
+ */
13
+ export declare const defaultIgnoredConsoleErrors: readonly string[];
@@ -0,0 +1,27 @@
1
+ /**
2
+ * Firebase Auth error codes this package's own `createLoginAction`/
3
+ * `createSignUpAction`/`createForgotPasswordAction` already catch and
4
+ * translate into a localized message (see
5
+ * `firebase_auth/error_messages/firebase_auth_error_helper.ts`) — expected
6
+ * user-input failures (wrong password, email already in use, etc.), not
7
+ * bugs. They never reach `console.error`/`reportError` through this
8
+ * package's own code; this list exists as defense-in-depth for consumers
9
+ * whose own code logs one of these codes directly. Passing your own
10
+ * `ignoreConsoleError` array replaces this default entirely — pass `[]` to
11
+ * report everything.
12
+ */
13
+ export const defaultIgnoredConsoleErrors = [
14
+ 'auth/invalid-email',
15
+ 'auth/user-disabled',
16
+ 'auth/user-not-found',
17
+ 'auth/wrong-password',
18
+ 'auth/invalid-credential',
19
+ 'auth/email-already-in-use',
20
+ 'auth/weak-password',
21
+ 'auth/too-many-requests',
22
+ 'auth/network-request-failed',
23
+ 'auth/requires-recent-login',
24
+ 'auth/expired-action-code',
25
+ 'auth/invalid-action-code',
26
+ 'auth/user-token-expired',
27
+ ];
@@ -0,0 +1,9 @@
1
+ import type { ErrorHandlingParams } from '../types/types';
2
+ /**
3
+ * Builds a human-readable one-string summary of an `ErrorHandlingParams` —
4
+ * `[classOrMethodName] Error: <message>` followed by non-empty `Params`/
5
+ * `IsClient` sections. Never throws (`stringifyUnknown` is safe). Used as
6
+ * `ErrorHandlingParams.formattedMessage` — read this instead of `error`/
7
+ * `params` directly when you just want something printable.
8
+ */
9
+ export default function formatErrorMessage(params: ErrorHandlingParams): string;
@@ -0,0 +1,23 @@
1
+ import stringifyUnknown from './stringify_unknown';
2
+ function formatSection(title, value, isClient) {
3
+ if (value === undefined)
4
+ return '';
5
+ const text = stringifyUnknown(value, isClient, true);
6
+ if (!text || text === '{}' || text === '[]')
7
+ return '';
8
+ return `\n${title}: ${text}`;
9
+ }
10
+ /**
11
+ * Builds a human-readable one-string summary of an `ErrorHandlingParams` —
12
+ * `[classOrMethodName] Error: <message>` followed by non-empty `Params`/
13
+ * `IsClient` sections. Never throws (`stringifyUnknown` is safe). Used as
14
+ * `ErrorHandlingParams.formattedMessage` — read this instead of `error`/
15
+ * `params` directly when you just want something printable.
16
+ */
17
+ export default function formatErrorMessage(params) {
18
+ const { error, classOrMethodName, params: extraParams, isClient } = params;
19
+ const errorText = stringifyUnknown(error, isClient);
20
+ const paramsSection = formatSection('Params', extraParams, isClient);
21
+ const clientSection = isClient ? '\nSource: client' : '';
22
+ return `[${classOrMethodName}] Error: ${errorText}${paramsSection}${clientSection}`;
23
+ }
@@ -0,0 +1,9 @@
1
+ export { default as withErrorHandling } from './with_error_handling';
2
+ export type { WithErrorHandlingOptions } from './with_error_handling';
3
+ export { default as reportError } from './report_error';
4
+ export type { ReportErrorConfig } from './report_error';
5
+ export { default as installConsoleErrorOverride } from './install_console_error_override';
6
+ export { default as stringifyUnknown } from './stringify_unknown';
7
+ export { default as formatErrorMessage } from './format_error_message';
8
+ export { defaultIgnoredConsoleErrors } from './default_ignored_console_errors';
9
+ export type { ErrorHandlingParams, ErrorHandlingRoutingConfig } from '../types/types';
@@ -0,0 +1,6 @@
1
+ export { default as withErrorHandling } from './with_error_handling';
2
+ export { default as reportError } from './report_error';
3
+ export { default as installConsoleErrorOverride } from './install_console_error_override';
4
+ export { default as stringifyUnknown } from './stringify_unknown';
5
+ export { default as formatErrorMessage } from './format_error_message';
6
+ export { defaultIgnoredConsoleErrors } from './default_ignored_console_errors';
@@ -0,0 +1,10 @@
1
+ import { bench, describe } from 'vitest';
2
+ import installConsoleErrorOverride from './install_console_error_override';
3
+ describe('installConsoleErrorOverride: repeated install calls (no-op after first)', () => {
4
+ const originalConsoleError = console.error;
5
+ console.error = () => { };
6
+ bench('install() called repeatedly (marker check short-circuits)', () => {
7
+ installConsoleErrorOverride({ errorHandling: { overrideConsoleError: true } });
8
+ });
9
+ console.error = originalConsoleError;
10
+ });
@@ -0,0 +1,30 @@
1
+ import { type ReportErrorConfig } from './report_error';
2
+ /**
3
+ * Replaces the global `console.error` so every `console.error(...)` call is
4
+ * also routed through `config.errorHandling.onError`/`reportError` — the
5
+ * original `console.error` still runs afterwards, nothing is swallowed.
6
+ * Safe to call more than once (a no-op after the first call in this JS
7
+ * realm — call it separately on the server and on the client, each has its
8
+ * own `console`). Only takes effect when `config.errorHandling.overrideConsoleError`
9
+ * is `true`.
10
+ *
11
+ * Caps at `MAX_REPORTS_PER_INSTALL` (20) reports per install — a component
12
+ * stuck in a render-error loop calls `console.error` on every render, and
13
+ * without a cap this would report (and, server-side, background via
14
+ * `waitUntil`) unboundedly. Once the cap is hit, `console.error` still runs
15
+ * normally, it just stops being reported.
16
+ *
17
+ * `config.errorHandling.ignoreConsoleErrors` (default
18
+ * `defaultIgnoredConsoleErrors` — this package's own Firebase Auth error
19
+ * codes for expected user-input failures) and `ignoreConsoleError` both
20
+ * skip reporting a matching call while still logging it normally.
21
+ *
22
+ * @param config Pass the relevant slices of your `RoutingConfig` directly —
23
+ * `{ errorHandling: config.errorHandling, generate: config.generate }`.
24
+ * @param isClient Passed through to every report's `ErrorHandlingParams.isClient`
25
+ * — set `true` when installing from client-side code (e.g. the client
26
+ * `LocationzationClientProvider`), omit/`false` on the server. There's no
27
+ * `getCloudflareContext`/`ctx.waitUntil` available in the browser, so
28
+ * client-side reports always await `onError` directly.
29
+ */
30
+ export default function installConsoleErrorOverride(config: ReportErrorConfig | undefined, isClient?: boolean): void;
@@ -0,0 +1,55 @@
1
+ import reportError from './report_error';
2
+ import stringifyUnknown from './stringify_unknown';
3
+ import { defaultIgnoredConsoleErrors } from './default_ignored_console_errors';
4
+ const MAX_REPORTS_PER_INSTALL = 20;
5
+ /**
6
+ * Replaces the global `console.error` so every `console.error(...)` call is
7
+ * also routed through `config.errorHandling.onError`/`reportError` — the
8
+ * original `console.error` still runs afterwards, nothing is swallowed.
9
+ * Safe to call more than once (a no-op after the first call in this JS
10
+ * realm — call it separately on the server and on the client, each has its
11
+ * own `console`). Only takes effect when `config.errorHandling.overrideConsoleError`
12
+ * is `true`.
13
+ *
14
+ * Caps at `MAX_REPORTS_PER_INSTALL` (20) reports per install — a component
15
+ * stuck in a render-error loop calls `console.error` on every render, and
16
+ * without a cap this would report (and, server-side, background via
17
+ * `waitUntil`) unboundedly. Once the cap is hit, `console.error` still runs
18
+ * normally, it just stops being reported.
19
+ *
20
+ * `config.errorHandling.ignoreConsoleErrors` (default
21
+ * `defaultIgnoredConsoleErrors` — this package's own Firebase Auth error
22
+ * codes for expected user-input failures) and `ignoreConsoleError` both
23
+ * skip reporting a matching call while still logging it normally.
24
+ *
25
+ * @param config Pass the relevant slices of your `RoutingConfig` directly —
26
+ * `{ errorHandling: config.errorHandling, generate: config.generate }`.
27
+ * @param isClient Passed through to every report's `ErrorHandlingParams.isClient`
28
+ * — set `true` when installing from client-side code (e.g. the client
29
+ * `LocationzationClientProvider`), omit/`false` on the server. There's no
30
+ * `getCloudflareContext`/`ctx.waitUntil` available in the browser, so
31
+ * client-side reports always await `onError` directly.
32
+ */
33
+ export default function installConsoleErrorOverride(config, isClient) {
34
+ if (config?.errorHandling?.overrideConsoleError !== true)
35
+ return;
36
+ if (console.error.__isErrorHandlingOverride)
37
+ return;
38
+ const originalConsoleError = console.error.bind(console);
39
+ let reportCount = 0;
40
+ const override = (message, ...optionalParams) => {
41
+ originalConsoleError(message, ...optionalParams);
42
+ if (reportCount >= MAX_REPORTS_PER_INSTALL)
43
+ return;
44
+ const stringified = stringifyUnknown(message, isClient);
45
+ const ignoreList = config.errorHandling?.ignoreConsoleErrors ?? defaultIgnoredConsoleErrors;
46
+ if (ignoreList.some((ignored) => stringified.includes(ignored)))
47
+ return;
48
+ if (config.errorHandling?.ignoreConsoleError?.(stringified))
49
+ return;
50
+ reportCount++;
51
+ void reportError(config, { error: message, classOrMethodName: 'Global Console Error Handler', params: optionalParams, isClient });
52
+ };
53
+ override.__isErrorHandlingOverride = true;
54
+ console.error = override;
55
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,25 @@
1
+ import { bench, describe } from 'vitest';
2
+ import reportError from './report_error';
3
+ const noopOnError = () => { };
4
+ const waitUntil = () => { };
5
+ const fakeGetCloudflareContext = (() => ({ ctx: { waitUntil } }));
6
+ describe('reportError', () => {
7
+ bench('default console.error path (no config)', async () => {
8
+ const original = console.error;
9
+ console.error = noopOnError;
10
+ await reportError(undefined, { error: new Error('boom'), classOrMethodName: 'bench' });
11
+ console.error = original;
12
+ });
13
+ bench('custom onError, no getCloudflareContext (awaited inline)', async () => {
14
+ await reportError({ errorHandling: { onError: noopOnError } }, { error: new Error('boom'), classOrMethodName: 'bench' });
15
+ });
16
+ bench('backgrounded via ctx.waitUntil', async () => {
17
+ await reportError({ errorHandling: { onError: noopOnError }, generate: { getCloudflareContext: fakeGetCloudflareContext } }, { error: new Error('boom'), classOrMethodName: 'bench' });
18
+ });
19
+ bench('skipped entirely (enable: false, no formatting cost paid)', async () => {
20
+ await reportError({ errorHandling: { enable: false, onError: noopOnError } }, { error: new Error('boom'), classOrMethodName: 'bench' });
21
+ });
22
+ bench('skipped entirely (consent: false)', async () => {
23
+ await reportError({ errorHandling: { onError: noopOnError } }, { error: new Error('boom'), classOrMethodName: 'bench', consent: false });
24
+ });
25
+ });
@@ -0,0 +1,38 @@
1
+ import type { ErrorHandlingParams, ErrorHandlingRoutingConfig, GenerateRoutingConfig } from '../types/types';
2
+ export interface ReportErrorConfig {
3
+ errorHandling?: ErrorHandlingRoutingConfig;
4
+ generate?: GenerateRoutingConfig;
5
+ }
6
+ /**
7
+ * Reports `params` via `config.errorHandling.onError` (default
8
+ * `console.error(params.formattedMessage)`), unless
9
+ * `config.errorHandling.enable === false` or `params.consent` is set and
10
+ * not `true` (reporting to a third party without cookie consent can itself
11
+ * be GDPR-relevant). Never throws — a broken reporter must not mask the
12
+ * original error.
13
+ *
14
+ * Always overwrites `params.formattedMessage` with a fresh
15
+ * `formatErrorMessage(params)` before reporting — a human-readable one-line
16
+ * summary (`[classOrMethodName] Error: <message>` plus non-empty sections)
17
+ * instead of the raw `error`/`params` object, for a default reporter (or a
18
+ * simple `onError`) to print directly.
19
+ *
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
+ * When `config.generate?.getCloudflareContext` is set, `waitUntil` is called
26
+ * SYNCHRONOUSLY, in the same tick, with the `callOnError(...)` promise —
27
+ * Cloudflare Workers only extends the request's lifetime for work already
28
+ * registered with `waitUntil` by the time the handler returns; deferring
29
+ * that call through an extra microtask (e.g. `Promise.resolve().then(...)`)
30
+ * risks the isolate tearing down the request before `waitUntil` is ever
31
+ * actually invoked, silently dropping the report. Falls back to awaiting
32
+ * `onError` directly when `getCloudflareContext`/`ctx.waitUntil` is unset or
33
+ * unavailable (e.g. outside a Cloudflare Worker).
34
+ *
35
+ * @param config Pass the relevant slices of your `RoutingConfig` directly —
36
+ * `{ errorHandling: config.errorHandling, generate: config.generate }`.
37
+ */
38
+ export default function reportError(config: ReportErrorConfig | undefined, params: ErrorHandlingParams): Promise<void>;
@@ -0,0 +1,60 @@
1
+ import formatErrorMessage from './format_error_message';
2
+ async function callOnError(config, params) {
3
+ const paramsWithFormattedMessage = { ...params, formattedMessage: formatErrorMessage(params) };
4
+ try {
5
+ if (config?.onError) {
6
+ await config.onError(paramsWithFormattedMessage);
7
+ }
8
+ else {
9
+ console.error(paramsWithFormattedMessage.formattedMessage);
10
+ }
11
+ }
12
+ catch {
13
+ console.error(paramsWithFormattedMessage.formattedMessage);
14
+ }
15
+ }
16
+ /**
17
+ * Reports `params` via `config.errorHandling.onError` (default
18
+ * `console.error(params.formattedMessage)`), unless
19
+ * `config.errorHandling.enable === false` or `params.consent` is set and
20
+ * not `true` (reporting to a third party without cookie consent can itself
21
+ * be GDPR-relevant). Never throws — a broken reporter must not mask the
22
+ * original error.
23
+ *
24
+ * Always overwrites `params.formattedMessage` with a fresh
25
+ * `formatErrorMessage(params)` before reporting — a human-readable one-line
26
+ * summary (`[classOrMethodName] Error: <message>` plus non-empty sections)
27
+ * instead of the raw `error`/`params` object, for a default reporter (or a
28
+ * simple `onError`) to print directly.
29
+ *
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
+ * When `config.generate?.getCloudflareContext` is set, `waitUntil` is called
36
+ * SYNCHRONOUSLY, in the same tick, with the `callOnError(...)` promise —
37
+ * Cloudflare Workers only extends the request's lifetime for work already
38
+ * registered with `waitUntil` by the time the handler returns; deferring
39
+ * that call through an extra microtask (e.g. `Promise.resolve().then(...)`)
40
+ * risks the isolate tearing down the request before `waitUntil` is ever
41
+ * actually invoked, silently dropping the report. Falls back to awaiting
42
+ * `onError` directly when `getCloudflareContext`/`ctx.waitUntil` is unset or
43
+ * unavailable (e.g. outside a Cloudflare Worker).
44
+ *
45
+ * @param config Pass the relevant slices of your `RoutingConfig` directly —
46
+ * `{ errorHandling: config.errorHandling, generate: config.generate }`.
47
+ */
48
+ export default async function reportError(config, params) {
49
+ const errorHandling = config?.errorHandling;
50
+ if (errorHandling?.enable === false)
51
+ return;
52
+ if (params.consent !== undefined && params.consent !== true)
53
+ return;
54
+ const waitUntil = config?.generate?.getCloudflareContext?.({ async: false })?.ctx?.waitUntil;
55
+ if (waitUntil) {
56
+ waitUntil(callOnError(errorHandling, params));
57
+ return;
58
+ }
59
+ await callOnError(errorHandling, params);
60
+ }
@@ -0,0 +1,29 @@
1
+ import { bench, describe } from 'vitest';
2
+ import stringifyUnknown from './stringify_unknown';
3
+ const error = new Error('boom');
4
+ const plainObject = { a: 1, b: { c: 2, d: [1, 2, 3] } };
5
+ const circular = {};
6
+ circular.self = circular;
7
+ describe('stringifyUnknown', () => {
8
+ bench('string passthrough', () => {
9
+ stringifyUnknown('already a string');
10
+ });
11
+ bench('Error instance', () => {
12
+ stringifyUnknown(error);
13
+ });
14
+ bench('function-wrapped lazy error (server, resolved)', () => {
15
+ stringifyUnknown(() => 'lazy boom');
16
+ });
17
+ bench('function-wrapped lazy error (client, not resolved)', () => {
18
+ stringifyUnknown(() => 'lazy boom', true);
19
+ });
20
+ bench('plain object, pretty-printed', () => {
21
+ stringifyUnknown(plainObject);
22
+ });
23
+ bench('plain object, nested (compact)', () => {
24
+ stringifyUnknown(plainObject, false, true);
25
+ });
26
+ bench('circular object (falls back to [Unserializable value])', () => {
27
+ stringifyUnknown(circular, false, true);
28
+ });
29
+ });
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Safely converts an `unknown` value (typically `ErrorHandlingParams.error`)
3
+ * into a string, for logging/dedup-keying/display — never throws.
4
+ *
5
+ * @param isClient Skips resolving function-wrapped/lazy error values on the
6
+ * client (matches `ErrorHandlingParams.isClient`) — running arbitrary
7
+ * caught functions client-side isn't safe the way it is on the server.
8
+ * @param isNested Set when stringifying a value nested inside another
9
+ * object/array — falls back to a plain `JSON.stringify` (no pretty-print)
10
+ * so a single unserializable nested value can't crash the whole report.
11
+ */
12
+ export default function stringifyUnknown(value: unknown, isClient?: boolean, isNested?: boolean): string;
@@ -0,0 +1,42 @@
1
+ const MAX_FUNCTION_RESOLUTION_ATTEMPTS = 5;
2
+ function resolveFunctionError(value) {
3
+ let result = value;
4
+ try {
5
+ for (let i = 0; i < MAX_FUNCTION_RESOLUTION_ATTEMPTS && typeof result === 'function'; i++) {
6
+ result = result();
7
+ }
8
+ return result;
9
+ }
10
+ catch (error) {
11
+ return `Error during function resolution: ${String(error)}`;
12
+ }
13
+ }
14
+ /**
15
+ * Safely converts an `unknown` value (typically `ErrorHandlingParams.error`)
16
+ * into a string, for logging/dedup-keying/display — never throws.
17
+ *
18
+ * @param isClient Skips resolving function-wrapped/lazy error values on the
19
+ * client (matches `ErrorHandlingParams.isClient`) — running arbitrary
20
+ * caught functions client-side isn't safe the way it is on the server.
21
+ * @param isNested Set when stringifying a value nested inside another
22
+ * object/array — falls back to a plain `JSON.stringify` (no pretty-print)
23
+ * so a single unserializable nested value can't crash the whole report.
24
+ */
25
+ export default function stringifyUnknown(value, isClient, isNested = false) {
26
+ if (typeof value === 'string')
27
+ return value;
28
+ if (value instanceof Error)
29
+ return `${value.name}: ${value.message}\n\n${value.stack ?? ''}`;
30
+ if (typeof value === 'function') {
31
+ if (isClient)
32
+ return '[Function]';
33
+ const resolved = resolveFunctionError(value);
34
+ return typeof resolved !== 'function' ? stringifyUnknown(resolved, isClient) : '[Function]';
35
+ }
36
+ try {
37
+ return isNested ? JSON.stringify(value) : JSON.stringify(value, null, 2);
38
+ }
39
+ catch {
40
+ return '[Unserializable value]';
41
+ }
42
+ }
@@ -0,0 +1,15 @@
1
+ import type { ConsentValue } from '../cookie_consent/types';
2
+ import { type ReportErrorConfig } from './report_error';
3
+ export interface WithErrorHandlingOptions {
4
+ /** Pass the relevant slices of your `RoutingConfig` directly — `{ errorHandling: config.errorHandling, generate: config.generate }`. */
5
+ config?: ReportErrorConfig;
6
+ params?: unknown;
7
+ isClient?: boolean;
8
+ consent?: ConsentValue;
9
+ }
10
+ /**
11
+ * Wraps `fn`, reporting (via `options.config`) then rethrowing any error it
12
+ * throws or rejects with. Never swallows — callers keep their own
13
+ * catch/fallback behavior, this only adds reporting on top.
14
+ */
15
+ export default function withErrorHandling<Args extends unknown[], Result>(fn: (...args: Args) => Result | Promise<Result>, classOrMethodName: string, options?: WithErrorHandlingOptions): (...args: Args) => Promise<Result>;
@@ -0,0 +1,18 @@
1
+ import reportError from './report_error';
2
+ /**
3
+ * Wraps `fn`, reporting (via `options.config`) then rethrowing any error it
4
+ * throws or rejects with. Never swallows — callers keep their own
5
+ * catch/fallback behavior, this only adds reporting on top.
6
+ */
7
+ export default function withErrorHandling(fn, classOrMethodName, options = {}) {
8
+ const { config, params, isClient, consent } = options;
9
+ return async (...args) => {
10
+ try {
11
+ return await fn(...args);
12
+ }
13
+ catch (error) {
14
+ await reportError(config, { error, classOrMethodName, params, isClient, consent });
15
+ throw error;
16
+ }
17
+ };
18
+ }
@@ -3,6 +3,7 @@ import { cache } from 'react';
3
3
  import config from '@intl-config';
4
4
  import requireFirebaseAuthConfig from '../require_config';
5
5
  import { defaultSessionCookieName } from '../middleware/update_session';
6
+ import reportError from '../../error_handling/report_error';
6
7
  let baseApp;
7
8
  let firebaseAppModule;
8
9
  let firebaseAuthModule;
@@ -51,7 +52,8 @@ export const getAuthenticatedAppForUser = cache(async function getAuthenticatedA
51
52
  await auth.authStateReady();
52
53
  return { firebaseServerApp, currentUser: auth.currentUser };
53
54
  }
54
- catch {
55
+ catch (error) {
56
+ await reportError(config, { error, classOrMethodName: 'getAuthenticatedAppForUser' });
55
57
  return { firebaseServerApp: null, currentUser: null };
56
58
  }
57
59
  });
@@ -5,6 +5,8 @@ import dynamic from "next/dynamic";
5
5
  import { localesSet } from "../../config/middleware";
6
6
  import config from "../../config/intl_config";
7
7
  import resolveRequiresConsent from "../../cookie_consent/gdpr_countries";
8
+ import installConsoleErrorOverride from "../../error_handling/install_console_error_override";
9
+ import reportError from "../../error_handling/report_error";
8
10
  const LocationzationClientProvider = dynamic(() => import("../../client/components/client_provider"));
9
11
  let authUserServerProviderModule;
10
12
  /**
@@ -48,6 +50,7 @@ export default async function LocationzationProvider({ language, messages, child
48
50
  setMessageForLocaleCache(language, messages);
49
51
  }
50
52
  const messagesValue = messages ?? await getMessage(language);
53
+ installConsoleErrorOverride(config);
51
54
  let initialAuthUser = null;
52
55
  const autoWireClientProvider = config.firebaseAuth?.autoWireClientProvider !== false;
53
56
  if (config.firebaseAuth && autoWireClientProvider) {
@@ -68,13 +71,21 @@ export default async function LocationzationProvider({ language, messages, child
68
71
  // `getCloudflareContext` path in dev; fail-safe to `true`
69
72
  // (banner shown) same as an unresolved country would.
70
73
  requiresConsent = !isDevEnvironment
71
- ? await resolveRequiresConsent(config.cookieConsent.getCountryCode, config.cookieConsent.getCloudflareContext, config.cookieConsent.gdprCountries)
74
+ ? await resolveRequiresConsent(config.cookieConsent.getCountryCode, config.generate?.getCloudflareContext, config.cookieConsent.gdprCountries, config.errorHandling)
72
75
  : false;
73
76
  const analyticsAllowedInEnv = config.cookieConsent.enableAnalyticsInDevMode === true || !isDevEnvironment;
74
77
  if (config.cookieConsent.autoWireAnalytics !== false && analyticsAllowedInEnv) {
75
- analyticsConfig = config.cookieConsent.getAnalytics
76
- ? await config.cookieConsent.getAnalytics()
77
- : config.cookieConsent.analytics;
78
+ if (config.cookieConsent.getAnalytics) {
79
+ try {
80
+ analyticsConfig = await config.cookieConsent.getAnalytics();
81
+ }
82
+ catch (error) {
83
+ await reportError(config, { error, classOrMethodName: 'getAnalytics' });
84
+ }
85
+ }
86
+ else {
87
+ analyticsConfig = config.cookieConsent.analytics;
88
+ }
78
89
  }
79
90
  }
80
91
  return _jsx(LocationzationClientProvider, { language: language, messages: messagesValue, initialAuthUser: initialAuthUser, skipAuthProvider: !autoWireClientProvider, analyticsConfig: analyticsConfig, requiresConsent: requiresConsent, autoWireDialogs: config.cookieConsent?.autoWireDialogs !== false, dialogProps: config.cookieConsent?.dialogProps, updateDialogProps: config.cookieConsent?.updateDialogProps, children: children });
@@ -3,6 +3,7 @@ import type { Languages } from 'next/dist/lib/metadata/types/alternative-urls-ty
3
3
  import type { Videos } from 'next/dist/lib/metadata/types/metadata-types';
4
4
  import type { CookieConsentDialogProps } from '../cookie_consent/client/components/cookie_consent_dialog';
5
5
  import type { PrivacyPolicyUpdateDialogProps } from '../cookie_consent/client/components/privacy_policy_update_dialog';
6
+ import type { ConsentValue } from '../cookie_consent/types';
6
7
  /**
7
8
  * Custom middleware hook, run by `intlMiddleware` for your own logic
8
9
  * (e.g. auth, feature flags, A/B tests) — on top of the library's own
@@ -90,6 +91,108 @@ export interface RoutingConfig<AppLocales extends Locales, AppLocalePrefixMode e
90
91
  * error if called without this set.
91
92
  */
92
93
  cookieConsent?: CookieConsentRoutingConfig;
94
+ /**
95
+ * Request-time resolvers shared across submodules. Omit entirely to
96
+ * leave all of them unset.
97
+ */
98
+ generate?: GenerateRoutingConfig;
99
+ /**
100
+ * Configures the optional `error_handling` submodule (shared
101
+ * `withErrorHandling`/`reportError` helpers used internally by this
102
+ * package and available to your own app code). Omit entirely to keep
103
+ * the defaults: enabled, reporting via `console.error`.
104
+ */
105
+ errorHandling?: ErrorHandlingRoutingConfig;
106
+ }
107
+ export interface GenerateRoutingConfig {
108
+ /**
109
+ * Pass `getCloudflareContext` from `@opennextjs/cloudflare` directly
110
+ * (not a dependency of this package, so bring your own import) — its
111
+ * exact overloaded signature is accepted as-is; called internally with
112
+ * `{ async: true }`, so you never need to wrap it yourself. Only
113
+ * `cf.country` is read from the resolved context by `cookieConsent`.
114
+ *
115
+ * Country-based gating (via either `cookieConsent.getCountryCode` or
116
+ * this getter) decides whether the cookie-consent banner is required at
117
+ * all: visitors outside `gdprCountries` skip the banner and get
118
+ * analytics immediately (still gated by `enableAnalyticsInDevMode`).
119
+ * Omit BOTH to require consent for everyone (fail-safe default — the
120
+ * visitor's country can't be determined at all without either getter).
121
+ * Set one of the two getters to scope the banner to GDPR regions only.
122
+ */
123
+ getCloudflareContext?: CookieConsentGetCloudflareContext;
124
+ }
125
+ export interface ErrorHandlingParams {
126
+ /** The caught error, in whatever shape it was thrown/rejected with. */
127
+ error: unknown;
128
+ /** Name of the function/method the error was caught in, e.g. `"resolveRequiresConsent"`. */
129
+ classOrMethodName: string;
130
+ /** Extra context to include in the report (arguments, request info, etc). */
131
+ params?: unknown;
132
+ /** Whether this error originated in a client-side (browser) call. */
133
+ isClient?: boolean;
134
+ /**
135
+ * The visitor's current cookie-consent value (from `useCookieConsent()`
136
+ * or your own server-side resolution), when known. When passed and not
137
+ * `true`, `reportError`/`withErrorHandling` skip reporting entirely —
138
+ * sending error reports to a third party (Telegram, Sentry, etc.)
139
+ * without consent can itself be GDPR-relevant. Omit when consent isn't
140
+ * applicable (e.g. `cookieConsent` isn't configured at all).
141
+ */
142
+ consent?: ConsentValue;
143
+ /**
144
+ * Human-readable one-string summary — `[classOrMethodName] Error:
145
+ * <message>` plus non-empty `Params`/client-origin sections. Set by
146
+ * `reportError` before calling `onError`/`console.error`; read this
147
+ * instead of `error`/`params` directly when you just want something
148
+ * printable. Ignore when passing `params` to `withErrorHandling`
149
+ * yourself — it's always overwritten.
150
+ */
151
+ formattedMessage?: string;
152
+ }
153
+ export interface ErrorHandlingRoutingConfig {
154
+ /**
155
+ * Whether errors caught by this package's `withErrorHandling`/
156
+ * `reportError` helpers are reported at all. Defaults to `true`. Set
157
+ * `false` to fully disable reporting (errors are still rethrown by
158
+ * `withErrorHandling`, just never reported).
159
+ */
160
+ enable?: boolean;
161
+ /**
162
+ * Called with the caught error whenever one is reported. Defaults to
163
+ * `console.error`. Use this to wire your own error-tracking/logging
164
+ * transport (Sentry, Telegram, etc).
165
+ */
166
+ onError?: (params: ErrorHandlingParams) => void | Promise<void>;
167
+ /**
168
+ * Whether `reportError`/`withErrorHandling` replace the global
169
+ * `console.error` so every `console.error(...)` call in your app is
170
+ * also routed through `onError` (the original `console.error` still
171
+ * runs afterwards — nothing is swallowed). Defaults to `false`; call
172
+ * `installConsoleErrorOverride()` (or pass this `true` and call
173
+ * `IntlProvider`/`setIntlConfig`'s setup) to install it. Off by default
174
+ * since this package is shared across apps and a global override is a
175
+ * bigger behavior change than a plain function call.
176
+ */
177
+ overrideConsoleError?: boolean;
178
+ /**
179
+ * Substrings matched against the stringified message of each
180
+ * `console.error(...)` call (only consulted when `overrideConsoleError`
181
+ * is `true`) — a match skips reporting it (it's still logged normally).
182
+ * Defaults to `defaultIgnoredConsoleErrors` (this package's own Firebase
183
+ * Auth error codes for expected user-input failures — wrong password,
184
+ * email already in use, etc). Pass your own array to replace the
185
+ * default entirely; pass `[]` to report everything.
186
+ */
187
+ ignoreConsoleErrors?: readonly string[];
188
+ /**
189
+ * Called with the stringified message of each `console.error(...)` call
190
+ * (only consulted when `overrideConsoleError` is `true`), in addition to
191
+ * `ignoreConsoleErrors` — return `true` to skip reporting it (it's still
192
+ * logged normally). Use this for custom filtering logic beyond a plain
193
+ * substring match.
194
+ */
195
+ ignoreConsoleError?: (message: string) => boolean;
93
196
  }
94
197
  export interface CookieConsentRoutingConfig {
95
198
  /**
@@ -145,27 +248,10 @@ export interface CookieConsentRoutingConfig {
145
248
  * over `getCloudflareContext` when both are set.
146
249
  */
147
250
  getCountryCode?: () => string | undefined | Promise<string | undefined>;
148
- /**
149
- * Pass `getCloudflareContext` from `@opennextjs/cloudflare` directly
150
- * (not a dependency of this package, so bring your own import) — its
151
- * exact overloaded signature is accepted as-is; called internally with
152
- * `{ async: true }`, so you never need to wrap it yourself. Only
153
- * `cf.country` is read from the resolved context. Ignored when
154
- * `getCountryCode` is also set.
155
- *
156
- * Country-based gating (via either `getCountryCode` or
157
- * `getCloudflareContext`) decides whether the cookie-consent banner is
158
- * required at all: visitors outside `gdprCountries` skip the banner and
159
- * get analytics immediately (still gated by `enableAnalyticsInDevMode`).
160
- * Omit BOTH to require consent for everyone (fail-safe default — the
161
- * visitor's country can't be determined at all without either getter).
162
- * Set one of the two getters to scope the banner to GDPR regions only.
163
- */
164
- getCloudflareContext?: CookieConsentGetCloudflareContext;
165
251
  /**
166
252
  * Country codes (ISO 3166-1 alpha-2) for which the cookie-consent banner
167
253
  * is required. Only consulted when `getCountryCode` or
168
- * `getCloudflareContext` is set. Defaults to the EU/EEA + UK +
254
+ * `generate.getCloudflareContext` is set. Defaults to the EU/EEA + UK +
169
255
  * Switzerland (GDPR/UK-GDPR/nFADP scope). A visitor whose resolved
170
256
  * country isn't in this set is treated as NOT requiring consent; a
171
257
  * country that couldn't be resolved still requires it (fail-safe:
@@ -203,16 +289,21 @@ export interface CookieConsentRoutingConfig {
203
289
  updateDialogProps?: PrivacyPolicyUpdateDialogProps;
204
290
  }
205
291
  /**
206
- * Minimal shape read from your `getCloudflareContext()` return value — only
207
- * `cf.country` is consulted (read defensively at the call site, since `cf`'s
208
- * real type — `@opennextjs/cloudflare`'s `CfProperties`, a union of the
209
- * incoming-request and request-init variants — only has `country` on one
210
- * branch). `cf` is typed loosely here so the real (generic) function is
211
- * assignable to `CookieConsentGetCloudflareContext` without a hard
212
- * dependency on that package.
292
+ * Minimal shape read from your `getCloudflareContext()` return value.
293
+ * `cf.country` is consulted by `cookieConsent` (read defensively at the call
294
+ * site, since `cf`'s real type — `@opennextjs/cloudflare`'s `CfProperties`,
295
+ * a union of the incoming-request and request-init variants — only has
296
+ * `country` on one branch); `ctx.waitUntil` is used by `error_handling` to
297
+ * background error reports instead of awaiting them inline. Typed loosely
298
+ * here so the real (generic) function is assignable to
299
+ * `CookieConsentGetCloudflareContext` without a hard dependency on that
300
+ * package.
213
301
  */
214
302
  export interface CookieConsentCloudflareContext {
215
303
  cf?: Record<string, unknown>;
304
+ ctx?: {
305
+ waitUntil?: (promise: Promise<unknown>) => void;
306
+ };
216
307
  }
217
308
  /**
218
309
  * Matches `@opennextjs/cloudflare`'s `getCloudflareContext` overloaded
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cloudflare-next-intl",
3
- "version": "0.5.7",
3
+ "version": "0.6.0",
4
4
  "description": "Optimized Next Intl Package Special for App Router and Cloudflare",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -139,6 +139,26 @@
139
139
  "./cookieConsentAnalytics": {
140
140
  "types": "./dist/src/cookie_consent/client/components/cookie_consent_analytics.d.ts",
141
141
  "import": "./dist/src/cookie_consent/client/components/cookie_consent_analytics.js"
142
+ },
143
+ "./errorHandling": {
144
+ "types": "./dist/src/error_handling/index.d.ts",
145
+ "import": "./dist/src/error_handling/index.js"
146
+ },
147
+ "./installConsoleErrorOverride": {
148
+ "types": "./dist/src/error_handling/install_console_error_override.d.ts",
149
+ "import": "./dist/src/error_handling/install_console_error_override.js"
150
+ },
151
+ "./stringifyUnknown": {
152
+ "types": "./dist/src/error_handling/stringify_unknown.d.ts",
153
+ "import": "./dist/src/error_handling/stringify_unknown.js"
154
+ },
155
+ "./formatErrorMessage": {
156
+ "types": "./dist/src/error_handling/format_error_message.d.ts",
157
+ "import": "./dist/src/error_handling/format_error_message.js"
158
+ },
159
+ "./defaultIgnoredConsoleErrors": {
160
+ "types": "./dist/src/error_handling/default_ignored_console_errors.d.ts",
161
+ "import": "./dist/src/error_handling/default_ignored_console_errors.js"
142
162
  }
143
163
  },
144
164
  "scripts": {