@@ -26,6 +26,8 @@ let ReadableStream = globalThis.ReadableStream
26
26
27
27
/** @type {globalThis['File'] } */
28
28
const File = NativeFile ?? UndiciFile
29
+ const textEncoder = new TextEncoder ( )
30
+ const textDecoder = new TextDecoder ( )
29
31
30
32
// https://fetch.spec.whatwg.org/#concept-bodyinit-extract
31
33
function extractBody ( object , keepalive = false ) {
@@ -49,7 +51,7 @@ function extractBody (object, keepalive = false) {
49
51
stream = new ReadableStream ( {
50
52
async pull ( controller ) {
51
53
controller . enqueue (
52
- typeof source === 'string' ? new TextEncoder ( ) . encode ( source ) : source
54
+ typeof source === 'string' ? textEncoder . encode ( source ) : source
53
55
)
54
56
queueMicrotask ( ( ) => readableStreamClose ( controller ) )
55
57
} ,
@@ -119,21 +121,20 @@ function extractBody (object, keepalive = false) {
119
121
// - That the content-length is calculated in advance.
120
122
// - And that all parts are pre-encoded and ready to be sent.
121
123
122
- const enc = new TextEncoder ( )
123
124
const blobParts = [ ]
124
125
const rn = new Uint8Array ( [ 13 , 10 ] ) // '\r\n'
125
126
length = 0
126
127
let hasUnknownSizeValue = false
127
128
128
129
for ( const [ name , value ] of object ) {
129
130
if ( typeof value === 'string' ) {
130
- const chunk = enc . encode ( prefix +
131
+ const chunk = textEncoder . encode ( prefix +
131
132
`; name="${ escape ( normalizeLinefeeds ( name ) ) } "` +
132
133
`\r\n\r\n${ normalizeLinefeeds ( value ) } \r\n` )
133
134
blobParts . push ( chunk )
134
135
length += chunk . byteLength
135
136
} else {
136
- const chunk = enc . encode ( `${ prefix } ; name="${ escape ( normalizeLinefeeds ( name ) ) } "` +
137
+ const chunk = textEncoder . encode ( `${ prefix } ; name="${ escape ( normalizeLinefeeds ( name ) ) } "` +
137
138
( value . name ? `; filename="${ escape ( value . name ) } "` : '' ) + '\r\n' +
138
139
`Content-Type: ${
139
140
value . type || 'application/octet-stream'
@@ -147,7 +148,7 @@ function extractBody (object, keepalive = false) {
147
148
}
148
149
}
149
150
150
- const chunk = enc . encode ( `--${ boundary } --` )
151
+ const chunk = textEncoder . encode ( `--${ boundary } --` )
151
152
blobParts . push ( chunk )
152
153
length += chunk . byteLength
153
154
if ( hasUnknownSizeValue ) {
@@ -443,14 +444,16 @@ function bodyMixinMethods (instance) {
443
444
let text = ''
444
445
// application/x-www-form-urlencoded parser will keep the BOM.
445
446
// https://url.spec.whatwg.org/#concept-urlencoded-parser
446
- const textDecoder = new TextDecoder ( 'utf-8' , { ignoreBOM : true } )
447
+ // Note that streaming decoder is stateful and cannot be reused
448
+ const streamingDecoder = new TextDecoder ( 'utf-8' , { ignoreBOM : true } )
449
+
447
450
for await ( const chunk of consumeBody ( this [ kState ] . body ) ) {
448
451
if ( ! isUint8Array ( chunk ) ) {
449
452
throw new TypeError ( 'Expected Uint8Array chunk' )
450
453
}
451
- text += textDecoder . decode ( chunk , { stream : true } )
454
+ text += streamingDecoder . decode ( chunk , { stream : true } )
452
455
}
453
- text += textDecoder . decode ( )
456
+ text += streamingDecoder . decode ( )
454
457
entries = new URLSearchParams ( text )
455
458
} catch ( err ) {
456
459
// istanbul ignore next: Unclear when new URLSearchParams can fail on a string.
@@ -565,7 +568,7 @@ function utf8DecodeBytes (buffer) {
565
568
566
569
// 3. Process a queue with an instance of UTF-8’s
567
570
// decoder, ioQueue, output, and "replacement".
568
- const output = new TextDecoder ( ) . decode ( buffer )
571
+ const output = textDecoder . decode ( buffer )
569
572
570
573
// 4. Return output.
571
574
return output
0 commit comments