Skip to content

Commit 2aa9bea

Browse files
authored
more robust unpkg errors handling, small tweaks (#2414)
1 parent ee55193 commit 2aa9bea

6 files changed

Lines changed: 70 additions & 46 deletions

File tree

bun.lock

Lines changed: 14 additions & 14 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

components/Package/CodeBrowser/CodeBrowserContent.tsx

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,14 +59,20 @@ export default function CodeBrowserContent({
5959
: undefined,
6060
(url: string) =>
6161
fetch(url).then(res => {
62+
if (res.status >= 500) {
63+
throw new Error(`Failed to fetch "${filePath}" file content: ${res.status}`);
64+
}
65+
6266
if (res.status === 200) {
6367
return res.text();
6468
}
69+
6570
return res.json();
6671
}),
6772
{
6873
dedupingInterval: TimeRange.HOUR * 1000,
6974
revalidateOnFocus: false,
75+
shouldRetryOnError: false,
7076
}
7177
);
7278

next.config.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ const PACKAGES_TO_OPTIMIZE = [
1010
'@react-native-picker/picker',
1111
'@sentry/*',
1212
'@shikijs/*',
13+
'@videojs/*',
14+
'@visx/*',
1315
'node-emoji',
1416
'react-native',
1517
'react-native-safe-area-context',

package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@
2828
"@radix-ui/react-tooltip": "^1.2.8",
2929
"@react-native-picker/picker": "^2.11.4",
3030
"@sentry/react": "^10.50.0",
31-
"@videojs/react": "^10.0.0-beta.22",
31+
"@videojs/react": "^10.0.0-beta.23",
3232
"@visx/gradient": "^3.12.0",
3333
"@visx/responsive": "^3.12.0",
3434
"@visx/xychart": "^3.12.0",
@@ -75,7 +75,7 @@
7575
"next-images": "^1.8.5",
7676
"oxfmt": "^0.47.0",
7777
"oxlint": "^1.62.0",
78-
"oxlint-tsgolint": "^0.22.0",
78+
"oxlint-tsgolint": "^0.22.1",
7979
"simple-git-hooks": "^2.13.1",
8080
"typescript": "^6.0.3",
8181
"user-agent-data-types": "^0.4.3"

pages/api/proxy/unpkg.ts

Lines changed: 45 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ import { type NextApiRequest, type NextApiResponse } from 'next';
33
import { NEXT_10M_CACHE_HEADER } from '~/util/Constants';
44
import { parseQueryParams } from '~/util/queryParams';
55

6+
const UNPKG_TIMEOUT_MS = 15_000;
7+
68
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
79
const { name, path } = parseQueryParams(req.query);
810

@@ -20,47 +22,60 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
2022
return;
2123
}
2224

23-
const result = await fetch(`https://unpkg.com/${packageName}/${apiPath}`, {
24-
...NEXT_10M_CACHE_HEADER,
25-
redirect: 'manual',
26-
});
25+
try {
26+
const result = await fetch(`https://unpkg.com/${packageName}/${apiPath}`, {
27+
...NEXT_10M_CACHE_HEADER,
28+
redirect: 'manual',
29+
signal: AbortSignal.timeout(UNPKG_TIMEOUT_MS),
30+
});
31+
32+
if (result.status === 302) {
33+
const location = result.headers.get('location');
2734

28-
if (result.status === 302) {
29-
const location = result.headers.get('location');
35+
if (!location) {
36+
res.setHeader('Content-Type', 'application/json');
37+
res.statusCode = 502;
38+
res.json({
39+
error: 'Invalid response. Unpkg returned a redirect without a location header.',
40+
});
41+
return;
42+
}
3043

31-
if (!location) {
32-
res.setHeader('Content-Type', 'application/json');
33-
res.statusCode = 502;
34-
res.json({
35-
error: 'Invalid response. Unpkg returned a redirect without a location header.',
44+
const redirectResult = await fetch(new URL(location, 'https://unpkg.com').toString(), {
45+
...NEXT_10M_CACHE_HEADER,
46+
signal: AbortSignal.timeout(UNPKG_TIMEOUT_MS),
3647
});
48+
49+
if ('status' in redirectResult && redirectResult.status !== 200) {
50+
res.statusCode = redirectResult.status;
51+
res.json({});
52+
return;
53+
}
54+
55+
res.setHeader('Content-Type', 'text/plain');
56+
res.statusCode = 200;
57+
res.write(await redirectResult.text());
58+
res.end();
3759
return;
3860
}
3961

40-
const redirectResult = await fetch(new URL(location, 'https://unpkg.com').toString(), {
41-
...NEXT_10M_CACHE_HEADER,
42-
});
43-
44-
if ('status' in redirectResult && redirectResult.status !== 200) {
45-
res.statusCode = redirectResult.status;
62+
if ('status' in result && result.status !== 200) {
63+
res.statusCode = result.status;
4664
res.json({});
4765
return;
4866
}
4967

50-
res.setHeader('Content-Type', 'text/plain');
68+
res.setHeader('Content-Type', 'application/json');
5169
res.statusCode = 200;
52-
res.write(await redirectResult.text());
53-
res.end();
54-
return;
55-
}
70+
res.json(await result.json());
71+
} catch (error) {
72+
if (error instanceof Error && error.name !== 'AbortError' && error.name !== 'TimeoutError') {
73+
throw error;
74+
}
5675

57-
if ('status' in result && result.status !== 200) {
58-
res.statusCode = result.status;
59-
res.json({});
60-
return;
76+
res.setHeader('Cache-Control', 'private, no-cache, no-store, must-revalidate');
77+
res.setHeader('Content-Type', 'application/json');
78+
res.statusCode = 504;
79+
res.json({ error: `Request to unpkg timed out after ${UNPKG_TIMEOUT_MS / 1000}s.` });
6180
}
62-
63-
res.setHeader('Content-Type', 'application/json');
64-
res.statusCode = 200;
65-
res.json(await result.json());
6681
}

util/codeBrowser.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ export const FILE_WARNINGS = [
5858
'.watchmanconfig',
5959
'babel.config.js',
6060
'eslint.config*.js',
61+
'expo-module.config.json',
6162
'gradle-wrapper.jar',
6263
'gradlew',
6364
'gradlew.bat',

0 commit comments

Comments
 (0)