@@ -133,7 +133,10 @@ export class StreamHandler {
133133 #onStdout( data : string | Buffer , context : StreamContext ) : void {
134134 if ( data == null ) return ;
135135 if ( this . #isRetirementRequest == null || this . #stdoutEnded) {
136- this . #routeStdout( data , context ) ;
136+ // The decoder retains a code point split across chunks, which decoding
137+ // each chunk separately would turn into U+FFFD replacement characters.
138+ const output = this . #stdoutDecoder. write ( data ) ;
139+ if ( output . length > 0 ) this . #routeStdout( output , context ) ;
137140 return ;
138141 }
139142
@@ -232,7 +235,12 @@ export class StreamHandler {
232235 }
233236
234237 #endStdout( context : StreamContext ) : void {
235- if ( this . #isRetirementRequest == null || this . #stdoutEnded) return ;
238+ if ( this . #isRetirementRequest == null ) {
239+ const output = this . #stdoutDecoder. end ( ) ;
240+ if ( output . length > 0 ) this . #routeStdout( output , context ) ;
241+ return ;
242+ }
243+ if ( this . #stdoutEnded) return ;
236244 const hadIncompleteLine = this . hasIncompleteOutputLine ;
237245 this . #stdoutEnded = true ;
238246 this . #consumeStdout( this . #stdoutDecoder. end ( ) , context ) ;
@@ -252,11 +260,7 @@ export class StreamHandler {
252260 if ( fragment . length > 0 ) this . #routeStdout( fragment , context , owner ) ;
253261 }
254262
255- #routeStdout(
256- data : string | Buffer ,
257- context : StreamContext ,
258- owner ?: LineOwner ,
259- ) : void {
263+ #routeStdout( data : string , context : StreamContext , owner ?: LineOwner ) : void {
260264 const task = owner == null ? context . getCurrentTask ( ) : owner . task ;
261265 if ( task != null && task . pending ) {
262266 // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-argument
@@ -279,12 +283,13 @@ export class StreamHandler {
279283 this . #shouldIgnoreStderrLine == null &&
280284 this . #isRetirementRequest == null
281285 ) {
282- this . #routeStderr( data , context ) ;
286+ // See #onStdout:
287+ this . #routeStderr( this . #stderrDecoder. write ( data ) , context ) ;
283288 return ;
284289 }
285290
286291 if ( this . #stderrEnded) {
287- this . #routeStderr( data , context ) ;
292+ this . #routeStderr( this . #stderrDecoder . write ( data ) , context ) ;
288293 return ;
289294 }
290295
@@ -434,11 +439,13 @@ export class StreamHandler {
434439
435440 #endStderr( context : StreamContext ) : void {
436441 if (
437- ( this . #shouldIgnoreStderrLine == null &&
438- this . #isRetirementRequest == null ) ||
439- this . #stderrEnded
440- )
442+ this . #shouldIgnoreStderrLine == null &&
443+ this . #isRetirementRequest == null
444+ ) {
445+ this . #routeStderr ( this . #stderrDecoder . end ( ) , context ) ;
441446 return ;
447+ }
448+ if ( this . #stderrEnded) return ;
442449
443450 const hadIncompleteLine = this . hasIncompleteOutputLine ;
444451 this . #stderrEnded = true ;
@@ -461,11 +468,7 @@ export class StreamHandler {
461468 }
462469 }
463470
464- #routeStderr(
465- data : string | Buffer ,
466- context : StreamContext ,
467- owner ?: LineOwner ,
468- ) : void {
471+ #routeStderr( data : string , context : StreamContext , owner ?: LineOwner ) : void {
469472 if ( blank ( data ) ) return ;
470473
471474 const task = owner == null ? context . getCurrentTask ( ) : owner . task ;
0 commit comments