Comparing version 4.4.3 to 4.4.4
@@ -99,2 +99,4 @@ 'use strict' | ||
module.exports.Request = require('./lib/fetch/request').Request | ||
module.exports.FormData = require('./lib/fetch/formdata').FormData | ||
module.exports.File = require('./lib/fetch/file').File | ||
} | ||
@@ -101,0 +103,0 @@ |
@@ -5,12 +5,21 @@ 'use strict' | ||
const { toWebReadable } = require('./util') | ||
const { FormData } = require('./formdata') | ||
const { kState } = require('./symbols') | ||
const { Blob } = require('buffer') | ||
const { Readable } = require('stream') | ||
const { NotSupportedError } = require('../core/errors') | ||
const { kBodyUsed } = require('../core/symbols') | ||
const assert = require('assert') | ||
const nodeUtil = require('util') | ||
const { NotSupportedError } = require('../core/errors') | ||
let ReadableStream | ||
async function * blobGen (blob) { | ||
if (blob.stream) { | ||
yield * blob.stream() | ||
} else { | ||
yield await blob.arrayBuffer() | ||
} | ||
} | ||
// https://fetch.spec.whatwg.org/#concept-bodyinit-extract | ||
@@ -24,13 +33,3 @@ function extractBody (object, keepalive = false) { | ||
// Otherwise, let stream be a new ReadableStream, and set up stream. | ||
let stream = object | ||
let controller | ||
if (!stream || !(stream instanceof ReadableStream)) { | ||
stream = new ReadableStream({ | ||
async start (c) { | ||
controller = c | ||
}, | ||
async pull () {}, | ||
async cancel (reason) {} | ||
}) | ||
} | ||
let stream = null | ||
@@ -76,2 +75,54 @@ // 2. Let action be null. | ||
source = new Uint8Array(object) | ||
} else if (object instanceof FormData) { | ||
const boundary = '----formdata-undici-' + Math.random() | ||
const prefix = `--${boundary}\r\nContent-Disposition: form-data; name="` | ||
/*! formdata-polyfill. MIT License. Jimmy Wärting <https://jimmy.warting.se/opensource> */ | ||
const escape = (str, filename) => | ||
(filename ? str : str.replace(/\r?\n|\r/g, '\r\n')) | ||
.replace(/\n/g, '%0A') | ||
.replace(/\r/g, '%0D') | ||
.replace(/"/g, '%22') | ||
// Set action to this step: run the multipart/form-data | ||
// encoding algorithm, with object’s entry list and UTF-8. | ||
action = async function * (object) { | ||
const enc = new TextEncoder() | ||
for (const [name, value] of object) { | ||
if (typeof value === 'string') { | ||
yield enc.encode( | ||
prefix + | ||
escape(name) + | ||
`"\r\n\r\n${value.replace(/\r(?!\n)|(?<!\r)\n/g, '\r\n')}\r\n` | ||
) | ||
} else { | ||
yield enc.encode( | ||
prefix + | ||
escape(name) + | ||
`"; filename="${escape(value.name, 1)}"\r\n` + | ||
`Content-Type: ${ | ||
value.type || 'application/octet-stream' | ||
}\r\n\r\n` | ||
) | ||
yield * blobGen(value) | ||
yield enc.encode('\r\n') | ||
} | ||
} | ||
yield enc.encode(`--${boundary}--`) | ||
} | ||
// Set source to object. | ||
source = object | ||
// Set length to unclear, see html/6424 for improving this. | ||
// TODO | ||
// Set Content-Type to `multipart/form-data; boundary=`, | ||
// followed by the multipart/form-data boundary string generated | ||
// by the multipart/form-data encoding algorithm. | ||
contentType = 'multipart/form-data; boundary=' + boundary | ||
} else if (object instanceof Blob) { | ||
@@ -81,8 +132,5 @@ // Blob | ||
// Set action to this step: read object. | ||
action = async (onNext, onError, onComplete) => { | ||
try { | ||
onNext(await object.arrayBuffer()) | ||
onComplete() | ||
} catch (err) { | ||
onError(err) | ||
action = async function * (object) { | ||
for await (const chunk of blobGen(object)) { | ||
yield chunk | ||
} | ||
@@ -111,3 +159,3 @@ } | ||
// If object is disturbed or locked, then throw a TypeError. | ||
if (util.isDisturbed(stream) || stream.locked) { | ||
if (util.isDisturbed(object) || object.locked) { | ||
throw new TypeError( | ||
@@ -120,2 +168,4 @@ 'Response body object should not be disturbed or locked' | ||
stream = toWebReadable(object) | ||
} else { | ||
stream = object | ||
} | ||
@@ -141,29 +191,39 @@ } else { | ||
// Run action. | ||
action( | ||
(bytes) => { | ||
// Whenever one or more bytes are available and stream is not errored, | ||
// enqueue a Uint8Array wrapping an ArrayBuffer containing the available | ||
// bytes into stream. | ||
if (!/state: 'errored'/.test(nodeUtil.inspect(stream))) { | ||
controller.enqueue(new Uint8Array(bytes)) | ||
let iterator | ||
stream = new ReadableStream({ | ||
async start () { | ||
iterator = action(object)[Symbol.asyncIterator]() | ||
}, | ||
async pull (controller) { | ||
const { value, done } = await iterator.next() | ||
if (done) { | ||
// When running action is done, close stream. | ||
queueMicrotask(() => { | ||
controller.close() | ||
}) | ||
} else { | ||
// Whenever one or more bytes are available and stream is not errored, | ||
// enqueue a Uint8Array wrapping an ArrayBuffer containing the available | ||
// bytes into stream. | ||
if (!/state: 'errored'/.test(nodeUtil.inspect(stream))) { | ||
controller.enqueue(new Uint8Array(value)) | ||
} | ||
} | ||
return controller.desiredSize > 0 | ||
}, | ||
(err) => { | ||
// TODO: Spec doesn't say anything about this? | ||
controller.error(err) | ||
}, | ||
() => { | ||
// When running action is done, close stream. | ||
async cancel (reason) { | ||
await iterator.return() | ||
} | ||
}) | ||
} else if (!stream) { | ||
// TODO: Spec doesn't say anything about this? | ||
stream = new ReadableStream({ | ||
async pull (controller) { | ||
controller.enqueue( | ||
typeof source === 'string' ? new TextEncoder().encode(source) : source | ||
) | ||
queueMicrotask(() => { | ||
// See https://github.com/nodejs/node/issues/39758 | ||
controller.close() | ||
}) | ||
} | ||
) | ||
} else if (controller) { | ||
// TODO: Spec doesn't say anything about this? | ||
controller.enqueue(source) | ||
queueMicrotask(() => { | ||
// See https://github.com/nodejs/node/issues/39758 | ||
controller.close() | ||
}) | ||
@@ -260,4 +320,29 @@ } | ||
async formData () { | ||
// TODO: Implement. | ||
throw new NotSupportedError('formData') | ||
const contentType = this.headers.get('Content-Type') | ||
// If mimeType’s essence is "multipart/form-data", then: | ||
if (/multipart\/form-data/.test(contentType)) { | ||
throw new NotSupportedError('multipart/form-data not supported') | ||
} else if (/application\/x-www-form-urlencoded/.test(contentType)) { | ||
// Otherwise, if mimeType’s essence is "application/x-www-form-urlencoded", then: | ||
// 1. Let entries be the result of parsing bytes. | ||
let entries | ||
try { | ||
entries = new URLSearchParams(await this.text()) | ||
} catch (err) { | ||
// 2. If entries is failure, then throw a TypeError. | ||
throw Object.assign(new TypeError(), { cause: err }) | ||
} | ||
// 3. Return a new FormData object whose entries are entries. | ||
const formData = new FormData() | ||
for (const [name, value] of entries) { | ||
formData.append(name, value) | ||
} | ||
return formData | ||
} else { | ||
// Otherwise, throw a TypeError. | ||
throw new TypeError() | ||
} | ||
} | ||
@@ -264,0 +349,0 @@ } |
@@ -199,3 +199,5 @@ // https://github.com/Ethan-Arrowood/undici-fetch | ||
if (args.length < 2) { | ||
throw new TypeError(`Failed to execute 'append' on 'Headers': 2 arguments required, but only ${args.length} present.`) | ||
throw new TypeError( | ||
`Failed to execute 'append' on 'Headers': 2 arguments required, but only ${args.length} present.` | ||
) | ||
} | ||
@@ -229,3 +231,5 @@ | ||
if (args.length < 1) { | ||
throw new TypeError(`Failed to execute 'delete' on 'Headers': 1 argument required, but only ${args.length} present.`) | ||
throw new TypeError( | ||
`Failed to execute 'delete' on 'Headers': 1 argument required, but only ${args.length} present.` | ||
) | ||
} | ||
@@ -259,3 +263,5 @@ | ||
if (args.length < 1) { | ||
throw new TypeError(`Failed to execute 'get' on 'Headers': 1 argument required, but only ${args.length} present.`) | ||
throw new TypeError( | ||
`Failed to execute 'get' on 'Headers': 1 argument required, but only ${args.length} present.` | ||
) | ||
} | ||
@@ -271,3 +277,5 @@ | ||
if (args.length < 1) { | ||
throw new TypeError(`Failed to execute 'has' on 'Headers': 1 argument required, but only ${args.length} present.`) | ||
throw new TypeError( | ||
`Failed to execute 'has' on 'Headers': 1 argument required, but only ${args.length} present.` | ||
) | ||
} | ||
@@ -283,3 +291,5 @@ | ||
if (args.length < 2) { | ||
throw new TypeError(`Failed to execute 'set' on 'Headers': 2 arguments required, but only ${args.length} present.`) | ||
throw new TypeError( | ||
`Failed to execute 'set' on 'Headers': 2 arguments required, but only ${args.length} present.` | ||
) | ||
} | ||
@@ -343,6 +353,10 @@ | ||
if (args.length < 1) { | ||
throw new TypeError(`Failed to execute 'forEach' on 'Headers': 1 argument required, but only ${args.length} present.`) | ||
throw new TypeError( | ||
`Failed to execute 'forEach' on 'Headers': 1 argument required, but only ${args.length} present.` | ||
) | ||
} | ||
if (typeof args[0] !== 'function') { | ||
throw new TypeError('Failed to execute \'forEach\' on \'Headers\': parameter 1 is not of type \'Function\'.') | ||
throw new TypeError( | ||
"Failed to execute 'forEach' on 'Headers': parameter 1 is not of type 'Function'." | ||
) | ||
} | ||
@@ -349,0 +363,0 @@ const callback = args[0] |
@@ -41,3 +41,5 @@ 'use strict' | ||
if (args.length < 1) { | ||
throw new TypeError(`Failed to execute 'redirect' on 'Response': 1 argument required, but only ${args.length} present.`) | ||
throw new TypeError( | ||
`Failed to execute 'redirect' on 'Response': 1 argument required, but only ${args.length} present.` | ||
) | ||
} | ||
@@ -56,3 +58,5 @@ | ||
} catch (err) { | ||
throw Object.assign(new TypeError('Failed to parse URL from ' + url), { cause: err }) | ||
throw Object.assign(new TypeError('Failed to parse URL from ' + url), { | ||
cause: err | ||
}) | ||
} | ||
@@ -59,0 +63,0 @@ |
@@ -327,4 +327,6 @@ 'use strict' | ||
class EnvironmentSettingsObject {} // dummy | ||
class HTMLFormElement {} // dummy | ||
module.exports = { | ||
HTMLFormElement, | ||
ServiceWorkerGlobalScope, | ||
@@ -331,0 +333,0 @@ Window, |
{ | ||
"name": "undici", | ||
"version": "4.4.3", | ||
"version": "4.4.4", | ||
"description": "An HTTP/1.1 client, written from scratch for Node.js", | ||
@@ -5,0 +5,0 @@ "homepage": "https://undici.nodejs.org", |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
592366
79
9323