Skip to content

Commit f6f38e4

Browse files
committed
fix: expose preview error details to clients
1 parent 742e272 commit f6f38e4

18 files changed

Lines changed: 55 additions & 8 deletions

File tree

apps/site/app/[locale]/error.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ const ErrorPage: FC<ErrorPageProps> = ({ error }) => {
3636
{SHOW_ERROR_DETAILS && errorDetails && (
3737
<details className="max-w-2xl rounded-lg border border-neutral-300 bg-neutral-950/90 px-4 py-3 text-left text-neutral-50">
3838
<summary className="cursor-pointer font-medium">
39-
{t('components.downloadReleasesTable.details')}
39+
{t('layouts.error.details')}
4040
</summary>
4141

4242
<pre className="mt-3 overflow-x-auto font-mono text-xs leading-6 break-words whitespace-pre-wrap">

apps/site/next.config.mjs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,10 @@ const nextConfig = {
2929
basePath: BASE_PATH,
3030
// Vercel/Next.js Image Optimization Settings
3131
images: getImagesConfig(),
32+
env: {
33+
NEXT_PUBLIC_VERCEL_ENV:
34+
process.env.NEXT_PUBLIC_VERCEL_ENV || process.env.VERCEL_ENV,
35+
},
3236
serverExternalPackages: ['twoslash'],
3337
outputFileTracingIncludes: {
3438
// Twoslash needs TypeScript declarations to function, and, by default, Next.js

apps/site/next.constants.mjs

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,17 @@ export const IS_DEV_ENV = process.env.NODE_ENV === 'development';
1414
*/
1515
export const VERCEL_ENV = process.env.VERCEL_ENV || undefined;
1616

17+
/**
18+
* Public-facing Vercel environment, safe to use in client-side code.
19+
*/
20+
export const PUBLIC_VERCEL_ENV =
21+
process.env.NEXT_PUBLIC_VERCEL_ENV || VERCEL_ENV;
22+
1723
/**
1824
* Error details should only be exposed in local development or Vercel preview
1925
* deployments, never in production.
2026
*/
21-
export const SHOW_ERROR_DETAILS =
22-
process.env.NODE_ENV === 'development' || VERCEL_ENV === 'preview';
27+
export const SHOW_ERROR_DETAILS = IS_DEV_ENV || PUBLIC_VERCEL_ENV === 'preview';
2328

2429
/**
2530
* This is used for telling Next.js to do a Static Export Build of the Website

apps/site/tests/errorPage.test.jsx

Lines changed: 29 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { describe, it } from 'node:test';
44
import { render, screen } from '@testing-library/react';
55

66
describe('ErrorPage', () => {
7-
it('renders technical details in preview environments', async t => {
7+
const setupErrorPage = async (t, showErrorDetails, suffix = '') => {
88
t.mock.module('#site/components/Common/Button', {
99
defaultExport: ({ children, href }) => <a href={href}>{children}</a>,
1010
});
@@ -15,11 +15,15 @@ describe('ErrorPage', () => {
1515

1616
t.mock.module('#site/next.constants.mjs', {
1717
namedExports: {
18-
SHOW_ERROR_DETAILS: true,
18+
SHOW_ERROR_DETAILS: showErrorDetails,
1919
},
2020
});
2121

22-
const { default: ErrorPage } = await import('../app/[locale]/error.tsx');
22+
return import(`../app/[locale]/error.tsx${suffix}`);
23+
};
24+
25+
it('renders technical details in preview environments', async t => {
26+
const { default: ErrorPage } = await setupErrorPage(t, true);
2327

2428
render(
2529
<ErrorPage
@@ -38,8 +42,8 @@ describe('ErrorPage', () => {
3842
'layouts.error.backToHome'
3943
);
4044
assert.equal(
41-
screen.getByText('components.downloadReleasesTable.details').textContent,
42-
'components.downloadReleasesTable.details'
45+
screen.getByText('layouts.error.details').textContent,
46+
'layouts.error.details'
4347
);
4448
assert.match(
4549
screen.getByText(/Preview deployment failed/).textContent,
@@ -50,4 +54,24 @@ describe('ErrorPage', () => {
5054
/digest: abc123/i
5155
);
5256
});
57+
58+
it('hides technical details when the flag is disabled', async t => {
59+
const { default: ErrorPage } = await setupErrorPage(
60+
t,
61+
false,
62+
'?show-error-details-disabled'
63+
);
64+
65+
render(
66+
<ErrorPage
67+
error={Object.assign(new Error('Production should stay generic'), {
68+
digest: 'hidden123',
69+
})}
70+
/>
71+
);
72+
73+
assert.equal(screen.queryByText('layouts.error.details'), null);
74+
assert.equal(screen.queryByText(/Production should stay generic/), null);
75+
assert.equal(screen.queryByText(/digest: hidden123/i), null);
76+
});
5377
});

packages/i18n/src/locales/en.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -370,6 +370,7 @@
370370
"title": "Internal Server Error",
371371
"description": "This page has thrown a non-recoverable error."
372372
},
373+
"details": "Details",
373374
"backToHome": "Back to Home"
374375
},
375376
"download": {

packages/i18n/src/locales/es.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -275,6 +275,7 @@
275275
"title": "Error interno del servidor",
276276
"description": "Esta página ha generado un error no recuperable."
277277
},
278+
"details": "Detalles",
278279
"backToHome": "Volver al inicio"
279280
},
280281
"download": {

packages/i18n/src/locales/fr.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -370,6 +370,7 @@
370370
"title": "Erreur interne du serveur",
371371
"description": "Cette page a généré une erreur irrécupérable."
372372
},
373+
"details": "Détails",
373374
"backToHome": "Retourner à l'accueil"
374375
},
375376
"download": {

packages/i18n/src/locales/id.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -360,6 +360,7 @@
360360
"title": "Kesalahan Server Internal",
361361
"description": "Halaman ini mengalami error yang tidak dapat diperbaiki."
362362
},
363+
"details": "Rincian",
363364
"backToHome": "Kembali ke Beranda"
364365
},
365366
"download": {

packages/i18n/src/locales/ja.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -370,6 +370,7 @@
370370
"title": "内部サーバーエラー",
371371
"description": "このページで回復不可能なエラーが発生しました。"
372372
},
373+
"details": "詳細",
373374
"backToHome": "ホームに戻る"
374375
},
375376
"download": {

packages/i18n/src/locales/ko.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -249,6 +249,7 @@
249249
"title": "Internal Server Error",
250250
"description": "이 페이지에서 복구할 수 없는 오류가 발생했습니다."
251251
},
252+
"details": "세부정보",
252253
"backToHome": "홈으로 돌아가기"
253254
},
254255
"download": {

0 commit comments

Comments
 (0)