@@ -32,6 +32,15 @@ interface DomWaiterOptions {
3232export default function domwaiter ( pages : Permalink [ ] , opts : DomWaiterOptions = { } ) : EventEmitter {
3333 const emitter = new EventEmitter ( )
3434
35+ // Add a default no-op error handler to prevent EventEmitter from throwing
36+ // when errors are emitted before the caller attaches their error handler
37+ // This will be overridden/supplemented by the caller's error handler
38+ const defaultErrorHandler = ( ) => {
39+ // No-op: prevents EventEmitter from throwing
40+ // External handlers will still receive the error
41+ }
42+ emitter . on ( 'error' , defaultErrorHandler )
43+
3544 const defaults = {
3645 parseDOM : true ,
3746 json : false ,
@@ -43,7 +52,12 @@ export default function domwaiter(pages: Permalink[], opts: DomWaiterOptions = {
4352 const limiter = new Bottleneck ( opts )
4453
4554 pages . forEach ( ( page ) => {
46- limiter . schedule ( ( ) => getPage ( page , emitter , opts ) )
55+ limiter
56+ . schedule ( ( ) => getPage ( page , emitter , opts ) )
57+ . catch ( ( err ) => {
58+ // Catch any unhandled promise rejections
59+ emitter . emit ( 'error' , err )
60+ } )
4761 } )
4862
4963 limiter . on ( 'idle' , ( ) => {
@@ -58,46 +72,87 @@ export default function domwaiter(pages: Permalink[], opts: DomWaiterOptions = {
5872}
5973
6074async function getPage ( page : Permalink , emitter : EventEmitter , opts : DomWaiterOptions ) {
61- emitter . emit ( 'beforePageLoad' , page )
75+ // Wrap everything in a try-catch to ensure no errors escape
76+ try {
77+ emitter . emit ( 'beforePageLoad' , page )
6278
63- if ( opts . json ) {
64- try {
65- const response = await fetchWithRetry ( page . url ! , undefined , { retries : 3 , timeout : 60000 } )
66- if ( ! response . ok ) {
67- throw new HTTPError (
68- `HTTP ${ response . status } : ${ response . statusText } ` ,
69- { ok : response . ok , statusCode : response . status } ,
70- { requestUrl : { pathname : page . url } } ,
71- )
72- }
73- const json = await response . json ( )
74- const pageCopy = Object . assign ( { } , page , { json } )
75- emitter . emit ( 'page' , pageCopy )
76- } catch ( err ) {
77- if ( err instanceof Error ) {
78- err . message = `Failed to fetch ${ page . url } : ${ err . message } `
79+ if ( opts . json ) {
80+ try {
81+ const response = await fetchWithRetry ( page . url ! , undefined , {
82+ retries : 3 ,
83+ throwHttpErrors : false ,
84+ timeout : 60000 ,
85+ } )
86+ if ( ! response . ok ) {
87+ const httpError = new HTTPError (
88+ `HTTP ${ response . status } : ${ response . statusText } ` ,
89+ { ok : response . ok , statusCode : response . status } ,
90+ { requestUrl : { pathname : page . url } } ,
91+ )
92+ // Add URL and path info directly to the HTTPError
93+ ; ( httpError as any ) . url = page . url
94+ ; ( httpError as any ) . relativePath = page . relativePath
95+ // Emit error instead of throwing
96+ emitter . emit ( 'error' , httpError )
97+ return // Exit early, don't continue processing
98+ }
99+ const json = await response . json ( )
100+ const pageCopy = Object . assign ( { } , page , { json } )
101+ emitter . emit ( 'page' , pageCopy )
102+ } catch ( err ) {
103+ // Enhance error with URL information
104+ if ( err instanceof Error && page . url ) {
105+ const enhancedError = new Error ( err . message , { cause : err . cause } )
106+ enhancedError . name = err . name
107+ enhancedError . stack = err . stack
108+ ; ( enhancedError as any ) . url = page . url
109+ ; ( enhancedError as any ) . relativePath = page . relativePath
110+ emitter . emit ( 'error' , enhancedError )
111+ } else {
112+ emitter . emit ( 'error' , err )
113+ }
79114 }
80- emitter . emit ( 'error' , err )
81- }
82- } else {
83- try {
84- const response = await fetchWithRetry ( page . url ! , undefined , { retries : 3 , timeout : 60000 } )
85- if ( ! response . ok ) {
86- throw new HTTPError (
87- `HTTP ${ response . status } : ${ response . statusText } ` ,
88- { ok : response . ok , statusCode : response . status } ,
89- { requestUrl : { pathname : page . url } } ,
90- )
91- }
92- const body = await response . text ( )
93- const pageCopy = Object . assign ( { } , page , { body } )
94- if ( opts . parseDOM ) ( pageCopy as any ) . $ = cheerio . load ( body )
95- emitter . emit ( 'page' , pageCopy )
96- } catch ( err ) {
97- if ( err instanceof Error ) {
98- err . message = `Failed to fetch ${ page . url } : ${ err . message } `
115+ } else {
116+ try {
117+ const response = await fetchWithRetry ( page . url ! , undefined , {
118+ retries : 3 ,
119+ throwHttpErrors : false ,
120+ timeout : 60000 ,
121+ } )
122+ if ( ! response . ok ) {
123+ const httpError = new HTTPError (
124+ `HTTP ${ response . status } : ${ response . statusText } ` ,
125+ { ok : response . ok , statusCode : response . status } ,
126+ { requestUrl : { pathname : page . url } } ,
127+ )
128+ // Add URL and path info directly to the HTTPError
129+ ; ( httpError as any ) . url = page . url
130+ ; ( httpError as any ) . relativePath = page . relativePath
131+ // Emit error instead of throwing
132+ emitter . emit ( 'error' , httpError )
133+ return // Exit early, don't continue processing
134+ }
135+ const body = await response . text ( )
136+ const pageCopy = Object . assign ( { } , page , { body } )
137+ if ( opts . parseDOM ) ( pageCopy as any ) . $ = cheerio . load ( body )
138+ emitter . emit ( 'page' , pageCopy )
139+ } catch ( err ) {
140+ // Enhance error with URL information
141+ if ( err instanceof Error && page . url ) {
142+ const enhancedError = new Error ( err . message , { cause : err . cause } )
143+ enhancedError . name = err . name
144+ enhancedError . stack = err . stack
145+ ; ( enhancedError as any ) . url = page . url
146+ ; ( enhancedError as any ) . relativePath = page . relativePath
147+ emitter . emit ( 'error' , enhancedError )
148+ } else {
149+ emitter . emit ( 'error' , err )
150+ }
99151 }
100- emitter . emit ( 'error' , err )
101152 }
153+ } catch ( err ) {
154+ // Ultimate catch-all to ensure nothing escapes
155+ console . error ( 'Unexpected error in getPage:' , err )
156+ emitter . emit ( 'error' , err )
102157 }
103158}
0 commit comments