@@ -37,6 +37,7 @@ import {
3737
3838const maxErrorResponseBodySize = 10 * 1024 * 1024 ;
3939const prefixUrlRenamedErrorMessage = 'The `prefixUrl` option has been renamed `prefix` in v2 and enhanced to allow slashes in input. See also the new `baseUrl` option for improved flexibility with standard URL resolution: https://github.com/sindresorhus/ky#baseurl' ;
40+ const timedOutResponseData = Symbol ( 'timedOutResponseData' ) ;
4041
4142const createTextDecoder = ( contentType : string ) : TextDecoder => {
4243 const match = / ; \s * c h a r s e t \s * = \s * (?: " ( [ ^ " ] + ) " | ( [ ^ ; , \s ] + ) ) / i. exec ( contentType ) ;
@@ -505,8 +506,15 @@ export class Ky {
505506 async #getResponseData( response : Response ) : Promise < unknown > {
506507 // Even with request timeouts disabled, bound error-body reads so retries and error propagation
507508 // cannot be stalled indefinitely by never-ending response streams.
508- const errorDataTimeout = this . #options. timeout === false ? 10_000 : this . #options. timeout ;
509- const text = await this . #readResponseText( response , errorDataTimeout ) ;
509+ const readTimeout = this . #getErrorDataTimeout( ) ;
510+ const text = await this . #readResponseText( response , readTimeout . timeout ) ;
511+ if ( text === timedOutResponseData ) {
512+ if ( readTimeout . totalTimeoutReachedOnTimeout ) {
513+ throw new TimeoutError ( this . request ) ;
514+ }
515+
516+ return undefined ;
517+ }
510518
511519 if ( ! text ) {
512520 return undefined ;
@@ -516,7 +524,38 @@ export class Ky {
516524 return text ;
517525 }
518526
519- return this . #parseJson( text , response , errorDataTimeout ) ;
527+ const parseTimeout = this . #getErrorDataTimeout( ) ;
528+ const data = await this . #parseJson( text , response , parseTimeout . timeout ) ;
529+ if ( data === timedOutResponseData ) {
530+ if ( parseTimeout . totalTimeoutReachedOnTimeout ) {
531+ throw new TimeoutError ( this . request ) ;
532+ }
533+
534+ return undefined ;
535+ }
536+
537+ return data ;
538+ }
539+
540+ #getErrorDataTimeout( ) : { timeout : number ; totalTimeoutReachedOnTimeout : boolean } {
541+ const errorDataTimeout = this . #options. timeout === false ? 10_000 : this . #options. timeout ;
542+ const remainingTotal = this . #getRemainingTotalTimeout( ) ;
543+
544+ if ( remainingTotal === undefined ) {
545+ return {
546+ timeout : errorDataTimeout ,
547+ totalTimeoutReachedOnTimeout : false ,
548+ } ;
549+ }
550+
551+ if ( remainingTotal <= 0 ) {
552+ throw new TimeoutError ( this . request ) ;
553+ }
554+
555+ return {
556+ timeout : Math . min ( errorDataTimeout , remainingTotal ) ,
557+ totalTimeoutReachedOnTimeout : remainingTotal <= errorDataTimeout ,
558+ } ;
520559 }
521560
522561 #isJsonContentType( contentType : string ) : boolean {
@@ -525,7 +564,7 @@ export class Ky {
525564 return / \/ (?: .* [ . + - ] ) ? j s o n $ / . test ( mimeType ) ;
526565 }
527566
528- async #readResponseText( response : Response , timeoutMs : number ) : Promise < string | undefined > {
567+ async #readResponseText( response : Response , timeoutMs : number ) : Promise < string | typeof timedOutResponseData | undefined > {
529568 const { body} = response ;
530569 if ( ! body ) {
531570 try {
@@ -572,17 +611,17 @@ export class Ky {
572611 return chunks . join ( '' ) ;
573612 } ) ( ) ;
574613
575- const timeoutPromise = new Promise < undefined > ( resolve => {
614+ const timeoutPromise = new Promise < typeof timedOutResponseData > ( resolve => {
576615 const timeoutId = setTimeout ( ( ) => {
577- resolve ( undefined ) ;
616+ resolve ( timedOutResponseData ) ;
578617 } , timeoutMs ) ;
579618 void readAll . finally ( ( ) => {
580619 clearTimeout ( timeoutId ) ;
581620 } ) ;
582621 } ) ;
583622
584623 const result = await Promise . race ( [ readAll , timeoutPromise ] ) ;
585- if ( result === undefined ) {
624+ if ( result === timedOutResponseData ) {
586625 void reader . cancel ( ) . catch ( ( ) => undefined ) ;
587626 }
588627
@@ -597,9 +636,9 @@ export class Ky {
597636 ? this . #options. parseJson ( text , { request : this . request , response} )
598637 : JSON . parse ( text ) ,
599638 ) ,
600- new Promise < undefined > ( resolve => {
639+ new Promise < typeof timedOutResponseData > ( resolve => {
601640 timeoutId = setTimeout ( ( ) => {
602- resolve ( undefined ) ;
641+ resolve ( timedOutResponseData ) ;
603642 } , timeoutMs ) ;
604643 } ) ,
605644 ] ) ;
0 commit comments