First development of the deploy action (#6)
Some checks failed
Lint / pre-commit Linting (push) Has been cancelled

Deploy js code to an instance of screeps.
Some debugging tools are implemented.

Reviewed-on: #6
Co-authored-by: Philipp Horstenkamp <philipp@horstenkamp.de>
Co-committed-by: Philipp Horstenkamp <philipp@horstenkamp.de>
This commit is contained in:
2023-11-26 18:31:49 +01:00
committed by Philipp Horstenkamp
parent 0858cc69be
commit 6f5729c12a
1039 changed files with 228399 additions and 0 deletions

21
node_modules/undici/lib/fetch/LICENSE generated vendored Normal file
View File

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2020 Ethan Arrowood
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

605
node_modules/undici/lib/fetch/body.js generated vendored Normal file
View File

@ -0,0 +1,605 @@
'use strict'
const Busboy = require('@fastify/busboy')
const util = require('../core/util')
const {
ReadableStreamFrom,
isBlobLike,
isReadableStreamLike,
readableStreamClose,
createDeferredPromise,
fullyReadBody
} = require('./util')
const { FormData } = require('./formdata')
const { kState } = require('./symbols')
const { webidl } = require('./webidl')
const { DOMException, structuredClone } = require('./constants')
const { Blob, File: NativeFile } = require('buffer')
const { kBodyUsed } = require('../core/symbols')
const assert = require('assert')
const { isErrored } = require('../core/util')
const { isUint8Array, isArrayBuffer } = require('util/types')
const { File: UndiciFile } = require('./file')
const { parseMIMEType, serializeAMimeType } = require('./dataURL')
let ReadableStream = globalThis.ReadableStream
/** @type {globalThis['File']} */
const File = NativeFile ?? UndiciFile
const textEncoder = new TextEncoder()
const textDecoder = new TextDecoder()
// https://fetch.spec.whatwg.org/#concept-bodyinit-extract
function extractBody (object, keepalive = false) {
if (!ReadableStream) {
ReadableStream = require('stream/web').ReadableStream
}
// 1. Let stream be null.
let stream = null
// 2. If object is a ReadableStream object, then set stream to object.
if (object instanceof ReadableStream) {
stream = object
} else if (isBlobLike(object)) {
// 3. Otherwise, if object is a Blob object, set stream to the
// result of running objects get stream.
stream = object.stream()
} else {
// 4. Otherwise, set stream to a new ReadableStream object, and set
// up stream.
stream = new ReadableStream({
async pull (controller) {
controller.enqueue(
typeof source === 'string' ? textEncoder.encode(source) : source
)
queueMicrotask(() => readableStreamClose(controller))
},
start () {},
type: undefined
})
}
// 5. Assert: stream is a ReadableStream object.
assert(isReadableStreamLike(stream))
// 6. Let action be null.
let action = null
// 7. Let source be null.
let source = null
// 8. Let length be null.
let length = null
// 9. Let type be null.
let type = null
// 10. Switch on object:
if (typeof object === 'string') {
// Set source to the UTF-8 encoding of object.
// Note: setting source to a Uint8Array here breaks some mocking assumptions.
source = object
// Set type to `text/plain;charset=UTF-8`.
type = 'text/plain;charset=UTF-8'
} else if (object instanceof URLSearchParams) {
// URLSearchParams
// spec says to run application/x-www-form-urlencoded on body.list
// this is implemented in Node.js as apart of an URLSearchParams instance toString method
// See: https://github.com/nodejs/node/blob/e46c680bf2b211bbd52cf959ca17ee98c7f657f5/lib/internal/url.js#L490
// and https://github.com/nodejs/node/blob/e46c680bf2b211bbd52cf959ca17ee98c7f657f5/lib/internal/url.js#L1100
// Set source to the result of running the application/x-www-form-urlencoded serializer with objects list.
source = object.toString()
// Set type to `application/x-www-form-urlencoded;charset=UTF-8`.
type = 'application/x-www-form-urlencoded;charset=UTF-8'
} else if (isArrayBuffer(object)) {
// BufferSource/ArrayBuffer
// Set source to a copy of the bytes held by object.
source = new Uint8Array(object.slice())
} else if (ArrayBuffer.isView(object)) {
// BufferSource/ArrayBufferView
// Set source to a copy of the bytes held by object.
source = new Uint8Array(object.buffer.slice(object.byteOffset, object.byteOffset + object.byteLength))
} else if (util.isFormDataLike(object)) {
const boundary = `----formdata-undici-0${`${Math.floor(Math.random() * 1e11)}`.padStart(11, '0')}`
const prefix = `--${boundary}\r\nContent-Disposition: form-data`
/*! formdata-polyfill. MIT License. Jimmy Wärting <https://jimmy.warting.se/opensource> */
const escape = (str) =>
str.replace(/\n/g, '%0A').replace(/\r/g, '%0D').replace(/"/g, '%22')
const normalizeLinefeeds = (value) => value.replace(/\r?\n|\r/g, '\r\n')
// Set action to this step: run the multipart/form-data
// encoding algorithm, with objects entry list and UTF-8.
// - This ensures that the body is immutable and can't be changed afterwords
// - That the content-length is calculated in advance.
// - And that all parts are pre-encoded and ready to be sent.
const blobParts = []
const rn = new Uint8Array([13, 10]) // '\r\n'
length = 0
let hasUnknownSizeValue = false
for (const [name, value] of object) {
if (typeof value === 'string') {
const chunk = textEncoder.encode(prefix +
`; name="${escape(normalizeLinefeeds(name))}"` +
`\r\n\r\n${normalizeLinefeeds(value)}\r\n`)
blobParts.push(chunk)
length += chunk.byteLength
} else {
const chunk = textEncoder.encode(`${prefix}; name="${escape(normalizeLinefeeds(name))}"` +
(value.name ? `; filename="${escape(value.name)}"` : '') + '\r\n' +
`Content-Type: ${
value.type || 'application/octet-stream'
}\r\n\r\n`)
blobParts.push(chunk, value, rn)
if (typeof value.size === 'number') {
length += chunk.byteLength + value.size + rn.byteLength
} else {
hasUnknownSizeValue = true
}
}
}
const chunk = textEncoder.encode(`--${boundary}--`)
blobParts.push(chunk)
length += chunk.byteLength
if (hasUnknownSizeValue) {
length = null
}
// Set source to object.
source = object
action = async function * () {
for (const part of blobParts) {
if (part.stream) {
yield * part.stream()
} else {
yield part
}
}
}
// Set type to `multipart/form-data; boundary=`,
// followed by the multipart/form-data boundary string generated
// by the multipart/form-data encoding algorithm.
type = 'multipart/form-data; boundary=' + boundary
} else if (isBlobLike(object)) {
// Blob
// Set source to object.
source = object
// Set length to objects size.
length = object.size
// If objects type attribute is not the empty byte sequence, set
// type to its value.
if (object.type) {
type = object.type
}
} else if (typeof object[Symbol.asyncIterator] === 'function') {
// If keepalive is true, then throw a TypeError.
if (keepalive) {
throw new TypeError('keepalive')
}
// If object is disturbed or locked, then throw a TypeError.
if (util.isDisturbed(object) || object.locked) {
throw new TypeError(
'Response body object should not be disturbed or locked'
)
}
stream =
object instanceof ReadableStream ? object : ReadableStreamFrom(object)
}
// 11. If source is a byte sequence, then set action to a
// step that returns source and length to sources length.
if (typeof source === 'string' || util.isBuffer(source)) {
length = Buffer.byteLength(source)
}
// 12. If action is non-null, then run these steps in in parallel:
if (action != null) {
// Run action.
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 (!isErrored(stream)) {
controller.enqueue(new Uint8Array(value))
}
}
return controller.desiredSize > 0
},
async cancel (reason) {
await iterator.return()
},
type: undefined
})
}
// 13. Let body be a body whose stream is stream, source is source,
// and length is length.
const body = { stream, source, length }
// 14. Return (body, type).
return [body, type]
}
// https://fetch.spec.whatwg.org/#bodyinit-safely-extract
function safelyExtractBody (object, keepalive = false) {
if (!ReadableStream) {
// istanbul ignore next
ReadableStream = require('stream/web').ReadableStream
}
// To safely extract a body and a `Content-Type` value from
// a byte sequence or BodyInit object object, run these steps:
// 1. If object is a ReadableStream object, then:
if (object instanceof ReadableStream) {
// Assert: object is neither disturbed nor locked.
// istanbul ignore next
assert(!util.isDisturbed(object), 'The body has already been consumed.')
// istanbul ignore next
assert(!object.locked, 'The stream is locked.')
}
// 2. Return the results of extracting object.
return extractBody(object, keepalive)
}
function cloneBody (body) {
// To clone a body body, run these steps:
// https://fetch.spec.whatwg.org/#concept-body-clone
// 1. Let « out1, out2 » be the result of teeing bodys stream.
const [out1, out2] = body.stream.tee()
const out2Clone = structuredClone(out2, { transfer: [out2] })
// This, for whatever reasons, unrefs out2Clone which allows
// the process to exit by itself.
const [, finalClone] = out2Clone.tee()
// 2. Set bodys stream to out1.
body.stream = out1
// 3. Return a body whose stream is out2 and other members are copied from body.
return {
stream: finalClone,
length: body.length,
source: body.source
}
}
async function * consumeBody (body) {
if (body) {
if (isUint8Array(body)) {
yield body
} else {
const stream = body.stream
if (util.isDisturbed(stream)) {
throw new TypeError('The body has already been consumed.')
}
if (stream.locked) {
throw new TypeError('The stream is locked.')
}
// Compat.
stream[kBodyUsed] = true
yield * stream
}
}
}
function throwIfAborted (state) {
if (state.aborted) {
throw new DOMException('The operation was aborted.', 'AbortError')
}
}
function bodyMixinMethods (instance) {
const methods = {
blob () {
// The blob() method steps are to return the result of
// running consume body with this and the following step
// given a byte sequence bytes: return a Blob whose
// contents are bytes and whose type attribute is thiss
// MIME type.
return specConsumeBody(this, (bytes) => {
let mimeType = bodyMimeType(this)
if (mimeType === 'failure') {
mimeType = ''
} else if (mimeType) {
mimeType = serializeAMimeType(mimeType)
}
// Return a Blob whose contents are bytes and type attribute
// is mimeType.
return new Blob([bytes], { type: mimeType })
}, instance)
},
arrayBuffer () {
// The arrayBuffer() method steps are to return the result
// of running consume body with this and the following step
// given a byte sequence bytes: return a new ArrayBuffer
// whose contents are bytes.
return specConsumeBody(this, (bytes) => {
return new Uint8Array(bytes).buffer
}, instance)
},
text () {
// The text() method steps are to return the result of running
// consume body with this and UTF-8 decode.
return specConsumeBody(this, utf8DecodeBytes, instance)
},
json () {
// The json() method steps are to return the result of running
// consume body with this and parse JSON from bytes.
return specConsumeBody(this, parseJSONFromBytes, instance)
},
async formData () {
webidl.brandCheck(this, instance)
throwIfAborted(this[kState])
const contentType = this.headers.get('Content-Type')
// If mimeTypes essence is "multipart/form-data", then:
if (/multipart\/form-data/.test(contentType)) {
const headers = {}
for (const [key, value] of this.headers) headers[key.toLowerCase()] = value
const responseFormData = new FormData()
let busboy
try {
busboy = new Busboy({
headers,
preservePath: true
})
} catch (err) {
throw new DOMException(`${err}`, 'AbortError')
}
busboy.on('field', (name, value) => {
responseFormData.append(name, value)
})
busboy.on('file', (name, value, filename, encoding, mimeType) => {
const chunks = []
if (encoding === 'base64' || encoding.toLowerCase() === 'base64') {
let base64chunk = ''
value.on('data', (chunk) => {
base64chunk += chunk.toString().replace(/[\r\n]/gm, '')
const end = base64chunk.length - base64chunk.length % 4
chunks.push(Buffer.from(base64chunk.slice(0, end), 'base64'))
base64chunk = base64chunk.slice(end)
})
value.on('end', () => {
chunks.push(Buffer.from(base64chunk, 'base64'))
responseFormData.append(name, new File(chunks, filename, { type: mimeType }))
})
} else {
value.on('data', (chunk) => {
chunks.push(chunk)
})
value.on('end', () => {
responseFormData.append(name, new File(chunks, filename, { type: mimeType }))
})
}
})
const busboyResolve = new Promise((resolve, reject) => {
busboy.on('finish', resolve)
busboy.on('error', (err) => reject(new TypeError(err)))
})
if (this.body !== null) for await (const chunk of consumeBody(this[kState].body)) busboy.write(chunk)
busboy.end()
await busboyResolve
return responseFormData
} else if (/application\/x-www-form-urlencoded/.test(contentType)) {
// Otherwise, if mimeTypes essence is "application/x-www-form-urlencoded", then:
// 1. Let entries be the result of parsing bytes.
let entries
try {
let text = ''
// application/x-www-form-urlencoded parser will keep the BOM.
// https://url.spec.whatwg.org/#concept-urlencoded-parser
// Note that streaming decoder is stateful and cannot be reused
const streamingDecoder = new TextDecoder('utf-8', { ignoreBOM: true })
for await (const chunk of consumeBody(this[kState].body)) {
if (!isUint8Array(chunk)) {
throw new TypeError('Expected Uint8Array chunk')
}
text += streamingDecoder.decode(chunk, { stream: true })
}
text += streamingDecoder.decode()
entries = new URLSearchParams(text)
} catch (err) {
// istanbul ignore next: Unclear when new URLSearchParams can fail on a string.
// 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 {
// Wait a tick before checking if the request has been aborted.
// Otherwise, a TypeError can be thrown when an AbortError should.
await Promise.resolve()
throwIfAborted(this[kState])
// Otherwise, throw a TypeError.
throw webidl.errors.exception({
header: `${instance.name}.formData`,
message: 'Could not parse content as FormData.'
})
}
}
}
return methods
}
function mixinBody (prototype) {
Object.assign(prototype.prototype, bodyMixinMethods(prototype))
}
/**
* @see https://fetch.spec.whatwg.org/#concept-body-consume-body
* @param {Response|Request} object
* @param {(value: unknown) => unknown} convertBytesToJSValue
* @param {Response|Request} instance
*/
async function specConsumeBody (object, convertBytesToJSValue, instance) {
webidl.brandCheck(object, instance)
throwIfAborted(object[kState])
// 1. If object is unusable, then return a promise rejected
// with a TypeError.
if (bodyUnusable(object[kState].body)) {
throw new TypeError('Body is unusable')
}
// 2. Let promise be a new promise.
const promise = createDeferredPromise()
// 3. Let errorSteps given error be to reject promise with error.
const errorSteps = (error) => promise.reject(error)
// 4. Let successSteps given a byte sequence data be to resolve
// promise with the result of running convertBytesToJSValue
// with data. If that threw an exception, then run errorSteps
// with that exception.
const successSteps = (data) => {
try {
promise.resolve(convertBytesToJSValue(data))
} catch (e) {
errorSteps(e)
}
}
// 5. If objects body is null, then run successSteps with an
// empty byte sequence.
if (object[kState].body == null) {
successSteps(new Uint8Array())
return promise.promise
}
// 6. Otherwise, fully read objects body given successSteps,
// errorSteps, and objects relevant global object.
await fullyReadBody(object[kState].body, successSteps, errorSteps)
// 7. Return promise.
return promise.promise
}
// https://fetch.spec.whatwg.org/#body-unusable
function bodyUnusable (body) {
// An object including the Body interface mixin is
// said to be unusable if its body is non-null and
// its bodys stream is disturbed or locked.
return body != null && (body.stream.locked || util.isDisturbed(body.stream))
}
/**
* @see https://encoding.spec.whatwg.org/#utf-8-decode
* @param {Buffer} buffer
*/
function utf8DecodeBytes (buffer) {
if (buffer.length === 0) {
return ''
}
// 1. Let buffer be the result of peeking three bytes from
// ioQueue, converted to a byte sequence.
// 2. If buffer is 0xEF 0xBB 0xBF, then read three
// bytes from ioQueue. (Do nothing with those bytes.)
if (buffer[0] === 0xEF && buffer[1] === 0xBB && buffer[2] === 0xBF) {
buffer = buffer.subarray(3)
}
// 3. Process a queue with an instance of UTF-8s
// decoder, ioQueue, output, and "replacement".
const output = textDecoder.decode(buffer)
// 4. Return output.
return output
}
/**
* @see https://infra.spec.whatwg.org/#parse-json-bytes-to-a-javascript-value
* @param {Uint8Array} bytes
*/
function parseJSONFromBytes (bytes) {
return JSON.parse(utf8DecodeBytes(bytes))
}
/**
* @see https://fetch.spec.whatwg.org/#concept-body-mime-type
* @param {import('./response').Response|import('./request').Request} object
*/
function bodyMimeType (object) {
const { headersList } = object[kState]
const contentType = headersList.get('content-type')
if (contentType === null) {
return 'failure'
}
return parseMIMEType(contentType)
}
module.exports = {
extractBody,
safelyExtractBody,
cloneBody,
mixinBody
}

151
node_modules/undici/lib/fetch/constants.js generated vendored Normal file
View File

@ -0,0 +1,151 @@
'use strict'
const { MessageChannel, receiveMessageOnPort } = require('worker_threads')
const corsSafeListedMethods = ['GET', 'HEAD', 'POST']
const corsSafeListedMethodsSet = new Set(corsSafeListedMethods)
const nullBodyStatus = [101, 204, 205, 304]
const redirectStatus = [301, 302, 303, 307, 308]
const redirectStatusSet = new Set(redirectStatus)
// https://fetch.spec.whatwg.org/#block-bad-port
const badPorts = [
'1', '7', '9', '11', '13', '15', '17', '19', '20', '21', '22', '23', '25', '37', '42', '43', '53', '69', '77', '79',
'87', '95', '101', '102', '103', '104', '109', '110', '111', '113', '115', '117', '119', '123', '135', '137',
'139', '143', '161', '179', '389', '427', '465', '512', '513', '514', '515', '526', '530', '531', '532',
'540', '548', '554', '556', '563', '587', '601', '636', '989', '990', '993', '995', '1719', '1720', '1723',
'2049', '3659', '4045', '5060', '5061', '6000', '6566', '6665', '6666', '6667', '6668', '6669', '6697',
'10080'
]
const badPortsSet = new Set(badPorts)
// https://w3c.github.io/webappsec-referrer-policy/#referrer-policies
const referrerPolicy = [
'',
'no-referrer',
'no-referrer-when-downgrade',
'same-origin',
'origin',
'strict-origin',
'origin-when-cross-origin',
'strict-origin-when-cross-origin',
'unsafe-url'
]
const referrerPolicySet = new Set(referrerPolicy)
const requestRedirect = ['follow', 'manual', 'error']
const safeMethods = ['GET', 'HEAD', 'OPTIONS', 'TRACE']
const safeMethodsSet = new Set(safeMethods)
const requestMode = ['navigate', 'same-origin', 'no-cors', 'cors']
const requestCredentials = ['omit', 'same-origin', 'include']
const requestCache = [
'default',
'no-store',
'reload',
'no-cache',
'force-cache',
'only-if-cached'
]
// https://fetch.spec.whatwg.org/#request-body-header-name
const requestBodyHeader = [
'content-encoding',
'content-language',
'content-location',
'content-type',
// See https://github.com/nodejs/undici/issues/2021
// 'Content-Length' is a forbidden header name, which is typically
// removed in the Headers implementation. However, undici doesn't
// filter out headers, so we add it here.
'content-length'
]
// https://fetch.spec.whatwg.org/#enumdef-requestduplex
const requestDuplex = [
'half'
]
// http://fetch.spec.whatwg.org/#forbidden-method
const forbiddenMethods = ['CONNECT', 'TRACE', 'TRACK']
const forbiddenMethodsSet = new Set(forbiddenMethods)
const subresource = [
'audio',
'audioworklet',
'font',
'image',
'manifest',
'paintworklet',
'script',
'style',
'track',
'video',
'xslt',
''
]
const subresourceSet = new Set(subresource)
/** @type {globalThis['DOMException']} */
const DOMException = globalThis.DOMException ?? (() => {
// DOMException was only made a global in Node v17.0.0,
// but fetch supports >= v16.8.
try {
atob('~')
} catch (err) {
return Object.getPrototypeOf(err).constructor
}
})()
let channel
/** @type {globalThis['structuredClone']} */
const structuredClone =
globalThis.structuredClone ??
// https://github.com/nodejs/node/blob/b27ae24dcc4251bad726d9d84baf678d1f707fed/lib/internal/structured_clone.js
// structuredClone was added in v17.0.0, but fetch supports v16.8
function structuredClone (value, options = undefined) {
if (arguments.length === 0) {
throw new TypeError('missing argument')
}
if (!channel) {
channel = new MessageChannel()
}
channel.port1.unref()
channel.port2.unref()
channel.port1.postMessage(value, options?.transfer)
return receiveMessageOnPort(channel.port2).message
}
module.exports = {
DOMException,
structuredClone,
subresource,
forbiddenMethods,
requestBodyHeader,
referrerPolicy,
requestRedirect,
requestMode,
requestCredentials,
requestCache,
redirectStatus,
corsSafeListedMethods,
nullBodyStatus,
safeMethods,
badPorts,
requestDuplex,
subresourceSet,
badPortsSet,
redirectStatusSet,
corsSafeListedMethodsSet,
safeMethodsSet,
forbiddenMethodsSet,
referrerPolicySet
}

630
node_modules/undici/lib/fetch/dataURL.js generated vendored Normal file
View File

@ -0,0 +1,630 @@
const assert = require('assert')
const { atob } = require('buffer')
const { isomorphicDecode } = require('./util')
const encoder = new TextEncoder()
/**
* @see https://mimesniff.spec.whatwg.org/#http-token-code-point
*/
const HTTP_TOKEN_CODEPOINTS = /^[!#$%&'*+-.^_|~A-Za-z0-9]+$/
const HTTP_WHITESPACE_REGEX = /(\u000A|\u000D|\u0009|\u0020)/ // eslint-disable-line
/**
* @see https://mimesniff.spec.whatwg.org/#http-quoted-string-token-code-point
*/
const HTTP_QUOTED_STRING_TOKENS = /[\u0009|\u0020-\u007E|\u0080-\u00FF]/ // eslint-disable-line
// https://fetch.spec.whatwg.org/#data-url-processor
/** @param {URL} dataURL */
function dataURLProcessor (dataURL) {
// 1. Assert: dataURLs scheme is "data".
assert(dataURL.protocol === 'data:')
// 2. Let input be the result of running the URL
// serializer on dataURL with exclude fragment
// set to true.
let input = URLSerializer(dataURL, true)
// 3. Remove the leading "data:" string from input.
input = input.slice(5)
// 4. Let position point at the start of input.
const position = { position: 0 }
// 5. Let mimeType be the result of collecting a
// sequence of code points that are not equal
// to U+002C (,), given position.
let mimeType = collectASequenceOfCodePointsFast(
',',
input,
position
)
// 6. Strip leading and trailing ASCII whitespace
// from mimeType.
// Undici implementation note: we need to store the
// length because if the mimetype has spaces removed,
// the wrong amount will be sliced from the input in
// step #9
const mimeTypeLength = mimeType.length
mimeType = removeASCIIWhitespace(mimeType, true, true)
// 7. If position is past the end of input, then
// return failure
if (position.position >= input.length) {
return 'failure'
}
// 8. Advance position by 1.
position.position++
// 9. Let encodedBody be the remainder of input.
const encodedBody = input.slice(mimeTypeLength + 1)
// 10. Let body be the percent-decoding of encodedBody.
let body = stringPercentDecode(encodedBody)
// 11. If mimeType ends with U+003B (;), followed by
// zero or more U+0020 SPACE, followed by an ASCII
// case-insensitive match for "base64", then:
if (/;(\u0020){0,}base64$/i.test(mimeType)) {
// 1. Let stringBody be the isomorphic decode of body.
const stringBody = isomorphicDecode(body)
// 2. Set body to the forgiving-base64 decode of
// stringBody.
body = forgivingBase64(stringBody)
// 3. If body is failure, then return failure.
if (body === 'failure') {
return 'failure'
}
// 4. Remove the last 6 code points from mimeType.
mimeType = mimeType.slice(0, -6)
// 5. Remove trailing U+0020 SPACE code points from mimeType,
// if any.
mimeType = mimeType.replace(/(\u0020)+$/, '')
// 6. Remove the last U+003B (;) code point from mimeType.
mimeType = mimeType.slice(0, -1)
}
// 12. If mimeType starts with U+003B (;), then prepend
// "text/plain" to mimeType.
if (mimeType.startsWith(';')) {
mimeType = 'text/plain' + mimeType
}
// 13. Let mimeTypeRecord be the result of parsing
// mimeType.
let mimeTypeRecord = parseMIMEType(mimeType)
// 14. If mimeTypeRecord is failure, then set
// mimeTypeRecord to text/plain;charset=US-ASCII.
if (mimeTypeRecord === 'failure') {
mimeTypeRecord = parseMIMEType('text/plain;charset=US-ASCII')
}
// 15. Return a new data: URL struct whose MIME
// type is mimeTypeRecord and body is body.
// https://fetch.spec.whatwg.org/#data-url-struct
return { mimeType: mimeTypeRecord, body }
}
// https://url.spec.whatwg.org/#concept-url-serializer
/**
* @param {URL} url
* @param {boolean} excludeFragment
*/
function URLSerializer (url, excludeFragment = false) {
const href = url.href
if (!excludeFragment) {
return href
}
const hash = href.lastIndexOf('#')
if (hash === -1) {
return href
}
return href.slice(0, hash)
}
// https://infra.spec.whatwg.org/#collect-a-sequence-of-code-points
/**
* @param {(char: string) => boolean} condition
* @param {string} input
* @param {{ position: number }} position
*/
function collectASequenceOfCodePoints (condition, input, position) {
// 1. Let result be the empty string.
let result = ''
// 2. While position doesnt point past the end of input and the
// code point at position within input meets the condition condition:
while (position.position < input.length && condition(input[position.position])) {
// 1. Append that code point to the end of result.
result += input[position.position]
// 2. Advance position by 1.
position.position++
}
// 3. Return result.
return result
}
/**
* A faster collectASequenceOfCodePoints that only works when comparing a single character.
* @param {string} char
* @param {string} input
* @param {{ position: number }} position
*/
function collectASequenceOfCodePointsFast (char, input, position) {
const idx = input.indexOf(char, position.position)
const start = position.position
if (idx === -1) {
position.position = input.length
return input.slice(start)
}
position.position = idx
return input.slice(start, position.position)
}
// https://url.spec.whatwg.org/#string-percent-decode
/** @param {string} input */
function stringPercentDecode (input) {
// 1. Let bytes be the UTF-8 encoding of input.
const bytes = encoder.encode(input)
// 2. Return the percent-decoding of bytes.
return percentDecode(bytes)
}
// https://url.spec.whatwg.org/#percent-decode
/** @param {Uint8Array} input */
function percentDecode (input) {
// 1. Let output be an empty byte sequence.
/** @type {number[]} */
const output = []
// 2. For each byte byte in input:
for (let i = 0; i < input.length; i++) {
const byte = input[i]
// 1. If byte is not 0x25 (%), then append byte to output.
if (byte !== 0x25) {
output.push(byte)
// 2. Otherwise, if byte is 0x25 (%) and the next two bytes
// after byte in input are not in the ranges
// 0x30 (0) to 0x39 (9), 0x41 (A) to 0x46 (F),
// and 0x61 (a) to 0x66 (f), all inclusive, append byte
// to output.
} else if (
byte === 0x25 &&
!/^[0-9A-Fa-f]{2}$/i.test(String.fromCharCode(input[i + 1], input[i + 2]))
) {
output.push(0x25)
// 3. Otherwise:
} else {
// 1. Let bytePoint be the two bytes after byte in input,
// decoded, and then interpreted as hexadecimal number.
const nextTwoBytes = String.fromCharCode(input[i + 1], input[i + 2])
const bytePoint = Number.parseInt(nextTwoBytes, 16)
// 2. Append a byte whose value is bytePoint to output.
output.push(bytePoint)
// 3. Skip the next two bytes in input.
i += 2
}
}
// 3. Return output.
return Uint8Array.from(output)
}
// https://mimesniff.spec.whatwg.org/#parse-a-mime-type
/** @param {string} input */
function parseMIMEType (input) {
// 1. Remove any leading and trailing HTTP whitespace
// from input.
input = removeHTTPWhitespace(input, true, true)
// 2. Let position be a position variable for input,
// initially pointing at the start of input.
const position = { position: 0 }
// 3. Let type be the result of collecting a sequence
// of code points that are not U+002F (/) from
// input, given position.
const type = collectASequenceOfCodePointsFast(
'/',
input,
position
)
// 4. If type is the empty string or does not solely
// contain HTTP token code points, then return failure.
// https://mimesniff.spec.whatwg.org/#http-token-code-point
if (type.length === 0 || !HTTP_TOKEN_CODEPOINTS.test(type)) {
return 'failure'
}
// 5. If position is past the end of input, then return
// failure
if (position.position > input.length) {
return 'failure'
}
// 6. Advance position by 1. (This skips past U+002F (/).)
position.position++
// 7. Let subtype be the result of collecting a sequence of
// code points that are not U+003B (;) from input, given
// position.
let subtype = collectASequenceOfCodePointsFast(
';',
input,
position
)
// 8. Remove any trailing HTTP whitespace from subtype.
subtype = removeHTTPWhitespace(subtype, false, true)
// 9. If subtype is the empty string or does not solely
// contain HTTP token code points, then return failure.
if (subtype.length === 0 || !HTTP_TOKEN_CODEPOINTS.test(subtype)) {
return 'failure'
}
const typeLowercase = type.toLowerCase()
const subtypeLowercase = subtype.toLowerCase()
// 10. Let mimeType be a new MIME type record whose type
// is type, in ASCII lowercase, and subtype is subtype,
// in ASCII lowercase.
// https://mimesniff.spec.whatwg.org/#mime-type
const mimeType = {
type: typeLowercase,
subtype: subtypeLowercase,
/** @type {Map<string, string>} */
parameters: new Map(),
// https://mimesniff.spec.whatwg.org/#mime-type-essence
essence: `${typeLowercase}/${subtypeLowercase}`
}
// 11. While position is not past the end of input:
while (position.position < input.length) {
// 1. Advance position by 1. (This skips past U+003B (;).)
position.position++
// 2. Collect a sequence of code points that are HTTP
// whitespace from input given position.
collectASequenceOfCodePoints(
// https://fetch.spec.whatwg.org/#http-whitespace
char => HTTP_WHITESPACE_REGEX.test(char),
input,
position
)
// 3. Let parameterName be the result of collecting a
// sequence of code points that are not U+003B (;)
// or U+003D (=) from input, given position.
let parameterName = collectASequenceOfCodePoints(
(char) => char !== ';' && char !== '=',
input,
position
)
// 4. Set parameterName to parameterName, in ASCII
// lowercase.
parameterName = parameterName.toLowerCase()
// 5. If position is not past the end of input, then:
if (position.position < input.length) {
// 1. If the code point at position within input is
// U+003B (;), then continue.
if (input[position.position] === ';') {
continue
}
// 2. Advance position by 1. (This skips past U+003D (=).)
position.position++
}
// 6. If position is past the end of input, then break.
if (position.position > input.length) {
break
}
// 7. Let parameterValue be null.
let parameterValue = null
// 8. If the code point at position within input is
// U+0022 ("), then:
if (input[position.position] === '"') {
// 1. Set parameterValue to the result of collecting
// an HTTP quoted string from input, given position
// and the extract-value flag.
parameterValue = collectAnHTTPQuotedString(input, position, true)
// 2. Collect a sequence of code points that are not
// U+003B (;) from input, given position.
collectASequenceOfCodePointsFast(
';',
input,
position
)
// 9. Otherwise:
} else {
// 1. Set parameterValue to the result of collecting
// a sequence of code points that are not U+003B (;)
// from input, given position.
parameterValue = collectASequenceOfCodePointsFast(
';',
input,
position
)
// 2. Remove any trailing HTTP whitespace from parameterValue.
parameterValue = removeHTTPWhitespace(parameterValue, false, true)
// 3. If parameterValue is the empty string, then continue.
if (parameterValue.length === 0) {
continue
}
}
// 10. If all of the following are true
// - parameterName is not the empty string
// - parameterName solely contains HTTP token code points
// - parameterValue solely contains HTTP quoted-string token code points
// - mimeTypes parameters[parameterName] does not exist
// then set mimeTypes parameters[parameterName] to parameterValue.
if (
parameterName.length !== 0 &&
HTTP_TOKEN_CODEPOINTS.test(parameterName) &&
(parameterValue.length === 0 || HTTP_QUOTED_STRING_TOKENS.test(parameterValue)) &&
!mimeType.parameters.has(parameterName)
) {
mimeType.parameters.set(parameterName, parameterValue)
}
}
// 12. Return mimeType.
return mimeType
}
// https://infra.spec.whatwg.org/#forgiving-base64-decode
/** @param {string} data */
function forgivingBase64 (data) {
// 1. Remove all ASCII whitespace from data.
data = data.replace(/[\u0009\u000A\u000C\u000D\u0020]/g, '') // eslint-disable-line
// 2. If datas code point length divides by 4 leaving
// no remainder, then:
if (data.length % 4 === 0) {
// 1. If data ends with one or two U+003D (=) code points,
// then remove them from data.
data = data.replace(/=?=$/, '')
}
// 3. If datas code point length divides by 4 leaving
// a remainder of 1, then return failure.
if (data.length % 4 === 1) {
return 'failure'
}
// 4. If data contains a code point that is not one of
// U+002B (+)
// U+002F (/)
// ASCII alphanumeric
// then return failure.
if (/[^+/0-9A-Za-z]/.test(data)) {
return 'failure'
}
const binary = atob(data)
const bytes = new Uint8Array(binary.length)
for (let byte = 0; byte < binary.length; byte++) {
bytes[byte] = binary.charCodeAt(byte)
}
return bytes
}
// https://fetch.spec.whatwg.org/#collect-an-http-quoted-string
// tests: https://fetch.spec.whatwg.org/#example-http-quoted-string
/**
* @param {string} input
* @param {{ position: number }} position
* @param {boolean?} extractValue
*/
function collectAnHTTPQuotedString (input, position, extractValue) {
// 1. Let positionStart be position.
const positionStart = position.position
// 2. Let value be the empty string.
let value = ''
// 3. Assert: the code point at position within input
// is U+0022 (").
assert(input[position.position] === '"')
// 4. Advance position by 1.
position.position++
// 5. While true:
while (true) {
// 1. Append the result of collecting a sequence of code points
// that are not U+0022 (") or U+005C (\) from input, given
// position, to value.
value += collectASequenceOfCodePoints(
(char) => char !== '"' && char !== '\\',
input,
position
)
// 2. If position is past the end of input, then break.
if (position.position >= input.length) {
break
}
// 3. Let quoteOrBackslash be the code point at position within
// input.
const quoteOrBackslash = input[position.position]
// 4. Advance position by 1.
position.position++
// 5. If quoteOrBackslash is U+005C (\), then:
if (quoteOrBackslash === '\\') {
// 1. If position is past the end of input, then append
// U+005C (\) to value and break.
if (position.position >= input.length) {
value += '\\'
break
}
// 2. Append the code point at position within input to value.
value += input[position.position]
// 3. Advance position by 1.
position.position++
// 6. Otherwise:
} else {
// 1. Assert: quoteOrBackslash is U+0022 (").
assert(quoteOrBackslash === '"')
// 2. Break.
break
}
}
// 6. If the extract-value flag is set, then return value.
if (extractValue) {
return value
}
// 7. Return the code points from positionStart to position,
// inclusive, within input.
return input.slice(positionStart, position.position)
}
/**
* @see https://mimesniff.spec.whatwg.org/#serialize-a-mime-type
*/
function serializeAMimeType (mimeType) {
assert(mimeType !== 'failure')
const { parameters, essence } = mimeType
// 1. Let serialization be the concatenation of mimeTypes
// type, U+002F (/), and mimeTypes subtype.
let serialization = essence
// 2. For each name → value of mimeTypes parameters:
for (let [name, value] of parameters.entries()) {
// 1. Append U+003B (;) to serialization.
serialization += ';'
// 2. Append name to serialization.
serialization += name
// 3. Append U+003D (=) to serialization.
serialization += '='
// 4. If value does not solely contain HTTP token code
// points or value is the empty string, then:
if (!HTTP_TOKEN_CODEPOINTS.test(value)) {
// 1. Precede each occurence of U+0022 (") or
// U+005C (\) in value with U+005C (\).
value = value.replace(/(\\|")/g, '\\$1')
// 2. Prepend U+0022 (") to value.
value = '"' + value
// 3. Append U+0022 (") to value.
value += '"'
}
// 5. Append value to serialization.
serialization += value
}
// 3. Return serialization.
return serialization
}
/**
* @see https://fetch.spec.whatwg.org/#http-whitespace
* @param {string} char
*/
function isHTTPWhiteSpace (char) {
return char === '\r' || char === '\n' || char === '\t' || char === ' '
}
/**
* @see https://fetch.spec.whatwg.org/#http-whitespace
* @param {string} str
*/
function removeHTTPWhitespace (str, leading = true, trailing = true) {
let lead = 0
let trail = str.length - 1
if (leading) {
for (; lead < str.length && isHTTPWhiteSpace(str[lead]); lead++);
}
if (trailing) {
for (; trail > 0 && isHTTPWhiteSpace(str[trail]); trail--);
}
return str.slice(lead, trail + 1)
}
/**
* @see https://infra.spec.whatwg.org/#ascii-whitespace
* @param {string} char
*/
function isASCIIWhitespace (char) {
return char === '\r' || char === '\n' || char === '\t' || char === '\f' || char === ' '
}
/**
* @see https://infra.spec.whatwg.org/#strip-leading-and-trailing-ascii-whitespace
*/
function removeASCIIWhitespace (str, leading = true, trailing = true) {
let lead = 0
let trail = str.length - 1
if (leading) {
for (; lead < str.length && isASCIIWhitespace(str[lead]); lead++);
}
if (trailing) {
for (; trail > 0 && isASCIIWhitespace(str[trail]); trail--);
}
return str.slice(lead, trail + 1)
}
module.exports = {
dataURLProcessor,
URLSerializer,
collectASequenceOfCodePoints,
collectASequenceOfCodePointsFast,
stringPercentDecode,
parseMIMEType,
collectAnHTTPQuotedString,
serializeAMimeType
}

344
node_modules/undici/lib/fetch/file.js generated vendored Normal file
View File

@ -0,0 +1,344 @@
'use strict'
const { Blob, File: NativeFile } = require('buffer')
const { types } = require('util')
const { kState } = require('./symbols')
const { isBlobLike } = require('./util')
const { webidl } = require('./webidl')
const { parseMIMEType, serializeAMimeType } = require('./dataURL')
const { kEnumerableProperty } = require('../core/util')
const encoder = new TextEncoder()
class File extends Blob {
constructor (fileBits, fileName, options = {}) {
// The File constructor is invoked with two or three parameters, depending
// on whether the optional dictionary parameter is used. When the File()
// constructor is invoked, user agents must run the following steps:
webidl.argumentLengthCheck(arguments, 2, { header: 'File constructor' })
fileBits = webidl.converters['sequence<BlobPart>'](fileBits)
fileName = webidl.converters.USVString(fileName)
options = webidl.converters.FilePropertyBag(options)
// 1. Let bytes be the result of processing blob parts given fileBits and
// options.
// Note: Blob handles this for us
// 2. Let n be the fileName argument to the constructor.
const n = fileName
// 3. Process FilePropertyBag dictionary argument by running the following
// substeps:
// 1. If the type member is provided and is not the empty string, let t
// be set to the type dictionary member. If t contains any characters
// outside the range U+0020 to U+007E, then set t to the empty string
// and return from these substeps.
// 2. Convert every character in t to ASCII lowercase.
let t = options.type
let d
// eslint-disable-next-line no-labels
substep: {
if (t) {
t = parseMIMEType(t)
if (t === 'failure') {
t = ''
// eslint-disable-next-line no-labels
break substep
}
t = serializeAMimeType(t).toLowerCase()
}
// 3. If the lastModified member is provided, let d be set to the
// lastModified dictionary member. If it is not provided, set d to the
// current date and time represented as the number of milliseconds since
// the Unix Epoch (which is the equivalent of Date.now() [ECMA-262]).
d = options.lastModified
}
// 4. Return a new File object F such that:
// F refers to the bytes byte sequence.
// F.size is set to the number of total bytes in bytes.
// F.name is set to n.
// F.type is set to t.
// F.lastModified is set to d.
super(processBlobParts(fileBits, options), { type: t })
this[kState] = {
name: n,
lastModified: d,
type: t
}
}
get name () {
webidl.brandCheck(this, File)
return this[kState].name
}
get lastModified () {
webidl.brandCheck(this, File)
return this[kState].lastModified
}
get type () {
webidl.brandCheck(this, File)
return this[kState].type
}
}
class FileLike {
constructor (blobLike, fileName, options = {}) {
// TODO: argument idl type check
// The File constructor is invoked with two or three parameters, depending
// on whether the optional dictionary parameter is used. When the File()
// constructor is invoked, user agents must run the following steps:
// 1. Let bytes be the result of processing blob parts given fileBits and
// options.
// 2. Let n be the fileName argument to the constructor.
const n = fileName
// 3. Process FilePropertyBag dictionary argument by running the following
// substeps:
// 1. If the type member is provided and is not the empty string, let t
// be set to the type dictionary member. If t contains any characters
// outside the range U+0020 to U+007E, then set t to the empty string
// and return from these substeps.
// TODO
const t = options.type
// 2. Convert every character in t to ASCII lowercase.
// TODO
// 3. If the lastModified member is provided, let d be set to the
// lastModified dictionary member. If it is not provided, set d to the
// current date and time represented as the number of milliseconds since
// the Unix Epoch (which is the equivalent of Date.now() [ECMA-262]).
const d = options.lastModified ?? Date.now()
// 4. Return a new File object F such that:
// F refers to the bytes byte sequence.
// F.size is set to the number of total bytes in bytes.
// F.name is set to n.
// F.type is set to t.
// F.lastModified is set to d.
this[kState] = {
blobLike,
name: n,
type: t,
lastModified: d
}
}
stream (...args) {
webidl.brandCheck(this, FileLike)
return this[kState].blobLike.stream(...args)
}
arrayBuffer (...args) {
webidl.brandCheck(this, FileLike)
return this[kState].blobLike.arrayBuffer(...args)
}
slice (...args) {
webidl.brandCheck(this, FileLike)
return this[kState].blobLike.slice(...args)
}
text (...args) {
webidl.brandCheck(this, FileLike)
return this[kState].blobLike.text(...args)
}
get size () {
webidl.brandCheck(this, FileLike)
return this[kState].blobLike.size
}
get type () {
webidl.brandCheck(this, FileLike)
return this[kState].blobLike.type
}
get name () {
webidl.brandCheck(this, FileLike)
return this[kState].name
}
get lastModified () {
webidl.brandCheck(this, FileLike)
return this[kState].lastModified
}
get [Symbol.toStringTag] () {
return 'File'
}
}
Object.defineProperties(File.prototype, {
[Symbol.toStringTag]: {
value: 'File',
configurable: true
},
name: kEnumerableProperty,
lastModified: kEnumerableProperty
})
webidl.converters.Blob = webidl.interfaceConverter(Blob)
webidl.converters.BlobPart = function (V, opts) {
if (webidl.util.Type(V) === 'Object') {
if (isBlobLike(V)) {
return webidl.converters.Blob(V, { strict: false })
}
if (
ArrayBuffer.isView(V) ||
types.isAnyArrayBuffer(V)
) {
return webidl.converters.BufferSource(V, opts)
}
}
return webidl.converters.USVString(V, opts)
}
webidl.converters['sequence<BlobPart>'] = webidl.sequenceConverter(
webidl.converters.BlobPart
)
// https://www.w3.org/TR/FileAPI/#dfn-FilePropertyBag
webidl.converters.FilePropertyBag = webidl.dictionaryConverter([
{
key: 'lastModified',
converter: webidl.converters['long long'],
get defaultValue () {
return Date.now()
}
},
{
key: 'type',
converter: webidl.converters.DOMString,
defaultValue: ''
},
{
key: 'endings',
converter: (value) => {
value = webidl.converters.DOMString(value)
value = value.toLowerCase()
if (value !== 'native') {
value = 'transparent'
}
return value
},
defaultValue: 'transparent'
}
])
/**
* @see https://www.w3.org/TR/FileAPI/#process-blob-parts
* @param {(NodeJS.TypedArray|Blob|string)[]} parts
* @param {{ type: string, endings: string }} options
*/
function processBlobParts (parts, options) {
// 1. Let bytes be an empty sequence of bytes.
/** @type {NodeJS.TypedArray[]} */
const bytes = []
// 2. For each element in parts:
for (const element of parts) {
// 1. If element is a USVString, run the following substeps:
if (typeof element === 'string') {
// 1. Let s be element.
let s = element
// 2. If the endings member of options is "native", set s
// to the result of converting line endings to native
// of element.
if (options.endings === 'native') {
s = convertLineEndingsNative(s)
}
// 3. Append the result of UTF-8 encoding s to bytes.
bytes.push(encoder.encode(s))
} else if (
types.isAnyArrayBuffer(element) ||
types.isTypedArray(element)
) {
// 2. If element is a BufferSource, get a copy of the
// bytes held by the buffer source, and append those
// bytes to bytes.
if (!element.buffer) { // ArrayBuffer
bytes.push(new Uint8Array(element))
} else {
bytes.push(
new Uint8Array(element.buffer, element.byteOffset, element.byteLength)
)
}
} else if (isBlobLike(element)) {
// 3. If element is a Blob, append the bytes it represents
// to bytes.
bytes.push(element)
}
}
// 3. Return bytes.
return bytes
}
/**
* @see https://www.w3.org/TR/FileAPI/#convert-line-endings-to-native
* @param {string} s
*/
function convertLineEndingsNative (s) {
// 1. Let native line ending be be the code point U+000A LF.
let nativeLineEnding = '\n'
// 2. If the underlying platforms conventions are to
// represent newlines as a carriage return and line feed
// sequence, set native line ending to the code point
// U+000D CR followed by the code point U+000A LF.
if (process.platform === 'win32') {
nativeLineEnding = '\r\n'
}
return s.replace(/\r?\n/g, nativeLineEnding)
}
// If this function is moved to ./util.js, some tools (such as
// rollup) will warn about circular dependencies. See:
// https://github.com/nodejs/undici/issues/1629
function isFileLike (object) {
return (
(NativeFile && object instanceof NativeFile) ||
object instanceof File || (
object &&
(typeof object.stream === 'function' ||
typeof object.arrayBuffer === 'function') &&
object[Symbol.toStringTag] === 'File'
)
)
}
module.exports = { File, FileLike, isFileLike }

265
node_modules/undici/lib/fetch/formdata.js generated vendored Normal file
View File

@ -0,0 +1,265 @@
'use strict'
const { isBlobLike, toUSVString, makeIterator } = require('./util')
const { kState } = require('./symbols')
const { File: UndiciFile, FileLike, isFileLike } = require('./file')
const { webidl } = require('./webidl')
const { Blob, File: NativeFile } = require('buffer')
/** @type {globalThis['File']} */
const File = NativeFile ?? UndiciFile
// https://xhr.spec.whatwg.org/#formdata
class FormData {
constructor (form) {
if (form !== undefined) {
throw webidl.errors.conversionFailed({
prefix: 'FormData constructor',
argument: 'Argument 1',
types: ['undefined']
})
}
this[kState] = []
}
append (name, value, filename = undefined) {
webidl.brandCheck(this, FormData)
webidl.argumentLengthCheck(arguments, 2, { header: 'FormData.append' })
if (arguments.length === 3 && !isBlobLike(value)) {
throw new TypeError(
"Failed to execute 'append' on 'FormData': parameter 2 is not of type 'Blob'"
)
}
// 1. Let value be value if given; otherwise blobValue.
name = webidl.converters.USVString(name)
value = isBlobLike(value)
? webidl.converters.Blob(value, { strict: false })
: webidl.converters.USVString(value)
filename = arguments.length === 3
? webidl.converters.USVString(filename)
: undefined
// 2. Let entry be the result of creating an entry with
// name, value, and filename if given.
const entry = makeEntry(name, value, filename)
// 3. Append entry to thiss entry list.
this[kState].push(entry)
}
delete (name) {
webidl.brandCheck(this, FormData)
webidl.argumentLengthCheck(arguments, 1, { header: 'FormData.delete' })
name = webidl.converters.USVString(name)
// The delete(name) method steps are to remove all entries whose name
// is name from thiss entry list.
this[kState] = this[kState].filter(entry => entry.name !== name)
}
get (name) {
webidl.brandCheck(this, FormData)
webidl.argumentLengthCheck(arguments, 1, { header: 'FormData.get' })
name = webidl.converters.USVString(name)
// 1. If there is no entry whose name is name in thiss entry list,
// then return null.
const idx = this[kState].findIndex((entry) => entry.name === name)
if (idx === -1) {
return null
}
// 2. Return the value of the first entry whose name is name from
// thiss entry list.
return this[kState][idx].value
}
getAll (name) {
webidl.brandCheck(this, FormData)
webidl.argumentLengthCheck(arguments, 1, { header: 'FormData.getAll' })
name = webidl.converters.USVString(name)
// 1. If there is no entry whose name is name in thiss entry list,
// then return the empty list.
// 2. Return the values of all entries whose name is name, in order,
// from thiss entry list.
return this[kState]
.filter((entry) => entry.name === name)
.map((entry) => entry.value)
}
has (name) {
webidl.brandCheck(this, FormData)
webidl.argumentLengthCheck(arguments, 1, { header: 'FormData.has' })
name = webidl.converters.USVString(name)
// The has(name) method steps are to return true if there is an entry
// whose name is name in thiss entry list; otherwise false.
return this[kState].findIndex((entry) => entry.name === name) !== -1
}
set (name, value, filename = undefined) {
webidl.brandCheck(this, FormData)
webidl.argumentLengthCheck(arguments, 2, { header: 'FormData.set' })
if (arguments.length === 3 && !isBlobLike(value)) {
throw new TypeError(
"Failed to execute 'set' on 'FormData': parameter 2 is not of type 'Blob'"
)
}
// The set(name, value) and set(name, blobValue, filename) method steps
// are:
// 1. Let value be value if given; otherwise blobValue.
name = webidl.converters.USVString(name)
value = isBlobLike(value)
? webidl.converters.Blob(value, { strict: false })
: webidl.converters.USVString(value)
filename = arguments.length === 3
? toUSVString(filename)
: undefined
// 2. Let entry be the result of creating an entry with name, value, and
// filename if given.
const entry = makeEntry(name, value, filename)
// 3. If there are entries in thiss entry list whose name is name, then
// replace the first such entry with entry and remove the others.
const idx = this[kState].findIndex((entry) => entry.name === name)
if (idx !== -1) {
this[kState] = [
...this[kState].slice(0, idx),
entry,
...this[kState].slice(idx + 1).filter((entry) => entry.name !== name)
]
} else {
// 4. Otherwise, append entry to thiss entry list.
this[kState].push(entry)
}
}
entries () {
webidl.brandCheck(this, FormData)
return makeIterator(
() => this[kState].map(pair => [pair.name, pair.value]),
'FormData',
'key+value'
)
}
keys () {
webidl.brandCheck(this, FormData)
return makeIterator(
() => this[kState].map(pair => [pair.name, pair.value]),
'FormData',
'key'
)
}
values () {
webidl.brandCheck(this, FormData)
return makeIterator(
() => this[kState].map(pair => [pair.name, pair.value]),
'FormData',
'value'
)
}
/**
* @param {(value: string, key: string, self: FormData) => void} callbackFn
* @param {unknown} thisArg
*/
forEach (callbackFn, thisArg = globalThis) {
webidl.brandCheck(this, FormData)
webidl.argumentLengthCheck(arguments, 1, { header: 'FormData.forEach' })
if (typeof callbackFn !== 'function') {
throw new TypeError(
"Failed to execute 'forEach' on 'FormData': parameter 1 is not of type 'Function'."
)
}
for (const [key, value] of this) {
callbackFn.apply(thisArg, [value, key, this])
}
}
}
FormData.prototype[Symbol.iterator] = FormData.prototype.entries
Object.defineProperties(FormData.prototype, {
[Symbol.toStringTag]: {
value: 'FormData',
configurable: true
}
})
/**
* @see https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#create-an-entry
* @param {string} name
* @param {string|Blob} value
* @param {?string} filename
* @returns
*/
function makeEntry (name, value, filename) {
// 1. Set name to the result of converting name into a scalar value string.
// "To convert a string into a scalar value string, replace any surrogates
// with U+FFFD."
// see: https://nodejs.org/dist/latest-v18.x/docs/api/buffer.html#buftostringencoding-start-end
name = Buffer.from(name).toString('utf8')
// 2. If value is a string, then set value to the result of converting
// value into a scalar value string.
if (typeof value === 'string') {
value = Buffer.from(value).toString('utf8')
} else {
// 3. Otherwise:
// 1. If value is not a File object, then set value to a new File object,
// representing the same bytes, whose name attribute value is "blob"
if (!isFileLike(value)) {
value = value instanceof Blob
? new File([value], 'blob', { type: value.type })
: new FileLike(value, 'blob', { type: value.type })
}
// 2. If filename is given, then set value to a new File object,
// representing the same bytes, whose name attribute is filename.
if (filename !== undefined) {
/** @type {FilePropertyBag} */
const options = {
type: value.type,
lastModified: value.lastModified
}
value = (NativeFile && value instanceof NativeFile) || value instanceof UndiciFile
? new File([value], filename, options)
: new FileLike(value, filename, options)
}
}
// 4. Return an entry whose name is name and whose value is value.
return { name, value }
}
module.exports = { FormData }

40
node_modules/undici/lib/fetch/global.js generated vendored Normal file
View File

@ -0,0 +1,40 @@
'use strict'
// In case of breaking changes, increase the version
// number to avoid conflicts.
const globalOrigin = Symbol.for('undici.globalOrigin.1')
function getGlobalOrigin () {
return globalThis[globalOrigin]
}
function setGlobalOrigin (newOrigin) {
if (newOrigin === undefined) {
Object.defineProperty(globalThis, globalOrigin, {
value: undefined,
writable: true,
enumerable: false,
configurable: false
})
return
}
const parsedURL = new URL(newOrigin)
if (parsedURL.protocol !== 'http:' && parsedURL.protocol !== 'https:') {
throw new TypeError(`Only http & https urls are allowed, received ${parsedURL.protocol}`)
}
Object.defineProperty(globalThis, globalOrigin, {
value: parsedURL,
writable: true,
enumerable: false,
configurable: false
})
}
module.exports = {
getGlobalOrigin,
setGlobalOrigin
}

586
node_modules/undici/lib/fetch/headers.js generated vendored Normal file
View File

@ -0,0 +1,586 @@
// https://github.com/Ethan-Arrowood/undici-fetch
'use strict'
const { kHeadersList } = require('../core/symbols')
const { kGuard } = require('./symbols')
const { kEnumerableProperty } = require('../core/util')
const {
makeIterator,
isValidHeaderName,
isValidHeaderValue
} = require('./util')
const { webidl } = require('./webidl')
const assert = require('assert')
const kHeadersMap = Symbol('headers map')
const kHeadersSortedMap = Symbol('headers map sorted')
/**
* @param {number} code
*/
function isHTTPWhiteSpaceCharCode (code) {
return code === 0x00a || code === 0x00d || code === 0x009 || code === 0x020
}
/**
* @see https://fetch.spec.whatwg.org/#concept-header-value-normalize
* @param {string} potentialValue
*/
function headerValueNormalize (potentialValue) {
// To normalize a byte sequence potentialValue, remove
// any leading and trailing HTTP whitespace bytes from
// potentialValue.
let i = 0; let j = potentialValue.length
while (j > i && isHTTPWhiteSpaceCharCode(potentialValue.charCodeAt(j - 1))) --j
while (j > i && isHTTPWhiteSpaceCharCode(potentialValue.charCodeAt(i))) ++i
return i === 0 && j === potentialValue.length ? potentialValue : potentialValue.substring(i, j)
}
function fill (headers, object) {
// To fill a Headers object headers with a given object object, run these steps:
// 1. If object is a sequence, then for each header in object:
// Note: webidl conversion to array has already been done.
if (Array.isArray(object)) {
for (let i = 0; i < object.length; ++i) {
const header = object[i]
// 1. If header does not contain exactly two items, then throw a TypeError.
if (header.length !== 2) {
throw webidl.errors.exception({
header: 'Headers constructor',
message: `expected name/value pair to be length 2, found ${header.length}.`
})
}
// 2. Append (headers first item, headers second item) to headers.
appendHeader(headers, header[0], header[1])
}
} else if (typeof object === 'object' && object !== null) {
// Note: null should throw
// 2. Otherwise, object is a record, then for each key → value in object,
// append (key, value) to headers
const keys = Object.keys(object)
for (let i = 0; i < keys.length; ++i) {
appendHeader(headers, keys[i], object[keys[i]])
}
} else {
throw webidl.errors.conversionFailed({
prefix: 'Headers constructor',
argument: 'Argument 1',
types: ['sequence<sequence<ByteString>>', 'record<ByteString, ByteString>']
})
}
}
/**
* @see https://fetch.spec.whatwg.org/#concept-headers-append
*/
function appendHeader (headers, name, value) {
// 1. Normalize value.
value = headerValueNormalize(value)
// 2. If name is not a header name or value is not a
// header value, then throw a TypeError.
if (!isValidHeaderName(name)) {
throw webidl.errors.invalidArgument({
prefix: 'Headers.append',
value: name,
type: 'header name'
})
} else if (!isValidHeaderValue(value)) {
throw webidl.errors.invalidArgument({
prefix: 'Headers.append',
value,
type: 'header value'
})
}
// 3. If headerss guard is "immutable", then throw a TypeError.
// 4. Otherwise, if headerss guard is "request" and name is a
// forbidden header name, return.
// Note: undici does not implement forbidden header names
if (headers[kGuard] === 'immutable') {
throw new TypeError('immutable')
} else if (headers[kGuard] === 'request-no-cors') {
// 5. Otherwise, if headerss guard is "request-no-cors":
// TODO
}
// 6. Otherwise, if headerss guard is "response" and name is a
// forbidden response-header name, return.
// 7. Append (name, value) to headerss header list.
return headers[kHeadersList].append(name, value)
// 8. If headerss guard is "request-no-cors", then remove
// privileged no-CORS request headers from headers
}
class HeadersList {
/** @type {[string, string][]|null} */
cookies = null
constructor (init) {
if (init instanceof HeadersList) {
this[kHeadersMap] = new Map(init[kHeadersMap])
this[kHeadersSortedMap] = init[kHeadersSortedMap]
this.cookies = init.cookies === null ? null : [...init.cookies]
} else {
this[kHeadersMap] = new Map(init)
this[kHeadersSortedMap] = null
}
}
// https://fetch.spec.whatwg.org/#header-list-contains
contains (name) {
// A header list list contains a header name name if list
// contains a header whose name is a byte-case-insensitive
// match for name.
name = name.toLowerCase()
return this[kHeadersMap].has(name)
}
clear () {
this[kHeadersMap].clear()
this[kHeadersSortedMap] = null
this.cookies = null
}
// https://fetch.spec.whatwg.org/#concept-header-list-append
append (name, value) {
this[kHeadersSortedMap] = null
// 1. If list contains name, then set name to the first such
// headers name.
const lowercaseName = name.toLowerCase()
const exists = this[kHeadersMap].get(lowercaseName)
// 2. Append (name, value) to list.
if (exists) {
const delimiter = lowercaseName === 'cookie' ? '; ' : ', '
this[kHeadersMap].set(lowercaseName, {
name: exists.name,
value: `${exists.value}${delimiter}${value}`
})
} else {
this[kHeadersMap].set(lowercaseName, { name, value })
}
if (lowercaseName === 'set-cookie') {
this.cookies ??= []
this.cookies.push(value)
}
}
// https://fetch.spec.whatwg.org/#concept-header-list-set
set (name, value) {
this[kHeadersSortedMap] = null
const lowercaseName = name.toLowerCase()
if (lowercaseName === 'set-cookie') {
this.cookies = [value]
}
// 1. If list contains name, then set the value of
// the first such header to value and remove the
// others.
// 2. Otherwise, append header (name, value) to list.
this[kHeadersMap].set(lowercaseName, { name, value })
}
// https://fetch.spec.whatwg.org/#concept-header-list-delete
delete (name) {
this[kHeadersSortedMap] = null
name = name.toLowerCase()
if (name === 'set-cookie') {
this.cookies = null
}
this[kHeadersMap].delete(name)
}
// https://fetch.spec.whatwg.org/#concept-header-list-get
get (name) {
const value = this[kHeadersMap].get(name.toLowerCase())
// 1. If list does not contain name, then return null.
// 2. Return the values of all headers in list whose name
// is a byte-case-insensitive match for name,
// separated from each other by 0x2C 0x20, in order.
return value === undefined ? null : value.value
}
* [Symbol.iterator] () {
// use the lowercased name
for (const [name, { value }] of this[kHeadersMap]) {
yield [name, value]
}
}
get entries () {
const headers = {}
if (this[kHeadersMap].size) {
for (const { name, value } of this[kHeadersMap].values()) {
headers[name] = value
}
}
return headers
}
}
// https://fetch.spec.whatwg.org/#headers-class
class Headers {
constructor (init = undefined) {
this[kHeadersList] = new HeadersList()
// The new Headers(init) constructor steps are:
// 1. Set thiss guard to "none".
this[kGuard] = 'none'
// 2. If init is given, then fill this with init.
if (init !== undefined) {
init = webidl.converters.HeadersInit(init)
fill(this, init)
}
}
// https://fetch.spec.whatwg.org/#dom-headers-append
append (name, value) {
webidl.brandCheck(this, Headers)
webidl.argumentLengthCheck(arguments, 2, { header: 'Headers.append' })
name = webidl.converters.ByteString(name)
value = webidl.converters.ByteString(value)
return appendHeader(this, name, value)
}
// https://fetch.spec.whatwg.org/#dom-headers-delete
delete (name) {
webidl.brandCheck(this, Headers)
webidl.argumentLengthCheck(arguments, 1, { header: 'Headers.delete' })
name = webidl.converters.ByteString(name)
// 1. If name is not a header name, then throw a TypeError.
if (!isValidHeaderName(name)) {
throw webidl.errors.invalidArgument({
prefix: 'Headers.delete',
value: name,
type: 'header name'
})
}
// 2. If thiss guard is "immutable", then throw a TypeError.
// 3. Otherwise, if thiss guard is "request" and name is a
// forbidden header name, return.
// 4. Otherwise, if thiss guard is "request-no-cors", name
// is not a no-CORS-safelisted request-header name, and
// name is not a privileged no-CORS request-header name,
// return.
// 5. Otherwise, if thiss guard is "response" and name is
// a forbidden response-header name, return.
// Note: undici does not implement forbidden header names
if (this[kGuard] === 'immutable') {
throw new TypeError('immutable')
} else if (this[kGuard] === 'request-no-cors') {
// TODO
}
// 6. If thiss header list does not contain name, then
// return.
if (!this[kHeadersList].contains(name)) {
return
}
// 7. Delete name from thiss header list.
// 8. If thiss guard is "request-no-cors", then remove
// privileged no-CORS request headers from this.
this[kHeadersList].delete(name)
}
// https://fetch.spec.whatwg.org/#dom-headers-get
get (name) {
webidl.brandCheck(this, Headers)
webidl.argumentLengthCheck(arguments, 1, { header: 'Headers.get' })
name = webidl.converters.ByteString(name)
// 1. If name is not a header name, then throw a TypeError.
if (!isValidHeaderName(name)) {
throw webidl.errors.invalidArgument({
prefix: 'Headers.get',
value: name,
type: 'header name'
})
}
// 2. Return the result of getting name from thiss header
// list.
return this[kHeadersList].get(name)
}
// https://fetch.spec.whatwg.org/#dom-headers-has
has (name) {
webidl.brandCheck(this, Headers)
webidl.argumentLengthCheck(arguments, 1, { header: 'Headers.has' })
name = webidl.converters.ByteString(name)
// 1. If name is not a header name, then throw a TypeError.
if (!isValidHeaderName(name)) {
throw webidl.errors.invalidArgument({
prefix: 'Headers.has',
value: name,
type: 'header name'
})
}
// 2. Return true if thiss header list contains name;
// otherwise false.
return this[kHeadersList].contains(name)
}
// https://fetch.spec.whatwg.org/#dom-headers-set
set (name, value) {
webidl.brandCheck(this, Headers)
webidl.argumentLengthCheck(arguments, 2, { header: 'Headers.set' })
name = webidl.converters.ByteString(name)
value = webidl.converters.ByteString(value)
// 1. Normalize value.
value = headerValueNormalize(value)
// 2. If name is not a header name or value is not a
// header value, then throw a TypeError.
if (!isValidHeaderName(name)) {
throw webidl.errors.invalidArgument({
prefix: 'Headers.set',
value: name,
type: 'header name'
})
} else if (!isValidHeaderValue(value)) {
throw webidl.errors.invalidArgument({
prefix: 'Headers.set',
value,
type: 'header value'
})
}
// 3. If thiss guard is "immutable", then throw a TypeError.
// 4. Otherwise, if thiss guard is "request" and name is a
// forbidden header name, return.
// 5. Otherwise, if thiss guard is "request-no-cors" and
// name/value is not a no-CORS-safelisted request-header,
// return.
// 6. Otherwise, if thiss guard is "response" and name is a
// forbidden response-header name, return.
// Note: undici does not implement forbidden header names
if (this[kGuard] === 'immutable') {
throw new TypeError('immutable')
} else if (this[kGuard] === 'request-no-cors') {
// TODO
}
// 7. Set (name, value) in thiss header list.
// 8. If thiss guard is "request-no-cors", then remove
// privileged no-CORS request headers from this
this[kHeadersList].set(name, value)
}
// https://fetch.spec.whatwg.org/#dom-headers-getsetcookie
getSetCookie () {
webidl.brandCheck(this, Headers)
// 1. If thiss header list does not contain `Set-Cookie`, then return « ».
// 2. Return the values of all headers in thiss header list whose name is
// a byte-case-insensitive match for `Set-Cookie`, in order.
const list = this[kHeadersList].cookies
if (list) {
return [...list]
}
return []
}
// https://fetch.spec.whatwg.org/#concept-header-list-sort-and-combine
get [kHeadersSortedMap] () {
if (this[kHeadersList][kHeadersSortedMap]) {
return this[kHeadersList][kHeadersSortedMap]
}
// 1. Let headers be an empty list of headers with the key being the name
// and value the value.
const headers = []
// 2. Let names be the result of convert header names to a sorted-lowercase
// set with all the names of the headers in list.
const names = [...this[kHeadersList]].sort((a, b) => a[0] < b[0] ? -1 : 1)
const cookies = this[kHeadersList].cookies
// 3. For each name of names:
for (let i = 0; i < names.length; ++i) {
const [name, value] = names[i]
// 1. If name is `set-cookie`, then:
if (name === 'set-cookie') {
// 1. Let values be a list of all values of headers in list whose name
// is a byte-case-insensitive match for name, in order.
// 2. For each value of values:
// 1. Append (name, value) to headers.
for (let j = 0; j < cookies.length; ++j) {
headers.push([name, cookies[j]])
}
} else {
// 2. Otherwise:
// 1. Let value be the result of getting name from list.
// 2. Assert: value is non-null.
assert(value !== null)
// 3. Append (name, value) to headers.
headers.push([name, value])
}
}
this[kHeadersList][kHeadersSortedMap] = headers
// 4. Return headers.
return headers
}
keys () {
webidl.brandCheck(this, Headers)
if (this[kGuard] === 'immutable') {
const value = this[kHeadersSortedMap]
return makeIterator(() => value, 'Headers',
'key')
}
return makeIterator(
() => [...this[kHeadersSortedMap].values()],
'Headers',
'key'
)
}
values () {
webidl.brandCheck(this, Headers)
if (this[kGuard] === 'immutable') {
const value = this[kHeadersSortedMap]
return makeIterator(() => value, 'Headers',
'value')
}
return makeIterator(
() => [...this[kHeadersSortedMap].values()],
'Headers',
'value'
)
}
entries () {
webidl.brandCheck(this, Headers)
if (this[kGuard] === 'immutable') {
const value = this[kHeadersSortedMap]
return makeIterator(() => value, 'Headers',
'key+value')
}
return makeIterator(
() => [...this[kHeadersSortedMap].values()],
'Headers',
'key+value'
)
}
/**
* @param {(value: string, key: string, self: Headers) => void} callbackFn
* @param {unknown} thisArg
*/
forEach (callbackFn, thisArg = globalThis) {
webidl.brandCheck(this, Headers)
webidl.argumentLengthCheck(arguments, 1, { header: 'Headers.forEach' })
if (typeof callbackFn !== 'function') {
throw new TypeError(
"Failed to execute 'forEach' on 'Headers': parameter 1 is not of type 'Function'."
)
}
for (const [key, value] of this) {
callbackFn.apply(thisArg, [value, key, this])
}
}
[Symbol.for('nodejs.util.inspect.custom')] () {
webidl.brandCheck(this, Headers)
return this[kHeadersList]
}
}
Headers.prototype[Symbol.iterator] = Headers.prototype.entries
Object.defineProperties(Headers.prototype, {
append: kEnumerableProperty,
delete: kEnumerableProperty,
get: kEnumerableProperty,
has: kEnumerableProperty,
set: kEnumerableProperty,
getSetCookie: kEnumerableProperty,
keys: kEnumerableProperty,
values: kEnumerableProperty,
entries: kEnumerableProperty,
forEach: kEnumerableProperty,
[Symbol.iterator]: { enumerable: false },
[Symbol.toStringTag]: {
value: 'Headers',
configurable: true
}
})
webidl.converters.HeadersInit = function (V) {
if (webidl.util.Type(V) === 'Object') {
if (V[Symbol.iterator]) {
return webidl.converters['sequence<sequence<ByteString>>'](V)
}
return webidl.converters['record<ByteString, ByteString>'](V)
}
throw webidl.errors.conversionFailed({
prefix: 'Headers constructor',
argument: 'Argument 1',
types: ['sequence<sequence<ByteString>>', 'record<ByteString, ByteString>']
})
}
module.exports = {
fill,
Headers,
HeadersList
}

2145
node_modules/undici/lib/fetch/index.js generated vendored Normal file

File diff suppressed because it is too large Load Diff

944
node_modules/undici/lib/fetch/request.js generated vendored Normal file
View File

@ -0,0 +1,944 @@
/* globals AbortController */
'use strict'
const { extractBody, mixinBody, cloneBody } = require('./body')
const { Headers, fill: fillHeaders, HeadersList } = require('./headers')
const { FinalizationRegistry } = require('../compat/dispatcher-weakref')()
const util = require('../core/util')
const {
isValidHTTPToken,
sameOrigin,
normalizeMethod,
makePolicyContainer
} = require('./util')
const {
forbiddenMethodsSet,
corsSafeListedMethodsSet,
referrerPolicy,
requestRedirect,
requestMode,
requestCredentials,
requestCache,
requestDuplex
} = require('./constants')
const { kEnumerableProperty } = util
const { kHeaders, kSignal, kState, kGuard, kRealm } = require('./symbols')
const { webidl } = require('./webidl')
const { getGlobalOrigin } = require('./global')
const { URLSerializer } = require('./dataURL')
const { kHeadersList } = require('../core/symbols')
const assert = require('assert')
const { getMaxListeners, setMaxListeners, getEventListeners, defaultMaxListeners } = require('events')
let TransformStream = globalThis.TransformStream
const kInit = Symbol('init')
const kAbortController = Symbol('abortController')
const requestFinalizer = new FinalizationRegistry(({ signal, abort }) => {
signal.removeEventListener('abort', abort)
})
// https://fetch.spec.whatwg.org/#request-class
class Request {
// https://fetch.spec.whatwg.org/#dom-request
constructor (input, init = {}) {
if (input === kInit) {
return
}
webidl.argumentLengthCheck(arguments, 1, { header: 'Request constructor' })
input = webidl.converters.RequestInfo(input)
init = webidl.converters.RequestInit(init)
// https://html.spec.whatwg.org/multipage/webappapis.html#environment-settings-object
this[kRealm] = {
settingsObject: {
baseUrl: getGlobalOrigin(),
get origin () {
return this.baseUrl?.origin
},
policyContainer: makePolicyContainer()
}
}
// 1. Let request be null.
let request = null
// 2. Let fallbackMode be null.
let fallbackMode = null
// 3. Let baseURL be thiss relevant settings objects API base URL.
const baseUrl = this[kRealm].settingsObject.baseUrl
// 4. Let signal be null.
let signal = null
// 5. If input is a string, then:
if (typeof input === 'string') {
// 1. Let parsedURL be the result of parsing input with baseURL.
// 2. If parsedURL is failure, then throw a TypeError.
let parsedURL
try {
parsedURL = new URL(input, baseUrl)
} catch (err) {
throw new TypeError('Failed to parse URL from ' + input, { cause: err })
}
// 3. If parsedURL includes credentials, then throw a TypeError.
if (parsedURL.username || parsedURL.password) {
throw new TypeError(
'Request cannot be constructed from a URL that includes credentials: ' +
input
)
}
// 4. Set request to a new request whose URL is parsedURL.
request = makeRequest({ urlList: [parsedURL] })
// 5. Set fallbackMode to "cors".
fallbackMode = 'cors'
} else {
// 6. Otherwise:
// 7. Assert: input is a Request object.
assert(input instanceof Request)
// 8. Set request to inputs request.
request = input[kState]
// 9. Set signal to inputs signal.
signal = input[kSignal]
}
// 7. Let origin be thiss relevant settings objects origin.
const origin = this[kRealm].settingsObject.origin
// 8. Let window be "client".
let window = 'client'
// 9. If requests window is an environment settings object and its origin
// is same origin with origin, then set window to requests window.
if (
request.window?.constructor?.name === 'EnvironmentSettingsObject' &&
sameOrigin(request.window, origin)
) {
window = request.window
}
// 10. If init["window"] exists and is non-null, then throw a TypeError.
if (init.window != null) {
throw new TypeError(`'window' option '${window}' must be null`)
}
// 11. If init["window"] exists, then set window to "no-window".
if ('window' in init) {
window = 'no-window'
}
// 12. Set request to a new request with the following properties:
request = makeRequest({
// URL requests URL.
// undici implementation note: this is set as the first item in request's urlList in makeRequest
// method requests method.
method: request.method,
// header list A copy of requests header list.
// undici implementation note: headersList is cloned in makeRequest
headersList: request.headersList,
// unsafe-request flag Set.
unsafeRequest: request.unsafeRequest,
// client Thiss relevant settings object.
client: this[kRealm].settingsObject,
// window window.
window,
// priority requests priority.
priority: request.priority,
// origin requests origin. The propagation of the origin is only significant for navigation requests
// being handled by a service worker. In this scenario a request can have an origin that is different
// from the current client.
origin: request.origin,
// referrer requests referrer.
referrer: request.referrer,
// referrer policy requests referrer policy.
referrerPolicy: request.referrerPolicy,
// mode requests mode.
mode: request.mode,
// credentials mode requests credentials mode.
credentials: request.credentials,
// cache mode requests cache mode.
cache: request.cache,
// redirect mode requests redirect mode.
redirect: request.redirect,
// integrity metadata requests integrity metadata.
integrity: request.integrity,
// keepalive requests keepalive.
keepalive: request.keepalive,
// reload-navigation flag requests reload-navigation flag.
reloadNavigation: request.reloadNavigation,
// history-navigation flag requests history-navigation flag.
historyNavigation: request.historyNavigation,
// URL list A clone of requests URL list.
urlList: [...request.urlList]
})
// 13. If init is not empty, then:
if (Object.keys(init).length > 0) {
// 1. If requests mode is "navigate", then set it to "same-origin".
if (request.mode === 'navigate') {
request.mode = 'same-origin'
}
// 2. Unset requests reload-navigation flag.
request.reloadNavigation = false
// 3. Unset requests history-navigation flag.
request.historyNavigation = false
// 4. Set requests origin to "client".
request.origin = 'client'
// 5. Set requests referrer to "client"
request.referrer = 'client'
// 6. Set requests referrer policy to the empty string.
request.referrerPolicy = ''
// 7. Set requests URL to requests current URL.
request.url = request.urlList[request.urlList.length - 1]
// 8. Set requests URL list to « requests URL ».
request.urlList = [request.url]
}
// 14. If init["referrer"] exists, then:
if (init.referrer !== undefined) {
// 1. Let referrer be init["referrer"].
const referrer = init.referrer
// 2. If referrer is the empty string, then set requests referrer to "no-referrer".
if (referrer === '') {
request.referrer = 'no-referrer'
} else {
// 1. Let parsedReferrer be the result of parsing referrer with
// baseURL.
// 2. If parsedReferrer is failure, then throw a TypeError.
let parsedReferrer
try {
parsedReferrer = new URL(referrer, baseUrl)
} catch (err) {
throw new TypeError(`Referrer "${referrer}" is not a valid URL.`, { cause: err })
}
// 3. If one of the following is true
// - parsedReferrers scheme is "about" and path is the string "client"
// - parsedReferrers origin is not same origin with origin
// then set requests referrer to "client".
if (
(parsedReferrer.protocol === 'about:' && parsedReferrer.hostname === 'client') ||
(origin && !sameOrigin(parsedReferrer, this[kRealm].settingsObject.baseUrl))
) {
request.referrer = 'client'
} else {
// 4. Otherwise, set requests referrer to parsedReferrer.
request.referrer = parsedReferrer
}
}
}
// 15. If init["referrerPolicy"] exists, then set requests referrer policy
// to it.
if (init.referrerPolicy !== undefined) {
request.referrerPolicy = init.referrerPolicy
}
// 16. Let mode be init["mode"] if it exists, and fallbackMode otherwise.
let mode
if (init.mode !== undefined) {
mode = init.mode
} else {
mode = fallbackMode
}
// 17. If mode is "navigate", then throw a TypeError.
if (mode === 'navigate') {
throw webidl.errors.exception({
header: 'Request constructor',
message: 'invalid request mode navigate.'
})
}
// 18. If mode is non-null, set requests mode to mode.
if (mode != null) {
request.mode = mode
}
// 19. If init["credentials"] exists, then set requests credentials mode
// to it.
if (init.credentials !== undefined) {
request.credentials = init.credentials
}
// 18. If init["cache"] exists, then set requests cache mode to it.
if (init.cache !== undefined) {
request.cache = init.cache
}
// 21. If requests cache mode is "only-if-cached" and requests mode is
// not "same-origin", then throw a TypeError.
if (request.cache === 'only-if-cached' && request.mode !== 'same-origin') {
throw new TypeError(
"'only-if-cached' can be set only with 'same-origin' mode"
)
}
// 22. If init["redirect"] exists, then set requests redirect mode to it.
if (init.redirect !== undefined) {
request.redirect = init.redirect
}
// 23. If init["integrity"] exists, then set requests integrity metadata to it.
if (init.integrity !== undefined && init.integrity != null) {
request.integrity = String(init.integrity)
}
// 24. If init["keepalive"] exists, then set requests keepalive to it.
if (init.keepalive !== undefined) {
request.keepalive = Boolean(init.keepalive)
}
// 25. If init["method"] exists, then:
if (init.method !== undefined) {
// 1. Let method be init["method"].
let method = init.method
// 2. If method is not a method or method is a forbidden method, then
// throw a TypeError.
if (!isValidHTTPToken(init.method)) {
throw new TypeError(`'${init.method}' is not a valid HTTP method.`)
}
if (forbiddenMethodsSet.has(method.toUpperCase())) {
throw new TypeError(`'${init.method}' HTTP method is unsupported.`)
}
// 3. Normalize method.
method = normalizeMethod(init.method)
// 4. Set requests method to method.
request.method = method
}
// 26. If init["signal"] exists, then set signal to it.
if (init.signal !== undefined) {
signal = init.signal
}
// 27. Set thiss request to request.
this[kState] = request
// 28. Set thiss signal to a new AbortSignal object with thiss relevant
// Realm.
// TODO: could this be simplified with AbortSignal.any
// (https://dom.spec.whatwg.org/#dom-abortsignal-any)
const ac = new AbortController()
this[kSignal] = ac.signal
this[kSignal][kRealm] = this[kRealm]
// 29. If signal is not null, then make thiss signal follow signal.
if (signal != null) {
if (
!signal ||
typeof signal.aborted !== 'boolean' ||
typeof signal.addEventListener !== 'function'
) {
throw new TypeError(
"Failed to construct 'Request': member signal is not of type AbortSignal."
)
}
if (signal.aborted) {
ac.abort(signal.reason)
} else {
// Keep a strong ref to ac while request object
// is alive. This is needed to prevent AbortController
// from being prematurely garbage collected.
// See, https://github.com/nodejs/undici/issues/1926.
this[kAbortController] = ac
const acRef = new WeakRef(ac)
const abort = function () {
const ac = acRef.deref()
if (ac !== undefined) {
ac.abort(this.reason)
}
}
// Third-party AbortControllers may not work with these.
// See, https://github.com/nodejs/undici/pull/1910#issuecomment-1464495619.
try {
// If the max amount of listeners is equal to the default, increase it
// This is only available in node >= v19.9.0
if (typeof getMaxListeners === 'function' && getMaxListeners(signal) === defaultMaxListeners) {
setMaxListeners(100, signal)
} else if (getEventListeners(signal, 'abort').length >= defaultMaxListeners) {
setMaxListeners(100, signal)
}
} catch {}
util.addAbortListener(signal, abort)
requestFinalizer.register(ac, { signal, abort })
}
}
// 30. Set thiss headers to a new Headers object with thiss relevant
// Realm, whose header list is requests header list and guard is
// "request".
this[kHeaders] = new Headers()
this[kHeaders][kHeadersList] = request.headersList
this[kHeaders][kGuard] = 'request'
this[kHeaders][kRealm] = this[kRealm]
// 31. If thiss requests mode is "no-cors", then:
if (mode === 'no-cors') {
// 1. If thiss requests method is not a CORS-safelisted method,
// then throw a TypeError.
if (!corsSafeListedMethodsSet.has(request.method)) {
throw new TypeError(
`'${request.method} is unsupported in no-cors mode.`
)
}
// 2. Set thiss headerss guard to "request-no-cors".
this[kHeaders][kGuard] = 'request-no-cors'
}
// 32. If init is not empty, then:
if (Object.keys(init).length !== 0) {
// 1. Let headers be a copy of thiss headers and its associated header
// list.
let headers = new Headers(this[kHeaders])
// 2. If init["headers"] exists, then set headers to init["headers"].
if (init.headers !== undefined) {
headers = init.headers
}
// 3. Empty thiss headerss header list.
this[kHeaders][kHeadersList].clear()
// 4. If headers is a Headers object, then for each header in its header
// list, append headers name/headers value to thiss headers.
if (headers.constructor.name === 'Headers') {
for (const [key, val] of headers) {
this[kHeaders].append(key, val)
}
} else {
// 5. Otherwise, fill thiss headers with headers.
fillHeaders(this[kHeaders], headers)
}
}
// 33. Let inputBody be inputs requests body if input is a Request
// object; otherwise null.
const inputBody = input instanceof Request ? input[kState].body : null
// 34. If either init["body"] exists and is non-null or inputBody is
// non-null, and requests method is `GET` or `HEAD`, then throw a
// TypeError.
if (
(init.body != null || inputBody != null) &&
(request.method === 'GET' || request.method === 'HEAD')
) {
throw new TypeError('Request with GET/HEAD method cannot have body.')
}
// 35. Let initBody be null.
let initBody = null
// 36. If init["body"] exists and is non-null, then:
if (init.body != null) {
// 1. Let Content-Type be null.
// 2. Set initBody and Content-Type to the result of extracting
// init["body"], with keepalive set to requests keepalive.
const [extractedBody, contentType] = extractBody(
init.body,
request.keepalive
)
initBody = extractedBody
// 3, If Content-Type is non-null and thiss headerss header list does
// not contain `Content-Type`, then append `Content-Type`/Content-Type to
// thiss headers.
if (contentType && !this[kHeaders][kHeadersList].contains('content-type')) {
this[kHeaders].append('content-type', contentType)
}
}
// 37. Let inputOrInitBody be initBody if it is non-null; otherwise
// inputBody.
const inputOrInitBody = initBody ?? inputBody
// 38. If inputOrInitBody is non-null and inputOrInitBodys source is
// null, then:
if (inputOrInitBody != null && inputOrInitBody.source == null) {
// 1. If initBody is non-null and init["duplex"] does not exist,
// then throw a TypeError.
if (initBody != null && init.duplex == null) {
throw new TypeError('RequestInit: duplex option is required when sending a body.')
}
// 2. If thiss requests mode is neither "same-origin" nor "cors",
// then throw a TypeError.
if (request.mode !== 'same-origin' && request.mode !== 'cors') {
throw new TypeError(
'If request is made from ReadableStream, mode should be "same-origin" or "cors"'
)
}
// 3. Set thiss requests use-CORS-preflight flag.
request.useCORSPreflightFlag = true
}
// 39. Let finalBody be inputOrInitBody.
let finalBody = inputOrInitBody
// 40. If initBody is null and inputBody is non-null, then:
if (initBody == null && inputBody != null) {
// 1. If input is unusable, then throw a TypeError.
if (util.isDisturbed(inputBody.stream) || inputBody.stream.locked) {
throw new TypeError(
'Cannot construct a Request with a Request object that has already been used.'
)
}
// 2. Set finalBody to the result of creating a proxy for inputBody.
if (!TransformStream) {
TransformStream = require('stream/web').TransformStream
}
// https://streams.spec.whatwg.org/#readablestream-create-a-proxy
const identityTransform = new TransformStream()
inputBody.stream.pipeThrough(identityTransform)
finalBody = {
source: inputBody.source,
length: inputBody.length,
stream: identityTransform.readable
}
}
// 41. Set thiss requests body to finalBody.
this[kState].body = finalBody
}
// Returns requests HTTP method, which is "GET" by default.
get method () {
webidl.brandCheck(this, Request)
// The method getter steps are to return thiss requests method.
return this[kState].method
}
// Returns the URL of request as a string.
get url () {
webidl.brandCheck(this, Request)
// The url getter steps are to return thiss requests URL, serialized.
return URLSerializer(this[kState].url)
}
// Returns a Headers object consisting of the headers associated with request.
// Note that headers added in the network layer by the user agent will not
// be accounted for in this object, e.g., the "Host" header.
get headers () {
webidl.brandCheck(this, Request)
// The headers getter steps are to return thiss headers.
return this[kHeaders]
}
// Returns the kind of resource requested by request, e.g., "document"
// or "script".
get destination () {
webidl.brandCheck(this, Request)
// The destination getter are to return thiss requests destination.
return this[kState].destination
}
// Returns the referrer of request. Its value can be a same-origin URL if
// explicitly set in init, the empty string to indicate no referrer, and
// "about:client" when defaulting to the globals default. This is used
// during fetching to determine the value of the `Referer` header of the
// request being made.
get referrer () {
webidl.brandCheck(this, Request)
// 1. If thiss requests referrer is "no-referrer", then return the
// empty string.
if (this[kState].referrer === 'no-referrer') {
return ''
}
// 2. If thiss requests referrer is "client", then return
// "about:client".
if (this[kState].referrer === 'client') {
return 'about:client'
}
// Return thiss requests referrer, serialized.
return this[kState].referrer.toString()
}
// Returns the referrer policy associated with request.
// This is used during fetching to compute the value of the requests
// referrer.
get referrerPolicy () {
webidl.brandCheck(this, Request)
// The referrerPolicy getter steps are to return thiss requests referrer policy.
return this[kState].referrerPolicy
}
// Returns the mode associated with request, which is a string indicating
// whether the request will use CORS, or will be restricted to same-origin
// URLs.
get mode () {
webidl.brandCheck(this, Request)
// The mode getter steps are to return thiss requests mode.
return this[kState].mode
}
// Returns the credentials mode associated with request,
// which is a string indicating whether credentials will be sent with the
// request always, never, or only when sent to a same-origin URL.
get credentials () {
// The credentials getter steps are to return thiss requests credentials mode.
return this[kState].credentials
}
// Returns the cache mode associated with request,
// which is a string indicating how the request will
// interact with the browsers cache when fetching.
get cache () {
webidl.brandCheck(this, Request)
// The cache getter steps are to return thiss requests cache mode.
return this[kState].cache
}
// Returns the redirect mode associated with request,
// which is a string indicating how redirects for the
// request will be handled during fetching. A request
// will follow redirects by default.
get redirect () {
webidl.brandCheck(this, Request)
// The redirect getter steps are to return thiss requests redirect mode.
return this[kState].redirect
}
// Returns requests subresource integrity metadata, which is a
// cryptographic hash of the resource being fetched. Its value
// consists of multiple hashes separated by whitespace. [SRI]
get integrity () {
webidl.brandCheck(this, Request)
// The integrity getter steps are to return thiss requests integrity
// metadata.
return this[kState].integrity
}
// Returns a boolean indicating whether or not request can outlive the
// global in which it was created.
get keepalive () {
webidl.brandCheck(this, Request)
// The keepalive getter steps are to return thiss requests keepalive.
return this[kState].keepalive
}
// Returns a boolean indicating whether or not request is for a reload
// navigation.
get isReloadNavigation () {
webidl.brandCheck(this, Request)
// The isReloadNavigation getter steps are to return true if thiss
// requests reload-navigation flag is set; otherwise false.
return this[kState].reloadNavigation
}
// Returns a boolean indicating whether or not request is for a history
// navigation (a.k.a. back-foward navigation).
get isHistoryNavigation () {
webidl.brandCheck(this, Request)
// The isHistoryNavigation getter steps are to return true if thiss requests
// history-navigation flag is set; otherwise false.
return this[kState].historyNavigation
}
// Returns the signal associated with request, which is an AbortSignal
// object indicating whether or not request has been aborted, and its
// abort event handler.
get signal () {
webidl.brandCheck(this, Request)
// The signal getter steps are to return thiss signal.
return this[kSignal]
}
get body () {
webidl.brandCheck(this, Request)
return this[kState].body ? this[kState].body.stream : null
}
get bodyUsed () {
webidl.brandCheck(this, Request)
return !!this[kState].body && util.isDisturbed(this[kState].body.stream)
}
get duplex () {
webidl.brandCheck(this, Request)
return 'half'
}
// Returns a clone of request.
clone () {
webidl.brandCheck(this, Request)
// 1. If this is unusable, then throw a TypeError.
if (this.bodyUsed || this.body?.locked) {
throw new TypeError('unusable')
}
// 2. Let clonedRequest be the result of cloning thiss request.
const clonedRequest = cloneRequest(this[kState])
// 3. Let clonedRequestObject be the result of creating a Request object,
// given clonedRequest, thiss headerss guard, and thiss relevant Realm.
const clonedRequestObject = new Request(kInit)
clonedRequestObject[kState] = clonedRequest
clonedRequestObject[kRealm] = this[kRealm]
clonedRequestObject[kHeaders] = new Headers()
clonedRequestObject[kHeaders][kHeadersList] = clonedRequest.headersList
clonedRequestObject[kHeaders][kGuard] = this[kHeaders][kGuard]
clonedRequestObject[kHeaders][kRealm] = this[kHeaders][kRealm]
// 4. Make clonedRequestObjects signal follow thiss signal.
const ac = new AbortController()
if (this.signal.aborted) {
ac.abort(this.signal.reason)
} else {
util.addAbortListener(
this.signal,
() => {
ac.abort(this.signal.reason)
}
)
}
clonedRequestObject[kSignal] = ac.signal
// 4. Return clonedRequestObject.
return clonedRequestObject
}
}
mixinBody(Request)
function makeRequest (init) {
// https://fetch.spec.whatwg.org/#requests
const request = {
method: 'GET',
localURLsOnly: false,
unsafeRequest: false,
body: null,
client: null,
reservedClient: null,
replacesClientId: '',
window: 'client',
keepalive: false,
serviceWorkers: 'all',
initiator: '',
destination: '',
priority: null,
origin: 'client',
policyContainer: 'client',
referrer: 'client',
referrerPolicy: '',
mode: 'no-cors',
useCORSPreflightFlag: false,
credentials: 'same-origin',
useCredentials: false,
cache: 'default',
redirect: 'follow',
integrity: '',
cryptoGraphicsNonceMetadata: '',
parserMetadata: '',
reloadNavigation: false,
historyNavigation: false,
userActivation: false,
taintedOrigin: false,
redirectCount: 0,
responseTainting: 'basic',
preventNoCacheCacheControlHeaderModification: false,
done: false,
timingAllowFailed: false,
...init,
headersList: init.headersList
? new HeadersList(init.headersList)
: new HeadersList()
}
request.url = request.urlList[0]
return request
}
// https://fetch.spec.whatwg.org/#concept-request-clone
function cloneRequest (request) {
// To clone a request request, run these steps:
// 1. Let newRequest be a copy of request, except for its body.
const newRequest = makeRequest({ ...request, body: null })
// 2. If requests body is non-null, set newRequests body to the
// result of cloning requests body.
if (request.body != null) {
newRequest.body = cloneBody(request.body)
}
// 3. Return newRequest.
return newRequest
}
Object.defineProperties(Request.prototype, {
method: kEnumerableProperty,
url: kEnumerableProperty,
headers: kEnumerableProperty,
redirect: kEnumerableProperty,
clone: kEnumerableProperty,
signal: kEnumerableProperty,
duplex: kEnumerableProperty,
destination: kEnumerableProperty,
body: kEnumerableProperty,
bodyUsed: kEnumerableProperty,
isHistoryNavigation: kEnumerableProperty,
isReloadNavigation: kEnumerableProperty,
keepalive: kEnumerableProperty,
integrity: kEnumerableProperty,
cache: kEnumerableProperty,
credentials: kEnumerableProperty,
attribute: kEnumerableProperty,
referrerPolicy: kEnumerableProperty,
referrer: kEnumerableProperty,
mode: kEnumerableProperty,
[Symbol.toStringTag]: {
value: 'Request',
configurable: true
}
})
webidl.converters.Request = webidl.interfaceConverter(
Request
)
// https://fetch.spec.whatwg.org/#requestinfo
webidl.converters.RequestInfo = function (V) {
if (typeof V === 'string') {
return webidl.converters.USVString(V)
}
if (V instanceof Request) {
return webidl.converters.Request(V)
}
return webidl.converters.USVString(V)
}
webidl.converters.AbortSignal = webidl.interfaceConverter(
AbortSignal
)
// https://fetch.spec.whatwg.org/#requestinit
webidl.converters.RequestInit = webidl.dictionaryConverter([
{
key: 'method',
converter: webidl.converters.ByteString
},
{
key: 'headers',
converter: webidl.converters.HeadersInit
},
{
key: 'body',
converter: webidl.nullableConverter(
webidl.converters.BodyInit
)
},
{
key: 'referrer',
converter: webidl.converters.USVString
},
{
key: 'referrerPolicy',
converter: webidl.converters.DOMString,
// https://w3c.github.io/webappsec-referrer-policy/#referrer-policy
allowedValues: referrerPolicy
},
{
key: 'mode',
converter: webidl.converters.DOMString,
// https://fetch.spec.whatwg.org/#concept-request-mode
allowedValues: requestMode
},
{
key: 'credentials',
converter: webidl.converters.DOMString,
// https://fetch.spec.whatwg.org/#requestcredentials
allowedValues: requestCredentials
},
{
key: 'cache',
converter: webidl.converters.DOMString,
// https://fetch.spec.whatwg.org/#requestcache
allowedValues: requestCache
},
{
key: 'redirect',
converter: webidl.converters.DOMString,
// https://fetch.spec.whatwg.org/#requestredirect
allowedValues: requestRedirect
},
{
key: 'integrity',
converter: webidl.converters.DOMString
},
{
key: 'keepalive',
converter: webidl.converters.boolean
},
{
key: 'signal',
converter: webidl.nullableConverter(
(signal) => webidl.converters.AbortSignal(
signal,
{ strict: false }
)
)
},
{
key: 'window',
converter: webidl.converters.any
},
{
key: 'duplex',
converter: webidl.converters.DOMString,
allowedValues: requestDuplex
}
])
module.exports = { Request, makeRequest }

575
node_modules/undici/lib/fetch/response.js generated vendored Normal file
View File

@ -0,0 +1,575 @@
'use strict'
const { Headers, HeadersList, fill } = require('./headers')
const { extractBody, cloneBody, mixinBody } = require('./body')
const util = require('../core/util')
const { kEnumerableProperty } = util
const {
isValidReasonPhrase,
isCancelled,
isAborted,
isBlobLike,
serializeJavascriptValueToJSONString,
isErrorLike,
isomorphicEncode
} = require('./util')
const {
redirectStatusSet,
nullBodyStatus,
DOMException
} = require('./constants')
const { kState, kHeaders, kGuard, kRealm } = require('./symbols')
const { webidl } = require('./webidl')
const { FormData } = require('./formdata')
const { getGlobalOrigin } = require('./global')
const { URLSerializer } = require('./dataURL')
const { kHeadersList } = require('../core/symbols')
const assert = require('assert')
const { types } = require('util')
const ReadableStream = globalThis.ReadableStream || require('stream/web').ReadableStream
const textEncoder = new TextEncoder('utf-8')
// https://fetch.spec.whatwg.org/#response-class
class Response {
// Creates network error Response.
static error () {
// TODO
const relevantRealm = { settingsObject: {} }
// The static error() method steps are to return the result of creating a
// Response object, given a new network error, "immutable", and thiss
// relevant Realm.
const responseObject = new Response()
responseObject[kState] = makeNetworkError()
responseObject[kRealm] = relevantRealm
responseObject[kHeaders][kHeadersList] = responseObject[kState].headersList
responseObject[kHeaders][kGuard] = 'immutable'
responseObject[kHeaders][kRealm] = relevantRealm
return responseObject
}
// https://fetch.spec.whatwg.org/#dom-response-json
static json (data, init = {}) {
webidl.argumentLengthCheck(arguments, 1, { header: 'Response.json' })
if (init !== null) {
init = webidl.converters.ResponseInit(init)
}
// 1. Let bytes the result of running serialize a JavaScript value to JSON bytes on data.
const bytes = textEncoder.encode(
serializeJavascriptValueToJSONString(data)
)
// 2. Let body be the result of extracting bytes.
const body = extractBody(bytes)
// 3. Let responseObject be the result of creating a Response object, given a new response,
// "response", and thiss relevant Realm.
const relevantRealm = { settingsObject: {} }
const responseObject = new Response()
responseObject[kRealm] = relevantRealm
responseObject[kHeaders][kGuard] = 'response'
responseObject[kHeaders][kRealm] = relevantRealm
// 4. Perform initialize a response given responseObject, init, and (body, "application/json").
initializeResponse(responseObject, init, { body: body[0], type: 'application/json' })
// 5. Return responseObject.
return responseObject
}
// Creates a redirect Response that redirects to url with status status.
static redirect (url, status = 302) {
const relevantRealm = { settingsObject: {} }
webidl.argumentLengthCheck(arguments, 1, { header: 'Response.redirect' })
url = webidl.converters.USVString(url)
status = webidl.converters['unsigned short'](status)
// 1. Let parsedURL be the result of parsing url with current settings
// objects API base URL.
// 2. If parsedURL is failure, then throw a TypeError.
// TODO: base-URL?
let parsedURL
try {
parsedURL = new URL(url, getGlobalOrigin())
} catch (err) {
throw Object.assign(new TypeError('Failed to parse URL from ' + url), {
cause: err
})
}
// 3. If status is not a redirect status, then throw a RangeError.
if (!redirectStatusSet.has(status)) {
throw new RangeError('Invalid status code ' + status)
}
// 4. Let responseObject be the result of creating a Response object,
// given a new response, "immutable", and thiss relevant Realm.
const responseObject = new Response()
responseObject[kRealm] = relevantRealm
responseObject[kHeaders][kGuard] = 'immutable'
responseObject[kHeaders][kRealm] = relevantRealm
// 5. Set responseObjects responses status to status.
responseObject[kState].status = status
// 6. Let value be parsedURL, serialized and isomorphic encoded.
const value = isomorphicEncode(URLSerializer(parsedURL))
// 7. Append `Location`/value to responseObjects responses header list.
responseObject[kState].headersList.append('location', value)
// 8. Return responseObject.
return responseObject
}
// https://fetch.spec.whatwg.org/#dom-response
constructor (body = null, init = {}) {
if (body !== null) {
body = webidl.converters.BodyInit(body)
}
init = webidl.converters.ResponseInit(init)
// TODO
this[kRealm] = { settingsObject: {} }
// 1. Set thiss response to a new response.
this[kState] = makeResponse({})
// 2. Set thiss headers to a new Headers object with thiss relevant
// Realm, whose header list is thiss responses header list and guard
// is "response".
this[kHeaders] = new Headers()
this[kHeaders][kGuard] = 'response'
this[kHeaders][kHeadersList] = this[kState].headersList
this[kHeaders][kRealm] = this[kRealm]
// 3. Let bodyWithType be null.
let bodyWithType = null
// 4. If body is non-null, then set bodyWithType to the result of extracting body.
if (body != null) {
const [extractedBody, type] = extractBody(body)
bodyWithType = { body: extractedBody, type }
}
// 5. Perform initialize a response given this, init, and bodyWithType.
initializeResponse(this, init, bodyWithType)
}
// Returns responses type, e.g., "cors".
get type () {
webidl.brandCheck(this, Response)
// The type getter steps are to return thiss responses type.
return this[kState].type
}
// Returns responses URL, if it has one; otherwise the empty string.
get url () {
webidl.brandCheck(this, Response)
const urlList = this[kState].urlList
// The url getter steps are to return the empty string if thiss
// responses URL is null; otherwise thiss responses URL,
// serialized with exclude fragment set to true.
const url = urlList[urlList.length - 1] ?? null
if (url === null) {
return ''
}
return URLSerializer(url, true)
}
// Returns whether response was obtained through a redirect.
get redirected () {
webidl.brandCheck(this, Response)
// The redirected getter steps are to return true if thiss responses URL
// list has more than one item; otherwise false.
return this[kState].urlList.length > 1
}
// Returns responses status.
get status () {
webidl.brandCheck(this, Response)
// The status getter steps are to return thiss responses status.
return this[kState].status
}
// Returns whether responses status is an ok status.
get ok () {
webidl.brandCheck(this, Response)
// The ok getter steps are to return true if thiss responses status is an
// ok status; otherwise false.
return this[kState].status >= 200 && this[kState].status <= 299
}
// Returns responses status message.
get statusText () {
webidl.brandCheck(this, Response)
// The statusText getter steps are to return thiss responses status
// message.
return this[kState].statusText
}
// Returns responses headers as Headers.
get headers () {
webidl.brandCheck(this, Response)
// The headers getter steps are to return thiss headers.
return this[kHeaders]
}
get body () {
webidl.brandCheck(this, Response)
return this[kState].body ? this[kState].body.stream : null
}
get bodyUsed () {
webidl.brandCheck(this, Response)
return !!this[kState].body && util.isDisturbed(this[kState].body.stream)
}
// Returns a clone of response.
clone () {
webidl.brandCheck(this, Response)
// 1. If this is unusable, then throw a TypeError.
if (this.bodyUsed || (this.body && this.body.locked)) {
throw webidl.errors.exception({
header: 'Response.clone',
message: 'Body has already been consumed.'
})
}
// 2. Let clonedResponse be the result of cloning thiss response.
const clonedResponse = cloneResponse(this[kState])
// 3. Return the result of creating a Response object, given
// clonedResponse, thiss headerss guard, and thiss relevant Realm.
const clonedResponseObject = new Response()
clonedResponseObject[kState] = clonedResponse
clonedResponseObject[kRealm] = this[kRealm]
clonedResponseObject[kHeaders][kHeadersList] = clonedResponse.headersList
clonedResponseObject[kHeaders][kGuard] = this[kHeaders][kGuard]
clonedResponseObject[kHeaders][kRealm] = this[kHeaders][kRealm]
return clonedResponseObject
}
}
mixinBody(Response)
Object.defineProperties(Response.prototype, {
type: kEnumerableProperty,
url: kEnumerableProperty,
status: kEnumerableProperty,
ok: kEnumerableProperty,
redirected: kEnumerableProperty,
statusText: kEnumerableProperty,
headers: kEnumerableProperty,
clone: kEnumerableProperty,
body: kEnumerableProperty,
bodyUsed: kEnumerableProperty,
[Symbol.toStringTag]: {
value: 'Response',
configurable: true
}
})
Object.defineProperties(Response, {
json: kEnumerableProperty,
redirect: kEnumerableProperty,
error: kEnumerableProperty
})
// https://fetch.spec.whatwg.org/#concept-response-clone
function cloneResponse (response) {
// To clone a response response, run these steps:
// 1. If response is a filtered response, then return a new identical
// filtered response whose internal response is a clone of responses
// internal response.
if (response.internalResponse) {
return filterResponse(
cloneResponse(response.internalResponse),
response.type
)
}
// 2. Let newResponse be a copy of response, except for its body.
const newResponse = makeResponse({ ...response, body: null })
// 3. If responses body is non-null, then set newResponses body to the
// result of cloning responses body.
if (response.body != null) {
newResponse.body = cloneBody(response.body)
}
// 4. Return newResponse.
return newResponse
}
function makeResponse (init) {
return {
aborted: false,
rangeRequested: false,
timingAllowPassed: false,
requestIncludesCredentials: false,
type: 'default',
status: 200,
timingInfo: null,
cacheState: '',
statusText: '',
...init,
headersList: init.headersList
? new HeadersList(init.headersList)
: new HeadersList(),
urlList: init.urlList ? [...init.urlList] : []
}
}
function makeNetworkError (reason) {
const isError = isErrorLike(reason)
return makeResponse({
type: 'error',
status: 0,
error: isError
? reason
: new Error(reason ? String(reason) : reason),
aborted: reason && reason.name === 'AbortError'
})
}
function makeFilteredResponse (response, state) {
state = {
internalResponse: response,
...state
}
return new Proxy(response, {
get (target, p) {
return p in state ? state[p] : target[p]
},
set (target, p, value) {
assert(!(p in state))
target[p] = value
return true
}
})
}
// https://fetch.spec.whatwg.org/#concept-filtered-response
function filterResponse (response, type) {
// Set response to the following filtered response with response as its
// internal response, depending on requests response tainting:
if (type === 'basic') {
// A basic filtered response is a filtered response whose type is "basic"
// and header list excludes any headers in internal responses header list
// whose name is a forbidden response-header name.
// Note: undici does not implement forbidden response-header names
return makeFilteredResponse(response, {
type: 'basic',
headersList: response.headersList
})
} else if (type === 'cors') {
// A CORS filtered response is a filtered response whose type is "cors"
// and header list excludes any headers in internal responses header
// list whose name is not a CORS-safelisted response-header name, given
// internal responses CORS-exposed header-name list.
// Note: undici does not implement CORS-safelisted response-header names
return makeFilteredResponse(response, {
type: 'cors',
headersList: response.headersList
})
} else if (type === 'opaque') {
// An opaque filtered response is a filtered response whose type is
// "opaque", URL list is the empty list, status is 0, status message
// is the empty byte sequence, header list is empty, and body is null.
return makeFilteredResponse(response, {
type: 'opaque',
urlList: Object.freeze([]),
status: 0,
statusText: '',
body: null
})
} else if (type === 'opaqueredirect') {
// An opaque-redirect filtered response is a filtered response whose type
// is "opaqueredirect", status is 0, status message is the empty byte
// sequence, header list is empty, and body is null.
return makeFilteredResponse(response, {
type: 'opaqueredirect',
status: 0,
statusText: '',
headersList: [],
body: null
})
} else {
assert(false)
}
}
// https://fetch.spec.whatwg.org/#appropriate-network-error
function makeAppropriateNetworkError (fetchParams, err = null) {
// 1. Assert: fetchParams is canceled.
assert(isCancelled(fetchParams))
// 2. Return an aborted network error if fetchParams is aborted;
// otherwise return a network error.
return isAborted(fetchParams)
? makeNetworkError(Object.assign(new DOMException('The operation was aborted.', 'AbortError'), { cause: err }))
: makeNetworkError(Object.assign(new DOMException('Request was cancelled.'), { cause: err }))
}
// https://whatpr.org/fetch/1392.html#initialize-a-response
function initializeResponse (response, init, body) {
// 1. If init["status"] is not in the range 200 to 599, inclusive, then
// throw a RangeError.
if (init.status !== null && (init.status < 200 || init.status > 599)) {
throw new RangeError('init["status"] must be in the range of 200 to 599, inclusive.')
}
// 2. If init["statusText"] does not match the reason-phrase token production,
// then throw a TypeError.
if ('statusText' in init && init.statusText != null) {
// See, https://datatracker.ietf.org/doc/html/rfc7230#section-3.1.2:
// reason-phrase = *( HTAB / SP / VCHAR / obs-text )
if (!isValidReasonPhrase(String(init.statusText))) {
throw new TypeError('Invalid statusText')
}
}
// 3. Set responses responses status to init["status"].
if ('status' in init && init.status != null) {
response[kState].status = init.status
}
// 4. Set responses responses status message to init["statusText"].
if ('statusText' in init && init.statusText != null) {
response[kState].statusText = init.statusText
}
// 5. If init["headers"] exists, then fill responses headers with init["headers"].
if ('headers' in init && init.headers != null) {
fill(response[kHeaders], init.headers)
}
// 6. If body was given, then:
if (body) {
// 1. If response's status is a null body status, then throw a TypeError.
if (nullBodyStatus.includes(response.status)) {
throw webidl.errors.exception({
header: 'Response constructor',
message: 'Invalid response status code ' + response.status
})
}
// 2. Set response's body to body's body.
response[kState].body = body.body
// 3. If body's type is non-null and response's header list does not contain
// `Content-Type`, then append (`Content-Type`, body's type) to response's header list.
if (body.type != null && !response[kState].headersList.contains('Content-Type')) {
response[kState].headersList.append('content-type', body.type)
}
}
}
webidl.converters.ReadableStream = webidl.interfaceConverter(
ReadableStream
)
webidl.converters.FormData = webidl.interfaceConverter(
FormData
)
webidl.converters.URLSearchParams = webidl.interfaceConverter(
URLSearchParams
)
// https://fetch.spec.whatwg.org/#typedefdef-xmlhttprequestbodyinit
webidl.converters.XMLHttpRequestBodyInit = function (V) {
if (typeof V === 'string') {
return webidl.converters.USVString(V)
}
if (isBlobLike(V)) {
return webidl.converters.Blob(V, { strict: false })
}
if (
types.isAnyArrayBuffer(V) ||
types.isTypedArray(V) ||
types.isDataView(V)
) {
return webidl.converters.BufferSource(V)
}
if (util.isFormDataLike(V)) {
return webidl.converters.FormData(V, { strict: false })
}
if (V instanceof URLSearchParams) {
return webidl.converters.URLSearchParams(V)
}
return webidl.converters.DOMString(V)
}
// https://fetch.spec.whatwg.org/#bodyinit
webidl.converters.BodyInit = function (V) {
if (V instanceof ReadableStream) {
return webidl.converters.ReadableStream(V)
}
// Note: the spec doesn't include async iterables,
// this is an undici extension.
if (V?.[Symbol.asyncIterator]) {
return V
}
return webidl.converters.XMLHttpRequestBodyInit(V)
}
webidl.converters.ResponseInit = webidl.dictionaryConverter([
{
key: 'status',
converter: webidl.converters['unsigned short'],
defaultValue: 200
},
{
key: 'statusText',
converter: webidl.converters.ByteString,
defaultValue: ''
},
{
key: 'headers',
converter: webidl.converters.HeadersInit
}
])
module.exports = {
makeNetworkError,
makeResponse,
makeAppropriateNetworkError,
filterResponse,
Response,
cloneResponse
}

10
node_modules/undici/lib/fetch/symbols.js generated vendored Normal file
View File

@ -0,0 +1,10 @@
'use strict'
module.exports = {
kUrl: Symbol('url'),
kHeaders: Symbol('headers'),
kSignal: Symbol('signal'),
kState: Symbol('state'),
kGuard: Symbol('guard'),
kRealm: Symbol('realm')
}

1051
node_modules/undici/lib/fetch/util.js generated vendored Normal file

File diff suppressed because it is too large Load Diff

646
node_modules/undici/lib/fetch/webidl.js generated vendored Normal file
View File

@ -0,0 +1,646 @@
'use strict'
const { types } = require('util')
const { hasOwn, toUSVString } = require('./util')
/** @type {import('../../types/webidl').Webidl} */
const webidl = {}
webidl.converters = {}
webidl.util = {}
webidl.errors = {}
webidl.errors.exception = function (message) {
return new TypeError(`${message.header}: ${message.message}`)
}
webidl.errors.conversionFailed = function (context) {
const plural = context.types.length === 1 ? '' : ' one of'
const message =
`${context.argument} could not be converted to` +
`${plural}: ${context.types.join(', ')}.`
return webidl.errors.exception({
header: context.prefix,
message
})
}
webidl.errors.invalidArgument = function (context) {
return webidl.errors.exception({
header: context.prefix,
message: `"${context.value}" is an invalid ${context.type}.`
})
}
// https://webidl.spec.whatwg.org/#implements
webidl.brandCheck = function (V, I, opts = undefined) {
if (opts?.strict !== false && !(V instanceof I)) {
throw new TypeError('Illegal invocation')
} else {
return V?.[Symbol.toStringTag] === I.prototype[Symbol.toStringTag]
}
}
webidl.argumentLengthCheck = function ({ length }, min, ctx) {
if (length < min) {
throw webidl.errors.exception({
message: `${min} argument${min !== 1 ? 's' : ''} required, ` +
`but${length ? ' only' : ''} ${length} found.`,
...ctx
})
}
}
webidl.illegalConstructor = function () {
throw webidl.errors.exception({
header: 'TypeError',
message: 'Illegal constructor'
})
}
// https://tc39.es/ecma262/#sec-ecmascript-data-types-and-values
webidl.util.Type = function (V) {
switch (typeof V) {
case 'undefined': return 'Undefined'
case 'boolean': return 'Boolean'
case 'string': return 'String'
case 'symbol': return 'Symbol'
case 'number': return 'Number'
case 'bigint': return 'BigInt'
case 'function':
case 'object': {
if (V === null) {
return 'Null'
}
return 'Object'
}
}
}
// https://webidl.spec.whatwg.org/#abstract-opdef-converttoint
webidl.util.ConvertToInt = function (V, bitLength, signedness, opts = {}) {
let upperBound
let lowerBound
// 1. If bitLength is 64, then:
if (bitLength === 64) {
// 1. Let upperBound be 2^53 1.
upperBound = Math.pow(2, 53) - 1
// 2. If signedness is "unsigned", then let lowerBound be 0.
if (signedness === 'unsigned') {
lowerBound = 0
} else {
// 3. Otherwise let lowerBound be 2^53 + 1.
lowerBound = Math.pow(-2, 53) + 1
}
} else if (signedness === 'unsigned') {
// 2. Otherwise, if signedness is "unsigned", then:
// 1. Let lowerBound be 0.
lowerBound = 0
// 2. Let upperBound be 2^bitLength 1.
upperBound = Math.pow(2, bitLength) - 1
} else {
// 3. Otherwise:
// 1. Let lowerBound be -2^bitLength 1.
lowerBound = Math.pow(-2, bitLength) - 1
// 2. Let upperBound be 2^bitLength 1 1.
upperBound = Math.pow(2, bitLength - 1) - 1
}
// 4. Let x be ? ToNumber(V).
let x = Number(V)
// 5. If x is 0, then set x to +0.
if (x === 0) {
x = 0
}
// 6. If the conversion is to an IDL type associated
// with the [EnforceRange] extended attribute, then:
if (opts.enforceRange === true) {
// 1. If x is NaN, +∞, or −∞, then throw a TypeError.
if (
Number.isNaN(x) ||
x === Number.POSITIVE_INFINITY ||
x === Number.NEGATIVE_INFINITY
) {
throw webidl.errors.exception({
header: 'Integer conversion',
message: `Could not convert ${V} to an integer.`
})
}
// 2. Set x to IntegerPart(x).
x = webidl.util.IntegerPart(x)
// 3. If x < lowerBound or x > upperBound, then
// throw a TypeError.
if (x < lowerBound || x > upperBound) {
throw webidl.errors.exception({
header: 'Integer conversion',
message: `Value must be between ${lowerBound}-${upperBound}, got ${x}.`
})
}
// 4. Return x.
return x
}
// 7. If x is not NaN and the conversion is to an IDL
// type associated with the [Clamp] extended
// attribute, then:
if (!Number.isNaN(x) && opts.clamp === true) {
// 1. Set x to min(max(x, lowerBound), upperBound).
x = Math.min(Math.max(x, lowerBound), upperBound)
// 2. Round x to the nearest integer, choosing the
// even integer if it lies halfway between two,
// and choosing +0 rather than 0.
if (Math.floor(x) % 2 === 0) {
x = Math.floor(x)
} else {
x = Math.ceil(x)
}
// 3. Return x.
return x
}
// 8. If x is NaN, +0, +∞, or −∞, then return +0.
if (
Number.isNaN(x) ||
(x === 0 && Object.is(0, x)) ||
x === Number.POSITIVE_INFINITY ||
x === Number.NEGATIVE_INFINITY
) {
return 0
}
// 9. Set x to IntegerPart(x).
x = webidl.util.IntegerPart(x)
// 10. Set x to x modulo 2^bitLength.
x = x % Math.pow(2, bitLength)
// 11. If signedness is "signed" and x ≥ 2^bitLength 1,
// then return x 2^bitLength.
if (signedness === 'signed' && x >= Math.pow(2, bitLength) - 1) {
return x - Math.pow(2, bitLength)
}
// 12. Otherwise, return x.
return x
}
// https://webidl.spec.whatwg.org/#abstract-opdef-integerpart
webidl.util.IntegerPart = function (n) {
// 1. Let r be floor(abs(n)).
const r = Math.floor(Math.abs(n))
// 2. If n < 0, then return -1 × r.
if (n < 0) {
return -1 * r
}
// 3. Otherwise, return r.
return r
}
// https://webidl.spec.whatwg.org/#es-sequence
webidl.sequenceConverter = function (converter) {
return (V) => {
// 1. If Type(V) is not Object, throw a TypeError.
if (webidl.util.Type(V) !== 'Object') {
throw webidl.errors.exception({
header: 'Sequence',
message: `Value of type ${webidl.util.Type(V)} is not an Object.`
})
}
// 2. Let method be ? GetMethod(V, @@iterator).
/** @type {Generator} */
const method = V?.[Symbol.iterator]?.()
const seq = []
// 3. If method is undefined, throw a TypeError.
if (
method === undefined ||
typeof method.next !== 'function'
) {
throw webidl.errors.exception({
header: 'Sequence',
message: 'Object is not an iterator.'
})
}
// https://webidl.spec.whatwg.org/#create-sequence-from-iterable
while (true) {
const { done, value } = method.next()
if (done) {
break
}
seq.push(converter(value))
}
return seq
}
}
// https://webidl.spec.whatwg.org/#es-to-record
webidl.recordConverter = function (keyConverter, valueConverter) {
return (O) => {
// 1. If Type(O) is not Object, throw a TypeError.
if (webidl.util.Type(O) !== 'Object') {
throw webidl.errors.exception({
header: 'Record',
message: `Value of type ${webidl.util.Type(O)} is not an Object.`
})
}
// 2. Let result be a new empty instance of record<K, V>.
const result = {}
if (!types.isProxy(O)) {
// Object.keys only returns enumerable properties
const keys = Object.keys(O)
for (const key of keys) {
// 1. Let typedKey be key converted to an IDL value of type K.
const typedKey = keyConverter(key)
// 2. Let value be ? Get(O, key).
// 3. Let typedValue be value converted to an IDL value of type V.
const typedValue = valueConverter(O[key])
// 4. Set result[typedKey] to typedValue.
result[typedKey] = typedValue
}
// 5. Return result.
return result
}
// 3. Let keys be ? O.[[OwnPropertyKeys]]().
const keys = Reflect.ownKeys(O)
// 4. For each key of keys.
for (const key of keys) {
// 1. Let desc be ? O.[[GetOwnProperty]](key).
const desc = Reflect.getOwnPropertyDescriptor(O, key)
// 2. If desc is not undefined and desc.[[Enumerable]] is true:
if (desc?.enumerable) {
// 1. Let typedKey be key converted to an IDL value of type K.
const typedKey = keyConverter(key)
// 2. Let value be ? Get(O, key).
// 3. Let typedValue be value converted to an IDL value of type V.
const typedValue = valueConverter(O[key])
// 4. Set result[typedKey] to typedValue.
result[typedKey] = typedValue
}
}
// 5. Return result.
return result
}
}
webidl.interfaceConverter = function (i) {
return (V, opts = {}) => {
if (opts.strict !== false && !(V instanceof i)) {
throw webidl.errors.exception({
header: i.name,
message: `Expected ${V} to be an instance of ${i.name}.`
})
}
return V
}
}
webidl.dictionaryConverter = function (converters) {
return (dictionary) => {
const type = webidl.util.Type(dictionary)
const dict = {}
if (type === 'Null' || type === 'Undefined') {
return dict
} else if (type !== 'Object') {
throw webidl.errors.exception({
header: 'Dictionary',
message: `Expected ${dictionary} to be one of: Null, Undefined, Object.`
})
}
for (const options of converters) {
const { key, defaultValue, required, converter } = options
if (required === true) {
if (!hasOwn(dictionary, key)) {
throw webidl.errors.exception({
header: 'Dictionary',
message: `Missing required key "${key}".`
})
}
}
let value = dictionary[key]
const hasDefault = hasOwn(options, 'defaultValue')
// Only use defaultValue if value is undefined and
// a defaultValue options was provided.
if (hasDefault && value !== null) {
value = value ?? defaultValue
}
// A key can be optional and have no default value.
// When this happens, do not perform a conversion,
// and do not assign the key a value.
if (required || hasDefault || value !== undefined) {
value = converter(value)
if (
options.allowedValues &&
!options.allowedValues.includes(value)
) {
throw webidl.errors.exception({
header: 'Dictionary',
message: `${value} is not an accepted type. Expected one of ${options.allowedValues.join(', ')}.`
})
}
dict[key] = value
}
}
return dict
}
}
webidl.nullableConverter = function (converter) {
return (V) => {
if (V === null) {
return V
}
return converter(V)
}
}
// https://webidl.spec.whatwg.org/#es-DOMString
webidl.converters.DOMString = function (V, opts = {}) {
// 1. If V is null and the conversion is to an IDL type
// associated with the [LegacyNullToEmptyString]
// extended attribute, then return the DOMString value
// that represents the empty string.
if (V === null && opts.legacyNullToEmptyString) {
return ''
}
// 2. Let x be ? ToString(V).
if (typeof V === 'symbol') {
throw new TypeError('Could not convert argument of type symbol to string.')
}
// 3. Return the IDL DOMString value that represents the
// same sequence of code units as the one the
// ECMAScript String value x represents.
return String(V)
}
// https://webidl.spec.whatwg.org/#es-ByteString
webidl.converters.ByteString = function (V) {
// 1. Let x be ? ToString(V).
// Note: DOMString converter perform ? ToString(V)
const x = webidl.converters.DOMString(V)
// 2. If the value of any element of x is greater than
// 255, then throw a TypeError.
for (let index = 0; index < x.length; index++) {
if (x.charCodeAt(index) > 255) {
throw new TypeError(
'Cannot convert argument to a ByteString because the character at ' +
`index ${index} has a value of ${x.charCodeAt(index)} which is greater than 255.`
)
}
}
// 3. Return an IDL ByteString value whose length is the
// length of x, and where the value of each element is
// the value of the corresponding element of x.
return x
}
// https://webidl.spec.whatwg.org/#es-USVString
webidl.converters.USVString = toUSVString
// https://webidl.spec.whatwg.org/#es-boolean
webidl.converters.boolean = function (V) {
// 1. Let x be the result of computing ToBoolean(V).
const x = Boolean(V)
// 2. Return the IDL boolean value that is the one that represents
// the same truth value as the ECMAScript Boolean value x.
return x
}
// https://webidl.spec.whatwg.org/#es-any
webidl.converters.any = function (V) {
return V
}
// https://webidl.spec.whatwg.org/#es-long-long
webidl.converters['long long'] = function (V) {
// 1. Let x be ? ConvertToInt(V, 64, "signed").
const x = webidl.util.ConvertToInt(V, 64, 'signed')
// 2. Return the IDL long long value that represents
// the same numeric value as x.
return x
}
// https://webidl.spec.whatwg.org/#es-unsigned-long-long
webidl.converters['unsigned long long'] = function (V) {
// 1. Let x be ? ConvertToInt(V, 64, "unsigned").
const x = webidl.util.ConvertToInt(V, 64, 'unsigned')
// 2. Return the IDL unsigned long long value that
// represents the same numeric value as x.
return x
}
// https://webidl.spec.whatwg.org/#es-unsigned-long
webidl.converters['unsigned long'] = function (V) {
// 1. Let x be ? ConvertToInt(V, 32, "unsigned").
const x = webidl.util.ConvertToInt(V, 32, 'unsigned')
// 2. Return the IDL unsigned long value that
// represents the same numeric value as x.
return x
}
// https://webidl.spec.whatwg.org/#es-unsigned-short
webidl.converters['unsigned short'] = function (V, opts) {
// 1. Let x be ? ConvertToInt(V, 16, "unsigned").
const x = webidl.util.ConvertToInt(V, 16, 'unsigned', opts)
// 2. Return the IDL unsigned short value that represents
// the same numeric value as x.
return x
}
// https://webidl.spec.whatwg.org/#idl-ArrayBuffer
webidl.converters.ArrayBuffer = function (V, opts = {}) {
// 1. If Type(V) is not Object, or V does not have an
// [[ArrayBufferData]] internal slot, then throw a
// TypeError.
// see: https://tc39.es/ecma262/#sec-properties-of-the-arraybuffer-instances
// see: https://tc39.es/ecma262/#sec-properties-of-the-sharedarraybuffer-instances
if (
webidl.util.Type(V) !== 'Object' ||
!types.isAnyArrayBuffer(V)
) {
throw webidl.errors.conversionFailed({
prefix: `${V}`,
argument: `${V}`,
types: ['ArrayBuffer']
})
}
// 2. If the conversion is not to an IDL type associated
// with the [AllowShared] extended attribute, and
// IsSharedArrayBuffer(V) is true, then throw a
// TypeError.
if (opts.allowShared === false && types.isSharedArrayBuffer(V)) {
throw webidl.errors.exception({
header: 'ArrayBuffer',
message: 'SharedArrayBuffer is not allowed.'
})
}
// 3. If the conversion is not to an IDL type associated
// with the [AllowResizable] extended attribute, and
// IsResizableArrayBuffer(V) is true, then throw a
// TypeError.
// Note: resizable ArrayBuffers are currently a proposal.
// 4. Return the IDL ArrayBuffer value that is a
// reference to the same object as V.
return V
}
webidl.converters.TypedArray = function (V, T, opts = {}) {
// 1. Let T be the IDL type V is being converted to.
// 2. If Type(V) is not Object, or V does not have a
// [[TypedArrayName]] internal slot with a value
// equal to Ts name, then throw a TypeError.
if (
webidl.util.Type(V) !== 'Object' ||
!types.isTypedArray(V) ||
V.constructor.name !== T.name
) {
throw webidl.errors.conversionFailed({
prefix: `${T.name}`,
argument: `${V}`,
types: [T.name]
})
}
// 3. If the conversion is not to an IDL type associated
// with the [AllowShared] extended attribute, and
// IsSharedArrayBuffer(V.[[ViewedArrayBuffer]]) is
// true, then throw a TypeError.
if (opts.allowShared === false && types.isSharedArrayBuffer(V.buffer)) {
throw webidl.errors.exception({
header: 'ArrayBuffer',
message: 'SharedArrayBuffer is not allowed.'
})
}
// 4. If the conversion is not to an IDL type associated
// with the [AllowResizable] extended attribute, and
// IsResizableArrayBuffer(V.[[ViewedArrayBuffer]]) is
// true, then throw a TypeError.
// Note: resizable array buffers are currently a proposal
// 5. Return the IDL value of type T that is a reference
// to the same object as V.
return V
}
webidl.converters.DataView = function (V, opts = {}) {
// 1. If Type(V) is not Object, or V does not have a
// [[DataView]] internal slot, then throw a TypeError.
if (webidl.util.Type(V) !== 'Object' || !types.isDataView(V)) {
throw webidl.errors.exception({
header: 'DataView',
message: 'Object is not a DataView.'
})
}
// 2. If the conversion is not to an IDL type associated
// with the [AllowShared] extended attribute, and
// IsSharedArrayBuffer(V.[[ViewedArrayBuffer]]) is true,
// then throw a TypeError.
if (opts.allowShared === false && types.isSharedArrayBuffer(V.buffer)) {
throw webidl.errors.exception({
header: 'ArrayBuffer',
message: 'SharedArrayBuffer is not allowed.'
})
}
// 3. If the conversion is not to an IDL type associated
// with the [AllowResizable] extended attribute, and
// IsResizableArrayBuffer(V.[[ViewedArrayBuffer]]) is
// true, then throw a TypeError.
// Note: resizable ArrayBuffers are currently a proposal
// 4. Return the IDL DataView value that is a reference
// to the same object as V.
return V
}
// https://webidl.spec.whatwg.org/#BufferSource
webidl.converters.BufferSource = function (V, opts = {}) {
if (types.isAnyArrayBuffer(V)) {
return webidl.converters.ArrayBuffer(V, opts)
}
if (types.isTypedArray(V)) {
return webidl.converters.TypedArray(V, V.constructor)
}
if (types.isDataView(V)) {
return webidl.converters.DataView(V, opts)
}
throw new TypeError(`Could not convert ${V} to a BufferSource.`)
}
webidl.converters['sequence<ByteString>'] = webidl.sequenceConverter(
webidl.converters.ByteString
)
webidl.converters['sequence<sequence<ByteString>>'] = webidl.sequenceConverter(
webidl.converters['sequence<ByteString>']
)
webidl.converters['record<ByteString, ByteString>'] = webidl.recordConverter(
webidl.converters.ByteString,
webidl.converters.ByteString
)
module.exports = {
webidl
}