@@ -3,6 +3,8 @@ import { type NextApiRequest, type NextApiResponse } from 'next';
33import { NEXT_10M_CACHE_HEADER } from '~/util/Constants' ;
44import { parseQueryParams } from '~/util/queryParams' ;
55
6+ const UNPKG_TIMEOUT_MS = 15_000 ;
7+
68export 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}
0 commit comments