@@ -120,6 +120,23 @@ function maybeGetFile(
120120 // Otherwise, something's gone wrong, so don't do any source mapping.
121121}
122122
123+ // Like Object.fromEntries(), but preserves all values per header (string or array)
124+ function getHeaders ( request : Request ) {
125+ const headers : Record < string , string | string [ ] > = { } ;
126+
127+ for ( const [ key , value ] of request . headers . entries ( ) ) {
128+ if ( headers [ key ] === undefined ) {
129+ headers [ key ] = value ;
130+ } else if ( typeof headers [ key ] === "string" ) {
131+ headers [ key ] = [ headers [ key ] , value ] ;
132+ } else {
133+ headers [ key ] . push ( value ) ;
134+ }
135+ }
136+
137+ return headers ;
138+ }
139+
123140function getSourceMappedStack (
124141 workerSrcOpts : NameSourceOptions [ ] ,
125142 error : Error
@@ -255,21 +272,40 @@ export async function handlePrettyErrorRequest(
255272 }
256273
257274 // Lazily import `youch` when required
258- const Youch : typeof import ( "youch" ) . default = require ( "youch" ) ;
275+ const { Youch } : typeof import ( "youch" ) = require ( "youch" ) ;
259276 // `cause` is usually more useful than the error itself, display that instead
260277 // TODO(someday): would be nice if we could display both
261- const youch = new Youch ( error . cause ?? error , {
262- url : request . cf ?. prettyErrorOriginalUrl ?? request . url ,
263- method : request . method ,
264- headers : Object . fromEntries ( request . headers ) ,
265- } ) ;
266- youch . addLink ( ( ) => {
267- return [
268- '<a href="https://developers.cloudflare.com/workers/" target="_blank" style="text-decoration:none">📚 Workers Docs</a>' ,
269- '<a href="https://discord.cloudflare.com" target="_blank" style="text-decoration:none">💬 Workers Discord</a>' ,
278+ const youch = new Youch ( ) ;
279+
280+ youch . useTransformer ( ( error ) => {
281+ error . frames = error . frames
282+ . filter (
283+ ( frame ) =>
284+ ! frame . fileName ?. includes ( ".wrangler/tmp" ) &&
285+ ! frame . fileName ?. includes ( "wrangler/templates/middleware" )
286+ )
287+ . map ( ( frame ) => {
288+ // To avoid Youch throwing an error if the frame has no fileName
289+ // This happens in tests which hides some parts of the stack trace
290+ frame . fileName ??= "" ;
291+
292+ return frame ;
293+ } ) ;
294+ error . hint = [
295+ '<a href="https://developers.cloudflare.com/workers/" target="_blank" style="text-decoration:none;font-style:normal;padding:5px">📚 Workers Docs</a>' ,
296+ '<a href="https://discord.cloudflare.com" target="_blank" style="text-decoration:none;font-style: normal;padding:5px">💬 Workers Discord</a>' ,
270297 ] . join ( "" ) ;
271298 } ) ;
272- return new Response ( await youch . toHTML ( ) , {
299+
300+ const html = await youch . toHTML ( error , {
301+ request : {
302+ url : `${ request . cf ?. prettyErrorOriginalUrl ?? request . url } ` ,
303+ method : request . method ,
304+ headers : getHeaders ( request ) ,
305+ } ,
306+ } ) ;
307+
308+ return new Response ( html , {
273309 status : 500 ,
274310 headers : { "Content-Type" : "text/html;charset=utf-8" } ,
275311 } ) ;
0 commit comments