@@ -26,6 +26,8 @@ let ReadableStream = globalThis.ReadableStream
2626
2727/** @type {globalThis['File'] } */
2828const File = NativeFile ?? UndiciFile
29+ const textEncoder = new TextEncoder ( )
30+ const textDecoder = new TextDecoder ( )
2931
3032// https://fetch.spec.whatwg.org/#concept-bodyinit-extract
3133function extractBody ( object , keepalive = false ) {
@@ -49,7 +51,7 @@ function extractBody (object, keepalive = false) {
4951stream = new ReadableStream ( {
5052async pull ( controller ) {
5153controller . enqueue (
52- typeof source === 'string' ? new TextEncoder ( ) . encode ( source ) : source
54+ typeof source === 'string' ? textEncoder . encode ( source ) : source
5355)
5456queueMicrotask ( ( ) => readableStreamClose ( controller ) )
5557} ,
@@ -119,21 +121,20 @@ function extractBody (object, keepalive = false) {
119121// - That the content-length is calculated in advance.
120122// - And that all parts are pre-encoded and ready to be sent.
121123
122- const enc = new TextEncoder ( )
123124const blobParts = [ ]
124125const rn = new Uint8Array ( [ 13 , 10 ] ) // '\r\n'
125126length = 0
126127let hasUnknownSizeValue = false
127128
128129for ( const [ name , value ] of object ) {
129130if ( typeof value === 'string' ) {
130- const chunk = enc . encode ( prefix +
131+ const chunk = textEncoder . encode ( prefix +
131132`; name="${ escape ( normalizeLinefeeds ( name ) ) } "` +
132133`\r\n\r\n${ normalizeLinefeeds ( value ) } \r\n` )
133134blobParts . push ( chunk )
134135length += chunk . byteLength
135136} else {
136- const chunk = enc . encode ( `${ prefix } ; name="${ escape ( normalizeLinefeeds ( name ) ) } "` +
137+ const chunk = textEncoder . encode ( `${ prefix } ; name="${ escape ( normalizeLinefeeds ( name ) ) } "` +
137138( value . name ? `; filename="${ escape ( value . name ) } "` : '' ) + '\r\n' +
138139`Content-Type: ${
139140value . type || 'application/octet-stream'
@@ -147,7 +148,7 @@ function extractBody (object, keepalive = false) {
147148}
148149}
149150
150- const chunk = enc . encode ( `--${ boundary } --` )
151+ const chunk = textEncoder . encode ( `--${ boundary } --` )
151152blobParts . push ( chunk )
152153length += chunk . byteLength
153154if ( hasUnknownSizeValue ) {
@@ -445,14 +446,16 @@ function bodyMixinMethods (instance) {
445446let text = ''
446447// application/x-www-form-urlencoded parser will keep the BOM.
447448// https://url.spec.whatwg.org/#concept-urlencoded-parser
448- const textDecoder = new TextDecoder ( 'utf-8' , { ignoreBOM : true } )
449+ // Note that streaming decoder is stateful and cannot be reused
450+ const streamingDecoder = new TextDecoder ( 'utf-8' , { ignoreBOM : true } )
451+
449452for await ( const chunk of consumeBody ( this [ kState ] . body ) ) {
450453if ( ! isUint8Array ( chunk ) ) {
451454throw new TypeError ( 'Expected Uint8Array chunk' )
452455}
453- text += textDecoder . decode ( chunk , { stream : true } )
456+ text += streamingDecoder . decode ( chunk , { stream : true } )
454457}
455- text += textDecoder . decode ( )
458+ text += streamingDecoder . decode ( )
456459entries = new URLSearchParams ( text )
457460} catch ( err ) {
458461// istanbul ignore next: Unclear when new URLSearchParams can fail on a string.
@@ -567,7 +570,7 @@ function utf8DecodeBytes (buffer) {
567570
568571// 3. Process a queue with an instance of UTF-8’s
569572// decoder, ioQueue, output, and "replacement".
570- const output = new TextDecoder ( ) . decode ( buffer )
573+ const output = textDecoder . decode ( buffer )
571574
572575// 4. Return output.
573576return output
0 commit comments