@@ -18,73 +18,75 @@ abstract class BaseSSEDecoder implements SSEDecoder {
18
18
throw new StreamingDecodeError ( `Invalid SSE line: ${ line } ` ) ;
19
19
}
20
20
21
- protected abstract getReader ( response : CrossPlatformResponse ) : ReadableStreamDefaultReader < Uint8Array > ;
21
+ abstract iterLines ( response : CrossPlatformResponse ) : AsyncIterableIterator < string > ;
22
22
23
+ protected processLine ( line : string ) : string {
24
+ if ( line . startsWith ( 'data: ' ) ) {
25
+ return line . slice ( 6 ) ;
26
+ }
27
+ return line ;
28
+ }
29
+ }
30
+
31
+ export class BrowserSSEDecoder extends BaseSSEDecoder {
23
32
async * iterLines ( response : CrossPlatformResponse ) : AsyncIterableIterator < string > {
24
33
if ( ! response . body ) {
25
34
throw new Error ( 'Response body is null' ) ;
26
35
}
27
36
28
- yield * this . _iterLines ( this . getReader ( response ) ) ;
29
- }
30
-
31
- async * _iterLines ( reader : ReadableStreamDefaultReader < Uint8Array > ) : AsyncIterableIterator < string > {
37
+ const reader = ( response . body as ReadableStream < Uint8Array > ) . getReader ( ) ;
38
+ const decoder = new TextDecoder ( ) ;
32
39
let buffer = '' ;
33
40
34
41
try {
35
42
while ( true ) {
36
43
const { done, value } = await reader . read ( ) ;
44
+ if ( done ) break ;
37
45
38
- if ( done ) {
39
- if ( buffer . length > 0 ) {
40
- const decoded = this . decode ( buffer . trim ( ) ) ;
41
- if ( decoded ) yield decoded ;
42
- }
43
- break ;
44
- }
45
-
46
- buffer += new TextDecoder ( ) . decode ( value ) ;
47
- const lines = buffer . split ( '\n' ) ;
46
+ buffer += decoder . decode ( value , { stream : true } ) ;
47
+ const lines = buffer . split ( / \r \n | \n / ) ;
48
48
buffer = lines . pop ( ) || '' ;
49
49
50
50
for ( const line of lines ) {
51
- const decoded = this . decode ( line . trim ( ) ) ;
52
- if ( decoded ) yield decoded ;
51
+ if ( line . trim ( ) ) {
52
+ yield this . processLine ( line ) ;
53
+ }
53
54
}
54
55
}
56
+
57
+ if ( buffer . trim ( ) ) {
58
+ yield this . processLine ( buffer ) ;
59
+ }
55
60
} finally {
56
61
reader . releaseLock ( ) ;
57
62
}
58
63
}
59
64
}
60
65
61
- export class BrowserSSEDecoder extends BaseSSEDecoder {
62
- protected getReader ( response : CrossPlatformResponse ) : ReadableStreamDefaultReader < Uint8Array > {
63
- const body = response . body as ReadableStream < Uint8Array > ;
64
- return body . getReader ( ) ;
65
- }
66
- }
67
-
68
66
export class NodeSSEDecoder extends BaseSSEDecoder {
69
- protected getReader ( response : CrossPlatformResponse ) : ReadableStreamDefaultReader < Uint8Array > {
67
+ async * iterLines ( response : CrossPlatformResponse ) : AsyncIterableIterator < string > {
68
+ if ( ! response . body ) {
69
+ throw new Error ( 'Response body is null' ) ;
70
+ }
71
+
70
72
const stream = response . body as NodeJS . ReadableStream ;
71
- // Convert Node readable stream to Web readable stream
72
- const webStream = new ReadableStream ( {
73
- async start ( controller ) {
74
- try {
75
- for await ( const chunk of stream ) {
76
- const uint8Array =
77
- typeof chunk === 'string' ? new TextEncoder ( ) . encode ( chunk )
78
- : chunk instanceof Uint8Array ? chunk
79
- : new Uint8Array ( chunk ) ;
80
- controller . enqueue ( uint8Array ) ;
81
- }
82
- controller . close ( ) ;
83
- } catch ( error ) {
84
- controller . error ( error ) ;
73
+ let buffer = '' ;
74
+
75
+ for await ( const chunk of stream ) {
76
+ const text = typeof chunk === 'string' ? chunk : chunk . toString ( 'utf-8' ) ;
77
+ buffer += text ;
78
+ const lines = buffer . split ( / \r \n | \n / ) ;
79
+ buffer = lines . pop ( ) || '' ;
80
+
81
+ for ( const line of lines ) {
82
+ if ( line . trim ( ) ) {
83
+ yield this . processLine ( line ) ;
85
84
}
86
- } ,
87
- } ) ;
88
- return webStream . getReader ( ) ;
85
+ }
86
+ }
87
+
88
+ if ( buffer . trim ( ) ) {
89
+ yield this . processLine ( buffer ) ;
90
+ }
89
91
}
90
92
}
0 commit comments